2023-08-11 13:12:13 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
|
2023-07-18 23:41:30 +00:00
|
|
|
package peering
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
2024-05-21 19:52:19 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
2023-11-02 19:25:48 +00:00
|
|
|
"github.com/hashicorp/consul/api"
|
2023-07-18 23:41:30 +00:00
|
|
|
"github.com/hashicorp/consul/testing/deployer/topology"
|
|
|
|
)
|
|
|
|
|
|
|
|
// TestRotateGW ensures that peered services continue to be able to talk to their
|
|
|
|
// upstreams during a mesh gateway rotation
|
|
|
|
// NOTE: because suiteRotateGW needs to mutate the topo, we actually *DO NOT* share a topo
|
|
|
|
|
|
|
|
type suiteRotateGW struct {
|
|
|
|
DC string
|
|
|
|
Peer string
|
|
|
|
|
2023-11-10 19:22:06 +00:00
|
|
|
sidServer topology.ID
|
2023-07-18 23:41:30 +00:00
|
|
|
nodeServer topology.NodeID
|
|
|
|
|
2023-11-10 19:22:06 +00:00
|
|
|
sidClient topology.ID
|
2023-07-18 23:41:30 +00:00
|
|
|
nodeClient topology.NodeID
|
|
|
|
|
2024-05-21 19:52:19 +00:00
|
|
|
upstream *topology.Upstream
|
2023-07-18 23:41:30 +00:00
|
|
|
|
|
|
|
newMGWNodeName string
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRotateGW(t *testing.T) {
|
|
|
|
suites := []*suiteRotateGW{
|
|
|
|
{DC: "dc1", Peer: "dc2"},
|
|
|
|
{DC: "dc2", Peer: "dc1"},
|
|
|
|
}
|
|
|
|
ct := NewCommonTopo(t)
|
|
|
|
for _, s := range suites {
|
|
|
|
s.setup(t, ct)
|
|
|
|
}
|
|
|
|
ct.Launch(t)
|
|
|
|
for _, s := range suites {
|
|
|
|
s := s
|
|
|
|
t.Run(fmt.Sprintf("%s->%s", s.DC, s.Peer), func(t *testing.T) {
|
|
|
|
// no t.Parallel() due to Relaunch
|
|
|
|
s.test(t, ct)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *suiteRotateGW) setup(t *testing.T, ct *commonTopo) {
|
|
|
|
const prefix = "ac7-1-"
|
|
|
|
|
|
|
|
clu := ct.ClusterByDatacenter(t, s.DC)
|
|
|
|
peerClu := ct.ClusterByDatacenter(t, s.Peer)
|
|
|
|
partition := "default"
|
|
|
|
peer := LocalPeerName(peerClu, "default")
|
|
|
|
cluPeerName := LocalPeerName(clu, "default")
|
|
|
|
|
|
|
|
server := NewFortioServiceWithDefaults(
|
|
|
|
peerClu.Datacenter,
|
2023-11-10 19:22:06 +00:00
|
|
|
topology.ID{
|
2023-07-18 23:41:30 +00:00
|
|
|
Name: prefix + "server-http",
|
|
|
|
Partition: partition,
|
|
|
|
},
|
|
|
|
nil,
|
|
|
|
)
|
|
|
|
|
|
|
|
// Make clients which have server upstreams
|
2024-05-21 19:52:19 +00:00
|
|
|
upstream := &topology.Upstream{
|
2023-11-10 19:22:06 +00:00
|
|
|
ID: topology.ID{
|
2023-07-18 23:41:30 +00:00
|
|
|
Name: server.ID.Name,
|
|
|
|
Partition: partition,
|
|
|
|
},
|
|
|
|
// TODO: we shouldn't need this, need to investigate
|
|
|
|
LocalAddress: "0.0.0.0",
|
|
|
|
LocalPort: 5001,
|
|
|
|
Peer: peer,
|
|
|
|
}
|
|
|
|
// create client in us
|
|
|
|
client := NewFortioServiceWithDefaults(
|
|
|
|
clu.Datacenter,
|
2023-11-10 19:22:06 +00:00
|
|
|
topology.ID{
|
2023-07-18 23:41:30 +00:00
|
|
|
Name: prefix + "client",
|
|
|
|
Partition: partition,
|
|
|
|
},
|
2023-11-10 19:22:06 +00:00
|
|
|
func(s *topology.Workload) {
|
2024-05-21 19:52:19 +00:00
|
|
|
s.Upstreams = []*topology.Upstream{
|
2023-07-18 23:41:30 +00:00
|
|
|
upstream,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)
|
2023-11-10 19:22:06 +00:00
|
|
|
clientNode := ct.AddServiceNode(clu, serviceExt{Workload: client,
|
2023-07-18 23:41:30 +00:00
|
|
|
Config: &api.ServiceConfigEntry{
|
|
|
|
Kind: api.ServiceDefaults,
|
|
|
|
Name: client.ID.Name,
|
|
|
|
Partition: ConfigEntryPartition(client.ID.Partition),
|
|
|
|
Protocol: "http",
|
|
|
|
UpstreamConfig: &api.UpstreamConfiguration{
|
|
|
|
Defaults: &api.UpstreamConfig{
|
|
|
|
MeshGateway: api.MeshGatewayConfig{
|
|
|
|
Mode: api.MeshGatewayModeLocal,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
// actually to be used by the other pairing
|
|
|
|
serverNode := ct.AddServiceNode(peerClu, serviceExt{
|
2023-11-10 19:22:06 +00:00
|
|
|
Workload: server,
|
2023-07-18 23:41:30 +00:00
|
|
|
Config: &api.ServiceConfigEntry{
|
|
|
|
Kind: api.ServiceDefaults,
|
|
|
|
Name: server.ID.Name,
|
|
|
|
Partition: ConfigEntryPartition(partition),
|
|
|
|
Protocol: "http",
|
|
|
|
},
|
|
|
|
Exports: []api.ServiceConsumer{{Peer: cluPeerName}},
|
|
|
|
Intentions: &api.ServiceIntentionsConfigEntry{
|
|
|
|
Kind: api.ServiceIntentions,
|
|
|
|
Name: server.ID.Name,
|
|
|
|
Partition: ConfigEntryPartition(partition),
|
|
|
|
Sources: []*api.SourceIntention{
|
|
|
|
{
|
|
|
|
Name: client.ID.Name,
|
|
|
|
Peer: cluPeerName,
|
|
|
|
Action: api.IntentionActionAllow,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
s.sidClient = client.ID
|
|
|
|
s.nodeClient = clientNode.ID()
|
|
|
|
s.upstream = upstream
|
|
|
|
s.sidServer = server.ID
|
|
|
|
s.nodeServer = serverNode.ID()
|
|
|
|
|
|
|
|
// add a second mesh gateway "new"
|
|
|
|
s.newMGWNodeName = fmt.Sprintf("new-%s-default-mgw", clu.Name)
|
2023-09-06 23:46:34 +00:00
|
|
|
nodeKind := topology.NodeKindClient
|
2023-11-03 16:43:43 +00:00
|
|
|
if clu.Datacenter == ct.agentlessDC {
|
2023-09-06 23:46:34 +00:00
|
|
|
nodeKind = topology.NodeKindDataplane
|
|
|
|
}
|
2023-11-03 16:43:43 +00:00
|
|
|
_, mgwNodes := newTopologyMeshGatewaySet(
|
2023-09-06 23:46:34 +00:00
|
|
|
nodeKind,
|
2023-07-18 23:41:30 +00:00
|
|
|
"default",
|
|
|
|
s.newMGWNodeName,
|
|
|
|
1,
|
|
|
|
[]string{clu.Datacenter, "wan"},
|
|
|
|
func(i int, node *topology.Node) {
|
|
|
|
node.Disabled = true
|
|
|
|
},
|
2023-11-03 16:43:43 +00:00
|
|
|
)
|
|
|
|
clu.Nodes = append(clu.Nodes, mgwNodes...)
|
2023-07-18 23:41:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *suiteRotateGW) test(t *testing.T, ct *commonTopo) {
|
|
|
|
dc := ct.Sprawl.Topology().Clusters[s.DC]
|
|
|
|
peer := ct.Sprawl.Topology().Clusters[s.Peer]
|
|
|
|
|
2023-11-10 19:22:06 +00:00
|
|
|
svcHTTPServer := peer.WorkloadByID(
|
2023-07-18 23:41:30 +00:00
|
|
|
s.nodeServer,
|
|
|
|
s.sidServer,
|
|
|
|
)
|
2023-11-10 19:22:06 +00:00
|
|
|
svcHTTPClient := dc.WorkloadByID(
|
2023-07-18 23:41:30 +00:00
|
|
|
s.nodeClient,
|
|
|
|
s.sidClient,
|
|
|
|
)
|
|
|
|
ct.Assert.HealthyWithPeer(t, dc.Name, svcHTTPServer.ID, LocalPeerName(peer, "default"))
|
|
|
|
|
|
|
|
ct.Assert.FortioFetch2HeaderEcho(t, svcHTTPClient, s.upstream)
|
|
|
|
|
|
|
|
t.Log("relaunching with new gateways")
|
|
|
|
cfg := ct.Sprawl.Config()
|
|
|
|
for _, n := range dc.Nodes {
|
|
|
|
if strings.HasPrefix(n.Name, s.newMGWNodeName) {
|
|
|
|
n.Disabled = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
require.NoError(t, ct.Sprawl.Relaunch(cfg))
|
|
|
|
ct.Assert.FortioFetch2HeaderEcho(t, svcHTTPClient, s.upstream)
|
|
|
|
|
|
|
|
t.Log("relaunching without old gateways")
|
|
|
|
cfg = ct.Sprawl.Config()
|
|
|
|
for _, n := range dc.Nodes {
|
|
|
|
if strings.HasPrefix(n.Name, fmt.Sprintf("%s-default-mgw", dc.Name)) {
|
|
|
|
n.Disabled = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
require.NoError(t, ct.Sprawl.Relaunch(cfg))
|
|
|
|
ct.Assert.FortioFetch2HeaderEcho(t, svcHTTPClient, s.upstream)
|
|
|
|
}
|