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.
1135 lines
32 KiB
1135 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}
|
|
}
|