consul/api/peering_test.go

233 lines
6.7 KiB
Go

package api
import (
"context"
"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
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) {
resp, qm, err := peerings.Read(ctx, PeeringReadRequest{}, nil)
// basic checks
require.EqualError(t, err, "peering name cannot be empty")
require.Empty(t, qm)
require.Empty(t, resp)
})
t.Run("read peer that does not exist on server", func(t *testing.T) {
resp, qm, err := peerings.Read(ctx, PeeringReadRequest{Name: "peer1"}, nil)
// basic checks
require.NotNil(t, err) // 404
require.Empty(t, qm)
require.Empty(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()
// "call List when no peers should exist"
resp, qm, err := peerings.List(ctx, PeeringListRequest{}, nil)
// basic checks
require.NoError(t, err)
require.NotEmpty(t, qm)
require.Empty(t, resp) // no peerings so this should be empty
// "call List when peers are present"
resp2, wm, err := peerings.GenerateToken(ctx, PeeringGenerateTokenRequest{PeerName: "peer1"}, nil)
require.NoError(t, err)
require.NotEmpty(t, wm)
require.NotEmpty(t, resp2)
resp3, wm, err := peerings.GenerateToken(ctx, PeeringGenerateTokenRequest{PeerName: "peer2"}, nil)
require.NoError(t, err)
require.NotEmpty(t, wm)
require.NotEmpty(t, resp3)
peering1, qm, err2 := peerings.Read(ctx, PeeringReadRequest{Name: "peer1"}, nil)
require.NoError(t, err2)
require.NotEmpty(t, qm)
require.NotEmpty(t, peering1)
peering2, qm, err2 := peerings.Read(ctx, PeeringReadRequest{Name: "peer2"}, nil)
require.NoError(t, err2)
require.NotEmpty(t, qm)
require.NotEmpty(t, peering2)
peeringsList, qm, err := peerings.List(ctx, PeeringListRequest{}, nil)
require.NoError(t, err)
require.NotEmpty(t, qm)
require.Equal(t, 2, len(peeringsList))
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
resp, wm, err := peerings.GenerateToken(ctx, PeeringGenerateTokenRequest{PeerName: "peer2", Datacenter: "dc2"}, nil)
require.Error(t, err)
require.Empty(t, wm)
require.Empty(t, resp)
})
}
// TODO(peering): cover the following test cases: bad/ malformed input, peering with wrong token,
// peering with the wrong PeerName
// TestAPI_Peering_GenerateToken_Read_Initiate_Delete tests the following use case:
// a server creates a peering token, reads the token, then another server calls initiate peering
// finally, we delete the token on the first server
func TestAPI_Peering_GenerateToken_Read_Initiate_Delete(t *testing.T) {
t.Parallel()
c, s := makeClientWithCA(t)
defer s.Stop()
s.WaitForSerfCheck(t)
options := &WriteOptions{Datacenter: "dc1"}
ctx, cancel := context.WithTimeout(context.Background(), DefaultCtxDuration)
defer cancel()
peerings := c.Peerings()
p1 := PeeringGenerateTokenRequest{
PeerName: "peer1",
Meta: map[string]string{"foo": "bar"},
}
var token1 string
// Generate a token happy path
resp, wm, err := peerings.GenerateToken(ctx, p1, options)
token1 = resp.PeeringToken
require.NoError(t, err)
require.NotEmpty(t, wm)
require.NotEmpty(t, resp)
// Read token generated on server
resp2, qm, err2 := peerings.Read(ctx, PeeringReadRequest{Name: "peer1"}, nil)
// basic ok checking
require.NoError(t, err2)
require.NotEmpty(t, qm)
require.NotEmpty(t, resp2)
// token specific assertions on the "server"
require.Equal(t, "peer1", resp2.Name)
require.Equal(t, PeeringStateInitial, resp2.State)
require.Equal(t, map[string]string{"foo": "bar"}, resp2.Meta)
// Initiate peering
// make a "client" server in second DC for peering
c2, s2 := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) {
conf.Datacenter = "dc2"
})
defer s2.Stop()
i := PeeringInitiateRequest{
Datacenter: c2.config.Datacenter,
PeerName: "peer1",
PeeringToken: token1,
Meta: map[string]string{"foo": "bar"},
}
respi, wm3, err3 := c2.Peerings().Initiate(ctx, i, options)
// basic checks
require.NoError(t, err3)
require.NotEmpty(t, wm3)
// at first the token will be undefined
require.Equal(t, PeeringStateUndefined, PeeringState(respi.Status))
// wait for the peering backend to finish the peering connection
time.Sleep(2 * time.Second)
retry.Run(t, func(r *retry.R) {
respr, qm2, err4 := c2.Peerings().Read(ctx, PeeringReadRequest{Name: "peer1"}, nil)
// basic ok checking
require.NoError(r, err4)
require.NotEmpty(r, qm2)
// require that the peering state is not undefined
require.Equal(r, PeeringStateInitial, respr.State)
require.Equal(r, map[string]string{"foo": "bar"}, respr.Meta)
// TODO(peering) -- let's go all the way and test in code either here or somewhere else that PeeringState does move to Active
})
// Delete the token on server 1
resp4, qm3, err5 := peerings.Delete(ctx, PeeringDeleteRequest{Name: "peer1"}, nil)
require.NoError(t, err5)
require.NotEmpty(t, qm3)
// {} is returned on success for now
require.Empty(t, resp4)
// Read to see if the token is "gone"
resp5, qm4, err6 := peerings.Read(ctx, PeeringReadRequest{Name: "peer1"}, nil)
// basic checks
require.NotNil(t, err6)
require.Empty(t, qm4)
require.Empty(t, resp5)
}