consul/api/peering_test.go
Nitya Dhanushkodi f47319b7c6
update generate token endpoint to take external addresses (#13844)
Update generate token endpoint (rpc, http, and api module)

If ServerExternalAddresses are set, it will override any addresses gotten from the "consul" service, and be used in the token instead, and dialed by the dialer. This allows for setting up a load balancer for example, in front of the consul servers.
2022-07-21 14:56:11 -07:00

292 lines
8.4 KiB
Go

package api
import (
"context"
"encoding/base64"
"reflect"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/hashicorp/consul/sdk/testutil"
"github.com/hashicorp/consul/sdk/testutil/retry"
)
const DefaultCtxDuration = 15 * time.Second
func peerExistsInPeerListings(peer *Peering, peerings []*Peering) bool {
for _, aPeer := range peerings {
isEqual := (peer.PeerID == aPeer.PeerID) &&
(reflect.DeepEqual(peer.PeerCAPems, aPeer.PeerCAPems)) &&
(peer.PeerServerName == aPeer.PeerServerName) &&
(peer.Partition == aPeer.Partition) &&
(peer.Name == aPeer.Name) &&
(reflect.DeepEqual(peer.PeerServerAddresses, aPeer.PeerServerAddresses)) &&
(peer.State == aPeer.State) &&
(peer.CreateIndex == aPeer.CreateIndex) &&
(peer.ModifyIndex == aPeer.ModifyIndex) &&
(peer.ImportedServiceCount == aPeer.ImportedServiceCount) &&
(peer.ExportedServiceCount == aPeer.ExportedServiceCount)
if isEqual {
return true
}
}
return false
}
func TestAPI_Peering_Read_ErrorHandling(t *testing.T) {
t.Parallel()
c, s := makeClientWithCA(t)
defer s.Stop()
s.WaitForSerfCheck(t)
ctx, cancel := context.WithTimeout(context.Background(), DefaultCtxDuration)
defer cancel()
peerings := c.Peerings()
t.Run("call Read with no name", func(t *testing.T) {
_, _, err := peerings.Read(ctx, "", nil)
require.EqualError(t, err, "peering name cannot be empty")
})
t.Run("read peer that does not exist on server", func(t *testing.T) {
resp, qm, err := peerings.Read(ctx, "peer1", nil)
require.NoError(t, err)
require.NotNil(t, qm)
require.Nil(t, resp)
})
}
// TestAPI_Peering_List
func TestAPI_Peering_List(t *testing.T) {
t.Parallel()
c, s := makeClientWithCA(t)
defer s.Stop()
s.WaitForSerfCheck(t)
ctx, cancel := context.WithTimeout(context.Background(), DefaultCtxDuration)
defer cancel()
peerings := c.Peerings()
testutil.RunStep(t, "list with no peers", func(t *testing.T) {
// call List when no peers should exist
resp, qm, err := peerings.List(ctx, nil)
require.NoError(t, err)
require.NotNil(t, qm)
require.Empty(t, resp) // no peerings so this should be empty
})
testutil.RunStep(t, "list with some peers", func(t *testing.T) {
// call List when peers are present
resp1, wm, err := peerings.GenerateToken(ctx, PeeringGenerateTokenRequest{PeerName: "peer1"}, nil)
require.NoError(t, err)
require.NotNil(t, wm)
require.NotNil(t, resp1)
resp2, wm, err := peerings.GenerateToken(ctx, PeeringGenerateTokenRequest{PeerName: "peer2"}, nil)
require.NoError(t, err)
require.NotNil(t, wm)
require.NotNil(t, resp2)
peering1, qm, err := peerings.Read(ctx, "peer1", nil)
require.NoError(t, err)
require.NotNil(t, qm)
require.NotNil(t, peering1)
peering2, qm, err := peerings.Read(ctx, "peer2", nil)
require.NoError(t, err)
require.NotNil(t, qm)
require.NotNil(t, peering2)
peeringsList, qm, err := peerings.List(ctx, nil)
require.NoError(t, err)
require.NotNil(t, qm)
require.Len(t, peeringsList, 2)
require.True(t, peerExistsInPeerListings(peering1, peeringsList), "expected to find peering in list response")
require.True(t, peerExistsInPeerListings(peering2, peeringsList), "expected to find peering in list response")
})
}
func TestAPI_Peering_GenerateToken(t *testing.T) {
t.Parallel()
c, s := makeClientWithCA(t)
defer s.Stop()
s.WaitForSerfCheck(t)
ctx, cancel := context.WithTimeout(context.Background(), DefaultCtxDuration)
defer cancel()
peerings := c.Peerings()
t.Run("cannot have GenerateToken forward DC requests", func(t *testing.T) {
// Try to generate a token in dc2
_, _, err := peerings.GenerateToken(ctx, PeeringGenerateTokenRequest{PeerName: "peer2", Datacenter: "dc2"}, nil)
require.Error(t, err)
})
}
func TestAPI_Peering_GenerateToken_ExternalAddresses(t *testing.T) {
t.Parallel()
c, s := makeClient(t) // this is "dc1"
defer s.Stop()
s.WaitForSerfCheck(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
externalAddress := "32.1.2.3:8502"
// Generate a token happy path
p1 := PeeringGenerateTokenRequest{
PeerName: "peer1",
Meta: map[string]string{"foo": "bar"},
ServerExternalAddresses: []string{externalAddress},
}
resp, wm, err := c.Peerings().GenerateToken(ctx, p1, nil)
require.NoError(t, err)
require.NotNil(t, wm)
require.NotNil(t, resp)
tokenJSON, err := base64.StdEncoding.DecodeString(resp.PeeringToken)
require.NoError(t, err)
require.Contains(t, string(tokenJSON), externalAddress)
}
// TODO(peering): cover the following test cases: bad/ malformed input, peering with wrong token,
// peering with the wrong PeerName
// TestAPI_Peering_GenerateToken_Read_Establish_Delete tests the following use case:
// a server creates a peering token, reads the token, then another server calls establish peering
// finally, we delete the token on the first server
func TestAPI_Peering_GenerateToken_Read_Establish_Delete(t *testing.T) {
t.Parallel()
c, s := makeClient(t) // this is "dc1"
defer s.Stop()
s.WaitForSerfCheck(t)
// make a "client" server in second DC for peering
c2, s2 := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) {
conf.Datacenter = "dc2"
})
defer s2.Stop()
testNodeServiceCheckRegistrations(t, c2, "dc2")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
var token1 string
testutil.RunStep(t, "generate token", func(t *testing.T) {
// Generate a token happy path
p1 := PeeringGenerateTokenRequest{
PeerName: "peer1",
Meta: map[string]string{"foo": "bar"},
}
resp, wm, err := c.Peerings().GenerateToken(ctx, p1, nil)
require.NoError(t, err)
require.NotNil(t, wm)
require.NotNil(t, resp)
token1 = resp.PeeringToken
})
testutil.RunStep(t, "verify token", func(t *testing.T) {
// Read token generated on server
resp, qm, err := c.Peerings().Read(ctx, "peer1", nil)
require.NoError(t, err)
require.NotNil(t, qm)
require.NotNil(t, resp)
// token specific assertions on the "server"
require.Equal(t, "peer1", resp.Name)
require.Equal(t, PeeringStatePending, resp.State)
require.Equal(t, map[string]string{"foo": "bar"}, resp.Meta)
})
testutil.RunStep(t, "establish peering", func(t *testing.T) {
i := PeeringEstablishRequest{
Datacenter: c2.config.Datacenter,
PeerName: "peer1",
PeeringToken: token1,
Meta: map[string]string{"foo": "bar"},
}
_, wm, err := c2.Peerings().Establish(ctx, i, nil)
require.NoError(t, err)
require.NotNil(t, wm)
retry.Run(t, func(r *retry.R) {
resp, qm, err := c2.Peerings().Read(ctx, "peer1", nil)
require.NoError(r, err)
require.NotNil(r, qm)
// require that the peering state is not undefined
require.Equal(r, PeeringStateEstablishing, resp.State)
require.Equal(r, map[string]string{"foo": "bar"}, resp.Meta)
})
})
testutil.RunStep(t, "look for active state of peering in dc2", func(t *testing.T) {
// read and list the peer to make sure the status transitions to active
retry.Run(t, func(r *retry.R) {
peering, qm, err := c2.Peerings().Read(ctx, "peer1", nil)
require.NoError(r, err)
require.NotNil(r, qm)
require.NotNil(r, peering)
require.Equal(r, PeeringStateActive, peering.State)
peerings, qm, err := c2.Peerings().List(ctx, nil)
require.NoError(r, err)
require.NotNil(r, qm)
require.NotNil(r, peerings)
require.Equal(r, PeeringStateActive, peerings[0].State)
})
})
testutil.RunStep(t, "look for active state of peering in dc1", func(t *testing.T) {
// read and list the peer to make sure the status transitions to active
retry.Run(t, func(r *retry.R) {
peering, qm, err := c.Peerings().Read(ctx, "peer1", nil)
require.NoError(r, err)
require.NotNil(r, qm)
require.NotNil(r, peering)
require.Equal(r, PeeringStateActive, peering.State)
peerings, qm, err := c.Peerings().List(ctx, nil)
require.NoError(r, err)
require.NotNil(r, qm)
require.NotNil(r, peerings)
require.Equal(r, PeeringStateActive, peerings[0].State)
})
})
testutil.RunStep(t, "delete peering at source", func(t *testing.T) {
// Delete the token on server 1
wm, err := c.Peerings().Delete(ctx, "peer1", nil)
require.NoError(t, err)
require.NotNil(t, wm)
// Read to see if the token is gone
retry.Run(t, func(r *retry.R) {
resp, qm, err := c.Peerings().Read(ctx, "peer1", nil)
require.NoError(r, err)
require.NotNil(r, qm)
require.Nil(r, resp)
})
})
}