mirror of https://github.com/hashicorp/consul
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1134 lines
32 KiB
1134 lines
32 KiB
// Copyright (c) HashiCorp, Inc. |
|
// SPDX-License-Identifier: BUSL-1.1 |
|
|
|
package proxycfg |
|
|
|
import ( |
|
"time" |
|
|
|
"github.com/mitchellh/go-testing-interface" |
|
|
|
"github.com/hashicorp/consul/acl" |
|
"github.com/hashicorp/consul/agent/configentry" |
|
"github.com/hashicorp/consul/agent/connect" |
|
"github.com/hashicorp/consul/agent/consul/discoverychain" |
|
"github.com/hashicorp/consul/agent/structs" |
|
"github.com/hashicorp/consul/proto/private/pbpeering" |
|
) |
|
|
|
func setupTestVariationConfigEntriesAndSnapshot( |
|
t testing.T, |
|
variation string, |
|
enterprise bool, |
|
upstreams structs.Upstreams, |
|
additionalEntries ...structs.ConfigEntry, |
|
) []UpdateEvent { |
|
var ( |
|
dbUpstream = upstreams[0] |
|
|
|
dbUID = NewUpstreamID(&dbUpstream) |
|
) |
|
|
|
dbChain := setupTestVariationDiscoveryChain(t, variation, enterprise, dbUID.EnterpriseMeta, additionalEntries...) |
|
|
|
nodes := TestUpstreamNodes(t, "db") |
|
if variation == "register-to-terminating-gateway" { |
|
for _, node := range nodes { |
|
node.Service.Kind = structs.ServiceKindTerminatingGateway |
|
} |
|
} |
|
events := []UpdateEvent{ |
|
{ |
|
CorrelationID: "discovery-chain:" + dbUID.String(), |
|
Result: &structs.DiscoveryChainResponse{ |
|
Chain: dbChain, |
|
}, |
|
}, |
|
{ |
|
CorrelationID: "upstream-target:" + dbChain.ID() + ":" + dbUID.String(), |
|
Result: &structs.IndexedCheckServiceNodes{ |
|
Nodes: nodes, |
|
}, |
|
}, |
|
} |
|
|
|
dbOpts := structs.DiscoveryTargetOpts{ |
|
Service: dbUID.Name, |
|
Namespace: dbUID.NamespaceOrDefault(), |
|
Partition: dbUID.PartitionOrDefault(), |
|
Datacenter: "dc1", |
|
} |
|
dbChainID := structs.ChainID(dbOpts) |
|
makeChainID := func(opts structs.DiscoveryTargetOpts) string { |
|
finalOpts := structs.MergeDiscoveryTargetOpts(dbOpts, opts) |
|
return structs.ChainID(finalOpts) |
|
} |
|
|
|
switch variation { |
|
case "default": |
|
case "simple-with-overrides": |
|
case "simple": |
|
case "external-sni": |
|
case "failover": |
|
chainID := makeChainID(structs.DiscoveryTargetOpts{Service: "fail"}) |
|
events = append(events, UpdateEvent{ |
|
CorrelationID: "upstream-target:" + chainID + ":" + dbUID.String(), |
|
Result: &structs.IndexedCheckServiceNodes{ |
|
Nodes: TestUpstreamNodesAlternate(t), |
|
}, |
|
}) |
|
case "failover-through-remote-gateway-triggered": |
|
events = append(events, UpdateEvent{ |
|
CorrelationID: "upstream-target:" + dbChainID + ":" + dbUID.String(), |
|
Result: &structs.IndexedCheckServiceNodes{ |
|
Nodes: TestUpstreamNodesInStatus(t, "critical"), |
|
}, |
|
}) |
|
fallthrough |
|
case "failover-through-remote-gateway": |
|
chainID := makeChainID(structs.DiscoveryTargetOpts{Datacenter: "dc2"}) |
|
events = append(events, UpdateEvent{ |
|
CorrelationID: "upstream-target:" + chainID + ":" + dbUID.String(), |
|
Result: &structs.IndexedCheckServiceNodes{ |
|
Nodes: TestUpstreamNodesDC2(t), |
|
}, |
|
}) |
|
events = append(events, UpdateEvent{ |
|
CorrelationID: "mesh-gateway:dc2:" + dbUID.String(), |
|
Result: &structs.IndexedCheckServiceNodes{ |
|
Nodes: TestGatewayNodesDC2(t), |
|
}, |
|
}) |
|
case "failover-to-cluster-peer": |
|
uid := UpstreamID{ |
|
Name: "db", |
|
Peer: "cluster-01", |
|
EnterpriseMeta: acl.NewEnterpriseMetaWithPartition(dbUID.PartitionOrDefault(), ""), |
|
} |
|
events = append(events, UpdateEvent{ |
|
CorrelationID: "peer-trust-bundle:cluster-01", |
|
Result: &pbpeering.TrustBundleReadResponse{ |
|
Bundle: &pbpeering.PeeringTrustBundle{ |
|
PeerName: "peer1", |
|
TrustDomain: "peer1.domain", |
|
ExportedPartition: "peer1ap", |
|
RootPEMs: []string{"peer1-root-1"}, |
|
}, |
|
}, |
|
}) |
|
if enterprise { |
|
uid.EnterpriseMeta = acl.NewEnterpriseMetaWithPartition(dbUID.PartitionOrDefault(), "ns9") |
|
} |
|
events = append(events, UpdateEvent{ |
|
CorrelationID: "upstream-peer:" + uid.String(), |
|
Result: &structs.IndexedCheckServiceNodes{ |
|
Nodes: structs.CheckServiceNodes{structs.TestCheckNodeServiceWithNameInPeer(t, "db", "dc2", "cluster-01", "10.40.1.1", false, uid.EnterpriseMeta)}, |
|
}, |
|
}) |
|
case "redirect-to-cluster-peer": |
|
events = append(events, UpdateEvent{ |
|
CorrelationID: "peer-trust-bundle:cluster-01", |
|
Result: &pbpeering.TrustBundleReadResponse{ |
|
Bundle: &pbpeering.PeeringTrustBundle{ |
|
PeerName: "peer1", |
|
TrustDomain: "peer1.domain", |
|
ExportedPartition: "peer1ap", |
|
RootPEMs: []string{"peer1-root-1"}, |
|
}, |
|
}, |
|
}) |
|
uid := UpstreamID{ |
|
Name: "db", |
|
Peer: "cluster-01", |
|
} |
|
if enterprise { |
|
uid.EnterpriseMeta = acl.NewEnterpriseMetaWithPartition(dbUID.PartitionOrDefault(), "ns9") |
|
} |
|
events = append(events, UpdateEvent{ |
|
CorrelationID: "upstream-peer:" + uid.String(), |
|
Result: &structs.IndexedCheckServiceNodes{ |
|
Nodes: structs.CheckServiceNodes{structs.TestCheckNodeServiceWithNameInPeer(t, "db", "dc2", "cluster-01", "10.40.1.1", false, uid.EnterpriseMeta)}, |
|
}, |
|
}) |
|
case "failover-through-double-remote-gateway-triggered": |
|
chainID := makeChainID(structs.DiscoveryTargetOpts{Datacenter: "dc2"}) |
|
events = append(events, UpdateEvent{ |
|
CorrelationID: "upstream-target:" + dbChainID + ":" + dbUID.String(), |
|
Result: &structs.IndexedCheckServiceNodes{ |
|
Nodes: TestUpstreamNodesInStatus(t, "critical"), |
|
}, |
|
}, |
|
UpdateEvent{ |
|
CorrelationID: "upstream-target:" + chainID + ":" + dbUID.String(), |
|
Result: &structs.IndexedCheckServiceNodes{ |
|
Nodes: TestUpstreamNodesInStatusDC2(t, "critical"), |
|
}, |
|
}) |
|
fallthrough |
|
case "failover-through-double-remote-gateway": |
|
chainID := makeChainID(structs.DiscoveryTargetOpts{Datacenter: "dc3"}) |
|
events = append(events, UpdateEvent{ |
|
CorrelationID: "upstream-target:" + chainID + ":" + dbUID.String(), |
|
Result: &structs.IndexedCheckServiceNodes{ |
|
Nodes: TestUpstreamNodesDC2(t), |
|
}, |
|
}, |
|
UpdateEvent{ |
|
CorrelationID: "mesh-gateway:dc2:" + dbUID.String(), |
|
Result: &structs.IndexedCheckServiceNodes{ |
|
Nodes: TestGatewayNodesDC2(t), |
|
}, |
|
}, |
|
UpdateEvent{ |
|
CorrelationID: "mesh-gateway:dc3:" + dbUID.String(), |
|
Result: &structs.IndexedCheckServiceNodes{ |
|
Nodes: TestGatewayNodesDC3(t), |
|
}, |
|
}) |
|
case "failover-through-local-gateway-triggered": |
|
events = append(events, UpdateEvent{ |
|
CorrelationID: "upstream-target:" + dbChainID + ":" + dbUID.String(), |
|
Result: &structs.IndexedCheckServiceNodes{ |
|
Nodes: TestUpstreamNodesInStatus(t, "critical"), |
|
}, |
|
}) |
|
fallthrough |
|
case "failover-through-local-gateway": |
|
chainID := makeChainID(structs.DiscoveryTargetOpts{Datacenter: "dc2"}) |
|
events = append(events, UpdateEvent{ |
|
CorrelationID: "upstream-target:" + chainID + ":" + dbUID.String(), |
|
Result: &structs.IndexedCheckServiceNodes{ |
|
Nodes: TestUpstreamNodesDC2(t), |
|
}, |
|
}, |
|
UpdateEvent{ |
|
CorrelationID: "mesh-gateway:dc1:" + dbUID.String(), |
|
Result: &structs.IndexedCheckServiceNodes{ |
|
Nodes: TestGatewayNodesDC1(t), |
|
}, |
|
}) |
|
case "failover-through-double-local-gateway-triggered": |
|
db2ChainID := makeChainID(structs.DiscoveryTargetOpts{Datacenter: "dc2"}) |
|
events = append(events, UpdateEvent{ |
|
CorrelationID: "upstream-target:" + dbChainID + ":" + dbUID.String(), |
|
Result: &structs.IndexedCheckServiceNodes{ |
|
Nodes: TestUpstreamNodesInStatus(t, "critical"), |
|
}, |
|
}, |
|
UpdateEvent{ |
|
CorrelationID: "upstream-target:" + db2ChainID + ":" + dbUID.String(), |
|
Result: &structs.IndexedCheckServiceNodes{ |
|
Nodes: TestUpstreamNodesInStatusDC2(t, "critical"), |
|
}, |
|
}) |
|
fallthrough |
|
case "failover-through-double-local-gateway": |
|
db3ChainID := makeChainID(structs.DiscoveryTargetOpts{Datacenter: "dc3"}) |
|
events = append(events, UpdateEvent{ |
|
CorrelationID: "upstream-target:" + db3ChainID + ":" + dbUID.String(), |
|
Result: &structs.IndexedCheckServiceNodes{ |
|
Nodes: TestUpstreamNodesDC2(t), |
|
}, |
|
}) |
|
events = append(events, UpdateEvent{ |
|
CorrelationID: "mesh-gateway:dc1:" + dbUID.String(), |
|
Result: &structs.IndexedCheckServiceNodes{ |
|
Nodes: TestGatewayNodesDC1(t), |
|
}, |
|
}) |
|
case "splitter-with-resolver-redirect-multidc": |
|
v1ChainID := makeChainID(structs.DiscoveryTargetOpts{ServiceSubset: "v1"}) |
|
v2ChainID := makeChainID(structs.DiscoveryTargetOpts{ServiceSubset: "v2", Datacenter: "dc2"}) |
|
events = append(events, UpdateEvent{ |
|
CorrelationID: "upstream-target:" + v1ChainID + ":" + dbUID.String(), |
|
Result: &structs.IndexedCheckServiceNodes{ |
|
Nodes: TestUpstreamNodes(t, "db"), |
|
}, |
|
}) |
|
events = append(events, UpdateEvent{ |
|
CorrelationID: "upstream-target:" + v2ChainID + ":" + dbUID.String(), |
|
Result: &structs.IndexedCheckServiceNodes{ |
|
Nodes: TestUpstreamNodesDC2(t), |
|
}, |
|
}) |
|
case "chain-and-splitter": |
|
case "grpc-router": |
|
case "chain-and-router": |
|
case "lb-resolver": |
|
case "register-to-terminating-gateway": |
|
case "redirect-to-lb-node": |
|
case "resolver-with-lb": |
|
case "splitter-overweight": |
|
default: |
|
extraEvents := extraUpdateEvents(t, variation, dbUID) |
|
events = append(events, extraEvents...) |
|
} |
|
|
|
return events |
|
} |
|
|
|
func setupTestVariationDiscoveryChain( |
|
t testing.T, |
|
variation string, |
|
enterprise bool, |
|
entMeta acl.EnterpriseMeta, |
|
additionalEntries ...structs.ConfigEntry, |
|
) *structs.CompiledDiscoveryChain { |
|
// Compile a chain. |
|
var ( |
|
peers []*pbpeering.Peering |
|
entries []structs.ConfigEntry |
|
compileSetup func(req *discoverychain.CompileRequest) |
|
) |
|
|
|
switch variation { |
|
case "default": |
|
// no config entries |
|
case "register-to-terminating-gateway": |
|
case "simple-with-overrides": |
|
compileSetup = func(req *discoverychain.CompileRequest) { |
|
req.OverrideMeshGateway.Mode = structs.MeshGatewayModeLocal |
|
req.OverrideProtocol = "grpc" |
|
req.OverrideConnectTimeout = 66 * time.Second |
|
} |
|
fallthrough |
|
case "simple": |
|
entries = append(entries, |
|
&structs.ServiceResolverConfigEntry{ |
|
Kind: structs.ServiceResolver, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
ConnectTimeout: 33 * time.Second, |
|
RequestTimeout: 33 * time.Second, |
|
}, |
|
) |
|
case "external-sni": |
|
entries = append(entries, |
|
&structs.ServiceConfigEntry{ |
|
Kind: structs.ServiceDefaults, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
ExternalSNI: "db.some.other.service.mesh", |
|
}, |
|
&structs.ServiceResolverConfigEntry{ |
|
Kind: structs.ServiceResolver, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
ConnectTimeout: 33 * time.Second, |
|
RequestTimeout: 33 * time.Second, |
|
}, |
|
) |
|
case "failover": |
|
entries = append(entries, |
|
&structs.ServiceResolverConfigEntry{ |
|
Kind: structs.ServiceResolver, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
ConnectTimeout: 33 * time.Second, |
|
RequestTimeout: 33 * time.Second, |
|
Failover: map[string]structs.ServiceResolverFailover{ |
|
"*": { |
|
Service: "fail", |
|
}, |
|
}, |
|
}, |
|
) |
|
case "failover-through-remote-gateway-triggered": |
|
fallthrough |
|
case "failover-through-remote-gateway": |
|
entries = append(entries, |
|
&structs.ServiceConfigEntry{ |
|
Kind: structs.ServiceDefaults, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
MeshGateway: structs.MeshGatewayConfig{ |
|
Mode: structs.MeshGatewayModeRemote, |
|
}, |
|
}, |
|
&structs.ServiceResolverConfigEntry{ |
|
Kind: structs.ServiceResolver, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
ConnectTimeout: 33 * time.Second, |
|
RequestTimeout: 33 * time.Second, |
|
Failover: map[string]structs.ServiceResolverFailover{ |
|
"*": { |
|
Datacenters: []string{"dc2"}, |
|
}, |
|
}, |
|
}, |
|
) |
|
case "failover-to-cluster-peer": |
|
target := structs.ServiceResolverFailoverTarget{ |
|
Peer: "cluster-01", |
|
} |
|
|
|
if enterprise { |
|
target.Namespace = "ns9" |
|
} |
|
|
|
entries = append(entries, |
|
&structs.ServiceResolverConfigEntry{ |
|
Kind: structs.ServiceResolver, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
ConnectTimeout: 33 * time.Second, |
|
RequestTimeout: 33 * time.Second, |
|
Failover: map[string]structs.ServiceResolverFailover{ |
|
"*": { |
|
Targets: []structs.ServiceResolverFailoverTarget{target}, |
|
}, |
|
}, |
|
}, |
|
) |
|
case "redirect-to-cluster-peer": |
|
redirect := &structs.ServiceResolverRedirect{ |
|
Peer: "cluster-01", |
|
} |
|
if enterprise { |
|
redirect.Namespace = "ns9" |
|
} |
|
|
|
entries = append(entries, |
|
&structs.ServiceResolverConfigEntry{ |
|
Kind: structs.ServiceResolver, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
ConnectTimeout: 33 * time.Second, |
|
RequestTimeout: 33 * time.Second, |
|
Redirect: redirect, |
|
}, |
|
) |
|
case "failover-through-double-remote-gateway-triggered": |
|
fallthrough |
|
case "failover-through-double-remote-gateway": |
|
entries = append(entries, |
|
&structs.ServiceConfigEntry{ |
|
Kind: structs.ServiceDefaults, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
MeshGateway: structs.MeshGatewayConfig{ |
|
Mode: structs.MeshGatewayModeRemote, |
|
}, |
|
}, |
|
&structs.ServiceResolverConfigEntry{ |
|
Kind: structs.ServiceResolver, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
ConnectTimeout: 33 * time.Second, |
|
RequestTimeout: 33 * time.Second, |
|
Failover: map[string]structs.ServiceResolverFailover{ |
|
"*": { |
|
Datacenters: []string{"dc2", "dc3"}, |
|
}, |
|
}, |
|
}, |
|
) |
|
case "failover-through-local-gateway-triggered": |
|
fallthrough |
|
case "failover-through-local-gateway": |
|
entries = append(entries, |
|
&structs.ServiceConfigEntry{ |
|
Kind: structs.ServiceDefaults, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
MeshGateway: structs.MeshGatewayConfig{ |
|
Mode: structs.MeshGatewayModeLocal, |
|
}, |
|
}, |
|
&structs.ServiceResolverConfigEntry{ |
|
Kind: structs.ServiceResolver, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
ConnectTimeout: 33 * time.Second, |
|
RequestTimeout: 33 * time.Second, |
|
Failover: map[string]structs.ServiceResolverFailover{ |
|
"*": { |
|
Datacenters: []string{"dc2"}, |
|
}, |
|
}, |
|
}, |
|
) |
|
case "failover-through-double-local-gateway-triggered": |
|
fallthrough |
|
case "failover-through-double-local-gateway": |
|
entries = append(entries, |
|
&structs.ServiceConfigEntry{ |
|
Kind: structs.ServiceDefaults, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
MeshGateway: structs.MeshGatewayConfig{ |
|
Mode: structs.MeshGatewayModeLocal, |
|
}, |
|
}, |
|
&structs.ServiceResolverConfigEntry{ |
|
Kind: structs.ServiceResolver, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
ConnectTimeout: 33 * time.Second, |
|
RequestTimeout: 33 * time.Second, |
|
Failover: map[string]structs.ServiceResolverFailover{ |
|
"*": { |
|
Datacenters: []string{"dc2", "dc3"}, |
|
}, |
|
}, |
|
}, |
|
) |
|
case "splitter-with-resolver-redirect-multidc": |
|
em := acl.NewEnterpriseMetaWithPartition(entMeta.PartitionOrDefault(), acl.NamespaceOrDefault("")) |
|
entries = append(entries, |
|
&structs.ProxyConfigEntry{ |
|
Kind: structs.ProxyDefaults, |
|
Name: structs.ProxyConfigGlobal, |
|
EnterpriseMeta: em, |
|
Protocol: "http", |
|
Config: map[string]interface{}{ |
|
"protocol": "http", |
|
}, |
|
}, |
|
&structs.ServiceSplitterConfigEntry{ |
|
Kind: structs.ServiceResolver, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
Splits: []structs.ServiceSplit{ |
|
{Weight: 50, Service: "db-dc1"}, |
|
{Weight: 50, Service: "db-dc2"}, |
|
}, |
|
}, |
|
&structs.ServiceResolverConfigEntry{ |
|
Kind: structs.ServiceResolver, |
|
Name: "db-dc1", |
|
EnterpriseMeta: entMeta, |
|
Redirect: &structs.ServiceResolverRedirect{ |
|
Service: "db", |
|
ServiceSubset: "v1", |
|
Datacenter: "dc1", |
|
}, |
|
}, |
|
&structs.ServiceResolverConfigEntry{ |
|
Kind: structs.ServiceResolver, |
|
Name: "db-dc2", |
|
EnterpriseMeta: entMeta, |
|
Redirect: &structs.ServiceResolverRedirect{ |
|
Service: "db", |
|
ServiceSubset: "v2", |
|
Datacenter: "dc2", |
|
}, |
|
}, |
|
&structs.ServiceResolverConfigEntry{ |
|
Kind: structs.ServiceResolver, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
Subsets: map[string]structs.ServiceResolverSubset{ |
|
"v1": { |
|
Filter: "Service.Meta.version == v1", |
|
}, |
|
"v2": { |
|
Filter: "Service.Meta.version == v2", |
|
}, |
|
}, |
|
}, |
|
) |
|
case "chain-and-splitter": |
|
entries = append(entries, |
|
&structs.ServiceResolverConfigEntry{ |
|
Kind: structs.ServiceResolver, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
ConnectTimeout: 25 * time.Second, |
|
}, |
|
&structs.ProxyConfigEntry{ |
|
Kind: structs.ProxyDefaults, |
|
Name: structs.ProxyConfigGlobal, |
|
EnterpriseMeta: entMeta, |
|
Protocol: "http", |
|
Config: map[string]interface{}{ |
|
"protocol": "http", |
|
}, |
|
}, |
|
// Adding a ServiceRouter in this case allows testing ServiceRoute.Destination timeouts. |
|
&structs.ServiceRouterConfigEntry{ |
|
Kind: structs.ServiceRouter, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
Routes: []structs.ServiceRoute{ |
|
{ |
|
Match: &structs.ServiceRouteMatch{ |
|
HTTP: &structs.ServiceRouteHTTPMatch{ |
|
PathPrefix: "/big-side", |
|
}, |
|
}, |
|
Destination: &structs.ServiceRouteDestination{ |
|
Service: "big-side", |
|
// Test disabling idle timeout. |
|
IdleTimeout: -1 * time.Second, |
|
// Test a positive value for request timeout. |
|
RequestTimeout: 10 * time.Second, |
|
}, |
|
}, |
|
{ |
|
Match: &structs.ServiceRouteMatch{ |
|
HTTP: &structs.ServiceRouteHTTPMatch{ |
|
PathPrefix: "/lil-bit-side", |
|
}, |
|
}, |
|
Destination: &structs.ServiceRouteDestination{ |
|
Service: "lil-bit-side", |
|
// Test zero values for these timeouts. |
|
IdleTimeout: 0 * time.Second, |
|
RequestTimeout: 0 * time.Second, |
|
}, |
|
}, |
|
}, |
|
}, |
|
&structs.ServiceSplitterConfigEntry{ |
|
Kind: structs.ServiceSplitter, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
Splits: []structs.ServiceSplit{ |
|
{ |
|
Weight: 1, |
|
Service: "db", |
|
RequestHeaders: &structs.HTTPHeaderModifiers{ |
|
Set: map[string]string{"x-split-leg": "db"}, |
|
}, |
|
ResponseHeaders: &structs.HTTPHeaderModifiers{ |
|
Set: map[string]string{"x-split-leg": "db"}, |
|
}, |
|
}, |
|
{ |
|
Weight: 95.5, |
|
Service: "big-side", |
|
RequestHeaders: &structs.HTTPHeaderModifiers{ |
|
Set: map[string]string{"x-split-leg": "big"}, |
|
}, |
|
ResponseHeaders: &structs.HTTPHeaderModifiers{ |
|
Set: map[string]string{"x-split-leg": "big"}, |
|
}, |
|
}, |
|
{ |
|
Weight: 3, |
|
Service: "goldilocks-side", |
|
RequestHeaders: &structs.HTTPHeaderModifiers{ |
|
Set: map[string]string{"x-split-leg": "goldilocks"}, |
|
}, |
|
ResponseHeaders: &structs.HTTPHeaderModifiers{ |
|
Set: map[string]string{"x-split-leg": "goldilocks"}, |
|
}, |
|
}, |
|
{ |
|
Weight: 0.5, |
|
Service: "lil-bit-side", |
|
RequestHeaders: &structs.HTTPHeaderModifiers{ |
|
Set: map[string]string{"x-split-leg": "small"}, |
|
}, |
|
ResponseHeaders: &structs.HTTPHeaderModifiers{ |
|
Set: map[string]string{"x-split-leg": "small"}, |
|
}, |
|
}, |
|
}, |
|
}, |
|
) |
|
case "splitter-overweight": |
|
entries = append(entries, |
|
&structs.ServiceResolverConfigEntry{ |
|
Kind: structs.ServiceResolver, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
ConnectTimeout: 33 * time.Second, |
|
RequestTimeout: 33 * time.Second, |
|
}, |
|
&structs.ProxyConfigEntry{ |
|
Kind: structs.ProxyDefaults, |
|
Name: structs.ProxyConfigGlobal, |
|
EnterpriseMeta: entMeta, |
|
Protocol: "http", |
|
Config: map[string]interface{}{ |
|
"protocol": "http", |
|
}, |
|
}, |
|
&structs.ServiceSplitterConfigEntry{ |
|
Kind: structs.ServiceSplitter, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
Splits: []structs.ServiceSplit{ |
|
{ |
|
Weight: 100.0, |
|
Service: "big-side", |
|
RequestHeaders: &structs.HTTPHeaderModifiers{ |
|
Set: map[string]string{"x-split-leg": "big"}, |
|
}, |
|
ResponseHeaders: &structs.HTTPHeaderModifiers{ |
|
Set: map[string]string{"x-split-leg": "big"}, |
|
}, |
|
}, |
|
{ |
|
Weight: 100.0, |
|
Service: "goldilocks-side", |
|
RequestHeaders: &structs.HTTPHeaderModifiers{ |
|
Set: map[string]string{"x-split-leg": "goldilocks"}, |
|
}, |
|
ResponseHeaders: &structs.HTTPHeaderModifiers{ |
|
Set: map[string]string{"x-split-leg": "goldilocks"}, |
|
}, |
|
}, |
|
{ |
|
Weight: 100.0, |
|
Service: "lil-bit-side", |
|
RequestHeaders: &structs.HTTPHeaderModifiers{ |
|
Set: map[string]string{"x-split-leg": "small"}, |
|
}, |
|
ResponseHeaders: &structs.HTTPHeaderModifiers{ |
|
Set: map[string]string{"x-split-leg": "small"}, |
|
}, |
|
}, |
|
}, |
|
}, |
|
) |
|
case "grpc-router": |
|
entries = append(entries, |
|
&structs.ServiceResolverConfigEntry{ |
|
Kind: structs.ServiceResolver, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
ConnectTimeout: 33 * time.Second, |
|
RequestTimeout: 33 * time.Second, |
|
}, |
|
&structs.ProxyConfigEntry{ |
|
Kind: structs.ProxyDefaults, |
|
Name: structs.ProxyConfigGlobal, |
|
EnterpriseMeta: entMeta, |
|
Protocol: "grpc", |
|
Config: map[string]interface{}{ |
|
"protocol": "grpc", |
|
}, |
|
}, |
|
&structs.ServiceRouterConfigEntry{ |
|
Kind: structs.ServiceRouter, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
Routes: []structs.ServiceRoute{ |
|
{ |
|
Match: &structs.ServiceRouteMatch{ |
|
HTTP: &structs.ServiceRouteHTTPMatch{ |
|
PathExact: "/fgrpc.PingServer/Ping", |
|
}, |
|
}, |
|
Destination: &structs.ServiceRouteDestination{ |
|
Service: "prefix", |
|
}, |
|
}, |
|
}, |
|
}, |
|
) |
|
case "chain-and-router": |
|
entries = append(entries, |
|
&structs.ServiceResolverConfigEntry{ |
|
Kind: structs.ServiceResolver, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
ConnectTimeout: 33 * time.Second, |
|
RequestTimeout: 33 * time.Second, |
|
}, |
|
&structs.ProxyConfigEntry{ |
|
Kind: structs.ProxyDefaults, |
|
Name: structs.ProxyConfigGlobal, |
|
EnterpriseMeta: entMeta, |
|
Protocol: "http", |
|
Config: map[string]interface{}{ |
|
"protocol": "http", |
|
}, |
|
}, |
|
&structs.ServiceSplitterConfigEntry{ |
|
Kind: structs.ServiceSplitter, |
|
Name: "split-3-ways", |
|
EnterpriseMeta: entMeta, |
|
Splits: []structs.ServiceSplit{ |
|
{Weight: 95.5, Service: "big-side"}, |
|
{Weight: 4, Service: "goldilocks-side"}, |
|
{Weight: 0.5, Service: "lil-bit-side"}, |
|
}, |
|
}, |
|
&structs.ServiceRouterConfigEntry{ |
|
Kind: structs.ServiceRouter, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
Routes: []structs.ServiceRoute{ |
|
{ |
|
Match: httpMatch(&structs.ServiceRouteHTTPMatch{ |
|
PathPrefix: "/prefix", |
|
}), |
|
Destination: toService("prefix"), |
|
}, |
|
{ |
|
Match: httpMatch(&structs.ServiceRouteHTTPMatch{ |
|
PathExact: "/exact", |
|
}), |
|
Destination: toService("exact"), |
|
}, |
|
{ |
|
Match: httpMatch(&structs.ServiceRouteHTTPMatch{ |
|
PathRegex: "/regex", |
|
}), |
|
Destination: toService("regex"), |
|
}, |
|
{ |
|
Match: httpMatchHeader(structs.ServiceRouteHTTPMatchHeader{ |
|
Name: "x-debug", |
|
Present: true, |
|
}), |
|
Destination: toService("hdr-present"), |
|
}, |
|
{ |
|
Match: httpMatchHeader(structs.ServiceRouteHTTPMatchHeader{ |
|
Name: "x-debug", |
|
Present: true, |
|
Invert: true, |
|
}), |
|
Destination: toService("hdr-not-present"), |
|
}, |
|
{ |
|
Match: httpMatchHeader(structs.ServiceRouteHTTPMatchHeader{ |
|
Name: "x-debug", |
|
Exact: "exact", |
|
}), |
|
Destination: toService("hdr-exact"), |
|
}, |
|
{ |
|
Match: httpMatchHeader(structs.ServiceRouteHTTPMatchHeader{ |
|
Name: "x-debug", |
|
Prefix: "prefix", |
|
}), |
|
Destination: toService("hdr-prefix"), |
|
}, |
|
{ |
|
Match: httpMatchHeader(structs.ServiceRouteHTTPMatchHeader{ |
|
Name: "x-debug", |
|
Suffix: "suffix", |
|
}), |
|
Destination: toService("hdr-suffix"), |
|
}, |
|
{ |
|
Match: httpMatchHeader(structs.ServiceRouteHTTPMatchHeader{ |
|
Name: "x-debug", |
|
Regex: "regex", |
|
}), |
|
Destination: toService("hdr-regex"), |
|
}, |
|
{ |
|
Match: httpMatch(&structs.ServiceRouteHTTPMatch{ |
|
Methods: []string{"GET", "PUT"}, |
|
}), |
|
Destination: toService("just-methods"), |
|
}, |
|
{ |
|
Match: httpMatch(&structs.ServiceRouteHTTPMatch{ |
|
Header: []structs.ServiceRouteHTTPMatchHeader{ |
|
{ |
|
Name: "x-debug", |
|
Exact: "exact", |
|
}, |
|
}, |
|
Methods: []string{"GET", "PUT"}, |
|
}), |
|
Destination: toService("hdr-exact-with-method"), |
|
}, |
|
{ |
|
Match: httpMatchParam(structs.ServiceRouteHTTPMatchQueryParam{ |
|
Name: "secretparam1", |
|
Exact: "exact", |
|
}), |
|
Destination: toService("prm-exact"), |
|
}, |
|
{ |
|
Match: httpMatchParam(structs.ServiceRouteHTTPMatchQueryParam{ |
|
Name: "secretparam2", |
|
Regex: "regex", |
|
}), |
|
Destination: toService("prm-regex"), |
|
}, |
|
{ |
|
Match: httpMatchParam(structs.ServiceRouteHTTPMatchQueryParam{ |
|
Name: "secretparam3", |
|
Present: true, |
|
}), |
|
Destination: toService("prm-present"), |
|
}, |
|
{ |
|
Match: nil, |
|
Destination: toService("nil-match"), |
|
}, |
|
{ |
|
Match: &structs.ServiceRouteMatch{}, |
|
Destination: toService("empty-match-1"), |
|
}, |
|
{ |
|
Match: &structs.ServiceRouteMatch{ |
|
HTTP: &structs.ServiceRouteHTTPMatch{}, |
|
}, |
|
Destination: toService("empty-match-2"), |
|
}, |
|
{ |
|
Match: httpMatch(&structs.ServiceRouteHTTPMatch{ |
|
PathPrefix: "/prefix", |
|
}), |
|
Destination: &structs.ServiceRouteDestination{ |
|
Service: "prefix-rewrite-1", |
|
PrefixRewrite: "/", |
|
}, |
|
}, |
|
{ |
|
Match: httpMatch(&structs.ServiceRouteHTTPMatch{ |
|
PathPrefix: "/prefix", |
|
}), |
|
Destination: &structs.ServiceRouteDestination{ |
|
Service: "prefix-rewrite-2", |
|
PrefixRewrite: "/nested/newlocation", |
|
}, |
|
}, |
|
{ |
|
Match: httpMatch(&structs.ServiceRouteHTTPMatch{ |
|
PathPrefix: "/timeout", |
|
}), |
|
Destination: &structs.ServiceRouteDestination{ |
|
Service: "req-timeout", |
|
RequestTimeout: 33 * time.Second, |
|
}, |
|
}, |
|
{ |
|
Match: httpMatch(&structs.ServiceRouteHTTPMatch{ |
|
PathPrefix: "/idle-timeout", |
|
}), |
|
Destination: &structs.ServiceRouteDestination{ |
|
Service: "idle-timeout", |
|
IdleTimeout: 33 * time.Second, |
|
}, |
|
}, |
|
{ |
|
Match: httpMatch(&structs.ServiceRouteHTTPMatch{ |
|
PathPrefix: "/retry-connect", |
|
}), |
|
Destination: &structs.ServiceRouteDestination{ |
|
Service: "retry-connect", |
|
NumRetries: 15, |
|
RetryOnConnectFailure: true, |
|
}, |
|
}, |
|
{ |
|
Match: httpMatch(&structs.ServiceRouteHTTPMatch{ |
|
PathPrefix: "/retry-reset", |
|
}), |
|
Destination: &structs.ServiceRouteDestination{ |
|
Service: "retry-reset", |
|
NumRetries: 15, |
|
RetryOn: []string{"reset"}, |
|
}, |
|
}, |
|
{ |
|
Match: httpMatch(&structs.ServiceRouteHTTPMatch{ |
|
PathPrefix: "/retry-codes", |
|
}), |
|
Destination: &structs.ServiceRouteDestination{ |
|
Service: "retry-codes", |
|
NumRetries: 15, |
|
RetryOnStatusCodes: []uint32{401, 409, 451}, |
|
}, |
|
}, |
|
{ |
|
Match: httpMatch(&structs.ServiceRouteHTTPMatch{ |
|
PathPrefix: "/retry-all", |
|
}), |
|
Destination: &structs.ServiceRouteDestination{ |
|
Service: "retry-all", |
|
RetryOnConnectFailure: true, |
|
RetryOn: []string{"5xx", "gateway-error", "reset", "connect-failure", "envoy-ratelimited", "retriable-4xx", "refused-stream", "cancelled", "deadline-exceeded", "internal", "resource-exhausted", "unavailable"}, |
|
RetryOnStatusCodes: []uint32{401, 409, 451}, |
|
}, |
|
}, |
|
{ |
|
Match: httpMatch(&structs.ServiceRouteHTTPMatch{ |
|
PathPrefix: "/split-3-ways", |
|
}), |
|
Destination: toService("split-3-ways"), |
|
}, |
|
{ |
|
Match: httpMatch(&structs.ServiceRouteHTTPMatch{ |
|
PathExact: "/header-manip", |
|
}), |
|
Destination: &structs.ServiceRouteDestination{ |
|
Service: "header-manip", |
|
RequestHeaders: &structs.HTTPHeaderModifiers{ |
|
Add: map[string]string{ |
|
"request": "bar", |
|
}, |
|
Set: map[string]string{ |
|
"bar": "baz", |
|
}, |
|
Remove: []string{"qux"}, |
|
}, |
|
ResponseHeaders: &structs.HTTPHeaderModifiers{ |
|
Add: map[string]string{ |
|
"response": "bar", |
|
}, |
|
Set: map[string]string{ |
|
"bar": "baz", |
|
}, |
|
Remove: []string{"qux"}, |
|
}, |
|
}, |
|
}, |
|
}, |
|
}, |
|
) |
|
case "lb-resolver": |
|
entries = append(entries, |
|
&structs.ProxyConfigEntry{ |
|
Kind: structs.ProxyDefaults, |
|
Name: structs.ProxyConfigGlobal, |
|
EnterpriseMeta: entMeta, |
|
Protocol: "http", |
|
Config: map[string]interface{}{ |
|
"protocol": "http", |
|
}, |
|
}, |
|
&structs.ServiceSplitterConfigEntry{ |
|
Kind: structs.ServiceSplitter, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
Splits: []structs.ServiceSplit{ |
|
{Weight: 95.5, Service: "something-else"}, |
|
{Weight: 4.5, Service: "db"}, |
|
}, |
|
}, |
|
&structs.ServiceResolverConfigEntry{ |
|
Kind: structs.ServiceResolver, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
LoadBalancer: &structs.LoadBalancer{ |
|
Policy: "ring_hash", |
|
RingHashConfig: &structs.RingHashConfig{ |
|
MinimumRingSize: 20, |
|
MaximumRingSize: 30, |
|
}, |
|
HashPolicies: []structs.HashPolicy{ |
|
{ |
|
Field: "cookie", |
|
FieldValue: "chocolate-chip", |
|
Terminal: true, |
|
}, |
|
{ |
|
Field: "cookie", |
|
FieldValue: "chocolate-chip", |
|
CookieConfig: &structs.CookieConfig{Session: true}, |
|
}, |
|
{ |
|
Field: "header", |
|
FieldValue: "x-user-id", |
|
}, |
|
{ |
|
Field: "query_parameter", |
|
FieldValue: "my-pretty-param", |
|
}, |
|
{ |
|
SourceIP: true, |
|
Terminal: true, |
|
}, |
|
}, |
|
}, |
|
}) |
|
case "redirect-to-lb-node": |
|
entries = append(entries, |
|
&structs.ProxyConfigEntry{ |
|
Kind: structs.ProxyDefaults, |
|
Name: structs.ProxyConfigGlobal, |
|
EnterpriseMeta: entMeta, |
|
Protocol: "http", |
|
Config: map[string]interface{}{ |
|
"protocol": "http", |
|
}, |
|
}, |
|
&structs.ServiceRouterConfigEntry{ |
|
Kind: structs.ServiceRouter, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
Routes: []structs.ServiceRoute{ |
|
{ |
|
Match: httpMatch(&structs.ServiceRouteHTTPMatch{ |
|
PathPrefix: "/web", |
|
}), |
|
Destination: toService("web"), |
|
}, |
|
}, |
|
}, |
|
&structs.ServiceResolverConfigEntry{ |
|
Kind: structs.ServiceResolver, |
|
Name: "web", |
|
EnterpriseMeta: entMeta, |
|
LoadBalancer: &structs.LoadBalancer{ |
|
Policy: "ring_hash", |
|
RingHashConfig: &structs.RingHashConfig{ |
|
MinimumRingSize: 20, |
|
MaximumRingSize: 30, |
|
}, |
|
}, |
|
}, |
|
) |
|
case "resolver-with-lb": |
|
entries = append(entries, |
|
&structs.ProxyConfigEntry{ |
|
Kind: structs.ProxyDefaults, |
|
Name: structs.ProxyConfigGlobal, |
|
EnterpriseMeta: entMeta, |
|
Protocol: "http", |
|
Config: map[string]interface{}{ |
|
"protocol": "http", |
|
}, |
|
}, |
|
&structs.ServiceResolverConfigEntry{ |
|
Kind: structs.ServiceResolver, |
|
Name: "db", |
|
EnterpriseMeta: entMeta, |
|
LoadBalancer: &structs.LoadBalancer{ |
|
Policy: "ring_hash", |
|
RingHashConfig: &structs.RingHashConfig{ |
|
MinimumRingSize: 20, |
|
MaximumRingSize: 30, |
|
}, |
|
}, |
|
}, |
|
) |
|
default: |
|
e, p := extraDiscoChainConfig(t, variation, entMeta) |
|
|
|
entries = append(entries, e...) |
|
peers = append(peers, p...) |
|
} |
|
|
|
if len(additionalEntries) > 0 { |
|
entries = append(entries, additionalEntries...) |
|
} |
|
|
|
set := configentry.NewDiscoveryChainSet() |
|
|
|
set.AddEntries(entries...) |
|
set.AddPeers(peers...) |
|
|
|
return discoverychain.TestCompileConfigEntries(t, "db", entMeta.NamespaceOrDefault(), entMeta.PartitionOrDefault(), "dc1", connect.TestClusterID+".consul", compileSetup, set) |
|
} |
|
|
|
func httpMatch(http *structs.ServiceRouteHTTPMatch) *structs.ServiceRouteMatch { |
|
return &structs.ServiceRouteMatch{HTTP: http} |
|
} |
|
func httpMatchHeader(headers ...structs.ServiceRouteHTTPMatchHeader) *structs.ServiceRouteMatch { |
|
return httpMatch(&structs.ServiceRouteHTTPMatch{ |
|
Header: headers, |
|
}) |
|
} |
|
func httpMatchParam(params ...structs.ServiceRouteHTTPMatchQueryParam) *structs.ServiceRouteMatch { |
|
return httpMatch(&structs.ServiceRouteHTTPMatch{ |
|
QueryParam: params, |
|
}) |
|
} |
|
func toService(svc string) *structs.ServiceRouteDestination { |
|
return &structs.ServiceRouteDestination{Service: svc} |
|
}
|
|
|