consul/agent/proxycfg/testing_mesh_gateway.go

516 lines
14 KiB
Go
Raw Normal View History

package proxycfg
import (
"math"
"time"
"github.com/mitchellh/go-testing-interface"
"github.com/stretchr/testify/assert"
"github.com/hashicorp/consul/agent/connect"
"github.com/hashicorp/consul/agent/consul/discoverychain"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/proto/pbpeering"
)
func TestConfigSnapshotMeshGateway(t testing.T, variant string, nsFn func(ns *structs.NodeService), extraUpdates []UpdateEvent) *ConfigSnapshot {
roots, leaf := TestCertsForMeshGateway(t)
var (
populateServices = true
useFederationStates = false
deleteCrossDCEntry = false
)
switch variant {
case "default":
case "peered-services":
var (
fooSN = structs.NewServiceName("foo", nil)
barSN = structs.NewServiceName("bar", nil)
girSN = structs.NewServiceName("gir", nil)
fooChain = discoverychain.TestCompileConfigEntries(t, "foo", "default", "default", "dc1", connect.TestClusterID+".consul", nil)
barChain = discoverychain.TestCompileConfigEntries(t, "bar", "default", "default", "dc1", connect.TestClusterID+".consul", nil)
girChain = discoverychain.TestCompileConfigEntries(t, "gir", "default", "default", "dc1", connect.TestClusterID+".consul", nil)
)
assert.True(t, fooChain.Default)
assert.True(t, barChain.Default)
assert.True(t, girChain.Default)
extraUpdates = append(extraUpdates,
UpdateEvent{
CorrelationID: exportedServiceListWatchID,
Result: &structs.IndexedExportedServiceList{
Services: map[string]structs.ServiceList{
"peer-a": []structs.ServiceName{fooSN, barSN},
"peer-b": []structs.ServiceName{girSN},
},
},
},
UpdateEvent{
CorrelationID: "discovery-chain:" + fooSN.String(),
Result: &structs.DiscoveryChainResponse{
Chain: fooChain,
},
},
UpdateEvent{
CorrelationID: "discovery-chain:" + barSN.String(),
Result: &structs.DiscoveryChainResponse{
Chain: barChain,
},
},
UpdateEvent{
CorrelationID: "discovery-chain:" + girSN.String(),
Result: &structs.DiscoveryChainResponse{
Chain: girChain,
},
},
UpdateEvent{
CorrelationID: peeringTrustBundlesWatchID,
Result: TestPeerTrustBundles(t),
},
UpdateEvent{
CorrelationID: leafWatchID,
Result: leaf,
},
)
case "federation-states":
populateServices = true
useFederationStates = true
deleteCrossDCEntry = true
case "newer-info-in-federation-states":
populateServices = true
useFederationStates = true
deleteCrossDCEntry = false
case "older-info-in-federation-states":
populateServices = true
useFederationStates = true
deleteCrossDCEntry = false
case "no-services":
populateServices = false
useFederationStates = false
deleteCrossDCEntry = false
case "service-subsets":
extraUpdates = append(extraUpdates, UpdateEvent{
CorrelationID: serviceResolversWatchID,
Result: &structs.IndexedConfigEntries{
Kind: structs.ServiceResolver,
Entries: []structs.ConfigEntry{
&structs.ServiceResolverConfigEntry{
Kind: structs.ServiceResolver,
Name: "bar",
Subsets: map[string]structs.ServiceResolverSubset{
"v1": {
Filter: "Service.Meta.Version == 1",
},
"v2": {
Filter: "Service.Meta.Version == 2",
OnlyPassing: true,
},
},
},
},
},
})
case "service-subsets2": // TODO(rb): make this merge with 'service-subsets'
extraUpdates = append(extraUpdates, UpdateEvent{
CorrelationID: serviceResolversWatchID,
Result: &structs.IndexedConfigEntries{
Kind: structs.ServiceResolver,
Entries: []structs.ConfigEntry{
&structs.ServiceResolverConfigEntry{
Kind: structs.ServiceResolver,
Name: "bar",
Subsets: map[string]structs.ServiceResolverSubset{
"v1": {
Filter: "Service.Meta.version == 1",
},
"v2": {
Filter: "Service.Meta.version == 2",
OnlyPassing: true,
},
},
},
&structs.ServiceResolverConfigEntry{
Kind: structs.ServiceResolver,
Name: "foo",
Subsets: map[string]structs.ServiceResolverSubset{
"v1": {
Filter: "Service.Meta.version == 1",
},
"v2": {
Filter: "Service.Meta.version == 2",
OnlyPassing: true,
},
},
},
},
},
})
case "default-service-subsets2": // TODO(rb): rename to strip the 2 when the prior is merged with 'service-subsets'
extraUpdates = append(extraUpdates, UpdateEvent{
CorrelationID: serviceResolversWatchID,
Result: &structs.IndexedConfigEntries{
Kind: structs.ServiceResolver,
Entries: []structs.ConfigEntry{
&structs.ServiceResolverConfigEntry{
Kind: structs.ServiceResolver,
Name: "bar",
DefaultSubset: "v2",
Subsets: map[string]structs.ServiceResolverSubset{
"v1": {
Filter: "Service.Meta.version == 1",
},
"v2": {
Filter: "Service.Meta.version == 2",
OnlyPassing: true,
},
},
},
&structs.ServiceResolverConfigEntry{
Kind: structs.ServiceResolver,
Name: "foo",
DefaultSubset: "v2",
Subsets: map[string]structs.ServiceResolverSubset{
"v1": {
Filter: "Service.Meta.version == 1",
},
"v2": {
Filter: "Service.Meta.version == 2",
OnlyPassing: true,
},
},
},
},
},
})
case "ignore-extra-resolvers":
extraUpdates = append(extraUpdates, UpdateEvent{
CorrelationID: serviceResolversWatchID,
Result: &structs.IndexedConfigEntries{
Kind: structs.ServiceResolver,
Entries: []structs.ConfigEntry{
&structs.ServiceResolverConfigEntry{
Kind: structs.ServiceResolver,
Name: "bar",
DefaultSubset: "v2",
Subsets: map[string]structs.ServiceResolverSubset{
"v1": {
Filter: "Service.Meta.Version == 1",
},
"v2": {
Filter: "Service.Meta.Version == 2",
OnlyPassing: true,
},
},
},
&structs.ServiceResolverConfigEntry{
Kind: structs.ServiceResolver,
Name: "notfound",
DefaultSubset: "v2",
Subsets: map[string]structs.ServiceResolverSubset{
"v1": {
Filter: "Service.Meta.Version == 1",
},
"v2": {
Filter: "Service.Meta.Version == 2",
OnlyPassing: true,
},
},
},
},
},
})
case "service-timeouts":
extraUpdates = append(extraUpdates, UpdateEvent{
CorrelationID: serviceResolversWatchID,
Result: &structs.IndexedConfigEntries{
Kind: structs.ServiceResolver,
Entries: []structs.ConfigEntry{
&structs.ServiceResolverConfigEntry{
Kind: structs.ServiceResolver,
Name: "bar",
ConnectTimeout: 10 * time.Second,
Subsets: map[string]structs.ServiceResolverSubset{
"v1": {
Filter: "Service.Meta.Version == 1",
},
"v2": {
Filter: "Service.Meta.Version == 2",
OnlyPassing: true,
},
},
},
},
},
})
case "non-hash-lb-injected":
extraUpdates = append(extraUpdates, UpdateEvent{
CorrelationID: "service-resolvers", // serviceResolversWatchID
Result: &structs.IndexedConfigEntries{
Kind: structs.ServiceResolver,
Entries: []structs.ConfigEntry{
&structs.ServiceResolverConfigEntry{
Kind: structs.ServiceResolver,
Name: "bar",
Subsets: map[string]structs.ServiceResolverSubset{
"v1": {
Filter: "Service.Meta.Version == 1",
},
"v2": {
Filter: "Service.Meta.Version == 2",
OnlyPassing: true,
},
},
LoadBalancer: &structs.LoadBalancer{
Policy: "least_request",
LeastRequestConfig: &structs.LeastRequestConfig{
ChoiceCount: 5,
},
},
},
},
},
})
case "hash-lb-ignored":
extraUpdates = append(extraUpdates, UpdateEvent{
CorrelationID: "service-resolvers", // serviceResolversWatchID
Result: &structs.IndexedConfigEntries{
Kind: structs.ServiceResolver,
Entries: []structs.ConfigEntry{
&structs.ServiceResolverConfigEntry{
Kind: structs.ServiceResolver,
Name: "bar",
Subsets: map[string]structs.ServiceResolverSubset{
"v1": {
Filter: "Service.Meta.Version == 1",
},
"v2": {
Filter: "Service.Meta.Version == 2",
OnlyPassing: true,
},
},
LoadBalancer: &structs.LoadBalancer{
Policy: "ring_hash",
RingHashConfig: &structs.RingHashConfig{
MinimumRingSize: 20,
MaximumRingSize: 50,
},
},
},
},
},
})
default:
t.Fatalf("unknown variant: %s", variant)
return nil
}
baseEvents := []UpdateEvent{
{
CorrelationID: rootsWatchID,
Result: roots,
},
{
CorrelationID: exportedServiceListWatchID,
Result: &structs.IndexedExportedServiceList{
Services: nil,
},
},
{
CorrelationID: serviceListWatchID,
Result: &structs.IndexedServiceList{
Services: nil,
},
},
{
CorrelationID: serviceResolversWatchID,
Result: &structs.IndexedConfigEntries{
Kind: structs.ServiceResolver,
Entries: nil,
},
},
{
CorrelationID: datacentersWatchID,
Result: &[]string{"dc1"},
},
{
CorrelationID: peeringTrustBundlesWatchID,
Result: &pbpeering.TrustBundleListByServiceResponse{
Bundles: nil,
},
},
{
CorrelationID: meshConfigEntryID,
Result: &structs.ConfigEntryResponse{
Entry: nil,
},
},
}
if populateServices || useFederationStates {
baseEvents = testSpliceEvents(baseEvents, []UpdateEvent{
{
CorrelationID: datacentersWatchID,
Result: &[]string{"dc1", "dc2", "dc4", "dc6"},
},
})
}
if populateServices {
var (
foo = structs.NewServiceName("foo", nil)
bar = structs.NewServiceName("bar", nil)
)
baseEvents = testSpliceEvents(baseEvents, []UpdateEvent{
{
CorrelationID: "mesh-gateway:dc2",
Result: &structs.IndexedNodesWithGateways{
Nodes: TestGatewayNodesDC2(t),
},
},
{
CorrelationID: "mesh-gateway:dc4",
Result: &structs.IndexedNodesWithGateways{
Nodes: TestGatewayNodesDC4Hostname(t),
},
},
{
CorrelationID: "mesh-gateway:dc6",
Result: &structs.IndexedNodesWithGateways{
Nodes: TestGatewayNodesDC6Hostname(t),
},
},
{
CorrelationID: serviceListWatchID,
Result: &structs.IndexedServiceList{
Services: []structs.ServiceName{
foo,
bar,
},
},
},
{
CorrelationID: "connect-service:" + foo.String(),
Result: &structs.IndexedCheckServiceNodes{
Nodes: TestGatewayServiceGroupFooDC1(t),
},
},
{
CorrelationID: "connect-service:" + bar.String(),
Result: &structs.IndexedCheckServiceNodes{
Nodes: TestGatewayServiceGroupBarDC1(t),
},
},
{
CorrelationID: serviceResolversWatchID,
Result: &structs.IndexedConfigEntries{
Kind: structs.ServiceResolver,
Entries: []structs.ConfigEntry{
//
},
},
},
})
}
if useFederationStates {
nsFn = testSpliceNodeServiceFunc(nsFn, func(ns *structs.NodeService) {
ns.Meta[structs.MetaWANFederationKey] = "1"
})
if deleteCrossDCEntry {
baseEvents = testSpliceEvents(baseEvents, []UpdateEvent{
{
// Have the cross-dc query mechanism not work for dc2 so
// fedstates will infill.
CorrelationID: "mesh-gateway:dc2",
Result: &structs.IndexedNodesWithGateways{
Nodes: nil,
},
},
})
}
dc2Nodes := TestGatewayNodesDC2(t)
switch variant {
case "newer-info-in-federation-states":
// Create a duplicate entry in FedStateGateways, with a high ModifyIndex, to
// verify that fresh data in the federation state is preferred over stale data
// in GatewayGroups.
svc := structs.TestNodeServiceMeshGatewayWithAddrs(t,
"10.0.1.3", 8443,
structs.ServiceAddress{Address: "10.0.1.3", Port: 8443},
structs.ServiceAddress{Address: "198.18.1.3", Port: 443},
)
svc.RaftIndex.ModifyIndex = math.MaxUint64
dc2Nodes = structs.CheckServiceNodes{
{
Node: dc2Nodes[0].Node,
Service: svc,
},
}
case "older-info-in-federation-states":
// Create a duplicate entry in FedStateGateways, with a low ModifyIndex, to
// verify that stale data in the federation state is ignored in favor of the
// fresher data in GatewayGroups.
svc := structs.TestNodeServiceMeshGatewayWithAddrs(t,
"10.0.1.3", 8443,
structs.ServiceAddress{Address: "10.0.1.3", Port: 8443},
structs.ServiceAddress{Address: "198.18.1.3", Port: 443},
)
svc.RaftIndex.ModifyIndex = 0
dc2Nodes = structs.CheckServiceNodes{
{
Node: dc2Nodes[0].Node,
Service: svc,
},
}
}
baseEvents = testSpliceEvents(baseEvents, []UpdateEvent{
{
CorrelationID: federationStateListGatewaysWatchID,
Result: &structs.DatacenterIndexedCheckServiceNodes{
DatacenterNodes: map[string]structs.CheckServiceNodes{
"dc2": dc2Nodes,
"dc4": TestGatewayNodesDC4Hostname(t),
"dc6": TestGatewayNodesDC6Hostname(t),
},
},
},
{
CorrelationID: consulServerListWatchID,
Result: &structs.IndexedCheckServiceNodes{
Nodes: nil, // TODO
},
},
})
}
return testConfigSnapshotFixture(t, &structs.NodeService{
Kind: structs.ServiceKindMeshGateway,
Service: "mesh-gateway",
Address: "1.2.3.4",
Port: 8443,
Proxy: structs.ConnectProxyConfig{
Config: map[string]interface{}{},
},
Meta: make(map[string]string),
TaggedAddresses: map[string]structs.ServiceAddress{
structs.TaggedAddressLAN: {
Address: "1.2.3.4",
Port: 8443,
},
structs.TaggedAddressWAN: {
Address: "198.18.0.1",
Port: 443,
},
},
}, nsFn, nil, testSpliceEvents(baseEvents, extraUpdates))
}