From 2a56e0055b79d9ed29013431348a5e378a08bbc2 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" <4903+rboyer@users.noreply.github.com> Date: Mon, 7 Mar 2022 11:47:14 -0600 Subject: [PATCH] proxycfg: change how various proxycfg test helpers for making ConfigSnapshot copies works to be more correct and less error prone (#12531) Prior to this PR for the envoy xDS golden tests in the agent/xds package we were hand-creating a proxycfg.ConfigSnapshot structure in the proper format for input to the xDS generator. Over time this intermediate structure has gotten trickier to build correctly for the various tests. This PR proposes to switch to using the existing mechanism for turning a structs.NodeService and a sequence of cache.UpdateEvent copies into a proxycfg.ConfigSnapshot, as that is less error prone to construct and aligns more with how the data arrives. NOTE: almost all of this is in test-related code. I tried super hard to craft correct event inputs to get the golden files to be the same, or similar enough after construction to feel ok that i recreated the spirit of the original test cases. --- agent/proxycfg/state.go | 27 +- agent/proxycfg/testing.go | 1592 ++--------------- agent/proxycfg/testing_connect_proxy.go | 271 +++ agent/proxycfg/testing_ingress_gateway.go | 1500 ++++++++++++++++ agent/proxycfg/testing_mesh_gateway.go | 442 +++++ agent/proxycfg/testing_terminating_gateway.go | 643 +++++++ agent/proxycfg/testing_tproxy.go | 519 ++++++ agent/proxycfg/testing_upstreams.go | 764 ++++++++ agent/xds/clusters_test.go | 892 ++++----- agent/xds/delta_test.go | 4 +- agent/xds/endpoints_test.go | 508 +++--- agent/xds/listeners_ingress.go | 2 +- agent/xds/listeners_test.go | 1522 +++++----------- agent/xds/routes.go | 26 +- agent/xds/routes_test.go | 507 +----- ...th-chain-and-overrides.envoy-1-20-x.golden | 68 - ...alog-destinations-only.envoy-1-20-x.golden | 116 ++ ...ial-instances-directly.envoy-1-20-x.golden | 58 + .../transparent-proxy.envoy-1-20-x.golden | 116 ++ ...th-chain-and-overrides.envoy-1-20-x.golden | 41 - .../expose-checks.envoy-1-20-x.golden | 20 + ...-paths-local-app-paths.envoy-1-20-x.golden | 20 + ...aths-new-cluster-http2.envoy-1-20-x.golden | 20 + ...ess-gateway-bind-addrs.envoy-1-20-x.golden | 12 +- .../ingress-gateway.envoy-1-20-x.golden | 4 +- ...with-resolver-redirect.envoy-1-20-x.golden | 8 +- ...th-chain-and-overrides.envoy-1-20-x.golden | 63 - ...ith-chain-external-sni.envoy-1-20-x.golden | 4 +- ...-sds-listener-gw-level.envoy-1-20-x.golden | 4 +- ...-through-local-gateway.envoy-1-20-x.golden | 4 +- ...through-remote-gateway.envoy-1-20-x.golden | 4 +- ...listener-cipher-suites.envoy-1-20-x.golden | 4 +- ...s-listener-max-version.envoy-1-20-x.golden | 4 +- ...s-listener-min-version.envoy-1-20-x.golden | 4 +- ...ress-with-tls-listener.envoy-1-20-x.golden | 4 +- ...-x.golden => defaults.envoy-1-20-x.golden} | 0 ...ingress-lb-in-resolver.envoy-1-20-x.golden | 4 +- ...with-resolver-redirect.envoy-1-20-x.golden | 4 +- ...th-chain-and-overrides.envoy-1-20-x.golden | 7 - ...nd-router-header-manip.envoy-1-20-x.golden | 4 +- ...-with-chain-and-router.envoy-1-20-x.golden | 4 +- ...ith-chain-and-splitter.envoy-1-20-x.golden | 4 +- ...gress-with-grpc-router.envoy-1-20-x.golden | 4 +- agent/xds/xds_protocol_helpers_test.go | 2 +- 44 files changed, 5690 insertions(+), 4140 deletions(-) create mode 100644 agent/proxycfg/testing_connect_proxy.go create mode 100644 agent/proxycfg/testing_ingress_gateway.go create mode 100644 agent/proxycfg/testing_mesh_gateway.go create mode 100644 agent/proxycfg/testing_terminating_gateway.go create mode 100644 agent/proxycfg/testing_tproxy.go create mode 100644 agent/proxycfg/testing_upstreams.go delete mode 100644 agent/xds/testdata/clusters/ingress-with-chain-and-overrides.envoy-1-20-x.golden delete mode 100644 agent/xds/testdata/endpoints/ingress-with-chain-and-overrides.envoy-1-20-x.golden delete mode 100644 agent/xds/testdata/listeners/ingress-with-chain-and-overrides.envoy-1-20-x.golden rename agent/xds/testdata/routes/{defaults-no-chain.envoy-1-20-x.golden => defaults.envoy-1-20-x.golden} (100%) delete mode 100644 agent/xds/testdata/routes/ingress-with-chain-and-overrides.envoy-1-20-x.golden diff --git a/agent/proxycfg/state.go b/agent/proxycfg/state.go index 0c55f034a1..9d4488e1eb 100644 --- a/agent/proxycfg/state.go +++ b/agent/proxycfg/state.go @@ -152,10 +152,26 @@ func newState(ns *structs.NodeService, token string, config stateConfig) (*state return nil, err } + handler, err := newKindHandler(config, s, ch) + if err != nil { + return nil, err + } + + return &state{ + logger: config.logger.With("proxy", s.proxyID, "kind", s.kind), + serviceInstance: s, + handler: handler, + ch: ch, + snapCh: make(chan ConfigSnapshot, 1), + reqCh: make(chan chan *ConfigSnapshot, 1), + }, nil +} + +func newKindHandler(config stateConfig, s serviceInstance, ch chan cache.UpdateEvent) (kindHandler, error) { var handler kindHandler h := handlerState{stateConfig: config, serviceInstance: s, ch: ch} - switch ns.Kind { + switch s.kind { case structs.ServiceKindConnectProxy: handler = &handlerConnectProxy{handlerState: h} case structs.ServiceKindTerminatingGateway: @@ -170,14 +186,7 @@ func newState(ns *structs.NodeService, token string, config stateConfig) (*state return nil, errors.New("not a connect-proxy, terminating-gateway, mesh-gateway, or ingress-gateway") } - return &state{ - logger: config.logger.With("proxy", s.proxyID, "kind", s.kind), - serviceInstance: s, - handler: handler, - ch: ch, - snapCh: make(chan ConfigSnapshot, 1), - reqCh: make(chan chan *ConfigSnapshot, 1), - }, nil + return handler, nil } func newServiceInstanceFromNodeService(ns *structs.NodeService, token string) (serviceInstance, error) { diff --git a/agent/proxycfg/testing.go b/agent/proxycfg/testing.go index 11730819cc..f95ccfe374 100644 --- a/agent/proxycfg/testing.go +++ b/agent/proxycfg/testing.go @@ -4,21 +4,19 @@ import ( "context" "fmt" "io/ioutil" - "math" "path" "path/filepath" "sync" "sync/atomic" "time" + "github.com/hashicorp/go-hclog" "github.com/mitchellh/go-testing-interface" "github.com/stretchr/testify/require" - "github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/agent/cache" cachetype "github.com/hashicorp/consul/agent/cache-types" "github.com/hashicorp/consul/agent/connect" - "github.com/hashicorp/consul/agent/consul/discoverychain" "github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/api" ) @@ -666,1512 +664,130 @@ func TestGatewayServiceGroupFooDC1(t testing.T) structs.CheckServiceNodes { } } -// TestConfigSnapshot returns a fully populated snapshot -func TestConfigSnapshot(t testing.T) *ConfigSnapshot { - roots, leaf := TestCerts(t) +type noopCacheNotifier struct{} - // no entries implies we'll get a default chain - dbChain := discoverychain.TestCompileConfigEntries(t, "db", "default", "default", "dc1", connect.TestClusterID+".consul", nil) +var _ CacheNotifier = (*noopCacheNotifier)(nil) - upstreams := structs.TestUpstreams(t) - - return &ConfigSnapshot{ - Locality: GatewayKey{Datacenter: "dc1", Partition: acl.DefaultPartitionName}, - Kind: structs.ServiceKindConnectProxy, - Service: "web-sidecar-proxy", - ProxyID: structs.NewServiceID("web-sidecar-proxy", nil), - Address: "0.0.0.0", - Port: 9999, - Proxy: structs.ConnectProxyConfig{ - DestinationServiceID: "web", - DestinationServiceName: "web", - LocalServiceAddress: "127.0.0.1", - LocalServicePort: 8080, - Config: map[string]interface{}{ - "foo": "bar", - }, - Upstreams: upstreams, - }, - Roots: roots, - ConnectProxy: configSnapshotConnectProxy{ - ConfigSnapshotUpstreams: ConfigSnapshotUpstreams{ - Leaf: leaf, - UpstreamConfig: UpstreamsToMap(upstreams), - DiscoveryChain: map[UpstreamID]*structs.CompiledDiscoveryChain{ - UpstreamIDFromString("db"): dbChain, - }, - WatchedUpstreamEndpoints: map[UpstreamID]map[string]structs.CheckServiceNodes{ - UpstreamIDFromString("db"): { - "db.default.default.dc1": TestUpstreamNodes(t, "db"), - }, - }, - }, - PreparedQueryEndpoints: map[UpstreamID]structs.CheckServiceNodes{ - UpstreamIDFromString("prepared_query:geo-cache"): TestPreparedQueryNodes(t, "geo-cache"), - }, - Intentions: nil, // no intentions defined - IntentionsSet: true, - }, - Datacenter: "dc1", - } +func (*noopCacheNotifier) Notify(_ context.Context, _ string, _ cache.Request, _ string, _ chan<- cache.UpdateEvent) error { + return nil } -// TestConfigSnapshotDiscoveryChain returns a fully populated snapshot using a discovery chain -func TestConfigSnapshotDiscoveryChain(t testing.T) *ConfigSnapshot { - return testConfigSnapshotDiscoveryChain(t, "simple") +type noopHealth struct{} + +var _ Health = (*noopHealth)(nil) + +func (*noopHealth) Notify(_ context.Context, _ structs.ServiceSpecificRequest, _ string, _ chan<- cache.UpdateEvent) error { + return nil } -func TestConfigSnapshotDiscoveryChainExternalSNI(t testing.T) *ConfigSnapshot { - return testConfigSnapshotDiscoveryChain(t, "external-sni") -} - -func TestConfigSnapshotDiscoveryChainWithOverrides(t testing.T) *ConfigSnapshot { - return testConfigSnapshotDiscoveryChain(t, "simple-with-overrides") -} - -func TestConfigSnapshotDiscoveryChainWithFailover(t testing.T) *ConfigSnapshot { - return testConfigSnapshotDiscoveryChain(t, "failover") -} - -func TestConfigSnapshotDiscoveryChainWithFailoverThroughRemoteGateway(t testing.T) *ConfigSnapshot { - return testConfigSnapshotDiscoveryChain(t, "failover-through-remote-gateway") -} - -func TestConfigSnapshotDiscoveryChainWithFailoverThroughRemoteGatewayTriggered(t testing.T) *ConfigSnapshot { - return testConfigSnapshotDiscoveryChain(t, "failover-through-remote-gateway-triggered") -} - -func TestConfigSnapshotDiscoveryChainWithDoubleFailoverThroughRemoteGateway(t testing.T) *ConfigSnapshot { - return testConfigSnapshotDiscoveryChain(t, "failover-through-double-remote-gateway") -} - -func TestConfigSnapshotDiscoveryChainWithDoubleFailoverThroughRemoteGatewayTriggered(t testing.T) *ConfigSnapshot { - return testConfigSnapshotDiscoveryChain(t, "failover-through-double-remote-gateway-triggered") -} - -func TestConfigSnapshotDiscoveryChainWithFailoverThroughLocalGateway(t testing.T) *ConfigSnapshot { - return testConfigSnapshotDiscoveryChain(t, "failover-through-local-gateway") -} - -func TestConfigSnapshotDiscoveryChainWithFailoverThroughLocalGatewayTriggered(t testing.T) *ConfigSnapshot { - return testConfigSnapshotDiscoveryChain(t, "failover-through-local-gateway-triggered") -} - -func TestConfigSnapshotDiscoveryChainWithDoubleFailoverThroughLocalGateway(t testing.T) *ConfigSnapshot { - return testConfigSnapshotDiscoveryChain(t, "failover-through-double-local-gateway") -} - -func TestConfigSnapshotDiscoveryChainWithDoubleFailoverThroughLocalGatewayTriggered(t testing.T) *ConfigSnapshot { - return testConfigSnapshotDiscoveryChain(t, "failover-through-double-local-gateway-triggered") -} - -func TestConfigSnapshotDiscoveryChain_SplitterWithResolverRedirectMultiDC(t testing.T) *ConfigSnapshot { - return testConfigSnapshotDiscoveryChain(t, "splitter-with-resolver-redirect-multidc") -} - -func TestConfigSnapshotDiscoveryChainWithEntries(t testing.T, additionalEntries ...structs.ConfigEntry) *ConfigSnapshot { - return testConfigSnapshotDiscoveryChain(t, "simple", additionalEntries...) -} - -func TestConfigSnapshotDiscoveryChainDefaultWithEntries(t testing.T, additionalEntries ...structs.ConfigEntry) *ConfigSnapshot { - return testConfigSnapshotDiscoveryChain(t, "default", additionalEntries...) -} - -func TestConfigSnapshotDiscoveryChainDefault(t testing.T) *ConfigSnapshot { - return testConfigSnapshotDiscoveryChain(t, "default") -} - -func TestConfigSnapshotDiscoveryChainWithSplitter(t testing.T) *ConfigSnapshot { - return testConfigSnapshotDiscoveryChain(t, "chain-and-splitter") -} - -func TestConfigSnapshotDiscoveryChainWithGRPCRouter(t testing.T) *ConfigSnapshot { - return testConfigSnapshotDiscoveryChain(t, "grpc-router") -} - -func TestConfigSnapshotDiscoveryChainWithRouter(t testing.T) *ConfigSnapshot { - return testConfigSnapshotDiscoveryChain(t, "chain-and-router") -} - -func TestConfigSnapshotDiscoveryChainWithLB(t testing.T) *ConfigSnapshot { - return testConfigSnapshotDiscoveryChain(t, "lb-resolver") -} - -func testConfigSnapshotDiscoveryChain(t testing.T, variation string, additionalEntries ...structs.ConfigEntry) *ConfigSnapshot { - roots, leaf := TestCerts(t) - - snap := &ConfigSnapshot{ - Locality: GatewayKey{Datacenter: "dc1", Partition: acl.DefaultPartitionName}, - Kind: structs.ServiceKindConnectProxy, - Service: "web-sidecar-proxy", - ProxyID: structs.NewServiceID("web-sidecar-proxy", nil), - Address: "0.0.0.0", - Port: 9999, - Proxy: structs.ConnectProxyConfig{ - DestinationServiceID: "web", - DestinationServiceName: "web", - LocalServiceAddress: "127.0.0.1", - LocalServicePort: 8080, - Config: map[string]interface{}{ - "foo": "bar", - }, - Upstreams: structs.TestUpstreams(t), - }, - Roots: roots, - ConnectProxy: configSnapshotConnectProxy{ - ConfigSnapshotUpstreams: setupTestVariationConfigEntriesAndSnapshot( - t, variation, leaf, additionalEntries..., - ), - PreparedQueryEndpoints: map[UpstreamID]structs.CheckServiceNodes{ - UpstreamIDFromString("prepared_query:geo-cache"): TestPreparedQueryNodes(t, "geo-cache"), - }, - Intentions: nil, // no intentions defined - IntentionsSet: true, - }, - Datacenter: "dc1", - } - - return snap -} - -func setupTestVariationConfigEntriesAndSnapshot( +// testConfigSnapshotFixture helps you execute normal proxycfg event machinery +// to assemble a ConfigSnapshot via standard means to ensure test data used in +// any tests is actually a valid configuration. +// +// The provided ns argument will be manipulated by the nsFn callback if present +// before it is used. +// +// The events provided in the updates slice will be fed into the event +// machinery. +func testConfigSnapshotFixture( t testing.T, - variation string, - leaf *structs.IssuedCert, - additionalEntries ...structs.ConfigEntry, -) ConfigSnapshotUpstreams { - // Compile a chain. - var ( - entries []structs.ConfigEntry - compileSetup func(req *discoverychain.CompileRequest) - ) - - switch variation { - case "default": - // no config entries - 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", - ConnectTimeout: 33 * time.Second, - }, - ) - case "external-sni": - entries = append(entries, - &structs.ServiceConfigEntry{ - Kind: structs.ServiceDefaults, - Name: "db", - ExternalSNI: "db.some.other.service.mesh", - }, - &structs.ServiceResolverConfigEntry{ - Kind: structs.ServiceResolver, - Name: "db", - ConnectTimeout: 33 * time.Second, - }, - ) - case "failover": - entries = append(entries, - &structs.ServiceResolverConfigEntry{ - Kind: structs.ServiceResolver, - Name: "db", - ConnectTimeout: 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", - MeshGateway: structs.MeshGatewayConfig{ - Mode: structs.MeshGatewayModeRemote, - }, - }, - &structs.ServiceResolverConfigEntry{ - Kind: structs.ServiceResolver, - Name: "db", - ConnectTimeout: 33 * time.Second, - Failover: map[string]structs.ServiceResolverFailover{ - "*": { - Datacenters: []string{"dc2"}, - }, - }, - }, - ) - case "failover-through-double-remote-gateway-triggered": - fallthrough - case "failover-through-double-remote-gateway": - entries = append(entries, - &structs.ServiceConfigEntry{ - Kind: structs.ServiceDefaults, - Name: "db", - MeshGateway: structs.MeshGatewayConfig{ - Mode: structs.MeshGatewayModeRemote, - }, - }, - &structs.ServiceResolverConfigEntry{ - Kind: structs.ServiceResolver, - Name: "db", - ConnectTimeout: 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", - MeshGateway: structs.MeshGatewayConfig{ - Mode: structs.MeshGatewayModeLocal, - }, - }, - &structs.ServiceResolverConfigEntry{ - Kind: structs.ServiceResolver, - Name: "db", - ConnectTimeout: 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", - MeshGateway: structs.MeshGatewayConfig{ - Mode: structs.MeshGatewayModeLocal, - }, - }, - &structs.ServiceResolverConfigEntry{ - Kind: structs.ServiceResolver, - Name: "db", - ConnectTimeout: 33 * time.Second, - Failover: map[string]structs.ServiceResolverFailover{ - "*": { - Datacenters: []string{"dc2", "dc3"}, - }, - }, - }, - ) - case "splitter-with-resolver-redirect-multidc": - entries = append(entries, - &structs.ProxyConfigEntry{ - Kind: structs.ProxyDefaults, - Name: structs.ProxyConfigGlobal, - Config: map[string]interface{}{ - "protocol": "http", - }, - }, - &structs.ServiceSplitterConfigEntry{ - Kind: structs.ServiceResolver, - Name: "db", - Splits: []structs.ServiceSplit{ - {Weight: 50, Service: "db-dc1"}, - {Weight: 50, Service: "db-dc2"}, - }, - }, - &structs.ServiceResolverConfigEntry{ - Kind: structs.ServiceResolver, - Name: "db-dc1", - Redirect: &structs.ServiceResolverRedirect{ - Service: "db", - ServiceSubset: "v1", - Datacenter: "dc1", - }, - }, - &structs.ServiceResolverConfigEntry{ - Kind: structs.ServiceResolver, - Name: "db-dc2", - Redirect: &structs.ServiceResolverRedirect{ - Service: "db", - ServiceSubset: "v2", - Datacenter: "dc2", - }, - }, - &structs.ServiceResolverConfigEntry{ - Kind: structs.ServiceResolver, - Name: "db", - 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", - ConnectTimeout: 33 * time.Second, - }, - &structs.ProxyConfigEntry{ - Kind: structs.ProxyDefaults, - Name: structs.ProxyConfigGlobal, - Config: map[string]interface{}{ - "protocol": "http", - }, - }, - &structs.ServiceSplitterConfigEntry{ - Kind: structs.ServiceSplitter, - Name: "db", - Splits: []structs.ServiceSplit{ - { - 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: 4, - 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 "grpc-router": - entries = append(entries, - &structs.ServiceResolverConfigEntry{ - Kind: structs.ServiceResolver, - Name: "db", - ConnectTimeout: 33 * time.Second, - }, - &structs.ProxyConfigEntry{ - Kind: structs.ProxyDefaults, - Name: structs.ProxyConfigGlobal, - Config: map[string]interface{}{ - "protocol": "grpc", - }, - }, - &structs.ServiceRouterConfigEntry{ - Kind: structs.ServiceRouter, - Name: "db", - 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", - ConnectTimeout: 33 * time.Second, - }, - &structs.ProxyConfigEntry{ - Kind: structs.ProxyDefaults, - Name: structs.ProxyConfigGlobal, - Config: map[string]interface{}{ - "protocol": "http", - }, - }, - &structs.ServiceSplitterConfigEntry{ - Kind: structs.ServiceSplitter, - Name: "split-3-ways", - 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", - 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: "/retry-connect", - }), - Destination: &structs.ServiceRouteDestination{ - Service: "retry-connect", - NumRetries: 15, - RetryOnConnectFailure: true, - }, - }, - { - 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-both", - }), - Destination: &structs.ServiceRouteDestination{ - Service: "retry-both", - RetryOnConnectFailure: true, - 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, - Config: map[string]interface{}{ - "protocol": "http", - }, - }, - &structs.ServiceSplitterConfigEntry{ - Kind: structs.ServiceSplitter, - Name: "db", - Splits: []structs.ServiceSplit{ - {Weight: 95.5, Service: "something-else"}, - {Weight: 4.5, Service: "db"}, - }, - }, - &structs.ServiceResolverConfigEntry{ - Kind: structs.ServiceResolver, - Name: "db", - 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", - }, - { - SourceIP: true, - Terminal: true, - }, - }, - }, - }, - ) - case "http-multiple-services": - default: - t.Fatalf("unexpected variation: %q", variation) - return ConfigSnapshotUpstreams{} - } - - if len(additionalEntries) > 0 { - entries = append(entries, additionalEntries...) - } - - dbChain := discoverychain.TestCompileConfigEntries(t, "db", "default", "default", "dc1", connect.TestClusterID+".consul", compileSetup, entries...) - - dbUID := UpstreamIDFromString("db") - - upstreams := structs.TestUpstreams(t) - snap := ConfigSnapshotUpstreams{ - Leaf: leaf, - DiscoveryChain: map[UpstreamID]*structs.CompiledDiscoveryChain{ - dbUID: dbChain, - }, - WatchedUpstreamEndpoints: map[UpstreamID]map[string]structs.CheckServiceNodes{ - dbUID: { - "db.default.default.dc1": TestUpstreamNodes(t, "db"), - }, - }, - UpstreamConfig: UpstreamsToMap(upstreams), - } - - switch variation { - case "default": - case "simple-with-overrides": - case "simple": - case "external-sni": - case "failover": - snap.WatchedUpstreamEndpoints[dbUID]["fail.default.default.dc1"] = - TestUpstreamNodesAlternate(t) - case "failover-through-remote-gateway-triggered": - snap.WatchedUpstreamEndpoints[dbUID]["db.default.default.dc1"] = - TestUpstreamNodesInStatus(t, "critical") - fallthrough - case "failover-through-remote-gateway": - snap.WatchedUpstreamEndpoints[dbUID]["db.default.default.dc2"] = - TestUpstreamNodesDC2(t) - snap.WatchedGatewayEndpoints = map[UpstreamID]map[string]structs.CheckServiceNodes{ - dbUID: { - "dc2": TestGatewayNodesDC2(t), - }, - } - case "failover-through-double-remote-gateway-triggered": - snap.WatchedUpstreamEndpoints[dbUID]["db.default.default.dc1"] = - TestUpstreamNodesInStatus(t, "critical") - snap.WatchedUpstreamEndpoints[dbUID]["db.default.default.dc2"] = - TestUpstreamNodesInStatusDC2(t, "critical") - fallthrough - case "failover-through-double-remote-gateway": - snap.WatchedUpstreamEndpoints[dbUID]["db.default.default.dc3"] = TestUpstreamNodesDC2(t) - snap.WatchedGatewayEndpoints = map[UpstreamID]map[string]structs.CheckServiceNodes{ - dbUID: { - "dc2": TestGatewayNodesDC2(t), - "dc3": TestGatewayNodesDC3(t), - }, - } - case "failover-through-local-gateway-triggered": - snap.WatchedUpstreamEndpoints[dbUID]["db.default.default.dc1"] = - TestUpstreamNodesInStatus(t, "critical") - fallthrough - case "failover-through-local-gateway": - snap.WatchedUpstreamEndpoints[dbUID]["db.default.default.dc2"] = - TestUpstreamNodesDC2(t) - snap.WatchedGatewayEndpoints = map[UpstreamID]map[string]structs.CheckServiceNodes{ - dbUID: { - "dc1": TestGatewayNodesDC1(t), - }, - } - case "failover-through-double-local-gateway-triggered": - snap.WatchedUpstreamEndpoints[dbUID]["db.default.default.dc1"] = - TestUpstreamNodesInStatus(t, "critical") - snap.WatchedUpstreamEndpoints[dbUID]["db.default.default.dc2"] = - TestUpstreamNodesInStatusDC2(t, "critical") - fallthrough - case "failover-through-double-local-gateway": - snap.WatchedUpstreamEndpoints[dbUID]["db.default.default.dc3"] = TestUpstreamNodesDC2(t) - snap.WatchedGatewayEndpoints = map[UpstreamID]map[string]structs.CheckServiceNodes{ - dbUID: { - "dc1": TestGatewayNodesDC1(t), - }, - } - case "splitter-with-resolver-redirect-multidc": - snap.WatchedUpstreamEndpoints[dbUID] = map[string]structs.CheckServiceNodes{ - "v1.db.default.default.dc1": TestUpstreamNodes(t, "db"), - "v2.db.default.default.dc2": TestUpstreamNodesDC2(t), - } - case "chain-and-splitter": - case "grpc-router": - case "chain-and-router": - case "http-multiple-services": - snap.WatchedUpstreamEndpoints[UpstreamIDFromString("foo")] = map[string]structs.CheckServiceNodes{ - "foo.default.default.dc1": TestUpstreamNodes(t, "foo"), - } - snap.WatchedUpstreamEndpoints[UpstreamIDFromString("bar")] = map[string]structs.CheckServiceNodes{ - "bar.default.default.dc1": TestUpstreamNodesAlternate(t), - } - case "lb-resolver": - default: - t.Fatalf("unexpected variation: %q", variation) - return ConfigSnapshotUpstreams{} - } - - return snap -} - -func TestConfigSnapshotMeshGateway(t testing.T) *ConfigSnapshot { - return testConfigSnapshotMeshGateway(t, true, false) -} - -func TestConfigSnapshotMeshGatewayUsingFederationStates(t testing.T) *ConfigSnapshot { - return testConfigSnapshotMeshGateway(t, true, true) -} - -func TestConfigSnapshotMeshGatewayNewerInformationInFederationStates(t testing.T) *ConfigSnapshot { - snap := TestConfigSnapshotMeshGateway(t) - - // 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 - - snap.MeshGateway.FedStateGateways = map[string]structs.CheckServiceNodes{ - "dc2": { - { - Node: snap.MeshGateway.GatewayGroups["dc2"][0].Node, - Service: svc, - }, - }, - } - - return snap -} - -func TestConfigSnapshotMeshGatewayOlderInformationInFederationStates(t testing.T) *ConfigSnapshot { - snap := TestConfigSnapshotMeshGateway(t) - - // 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 - - snap.MeshGateway.FedStateGateways = map[string]structs.CheckServiceNodes{ - "dc2": { - { - Node: snap.MeshGateway.GatewayGroups["dc2"][0].Node, - Service: svc, - }, - }, - } - - return snap -} - -func TestConfigSnapshotMeshGatewayNoServices(t testing.T) *ConfigSnapshot { - return testConfigSnapshotMeshGateway(t, false, false) -} - -func testConfigSnapshotMeshGateway(t testing.T, populateServices bool, useFederationStates bool) *ConfigSnapshot { - roots, _ := TestCerts(t) - snap := &ConfigSnapshot{ - Locality: GatewayKey{Datacenter: "dc1", Partition: acl.DefaultPartitionName}, - Kind: structs.ServiceKindMeshGateway, - Service: "mesh-gateway", - ProxyID: structs.NewServiceID("mesh-gateway", nil), - Address: "1.2.3.4", - Port: 8443, - Proxy: structs.ConnectProxyConfig{ - Config: map[string]interface{}{}, - }, - TaggedAddresses: map[string]structs.ServiceAddress{ - structs.TaggedAddressLAN: { - Address: "1.2.3.4", - Port: 8443, - }, - structs.TaggedAddressWAN: { - Address: "198.18.0.1", - Port: 443, - }, - }, - Roots: roots, - Datacenter: "dc1", - MeshGateway: configSnapshotMeshGateway{ - WatchedServicesSet: true, - }, - } - - if populateServices { - snap.MeshGateway = configSnapshotMeshGateway{ - WatchedServices: map[structs.ServiceName]context.CancelFunc{ - structs.NewServiceName("foo", nil): nil, - structs.NewServiceName("bar", nil): nil, - }, - WatchedServicesSet: true, - WatchedGateways: map[string]context.CancelFunc{ - "dc2": nil, - }, - ServiceGroups: map[structs.ServiceName]structs.CheckServiceNodes{ - structs.NewServiceName("foo", nil): TestGatewayServiceGroupFooDC1(t), - structs.NewServiceName("bar", nil): TestGatewayServiceGroupBarDC1(t), - }, - GatewayGroups: map[string]structs.CheckServiceNodes{ - "dc2": TestGatewayNodesDC2(t), - "dc4": TestGatewayNodesDC4Hostname(t), - "dc6": TestGatewayNodesDC6Hostname(t), - }, - HostnameDatacenters: map[string]structs.CheckServiceNodes{ - "dc4": { - structs.CheckServiceNode{ - Node: &structs.Node{ - ID: "mesh-gateway-1", - Node: "mesh-gateway", - Address: "10.30.1.1", - Datacenter: "dc4", - }, - Service: structs.TestNodeServiceMeshGatewayWithAddrs(t, - "10.30.1.1", 8443, - structs.ServiceAddress{Address: "10.0.1.1", Port: 8443}, - structs.ServiceAddress{Address: "123.us-west-2.elb.notaws.com", Port: 443}), - }, - structs.CheckServiceNode{ - Node: &structs.Node{ - ID: "mesh-gateway-2", - Node: "mesh-gateway", - Address: "10.30.1.2", - Datacenter: "dc4", - }, - Service: structs.TestNodeServiceMeshGatewayWithAddrs(t, - "10.30.1.2", 8443, - structs.ServiceAddress{Address: "10.30.1.2", Port: 8443}, - structs.ServiceAddress{Address: "456.us-west-2.elb.notaws.com", Port: 443}), - }, - }, - "dc6": { - structs.CheckServiceNode{ - Node: &structs.Node{ - ID: "mesh-gateway-1", - Node: "mesh-gateway", - Address: "10.30.1.1", - Datacenter: "dc6", - }, - Service: structs.TestNodeServiceMeshGatewayWithAddrs(t, - "10.30.1.1", 8443, - structs.ServiceAddress{Address: "10.0.1.1", Port: 8443}, - structs.ServiceAddress{Address: "123.us-east-1.elb.notaws.com", Port: 443}), - Checks: structs.HealthChecks{ - { - Status: api.HealthCritical, - }, - }, - }, - }, - }, - } - if useFederationStates { - snap.MeshGateway.FedStateGateways = map[string]structs.CheckServiceNodes{ - "dc2": TestGatewayNodesDC2(t), - "dc4": TestGatewayNodesDC4Hostname(t), - "dc6": TestGatewayNodesDC6Hostname(t), - } - - delete(snap.MeshGateway.GatewayGroups, "dc2") - } - } - - return snap -} - -func TestConfigSnapshotIngress(t testing.T) *ConfigSnapshot { - return testConfigSnapshotIngressGateway(t, true, "tcp", "simple") -} - -func TestConfigSnapshotIngressWithTLSListener(t testing.T) *ConfigSnapshot { - snap := testConfigSnapshotIngressGateway(t, true, "tcp", "default") - snap.IngressGateway.TLSConfig.Enabled = true - return snap -} - -func TestConfigSnapshotIngressWithGatewaySDS(t testing.T) *ConfigSnapshot { - snap := testConfigSnapshotIngressGateway(t, true, "tcp", "default") - snap.IngressGateway.TLSConfig.SDS = &structs.GatewayTLSSDSConfig{ - ClusterName: "sds-cluster", - CertResource: "cert-resource", - } - return snap -} - -func TestConfigSnapshotIngressWithOverrides(t testing.T) *ConfigSnapshot { - return testConfigSnapshotIngressGateway(t, true, "tcp", "simple-with-overrides") -} -func TestConfigSnapshotIngress_SplitterWithResolverRedirectMultiDC(t testing.T) *ConfigSnapshot { - return testConfigSnapshotIngressGateway(t, true, "http", "splitter-with-resolver-redirect-multidc") -} - -func TestConfigSnapshotIngress_HTTPMultipleServices(t testing.T) *ConfigSnapshot { - return testConfigSnapshotIngressGateway(t, true, "http", "http-multiple-services") -} - -func TestConfigSnapshotIngressExternalSNI(t testing.T) *ConfigSnapshot { - return testConfigSnapshotIngressGateway(t, true, "tcp", "external-sni") -} - -func TestConfigSnapshotIngressWithFailover(t testing.T) *ConfigSnapshot { - return testConfigSnapshotIngressGateway(t, true, "tcp", "failover") -} - -func TestConfigSnapshotIngressWithFailoverThroughRemoteGateway(t testing.T) *ConfigSnapshot { - return testConfigSnapshotIngressGateway(t, true, "tcp", "failover-through-remote-gateway") -} - -func TestConfigSnapshotIngressWithFailoverThroughRemoteGatewayTriggered(t testing.T) *ConfigSnapshot { - return testConfigSnapshotIngressGateway(t, true, "tcp", "failover-through-remote-gateway-triggered") -} - -func TestConfigSnapshotIngressWithDoubleFailoverThroughRemoteGateway(t testing.T) *ConfigSnapshot { - return testConfigSnapshotIngressGateway(t, true, "tcp", "failover-through-double-remote-gateway") -} - -func TestConfigSnapshotIngressWithDoubleFailoverThroughRemoteGatewayTriggered(t testing.T) *ConfigSnapshot { - return testConfigSnapshotIngressGateway(t, true, "tcp", "failover-through-double-remote-gateway-triggered") -} - -func TestConfigSnapshotIngressWithFailoverThroughLocalGateway(t testing.T) *ConfigSnapshot { - return testConfigSnapshotIngressGateway(t, true, "tcp", "failover-through-local-gateway") -} - -func TestConfigSnapshotIngressWithFailoverThroughLocalGatewayTriggered(t testing.T) *ConfigSnapshot { - return testConfigSnapshotIngressGateway(t, true, "tcp", "failover-through-local-gateway-triggered") -} - -func TestConfigSnapshotIngressWithDoubleFailoverThroughLocalGateway(t testing.T) *ConfigSnapshot { - return testConfigSnapshotIngressGateway(t, true, "tcp", "failover-through-double-local-gateway") -} - -func TestConfigSnapshotIngressWithDoubleFailoverThroughLocalGatewayTriggered(t testing.T) *ConfigSnapshot { - return testConfigSnapshotIngressGateway(t, true, "tcp", "failover-through-double-local-gateway-triggered") -} - -func TestConfigSnapshotIngressWithSplitter(t testing.T) *ConfigSnapshot { - return testConfigSnapshotIngressGateway(t, true, "http", "chain-and-splitter") -} - -func TestConfigSnapshotIngressWithGRPCRouter(t testing.T) *ConfigSnapshot { - return testConfigSnapshotIngressGateway(t, true, "http", "grpc-router") -} - -func TestConfigSnapshotIngressWithRouter(t testing.T) *ConfigSnapshot { - return testConfigSnapshotIngressGateway(t, true, "http", "chain-and-router") -} - -func TestConfigSnapshotIngressGateway(t testing.T) *ConfigSnapshot { - return testConfigSnapshotIngressGateway(t, true, "tcp", "default") -} - -func TestConfigSnapshotIngressGatewayNoServices(t testing.T) *ConfigSnapshot { - return testConfigSnapshotIngressGateway(t, false, "tcp", "default") -} - -func TestConfigSnapshotIngressWithLB(t testing.T) *ConfigSnapshot { - return testConfigSnapshotIngressGateway(t, true, "http", "lb-resolver") -} - -func TestConfigSnapshotIngressDiscoveryChainWithEntries(t testing.T, additionalEntries ...structs.ConfigEntry) *ConfigSnapshot { - return testConfigSnapshotIngressGateway(t, true, "http", "simple", additionalEntries...) -} - -func testConfigSnapshotIngressGateway( - t testing.T, populateServices bool, protocol, variation string, - additionalEntries ...structs.ConfigEntry, + ns *structs.NodeService, + nsFn func(ns *structs.NodeService), + serverSNIFn ServerSNIFunc, + updates []cache.UpdateEvent, ) *ConfigSnapshot { - roots, leaf := TestCerts(t) + const token = "" - snap := &ConfigSnapshot{ - Locality: GatewayKey{Datacenter: "dc1", Partition: acl.DefaultPartitionName}, - Kind: structs.ServiceKindIngressGateway, - Service: "ingress-gateway", - ProxyID: structs.NewServiceID("ingress-gateway", nil), - Address: "1.2.3.4", - Roots: roots, - Datacenter: "dc1", + if nsFn != nil { + nsFn(ns) } - if populateServices { - snap.IngressGateway = configSnapshotIngressGateway{ - ConfigSnapshotUpstreams: setupTestVariationConfigEntriesAndSnapshot( - t, variation, leaf, additionalEntries..., - ), - Upstreams: map[IngressListenerKey]structs.Upstreams{ - {protocol, 9191}: { - { - // We rely on this one having default type in a few tests... - DestinationName: "db", - DestinationPartition: "default", - LocalBindPort: 9191, - LocalBindAddress: "2.3.4.5", - }, - }, - }, - Listeners: map[IngressListenerKey]structs.IngressListener{ - {protocol, 9191}: { - Port: 9191, - Protocol: protocol, - Services: []structs.IngressService{ - {Name: "db"}, - }, - }, - }, - } - } - return snap -} -func TestConfigSnapshotExposeConfig(t testing.T) *ConfigSnapshot { - return &ConfigSnapshot{ - Locality: GatewayKey{Datacenter: "dc1", Partition: acl.DefaultPartitionName}, - Kind: structs.ServiceKindConnectProxy, - Service: "web-proxy", - ProxyID: structs.NewServiceID("web-proxy", nil), - Address: "1.2.3.4", - Port: 8080, - Proxy: structs.ConnectProxyConfig{ - DestinationServiceName: "web", - DestinationServiceID: "web", - LocalServicePort: 8080, - Expose: structs.ExposeConfig{ - Checks: false, - Paths: []structs.ExposePath{ - { - LocalPathPort: 8080, - Path: "/health1", - ListenerPort: 21500, - }, - { - LocalPathPort: 8080, - Path: "/health2", - ListenerPort: 21501, - }, - }, - }, + config := stateConfig{ + logger: hclog.NewNullLogger(), + source: &structs.QuerySource{ + Datacenter: "dc1", }, - Datacenter: "dc1", - } -} - -func TestConfigSnapshotTerminatingGateway(t testing.T) *ConfigSnapshot { - return testConfigSnapshotTerminatingGateway(t, true) -} - -func TestConfigSnapshotTerminatingGatewayNoServices(t testing.T) *ConfigSnapshot { - return testConfigSnapshotTerminatingGateway(t, false) -} - -func testConfigSnapshotTerminatingGateway(t testing.T, populateServices bool) *ConfigSnapshot { - roots, _ := TestCerts(t) - - snap := &ConfigSnapshot{ - Locality: GatewayKey{Datacenter: "dc1", Partition: acl.DefaultPartitionName}, - Kind: structs.ServiceKindTerminatingGateway, - Service: "terminating-gateway", - ProxyID: structs.NewServiceID("terminating-gateway", nil), - Address: "1.2.3.4", - TaggedAddresses: map[string]structs.ServiceAddress{ - structs.TaggedAddressWAN: { - Address: "198.18.0.1", - Port: 443, - }, + cache: &noopCacheNotifier{}, + health: &noopHealth{}, + dnsConfig: DNSConfig{ // TODO: make configurable + Domain: "consul", + AltDomain: "", }, - Port: 8443, - Roots: roots, - Datacenter: "dc1", + serverSNIFn: serverSNIFn, + intentionDefaultAllow: false, // TODO: make configurable } - if populateServices { - web := structs.NewServiceName("web", nil) - webNodes := TestUpstreamNodes(t, web.Name) - webNodes[0].Service.Meta = map[string]string{ - "version": "1", - } - webNodes[1].Service.Meta = map[string]string{ - "version": "2", - } - - api := structs.NewServiceName("api", nil) - apiNodes := structs.CheckServiceNodes{ - structs.CheckServiceNode{ - Node: &structs.Node{ - ID: "api", - Node: "test1", - Address: "10.10.1.1", - Datacenter: "dc1", - }, - Service: &structs.NodeService{ - Service: "api", - Address: "api.mydomain", - Port: 8081, - }, - Checks: structs.HealthChecks{ - { - Status: "critical", - }, - }, - }, - structs.CheckServiceNode{ - Node: &structs.Node{ - ID: "test2", - Node: "test2", - Address: "10.10.1.2", - Datacenter: "dc1", - }, - Service: &structs.NodeService{ - Service: "api", - Address: "api.altdomain", - Port: 8081, - Meta: map[string]string{ - "domain": "alt", - }, - }, - }, - structs.CheckServiceNode{ - Node: &structs.Node{ - ID: "test3", - Node: "test3", - Address: "10.10.1.3", - Datacenter: "dc1", - }, - Service: &structs.NodeService{ - Service: "api", - Address: "10.10.1.3", - Port: 8081, - }, - }, - structs.CheckServiceNode{ - Node: &structs.Node{ - ID: "test4", - Node: "test4", - Address: "10.10.1.4", - Datacenter: "dc1", - }, - Service: &structs.NodeService{ - Service: "api", - Address: "api.thirddomain", - Port: 8081, - }, - }, - } - - // Has failing instance - db := structs.NewServiceName("db", nil) - dbNodes := structs.CheckServiceNodes{ - structs.CheckServiceNode{ - Node: &structs.Node{ - ID: "db", - Node: "test4", - Address: "10.10.1.4", - Datacenter: "dc1", - }, - Service: &structs.NodeService{ - Service: "db", - Address: "db.mydomain", - Port: 8081, - }, - Checks: structs.HealthChecks{ - { - Status: "critical", - }, - }, - }, - } - - // Has passing instance but failing subset - cache := structs.NewServiceName("cache", nil) - cacheNodes := structs.CheckServiceNodes{ - { - Node: &structs.Node{ - ID: "cache", - Node: "test5", - Address: "10.10.1.5", - Datacenter: "dc1", - }, - Service: &structs.NodeService{ - Service: "cache", - Address: "cache.mydomain", - Port: 8081, - }, - }, - { - Node: &structs.Node{ - ID: "cache", - Node: "test5", - Address: "10.10.1.5", - Datacenter: "dc1", - }, - Service: &structs.NodeService{ - Service: "cache", - Address: "cache.mydomain", - Port: 8081, - Meta: map[string]string{ - "Env": "prod", - }, - }, - Checks: structs.HealthChecks{ - { - Status: "critical", - }, - }, - }, - } - - snap.TerminatingGateway = configSnapshotTerminatingGateway{ - ServiceGroups: map[structs.ServiceName]structs.CheckServiceNodes{ - web: webNodes, - api: apiNodes, - db: dbNodes, - cache: cacheNodes, - }, - ServiceResolversSet: map[structs.ServiceName]bool{ - web: true, - api: true, - db: true, - cache: true, - }, - GatewayServices: map[structs.ServiceName]structs.GatewayService{ - web: { - Service: web, - CAFile: "ca.cert.pem", - }, - api: { - Service: api, - CAFile: "ca.cert.pem", - CertFile: "api.cert.pem", - KeyFile: "api.key.pem", - }, - db: { - Service: db, - }, - cache: { - Service: cache, - }, - }, - HostnameServices: map[structs.ServiceName]structs.CheckServiceNodes{ - api: {apiNodes[0], apiNodes[1]}, - db: {dbNodes[0]}, - cache: {cacheNodes[0], cacheNodes[1]}, - }, - } - - snap.TerminatingGateway.ServiceConfigs = map[structs.ServiceName]*structs.ServiceConfigResponse{ - web: { - ProxyConfig: map[string]interface{}{"protocol": "tcp"}, - }, - api: { - ProxyConfig: map[string]interface{}{"protocol": "tcp"}, - }, - db: { - ProxyConfig: map[string]interface{}{"protocol": "tcp"}, - }, - cache: { - ProxyConfig: map[string]interface{}{"protocol": "tcp"}, - }, - } - snap.TerminatingGateway.Intentions = map[structs.ServiceName]structs.Intentions{ - // no intentions defined for thse services - web: nil, - api: nil, - db: nil, - cache: nil, - } - - snap.TerminatingGateway.ServiceLeaves = map[structs.ServiceName]*structs.IssuedCert{ - web: { - CertPEM: golden(t, "test-leaf-cert"), - PrivateKeyPEM: golden(t, "test-leaf-key"), - }, - api: { - CertPEM: golden(t, "alt-test-leaf-cert"), - PrivateKeyPEM: golden(t, "alt-test-leaf-key"), - }, - db: { - CertPEM: golden(t, "db-test-leaf-cert"), - PrivateKeyPEM: golden(t, "db-test-leaf-key"), - }, - cache: { - CertPEM: golden(t, "cache-test-leaf-cert"), - PrivateKeyPEM: golden(t, "cache-test-leaf-key"), - }, - } - } - return snap -} - -func TestConfigSnapshotGRPCExposeHTTP1(t testing.T) *ConfigSnapshot { - return &ConfigSnapshot{ - Locality: GatewayKey{Datacenter: "dc1", Partition: acl.DefaultPartitionName}, - Kind: structs.ServiceKindConnectProxy, - Service: "grpc-proxy", - ProxyID: structs.NewServiceID("grpc-proxy", nil), - Address: "1.2.3.4", - Port: 8080, - Proxy: structs.ConnectProxyConfig{ - DestinationServiceName: "grpc", - DestinationServiceID: "grpc", - LocalServicePort: 8080, - Config: map[string]interface{}{ - "protocol": "grpc", - }, - Expose: structs.ExposeConfig{ - Checks: false, - Paths: []structs.ExposePath{ - { - LocalPathPort: 8090, - Path: "/healthz", - ListenerPort: 21500, - Protocol: "http", - }, - }, - }, - }, - Datacenter: "dc1", - } -} - -func TestConfigSnapshotIngress_MultipleListenersDuplicateService(t testing.T) *ConfigSnapshot { - snap := TestConfigSnapshotIngress_HTTPMultipleServices(t) - - snap.IngressGateway.Upstreams = map[IngressListenerKey]structs.Upstreams{ - {Protocol: "http", Port: 8080}: { - { - DestinationName: "foo", - LocalBindPort: 8080, - }, - { - DestinationName: "bar", - LocalBindPort: 8080, - }, - }, - {Protocol: "http", Port: 443}: { - { - DestinationName: "foo", - LocalBindPort: 443, - }, - }, + s, err := newServiceInstanceFromNodeService(ns, token) + if err != nil { + t.Fatalf("err: %v", err) + return 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) - - snap.IngressGateway.DiscoveryChain = map[UpstreamID]*structs.CompiledDiscoveryChain{ - UpstreamIDFromString("foo"): fooChain, - UpstreamIDFromString("bar"): barChain, + handler, err := newKindHandler(config, s, nil) // NOTE: nil channel + if err != nil { + t.Fatalf("err: %v", err) + return nil } - return snap + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + snap, err := handler.initialize(ctx) + if err != nil { + t.Fatalf("err: %v", err) + return nil + } + + for _, u := range updates { + if err := handler.handleUpdate(ctx, u, &snap); err != nil { + t.Fatalf("Failed to handle update from watch %q: %v", u.CorrelationID, err) + return nil + } + } + return &snap } -func httpMatch(http *structs.ServiceRouteHTTPMatch) *structs.ServiceRouteMatch { - return &structs.ServiceRouteMatch{HTTP: http} +func testSpliceEvents(base, extra []cache.UpdateEvent) []cache.UpdateEvent { + if len(extra) == 0 { + return base + } + var ( + hasExtra = make(map[string]cache.UpdateEvent) + completeExtra = make(map[string]struct{}) + + allEvents []cache.UpdateEvent + ) + + for _, e := range extra { + hasExtra[e.CorrelationID] = e + } + + // Override base events with extras if they share the same correlationID, + // then put the rest of the extras at the end. + for _, e := range base { + if extraEvt, ok := hasExtra[e.CorrelationID]; ok { + if extraEvt.Result != nil { // nil results are tombstones + allEvents = append(allEvents, extraEvt) + } + completeExtra[e.CorrelationID] = struct{}{} + } else { + allEvents = append(allEvents, e) + } + } + for _, e := range extra { + if _, ok := completeExtra[e.CorrelationID]; !ok { + allEvents = append(allEvents, e) + } + } + return allEvents } -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} + +func testSpliceNodeServiceFunc(prev, next func(ns *structs.NodeService)) func(ns *structs.NodeService) { + return func(ns *structs.NodeService) { + if prev != nil { + prev(ns) + } + next(ns) + } } // ControllableCacheType is a cache.Type that simulates a typical blocking RPC diff --git a/agent/proxycfg/testing_connect_proxy.go b/agent/proxycfg/testing_connect_proxy.go new file mode 100644 index 0000000000..b23f025859 --- /dev/null +++ b/agent/proxycfg/testing_connect_proxy.go @@ -0,0 +1,271 @@ +package proxycfg + +import ( + "github.com/mitchellh/go-testing-interface" + "github.com/stretchr/testify/assert" + + "github.com/hashicorp/consul/agent/cache" + "github.com/hashicorp/consul/agent/connect" + "github.com/hashicorp/consul/agent/consul/discoverychain" + "github.com/hashicorp/consul/agent/structs" +) + +// TestConfigSnapshot returns a fully populated snapshot +func TestConfigSnapshot(t testing.T, nsFn func(ns *structs.NodeService), extraUpdates []cache.UpdateEvent) *ConfigSnapshot { + roots, leaf := TestCerts(t) + + // no entries implies we'll get a default chain + dbChain := discoverychain.TestCompileConfigEntries(t, "db", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + assert.True(t, dbChain.IsDefault()) + + var ( + upstreams = structs.TestUpstreams(t) + dbUpstream = upstreams[0] + geoUpstream = upstreams[1] + + dbUID = NewUpstreamID(&dbUpstream) + geoUID = NewUpstreamID(&geoUpstream) + + webSN = structs.ServiceIDString("web", nil) + ) + + baseEvents := []cache.UpdateEvent{ + { + CorrelationID: rootsWatchID, + Result: roots, + }, + { + CorrelationID: leafWatchID, + Result: leaf, + }, + { + CorrelationID: intentionsWatchID, + Result: &structs.IndexedIntentionMatches{ + Matches: []structs.Intentions{ + nil, // no intentions defined + }, + }, + }, + { + CorrelationID: svcChecksWatchIDPrefix + webSN, + Result: []structs.CheckType{}, + }, + { + CorrelationID: "upstream:" + geoUID.String(), + Result: &structs.PreparedQueryExecuteResponse{ + Nodes: TestPreparedQueryNodes(t, "geo-cache"), + }, + }, + { + CorrelationID: "discovery-chain:" + dbUID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: dbChain, + }, + }, + { + CorrelationID: "upstream-target:" + dbChain.ID() + ":" + dbUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "db"), + }, + }, + } + + return testConfigSnapshotFixture(t, &structs.NodeService{ + Kind: structs.ServiceKindConnectProxy, + Service: "web-sidecar-proxy", + Port: 9999, + Proxy: structs.ConnectProxyConfig{ + DestinationServiceID: "web", + DestinationServiceName: "web", + LocalServiceAddress: "127.0.0.1", + LocalServicePort: 8080, + Config: map[string]interface{}{ + "foo": "bar", + }, + Upstreams: upstreams, + }, + Meta: nil, + TaggedAddresses: nil, + }, nsFn, nil, testSpliceEvents(baseEvents, extraUpdates)) +} + +// TestConfigSnapshotDiscoveryChain returns a fully populated snapshot using a discovery chain +func TestConfigSnapshotDiscoveryChain( + t testing.T, + variation string, + nsFn func(ns *structs.NodeService), + extraUpdates []cache.UpdateEvent, + additionalEntries ...structs.ConfigEntry, +) *ConfigSnapshot { + roots, leaf := TestCerts(t) + + var ( + upstreams = structs.TestUpstreams(t) + geoUpstream = upstreams[1] + + geoUID = NewUpstreamID(&geoUpstream) + + webSN = structs.ServiceIDString("web", nil) + ) + + baseEvents := testSpliceEvents([]cache.UpdateEvent{ + { + CorrelationID: rootsWatchID, + Result: roots, + }, + { + CorrelationID: leafWatchID, + Result: leaf, + }, + { + CorrelationID: intentionsWatchID, + Result: &structs.IndexedIntentionMatches{ + Matches: []structs.Intentions{ + nil, // no intentions defined + }, + }, + }, + { + CorrelationID: svcChecksWatchIDPrefix + webSN, + Result: []structs.CheckType{}, + }, + { + CorrelationID: "upstream:" + geoUID.String(), + Result: &structs.PreparedQueryExecuteResponse{ + Nodes: TestPreparedQueryNodes(t, "geo-cache"), + }, + }, + }, setupTestVariationConfigEntriesAndSnapshot( + t, variation, upstreams, additionalEntries..., + )) + + return testConfigSnapshotFixture(t, &structs.NodeService{ + Kind: structs.ServiceKindConnectProxy, + Service: "web-sidecar-proxy", + Port: 9999, + Proxy: structs.ConnectProxyConfig{ + DestinationServiceID: "web", + DestinationServiceName: "web", + LocalServiceAddress: "127.0.0.1", + LocalServicePort: 8080, + Config: map[string]interface{}{ + "foo": "bar", + }, + Upstreams: upstreams, + }, + Meta: nil, + TaggedAddresses: nil, + }, nsFn, nil, testSpliceEvents(baseEvents, extraUpdates)) +} + +func TestConfigSnapshotExposeConfig(t testing.T, nsFn func(ns *structs.NodeService)) *ConfigSnapshot { + roots, leaf := TestCerts(t) + + var ( + webSN = structs.ServiceIDString("web", nil) + ) + + baseEvents := []cache.UpdateEvent{ + { + CorrelationID: rootsWatchID, + Result: roots, + }, + { + CorrelationID: leafWatchID, Result: leaf, + }, + { + CorrelationID: intentionsWatchID, + Result: &structs.IndexedIntentionMatches{ + Matches: []structs.Intentions{ + nil, // no intentions defined + }, + }, + }, + { + CorrelationID: svcChecksWatchIDPrefix + webSN, + Result: []structs.CheckType{}, + }, + } + + return testConfigSnapshotFixture(t, &structs.NodeService{ + Kind: structs.ServiceKindConnectProxy, + Service: "web-sidecar-proxy", + Address: "1.2.3.4", + Port: 8080, + Proxy: structs.ConnectProxyConfig{ + DestinationServiceID: "web", + DestinationServiceName: "web", + LocalServicePort: 8080, + Expose: structs.ExposeConfig{ + Checks: false, + Paths: []structs.ExposePath{ + { + LocalPathPort: 8080, + Path: "/health1", + ListenerPort: 21500, + }, + { + LocalPathPort: 8080, + Path: "/health2", + ListenerPort: 21501, + }, + }, + }, + }, + Meta: nil, + TaggedAddresses: nil, + }, nsFn, nil, baseEvents) +} + +func TestConfigSnapshotGRPCExposeHTTP1(t testing.T) *ConfigSnapshot { + roots, leaf := TestCerts(t) + + return testConfigSnapshotFixture(t, &structs.NodeService{ + Kind: structs.ServiceKindConnectProxy, + Service: "grpc-proxy", + Address: "1.2.3.4", + Port: 8080, + Proxy: structs.ConnectProxyConfig{ + DestinationServiceName: "grpc", + DestinationServiceID: "grpc", + LocalServicePort: 8080, + Config: map[string]interface{}{ + "protocol": "grpc", + }, + Expose: structs.ExposeConfig{ + Checks: false, + Paths: []structs.ExposePath{ + { + LocalPathPort: 8090, + Path: "/healthz", + ListenerPort: 21500, + Protocol: "http", + }, + }, + }, + }, + Meta: nil, + TaggedAddresses: nil, + }, nil, nil, []cache.UpdateEvent{ + { + CorrelationID: rootsWatchID, + Result: roots, + }, + { + CorrelationID: leafWatchID, + Result: leaf, + }, + { + CorrelationID: intentionsWatchID, + Result: &structs.IndexedIntentionMatches{ + Matches: []structs.Intentions{ + nil, // no intentions defined + }, + }, + }, + { + CorrelationID: svcChecksWatchIDPrefix + structs.ServiceIDString("grpc", nil), + Result: []structs.CheckType{}, + }, + }) +} diff --git a/agent/proxycfg/testing_ingress_gateway.go b/agent/proxycfg/testing_ingress_gateway.go new file mode 100644 index 0000000000..c3360fd962 --- /dev/null +++ b/agent/proxycfg/testing_ingress_gateway.go @@ -0,0 +1,1500 @@ +package proxycfg + +import ( + "time" + + "github.com/mitchellh/go-testing-interface" + "github.com/stretchr/testify/require" + + "github.com/hashicorp/consul/agent/cache" + "github.com/hashicorp/consul/agent/connect" + "github.com/hashicorp/consul/agent/consul/discoverychain" + "github.com/hashicorp/consul/agent/structs" + "github.com/hashicorp/consul/types" +) + +func TestConfigSnapshotIngressGateway( + t testing.T, + populateServices bool, + protocol string, + variation string, + nsFn func(ns *structs.NodeService), + configFn func(entry *structs.IngressGatewayConfigEntry), + extraUpdates []cache.UpdateEvent, + additionalEntries ...structs.ConfigEntry, +) *ConfigSnapshot { + roots, placeholderLeaf := TestCerts(t) + + entry := &structs.IngressGatewayConfigEntry{ + Kind: structs.IngressGateway, + Name: "ingress-gateway", + } + + if populateServices { + entry.Listeners = []structs.IngressListener{ + { + Port: 8080, + Protocol: protocol, + Services: []structs.IngressService{ + {Name: "db"}, + }, + }, + } + } + + if configFn != nil { + configFn(entry) + } + + baseEvents := []cache.UpdateEvent{ + { + CorrelationID: rootsWatchID, + Result: roots, + }, + { + CorrelationID: gatewayConfigWatchID, + Result: &structs.ConfigEntryResponse{ + Entry: entry, + }, + }, + { + CorrelationID: leafWatchID, + Result: placeholderLeaf, // TODO(rb): should this be generated differently? + }, + { + CorrelationID: gatewayServicesWatchID, + Result: &structs.IndexedGatewayServices{ + Services: nil, + }, + }, + } + + if populateServices { + baseEvents = testSpliceEvents(baseEvents, []cache.UpdateEvent{{ + CorrelationID: gatewayServicesWatchID, + Result: &structs.IndexedGatewayServices{ + Services: []*structs.GatewayService{ + { + Service: structs.NewServiceName("db", nil), + Port: 8080, + Hosts: nil, + Protocol: protocol, + }, + }, + }, + }}) + + upstreams := structs.TestUpstreams(t) + upstreams = structs.Upstreams{upstreams[0]} // just keep 'db' + + baseEvents = testSpliceEvents(baseEvents, setupTestVariationConfigEntriesAndSnapshot( + t, variation, upstreams, additionalEntries..., + )) + } + + return testConfigSnapshotFixture(t, &structs.NodeService{ + Kind: structs.ServiceKindIngressGateway, + Service: "ingress-gateway", + Port: 9999, + Address: "1.2.3.4", + Meta: nil, + TaggedAddresses: nil, + }, nsFn, nil, testSpliceEvents(baseEvents, extraUpdates)) +} + +func TestConfigSnapshotIngressGatewaySDS_GatewayLevel_MixedTLS(t testing.T) *ConfigSnapshot { + secureUID := UpstreamIDFromString("secure") + secureChain := discoverychain.TestCompileConfigEntries( + t, + "secure", + "default", + "default", + "dc1", + connect.TestClusterID+".consul", + nil, + ) + + insecureUID := UpstreamIDFromString("insecure") + insecureChain := discoverychain.TestCompileConfigEntries( + t, + "insecure", + "default", + "default", + "dc1", + connect.TestClusterID+".consul", + nil, + ) + + return TestConfigSnapshotIngressGateway(t, false, "tcp", "default", nil, func(entry *structs.IngressGatewayConfigEntry) { + // Disable GW-level defaults so we can mix TLS and non-TLS listeners + entry.TLS = structs.GatewayTLSConfig{ + SDS: nil, + } + entry.Listeners = []structs.IngressListener{ + // Setup two TCP listeners, one with and one without SDS config + { + Port: 8080, + Protocol: "tcp", + TLS: &structs.GatewayTLSConfig{ + SDS: &structs.GatewayTLSSDSConfig{ + ClusterName: "listener-sds-cluster", + CertResource: "listener-cert", + }, + }, + Services: []structs.IngressService{ + {Name: "secure"}, + }, + }, + { + Port: 9090, + Protocol: "tcp", + TLS: nil, + Services: []structs.IngressService{ + {Name: "insecure"}, + }, + }, + } + }, []cache.UpdateEvent{ + { + CorrelationID: gatewayServicesWatchID, + Result: &structs.IndexedGatewayServices{ + Services: []*structs.GatewayService{ + { + Service: structs.NewServiceName("secure", nil), + Port: 8080, + Protocol: "tcp", + }, + { + Service: structs.NewServiceName("insecure", nil), + Port: 9090, + Protocol: "tcp", + }, + }, + }, + }, + { + CorrelationID: "discovery-chain:" + secureUID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: secureChain, + }, + }, + { + CorrelationID: "upstream-target:" + secureChain.ID() + ":" + secureUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "secure"), + }, + }, + { + CorrelationID: "discovery-chain:" + insecureUID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: insecureChain, + }, + }, + { + CorrelationID: "upstream-target:" + insecureChain.ID() + ":" + insecureUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "insecure"), + }, + }, + }) +} + +func TestConfigSnapshotIngressGatewaySDS_GatewayLevel(t testing.T) *ConfigSnapshot { + return TestConfigSnapshotIngressGateway(t, true, "tcp", "default", nil, func(entry *structs.IngressGatewayConfigEntry) { + entry.TLS = structs.GatewayTLSConfig{ + SDS: &structs.GatewayTLSSDSConfig{ + ClusterName: "sds-cluster", + CertResource: "cert-resource", + }, + } + }, nil) +} + +func TestConfigSnapshotIngressGatewaySDS_GatewayAndListenerLevel(t testing.T) *ConfigSnapshot { + return TestConfigSnapshotIngressGateway(t, true, "tcp", "default", nil, func(entry *structs.IngressGatewayConfigEntry) { + entry.TLS = structs.GatewayTLSConfig{ + SDS: &structs.GatewayTLSSDSConfig{ + ClusterName: "sds-cluster", + CertResource: "cert-resource", + }, + } + entry.Listeners[0].TLS = &structs.GatewayTLSConfig{ + SDS: &structs.GatewayTLSSDSConfig{ + // Override the cert, fall back to the cluster at gw level. We + // don't test every possible valid combination here since we + // already did that in TestResolveListenerSDSConfig. This is + // just an extra check to make sure that data is plumbed through + // correctly. + CertResource: "listener-cert", + }, + } + }, nil) +} + +func TestConfigSnapshotIngressGatewaySDS_GatewayAndListenerLevel_HTTP(t testing.T) *ConfigSnapshot { + var ( + http = structs.NewServiceName("http", nil) + httpUID = NewUpstreamIDFromServiceName(http) + httpChain = discoverychain.TestCompileConfigEntries(t, "http", "default", "default", "dc1", connect.TestClusterID+".consul", nil, + &structs.ServiceConfigEntry{ + Kind: structs.ServiceDefaults, + Name: "http", + Protocol: "http", + }) + ) + + return TestConfigSnapshotIngressGateway(t, false, "http", "default", nil, func(entry *structs.IngressGatewayConfigEntry) { + entry.TLS = structs.GatewayTLSConfig{ + SDS: &structs.GatewayTLSSDSConfig{ + ClusterName: "sds-cluster", + CertResource: "cert-resource", + }, + } + entry.Listeners = []structs.IngressListener{ + { + Port: 8080, + Protocol: "http", + Services: []structs.IngressService{ + {Name: "http"}, + }, + TLS: &structs.GatewayTLSConfig{ + SDS: &structs.GatewayTLSSDSConfig{ + // Override the cert, fall back to the cluster at gw level. We + // don't test every possible valid combination here since we + // already did that in TestResolveListenerSDSConfig. This is + // just an extra check to make sure that data is plumbed through + // correctly. + CertResource: "listener-cert", + }, + }, + }, + } + }, []cache.UpdateEvent{ + { + CorrelationID: gatewayServicesWatchID, + Result: &structs.IndexedGatewayServices{ + Services: []*structs.GatewayService{ + { + Service: http, + Port: 8080, + Protocol: "http", + }, + }, + }, + }, + { + CorrelationID: "discovery-chain:" + httpUID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: httpChain, + }, + }, + { + CorrelationID: "upstream-target:" + httpChain.ID() + ":" + httpUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "http"), + }, + }, + }) +} + +func TestConfigSnapshotIngressGatewaySDS_ServiceLevel(t testing.T) *ConfigSnapshot { + var ( + s1 = structs.NewServiceName("s1", nil) + s1UID = NewUpstreamIDFromServiceName(s1) + s1Chain = discoverychain.TestCompileConfigEntries(t, "s1", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + + s2 = structs.NewServiceName("s2", nil) + s2UID = NewUpstreamIDFromServiceName(s2) + s2Chain = discoverychain.TestCompileConfigEntries(t, "s2", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + ) + + return TestConfigSnapshotIngressGateway(t, false, "tcp", "default", nil, func(entry *structs.IngressGatewayConfigEntry) { + // Disable GW-level defaults so we can test only service-level + entry.TLS = structs.GatewayTLSConfig{ + SDS: nil, + } + entry.Listeners = []structs.IngressListener{ + // Setup http listeners, one multiple services with SDS + { + Port: 8080, + Protocol: "http", + TLS: nil, // no listener-level SDS config + Services: []structs.IngressService{ + { + Name: "s1", + Hosts: []string{"s1.example.com"}, + TLS: &structs.GatewayServiceTLSConfig{ + SDS: &structs.GatewayTLSSDSConfig{ + ClusterName: "sds-cluster-1", + CertResource: "s1.example.com-cert", + }, + }, + }, + { + Name: "s2", + Hosts: []string{"s2.example.com"}, + TLS: &structs.GatewayServiceTLSConfig{ + SDS: &structs.GatewayTLSSDSConfig{ + ClusterName: "sds-cluster-2", + CertResource: "s2.example.com-cert", + }, + }, + }, + }, + }, + } + }, []cache.UpdateEvent{ + { + CorrelationID: gatewayServicesWatchID, + Result: &structs.IndexedGatewayServices{ + Services: []*structs.GatewayService{ + { + Service: s1, + Port: 8080, + Protocol: "http", + }, + { + Service: s2, + Port: 8080, + Protocol: "http", + }, + }, + }, + }, + { + CorrelationID: "discovery-chain:" + s1UID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: s1Chain, + }, + }, + { + CorrelationID: "upstream-target:" + s1Chain.ID() + ":" + s1UID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "s1"), + }, + }, + { + CorrelationID: "discovery-chain:" + s2UID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: s2Chain, + }, + }, + { + CorrelationID: "upstream-target:" + s2Chain.ID() + ":" + s2UID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "s2"), + }, + }, + }) +} + +func TestConfigSnapshotIngressGatewaySDS_ListenerAndServiceLevel(t testing.T) *ConfigSnapshot { + var ( + s1 = structs.NewServiceName("s1", nil) + s1UID = NewUpstreamIDFromServiceName(s1) + s1Chain = discoverychain.TestCompileConfigEntries(t, "s1", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + + s2 = structs.NewServiceName("s2", nil) + s2UID = NewUpstreamIDFromServiceName(s2) + s2Chain = discoverychain.TestCompileConfigEntries(t, "s2", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + ) + + return TestConfigSnapshotIngressGateway(t, false, "tcp", "default", nil, func(entry *structs.IngressGatewayConfigEntry) { + // Disable GW-level defaults so we can test only service-level + entry.TLS = structs.GatewayTLSConfig{ + SDS: nil, + } + entry.Listeners = []structs.IngressListener{ + // Setup http listeners, one multiple services with SDS + { + Port: 8080, + Protocol: "http", + TLS: &structs.GatewayTLSConfig{ + SDS: &structs.GatewayTLSSDSConfig{ + ClusterName: "sds-cluster-2", + CertResource: "*.example.com-cert", + }, + }, + Services: []structs.IngressService{ + { + Name: "s1", + Hosts: []string{"s1.example.com"}, + TLS: &structs.GatewayServiceTLSConfig{ + SDS: &structs.GatewayTLSSDSConfig{ + ClusterName: "sds-cluster-1", + CertResource: "s1.example.com-cert", + }, + }, + }, + { + Name: "s2", + // s2 uses the default listener cert + }, + }, + }, + } + }, []cache.UpdateEvent{ + { + CorrelationID: gatewayServicesWatchID, + Result: &structs.IndexedGatewayServices{ + Services: []*structs.GatewayService{ + { + Service: s1, + Port: 8080, + Protocol: "http", + }, + { + Service: s2, + Port: 8080, + Protocol: "http", + }, + }, + }, + }, + { + CorrelationID: "discovery-chain:" + s1UID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: s1Chain, + }, + }, + { + CorrelationID: "upstream-target:" + s1Chain.ID() + ":" + s1UID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "s1"), + }, + }, + { + CorrelationID: "discovery-chain:" + s2UID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: s2Chain, + }, + }, + { + CorrelationID: "upstream-target:" + s2Chain.ID() + ":" + s2UID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "s2"), + }, + }, + }) +} + +func TestConfigSnapshotIngressGatewaySDS_MixedNoTLS(t testing.T) *ConfigSnapshot { + var ( + s1 = structs.NewServiceName("s1", nil) + s1UID = NewUpstreamIDFromServiceName(s1) + s1Chain = discoverychain.TestCompileConfigEntries(t, "s1", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + + s2 = structs.NewServiceName("s2", nil) + s2UID = NewUpstreamIDFromServiceName(s2) + s2Chain = discoverychain.TestCompileConfigEntries(t, "s2", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + ) + + return TestConfigSnapshotIngressGateway(t, false, "tcp", "default", nil, func(entry *structs.IngressGatewayConfigEntry) { + // Disable GW-level defaults so we can test only service-level + entry.TLS = structs.GatewayTLSConfig{ + SDS: nil, + } + entry.Listeners = []structs.IngressListener{ + // Setup http listeners, one multiple services with SDS + { + Port: 8080, + Protocol: "http", + TLS: nil, // No listener level TLS setup either + Services: []structs.IngressService{ + { + Name: "s1", + Hosts: []string{"s1.example.com"}, + TLS: &structs.GatewayServiceTLSConfig{ + SDS: &structs.GatewayTLSSDSConfig{ + ClusterName: "sds-cluster-1", + CertResource: "s1.example.com-cert", + }, + }, + }, + { + Name: "s2", + // s2 has no SDS config so should be non-TLS + }, + }, + }, + } + }, []cache.UpdateEvent{ + { + CorrelationID: gatewayServicesWatchID, + Result: &structs.IndexedGatewayServices{ + Services: []*structs.GatewayService{ + { + Service: s1, + Port: 8080, + Protocol: "http", + }, + { + Service: s2, + Port: 8080, + Protocol: "http", + }, + }, + }, + }, + { + CorrelationID: "discovery-chain:" + s1UID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: s1Chain, + }, + }, + { + CorrelationID: "upstream-target:" + s1Chain.ID() + ":" + s1UID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "s1"), + }, + }, + { + CorrelationID: "discovery-chain:" + s2UID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: s2Chain, + }, + }, + { + CorrelationID: "upstream-target:" + s2Chain.ID() + ":" + s2UID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "s2"), + }, + }, + }) +} + +func TestConfigSnapshotIngressGateway_MixedListeners(t testing.T) *ConfigSnapshot { + var ( + s1 = structs.NewServiceName("s1", nil) + s1UID = NewUpstreamIDFromServiceName(s1) + s1Chain = discoverychain.TestCompileConfigEntries(t, "s1", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + + s2 = structs.NewServiceName("s2", nil) + s2UID = NewUpstreamIDFromServiceName(s2) + s2Chain = discoverychain.TestCompileConfigEntries(t, "s2", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + ) + + return TestConfigSnapshotIngressGateway(t, false, "tcp", "default", nil, func(entry *structs.IngressGatewayConfigEntry) { + entry.TLS = structs.GatewayTLSConfig{ + Enabled: false, // No Gateway-level built-in TLS + SDS: nil, // Undo gateway-level SDS + } + entry.Listeners = []structs.IngressListener{ + // One listener has built-in TLS, one doesn't + { + Port: 8080, + Protocol: "http", + TLS: &structs.GatewayTLSConfig{ + Enabled: true, // built-in TLS enabled + }, + Services: []structs.IngressService{ + {Name: "s1"}, + }, + }, + { + Port: 9090, + Protocol: "http", + TLS: nil, // No TLS enabled + Services: []structs.IngressService{ + {Name: "s2"}, + }, + }, + } + }, []cache.UpdateEvent{ + { + CorrelationID: gatewayServicesWatchID, + Result: &structs.IndexedGatewayServices{ + Services: []*structs.GatewayService{ + { + Service: s1, + Port: 8080, + Protocol: "http", + }, + { + Service: s2, + Port: 9090, + Protocol: "http", + }, + }, + }, + }, + { + CorrelationID: "discovery-chain:" + s1UID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: s1Chain, + }, + }, + { + CorrelationID: "upstream-target:" + s1Chain.ID() + ":" + s1UID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "s1"), + }, + }, + { + CorrelationID: "discovery-chain:" + s2UID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: s2Chain, + }, + }, + { + CorrelationID: "upstream-target:" + s2Chain.ID() + ":" + s2UID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "s2"), + }, + }, + }) +} + +func TestConfigSnapshotIngress_HTTPMultipleServices(t testing.T) *ConfigSnapshot { + // We do not add baz/qux here so that we test the chain.IsDefault() case + entries := []structs.ConfigEntry{ + &structs.ProxyConfigEntry{ + Kind: structs.ProxyDefaults, + Name: structs.ProxyConfigGlobal, + Config: map[string]interface{}{ + "protocol": "http", + }, + }, + &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "foo", + ConnectTimeout: 22 * time.Second, + }, + &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "bar", + ConnectTimeout: 22 * time.Second, + }, + } + + var ( + foo = structs.NewServiceName("foo", nil) + fooUID = NewUpstreamIDFromServiceName(foo) + fooChain = discoverychain.TestCompileConfigEntries(t, "foo", "default", "default", "dc1", connect.TestClusterID+".consul", nil, entries...) + + bar = structs.NewServiceName("bar", nil) + barUID = NewUpstreamIDFromServiceName(bar) + barChain = discoverychain.TestCompileConfigEntries(t, "bar", "default", "default", "dc1", connect.TestClusterID+".consul", nil, entries...) + + baz = structs.NewServiceName("baz", nil) + bazUID = NewUpstreamIDFromServiceName(baz) + bazChain = discoverychain.TestCompileConfigEntries(t, "baz", "default", "default", "dc1", connect.TestClusterID+".consul", nil, entries...) + + qux = structs.NewServiceName("qux", nil) + quxUID = NewUpstreamIDFromServiceName(qux) + quxChain = discoverychain.TestCompileConfigEntries(t, "qux", "default", "default", "dc1", connect.TestClusterID+".consul", nil, entries...) + ) + + require.False(t, fooChain.IsDefault()) + require.False(t, barChain.IsDefault()) + require.True(t, bazChain.IsDefault()) + require.True(t, quxChain.IsDefault()) + + return TestConfigSnapshotIngressGateway(t, false, "http", "default", nil, func(entry *structs.IngressGatewayConfigEntry) { + entry.Listeners = []structs.IngressListener{ + { + Port: 8080, + Protocol: "http", + Services: []structs.IngressService{ + { + Name: "foo", + Hosts: []string{ + "test1.example.com", + "test2.example.com", + "test2.example.com:8080", + }, + }, + {Name: "bar"}, + }, + }, + { + Port: 443, + Protocol: "http", + Services: []structs.IngressService{ + {Name: "baz"}, + {Name: "qux"}, + }, + }, + } + }, []cache.UpdateEvent{ + { + CorrelationID: gatewayServicesWatchID, + Result: &structs.IndexedGatewayServices{ + Services: []*structs.GatewayService{ + { + Service: foo, + Port: 8080, + Protocol: "http", + Hosts: []string{ + "test1.example.com", + "test2.example.com", + "test2.example.com:8080", + }, + }, + { + Service: bar, + Port: 8080, + Protocol: "http", + }, + { + Service: baz, + Port: 443, + Protocol: "http", + }, + { + Service: qux, + Port: 443, + Protocol: "http", + }, + }, + }, + }, + { + CorrelationID: "discovery-chain:" + fooUID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: fooChain, + }, + }, + { + CorrelationID: "upstream-target:" + fooChain.ID() + ":" + fooUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "foo"), + }, + }, + { + CorrelationID: "discovery-chain:" + barUID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: barChain, + }, + }, + { + CorrelationID: "upstream-target:" + barChain.ID() + ":" + barUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "bar"), + }, + }, + { + CorrelationID: "discovery-chain:" + bazUID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: bazChain, + }, + }, + { + CorrelationID: "upstream-target:" + bazChain.ID() + ":" + bazUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "baz"), + }, + }, + { + CorrelationID: "discovery-chain:" + quxUID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: quxChain, + }, + }, + { + CorrelationID: "upstream-target:" + quxChain.ID() + ":" + quxUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "qux"), + }, + }, + }) +} + +func TestConfigSnapshotIngress_MultipleListenersDuplicateService(t testing.T) *ConfigSnapshot { + var ( + foo = structs.NewServiceName("foo", nil) + fooUID = NewUpstreamIDFromServiceName(foo) + fooChain = discoverychain.TestCompileConfigEntries(t, "foo", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + + bar = structs.NewServiceName("bar", nil) + barUID = NewUpstreamIDFromServiceName(bar) + barChain = discoverychain.TestCompileConfigEntries(t, "bar", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + ) + + return TestConfigSnapshotIngressGateway(t, false, "http", "default", nil, func(entry *structs.IngressGatewayConfigEntry) { + entry.Listeners = []structs.IngressListener{ + { + Port: 8080, + Protocol: "http", + Services: []structs.IngressService{ + {Name: "foo"}, + {Name: "bar"}, + }, + }, + { + Port: 443, + Protocol: "http", + Services: []structs.IngressService{ + {Name: "foo"}, + }, + }, + } + }, []cache.UpdateEvent{ + { + CorrelationID: gatewayServicesWatchID, + Result: &structs.IndexedGatewayServices{ + Services: []*structs.GatewayService{ + { + Service: foo, + Port: 8080, + Protocol: "http", + }, + { + Service: bar, + Port: 8080, + Protocol: "http", + }, + { + Service: foo, + Port: 443, + Protocol: "http", + }, + }, + }, + }, + { + CorrelationID: "discovery-chain:" + fooUID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: fooChain, + }, + }, + { + CorrelationID: "upstream-target:" + fooChain.ID() + ":" + fooUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "foo"), + }, + }, + { + CorrelationID: "discovery-chain:" + barUID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: barChain, + }, + }, + { + CorrelationID: "upstream-target:" + barChain.ID() + ":" + barUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodesAlternate(t), + }, + }, + }) +} + +func TestConfigSnapshotIngressGatewayWithChain( + t testing.T, + variant string, + webEntMeta, fooEntMeta *structs.EnterpriseMeta, +) *ConfigSnapshot { + if webEntMeta == nil { + webEntMeta = &structs.EnterpriseMeta{} + } + if fooEntMeta == nil { + fooEntMeta = &structs.EnterpriseMeta{} + } + + var ( + updates []cache.UpdateEvent + configFn func(entry *structs.IngressGatewayConfigEntry) + + populateServices bool + useSDS bool + listenerSDS, webSDS, fooSDS, wildcard bool + ) + switch variant { + case "router-header-manip": + configFn = func(entry *structs.IngressGatewayConfigEntry) { + entry.Listeners = []structs.IngressListener{ + { + Port: 8080, + Protocol: "http", + Services: []structs.IngressService{ + { + Name: "db", + RequestHeaders: &structs.HTTPHeaderModifiers{ + Add: map[string]string{ + "foo": "bar", + }, + Set: map[string]string{ + "bar": "baz", + }, + Remove: []string{"qux"}, + }, + ResponseHeaders: &structs.HTTPHeaderModifiers{ + Add: map[string]string{ + "foo": "bar", + }, + Set: map[string]string{ + "bar": "baz", + }, + Remove: []string{"qux"}, + }, + }, + }, + }, + } + } + populateServices = true + case "sds-listener-level": + // Listener-level SDS means all services share the default route. + useSDS = true + listenerSDS = true + case "sds-listener-level-wildcard": + // Listener-level SDS means all services share the default route. + useSDS = true + listenerSDS = true + wildcard = true + case "sds-service-level": + // Services should get separate routes and no default since they all + // have custom certs. + useSDS = true + webSDS = true + fooSDS = true + case "sds-service-level-mixed-tls": + // Web needs a separate route as it has custom filter chain but foo + // should use default route for listener. + useSDS = true + webSDS = true + default: + t.Fatalf("unknown variant %q", variant) + return nil + } + + if useSDS { + webUpstream := structs.Upstream{ + DestinationName: "web", + // We use empty not default here because of the way upstream identifiers + // vary between OSS and Enterprise currently causing test conflicts. In + // real life `proxycfg` always sets ingress upstream namespaces to + // `NamespaceOrDefault` which shouldn't matter because we should be + // consistent within a single binary it's just inconvenient if OSS and + // enterprise tests generate different output. + DestinationNamespace: webEntMeta.NamespaceOrEmpty(), + DestinationPartition: webEntMeta.PartitionOrEmpty(), + LocalBindPort: 9191, + IngressHosts: []string{ + "www.example.com", + }, + } + fooUpstream := structs.Upstream{ + DestinationName: "foo", + DestinationNamespace: fooEntMeta.NamespaceOrEmpty(), + DestinationPartition: fooEntMeta.PartitionOrEmpty(), + LocalBindPort: 9191, + IngressHosts: []string{ + "foo.example.com", + }, + } + + var ( + web = structs.NewServiceName("web", webEntMeta) + webUID = NewUpstreamID(&webUpstream) + + foo = structs.NewServiceName("foo", fooEntMeta) + fooUID = NewUpstreamID(&fooUpstream) + ) + + configFn = func(entry *structs.IngressGatewayConfigEntry) { + entry.TLS.SDS = nil + il := structs.IngressListener{ + Port: 9191, + Protocol: "http", + Services: []structs.IngressService{ + { + Name: "web", + Hosts: []string{"www.example.com"}, + EnterpriseMeta: *webEntMeta, + }, + { + Name: "foo", + Hosts: []string{"foo.example.com"}, + EnterpriseMeta: *fooEntMeta, + }, + }, + } + + // Now set the appropriate SDS configs + if listenerSDS { + il.TLS = &structs.GatewayTLSConfig{ + SDS: &structs.GatewayTLSSDSConfig{ + ClusterName: "listener-cluster", + CertResource: "listener-cert", + }, + } + } + if webSDS { + il.Services[0].TLS = &structs.GatewayServiceTLSConfig{ + SDS: &structs.GatewayTLSSDSConfig{ + ClusterName: "web-cluster", + CertResource: "www-cert", + }, + } + } + if fooSDS { + il.Services[1].TLS = &structs.GatewayServiceTLSConfig{ + SDS: &structs.GatewayTLSSDSConfig{ + ClusterName: "foo-cluster", + CertResource: "foo-cert", + }, + } + } + if wildcard { + // undo all that and set just a single wildcard config with no TLS to test + // the lookup path where we have to compare an actual resolved upstream to + // a wildcard config. + il.Services = []structs.IngressService{ + { + Name: "*", + }, + } + } + entry.Listeners = []structs.IngressListener{il} + } + + if wildcard { + // We also don't support user-specified hosts with wildcard so remove + // those from the upstreams. + webUpstream.IngressHosts = nil + fooUpstream.IngressHosts = nil + } + + entries := []structs.ConfigEntry{ + &structs.ProxyConfigEntry{ + Kind: structs.ProxyDefaults, + Name: structs.ProxyConfigGlobal, + Config: map[string]interface{}{ + "protocol": "http", + }, + }, + &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "web", + EnterpriseMeta: *webEntMeta, + ConnectTimeout: 22 * time.Second, + }, + &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "foo", + EnterpriseMeta: *fooEntMeta, + ConnectTimeout: 22 * time.Second, + }, + } + + webChain := discoverychain.TestCompileConfigEntries(t, "web", + webEntMeta.NamespaceOrDefault(), + webEntMeta.PartitionOrDefault(), "dc1", + connect.TestClusterID+".consul", nil, entries...) + fooChain := discoverychain.TestCompileConfigEntries(t, "foo", + fooEntMeta.NamespaceOrDefault(), + fooEntMeta.PartitionOrDefault(), "dc1", + connect.TestClusterID+".consul", nil, entries...) + + updates = []cache.UpdateEvent{ + { + CorrelationID: gatewayServicesWatchID, + Result: &structs.IndexedGatewayServices{ + Services: []*structs.GatewayService{ + { + Service: web, + Port: 9191, + Protocol: "http", + Hosts: webUpstream.IngressHosts, + }, + { + Service: foo, + Port: 9191, + Protocol: "http", + Hosts: fooUpstream.IngressHosts, + }, + }, + }, + }, + { + CorrelationID: "discovery-chain:" + webUID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: webChain, + }, + }, + { + CorrelationID: "discovery-chain:" + fooUID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: fooChain, + }, + }, + { + CorrelationID: "upstream-target:" + webChain.ID() + ":" + webUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "web"), + }, + }, + { + CorrelationID: "upstream-target:" + fooChain.ID() + ":" + fooUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "foo"), + }, + }, + } + } + + return TestConfigSnapshotIngressGateway(t, populateServices, "http", "chain-and-router", nil, configFn, updates) +} + +func TestConfigSnapshotIngressGateway_TLSMinVersionListenersGatewayDefaults(t testing.T) *ConfigSnapshot { + var ( + s1 = structs.NewServiceName("s1", nil) + s1UID = NewUpstreamIDFromServiceName(s1) + s1Chain = discoverychain.TestCompileConfigEntries(t, "s1", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + + s2 = structs.NewServiceName("s2", nil) + s2UID = NewUpstreamIDFromServiceName(s2) + s2Chain = discoverychain.TestCompileConfigEntries(t, "s2", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + + s3 = structs.NewServiceName("s3", nil) + s3UID = NewUpstreamIDFromServiceName(s3) + s3Chain = discoverychain.TestCompileConfigEntries(t, "s3", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + + s4 = structs.NewServiceName("s4", nil) + s4UID = NewUpstreamIDFromServiceName(s4) + s4Chain = discoverychain.TestCompileConfigEntries(t, "s4", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + ) + + return TestConfigSnapshotIngressGateway(t, true, "tcp", "default", nil, + func(entry *structs.IngressGatewayConfigEntry) { + entry.TLS.Enabled = true + entry.TLS.TLSMinVersion = types.TLSv1_2 + + // One listener disables TLS, one inherits TLS minimum version from the gateway + // config, two others set different versions + entry.Listeners = []structs.IngressListener{ + // Omits listener TLS config, should default to gateway TLS config + { + Port: 8080, + Protocol: "http", + Services: []structs.IngressService{ + {Name: "s1"}, + }, + }, + // Explicitly sets listener TLS config to nil, should default to gateway TLS config + { + Port: 8081, + Protocol: "http", + Services: []structs.IngressService{ + {Name: "s2"}, + }, + TLS: nil, + }, + // Explicitly enables TLS config, but with no listener default TLS params, + // should default to gateway TLS config + { + Port: 8082, + Protocol: "http", + Services: []structs.IngressService{ + {Name: "s3"}, + }, + TLS: &structs.GatewayTLSConfig{ + Enabled: true, + }, + }, + // Explicitly unset gateway default TLS min version in favor of proxy default + { + Port: 8083, + Protocol: "http", + Services: []structs.IngressService{ + {Name: "s3"}, + }, + TLS: &structs.GatewayTLSConfig{ + Enabled: true, + TLSMinVersion: types.TLSVersionAuto, + }, + }, + // Disables listener TLS + { + Port: 8084, + Protocol: "http", + Services: []structs.IngressService{ + {Name: "s4"}, + }, + TLS: &structs.GatewayTLSConfig{ + Enabled: false, + }, + }, + } + }, []cache.UpdateEvent{ + { + CorrelationID: gatewayServicesWatchID, + Result: &structs.IndexedGatewayServices{ + // One listener disables TLS, one inherits TLS minimum version from the gateway + // config, two others set different versions + Services: []*structs.GatewayService{ + { + Service: s1, + Port: 8080, + Protocol: "http", + }, + { + Service: s2, + Port: 8081, + Protocol: "http", + }, + { + Service: s3, + Port: 8082, + Protocol: "http", + }, + { + Service: s4, + Port: 8083, + Protocol: "http", + }, + { + Service: s4, + Port: 8084, + Protocol: "http", + }, + }, + }, + }, + { + CorrelationID: "discovery-chain:" + s1UID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: s1Chain, + }, + }, + { + CorrelationID: "discovery-chain:" + s2UID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: s2Chain, + }, + }, + { + CorrelationID: "discovery-chain:" + s3UID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: s3Chain, + }, + }, + { + CorrelationID: "discovery-chain:" + s4UID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: s4Chain, + }, + }, + { + CorrelationID: "upstream-target:" + s1Chain.ID() + ":" + s1UID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "s1"), + }, + }, + { + CorrelationID: "upstream-target:" + s2Chain.ID() + ":" + s2UID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "s2"), + }, + }, + { + CorrelationID: "upstream-target:" + s3Chain.ID() + ":" + s3UID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "s3"), + }, + }, + { + CorrelationID: "upstream-target:" + s4Chain.ID() + ":" + s4UID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "s4"), + }, + }, + }) +} + +func TestConfigSnapshotIngressGateway_SingleTLSListener(t testing.T) *ConfigSnapshot { + var ( + s1 = structs.NewServiceName("s1", nil) + s1UID = NewUpstreamIDFromServiceName(s1) + s1Chain = discoverychain.TestCompileConfigEntries(t, "s1", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + + s2 = structs.NewServiceName("s2", nil) + s2UID = NewUpstreamIDFromServiceName(s2) + s2Chain = discoverychain.TestCompileConfigEntries(t, "s2", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + ) + return TestConfigSnapshotIngressGateway(t, true, "tcp", "simple", nil, + func(entry *structs.IngressGatewayConfigEntry) { + entry.Listeners = []structs.IngressListener{ + { + Port: 8080, + Protocol: "http", + Services: []structs.IngressService{ + {Name: "s1"}, + }, + }, + { + Port: 8081, + Protocol: "http", + Services: []structs.IngressService{ + {Name: "s2"}, + }, + TLS: &structs.GatewayTLSConfig{ + Enabled: true, + TLSMinVersion: types.TLSv1_2, + }, + }, + } + }, []cache.UpdateEvent{ + { + CorrelationID: gatewayServicesWatchID, + Result: &structs.IndexedGatewayServices{ + // One listener should inherit non-TLS gateway config, another + // listener configures TLS with an explicit minimum version + Services: []*structs.GatewayService{ + { + Service: s1, + Port: 8080, + Protocol: "http", + }, + { + Service: s2, + Port: 8081, + Protocol: "http", + }, + }, + }, + }, + { + CorrelationID: "discovery-chain:" + s1UID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: s1Chain, + }, + }, + { + CorrelationID: "discovery-chain:" + s2UID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: s2Chain, + }, + }, + { + CorrelationID: "upstream-target:" + s1Chain.ID() + ":" + s1UID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "s1"), + }, + }, + { + CorrelationID: "upstream-target:" + s2Chain.ID() + ":" + s2UID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "s2"), + }, + }, + }) +} + +func TestConfigSnapshotIngressGateway_TLSMixedMinVersionListeners(t testing.T) *ConfigSnapshot { + var ( + s1 = structs.NewServiceName("s1", nil) + s1UID = NewUpstreamIDFromServiceName(s1) + s1Chain = discoverychain.TestCompileConfigEntries(t, "s1", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + + s2 = structs.NewServiceName("s2", nil) + s2UID = NewUpstreamIDFromServiceName(s2) + s2Chain = discoverychain.TestCompileConfigEntries(t, "s2", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + + s3 = structs.NewServiceName("s3", nil) + s3UID = NewUpstreamIDFromServiceName(s3) + s3Chain = discoverychain.TestCompileConfigEntries(t, "s3", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + ) + + return TestConfigSnapshotIngressGateway(t, true, "tcp", "default", nil, + func(entry *structs.IngressGatewayConfigEntry) { + entry.TLS.Enabled = true + entry.TLS.TLSMinVersion = types.TLSv1_2 + + // One listener disables TLS, one inherits TLS minimum version from the gateway + // config, two others set different versions + entry.Listeners = []structs.IngressListener{ + { + Port: 8080, + Protocol: "http", + Services: []structs.IngressService{ + {Name: "s1"}, + }, + }, + { + Port: 8081, + Protocol: "http", + Services: []structs.IngressService{ + {Name: "s2"}, + }, + TLS: &structs.GatewayTLSConfig{ + Enabled: true, + TLSMinVersion: types.TLSv1_0, + }, + }, + { + Port: 8082, + Protocol: "http", + Services: []structs.IngressService{ + {Name: "s3"}, + }, + TLS: &structs.GatewayTLSConfig{ + Enabled: true, + TLSMinVersion: types.TLSv1_3, + }, + }, + } + }, []cache.UpdateEvent{ + { + CorrelationID: gatewayServicesWatchID, + Result: &structs.IndexedGatewayServices{ + // One listener should inherit TLS minimum version from the gateway config, + // two others each set explicit TLS minimum versions + Services: []*structs.GatewayService{ + { + Service: s1, + Port: 8080, + Protocol: "http", + }, + { + Service: s2, + Port: 8081, + Protocol: "http", + }, + { + Service: s3, + Port: 8082, + Protocol: "http", + }, + }, + }, + }, + { + CorrelationID: "discovery-chain:" + s1UID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: s1Chain, + }, + }, + { + CorrelationID: "discovery-chain:" + s2UID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: s2Chain, + }, + }, + { + CorrelationID: "discovery-chain:" + s3UID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: s3Chain, + }, + }, + { + CorrelationID: "upstream-target:" + s1Chain.ID() + ":" + s1UID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "s1"), + }, + }, + { + CorrelationID: "upstream-target:" + s2Chain.ID() + ":" + s2UID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "s2"), + }, + }, + { + CorrelationID: "upstream-target:" + s3Chain.ID() + ":" + s3UID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "s3"), + }, + }, + }) +} diff --git a/agent/proxycfg/testing_mesh_gateway.go b/agent/proxycfg/testing_mesh_gateway.go new file mode 100644 index 0000000000..c374a9aad2 --- /dev/null +++ b/agent/proxycfg/testing_mesh_gateway.go @@ -0,0 +1,442 @@ +package proxycfg + +import ( + "math" + "time" + + "github.com/mitchellh/go-testing-interface" + + "github.com/hashicorp/consul/agent/cache" + "github.com/hashicorp/consul/agent/structs" +) + +func TestConfigSnapshotMeshGateway(t testing.T, variant string, nsFn func(ns *structs.NodeService), extraUpdates []cache.UpdateEvent) *ConfigSnapshot { + roots, _ := TestCerts(t) + + var ( + populateServices = true + useFederationStates = false + deleteCrossDCEntry = false + ) + + switch variant { + case "default": + 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, cache.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, cache.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, cache.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, cache.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, cache.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, cache.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, cache.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 := []cache.UpdateEvent{ + { + CorrelationID: rootsWatchID, + Result: roots, + }, + { + CorrelationID: serviceListWatchID, + Result: &structs.IndexedServiceList{ + Services: nil, + }, + }, + { + CorrelationID: serviceResolversWatchID, + Result: &structs.IndexedConfigEntries{ + Kind: structs.ServiceResolver, + Entries: nil, + }, + }, + { + CorrelationID: datacentersWatchID, + Result: &[]string{"dc1"}, + }, + } + + if populateServices || useFederationStates { + baseEvents = testSpliceEvents(baseEvents, []cache.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, []cache.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, []cache.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, []cache.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)) +} diff --git a/agent/proxycfg/testing_terminating_gateway.go b/agent/proxycfg/testing_terminating_gateway.go new file mode 100644 index 0000000000..155ae7c129 --- /dev/null +++ b/agent/proxycfg/testing_terminating_gateway.go @@ -0,0 +1,643 @@ +package proxycfg + +import ( + "github.com/mitchellh/go-testing-interface" + + "github.com/hashicorp/consul/agent/cache" + agentcache "github.com/hashicorp/consul/agent/cache" + "github.com/hashicorp/consul/agent/structs" +) + +func TestConfigSnapshotTerminatingGateway( + t testing.T, + populateServices bool, + nsFn func(ns *structs.NodeService), + extraUpdates []agentcache.UpdateEvent, +) *ConfigSnapshot { + roots, _ := TestCerts(t) + + var ( + web = structs.NewServiceName("web", nil) + api = structs.NewServiceName("api", nil) + db = structs.NewServiceName("db", nil) + cache = structs.NewServiceName("cache", nil) + ) + + baseEvents := []agentcache.UpdateEvent{ + { + CorrelationID: rootsWatchID, + Result: roots, + }, + { + CorrelationID: gatewayServicesWatchID, + Result: &structs.IndexedGatewayServices{ + Services: nil, + }, + }, + } + + if populateServices { + webNodes := TestUpstreamNodes(t, web.Name) + webNodes[0].Service.Meta = map[string]string{"version": "1"} + webNodes[1].Service.Meta = map[string]string{"version": "2"} + + apiNodes := structs.CheckServiceNodes{ + structs.CheckServiceNode{ + Node: &structs.Node{ + ID: "api", + Node: "test1", + Address: "10.10.1.1", + Datacenter: "dc1", + }, + Service: &structs.NodeService{ + Service: "api", + Address: "api.mydomain", + Port: 8081, + }, + Checks: structs.HealthChecks{ + {Status: "critical"}, + }, + }, + structs.CheckServiceNode{ + Node: &structs.Node{ + ID: "test2", + Node: "test2", + Address: "10.10.1.2", + Datacenter: "dc1", + }, + Service: &structs.NodeService{ + Service: "api", + Address: "api.altdomain", + Port: 8081, + Meta: map[string]string{ + "domain": "alt", + }, + }, + }, + structs.CheckServiceNode{ + Node: &structs.Node{ + ID: "test3", + Node: "test3", + Address: "10.10.1.3", + Datacenter: "dc1", + }, + Service: &structs.NodeService{ + Service: "api", + Address: "10.10.1.3", + Port: 8081, + }, + }, + structs.CheckServiceNode{ + Node: &structs.Node{ + ID: "test4", + Node: "test4", + Address: "10.10.1.4", + Datacenter: "dc1", + }, + Service: &structs.NodeService{ + Service: "api", + Address: "api.thirddomain", + Port: 8081, + }, + }, + } + + // Has failing instance + dbNodes := structs.CheckServiceNodes{ + structs.CheckServiceNode{ + Node: &structs.Node{ + ID: "db", + Node: "test4", + Address: "10.10.1.4", + Datacenter: "dc1", + }, + Service: &structs.NodeService{ + Service: "db", + Address: "db.mydomain", + Port: 8081, + }, + Checks: structs.HealthChecks{ + {Status: "critical"}, + }, + }, + } + + // Has passing instance but failing subset + cacheNodes := structs.CheckServiceNodes{ + { + Node: &structs.Node{ + ID: "cache", + Node: "test5", + Address: "10.10.1.5", + Datacenter: "dc1", + }, + Service: &structs.NodeService{ + Service: "cache", + Address: "cache.mydomain", + Port: 8081, + }, + }, + { + Node: &structs.Node{ + ID: "cache", + Node: "test5", + Address: "10.10.1.5", + Datacenter: "dc1", + }, + Service: &structs.NodeService{ + Service: "cache", + Address: "cache.mydomain", + Port: 8081, + Meta: map[string]string{ + "Env": "prod", + }, + }, + Checks: structs.HealthChecks{ + {Status: "critical"}, + }, + }, + } + + baseEvents = testSpliceEvents(baseEvents, []agentcache.UpdateEvent{ + { + CorrelationID: gatewayServicesWatchID, + Result: &structs.IndexedGatewayServices{ + Services: []*structs.GatewayService{ + { + Service: web, + CAFile: "ca.cert.pem", + }, + { + Service: api, + CAFile: "ca.cert.pem", + CertFile: "api.cert.pem", + KeyFile: "api.key.pem", + }, + { + Service: db, + }, + { + Service: cache, + }, + }, + }, + }, + { + CorrelationID: externalServiceIDPrefix + web.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: webNodes, + }, + }, + { + CorrelationID: externalServiceIDPrefix + api.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: apiNodes, + }, + }, + { + CorrelationID: externalServiceIDPrefix + db.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: dbNodes, + }, + }, + { + CorrelationID: externalServiceIDPrefix + cache.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: cacheNodes, + }, + }, + // ======== + // no intentions defined for these services + { + CorrelationID: serviceIntentionsIDPrefix + web.String(), + Result: &structs.IndexedIntentionMatches{ + Matches: []structs.Intentions{ + nil, + }, + }, + }, + { + CorrelationID: serviceIntentionsIDPrefix + api.String(), + Result: &structs.IndexedIntentionMatches{ + Matches: []structs.Intentions{ + nil, + }, + }, + }, + { + CorrelationID: serviceIntentionsIDPrefix + db.String(), + Result: &structs.IndexedIntentionMatches{ + Matches: []structs.Intentions{ + nil, + }, + }, + }, + { + CorrelationID: serviceIntentionsIDPrefix + cache.String(), + Result: &structs.IndexedIntentionMatches{ + Matches: []structs.Intentions{ + nil, + }, + }, + }, + // ======== + { + CorrelationID: serviceLeafIDPrefix + web.String(), + Result: &structs.IssuedCert{ + CertPEM: golden(t, "test-leaf-cert"), + PrivateKeyPEM: golden(t, "test-leaf-key"), + }, + }, + { + CorrelationID: serviceLeafIDPrefix + api.String(), + Result: &structs.IssuedCert{ + CertPEM: golden(t, "alt-test-leaf-cert"), + PrivateKeyPEM: golden(t, "alt-test-leaf-key"), + }, + }, + { + CorrelationID: serviceLeafIDPrefix + db.String(), + Result: &structs.IssuedCert{ + CertPEM: golden(t, "db-test-leaf-cert"), + PrivateKeyPEM: golden(t, "db-test-leaf-key"), + }, + }, + { + CorrelationID: serviceLeafIDPrefix + cache.String(), + Result: &structs.IssuedCert{ + CertPEM: golden(t, "cache-test-leaf-cert"), + PrivateKeyPEM: golden(t, "cache-test-leaf-key"), + }, + }, + // ======== + { + CorrelationID: serviceConfigIDPrefix + web.String(), + Result: &structs.ServiceConfigResponse{ + ProxyConfig: map[string]interface{}{"protocol": "tcp"}, + }, + }, + { + CorrelationID: serviceConfigIDPrefix + api.String(), + Result: &structs.ServiceConfigResponse{ + ProxyConfig: map[string]interface{}{"protocol": "tcp"}, + }, + }, + { + CorrelationID: serviceConfigIDPrefix + db.String(), + Result: &structs.ServiceConfigResponse{ + ProxyConfig: map[string]interface{}{"protocol": "tcp"}, + }, + }, + { + CorrelationID: serviceConfigIDPrefix + cache.String(), + Result: &structs.ServiceConfigResponse{ + ProxyConfig: map[string]interface{}{"protocol": "tcp"}, + }, + }, + // ======== + { + CorrelationID: serviceResolverIDPrefix + web.String(), + Result: &structs.IndexedConfigEntries{ + Kind: structs.ServiceResolver, + Entries: nil, + }, + }, + { + CorrelationID: serviceResolverIDPrefix + api.String(), + Result: &structs.IndexedConfigEntries{ + Kind: structs.ServiceResolver, + Entries: nil, + }, + }, + { + CorrelationID: serviceResolverIDPrefix + db.String(), + Result: &structs.IndexedConfigEntries{ + Kind: structs.ServiceResolver, + Entries: nil, + }, + }, + { + CorrelationID: serviceResolverIDPrefix + cache.String(), + Result: &structs.IndexedConfigEntries{ + Kind: structs.ServiceResolver, + Entries: nil, + }, + }, + }) + } + + return testConfigSnapshotFixture(t, &structs.NodeService{ + Kind: structs.ServiceKindTerminatingGateway, + Service: "terminating-gateway", + Address: "1.2.3.4", + Port: 8443, + TaggedAddresses: map[string]structs.ServiceAddress{ + structs.TaggedAddressWAN: { + Address: "198.18.0.1", + Port: 443, + }, + }, + }, nsFn, nil, testSpliceEvents(baseEvents, extraUpdates)) +} + +func TestConfigSnapshotTerminatingGatewayServiceSubsets(t testing.T) *ConfigSnapshot { + return testConfigSnapshotTerminatingGatewayServiceSubsets(t, false) +} +func TestConfigSnapshotTerminatingGatewayServiceSubsetsWebAndCache(t testing.T) *ConfigSnapshot { + return testConfigSnapshotTerminatingGatewayServiceSubsets(t, true) +} +func testConfigSnapshotTerminatingGatewayServiceSubsets(t testing.T, alsoAdjustCache bool) *ConfigSnapshot { + var ( + web = structs.NewServiceName("web", nil) + cache = structs.NewServiceName("cache", nil) + ) + + events := []agentcache.UpdateEvent{ + { + CorrelationID: serviceResolverIDPrefix + web.String(), + Result: &structs.IndexedConfigEntries{ + Kind: structs.ServiceResolver, + Entries: []structs.ConfigEntry{ + &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "web", + Subsets: map[string]structs.ServiceResolverSubset{ + "v1": { + Filter: "Service.Meta.version == 1", + }, + "v2": { + Filter: "Service.Meta.version == 2", + OnlyPassing: true, + }, + }, + }, + }, + }, + }, + { + CorrelationID: serviceConfigIDPrefix + web.String(), + Result: &structs.ServiceConfigResponse{ + ProxyConfig: map[string]interface{}{"protocol": "http"}, + }, + }, + } + + if alsoAdjustCache { + events = testSpliceEvents(events, []agentcache.UpdateEvent{ + { + CorrelationID: serviceResolverIDPrefix + cache.String(), + Result: &structs.IndexedConfigEntries{ + Kind: structs.ServiceResolver, + Entries: []structs.ConfigEntry{ + &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "cache", + Subsets: map[string]structs.ServiceResolverSubset{ + "prod": { + Filter: "Service.Meta.Env == prod", + }, + }, + }, + }, + }, + }, + { + CorrelationID: serviceConfigIDPrefix + web.String(), + Result: &structs.ServiceConfigResponse{ + ProxyConfig: map[string]interface{}{"protocol": "http"}, + }, + }, + }) + } + + return TestConfigSnapshotTerminatingGateway(t, true, nil, events) +} + +func TestConfigSnapshotTerminatingGatewayDefaultServiceSubset(t testing.T) *ConfigSnapshot { + web := structs.NewServiceName("web", nil) + + return TestConfigSnapshotTerminatingGateway(t, true, nil, []agentcache.UpdateEvent{ + { + CorrelationID: serviceResolverIDPrefix + web.String(), + Result: &structs.IndexedConfigEntries{ + Kind: structs.ServiceResolver, + Entries: []structs.ConfigEntry{ + &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "web", + DefaultSubset: "v2", + Subsets: map[string]structs.ServiceResolverSubset{ + "v1": { + Filter: "Service.Meta.version == 1", + }, + "v2": { + Filter: "Service.Meta.version == 2", + OnlyPassing: true, + }, + }, + }, + }, + }, + }, + // { + // CorrelationID: serviceConfigIDPrefix + web.String(), + // Result: &structs.ServiceConfigResponse{ + // ProxyConfig: map[string]interface{}{"protocol": "http"}, + // }, + // }, + }) +} + +func TestConfigSnapshotTerminatingGatewayLBConfig(t testing.T) *ConfigSnapshot { + return testConfigSnapshotTerminatingGatewayLBConfig(t, "default") +} +func TestConfigSnapshotTerminatingGatewayLBConfigNoHashPolicies(t testing.T) *ConfigSnapshot { + return testConfigSnapshotTerminatingGatewayLBConfig(t, "no-hash-policies") +} +func testConfigSnapshotTerminatingGatewayLBConfig(t testing.T, variant string) *ConfigSnapshot { + web := structs.NewServiceName("web", nil) + + entry := &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "web", + DefaultSubset: "v2", + 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, + }, + HashPolicies: []structs.HashPolicy{ + { + Field: structs.HashPolicyCookie, + FieldValue: "chocolate-chip", + Terminal: true, + }, + { + Field: structs.HashPolicyHeader, + FieldValue: "x-user-id", + }, + { + SourceIP: true, + Terminal: true, + }, + }, + }, + } + + switch variant { + case "default": + case "no-hash-policies": + entry.LoadBalancer.HashPolicies = nil + default: + t.Fatalf("unknown variant %q", variant) + return nil + } + + return TestConfigSnapshotTerminatingGateway(t, true, nil, []cache.UpdateEvent{ + { + CorrelationID: serviceConfigIDPrefix + web.String(), + Result: &structs.ServiceConfigResponse{ + ProxyConfig: map[string]interface{}{"protocol": "http"}, + }, + }, + { + CorrelationID: serviceResolverIDPrefix + web.String(), + Result: &structs.IndexedConfigEntries{ + Kind: structs.ServiceResolver, + Entries: []structs.ConfigEntry{entry}, + }, + }, + { + CorrelationID: serviceConfigIDPrefix + web.String(), + Result: &structs.ServiceConfigResponse{ + ProxyConfig: map[string]interface{}{"protocol": "http"}, + }, + }, + }) +} + +func TestConfigSnapshotTerminatingGatewayHostnameSubsets(t testing.T) *ConfigSnapshot { + var ( + api = structs.NewServiceName("api", nil) + cache = structs.NewServiceName("cache", nil) + ) + + return TestConfigSnapshotTerminatingGateway(t, true, nil, []agentcache.UpdateEvent{ + { + CorrelationID: serviceResolverIDPrefix + api.String(), + Result: &structs.IndexedConfigEntries{ + Kind: structs.ServiceResolver, + Entries: []structs.ConfigEntry{ + &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "api", + Subsets: map[string]structs.ServiceResolverSubset{ + "alt": { + Filter: "Service.Meta.domain == alt", + }, + }, + }, + }, + }, + }, + { + CorrelationID: serviceResolverIDPrefix + cache.String(), + Result: &structs.IndexedConfigEntries{ + Kind: structs.ServiceResolver, + Entries: []structs.ConfigEntry{ + &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "cache", + Subsets: map[string]structs.ServiceResolverSubset{ + "prod": { + Filter: "Service.Meta.Env == prod", + }, + }, + }, + }, + }, + }, + { + CorrelationID: serviceConfigIDPrefix + api.String(), + Result: &structs.ServiceConfigResponse{ + ProxyConfig: map[string]interface{}{"protocol": "http"}, + }, + }, + { + CorrelationID: serviceConfigIDPrefix + cache.String(), + Result: &structs.ServiceConfigResponse{ + ProxyConfig: map[string]interface{}{"protocol": "http"}, + }, + }, + }) +} + +func TestConfigSnapshotTerminatingGatewayIgnoreExtraResolvers(t testing.T) *ConfigSnapshot { + var ( + web = structs.NewServiceName("web", nil) + notfound = structs.NewServiceName("notfound", nil) + ) + + return TestConfigSnapshotTerminatingGateway(t, true, nil, []agentcache.UpdateEvent{ + { + CorrelationID: serviceResolverIDPrefix + web.String(), + Result: &structs.IndexedConfigEntries{ + Kind: structs.ServiceResolver, + Entries: []structs.ConfigEntry{ + &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "web", + DefaultSubset: "v2", + Subsets: map[string]structs.ServiceResolverSubset{ + "v1": { + Filter: "Service.Meta.Version == 1", + }, + "v2": { + Filter: "Service.Meta.Version == 2", + OnlyPassing: true, + }, + }, + }, + }, + }, + }, + { + CorrelationID: serviceResolverIDPrefix + notfound.String(), + Result: &structs.IndexedConfigEntries{ + Kind: structs.ServiceResolver, + Entries: []structs.ConfigEntry{ + &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, + }, + }, + }, + }, + }, + }, + { + CorrelationID: serviceConfigIDPrefix + web.String(), + Result: &structs.ServiceConfigResponse{ + ProxyConfig: map[string]interface{}{"protocol": "http"}, + }, + }, + }) +} diff --git a/agent/proxycfg/testing_tproxy.go b/agent/proxycfg/testing_tproxy.go new file mode 100644 index 0000000000..4c04c93462 --- /dev/null +++ b/agent/proxycfg/testing_tproxy.go @@ -0,0 +1,519 @@ +package proxycfg + +import ( + "github.com/mitchellh/go-testing-interface" + + "github.com/hashicorp/consul/agent/cache" + "github.com/hashicorp/consul/agent/connect" + "github.com/hashicorp/consul/agent/consul/discoverychain" + "github.com/hashicorp/consul/agent/structs" +) + +func TestConfigSnapshotTransparentProxy(t testing.T) *ConfigSnapshot { + // DiscoveryChain without an UpstreamConfig should yield a + // filter chain when in transparent proxy mode + var ( + google = structs.NewServiceName("google", nil) + googleUID = NewUpstreamIDFromServiceName(google) + googleChain = discoverychain.TestCompileConfigEntries(t, "google", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + + noEndpoints = structs.NewServiceName("no-endpoints", nil) + noEndpointsUID = NewUpstreamIDFromServiceName(noEndpoints) + noEndpointsChain = discoverychain.TestCompileConfigEntries(t, "no-endpoints", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + + db = structs.NewServiceName("db", nil) + ) + + return TestConfigSnapshot(t, func(ns *structs.NodeService) { + ns.Proxy.Mode = structs.ProxyModeTransparent + }, []cache.UpdateEvent{ + { + CorrelationID: meshConfigEntryID, + Result: &structs.ConfigEntryResponse{ + Entry: nil, + }, + }, + { + CorrelationID: intentionUpstreamsID, + Result: &structs.IndexedServiceList{ + Services: structs.ServiceList{ + google, + noEndpoints, + // In transparent proxy mode, watches for + // upstreams in the local DC are handled by the + // IntentionUpstreams watch! + db, + }, + }, + }, + { + CorrelationID: "discovery-chain:" + googleUID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: googleChain, + }, + }, + { + CorrelationID: "discovery-chain:" + noEndpointsUID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: noEndpointsChain, + }, + }, + { + CorrelationID: "upstream-target:google.default.default.dc1:" + googleUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: []structs.CheckServiceNode{ + { + Node: &structs.Node{ + Address: "8.8.8.8", + Datacenter: "dc1", + }, + Service: &structs.NodeService{ + Service: "google", + Address: "9.9.9.9", + Port: 9090, + TaggedAddresses: map[string]structs.ServiceAddress{ + "virtual": {Address: "10.0.0.1"}, + structs.TaggedAddressVirtualIP: {Address: "240.0.0.1"}, + }, + }, + }, + }, + }, + }, + { + CorrelationID: "upstream-target:google-v2.default.default.dc1:" + googleUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + // Other targets of the discovery chain should be ignored. + // We only match on the upstream's virtual IP, not the IPs of other targets. + Nodes: []structs.CheckServiceNode{ + { + Node: &structs.Node{ + Address: "7.7.7.7", + Datacenter: "dc1", + }, + Service: &structs.NodeService{ + Service: "google-v2", + TaggedAddresses: map[string]structs.ServiceAddress{ + "virtual": {Address: "10.10.10.10"}, + }, + }, + }, + }, + }, + }, + { + CorrelationID: "upstream-target:" + noEndpointsChain.ID() + ":" + noEndpointsUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + // DiscoveryChains without endpoints do not get a + // filter chain because there are no addresses to + // match on. + Nodes: []structs.CheckServiceNode{}, + }, + }, + }) +} + +func TestConfigSnapshotTransparentProxyHTTPUpstream(t testing.T) *ConfigSnapshot { + // DiscoveryChain without an UpstreamConfig should yield a + // filter chain when in transparent proxy mode + var ( + google = structs.NewServiceName("google", nil) + googleUID = NewUpstreamIDFromServiceName(google) + googleChain = discoverychain.TestCompileConfigEntries(t, "google", "default", "default", "dc1", connect.TestClusterID+".consul", nil, + // Set default service protocol to HTTP + &structs.ProxyConfigEntry{ + Kind: structs.ProxyDefaults, + Name: structs.ProxyConfigGlobal, + Config: map[string]interface{}{ + "protocol": "http", + }, + }, + ) + + noEndpoints = structs.NewServiceName("no-endpoints", nil) + noEndpointsUID = NewUpstreamIDFromServiceName(noEndpoints) + noEndpointsChain = discoverychain.TestCompileConfigEntries(t, "no-endpoints", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + + db = structs.NewServiceName("db", nil) + ) + + return TestConfigSnapshot(t, func(ns *structs.NodeService) { + ns.Proxy.Mode = structs.ProxyModeTransparent + }, []cache.UpdateEvent{ + { + CorrelationID: meshConfigEntryID, + Result: &structs.ConfigEntryResponse{ + Entry: nil, + }, + }, + { + CorrelationID: intentionUpstreamsID, + Result: &structs.IndexedServiceList{ + Services: structs.ServiceList{ + google, + noEndpoints, + // In transparent proxy mode, watches for + // upstreams in the local DC are handled by the + // IntentionUpstreams watch! + db, + }, + }, + }, + { + CorrelationID: "discovery-chain:" + googleUID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: googleChain, + }, + }, + { + CorrelationID: "discovery-chain:" + noEndpointsUID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: noEndpointsChain, + }, + }, + { + CorrelationID: "upstream-target:google.default.default.dc1:" + googleUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: []structs.CheckServiceNode{ + { + Node: &structs.Node{ + Address: "8.8.8.8", + Datacenter: "dc1", + }, + Service: &structs.NodeService{ + Service: "google", + Address: "9.9.9.9", + Port: 9090, + TaggedAddresses: map[string]structs.ServiceAddress{ + "virtual": {Address: "10.0.0.1"}, + structs.TaggedAddressVirtualIP: {Address: "240.0.0.1"}, + }, + }, + }, + }, + }, + }, + { + CorrelationID: "upstream-target:google-v2.default.default.dc1:" + googleUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + // Other targets of the discovery chain should be ignored. + // We only match on the upstream's virtual IP, not the IPs of other targets. + Nodes: []structs.CheckServiceNode{ + { + Node: &structs.Node{ + Address: "7.7.7.7", + Datacenter: "dc1", + }, + Service: &structs.NodeService{ + Service: "google-v2", + TaggedAddresses: map[string]structs.ServiceAddress{ + "virtual": {Address: "10.10.10.10"}, + }, + }, + }, + }, + }, + }, + { + CorrelationID: "upstream-target:" + noEndpointsChain.ID() + ":" + noEndpointsUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + // DiscoveryChains without endpoints do not get a + // filter chain because there are no addresses to + // match on. + Nodes: []structs.CheckServiceNode{}, + }, + }, + }) +} + +func TestConfigSnapshotTransparentProxyCatalogDestinationsOnly(t testing.T) *ConfigSnapshot { + // DiscoveryChain without an UpstreamConfig should yield a + // filter chain when in transparent proxy mode + var ( + google = structs.NewServiceName("google", nil) + googleUID = NewUpstreamIDFromServiceName(google) + googleChain = discoverychain.TestCompileConfigEntries(t, "google", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + + noEndpoints = structs.NewServiceName("no-endpoints", nil) + noEndpointsUID = NewUpstreamIDFromServiceName(noEndpoints) + noEndpointsChain = discoverychain.TestCompileConfigEntries(t, "no-endpoints", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + + db = structs.NewServiceName("db", nil) + ) + + return TestConfigSnapshot(t, func(ns *structs.NodeService) { + ns.Proxy.Mode = structs.ProxyModeTransparent + }, []cache.UpdateEvent{ + { + CorrelationID: meshConfigEntryID, + Result: &structs.ConfigEntryResponse{ + Entry: &structs.MeshConfigEntry{ + TransparentProxy: structs.TransparentProxyMeshConfig{ + MeshDestinationsOnly: true, + }, + }, + }, + }, + { + CorrelationID: intentionUpstreamsID, + Result: &structs.IndexedServiceList{ + Services: structs.ServiceList{ + google, + noEndpoints, + // In transparent proxy mode, watches for + // upstreams in the local DC are handled by the + // IntentionUpstreams watch! + db, + }, + }, + }, + { + CorrelationID: "discovery-chain:" + googleUID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: googleChain, + }, + }, + { + CorrelationID: "discovery-chain:" + noEndpointsUID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: noEndpointsChain, + }, + }, + { + CorrelationID: "upstream-target:" + googleChain.ID() + ":" + googleUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: []structs.CheckServiceNode{ + { + Node: &structs.Node{ + Address: "8.8.8.8", + Datacenter: "dc1", + }, + Service: &structs.NodeService{ + Service: "google", + Address: "9.9.9.9", + Port: 9090, + TaggedAddresses: map[string]structs.ServiceAddress{ + "virtual": {Address: "10.0.0.1"}, + }, + }, + }, + }, + }, + }, + { + CorrelationID: "upstream-target:" + noEndpointsChain.ID() + ":" + noEndpointsUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + // DiscoveryChains without endpoints do not get a + // filter chain because there are no addresses to + // match on. + Nodes: []structs.CheckServiceNode{}, + }, + }, + }) +} + +func TestConfigSnapshotTransparentProxyDialDirectly(t testing.T) *ConfigSnapshot { + // DiscoveryChain without an UpstreamConfig should yield a + // filter chain when in transparent proxy mode + var ( + kafka = structs.NewServiceName("kafka", nil) + kafkaUID = NewUpstreamIDFromServiceName(kafka) + kafkaChain = discoverychain.TestCompileConfigEntries(t, "kafka", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + + mongo = structs.NewServiceName("mongo", nil) + mongoUID = NewUpstreamIDFromServiceName(mongo) + mongoChain = discoverychain.TestCompileConfigEntries(t, "mongo", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + + db = structs.NewServiceName("db", nil) + ) + + return TestConfigSnapshot(t, func(ns *structs.NodeService) { + ns.Proxy.Mode = structs.ProxyModeTransparent + }, []cache.UpdateEvent{ + { + CorrelationID: meshConfigEntryID, + Result: &structs.ConfigEntryResponse{ + Entry: nil, + }, + }, + { + CorrelationID: intentionUpstreamsID, + Result: &structs.IndexedServiceList{ + Services: structs.ServiceList{ + kafka, + mongo, + // In transparent proxy mode, watches for + // upstreams in the local DC are handled by the + // IntentionUpstreams watch! + db, + }, + }, + }, + { + CorrelationID: "discovery-chain:" + kafkaUID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: kafkaChain, + }, + }, + { + CorrelationID: "discovery-chain:" + mongoUID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: mongoChain, + }, + }, + { + CorrelationID: "upstream-target:" + mongoChain.ID() + ":" + mongoUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + // There should still be a filter chain for mongo's virtual address + Nodes: []structs.CheckServiceNode{ + { + Node: &structs.Node{ + Datacenter: "dc1", + }, + Service: &structs.NodeService{ + Service: "mongo", + Address: "10.10.10.10", + Port: 27017, + TaggedAddresses: map[string]structs.ServiceAddress{ + "virtual": {Address: "6.6.6.6"}, + }, + Proxy: structs.ConnectProxyConfig{ + TransparentProxy: structs.TransparentProxyConfig{ + DialedDirectly: true, + }, + }, + }, + }, + { + Node: &structs.Node{ + Datacenter: "dc1", + }, + Service: &structs.NodeService{ + Service: "mongo", + Address: "10.10.10.12", + Port: 27017, + TaggedAddresses: map[string]structs.ServiceAddress{ + "virtual": {Address: "6.6.6.6"}, + }, + Proxy: structs.ConnectProxyConfig{ + TransparentProxy: structs.TransparentProxyConfig{ + DialedDirectly: true, + }, + }, + }, + }, + }, + }, + }, + { + CorrelationID: "upstream-target:" + kafkaChain.ID() + ":" + kafkaUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: []structs.CheckServiceNode{ + { + Node: &structs.Node{ + Datacenter: "dc1", + }, + Service: &structs.NodeService{ + Service: "kafka", + Address: "9.9.9.9", + Port: 9092, + Proxy: structs.ConnectProxyConfig{ + TransparentProxy: structs.TransparentProxyConfig{ + DialedDirectly: true, + }, + }, + }, + }, + }, + }, + }, + }) +} + +func TestConfigSnapshotTransparentProxyTerminatingGatewayCatalogDestinationsOnly(t testing.T) *ConfigSnapshot { + // DiscoveryChain without an UpstreamConfig should yield a + // filter chain when in transparent proxy mode + var ( + google = structs.NewServiceName("google", nil) + googleUID = NewUpstreamIDFromServiceName(google) + googleChain = discoverychain.TestCompileConfigEntries(t, "google", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + + kafka = structs.NewServiceName("kafka", nil) + kafkaUID = NewUpstreamIDFromServiceName(kafka) + kafkaChain = discoverychain.TestCompileConfigEntries(t, "kafka", "default", "default", "dc1", connect.TestClusterID+".consul", nil) + + db = structs.NewServiceName("db", nil) + ) + + // DiscoveryChain without an UpstreamConfig should yield a filter chain when in transparent proxy mode + + tgate := structs.CheckServiceNode{ + Node: &structs.Node{ + Address: "8.8.8.8", + Datacenter: "dc1", + }, + Service: &structs.NodeService{ + Service: "tgate1", + Kind: structs.ServiceKind(structs.TerminatingGateway), + Address: "9.9.9.9", + Port: 9090, + TaggedAddresses: map[string]structs.ServiceAddress{ + structs.ServiceGatewayVirtualIPTag(google): {Address: "10.0.0.1"}, + structs.ServiceGatewayVirtualIPTag(kafka): {Address: "10.0.0.2"}, + "virtual": {Address: "6.6.6.6"}, + }, + }, + } + + return TestConfigSnapshot(t, func(ns *structs.NodeService) { + ns.Proxy.Mode = structs.ProxyModeTransparent + }, []cache.UpdateEvent{ + { + CorrelationID: meshConfigEntryID, + Result: &structs.ConfigEntryResponse{ + Entry: &structs.MeshConfigEntry{ + TransparentProxy: structs.TransparentProxyMeshConfig{ + MeshDestinationsOnly: true, + }, + }, + }, + }, + { + CorrelationID: intentionUpstreamsID, + Result: &structs.IndexedServiceList{ + Services: structs.ServiceList{ + google, + kafka, + // In transparent proxy mode, watches for + // upstreams in the local DC are handled by the + // IntentionUpstreams watch! + db, + }, + }, + }, + { + CorrelationID: "discovery-chain:" + googleUID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: googleChain, + }, + }, + { + CorrelationID: "discovery-chain:" + kafkaUID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: kafkaChain, + }, + }, + { + CorrelationID: "upstream-target:" + googleChain.ID() + ":" + googleUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: []structs.CheckServiceNode{tgate}, + }, + }, + { + CorrelationID: "upstream-target:" + kafkaChain.ID() + ":" + kafkaUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: []structs.CheckServiceNode{tgate}, + }, + }, + }) +} diff --git a/agent/proxycfg/testing_upstreams.go b/agent/proxycfg/testing_upstreams.go new file mode 100644 index 0000000000..c97ac7a4ff --- /dev/null +++ b/agent/proxycfg/testing_upstreams.go @@ -0,0 +1,764 @@ +package proxycfg + +import ( + "time" + + "github.com/mitchellh/go-testing-interface" + + "github.com/hashicorp/consul/agent/cache" + "github.com/hashicorp/consul/agent/connect" + "github.com/hashicorp/consul/agent/consul/discoverychain" + "github.com/hashicorp/consul/agent/structs" +) + +func setupTestVariationConfigEntriesAndSnapshot( + t testing.T, + variation string, + upstreams structs.Upstreams, + additionalEntries ...structs.ConfigEntry, +) []cache.UpdateEvent { + var ( + dbUpstream = upstreams[0] + + dbUID = NewUpstreamID(&dbUpstream) + ) + + dbChain := setupTestVariationDiscoveryChain(t, variation, additionalEntries...) + + events := []cache.UpdateEvent{ + { + CorrelationID: "discovery-chain:" + dbUID.String(), + Result: &structs.DiscoveryChainResponse{ + Chain: dbChain, + }, + }, + { + CorrelationID: "upstream-target:" + dbChain.ID() + ":" + dbUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "db"), + }, + }, + } + + switch variation { + case "default": + case "simple-with-overrides": + case "simple": + case "external-sni": + case "failover": + events = append(events, cache.UpdateEvent{ + CorrelationID: "upstream-target:fail.default.default.dc1:" + dbUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodesAlternate(t), + }, + }) + case "failover-through-remote-gateway-triggered": + events = append(events, cache.UpdateEvent{ + CorrelationID: "upstream-target:db.default.default.dc1:" + dbUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodesInStatus(t, "critical"), + }, + }) + fallthrough + case "failover-through-remote-gateway": + events = append(events, cache.UpdateEvent{ + CorrelationID: "upstream-target:db.default.default.dc2:" + dbUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodesDC2(t), + }, + }) + events = append(events, cache.UpdateEvent{ + CorrelationID: "mesh-gateway:dc2:" + dbUID.String(), + Result: &structs.IndexedNodesWithGateways{ + Nodes: TestGatewayNodesDC2(t), + }, + }) + case "failover-through-double-remote-gateway-triggered": + events = append(events, cache.UpdateEvent{ + CorrelationID: "upstream-target:db.default.default.dc1:" + dbUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodesInStatus(t, "critical"), + }, + }) + events = append(events, cache.UpdateEvent{ + CorrelationID: "upstream-target:db.default.default.dc2:" + dbUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodesInStatusDC2(t, "critical"), + }, + }) + fallthrough + case "failover-through-double-remote-gateway": + events = append(events, cache.UpdateEvent{ + CorrelationID: "upstream-target:db.default.default.dc3:" + dbUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodesDC2(t), + }, + }) + events = append(events, cache.UpdateEvent{ + CorrelationID: "mesh-gateway:dc2:" + dbUID.String(), + Result: &structs.IndexedNodesWithGateways{ + Nodes: TestGatewayNodesDC2(t), + }, + }) + events = append(events, cache.UpdateEvent{ + CorrelationID: "mesh-gateway:dc3:" + dbUID.String(), + Result: &structs.IndexedNodesWithGateways{ + Nodes: TestGatewayNodesDC3(t), + }, + }) + case "failover-through-local-gateway-triggered": + events = append(events, cache.UpdateEvent{ + CorrelationID: "upstream-target:db.default.default.dc1:" + dbUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodesInStatus(t, "critical"), + }, + }) + fallthrough + case "failover-through-local-gateway": + events = append(events, cache.UpdateEvent{ + CorrelationID: "upstream-target:db.default.default.dc2:" + dbUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodesDC2(t), + }, + }) + events = append(events, cache.UpdateEvent{ + CorrelationID: "mesh-gateway:dc1:" + dbUID.String(), + Result: &structs.IndexedNodesWithGateways{ + Nodes: TestGatewayNodesDC1(t), + }, + }) + case "failover-through-double-local-gateway-triggered": + events = append(events, cache.UpdateEvent{ + CorrelationID: "upstream-target:db.default.default.dc1:" + dbUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodesInStatus(t, "critical"), + }, + }) + events = append(events, cache.UpdateEvent{ + CorrelationID: "upstream-target:db.default.default.dc2:" + dbUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodesInStatusDC2(t, "critical"), + }, + }) + fallthrough + case "failover-through-double-local-gateway": + events = append(events, cache.UpdateEvent{ + CorrelationID: "upstream-target:db.default.default.dc3:" + dbUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodesDC2(t), + }, + }) + events = append(events, cache.UpdateEvent{ + CorrelationID: "mesh-gateway:dc1:" + dbUID.String(), + Result: &structs.IndexedNodesWithGateways{ + Nodes: TestGatewayNodesDC1(t), + }, + }) + case "splitter-with-resolver-redirect-multidc": + events = append(events, cache.UpdateEvent{ + CorrelationID: "upstream-target:v1.db.default.default.dc1:" + dbUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t, "db"), + }, + }) + events = append(events, cache.UpdateEvent{ + CorrelationID: "upstream-target:v2.db.default.default.dc2:" + dbUID.String(), + Result: &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodesDC2(t), + }, + }) + case "chain-and-splitter": + case "grpc-router": + case "chain-and-router": + case "lb-resolver": + default: + t.Fatalf("unexpected variation: %q", variation) + return nil + } + + return events +} + +func setupTestVariationDiscoveryChain( + t testing.T, + variation string, + additionalEntries ...structs.ConfigEntry, +) *structs.CompiledDiscoveryChain { + // Compile a chain. + var ( + entries []structs.ConfigEntry + compileSetup func(req *discoverychain.CompileRequest) + ) + + switch variation { + case "default": + // no config entries + 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", + ConnectTimeout: 33 * time.Second, + }, + ) + case "external-sni": + entries = append(entries, + &structs.ServiceConfigEntry{ + Kind: structs.ServiceDefaults, + Name: "db", + ExternalSNI: "db.some.other.service.mesh", + }, + &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "db", + ConnectTimeout: 33 * time.Second, + }, + ) + case "failover": + entries = append(entries, + &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "db", + ConnectTimeout: 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", + MeshGateway: structs.MeshGatewayConfig{ + Mode: structs.MeshGatewayModeRemote, + }, + }, + &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "db", + ConnectTimeout: 33 * time.Second, + Failover: map[string]structs.ServiceResolverFailover{ + "*": { + Datacenters: []string{"dc2"}, + }, + }, + }, + ) + case "failover-through-double-remote-gateway-triggered": + fallthrough + case "failover-through-double-remote-gateway": + entries = append(entries, + &structs.ServiceConfigEntry{ + Kind: structs.ServiceDefaults, + Name: "db", + MeshGateway: structs.MeshGatewayConfig{ + Mode: structs.MeshGatewayModeRemote, + }, + }, + &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "db", + ConnectTimeout: 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", + MeshGateway: structs.MeshGatewayConfig{ + Mode: structs.MeshGatewayModeLocal, + }, + }, + &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "db", + ConnectTimeout: 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", + MeshGateway: structs.MeshGatewayConfig{ + Mode: structs.MeshGatewayModeLocal, + }, + }, + &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "db", + ConnectTimeout: 33 * time.Second, + Failover: map[string]structs.ServiceResolverFailover{ + "*": { + Datacenters: []string{"dc2", "dc3"}, + }, + }, + }, + ) + case "splitter-with-resolver-redirect-multidc": + entries = append(entries, + &structs.ProxyConfigEntry{ + Kind: structs.ProxyDefaults, + Name: structs.ProxyConfigGlobal, + Config: map[string]interface{}{ + "protocol": "http", + }, + }, + &structs.ServiceSplitterConfigEntry{ + Kind: structs.ServiceResolver, + Name: "db", + Splits: []structs.ServiceSplit{ + {Weight: 50, Service: "db-dc1"}, + {Weight: 50, Service: "db-dc2"}, + }, + }, + &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "db-dc1", + Redirect: &structs.ServiceResolverRedirect{ + Service: "db", + ServiceSubset: "v1", + Datacenter: "dc1", + }, + }, + &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "db-dc2", + Redirect: &structs.ServiceResolverRedirect{ + Service: "db", + ServiceSubset: "v2", + Datacenter: "dc2", + }, + }, + &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "db", + 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", + ConnectTimeout: 33 * time.Second, + }, + &structs.ProxyConfigEntry{ + Kind: structs.ProxyDefaults, + Name: structs.ProxyConfigGlobal, + Config: map[string]interface{}{ + "protocol": "http", + }, + }, + &structs.ServiceSplitterConfigEntry{ + Kind: structs.ServiceSplitter, + Name: "db", + Splits: []structs.ServiceSplit{ + { + 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: 4, + 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 "grpc-router": + entries = append(entries, + &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "db", + ConnectTimeout: 33 * time.Second, + }, + &structs.ProxyConfigEntry{ + Kind: structs.ProxyDefaults, + Name: structs.ProxyConfigGlobal, + Config: map[string]interface{}{ + "protocol": "grpc", + }, + }, + &structs.ServiceRouterConfigEntry{ + Kind: structs.ServiceRouter, + Name: "db", + 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", + ConnectTimeout: 33 * time.Second, + }, + &structs.ProxyConfigEntry{ + Kind: structs.ProxyDefaults, + Name: structs.ProxyConfigGlobal, + Config: map[string]interface{}{ + "protocol": "http", + }, + }, + &structs.ServiceSplitterConfigEntry{ + Kind: structs.ServiceSplitter, + Name: "split-3-ways", + 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", + 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: "/retry-connect", + }), + Destination: &structs.ServiceRouteDestination{ + Service: "retry-connect", + NumRetries: 15, + RetryOnConnectFailure: true, + }, + }, + { + 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-both", + }), + Destination: &structs.ServiceRouteDestination{ + Service: "retry-both", + RetryOnConnectFailure: true, + 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, + Config: map[string]interface{}{ + "protocol": "http", + }, + }, + &structs.ServiceSplitterConfigEntry{ + Kind: structs.ServiceSplitter, + Name: "db", + Splits: []structs.ServiceSplit{ + {Weight: 95.5, Service: "something-else"}, + {Weight: 4.5, Service: "db"}, + }, + }, + &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "db", + 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", + }, + { + SourceIP: true, + Terminal: true, + }, + }, + }, + }, + ) + default: + t.Fatalf("unexpected variation: %q", variation) + return nil + } + + if len(additionalEntries) > 0 { + entries = append(entries, additionalEntries...) + } + + return discoverychain.TestCompileConfigEntries(t, "db", "default", "default", "dc1", connect.TestClusterID+".consul", compileSetup, entries...) +} + +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} +} diff --git a/agent/xds/clusters_test.go b/agent/xds/clusters_test.go index 758ca3d032..3bda55904a 100644 --- a/agent/xds/clusters_test.go +++ b/agent/xds/clusters_test.go @@ -6,7 +6,6 @@ import ( "sort" "testing" "text/template" - "time" envoy_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" @@ -14,8 +13,6 @@ import ( testinf "github.com/mitchellh/go-testing-interface" "github.com/stretchr/testify/require" - "github.com/hashicorp/consul/agent/connect" - "github.com/hashicorp/consul/agent/consul/discoverychain" "github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/xds/proxysupport" @@ -28,229 +25,241 @@ func TestClustersFromSnapshot(t *testing.T) { } tests := []struct { - name string - create func(t testinf.T) *proxycfg.ConfigSnapshot - // Setup is called before the test starts. It is passed the snapshot from - // create func and is allowed to modify it in any way to setup the - // test input. - setup func(snap *proxycfg.ConfigSnapshot) + name string + create func(t testinf.T) *proxycfg.ConfigSnapshot overrideGoldenName string }{ { - name: "defaults", - create: proxycfg.TestConfigSnapshot, - setup: nil, // Default snapshot - }, - { - name: "custom-local-app", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config["envoy_local_cluster_json"] = - customAppClusterJSON(t, customClusterJSONOptions{ - Name: "mylocal", - }) + name: "defaults", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, nil, nil) }, }, { - name: "custom-upstream", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Upstreams[0].Config["envoy_cluster_json"] = - customAppClusterJSON(t, customClusterJSONOptions{ - Name: "myservice", - }) + name: "custom-local-app", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + ns.Proxy.Config["envoy_local_cluster_json"] = + customAppClusterJSON(t, customClusterJSONOptions{ + Name: "mylocal", + }) + }, nil) }, }, { - name: "custom-upstream-default-chain", - create: proxycfg.TestConfigSnapshotDiscoveryChainDefault, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Upstreams[0].Config["envoy_cluster_json"] = - customAppClusterJSON(t, customClusterJSONOptions{ - Name: "myservice", - }) - snap.ConnectProxy.UpstreamConfig = map[proxycfg.UpstreamID]*structs.Upstream{ - UID("db"): { - // The local bind port is overridden by the escape hatch, but is required for explicit upstreams. - LocalBindPort: 9191, - Config: map[string]interface{}{ - "envoy_cluster_json": customAppClusterJSON(t, customClusterJSONOptions{ - Name: "myservice", - }), - }, - }, - } + name: "custom-upstream", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + ns.Proxy.Upstreams[0].Config["envoy_cluster_json"] = + customAppClusterJSON(t, customClusterJSONOptions{ + Name: "myservice", + }) + }, nil) + }, + }, + { + name: "custom-upstream-default-chain", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", func(ns *structs.NodeService) { + ns.Proxy.Upstreams[0].Config["envoy_cluster_json"] = + customAppClusterJSON(t, customClusterJSONOptions{ + Name: "myservice", + }) + }, nil) }, }, { name: "custom-upstream-ignores-tls", - create: proxycfg.TestConfigSnapshot, overrideGoldenName: "custom-upstream", // should be the same - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Upstreams[0].Config["envoy_cluster_json"] = - customAppClusterJSON(t, customClusterJSONOptions{ - Name: "myservice", - // Attempt to override the TLS context should be ignored - TLSContext: `"allowRenegotiation": false`, - }) + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + ns.Proxy.Upstreams[0].Config["envoy_cluster_json"] = + customAppClusterJSON(t, customClusterJSONOptions{ + Name: "myservice", + // Attempt to override the TLS context should be ignored + TLSContext: `"allowRenegotiation": false`, + }) + }, nil) }, }, { - name: "custom-timeouts", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config["local_connect_timeout_ms"] = 1234 - snap.Proxy.Upstreams[0].Config["connect_timeout_ms"] = 2345 + name: "custom-timeouts", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + ns.Proxy.Config["local_connect_timeout_ms"] = 1234 + ns.Proxy.Upstreams[0].Config["connect_timeout_ms"] = 2345 + }, nil) }, }, { - name: "custom-limits-max-connections-only", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - for i := range snap.Proxy.Upstreams { - // We check if Config is nil because the prepared_query upstream is - // initialized without a Config map. Use Upstreams[i] syntax to - // modify the actual ConfigSnapshot instead of copying the Upstream - // in the range. - if snap.Proxy.Upstreams[i].Config == nil { - snap.Proxy.Upstreams[i].Config = map[string]interface{}{} - } + name: "custom-limits-max-connections-only", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + for i := range ns.Proxy.Upstreams { + // We check if Config is nil because the prepared_query upstream is + // initialized without a Config map. Use Upstreams[i] syntax to + // modify the actual ConfigSnapshot instead of copying the Upstream + // in the range. + if ns.Proxy.Upstreams[i].Config == nil { + ns.Proxy.Upstreams[i].Config = map[string]interface{}{} + } - snap.Proxy.Upstreams[i].Config["limits"] = map[string]interface{}{ - "max_connections": 500, + ns.Proxy.Upstreams[i].Config["limits"] = map[string]interface{}{ + "max_connections": 500, + } } - } + }, nil) }, }, { - name: "custom-limits-set-to-zero", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - for i := range snap.Proxy.Upstreams { - if snap.Proxy.Upstreams[i].Config == nil { - snap.Proxy.Upstreams[i].Config = map[string]interface{}{} - } + name: "custom-limits-set-to-zero", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + for i := range ns.Proxy.Upstreams { + if ns.Proxy.Upstreams[i].Config == nil { + ns.Proxy.Upstreams[i].Config = map[string]interface{}{} + } - snap.Proxy.Upstreams[i].Config["limits"] = map[string]interface{}{ - "max_connections": 0, - "max_pending_requests": 0, - "max_concurrent_requests": 0, + ns.Proxy.Upstreams[i].Config["limits"] = map[string]interface{}{ + "max_connections": 0, + "max_pending_requests": 0, + "max_concurrent_requests": 0, + } } - } + }, nil) }, }, { - name: "custom-limits", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - for i := range snap.Proxy.Upstreams { - if snap.Proxy.Upstreams[i].Config == nil { - snap.Proxy.Upstreams[i].Config = map[string]interface{}{} - } + name: "custom-limits", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + for i := range ns.Proxy.Upstreams { + if ns.Proxy.Upstreams[i].Config == nil { + ns.Proxy.Upstreams[i].Config = map[string]interface{}{} + } - snap.Proxy.Upstreams[i].Config["limits"] = map[string]interface{}{ - "max_connections": 500, - "max_pending_requests": 600, - "max_concurrent_requests": 700, + ns.Proxy.Upstreams[i].Config["limits"] = map[string]interface{}{ + "max_connections": 500, + "max_pending_requests": 600, + "max_concurrent_requests": 700, + } } - } + }, nil) }, }, { - name: "connect-proxy-with-chain", - create: proxycfg.TestConfigSnapshotDiscoveryChain, - setup: nil, - }, - { - name: "connect-proxy-with-chain-external-sni", - create: proxycfg.TestConfigSnapshotDiscoveryChainExternalSNI, - setup: nil, - }, - { - name: "connect-proxy-with-chain-and-overrides", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithOverrides, - setup: nil, - }, - { - name: "connect-proxy-with-chain-and-failover", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithFailover, - setup: nil, - }, - { - name: "connect-proxy-with-tcp-chain-failover-through-remote-gateway", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithFailoverThroughRemoteGateway, - setup: nil, - }, - { - name: "connect-proxy-with-tcp-chain-failover-through-remote-gateway-triggered", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithFailoverThroughRemoteGatewayTriggered, - setup: nil, - }, - { - name: "connect-proxy-with-tcp-chain-double-failover-through-remote-gateway", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithDoubleFailoverThroughRemoteGateway, - setup: nil, - }, - { - name: "connect-proxy-with-tcp-chain-double-failover-through-remote-gateway-triggered", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithDoubleFailoverThroughRemoteGatewayTriggered, - setup: nil, - }, - { - name: "connect-proxy-with-tcp-chain-failover-through-local-gateway", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithFailoverThroughLocalGateway, - setup: nil, - }, - { - name: "connect-proxy-with-tcp-chain-failover-through-local-gateway-triggered", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithFailoverThroughLocalGatewayTriggered, - setup: nil, - }, - { - name: "connect-proxy-with-tcp-chain-double-failover-through-local-gateway", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithDoubleFailoverThroughLocalGateway, - setup: nil, - }, - { - name: "connect-proxy-with-tcp-chain-double-failover-through-local-gateway-triggered", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithDoubleFailoverThroughLocalGatewayTriggered, - setup: nil, - }, - { - name: "splitter-with-resolver-redirect", - create: proxycfg.TestConfigSnapshotDiscoveryChain_SplitterWithResolverRedirectMultiDC, - setup: nil, - }, - { - name: "connect-proxy-lb-in-resolver", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithLB, - setup: nil, - }, - { - name: "expose-paths-local-app-paths", - create: proxycfg.TestConfigSnapshotExposeConfig, - }, - { - name: "downstream-service-with-unix-sockets", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Address = "" - snap.Port = 0 - snap.Proxy.LocalServiceAddress = "" - snap.Proxy.LocalServicePort = 0 - snap.Proxy.LocalServiceSocketPath = "/tmp/downstream_proxy.sock" + name: "connect-proxy-with-chain", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "simple", nil, nil) }, }, { - name: "expose-paths-new-cluster-http2", - create: proxycfg.TestConfigSnapshotExposeConfig, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Expose.Paths[1] = structs.ExposePath{ - LocalPathPort: 9090, - Path: "/grpc.health.v1.Health/Check", - ListenerPort: 21501, - Protocol: "http2", - } + name: "connect-proxy-with-chain-external-sni", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "external-sni", nil, nil) + }, + }, + { + name: "connect-proxy-with-chain-and-overrides", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "simple-with-overrides", nil, nil) + }, + }, + { + name: "connect-proxy-with-chain-and-failover", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover", nil, nil) + }, + }, + { + name: "connect-proxy-with-tcp-chain-failover-through-remote-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover-through-remote-gateway", nil, nil) + }, + }, + { + name: "connect-proxy-with-tcp-chain-failover-through-remote-gateway-triggered", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover-through-remote-gateway-triggered", nil, nil) + }, + }, + { + name: "connect-proxy-with-tcp-chain-double-failover-through-remote-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover-through-double-remote-gateway", nil, nil) + }, + }, + { + name: "connect-proxy-with-tcp-chain-double-failover-through-remote-gateway-triggered", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover-through-double-remote-gateway-triggered", nil, nil) + }, + }, + { + name: "connect-proxy-with-tcp-chain-failover-through-local-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover-through-local-gateway", nil, nil) + }, + }, + { + name: "connect-proxy-with-tcp-chain-failover-through-local-gateway-triggered", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover-through-local-gateway-triggered", nil, nil) + }, + }, + { + name: "connect-proxy-with-tcp-chain-double-failover-through-local-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover-through-double-local-gateway", nil, nil) + }, + }, + { + name: "connect-proxy-with-tcp-chain-double-failover-through-local-gateway-triggered", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover-through-double-local-gateway-triggered", nil, nil) + }, + }, + { + name: "splitter-with-resolver-redirect", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "splitter-with-resolver-redirect-multidc", nil, nil) + }, + }, + { + name: "connect-proxy-lb-in-resolver", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "lb-resolver", nil, nil) + }, + }, + { + name: "expose-paths-local-app-paths", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotExposeConfig(t, nil) + }, + }, + { + name: "downstream-service-with-unix-sockets", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + ns.Address = "" + ns.Port = 0 + ns.Proxy.LocalServiceAddress = "" + ns.Proxy.LocalServicePort = 0 + ns.Proxy.LocalServiceSocketPath = "/tmp/downstream_proxy.sock" + }, nil) + }, + }, + { + name: "expose-paths-new-cluster-http2", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotExposeConfig(t, func(ns *structs.NodeService) { + ns.Proxy.Expose.Paths[1] = structs.ExposePath{ + LocalPathPort: 9090, + Path: "/grpc.health.v1.Health/Check", + ListenerPort: 21501, + Protocol: "http2", + } + }) }, }, { @@ -258,460 +267,201 @@ func TestClustersFromSnapshot(t *testing.T) { create: proxycfg.TestConfigSnapshotGRPCExposeHTTP1, }, { - name: "mesh-gateway", - create: proxycfg.TestConfigSnapshotMeshGateway, - setup: nil, - }, - { - name: "mesh-gateway-using-federation-states", - create: proxycfg.TestConfigSnapshotMeshGatewayUsingFederationStates, - setup: nil, - }, - { - name: "mesh-gateway-no-services", - create: proxycfg.TestConfigSnapshotMeshGatewayNoServices, - setup: nil, - }, - { - name: "mesh-gateway-service-subsets", - create: proxycfg.TestConfigSnapshotMeshGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.MeshGateway.ServiceResolvers = map[structs.ServiceName]*structs.ServiceResolverConfigEntry{ - structs.NewServiceName("bar", nil): { - Kind: structs.ServiceResolver, - Name: "bar", - Subsets: map[string]structs.ServiceResolverSubset{ - "v1": { - Filter: "Service.Meta.Version == 1", - }, - "v2": { - Filter: "Service.Meta.Version == 2", - OnlyPassing: true, - }, - }, - }, - } + name: "mesh-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotMeshGateway(t, "default", nil, nil) }, }, { - name: "mesh-gateway-ignore-extra-resolvers", - create: proxycfg.TestConfigSnapshotMeshGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.MeshGateway.ServiceResolvers = map[structs.ServiceName]*structs.ServiceResolverConfigEntry{ - structs.NewServiceName("bar", nil): { - 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.NewServiceName("notfound", nil): { - 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, - }, - }, - }, - } + name: "mesh-gateway-using-federation-states", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotMeshGateway(t, "federation-states", nil, nil) }, }, { - name: "mesh-gateway-service-timeouts", - create: proxycfg.TestConfigSnapshotMeshGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.MeshGateway.ServiceResolvers = map[structs.ServiceName]*structs.ServiceResolverConfigEntry{ - structs.NewServiceName("bar", nil): { - 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, - }, - }, - }, - } + name: "mesh-gateway-no-services", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotMeshGateway(t, "no-services", nil, nil) }, }, { - name: "mesh-gateway-non-hash-lb-injected", - create: proxycfg.TestConfigSnapshotMeshGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.MeshGateway.ServiceResolvers = map[structs.ServiceName]*structs.ServiceResolverConfigEntry{ - structs.NewServiceName("bar", nil): { - 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, - }, - }, - }, - } + name: "mesh-gateway-service-subsets", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotMeshGateway(t, "service-subsets", nil, nil) }, }, { - name: "mesh-gateway-hash-lb-ignored", - create: proxycfg.TestConfigSnapshotMeshGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.MeshGateway.ServiceResolvers = map[structs.ServiceName]*structs.ServiceResolverConfigEntry{ - structs.NewServiceName("bar", nil): { - 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, - }, - }, - }, - } + name: "mesh-gateway-ignore-extra-resolvers", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotMeshGateway(t, "ignore-extra-resolvers", nil, nil) }, }, { - name: "ingress-gateway", - create: proxycfg.TestConfigSnapshotIngressGateway, - setup: nil, + name: "mesh-gateway-service-timeouts", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotMeshGateway(t, "service-timeouts", nil, nil) + }, }, { - name: "ingress-gateway-no-services", - create: proxycfg.TestConfigSnapshotIngressGatewayNoServices, - setup: nil, + name: "mesh-gateway-non-hash-lb-injected", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotMeshGateway(t, "non-hash-lb-injected", nil, nil) + }, }, { - name: "ingress-with-chain", - create: proxycfg.TestConfigSnapshotIngress, - setup: nil, + name: "mesh-gateway-hash-lb-ignored", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotMeshGateway(t, "hash-lb-ignored", nil, nil) + }, }, { - name: "ingress-with-chain-external-sni", - create: proxycfg.TestConfigSnapshotIngressExternalSNI, - setup: nil, + name: "ingress-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "default", nil, nil, nil) + }, }, { - name: "ingress-with-chain-and-overrides", - create: proxycfg.TestConfigSnapshotIngressWithOverrides, - setup: nil, + name: "ingress-gateway-no-services", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, false, "tcp", + "default", nil, nil, nil) + }, }, { - name: "ingress-with-chain-and-failover", - create: proxycfg.TestConfigSnapshotIngressWithFailover, - setup: nil, + name: "ingress-with-chain", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "simple", nil, nil, nil) + }, }, { - name: "ingress-with-tcp-chain-failover-through-remote-gateway", - create: proxycfg.TestConfigSnapshotIngressWithFailoverThroughRemoteGateway, - setup: nil, + name: "ingress-with-chain-external-sni", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "external-sni", nil, nil, nil) + }, }, { - name: "ingress-with-tcp-chain-failover-through-remote-gateway-triggered", - create: proxycfg.TestConfigSnapshotIngressWithFailoverThroughRemoteGatewayTriggered, - setup: nil, + name: "ingress-with-chain-and-failover", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "failover", nil, nil, nil) + }, }, { - name: "ingress-with-tcp-chain-double-failover-through-remote-gateway", - create: proxycfg.TestConfigSnapshotIngressWithDoubleFailoverThroughRemoteGateway, - setup: nil, + name: "ingress-with-tcp-chain-failover-through-remote-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "failover-through-remote-gateway", nil, nil, nil) + }, }, { - name: "ingress-with-tcp-chain-double-failover-through-remote-gateway-triggered", - create: proxycfg.TestConfigSnapshotIngressWithDoubleFailoverThroughRemoteGatewayTriggered, - setup: nil, + name: "ingress-with-tcp-chain-failover-through-remote-gateway-triggered", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "failover-through-remote-gateway-triggered", nil, nil, nil) + }, }, { - name: "ingress-with-tcp-chain-failover-through-local-gateway", - create: proxycfg.TestConfigSnapshotIngressWithFailoverThroughLocalGateway, - setup: nil, + name: "ingress-with-tcp-chain-double-failover-through-remote-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "failover-through-double-remote-gateway", nil, nil, nil) + }, }, { - name: "ingress-with-tcp-chain-failover-through-local-gateway-triggered", - create: proxycfg.TestConfigSnapshotIngressWithFailoverThroughLocalGatewayTriggered, - setup: nil, + name: "ingress-with-tcp-chain-double-failover-through-remote-gateway-triggered", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "failover-through-double-remote-gateway-triggered", nil, nil, nil) + }, }, { - name: "ingress-with-tcp-chain-double-failover-through-local-gateway", - create: proxycfg.TestConfigSnapshotIngressWithDoubleFailoverThroughLocalGateway, - setup: nil, + name: "ingress-with-tcp-chain-failover-through-local-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "failover-through-local-gateway", nil, nil, nil) + }, }, { - name: "ingress-with-tcp-chain-double-failover-through-local-gateway-triggered", - create: proxycfg.TestConfigSnapshotIngressWithDoubleFailoverThroughLocalGatewayTriggered, - setup: nil, + name: "ingress-with-tcp-chain-failover-through-local-gateway-triggered", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "failover-through-local-gateway-triggered", nil, nil, nil) + }, }, { - name: "ingress-splitter-with-resolver-redirect", - create: proxycfg.TestConfigSnapshotIngress_SplitterWithResolverRedirectMultiDC, - setup: nil, + name: "ingress-with-tcp-chain-double-failover-through-local-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "failover-through-double-local-gateway", nil, nil, nil) + }, }, { - name: "ingress-lb-in-resolver", - create: proxycfg.TestConfigSnapshotIngressWithLB, - setup: nil, + name: "ingress-with-tcp-chain-double-failover-through-local-gateway-triggered", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "failover-through-double-local-gateway-triggered", nil, nil, nil) + }, }, { - name: "terminating-gateway", - create: proxycfg.TestConfigSnapshotTerminatingGateway, - setup: nil, + name: "ingress-splitter-with-resolver-redirect", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "http", + "splitter-with-resolver-redirect-multidc", nil, nil, nil) + }, }, { - name: "terminating-gateway-no-services", - create: proxycfg.TestConfigSnapshotTerminatingGatewayNoServices, - setup: nil, + name: "ingress-lb-in-resolver", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "http", + "lb-resolver", nil, nil, nil) + }, + }, + { + name: "terminating-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotTerminatingGateway(t, true, nil, nil) + }, + }, + { + name: "terminating-gateway-no-services", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotTerminatingGateway(t, false, nil, nil) + }, }, { name: "terminating-gateway-service-subsets", - create: proxycfg.TestConfigSnapshotTerminatingGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.TerminatingGateway.ServiceResolvers = map[structs.ServiceName]*structs.ServiceResolverConfigEntry{ - structs.NewServiceName("web", nil): { - Kind: structs.ServiceResolver, - Name: "web", - Subsets: map[string]structs.ServiceResolverSubset{ - "v1": { - Filter: "Service.Meta.Version == 1", - }, - "v2": { - Filter: "Service.Meta.Version == 2", - OnlyPassing: true, - }, - }, - }, - structs.NewServiceName("cache", nil): { - Kind: structs.ServiceResolver, - Name: "cache", - Subsets: map[string]structs.ServiceResolverSubset{ - "prod": { - Filter: "Service.Meta.Env == prod", - }, - }, - }, - } - snap.TerminatingGateway.ServiceConfigs[structs.NewServiceName("web", nil)] = &structs.ServiceConfigResponse{ - ProxyConfig: map[string]interface{}{"protocol": "http"}, - } - snap.TerminatingGateway.ServiceConfigs[structs.NewServiceName("cache", nil)] = &structs.ServiceConfigResponse{ - ProxyConfig: map[string]interface{}{"protocol": "http"}, - } - }, + create: proxycfg.TestConfigSnapshotTerminatingGatewayServiceSubsetsWebAndCache, }, { name: "terminating-gateway-hostname-service-subsets", - create: proxycfg.TestConfigSnapshotTerminatingGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.TerminatingGateway.ServiceResolvers = map[structs.ServiceName]*structs.ServiceResolverConfigEntry{ - structs.NewServiceName("api", nil): { - Kind: structs.ServiceResolver, - Name: "api", - Subsets: map[string]structs.ServiceResolverSubset{ - "alt": { - Filter: "Service.Meta.domain == alt", - }, - }, - }, - structs.NewServiceName("cache", nil): { - Kind: structs.ServiceResolver, - Name: "cache", - Subsets: map[string]structs.ServiceResolverSubset{ - "prod": { - Filter: "Service.Meta.Env == prod", - }, - }, - }, - } - snap.TerminatingGateway.ServiceConfigs[structs.NewServiceName("api", nil)] = &structs.ServiceConfigResponse{ - ProxyConfig: map[string]interface{}{"protocol": "http"}, - } - snap.TerminatingGateway.ServiceConfigs[structs.NewServiceName("cache", nil)] = &structs.ServiceConfigResponse{ - ProxyConfig: map[string]interface{}{"protocol": "http"}, - } - }, + create: proxycfg.TestConfigSnapshotTerminatingGatewayHostnameSubsets, }, { name: "terminating-gateway-ignore-extra-resolvers", - create: proxycfg.TestConfigSnapshotTerminatingGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.TerminatingGateway.ServiceResolvers = map[structs.ServiceName]*structs.ServiceResolverConfigEntry{ - structs.NewServiceName("web", nil): { - Kind: structs.ServiceResolver, - Name: "web", - DefaultSubset: "v2", - Subsets: map[string]structs.ServiceResolverSubset{ - "v1": { - Filter: "Service.Meta.Version == 1", - }, - "v2": { - Filter: "Service.Meta.Version == 2", - OnlyPassing: true, - }, - }, - }, - structs.NewServiceName("notfound", nil): { - 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, - }, - }, - }, - } - snap.TerminatingGateway.ServiceConfigs[structs.NewServiceName("web", nil)] = &structs.ServiceConfigResponse{ - ProxyConfig: map[string]interface{}{"protocol": "http"}, - } - }, + create: proxycfg.TestConfigSnapshotTerminatingGatewayIgnoreExtraResolvers, }, { name: "terminating-gateway-lb-config", - create: proxycfg.TestConfigSnapshotTerminatingGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.TerminatingGateway.ServiceResolvers = map[structs.ServiceName]*structs.ServiceResolverConfigEntry{ - structs.NewServiceName("web", nil): { - Kind: structs.ServiceResolver, - Name: "web", - DefaultSubset: "v2", - 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, - }, - }, - }, - } - snap.TerminatingGateway.ServiceConfigs[structs.NewServiceName("web", nil)] = &structs.ServiceConfigResponse{ - ProxyConfig: map[string]interface{}{"protocol": "http"}, - } - }, + create: proxycfg.TestConfigSnapshotTerminatingGatewayLBConfigNoHashPolicies, }, { name: "ingress-multiple-listeners-duplicate-service", create: proxycfg.TestConfigSnapshotIngress_MultipleListenersDuplicateService, - setup: nil, }, { name: "transparent-proxy", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Mode = structs.ProxyModeTransparent - snap.ConnectProxy.MeshConfigSet = true - }, + create: proxycfg.TestConfigSnapshotTransparentProxy, }, { name: "transparent-proxy-catalog-destinations-only", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Mode = structs.ProxyModeTransparent - - snap.ConnectProxy.MeshConfigSet = true - snap.ConnectProxy.MeshConfig = &structs.MeshConfigEntry{ - TransparentProxy: structs.TransparentProxyMeshConfig{ - MeshDestinationsOnly: true, - }, - } - }, + create: proxycfg.TestConfigSnapshotTransparentProxyCatalogDestinationsOnly, }, { name: "transparent-proxy-dial-instances-directly", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Mode = structs.ProxyModeTransparent - kafka := structs.NewServiceName("kafka", nil) - mongo := structs.NewServiceName("mongo", nil) - kafkaUID := proxycfg.NewUpstreamIDFromServiceName(kafka) - mongoUID := proxycfg.NewUpstreamIDFromServiceName(mongo) - - snap.ConnectProxy.IntentionUpstreams = map[proxycfg.UpstreamID]struct{}{ - kafkaUID: {}, - mongoUID: {}, - } - - // We add a passthrough cluster for each upstream service name - snap.ConnectProxy.PassthroughUpstreams = map[proxycfg.UpstreamID]map[string]map[string]struct{}{ - kafkaUID: { - "kafka.default.default.dc1": map[string]struct{}{ - "9.9.9.9": {}, - }, - }, - mongoUID: { - "mongo.default.default.dc1": map[string]struct{}{ - "10.10.10.10": {}, - "10.10.10.12": {}, - }, - }, - } - - // There should still be a cluster for non-passthrough requests - snap.ConnectProxy.DiscoveryChain[mongoUID] = discoverychain.TestCompileConfigEntries(t, "mongo", "default", "default", "dc1", connect.TestClusterID+".consul", nil) - snap.ConnectProxy.WatchedUpstreamEndpoints[mongoUID] = map[string]structs.CheckServiceNodes{ - "mongo.default.default.dc1": { - structs.CheckServiceNode{ - Node: &structs.Node{ - Datacenter: "dc1", - }, - Service: &structs.NodeService{ - Service: "mongo", - Address: "7.7.7.7", - Port: 27017, - TaggedAddresses: map[string]structs.ServiceAddress{ - "virtual": {Address: "6.6.6.6"}, - }, - }, - }, - }, - } - }, + create: proxycfg.TestConfigSnapshotTransparentProxyDialDirectly, }, } @@ -730,10 +480,6 @@ func TestClustersFromSnapshot(t *testing.T) { // golder files for every test case and so not be any use! setupTLSRootsAndLeaf(t, snap) - if tt.setup != nil { - tt.setup(snap) - } - // Need server just for logger dependency g := newResourceGenerator(testutil.Logger(t), nil, nil, false) g.ProxyFeatures = sf @@ -805,7 +551,7 @@ var customAppClusterJSONTpl = `{ var customAppClusterJSONTemplate = template.Must(template.New("").Parse(customAppClusterJSONTpl)) -func customAppClusterJSON(t *testing.T, opts customClusterJSONOptions) string { +func customAppClusterJSON(t testinf.T, opts customClusterJSONOptions) string { t.Helper() var buf bytes.Buffer err := customAppClusterJSONTemplate.Execute(&buf, opts) diff --git a/agent/xds/delta_test.go b/agent/xds/delta_test.go index fc0f05dca0..de1e9e13a7 100644 --- a/agent/xds/delta_test.go +++ b/agent/xds/delta_test.go @@ -1094,7 +1094,7 @@ func TestServer_DeltaAggregatedResources_v3_ACLEnforcement(t *testing.T) { acl: `service "not-ingress" { policy = "write" }`, token: "service-write-on-not-ingress", wantDenied: true, - cfgSnap: proxycfg.TestConfigSnapshotIngressGateway(t), + cfgSnap: proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", "default", nil, nil, nil), }, } @@ -1384,7 +1384,7 @@ func TestServer_DeltaAggregatedResources_v3_IngressEmptyResponse(t *testing.T) { assertDeltaChanBlocked(t, envoy.deltaStream.sendCh) // Deliver a new snapshot with no services - snap := proxycfg.TestConfigSnapshotIngressGatewayNoServices(t) + snap := proxycfg.TestConfigSnapshotIngressGateway(t, false, "tcp", "default", nil, nil, nil) mgr.DeliverConfig(t, sid, snap) // REQ: clusters diff --git a/agent/xds/endpoints_test.go b/agent/xds/endpoints_test.go index 83886a3714..62aad1e112 100644 --- a/agent/xds/endpoints_test.go +++ b/agent/xds/endpoints_test.go @@ -222,356 +222,268 @@ func TestEndpointsFromSnapshot(t *testing.T) { } tests := []struct { - name string - create func(t testinf.T) *proxycfg.ConfigSnapshot - // Setup is called before the test starts. It is passed the snapshot from - // create func and is allowed to modify it in any way to setup the - // test input. - setup func(snap *proxycfg.ConfigSnapshot) + name string + create func(t testinf.T) *proxycfg.ConfigSnapshot overrideGoldenName string }{ { - name: "defaults", - create: proxycfg.TestConfigSnapshot, - setup: nil, // Default snapshot - }, - { - name: "mesh-gateway", - create: proxycfg.TestConfigSnapshotMeshGateway, - setup: nil, - }, - { - name: "mesh-gateway-using-federation-states", - create: proxycfg.TestConfigSnapshotMeshGatewayUsingFederationStates, - setup: nil, - }, - { - name: "mesh-gateway-newer-information-in-federation-states", - create: proxycfg.TestConfigSnapshotMeshGatewayNewerInformationInFederationStates, - }, - { - name: "mesh-gateway-older-information-in-federation-states", - create: proxycfg.TestConfigSnapshotMeshGatewayOlderInformationInFederationStates, - }, - { - name: "mesh-gateway-no-services", - create: proxycfg.TestConfigSnapshotMeshGatewayNoServices, - }, - { - name: "connect-proxy-with-chain", - create: proxycfg.TestConfigSnapshotDiscoveryChain, - setup: nil, - }, - { - name: "connect-proxy-with-chain-external-sni", - create: proxycfg.TestConfigSnapshotDiscoveryChainExternalSNI, - setup: nil, - }, - { - name: "connect-proxy-with-chain-and-overrides", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithOverrides, - setup: nil, - }, - { - name: "connect-proxy-with-chain-and-failover", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithFailover, - setup: nil, - }, - { - name: "connect-proxy-with-tcp-chain-failover-through-remote-gateway", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithFailoverThroughRemoteGateway, - setup: nil, - }, - { - name: "connect-proxy-with-tcp-chain-failover-through-remote-gateway-triggered", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithFailoverThroughRemoteGatewayTriggered, - setup: nil, - }, - { - name: "connect-proxy-with-tcp-chain-double-failover-through-remote-gateway", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithDoubleFailoverThroughRemoteGateway, - setup: nil, - }, - { - name: "connect-proxy-with-tcp-chain-double-failover-through-remote-gateway-triggered", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithDoubleFailoverThroughRemoteGatewayTriggered, - setup: nil, - }, - { - name: "connect-proxy-with-tcp-chain-failover-through-local-gateway", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithFailoverThroughLocalGateway, - setup: nil, - }, - { - name: "connect-proxy-with-tcp-chain-failover-through-local-gateway-triggered", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithFailoverThroughLocalGatewayTriggered, - setup: nil, - }, - { - name: "connect-proxy-with-tcp-chain-double-failover-through-local-gateway", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithDoubleFailoverThroughLocalGateway, - setup: nil, - }, - { - name: "connect-proxy-with-tcp-chain-double-failover-through-local-gateway-triggered", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithDoubleFailoverThroughLocalGatewayTriggered, - setup: nil, - }, - { - name: "connect-proxy-with-default-chain-and-custom-cluster", - create: proxycfg.TestConfigSnapshotDiscoveryChainDefault, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Upstreams[0].Config["envoy_cluster_json"] = - customAppClusterJSON(t, customClusterJSONOptions{ - Name: "myservice", - }) - snap.ConnectProxy.UpstreamConfig = map[proxycfg.UpstreamID]*structs.Upstream{ - UID("db"): { - // The local bind port is overridden by the escape hatch, but is required for explicit upstreams. - LocalBindPort: 9191, - Config: map[string]interface{}{ - "envoy_cluster_json": customAppClusterJSON(t, customClusterJSONOptions{ - Name: "myservice", - }), - }, - }, - } + name: "defaults", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, nil, nil) }, }, { - name: "splitter-with-resolver-redirect", - create: proxycfg.TestConfigSnapshotDiscoveryChain_SplitterWithResolverRedirectMultiDC, - setup: nil, - }, - { - name: "mesh-gateway-service-subsets", - create: proxycfg.TestConfigSnapshotMeshGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.MeshGateway.ServiceResolvers = map[structs.ServiceName]*structs.ServiceResolverConfigEntry{ - structs.NewServiceName("bar", nil): { - 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.NewServiceName("foo", nil): { - Kind: structs.ServiceResolver, - Name: "foo", - Subsets: map[string]structs.ServiceResolverSubset{ - "v1": { - Filter: "Service.Meta.version == 1", - }, - "v2": { - Filter: "Service.Meta.version == 2", - OnlyPassing: true, - }, - }, - }, - } + name: "mesh-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotMeshGateway(t, "default", nil, nil) }, }, { - name: "mesh-gateway-default-service-subset", - create: proxycfg.TestConfigSnapshotMeshGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.MeshGateway.ServiceResolvers = map[structs.ServiceName]*structs.ServiceResolverConfigEntry{ - structs.NewServiceName("bar", nil): { - 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.NewServiceName("foo", nil): { - 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, - }, - }, - }, - } + name: "mesh-gateway-using-federation-states", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotMeshGateway(t, "federation-states", nil, nil) }, }, { - name: "ingress-gateway", - create: proxycfg.TestConfigSnapshotIngressGateway, - setup: nil, + name: "mesh-gateway-newer-information-in-federation-states", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotMeshGateway(t, "newer-info-in-federation-states", nil, nil) + }, }, { - name: "ingress-gateway-no-services", - create: proxycfg.TestConfigSnapshotIngressGatewayNoServices, - setup: nil, + name: "mesh-gateway-older-information-in-federation-states", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotMeshGateway(t, "older-info-in-federation-states", nil, nil) + }, }, { - name: "ingress-with-chain", - create: proxycfg.TestConfigSnapshotIngress, - setup: nil, + name: "mesh-gateway-no-services", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotMeshGateway(t, "no-services", nil, nil) + }, }, { - name: "ingress-with-chain-external-sni", - create: proxycfg.TestConfigSnapshotIngressExternalSNI, - setup: nil, + name: "connect-proxy-with-chain", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "simple", nil, nil) + }, }, { - name: "ingress-with-chain-and-overrides", - create: proxycfg.TestConfigSnapshotIngressWithOverrides, - setup: nil, + name: "connect-proxy-with-chain-external-sni", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "external-sni", nil, nil) + }, }, { - name: "ingress-with-chain-and-failover", - create: proxycfg.TestConfigSnapshotIngressWithFailover, - setup: nil, + name: "connect-proxy-with-chain-and-overrides", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "simple-with-overrides", nil, nil) + }, }, { - name: "ingress-with-tcp-chain-failover-through-remote-gateway", - create: proxycfg.TestConfigSnapshotIngressWithFailoverThroughRemoteGateway, - setup: nil, + name: "connect-proxy-with-chain-and-failover", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover", nil, nil) + }, }, { - name: "ingress-with-tcp-chain-failover-through-remote-gateway-triggered", - create: proxycfg.TestConfigSnapshotIngressWithFailoverThroughRemoteGatewayTriggered, - setup: nil, + name: "connect-proxy-with-tcp-chain-failover-through-remote-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover-through-remote-gateway", nil, nil) + }, }, { - name: "ingress-with-tcp-chain-double-failover-through-remote-gateway", - create: proxycfg.TestConfigSnapshotIngressWithDoubleFailoverThroughRemoteGateway, - setup: nil, + name: "connect-proxy-with-tcp-chain-failover-through-remote-gateway-triggered", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover-through-remote-gateway-triggered", nil, nil) + }, }, { - name: "ingress-with-tcp-chain-double-failover-through-remote-gateway-triggered", - create: proxycfg.TestConfigSnapshotIngressWithDoubleFailoverThroughRemoteGatewayTriggered, - setup: nil, + name: "connect-proxy-with-tcp-chain-double-failover-through-remote-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover-through-double-remote-gateway", nil, nil) + }, }, { - name: "ingress-with-tcp-chain-failover-through-local-gateway", - create: proxycfg.TestConfigSnapshotIngressWithFailoverThroughLocalGateway, - setup: nil, + name: "connect-proxy-with-tcp-chain-double-failover-through-remote-gateway-triggered", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover-through-double-remote-gateway-triggered", nil, nil) + }, }, { - name: "ingress-with-tcp-chain-failover-through-local-gateway-triggered", - create: proxycfg.TestConfigSnapshotIngressWithFailoverThroughLocalGatewayTriggered, - setup: nil, + name: "connect-proxy-with-tcp-chain-failover-through-local-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover-through-local-gateway", nil, nil) + }, }, { - name: "ingress-with-tcp-chain-double-failover-through-local-gateway", - create: proxycfg.TestConfigSnapshotIngressWithDoubleFailoverThroughLocalGateway, - setup: nil, + name: "connect-proxy-with-tcp-chain-failover-through-local-gateway-triggered", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover-through-local-gateway-triggered", nil, nil) + }, }, { - name: "ingress-with-tcp-chain-double-failover-through-local-gateway-triggered", - create: proxycfg.TestConfigSnapshotIngressWithDoubleFailoverThroughLocalGatewayTriggered, - setup: nil, + name: "connect-proxy-with-tcp-chain-double-failover-through-local-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover-through-double-local-gateway", nil, nil) + }, }, { - name: "ingress-splitter-with-resolver-redirect", - create: proxycfg.TestConfigSnapshotIngress_SplitterWithResolverRedirectMultiDC, - setup: nil, + name: "connect-proxy-with-tcp-chain-double-failover-through-local-gateway-triggered", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover-through-double-local-gateway-triggered", nil, nil) + }, }, { - name: "terminating-gateway", - create: proxycfg.TestConfigSnapshotTerminatingGateway, - setup: nil, + name: "connect-proxy-with-default-chain-and-custom-cluster", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", func(ns *structs.NodeService) { + ns.Proxy.Upstreams[0].Config["envoy_cluster_json"] = + customAppClusterJSON(t, customClusterJSONOptions{ + Name: "myservice", + }) + }, nil) + }, }, { - name: "terminating-gateway-no-services", - create: proxycfg.TestConfigSnapshotTerminatingGatewayNoServices, - setup: nil, + name: "splitter-with-resolver-redirect", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "splitter-with-resolver-redirect-multidc", nil, nil) + }, + }, + { + name: "mesh-gateway-service-subsets", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotMeshGateway(t, "service-subsets2", nil, nil) + }, + }, + { + name: "mesh-gateway-default-service-subset", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotMeshGateway(t, "default-service-subsets2", nil, nil) + }, + }, + { + name: "ingress-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "default", nil, nil, nil) + }, + }, + { + name: "ingress-gateway-no-services", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, false, "tcp", + "default", nil, nil, nil) + }, + }, + { + name: "ingress-with-chain", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "simple", nil, nil, nil) + }, + }, + { + name: "ingress-with-chain-external-sni", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "external-sni", nil, nil, nil) + }, + }, + { + name: "ingress-with-chain-and-failover", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "failover", nil, nil, nil) + }, + }, + { + name: "ingress-with-tcp-chain-failover-through-remote-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "failover-through-remote-gateway", nil, nil, nil) + }, + }, + { + name: "ingress-with-tcp-chain-failover-through-remote-gateway-triggered", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "failover-through-remote-gateway-triggered", nil, nil, nil) + }, + }, + { + name: "ingress-with-tcp-chain-double-failover-through-remote-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "failover-through-double-remote-gateway", nil, nil, nil) + }, + }, + { + name: "ingress-with-tcp-chain-double-failover-through-remote-gateway-triggered", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "failover-through-double-remote-gateway-triggered", nil, nil, nil) + }, + }, + { + name: "ingress-with-tcp-chain-failover-through-local-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "failover-through-local-gateway", nil, nil, nil) + }, + }, + { + name: "ingress-with-tcp-chain-failover-through-local-gateway-triggered", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "failover-through-local-gateway-triggered", nil, nil, nil) + }, + }, + { + name: "ingress-with-tcp-chain-double-failover-through-local-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "failover-through-double-local-gateway", nil, nil, nil) + }, + }, + { + name: "ingress-with-tcp-chain-double-failover-through-local-gateway-triggered", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "failover-through-double-local-gateway-triggered", nil, nil, nil) + }, + }, + { + name: "ingress-splitter-with-resolver-redirect", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "http", + "splitter-with-resolver-redirect-multidc", nil, nil, nil) + }, + }, + { + name: "terminating-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotTerminatingGateway(t, true, nil, nil) + }, + }, + { + name: "terminating-gateway-no-services", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotTerminatingGateway(t, false, nil, nil) + }, }, { name: "terminating-gateway-service-subsets", - create: proxycfg.TestConfigSnapshotTerminatingGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.TerminatingGateway.ServiceResolvers = map[structs.ServiceName]*structs.ServiceResolverConfigEntry{ - structs.NewServiceName("web", nil): { - Kind: structs.ServiceResolver, - Name: "web", - Subsets: map[string]structs.ServiceResolverSubset{ - "v1": { - Filter: "Service.Meta.version == 1", - }, - "v2": { - Filter: "Service.Meta.version == 2", - OnlyPassing: true, - }, - }, - }, - structs.NewServiceName("web", nil): { - Kind: structs.ServiceResolver, - Name: "web", - Subsets: map[string]structs.ServiceResolverSubset{ - "v1": { - Filter: "Service.Meta.version == 1", - }, - "v2": { - Filter: "Service.Meta.version == 2", - OnlyPassing: true, - }, - }, - }, - } - }, + create: proxycfg.TestConfigSnapshotTerminatingGatewayServiceSubsets, }, { name: "terminating-gateway-default-service-subset", - create: proxycfg.TestConfigSnapshotTerminatingGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.TerminatingGateway.ServiceResolvers = map[structs.ServiceName]*structs.ServiceResolverConfigEntry{ - structs.NewServiceName("web", nil): { - Kind: structs.ServiceResolver, - Name: "web", - DefaultSubset: "v2", - Subsets: map[string]structs.ServiceResolverSubset{ - "v1": { - Filter: "Service.Meta.version == 1", - }, - "v2": { - Filter: "Service.Meta.version == 2", - OnlyPassing: true, - }, - }, - }, - structs.NewServiceName("web", nil): { - Kind: structs.ServiceResolver, - Name: "web", - DefaultSubset: "v2", - Subsets: map[string]structs.ServiceResolverSubset{ - "v1": { - Filter: "Service.Meta.version == 1", - }, - "v2": { - Filter: "Service.Meta.version == 2", - OnlyPassing: true, - }, - }, - }, - } - }, + create: proxycfg.TestConfigSnapshotTerminatingGatewayDefaultServiceSubset, }, { name: "ingress-multiple-listeners-duplicate-service", create: proxycfg.TestConfigSnapshotIngress_MultipleListenersDuplicateService, - setup: nil, }, } @@ -590,10 +502,6 @@ func TestEndpointsFromSnapshot(t *testing.T) { // golden files for every test case and so not be any use! setupTLSRootsAndLeaf(t, snap) - if tt.setup != nil { - tt.setup(snap) - } - // Need server just for logger dependency g := newResourceGenerator(testutil.Logger(t), nil, nil, false) g.ProxyFeatures = sf diff --git a/agent/xds/listeners_ingress.go b/agent/xds/listeners_ingress.go index 899e842d45..3ab7de3c06 100644 --- a/agent/xds/listeners_ingress.go +++ b/agent/xds/listeners_ingress.go @@ -22,7 +22,7 @@ func (s *ResourceGenerator) makeIngressGatewayListeners(address string, cfgSnap for listenerKey, upstreams := range cfgSnap.IngressGateway.Upstreams { listenerCfg, ok := cfgSnap.IngressGateway.Listeners[listenerKey] if !ok { - return nil, fmt.Errorf("no listener config found for listener on port %d", listenerKey.Port) + return nil, fmt.Errorf("no listener config found for listener on proto/port %s/%d", listenerKey.Protocol, listenerKey.Port) } tlsContext, err := makeDownstreamTLSContextFromSnapshotListenerConfig(cfgSnap, listenerCfg) diff --git a/agent/xds/listeners_test.go b/agent/xds/listeners_test.go index 9f57af704f..5a3a018326 100644 --- a/agent/xds/listeners_test.go +++ b/agent/xds/listeners_test.go @@ -13,8 +13,7 @@ import ( testinf "github.com/mitchellh/go-testing-interface" "github.com/stretchr/testify/require" - "github.com/hashicorp/consul/agent/connect" - "github.com/hashicorp/consul/agent/consul/discoverychain" + "github.com/hashicorp/consul/agent/cache" "github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/xds/proxysupport" @@ -38,185 +37,195 @@ func TestListenersFromSnapshot(t *testing.T) { generatorSetup func(*ResourceGenerator) }{ { - name: "defaults", - create: proxycfg.TestConfigSnapshot, - setup: nil, // Default snapshot - }, - { - name: "listener-bind-address", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config["bind_address"] = "127.0.0.2" + name: "defaults", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, nil, nil) }, }, { - name: "listener-bind-port", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config["bind_port"] = 8888 + name: "listener-bind-address", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + ns.Proxy.Config["bind_address"] = "127.0.0.2" + }, nil) }, }, { - name: "listener-bind-address-port", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config["bind_address"] = "127.0.0.2" - snap.Proxy.Config["bind_port"] = 8888 + name: "listener-bind-port", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + ns.Proxy.Config["bind_port"] = 8888 + }, nil) }, }, { - name: "listener-unix-domain-socket", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Upstreams[0].LocalBindAddress = "" - snap.Proxy.Upstreams[0].LocalBindPort = 0 - snap.Proxy.Upstreams[0].LocalBindSocketPath = "/tmp/service-mesh/client-1/grpc-employee-server" - snap.Proxy.Upstreams[0].LocalBindSocketMode = "0640" - - snap.ConnectProxy.UpstreamConfig = proxycfg.UpstreamsToMap(snap.Proxy.Upstreams) + name: "listener-bind-address-port", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + ns.Proxy.Config["bind_address"] = "127.0.0.2" + ns.Proxy.Config["bind_port"] = 8888 + }, nil) }, }, { - name: "http-public-listener", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config["protocol"] = "http" + name: "listener-unix-domain-socket", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + ns.Proxy.Upstreams[0].LocalBindAddress = "" + ns.Proxy.Upstreams[0].LocalBindPort = 0 + ns.Proxy.Upstreams[0].LocalBindSocketPath = "/tmp/service-mesh/client-1/grpc-employee-server" + ns.Proxy.Upstreams[0].LocalBindSocketMode = "0640" + }, nil) }, }, { - name: "http-listener-with-timeouts", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config["protocol"] = "http" - snap.Proxy.Config["local_connect_timeout_ms"] = 1234 - snap.Proxy.Config["local_request_timeout_ms"] = 2345 + name: "http-public-listener", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + ns.Proxy.Config["protocol"] = "http" + }, nil) }, }, { - name: "http-upstream", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Upstreams[0].Config["protocol"] = "http" - - snap.ConnectProxy.UpstreamConfig = proxycfg.UpstreamsToMap(snap.Proxy.Upstreams) + name: "http-listener-with-timeouts", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + ns.Proxy.Config["protocol"] = "http" + ns.Proxy.Config["local_connect_timeout_ms"] = 1234 + ns.Proxy.Config["local_request_timeout_ms"] = 2345 + }, nil) }, }, { - name: "custom-public-listener", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config["envoy_public_listener_json"] = - customListenerJSON(t, customListenerJSONOptions{ - Name: "custom-public-listen", - }) + name: "http-upstream", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + ns.Proxy.Upstreams[0].Config["protocol"] = "http" + }, nil) }, }, { - name: "custom-public-listener-http", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config["protocol"] = "http" - snap.Proxy.Config["envoy_public_listener_json"] = - customHTTPListenerJSON(t, customHTTPListenerJSONOptions{ - Name: "custom-public-listen", - }) + name: "custom-public-listener", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + ns.Proxy.Config["envoy_public_listener_json"] = + customListenerJSON(t, customListenerJSONOptions{ + Name: "custom-public-listen", + }) + }, nil) }, }, { - name: "custom-public-listener-http-2", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config["protocol"] = "http" - snap.Proxy.Config["envoy_public_listener_json"] = - customHTTPListenerJSON(t, customHTTPListenerJSONOptions{ - Name: "custom-public-listen", - HTTPConnectionManagerName: httpConnectionManagerNewName, - }) + name: "custom-public-listener-http", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + ns.Proxy.Config["protocol"] = "http" + ns.Proxy.Config["envoy_public_listener_json"] = + customHTTPListenerJSON(t, customHTTPListenerJSONOptions{ + Name: "custom-public-listen", + }) + }, nil) }, }, { - name: "custom-public-listener-http-missing", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config["protocol"] = "http" - snap.Proxy.Config["envoy_public_listener_json"] = - customListenerJSON(t, customListenerJSONOptions{ - Name: "custom-public-listen", - }) + name: "custom-public-listener-http-2", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + ns.Proxy.Config["protocol"] = "http" + ns.Proxy.Config["envoy_public_listener_json"] = + customHTTPListenerJSON(t, customHTTPListenerJSONOptions{ + Name: "custom-public-listen", + HTTPConnectionManagerName: httpConnectionManagerNewName, + }) + }, nil) + }, + }, + { + name: "custom-public-listener-http-missing", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + ns.Proxy.Config["protocol"] = "http" + ns.Proxy.Config["envoy_public_listener_json"] = + customListenerJSON(t, customListenerJSONOptions{ + Name: "custom-public-listen", + }) + }, nil) }, }, { name: "custom-public-listener-ignores-tls", - create: proxycfg.TestConfigSnapshot, overrideGoldenName: "custom-public-listener", // should be the same - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config["envoy_public_listener_json"] = - customListenerJSON(t, customListenerJSONOptions{ - Name: "custom-public-listen", - // Attempt to override the TLS context should be ignored - TLSContext: `"allowRenegotiation": false`, - }) - }, - }, - { - name: "custom-upstream", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - for i := range snap.Proxy.Upstreams { - if snap.Proxy.Upstreams[i].DestinationName != "db" { - continue // only tweak the db upstream - } - if snap.Proxy.Upstreams[i].Config == nil { - snap.Proxy.Upstreams[i].Config = map[string]interface{}{} - } - - uid := proxycfg.NewUpstreamID(&snap.Proxy.Upstreams[i]) - - snap.Proxy.Upstreams[i].Config["envoy_listener_json"] = + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + ns.Proxy.Config["envoy_public_listener_json"] = customListenerJSON(t, customListenerJSONOptions{ - Name: uid.EnvoyID() + ":custom-upstream", + Name: "custom-public-listen", + // Attempt to override the TLS context should be ignored + TLSContext: `"allowRenegotiation": false`, }) - } - snap.ConnectProxy.UpstreamConfig = proxycfg.UpstreamsToMap(snap.Proxy.Upstreams) + }, nil) }, }, { - name: "custom-upstream-ignored-with-disco-chain", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithFailover, - setup: func(snap *proxycfg.ConfigSnapshot) { - for i := range snap.Proxy.Upstreams { - if snap.Proxy.Upstreams[i].DestinationName != "db" { - continue // only tweak the db upstream + name: "custom-upstream", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + for i := range ns.Proxy.Upstreams { + if ns.Proxy.Upstreams[i].DestinationName != "db" { + continue // only tweak the db upstream + } + if ns.Proxy.Upstreams[i].Config == nil { + ns.Proxy.Upstreams[i].Config = map[string]interface{}{} + } + + uid := proxycfg.NewUpstreamID(&ns.Proxy.Upstreams[i]) + + ns.Proxy.Upstreams[i].Config["envoy_listener_json"] = + customListenerJSON(t, customListenerJSONOptions{ + Name: uid.EnvoyID() + ":custom-upstream", + }) } - if snap.Proxy.Upstreams[i].Config == nil { - snap.Proxy.Upstreams[i].Config = map[string]interface{}{} - } - - uid := proxycfg.NewUpstreamID(&snap.Proxy.Upstreams[i]) - - snap.Proxy.Upstreams[i].Config["envoy_listener_json"] = - customListenerJSON(t, customListenerJSONOptions{ - Name: uid.EnvoyID() + ":custom-upstream", - }) - } - - snap.ConnectProxy.UpstreamConfig = proxycfg.UpstreamsToMap(snap.Proxy.Upstreams) + }, nil) }, }, { - name: "splitter-with-resolver-redirect", - create: proxycfg.TestConfigSnapshotDiscoveryChain_SplitterWithResolverRedirectMultiDC, - setup: nil, + name: "custom-upstream-ignored-with-disco-chain", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover", func(ns *structs.NodeService) { + for i := range ns.Proxy.Upstreams { + if ns.Proxy.Upstreams[i].DestinationName != "db" { + continue // only tweak the db upstream + } + if ns.Proxy.Upstreams[i].Config == nil { + ns.Proxy.Upstreams[i].Config = map[string]interface{}{} + } + + uid := proxycfg.NewUpstreamID(&ns.Proxy.Upstreams[i]) + + ns.Proxy.Upstreams[i].Config["envoy_listener_json"] = + customListenerJSON(t, customListenerJSONOptions{ + Name: uid.EnvoyID() + ":custom-upstream", + }) + } + }, nil) + }, }, { - name: "connect-proxy-with-tcp-chain", - create: proxycfg.TestConfigSnapshotDiscoveryChain, - setup: nil, + name: "splitter-with-resolver-redirect", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "splitter-with-resolver-redirect-multidc", nil, nil) + }, + }, + { + name: "connect-proxy-with-tcp-chain", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "simple", nil, nil) + }, }, { name: "connect-proxy-with-http-chain", create: func(t testinf.T) *proxycfg.ConfigSnapshot { - return proxycfg.TestConfigSnapshotDiscoveryChainWithEntries(t, + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "simple", nil, nil, &structs.ProxyConfigEntry{ Kind: structs.ProxyDefaults, Name: structs.ProxyConfigGlobal, @@ -226,12 +235,11 @@ func TestListenersFromSnapshot(t *testing.T) { }, ) }, - setup: nil, }, { name: "connect-proxy-with-http2-chain", create: func(t testinf.T) *proxycfg.ConfigSnapshot { - return proxycfg.TestConfigSnapshotDiscoveryChainWithEntries(t, + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "simple", nil, nil, &structs.ProxyConfigEntry{ Kind: structs.ProxyDefaults, Name: structs.ProxyConfigGlobal, @@ -241,12 +249,11 @@ func TestListenersFromSnapshot(t *testing.T) { }, ) }, - setup: nil, }, { name: "connect-proxy-with-grpc-chain", create: func(t testinf.T) *proxycfg.ConfigSnapshot { - return proxycfg.TestConfigSnapshotDiscoveryChainWithEntries(t, + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "simple", nil, nil, &structs.ProxyConfigEntry{ Kind: structs.ProxyDefaults, Name: structs.ProxyConfigGlobal, @@ -256,71 +263,79 @@ func TestListenersFromSnapshot(t *testing.T) { }, ) }, - setup: nil, }, { - name: "connect-proxy-with-chain-external-sni", - create: proxycfg.TestConfigSnapshotDiscoveryChainExternalSNI, - setup: nil, - }, - { - name: "connect-proxy-with-chain-and-overrides", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithOverrides, - setup: nil, - }, - { - name: "connect-proxy-with-tcp-chain-failover-through-remote-gateway", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithFailoverThroughRemoteGateway, - setup: nil, - }, - { - name: "connect-proxy-with-tcp-chain-failover-through-local-gateway", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithFailoverThroughLocalGateway, - setup: nil, - }, - { - name: "connect-proxy-upstream-defaults", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - for _, v := range snap.ConnectProxy.UpstreamConfig { - // Prepared queries do not get centrally configured upstream defaults merged into them. - if v.DestinationType == structs.UpstreamDestTypePreparedQuery { - continue - } - // Represent upstream config as if it came from centrally configured upstream defaults. - // The name/namespace must not make it onto the cluster name attached to the outbound listener. - v.CentrallyConfigured = true - v.DestinationNamespace = structs.WildcardSpecifier - v.DestinationName = structs.WildcardSpecifier - } + name: "connect-proxy-with-chain-external-sni", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "external-sni", nil, nil) }, }, { - name: "expose-paths-local-app-paths", - create: proxycfg.TestConfigSnapshotExposeConfig, + name: "connect-proxy-with-chain-and-overrides", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "simple-with-overrides", nil, nil) + }, }, { - name: "expose-paths-new-cluster-http2", - create: proxycfg.TestConfigSnapshotExposeConfig, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Expose.Paths[1] = structs.ExposePath{ - LocalPathPort: 9090, - Path: "/grpc.health.v1.Health/Check", - ListenerPort: 21501, - Protocol: "http2", - } + name: "connect-proxy-with-tcp-chain-failover-through-remote-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover-through-remote-gateway", nil, nil) + }, + }, + { + name: "connect-proxy-with-tcp-chain-failover-through-local-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover-through-local-gateway", nil, nil) + }, + }, + { + name: "connect-proxy-upstream-defaults", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + for _, v := range ns.Proxy.Upstreams { + // Prepared queries do not get centrally configured upstream defaults merged into them. + if v.DestinationType == structs.UpstreamDestTypePreparedQuery { + continue + } + // Represent upstream config as if it came from centrally configured upstream defaults. + // The name/namespace must not make it onto the cluster name attached to the outbound listener. + v.CentrallyConfigured = true + v.DestinationNamespace = structs.WildcardSpecifier + v.DestinationName = structs.WildcardSpecifier + } + }, nil) + }, + }, + { + name: "expose-paths-local-app-paths", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotExposeConfig(t, nil) + }, + }, + { + name: "expose-paths-new-cluster-http2", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotExposeConfig(t, func(ns *structs.NodeService) { + ns.Proxy.Expose.Paths[1] = structs.ExposePath{ + LocalPathPort: 9090, + Path: "/grpc.health.v1.Health/Check", + ListenerPort: 21501, + Protocol: "http2", + } + }) }, }, { // NOTE: if IPv6 is not supported in the kernel per // kernelSupportsIPv6() then this test will fail because the golden // files were generated assuming ipv6 support was present - name: "expose-checks", - create: proxycfg.TestConfigSnapshotExposeConfig, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Expose = structs.ExposeConfig{ - Checks: true, - } + name: "expose-checks", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotExposeConfig(t, func(ns *structs.NodeService) { + ns.Proxy.Expose = structs.ExposeConfig{ + Checks: true, + } + }) }, generatorSetup: func(s *ResourceGenerator) { s.CfgFetcher = configFetcherFunc(func() string { @@ -344,1020 +359,276 @@ func TestListenersFromSnapshot(t *testing.T) { }, }, { - name: "mesh-gateway", - create: proxycfg.TestConfigSnapshotMeshGateway, - }, - { - name: "mesh-gateway-using-federation-states", - create: proxycfg.TestConfigSnapshotMeshGatewayUsingFederationStates, - }, - { - name: "mesh-gateway-no-services", - create: proxycfg.TestConfigSnapshotMeshGatewayNoServices, - }, - { - name: "mesh-gateway-tagged-addresses", - create: proxycfg.TestConfigSnapshotMeshGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config = map[string]interface{}{ - "envoy_mesh_gateway_no_default_bind": true, - "envoy_mesh_gateway_bind_tagged_addresses": true, - } + name: "mesh-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotMeshGateway(t, "default", nil, nil) }, }, { - name: "mesh-gateway-custom-addresses", - create: proxycfg.TestConfigSnapshotMeshGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config = map[string]interface{}{ - "envoy_mesh_gateway_bind_addresses": map[string]structs.ServiceAddress{ - "foo": { - Address: "198.17.2.3", - Port: 8080, - }, - "bar": { - Address: "2001:db8::ff", - Port: 9999, - }, - "baz": { - Address: "127.0.0.1", - Port: 8765, - }, - }, - } + name: "mesh-gateway-using-federation-states", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotMeshGateway(t, "federation-states", nil, nil) }, }, { - name: "ingress-gateway", - create: proxycfg.TestConfigSnapshotIngressGateway, - setup: nil, - }, - { - name: "ingress-gateway-bind-addrs", - create: proxycfg.TestConfigSnapshotIngressGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.TaggedAddresses = map[string]structs.ServiceAddress{ - "lan": {Address: "10.0.0.1"}, - "wan": {Address: "172.16.0.1"}, - } - snap.Proxy.Config = map[string]interface{}{ - "envoy_gateway_no_default_bind": true, - "envoy_gateway_bind_tagged_addresses": true, - "envoy_gateway_bind_addresses": map[string]structs.ServiceAddress{ - "foo": {Address: "8.8.8.8"}, - }, - } + name: "mesh-gateway-no-services", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotMeshGateway(t, "no-services", nil, nil) }, }, { - name: "ingress-gateway-no-services", - create: proxycfg.TestConfigSnapshotIngressGatewayNoServices, - setup: nil, + name: "mesh-gateway-tagged-addresses", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotMeshGateway(t, "default", func(ns *structs.NodeService) { + ns.Proxy.Config = map[string]interface{}{ + "envoy_mesh_gateway_no_default_bind": true, + "envoy_mesh_gateway_bind_tagged_addresses": true, + } + }, nil) + }, }, { - name: "ingress-with-chain-external-sni", - create: proxycfg.TestConfigSnapshotIngressExternalSNI, - setup: nil, - }, - { - name: "ingress-with-chain-and-overrides", - create: proxycfg.TestConfigSnapshotIngressWithOverrides, - setup: nil, - }, - { - name: "ingress-with-tcp-chain-failover-through-remote-gateway", - create: proxycfg.TestConfigSnapshotIngressWithFailoverThroughRemoteGateway, - setup: nil, - }, - { - name: "ingress-with-tcp-chain-failover-through-local-gateway", - create: proxycfg.TestConfigSnapshotIngressWithFailoverThroughLocalGateway, - setup: nil, - }, - { - name: "ingress-splitter-with-resolver-redirect", - create: proxycfg.TestConfigSnapshotIngress_SplitterWithResolverRedirectMultiDC, - setup: nil, - }, - { - name: "terminating-gateway", - create: proxycfg.TestConfigSnapshotTerminatingGateway, - setup: nil, - }, - { - name: "terminating-gateway-no-services", - create: proxycfg.TestConfigSnapshotTerminatingGatewayNoServices, - setup: nil, - }, - { - name: "terminating-gateway-custom-and-tagged-addresses", - create: proxycfg.TestConfigSnapshotTerminatingGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config = map[string]interface{}{ - "envoy_gateway_no_default_bind": true, - "envoy_gateway_bind_tagged_addresses": true, - "envoy_gateway_bind_addresses": map[string]structs.ServiceAddress{ - // This bind address should not get a listener due to deduplication and it sorts to the end - "z-duplicate-of-tagged-wan-addr": { - Address: "198.18.0.1", - Port: 443, + name: "mesh-gateway-custom-addresses", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotMeshGateway(t, "default", func(ns *structs.NodeService) { + ns.Proxy.Config = map[string]interface{}{ + "envoy_mesh_gateway_bind_addresses": map[string]structs.ServiceAddress{ + "foo": { + Address: "198.17.2.3", + Port: 8080, + }, + "bar": { + Address: "2001:db8::ff", + Port: 9999, + }, + "baz": { + Address: "127.0.0.1", + Port: 8765, + }, }, - "foo": { - Address: "198.17.2.3", - Port: 8080, + } + }, nil) + }, + }, + { + name: "ingress-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", "default", nil, nil, nil) + }, + }, + { + name: "ingress-gateway-bind-addrs", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", "default", func(ns *structs.NodeService) { + // + ns.TaggedAddresses = map[string]structs.ServiceAddress{ + "lan": {Address: "10.0.0.1"}, + "wan": {Address: "172.16.0.1"}, + } + ns.Proxy.Config = map[string]interface{}{ + "envoy_gateway_no_default_bind": true, + "envoy_gateway_bind_tagged_addresses": true, + "envoy_gateway_bind_addresses": map[string]structs.ServiceAddress{ + "foo": {Address: "8.8.8.8"}, }, - }, - } + } + }, nil, nil) + }, + }, + { + name: "ingress-gateway-no-services", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, false, "tcp", + "default", nil, nil, nil) + }, + }, + { + name: "ingress-with-chain-external-sni", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "external-sni", nil, nil, nil) + }, + }, + { + name: "ingress-with-tcp-chain-failover-through-remote-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "failover-through-remote-gateway", nil, nil, nil) + }, + }, + { + name: "ingress-with-tcp-chain-failover-through-local-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "failover-through-local-gateway", nil, nil, nil) + }, + }, + { + name: "ingress-splitter-with-resolver-redirect", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "http", + "splitter-with-resolver-redirect-multidc", nil, nil, nil) + }, + }, + { + name: "terminating-gateway", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotTerminatingGateway(t, true, nil, nil) + }, + }, + { + name: "terminating-gateway-no-services", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotTerminatingGateway(t, false, nil, nil) + }, + }, + { + name: "terminating-gateway-custom-and-tagged-addresses", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotTerminatingGateway(t, true, func(ns *structs.NodeService) { + ns.Proxy.Config = map[string]interface{}{ + "envoy_gateway_no_default_bind": true, + "envoy_gateway_bind_tagged_addresses": true, + "envoy_gateway_bind_addresses": map[string]structs.ServiceAddress{ + // This bind address should not get a listener due to deduplication and it sorts to the end + "z-duplicate-of-tagged-wan-addr": { + Address: "198.18.0.1", + Port: 443, + }, + "foo": { + Address: "198.17.2.3", + Port: 8080, + }, + }, + } + }, nil) }, }, { name: "terminating-gateway-service-subsets", - create: proxycfg.TestConfigSnapshotTerminatingGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.TerminatingGateway.ServiceResolvers = map[structs.ServiceName]*structs.ServiceResolverConfigEntry{ - structs.NewServiceName("web", nil): { - Kind: structs.ServiceResolver, - Name: "web", - Subsets: map[string]structs.ServiceResolverSubset{ - "v1": { - Filter: "Service.Meta.version == 1", - }, - "v2": { - Filter: "Service.Meta.version == 2", - OnlyPassing: true, - }, - }, - }, - } - snap.TerminatingGateway.ServiceConfigs[structs.NewServiceName("web", nil)] = &structs.ServiceConfigResponse{ - ProxyConfig: map[string]interface{}{"protocol": "http"}, - } - }, + create: proxycfg.TestConfigSnapshotTerminatingGatewayServiceSubsets, }, { name: "ingress-http-multiple-services", create: proxycfg.TestConfigSnapshotIngress_HTTPMultipleServices, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.IngressGateway.Upstreams = map[proxycfg.IngressListenerKey]structs.Upstreams{ - {Protocol: "http", Port: 8080}: { - { - DestinationName: "foo", - LocalBindPort: 8080, - }, - { - DestinationName: "bar", - LocalBindPort: 8080, - }, + }, + { + name: "terminating-gateway-no-api-cert", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + api := structs.NewServiceName("api", nil) + return proxycfg.TestConfigSnapshotTerminatingGateway(t, true, nil, []cache.UpdateEvent{ + { + CorrelationID: "service-leaf:" + api.String(), // serviceLeafIDPrefix + Result: nil, // tombstone this }, - {Protocol: "http", Port: 443}: { - { - DestinationName: "baz", - LocalBindPort: 443, - }, - { - DestinationName: "qux", - LocalBindPort: 443, - }, - }, - } - snap.IngressGateway.Listeners = map[proxycfg.IngressListenerKey]structs.IngressListener{ - {Protocol: "http", Port: 8080}: {}, - {Protocol: "http", Port: 443}: {}, - } + }) }, }, { - name: "terminating-gateway-no-api-cert", - create: proxycfg.TestConfigSnapshotTerminatingGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.TerminatingGateway.ServiceLeaves[structs.NewServiceName("api", nil)] = nil + name: "ingress-with-tls-listener", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", "default", nil, + func(entry *structs.IngressGatewayConfigEntry) { + entry.TLS.Enabled = true + }, nil) }, }, { - name: "ingress-with-tls-listener", - create: proxycfg.TestConfigSnapshotIngressWithTLSListener, - setup: nil, - }, - { - name: "ingress-with-tls-listener-min-version", - create: proxycfg.TestConfigSnapshotIngressWithTLSListener, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.IngressGateway.TLSConfig.TLSMinVersion = types.TLSv1_3 + name: "ingress-with-tls-listener-min-version", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", "default", nil, + func(entry *structs.IngressGatewayConfigEntry) { + entry.TLS.Enabled = true + entry.TLS.TLSMinVersion = types.TLSv1_3 + }, nil) }, }, { - name: "ingress-with-tls-listener-max-version", - create: proxycfg.TestConfigSnapshotIngressWithTLSListener, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.IngressGateway.TLSConfig.TLSMaxVersion = types.TLSv1_2 + name: "ingress-with-tls-listener-max-version", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", "default", nil, + func(entry *structs.IngressGatewayConfigEntry) { + entry.TLS.Enabled = true + entry.TLS.TLSMaxVersion = types.TLSv1_2 + }, nil) }, }, { - name: "ingress-with-tls-listener-cipher-suites", - create: proxycfg.TestConfigSnapshotIngressWithTLSListener, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.IngressGateway.TLSConfig.CipherSuites = []types.TLSCipherSuite{ - types.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - types.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - } + name: "ingress-with-tls-listener-cipher-suites", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", "default", nil, + func(entry *structs.IngressGatewayConfigEntry) { + entry.TLS.Enabled = true + entry.TLS.CipherSuites = []types.TLSCipherSuite{ + types.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + types.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + } + }, nil) }, }, { - name: "ingress-with-tls-mixed-listeners", - // Use SDS helper even though we aren't testing SDS since it already sets - // up most things we need. - create: proxycfg.TestConfigSnapshotIngressWithGatewaySDS, - setup: func(snap *proxycfg.ConfigSnapshot) { - // Undo gateway-level SDS - snap.IngressGateway.TLSConfig.SDS = nil - - // No Gateway-level built-in TLS - snap.IngressGateway.TLSConfig.Enabled = false - - // One listener has built-in TLS, one doesn't - snap.IngressGateway.Upstreams = map[proxycfg.IngressListenerKey]structs.Upstreams{ - {Protocol: "http", Port: 8080}: { - { - DestinationName: "s1", - LocalBindPort: 8080, - }, - }, - {Protocol: "http", Port: 9090}: { - { - DestinationName: "s2", - LocalBindPort: 9090, - }, - }, - } - snap.IngressGateway.Listeners = map[proxycfg.IngressListenerKey]structs.IngressListener{ - {Protocol: "http", Port: 8080}: { - Port: 8080, - Services: []structs.IngressService{ - { - Name: "s1", - }, - }, - TLS: &structs.GatewayTLSConfig{ - // built-in TLS enabled - Enabled: true, - }, - }, - {Protocol: "http", Port: 9090}: { - Port: 9090, - Services: []structs.IngressService{ - { - Name: "s2", - }, - }, - // No TLS enabled - TLS: nil, - }, - } - }, + name: "ingress-with-tls-mixed-listeners", + create: proxycfg.TestConfigSnapshotIngressGateway_MixedListeners, }, { name: "ingress-with-tls-min-version-listeners-gateway-defaults", - create: proxycfg.TestConfigSnapshotIngressWithTLSListener, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.IngressGateway.TLSConfig.TLSMinVersion = types.TLSv1_2 - - // One listener disables TLS, one inherits TLS minimum version from the gateway - // config, two others set different versions - snap.IngressGateway.Upstreams = map[proxycfg.IngressListenerKey]structs.Upstreams{ - {Protocol: "http", Port: 8080}: { - { - DestinationName: "s1", - LocalBindPort: 8080, - }, - }, - {Protocol: "http", Port: 8081}: { - { - DestinationName: "s2", - LocalBindPort: 8081, - }, - }, - {Protocol: "http", Port: 8082}: { - { - DestinationName: "s3", - LocalBindPort: 8082, - }, - }, - {Protocol: "http", Port: 8083}: { - { - DestinationName: "s4", - LocalBindPort: 8083, - }, - }, - {Protocol: "http", Port: 8084}: { - { - DestinationName: "s4", - LocalBindPort: 8084, - }, - }, - } - snap.IngressGateway.Listeners = map[proxycfg.IngressListenerKey]structs.IngressListener{ - // Omits listener TLS config, should default to gateway TLS config - {Protocol: "http", Port: 8080}: { - Port: 8080, - Services: []structs.IngressService{ - { - Name: "s1", - }, - }, - }, - // Explicitly sets listener TLS config to nil, should default to gateway TLS config - {Protocol: "http", Port: 8081}: { - Port: 8081, - Services: []structs.IngressService{ - { - Name: "s2", - }, - }, - TLS: nil, - }, - // Explicitly enables TLS config, but with no listener default TLS params, - // should default to gateway TLS config - {Protocol: "http", Port: 8082}: { - Port: 8082, - Services: []structs.IngressService{ - { - Name: "s3", - }, - }, - TLS: &structs.GatewayTLSConfig{ - Enabled: true, - }, - }, - // Explicitly unset gateway default TLS min version in favor of proxy default - {Protocol: "http", Port: 8083}: { - Port: 8083, - Services: []structs.IngressService{ - { - Name: "s3", - }, - }, - TLS: &structs.GatewayTLSConfig{ - Enabled: true, - TLSMinVersion: types.TLSVersionAuto, - }, - }, - // Disables listener TLS - {Protocol: "http", Port: 8084}: { - Port: 8084, - Services: []structs.IngressService{ - { - Name: "s4", - }, - }, - TLS: &structs.GatewayTLSConfig{ - Enabled: false, - }, - }, - } - }, + create: proxycfg.TestConfigSnapshotIngressGateway_TLSMinVersionListenersGatewayDefaults, }, { name: "ingress-with-single-tls-listener", - create: proxycfg.TestConfigSnapshotIngress, - setup: func(snap *proxycfg.ConfigSnapshot) { - // One listener should inherit non-TLS gateway config, another - // listener configures TLS with an explicit minimum version - snap.IngressGateway.Upstreams = map[proxycfg.IngressListenerKey]structs.Upstreams{ - {Protocol: "http", Port: 8080}: { - { - DestinationName: "s1", - LocalBindPort: 8080, - }, - }, - {Protocol: "http", Port: 8081}: { - { - DestinationName: "s2", - LocalBindPort: 8081, - }, - }, - } - snap.IngressGateway.Listeners = map[proxycfg.IngressListenerKey]structs.IngressListener{ - {Protocol: "http", Port: 8080}: { - Port: 8080, - Services: []structs.IngressService{ - { - Name: "s1", - }, - }, - }, - {Protocol: "http", Port: 8081}: { - Port: 8081, - Services: []structs.IngressService{ - { - Name: "s2", - }, - }, - TLS: &structs.GatewayTLSConfig{ - Enabled: true, - TLSMinVersion: types.TLSv1_2, - }, - }, - } - }, + create: proxycfg.TestConfigSnapshotIngressGateway_SingleTLSListener, }, { name: "ingress-with-tls-mixed-min-version-listeners", - create: proxycfg.TestConfigSnapshotIngressWithTLSListener, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.IngressGateway.TLSConfig.TLSMinVersion = types.TLSv1_2 - - // One listener should inherit TLS minimum version from the gateway config, - // two others each set explicit TLS minimum versions - snap.IngressGateway.Upstreams = map[proxycfg.IngressListenerKey]structs.Upstreams{ - {Protocol: "http", Port: 8080}: { - { - DestinationName: "s1", - LocalBindPort: 8080, - }, - }, - {Protocol: "http", Port: 8081}: { - { - DestinationName: "s2", - LocalBindPort: 8081, - }, - }, - {Protocol: "http", Port: 8082}: { - { - DestinationName: "s3", - LocalBindPort: 8082, - }, - }, - } - snap.IngressGateway.Listeners = map[proxycfg.IngressListenerKey]structs.IngressListener{ - {Protocol: "http", Port: 8080}: { - Port: 8080, - Services: []structs.IngressService{ - { - Name: "s1", - }, - }, - }, - {Protocol: "http", Port: 8081}: { - Port: 8081, - Services: []structs.IngressService{ - { - Name: "s2", - }, - }, - TLS: &structs.GatewayTLSConfig{ - Enabled: true, - TLSMinVersion: types.TLSv1_0, - }, - }, - {Protocol: "http", Port: 8082}: { - Port: 8082, - Services: []structs.IngressService{ - { - Name: "s3", - }, - }, - TLS: &structs.GatewayTLSConfig{ - Enabled: true, - TLSMinVersion: types.TLSv1_3, - }, - }, - } - }, + create: proxycfg.TestConfigSnapshotIngressGateway_TLSMixedMinVersionListeners, }, { name: "ingress-with-sds-listener-gw-level", - create: proxycfg.TestConfigSnapshotIngressWithGatewaySDS, - setup: nil, + create: proxycfg.TestConfigSnapshotIngressGatewaySDS_GatewayLevel, }, { name: "ingress-with-sds-listener-listener-level", - create: proxycfg.TestConfigSnapshotIngressWithGatewaySDS, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.IngressGateway.Upstreams = map[proxycfg.IngressListenerKey]structs.Upstreams{ - {Protocol: "tcp", Port: 8080}: { - { - DestinationName: "db", - LocalBindPort: 8080, - }, - }, - } - snap.IngressGateway.Listeners = map[proxycfg.IngressListenerKey]structs.IngressListener{ - {Protocol: "tcp", Port: 8080}: { - Port: 8080, - TLS: &structs.GatewayTLSConfig{ - SDS: &structs.GatewayTLSSDSConfig{ - // Override the cert, fall back to the cluster at gw level. We - // don't test every possible valid combination here since we - // already did that in TestResolveListenerSDSConfig. This is - // just an extra check to make sure that data is plumbed through - // correctly. - CertResource: "listener-cert", - }, - }, - }, - } - }, + create: proxycfg.TestConfigSnapshotIngressGatewaySDS_GatewayAndListenerLevel, }, { name: "ingress-with-sds-listener-gw-level-http", - create: proxycfg.TestConfigSnapshotIngressWithGatewaySDS, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.IngressGateway.Upstreams = map[proxycfg.IngressListenerKey]structs.Upstreams{ - {Protocol: "http", Port: 8080}: { - { - DestinationName: "foo", - LocalBindPort: 8080, - }, - }, - } - snap.IngressGateway.Listeners = map[proxycfg.IngressListenerKey]structs.IngressListener{ - {Protocol: "http", Port: 8080}: { - Port: 8080, - TLS: &structs.GatewayTLSConfig{ - SDS: &structs.GatewayTLSSDSConfig{ - // Override the cert, fall back to the cluster at gw level. We - // don't test every possible valid combination here since we - // already did that in TestResolveListenerSDSConfig. This is - // just an extra check to make sure that data is plumbed through - // correctly. - CertResource: "listener-cert", - }, - }, - }, - } - }, + create: proxycfg.TestConfigSnapshotIngressGatewaySDS_GatewayAndListenerLevel_HTTP, }, { name: "ingress-with-sds-listener-gw-level-mixed-tls", - create: proxycfg.TestConfigSnapshotIngressWithGatewaySDS, - setup: func(snap *proxycfg.ConfigSnapshot) { - // Disable GW-level defaults so we can mix TLS and non-TLS listeners - snap.IngressGateway.TLSConfig.SDS = nil - - // Setup two TCP listeners, one with and one without SDS config - snap.IngressGateway.Upstreams = map[proxycfg.IngressListenerKey]structs.Upstreams{ - {Protocol: "tcp", Port: 8080}: { - { - DestinationName: "secure", - LocalBindPort: 8080, - }, - }, - {Protocol: "tcp", Port: 9090}: { - { - DestinationName: "insecure", - LocalBindPort: 9090, - }, - }, - } - - // Every ingress upstream has an associated discovery chain in the snapshot - secureChain := discoverychain.TestCompileConfigEntries( - t, - "secure", - "default", - "default", - "dc1", - connect.TestClusterID+".consul", - nil, - ) - snap.IngressGateway.DiscoveryChain[UID("secure")] = secureChain - - insecureChain := discoverychain.TestCompileConfigEntries( - t, - "insecure", - "default", - "default", - "dc1", - connect.TestClusterID+".consul", - nil, - ) - snap.IngressGateway.DiscoveryChain[UID("insecure")] = insecureChain - - snap.IngressGateway.Listeners = map[proxycfg.IngressListenerKey]structs.IngressListener{ - {Protocol: "tcp", Port: 8080}: { - Port: 8080, - TLS: &structs.GatewayTLSConfig{ - SDS: &structs.GatewayTLSSDSConfig{ - ClusterName: "listener-sds-cluster", - CertResource: "listener-cert", - }, - }, - }, - {Protocol: "tcp", Port: 9090}: { - Port: 9090, - TLS: nil, - }, - } - }, + create: proxycfg.TestConfigSnapshotIngressGatewaySDS_GatewayLevel_MixedTLS, }, { name: "ingress-with-sds-service-level", - create: proxycfg.TestConfigSnapshotIngressWithGatewaySDS, - setup: func(snap *proxycfg.ConfigSnapshot) { - // Disable GW-level defaults so we can test only service-level - snap.IngressGateway.TLSConfig.SDS = nil - - // Setup http listeners, one multiple services with SDS - snap.IngressGateway.Upstreams = map[proxycfg.IngressListenerKey]structs.Upstreams{ - {Protocol: "http", Port: 8080}: { - { - DestinationName: "s1", - LocalBindPort: 8080, - }, - { - DestinationName: "s2", - LocalBindPort: 8080, - }, - }, - } - snap.IngressGateway.Listeners = map[proxycfg.IngressListenerKey]structs.IngressListener{ - {Protocol: "http", Port: 8080}: { - Port: 8080, - Services: []structs.IngressService{ - { - Name: "s1", - Hosts: []string{"s1.example.com"}, - TLS: &structs.GatewayServiceTLSConfig{ - SDS: &structs.GatewayTLSSDSConfig{ - ClusterName: "sds-cluster-1", - CertResource: "s1.example.com-cert", - }, - }, - }, - { - Name: "s2", - Hosts: []string{"s2.example.com"}, - TLS: &structs.GatewayServiceTLSConfig{ - SDS: &structs.GatewayTLSSDSConfig{ - ClusterName: "sds-cluster-2", - CertResource: "s2.example.com-cert", - }, - }, - }, - }, - TLS: nil, // no listener-level SDS config - }, - } - }, + create: proxycfg.TestConfigSnapshotIngressGatewaySDS_ServiceLevel, }, { name: "ingress-with-sds-listener+service-level", - create: proxycfg.TestConfigSnapshotIngressWithGatewaySDS, - setup: func(snap *proxycfg.ConfigSnapshot) { - // Disable GW-level defaults so we can test only service-level - snap.IngressGateway.TLSConfig.SDS = nil - - // Setup http listeners, one multiple services with SDS - snap.IngressGateway.Upstreams = map[proxycfg.IngressListenerKey]structs.Upstreams{ - {Protocol: "http", Port: 8080}: { - { - DestinationName: "s1", - LocalBindPort: 8080, - }, - { - DestinationName: "s2", - LocalBindPort: 8080, - }, - }, - } - snap.IngressGateway.Listeners = map[proxycfg.IngressListenerKey]structs.IngressListener{ - {Protocol: "http", Port: 8080}: { - Port: 8080, - Services: []structs.IngressService{ - { - Name: "s1", - Hosts: []string{"s1.example.com"}, - TLS: &structs.GatewayServiceTLSConfig{ - SDS: &structs.GatewayTLSSDSConfig{ - ClusterName: "sds-cluster-1", - CertResource: "s1.example.com-cert", - }, - }, - }, - { - Name: "s2", - // s2 uses the default listener cert - }, - }, - TLS: &structs.GatewayTLSConfig{ - SDS: &structs.GatewayTLSSDSConfig{ - ClusterName: "sds-cluster-2", - CertResource: "*.example.com-cert", - }, - }, - }, - } - }, + create: proxycfg.TestConfigSnapshotIngressGatewaySDS_ListenerAndServiceLevel, }, { name: "ingress-with-sds-service-level-mixed-no-tls", - create: proxycfg.TestConfigSnapshotIngressWithGatewaySDS, - setup: func(snap *proxycfg.ConfigSnapshot) { - // Disable GW-level defaults so we can test only service-level - snap.IngressGateway.TLSConfig.SDS = nil - - // Setup http listeners, one multiple services with SDS - snap.IngressGateway.Upstreams = map[proxycfg.IngressListenerKey]structs.Upstreams{ - {Protocol: "http", Port: 8080}: { - { - DestinationName: "s1", - LocalBindPort: 8080, - }, - { - DestinationName: "s2", - LocalBindPort: 8080, - }, - }, - } - snap.IngressGateway.Listeners = map[proxycfg.IngressListenerKey]structs.IngressListener{ - {Protocol: "http", Port: 8080}: { - Port: 8080, - Services: []structs.IngressService{ - { - Name: "s1", - Hosts: []string{"s1.example.com"}, - TLS: &structs.GatewayServiceTLSConfig{ - SDS: &structs.GatewayTLSSDSConfig{ - ClusterName: "sds-cluster-1", - CertResource: "s1.example.com-cert", - }, - }, - }, - { - Name: "s2", - // s2 has no SDS config so should be non-TLS - }, - }, - TLS: nil, // No listener level TLS setup either - }, - } - }, + create: proxycfg.TestConfigSnapshotIngressGatewaySDS_MixedNoTLS, }, { name: "transparent-proxy", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Mode = structs.ProxyModeTransparent - - snap.ConnectProxy.MeshConfigSet = true - - // DiscoveryChain without an UpstreamConfig should yield a filter chain when in transparent proxy mode - google := structs.NewServiceName("google", nil) - googleUID := proxycfg.NewUpstreamIDFromServiceName(google) - snap.ConnectProxy.IntentionUpstreams = map[proxycfg.UpstreamID]struct{}{ - googleUID: {}, - } - snap.ConnectProxy.DiscoveryChain[googleUID] = discoverychain.TestCompileConfigEntries(t, "google", "default", "default", "dc1", connect.TestClusterID+".consul", nil) - - snap.ConnectProxy.WatchedUpstreamEndpoints[googleUID] = map[string]structs.CheckServiceNodes{ - "google.default.default.dc1": { - structs.CheckServiceNode{ - Node: &structs.Node{ - Address: "8.8.8.8", - Datacenter: "dc1", - }, - Service: &structs.NodeService{ - Service: "google", - Address: "9.9.9.9", - Port: 9090, - TaggedAddresses: map[string]structs.ServiceAddress{ - "virtual": {Address: "10.0.0.1"}, - structs.TaggedAddressVirtualIP: {Address: "240.0.0.1"}, - }, - }, - }, - }, - // Other targets of the discovery chain should be ignored. - // We only match on the upstream's virtual IP, not the IPs of other targets. - "google-v2.default.default.dc1": { - structs.CheckServiceNode{ - Node: &structs.Node{ - Address: "7.7.7.7", - Datacenter: "dc1", - }, - Service: &structs.NodeService{ - Service: "google-v2", - TaggedAddresses: map[string]structs.ServiceAddress{ - "virtual": {Address: "10.10.10.10"}, - }, - }, - }, - }, - } - - // DiscoveryChains without endpoints do not get a filter chain because there are no addresses to match on. - snap.ConnectProxy.DiscoveryChain[UID("no-endpoints")] = discoverychain.TestCompileConfigEntries(t, "no-endpoints", "default", "default", "dc1", connect.TestClusterID+".consul", nil) - }, + create: proxycfg.TestConfigSnapshotTransparentProxy, }, { name: "transparent-proxy-http-upstream", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Mode = structs.ProxyModeTransparent - - snap.ConnectProxy.MeshConfigSet = true - - // DiscoveryChain without an UpstreamConfig should yield a filter chain when in transparent proxy mode - google := structs.NewServiceName("google", nil) - googleUID := proxycfg.NewUpstreamIDFromServiceName(google) - snap.ConnectProxy.IntentionUpstreams = map[proxycfg.UpstreamID]struct{}{ - googleUID: {}, - } - snap.ConnectProxy.DiscoveryChain[googleUID] = discoverychain.TestCompileConfigEntries(t, "google", "default", "default", "dc1", connect.TestClusterID+".consul", nil, - // Set default service protocol to HTTP - &structs.ProxyConfigEntry{ - Kind: structs.ProxyDefaults, - Name: structs.ProxyConfigGlobal, - Config: map[string]interface{}{ - "protocol": "http", - }, - }) - - snap.ConnectProxy.WatchedUpstreamEndpoints[googleUID] = map[string]structs.CheckServiceNodes{ - "google.default.default.dc1": { - structs.CheckServiceNode{ - Node: &structs.Node{ - Address: "8.8.8.8", - Datacenter: "dc1", - }, - Service: &structs.NodeService{ - Service: "google", - Address: "9.9.9.9", - Port: 9090, - TaggedAddresses: map[string]structs.ServiceAddress{ - "virtual": {Address: "10.0.0.1"}, - structs.TaggedAddressVirtualIP: {Address: "240.0.0.1"}, - }, - }, - }, - }, - // Other targets of the discovery chain should be ignored. - // We only match on the upstream's virtual IP, not the IPs of other targets. - "google-v2.default.default.dc1": { - structs.CheckServiceNode{ - Node: &structs.Node{ - Address: "7.7.7.7", - Datacenter: "dc1", - }, - Service: &structs.NodeService{ - Service: "google-v2", - TaggedAddresses: map[string]structs.ServiceAddress{ - "virtual": {Address: "10.10.10.10"}, - }, - }, - }, - }, - } - - // DiscoveryChains without endpoints do not get a filter chain because there are no addresses to match on. - snap.ConnectProxy.DiscoveryChain[UID("no-endpoints")] = discoverychain.TestCompileConfigEntries(t, "no-endpoints", "default", "default", "dc1", connect.TestClusterID+".consul", nil) - }, + create: proxycfg.TestConfigSnapshotTransparentProxyHTTPUpstream, }, { name: "transparent-proxy-catalog-destinations-only", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Mode = structs.ProxyModeTransparent - - snap.ConnectProxy.MeshConfigSet = true - snap.ConnectProxy.MeshConfig = &structs.MeshConfigEntry{ - TransparentProxy: structs.TransparentProxyMeshConfig{ - MeshDestinationsOnly: true, - }, - } - - // DiscoveryChain without an UpstreamConfig should yield a filter chain when in transparent proxy mode - google := structs.NewServiceName("google", nil) - googleUID := proxycfg.NewUpstreamIDFromServiceName(google) - snap.ConnectProxy.IntentionUpstreams = map[proxycfg.UpstreamID]struct{}{ - googleUID: {}, - } - snap.ConnectProxy.DiscoveryChain[googleUID] = discoverychain.TestCompileConfigEntries(t, "google", "default", "default", "dc1", connect.TestClusterID+".consul", nil) - snap.ConnectProxy.WatchedUpstreamEndpoints[googleUID] = map[string]structs.CheckServiceNodes{ - "google.default.default.dc1": { - structs.CheckServiceNode{ - Node: &structs.Node{ - Address: "8.8.8.8", - Datacenter: "dc1", - }, - Service: &structs.NodeService{ - Service: "google", - Address: "9.9.9.9", - Port: 9090, - TaggedAddresses: map[string]structs.ServiceAddress{ - "virtual": {Address: "10.0.0.1"}, - }, - }, - }, - }, - } - - // DiscoveryChains without endpoints do not get a filter chain because there are no addresses to match on. - snap.ConnectProxy.DiscoveryChain[UID("no-endpoints")] = discoverychain.TestCompileConfigEntries(t, "no-endpoints", "default", "default", "dc1", connect.TestClusterID+".consul", nil) - }, + create: proxycfg.TestConfigSnapshotTransparentProxyCatalogDestinationsOnly, }, { name: "transparent-proxy-dial-instances-directly", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Mode = structs.ProxyModeTransparent - kafka := structs.NewServiceName("kafka", nil) - mongo := structs.NewServiceName("mongo", nil) - kafkaUID := proxycfg.NewUpstreamIDFromServiceName(kafka) - mongoUID := proxycfg.NewUpstreamIDFromServiceName(mongo) - - snap.ConnectProxy.IntentionUpstreams = map[proxycfg.UpstreamID]struct{}{ - kafkaUID: {}, - mongoUID: {}, - } - snap.ConnectProxy.DiscoveryChain[mongoUID] = discoverychain.TestCompileConfigEntries(t, "mongo", "default", "default", "dc1", connect.TestClusterID+".consul", nil) - snap.ConnectProxy.DiscoveryChain[kafkaUID] = discoverychain.TestCompileConfigEntries(t, "kafka", "default", "default", "dc1", connect.TestClusterID+".consul", nil) - - // We add a filter chains for each passthrough service name. - // The filter chain will route to a cluster with the same SNI name. - snap.ConnectProxy.PassthroughUpstreams = map[proxycfg.UpstreamID]map[string]map[string]struct{}{ - kafkaUID: { - "kafka.default.default.dc1": map[string]struct{}{ - "9.9.9.9": {}, - }, - }, - mongoUID: { - "mongo.default.default.dc1": map[string]struct{}{ - "10.10.10.10": {}, - "10.10.10.12": {}, - }, - }, - } - - // There should still be a filter chain for mongo's virtual address - snap.ConnectProxy.WatchedUpstreamEndpoints[mongoUID] = map[string]structs.CheckServiceNodes{ - "mongo.default.default.dc1": { - structs.CheckServiceNode{ - Node: &structs.Node{ - Datacenter: "dc1", - }, - Service: &structs.NodeService{ - Service: "mongo", - Address: "7.7.7.7", - Port: 27017, - TaggedAddresses: map[string]structs.ServiceAddress{ - "virtual": {Address: "6.6.6.6"}, - }, - }, - }, - }, - } - }, + create: proxycfg.TestConfigSnapshotTransparentProxyDialDirectly, }, { name: "transparent-proxy-terminating-gateway", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Mode = structs.ProxyModeTransparent - - snap.ConnectProxy.MeshConfigSet = true - snap.ConnectProxy.MeshConfig = &structs.MeshConfigEntry{ - TransparentProxy: structs.TransparentProxyMeshConfig{ - MeshDestinationsOnly: true, - }, - } - - // DiscoveryChain without an UpstreamConfig should yield a filter chain when in transparent proxy mode - google := structs.NewServiceName("google", nil) - kafka := structs.NewServiceName("kafka", nil) - googleUID := proxycfg.NewUpstreamIDFromServiceName(google) - kafkaUID := proxycfg.NewUpstreamIDFromServiceName(kafka) - snap.ConnectProxy.IntentionUpstreams = map[proxycfg.UpstreamID]struct{}{ - googleUID: {}, - kafkaUID: {}, - } - snap.ConnectProxy.DiscoveryChain[googleUID] = discoverychain.TestCompileConfigEntries(t, "google", "default", "default", "dc1", connect.TestClusterID+".consul", nil) - snap.ConnectProxy.DiscoveryChain[kafkaUID] = discoverychain.TestCompileConfigEntries(t, "kafka", "default", "default", "dc1", connect.TestClusterID+".consul", nil) - - tgate := structs.CheckServiceNode{ - Node: &structs.Node{ - Address: "8.8.8.8", - Datacenter: "dc1", - }, - Service: &structs.NodeService{ - Service: "tgate1", - Kind: structs.ServiceKind(structs.TerminatingGateway), - Address: "9.9.9.9", - Port: 9090, - TaggedAddresses: map[string]structs.ServiceAddress{ - structs.ServiceGatewayVirtualIPTag(google): {Address: "10.0.0.1"}, - structs.ServiceGatewayVirtualIPTag(kafka): {Address: "10.0.0.2"}, - "virtual": {Address: "6.6.6.6"}, - }, - }, - } - snap.ConnectProxy.WatchedUpstreamEndpoints[googleUID] = map[string]structs.CheckServiceNodes{ - "google.default.default.dc1": {tgate}, - } - snap.ConnectProxy.WatchedUpstreamEndpoints[kafkaUID] = map[string]structs.CheckServiceNodes{ - "kafka.default.default.dc1": {tgate}, - } - }, + create: proxycfg.TestConfigSnapshotTransparentProxyTerminatingGatewayCatalogDestinationsOnly, }, } @@ -1514,14 +785,14 @@ var ( customHTTPListenerJSONTemplate = template.Must(template.New("").Parse(customHTTPListenerJSONTpl)) ) -func customListenerJSON(t *testing.T, opts customListenerJSONOptions) string { +func customListenerJSON(t testinf.T, opts customListenerJSONOptions) string { t.Helper() var buf bytes.Buffer require.NoError(t, customListenerJSONTemplate.Execute(&buf, opts)) return buf.String() } -func customHTTPListenerJSON(t *testing.T, opts customHTTPListenerJSONOptions) string { +func customHTTPListenerJSON(t testinf.T, opts customHTTPListenerJSONOptions) string { t.Helper() if opts.HTTPConnectionManagerName == "" { opts.HTTPConnectionManagerName = httpConnectionManagerNewName @@ -1558,7 +829,14 @@ func TestResolveListenerSDSConfig(t *testing.T) { run := func(tc testCase) { // fake a snapshot with just the data we care about - snap := proxycfg.TestConfigSnapshotIngressWithGatewaySDS(t) + snap := proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", "default", nil, func(entry *structs.IngressGatewayConfigEntry) { + entry.TLS = structs.GatewayTLSConfig{ + SDS: &structs.GatewayTLSSDSConfig{ + ClusterName: "sds-cluster", + CertResource: "cert-resource", + }, + } + }, nil) // Override TLS configs snap.IngressGateway.TLSConfig.SDS = tc.gwSDS var listenerCfg structs.IngressListener diff --git a/agent/xds/routes.go b/agent/xds/routes.go index 8d0b6330ee..e6ed55df8a 100644 --- a/agent/xds/routes.go +++ b/agent/xds/routes.go @@ -30,11 +30,7 @@ func (s *ResourceGenerator) routesFromSnapshot(cfgSnap *proxycfg.ConfigSnapshot) case structs.ServiceKindConnectProxy: return s.routesForConnectProxy(cfgSnap) case structs.ServiceKindIngressGateway: - return s.routesForIngressGateway( - cfgSnap.IngressGateway.Listeners, - cfgSnap.IngressGateway.Upstreams, - cfgSnap.IngressGateway.DiscoveryChain, - ) + return s.routesForIngressGateway(cfgSnap) case structs.ServiceKindTerminatingGateway: return s.routesFromSnapshotTerminatingGateway(cfgSnap) case structs.ServiceKindMeshGateway: @@ -170,14 +166,9 @@ func makeNamedDefaultRouteWithLB(clusterName string, lb *structs.LoadBalancer, a // routesForIngressGateway returns the xDS API representation of the // "routes" in the snapshot. -func (s *ResourceGenerator) routesForIngressGateway( - listeners map[proxycfg.IngressListenerKey]structs.IngressListener, - upstreams map[proxycfg.IngressListenerKey]structs.Upstreams, - chains map[proxycfg.UpstreamID]*structs.CompiledDiscoveryChain, -) ([]proto.Message, error) { - +func (s *ResourceGenerator) routesForIngressGateway(cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) { var result []proto.Message - for listenerKey, upstreams := range upstreams { + for listenerKey, upstreams := range cfgSnap.IngressGateway.Upstreams { // Do not create any route configuration for TCP listeners if listenerKey.Protocol == "tcp" { continue @@ -196,7 +187,7 @@ func (s *ResourceGenerator) routesForIngressGateway( for _, u := range upstreams { uid := proxycfg.NewUpstreamID(&u) - chain := chains[uid] + chain := cfgSnap.IngressGateway.DiscoveryChain[uid] if chain == nil { continue } @@ -209,14 +200,15 @@ func (s *ResourceGenerator) routesForIngressGateway( // Lookup listener and service config details from ingress gateway // definition. - lCfg, ok := listeners[listenerKey] + lCfg, ok := cfgSnap.IngressGateway.Listeners[listenerKey] if !ok { - return nil, fmt.Errorf("missing ingress listener config (listener on port %d)", listenerKey.Port) + return nil, fmt.Errorf("missing ingress listener config (service %q listener on proto/port %s/%d)", + u.DestinationID(), listenerKey.Protocol, listenerKey.Port) } svc := findIngressServiceMatchingUpstream(lCfg, u) if svc == nil { - return nil, fmt.Errorf("missing service in listener config (service %q listener on port %d)", - u.DestinationID(), listenerKey.Port) + return nil, fmt.Errorf("missing service in listener config (service %q listener on proto/port %s/%d)", + u.DestinationID(), listenerKey.Protocol, listenerKey.Port) } if err := injectHeaderManipToVirtualHost(svc, virtualHost); err != nil { diff --git a/agent/xds/routes_test.go b/agent/xds/routes_test.go index d0c1b44d9b..7284a715d8 100644 --- a/agent/xds/routes_test.go +++ b/agent/xds/routes_test.go @@ -12,8 +12,6 @@ import ( testinf "github.com/mitchellh/go-testing-interface" "github.com/stretchr/testify/require" - "github.com/hashicorp/consul/agent/connect" - "github.com/hashicorp/consul/agent/consul/discoverychain" "github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/xds/proxysupport" @@ -23,307 +21,159 @@ import ( func TestRoutesFromSnapshot(t *testing.T) { tests := []struct { - name string - create func(t testinf.T) *proxycfg.ConfigSnapshot - // Setup is called before the test starts. It is passed the snapshot from - // create func and is allowed to modify it in any way to setup the - // test input. - setup func(snap *proxycfg.ConfigSnapshot) + name string + create func(t testinf.T) *proxycfg.ConfigSnapshot overrideGoldenName string }{ { - name: "defaults-no-chain", - create: proxycfg.TestConfigSnapshot, - setup: nil, // Default snapshot + name: "defaults", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, nil, nil) + }, }, { - name: "connect-proxy-with-chain", - create: proxycfg.TestConfigSnapshotDiscoveryChain, - setup: nil, + name: "connect-proxy-with-chain", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "simple", nil, nil) + }, }, { - name: "connect-proxy-with-chain-external-sni", - create: proxycfg.TestConfigSnapshotDiscoveryChainExternalSNI, - setup: nil, + name: "connect-proxy-with-chain-external-sni", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "external-sni", nil, nil) + }, }, { - name: "connect-proxy-with-chain-and-overrides", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithOverrides, - setup: nil, + name: "connect-proxy-with-chain-and-overrides", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "simple-with-overrides", nil, nil) + }, }, { - name: "splitter-with-resolver-redirect", - create: proxycfg.TestConfigSnapshotDiscoveryChain_SplitterWithResolverRedirectMultiDC, - setup: nil, + name: "splitter-with-resolver-redirect", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "splitter-with-resolver-redirect-multidc", nil, nil) + }, }, { - name: "connect-proxy-with-chain-and-splitter", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithSplitter, - setup: nil, + name: "connect-proxy-with-chain-and-splitter", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "chain-and-splitter", nil, nil) + }, }, { - name: "connect-proxy-with-grpc-router", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithGRPCRouter, - setup: nil, + name: "connect-proxy-with-grpc-router", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "grpc-router", nil, nil) + }, }, { - name: "connect-proxy-with-chain-and-router", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithRouter, - setup: nil, + name: "connect-proxy-with-chain-and-router", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "chain-and-router", nil, nil) + }, }, { - name: "connect-proxy-lb-in-resolver", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithLB, - setup: nil, + name: "connect-proxy-lb-in-resolver", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "lb-resolver", nil, nil) + }, }, // TODO(rb): test match stanza skipped for grpc // Start ingress gateway test cases { - name: "ingress-defaults-no-chain", - create: proxycfg.TestConfigSnapshotIngressGateway, - setup: nil, // Default snapshot + name: "ingress-defaults-no-chain", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, false, "tcp", + "default", nil, nil, nil) + }, }, { - name: "ingress-with-chain", - create: proxycfg.TestConfigSnapshotIngress, - setup: nil, + name: "ingress-with-chain", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "simple", nil, nil, nil) + }, }, { - name: "ingress-with-chain-external-sni", - create: proxycfg.TestConfigSnapshotIngressExternalSNI, - setup: nil, + name: "ingress-with-chain-external-sni", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp", + "external-sni", nil, nil, nil) + }, }, { - name: "ingress-with-chain-and-overrides", - create: proxycfg.TestConfigSnapshotIngressWithOverrides, - setup: nil, + name: "ingress-splitter-with-resolver-redirect", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "http", + "splitter-with-resolver-redirect-multidc", nil, nil, nil) + }, }, { - name: "ingress-splitter-with-resolver-redirect", - create: proxycfg.TestConfigSnapshotIngress_SplitterWithResolverRedirectMultiDC, - setup: nil, + name: "ingress-with-chain-and-splitter", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "http", + "chain-and-splitter", nil, nil, nil) + }, }, { - name: "ingress-with-chain-and-splitter", - create: proxycfg.TestConfigSnapshotIngressWithSplitter, - setup: nil, + name: "ingress-with-grpc-router", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "http", + "grpc-router", nil, nil, nil) + }, }, { - name: "ingress-with-grpc-router", - create: proxycfg.TestConfigSnapshotIngressWithGRPCRouter, - setup: nil, + name: "ingress-with-chain-and-router", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "http", + "chain-and-router", nil, nil, nil) + }, }, { - name: "ingress-with-chain-and-router", - create: proxycfg.TestConfigSnapshotIngressWithRouter, - setup: nil, - }, - { - name: "ingress-lb-in-resolver", - create: proxycfg.TestConfigSnapshotIngressWithLB, - setup: nil, + name: "ingress-lb-in-resolver", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGateway(t, true, "http", + "lb-resolver", nil, nil, nil) + }, }, { name: "ingress-http-multiple-services", create: proxycfg.TestConfigSnapshotIngress_HTTPMultipleServices, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.IngressGateway.Upstreams = map[proxycfg.IngressListenerKey]structs.Upstreams{ - {Protocol: "http", Port: 8080}: { - { - DestinationName: "foo", - LocalBindPort: 8080, - IngressHosts: []string{ - "test1.example.com", - "test2.example.com", - "test2.example.com:8080", - }, - }, - { - DestinationName: "bar", - LocalBindPort: 8080, - }, - }, - {Protocol: "http", Port: 443}: { - { - DestinationName: "baz", - LocalBindPort: 443, - }, - { - DestinationName: "qux", - LocalBindPort: 443, - }, - }, - } - snap.IngressGateway.Listeners = map[proxycfg.IngressListenerKey]structs.IngressListener{ - {Protocol: "http", Port: 8080}: { - Port: 8080, - Services: []structs.IngressService{ - { - Name: "foo", - }, - { - Name: "bar", - }, - }, - }, - {Protocol: "http", Port: 443}: { - Port: 443, - Services: []structs.IngressService{ - { - Name: "baz", - }, - { - Name: "qux", - }, - }, - }, - } - - // We do not add baz/qux here so that we test the chain.IsDefault() case - entries := []structs.ConfigEntry{ - &structs.ProxyConfigEntry{ - Kind: structs.ProxyDefaults, - Name: structs.ProxyConfigGlobal, - Config: map[string]interface{}{ - "protocol": "http", - }, - }, - &structs.ServiceResolverConfigEntry{ - Kind: structs.ServiceResolver, - Name: "foo", - ConnectTimeout: 22 * time.Second, - }, - &structs.ServiceResolverConfigEntry{ - Kind: structs.ServiceResolver, - Name: "bar", - ConnectTimeout: 22 * time.Second, - }, - } - fooChain := discoverychain.TestCompileConfigEntries(t, "foo", "default", "default", "dc1", connect.TestClusterID+".consul", nil, entries...) - barChain := discoverychain.TestCompileConfigEntries(t, "bar", "default", "default", "dc1", connect.TestClusterID+".consul", nil, entries...) - bazChain := discoverychain.TestCompileConfigEntries(t, "baz", "default", "default", "dc1", connect.TestClusterID+".consul", nil, entries...) - quxChain := discoverychain.TestCompileConfigEntries(t, "qux", "default", "default", "dc1", connect.TestClusterID+".consul", nil, entries...) - - snap.IngressGateway.DiscoveryChain = map[proxycfg.UpstreamID]*structs.CompiledDiscoveryChain{ - UID("foo"): fooChain, - UID("bar"): barChain, - UID("baz"): bazChain, - UID("qux"): quxChain, - } + }, + { + name: "ingress-with-chain-and-router-header-manip", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGatewayWithChain(t, "router-header-manip", nil, nil) }, }, { - name: "ingress-with-chain-and-router-header-manip", - create: proxycfg.TestConfigSnapshotIngressWithRouter, - setup: func(snap *proxycfg.ConfigSnapshot) { - k := proxycfg.IngressListenerKey{Port: 9191, Protocol: "http"} - l := snap.IngressGateway.Listeners[k] - l.Services[0].RequestHeaders = &structs.HTTPHeaderModifiers{ - Add: map[string]string{ - "foo": "bar", - }, - Set: map[string]string{ - "bar": "baz", - }, - Remove: []string{"qux"}, - } - l.Services[0].ResponseHeaders = &structs.HTTPHeaderModifiers{ - Add: map[string]string{ - "foo": "bar", - }, - Set: map[string]string{ - "bar": "baz", - }, - Remove: []string{"qux"}, - } - snap.IngressGateway.Listeners[k] = l + name: "ingress-with-sds-listener-level", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGatewayWithChain(t, "sds-listener-level", nil, nil) }, }, { - name: "ingress-with-sds-listener-level", - create: proxycfg.TestConfigSnapshotIngressWithRouter, - setup: setupIngressWithTwoHTTPServices(t, ingressSDSOpts{ - // Listener-level SDS means all services share the default route. - listenerSDS: true, - }), + name: "ingress-with-sds-listener-level-wildcard", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGatewayWithChain(t, "sds-listener-level-wildcard", nil, nil) + }, }, { - name: "ingress-with-sds-listener-level-wildcard", - create: proxycfg.TestConfigSnapshotIngressWithRouter, - setup: setupIngressWithTwoHTTPServices(t, ingressSDSOpts{ - // Listener-level SDS means all services share the default route. - listenerSDS: true, - wildcard: true, - }), + name: "ingress-with-sds-service-level", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGatewayWithChain(t, "sds-service-level", nil, nil) + }, }, { - name: "ingress-with-sds-service-level", - create: proxycfg.TestConfigSnapshotIngressWithRouter, - setup: setupIngressWithTwoHTTPServices(t, ingressSDSOpts{ - listenerSDS: false, - // Services should get separate routes and no default since they all - // have custom certs. - webSDS: true, - fooSDS: true, - }), - }, - { - name: "ingress-with-sds-service-level-mixed-tls", - create: proxycfg.TestConfigSnapshotIngressWithRouter, - setup: setupIngressWithTwoHTTPServices(t, ingressSDSOpts{ - listenerSDS: false, - // Web needs a separate route as it has custom filter chain but foo - // should use default route for listener. - webSDS: true, - fooSDS: false, - }), + name: "ingress-with-sds-service-level-mixed-tls", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotIngressGatewayWithChain(t, "sds-service-level-mixed-tls", nil, nil) + }, }, { name: "terminating-gateway-lb-config", - create: proxycfg.TestConfigSnapshotTerminatingGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.TerminatingGateway.ServiceResolvers = map[structs.ServiceName]*structs.ServiceResolverConfigEntry{ - structs.NewServiceName("web", nil): { - Kind: structs.ServiceResolver, - Name: "web", - DefaultSubset: "v2", - 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, - }, - HashPolicies: []structs.HashPolicy{ - { - Field: structs.HashPolicyCookie, - FieldValue: "chocolate-chip", - Terminal: true, - }, - { - Field: structs.HashPolicyHeader, - FieldValue: "x-user-id", - }, - { - SourceIP: true, - Terminal: true, - }, - }, - }, - }, - } - snap.TerminatingGateway.ServiceConfigs[structs.NewServiceName("web", nil)] = &structs.ServiceConfigResponse{ - ProxyConfig: map[string]interface{}{"protocol": "http"}, - } - }, + create: proxycfg.TestConfigSnapshotTerminatingGatewayLBConfig, }, } @@ -342,10 +192,6 @@ func TestRoutesFromSnapshot(t *testing.T) { // golden files for every test case and so not be any use! setupTLSRootsAndLeaf(t, snap) - if tt.setup != nil { - tt.setup(snap) - } - g := newResourceGenerator(testutil.Logger(t), nil, nil, false) g.ProxyFeatures = sf @@ -627,158 +473,3 @@ func TestEnvoyLBConfig_InjectToRouteAction(t *testing.T) { }) } } - -type ingressSDSOpts struct { - listenerSDS, webSDS, fooSDS, wildcard bool - entMetas map[string]*structs.EnterpriseMeta -} - -// setupIngressWithTwoHTTPServices can be used with -// proxycfg.TestConfigSnapshotIngressWithRouter to generate a setup func for an -// ingress listener with multiple HTTP services and varying SDS configurations -// since those affect how we generate routes. -func setupIngressWithTwoHTTPServices(t *testing.T, o ingressSDSOpts) func(snap *proxycfg.ConfigSnapshot) { - return func(snap *proxycfg.ConfigSnapshot) { - - snap.IngressGateway.TLSConfig.SDS = nil - - webUpstream := structs.Upstream{ - DestinationName: "web", - // We use empty not default here because of the way upstream identifiers - // vary between OSS and Enterprise currently causing test conflicts. In - // real life `proxycfg` always sets ingress upstream namespaces to - // `NamespaceOrDefault` which shouldn't matter because we should be - // consistent within a single binary it's just inconvenient if OSS and - // enterprise tests generate different output. - DestinationNamespace: o.entMetas["web"].NamespaceOrEmpty(), - DestinationPartition: o.entMetas["web"].PartitionOrEmpty(), - LocalBindPort: 9191, - IngressHosts: []string{ - "www.example.com", - }, - } - fooUpstream := structs.Upstream{ - DestinationName: "foo", - DestinationNamespace: o.entMetas["foo"].NamespaceOrEmpty(), - DestinationPartition: o.entMetas["foo"].PartitionOrEmpty(), - LocalBindPort: 9191, - IngressHosts: []string{ - "foo.example.com", - }, - } - - webUID := proxycfg.NewUpstreamID(&webUpstream) - fooUID := proxycfg.NewUpstreamID(&fooUpstream) - - // Setup additional HTTP service on same listener with default router - snap.IngressGateway.Upstreams = map[proxycfg.IngressListenerKey]structs.Upstreams{ - {Protocol: "http", Port: 9191}: {webUpstream, fooUpstream}, - } - il := structs.IngressListener{ - Port: 9191, - Services: []structs.IngressService{ - { - Name: "web", - Hosts: []string{"www.example.com"}, - }, - { - Name: "foo", - Hosts: []string{"foo.example.com"}, - }, - }, - } - for i, svc := range il.Services { - if em, ok := o.entMetas[svc.Name]; ok && em != nil { - il.Services[i].EnterpriseMeta = *em - } - } - - // Now set the appropriate SDS configs - if o.listenerSDS { - il.TLS = &structs.GatewayTLSConfig{ - SDS: &structs.GatewayTLSSDSConfig{ - ClusterName: "listener-cluster", - CertResource: "listener-cert", - }, - } - } - if o.webSDS { - il.Services[0].TLS = &structs.GatewayServiceTLSConfig{ - SDS: &structs.GatewayTLSSDSConfig{ - ClusterName: "web-cluster", - CertResource: "www-cert", - }, - } - } - if o.fooSDS { - il.Services[1].TLS = &structs.GatewayServiceTLSConfig{ - SDS: &structs.GatewayTLSSDSConfig{ - ClusterName: "foo-cluster", - CertResource: "foo-cert", - }, - } - } - - if o.wildcard { - // undo all that and set just a single wildcard config with no TLS to test - // the lookup path where we have to compare an actual resolved upstream to - // a wildcard config. - il.Services = []structs.IngressService{ - { - Name: "*", - }, - } - // We also don't support user-specified hosts with wildcard so remove - // those from the upstreams. - ups := snap.IngressGateway.Upstreams[proxycfg.IngressListenerKey{Protocol: "http", Port: 9191}] - for i := range ups { - ups[i].IngressHosts = nil - } - snap.IngressGateway.Upstreams[proxycfg.IngressListenerKey{Protocol: "http", Port: 9191}] = ups - } - - snap.IngressGateway.Listeners[proxycfg.IngressListenerKey{Protocol: "http", Port: 9191}] = il - - entries := []structs.ConfigEntry{ - &structs.ProxyConfigEntry{ - Kind: structs.ProxyDefaults, - Name: structs.ProxyConfigGlobal, - Config: map[string]interface{}{ - "protocol": "http", - }, - }, - &structs.ServiceResolverConfigEntry{ - Kind: structs.ServiceResolver, - Name: "web", - ConnectTimeout: 22 * time.Second, - }, - &structs.ServiceResolverConfigEntry{ - Kind: structs.ServiceResolver, - Name: "foo", - ConnectTimeout: 22 * time.Second, - }, - } - for i, e := range entries { - switch v := e.(type) { - // Add other Service types here if we ever need them above - case *structs.ServiceResolverConfigEntry: - if em, ok := o.entMetas[v.Name]; ok && em != nil { - v.EnterpriseMeta = *em - entries[i] = v - } - } - } - - webChain := discoverychain.TestCompileConfigEntries(t, "web", - o.entMetas["web"].NamespaceOrDefault(), - o.entMetas["web"].PartitionOrDefault(), "dc1", - connect.TestClusterID+".consul", nil, entries...) - fooChain := discoverychain.TestCompileConfigEntries(t, "foo", - o.entMetas["foo"].NamespaceOrDefault(), - o.entMetas["web"].PartitionOrDefault(), "dc1", - connect.TestClusterID+".consul", nil, entries...) - - snap.IngressGateway.DiscoveryChain[webUID] = webChain - snap.IngressGateway.DiscoveryChain[fooUID] = fooChain - } -} diff --git a/agent/xds/testdata/clusters/ingress-with-chain-and-overrides.envoy-1-20-x.golden b/agent/xds/testdata/clusters/ingress-with-chain-and-overrides.envoy-1-20-x.golden deleted file mode 100644 index b7d347d02e..0000000000 --- a/agent/xds/testdata/clusters/ingress-with-chain-and-overrides.envoy-1-20-x.golden +++ /dev/null @@ -1,68 +0,0 @@ -{ - "versionInfo": "00000001", - "resources": [ - { - "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", - "name": "78ebd528~db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", - "altStatName": "78ebd528~db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", - "type": "EDS", - "edsClusterConfig": { - "edsConfig": { - "ads": { - - }, - "resourceApiVersion": "V3" - } - }, - "connectTimeout": "66s", - "circuitBreakers": { - - }, - "http2ProtocolOptions": { - - }, - "outlierDetection": { - - }, - "commonLbConfig": { - "healthyPanicThreshold": { - - } - }, - "transportSocket": { - "name": "tls", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", - "commonTlsContext": { - "tlsParams": { - - }, - "tlsCertificates": [ - { - "certificateChain": { - "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" - }, - "privateKey": { - "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" - } - } - ], - "validationContext": { - "trustedCa": { - "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" - }, - "matchSubjectAltNames": [ - { - "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/db" - } - ] - } - }, - "sni": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" - } - } - } - ], - "typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster", - "nonce": "00000001" -} \ No newline at end of file diff --git a/agent/xds/testdata/clusters/transparent-proxy-catalog-destinations-only.envoy-1-20-x.golden b/agent/xds/testdata/clusters/transparent-proxy-catalog-destinations-only.envoy-1-20-x.golden index 2e3c9ea20e..6b2c84d60c 100644 --- a/agent/xds/testdata/clusters/transparent-proxy-catalog-destinations-only.envoy-1-20-x.golden +++ b/agent/xds/testdata/clusters/transparent-proxy-catalog-destinations-only.envoy-1-20-x.golden @@ -114,6 +114,64 @@ } } }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "google.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "altStatName": "google.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": { + + }, + "resourceApiVersion": "V3" + } + }, + "connectTimeout": "5s", + "circuitBreakers": { + + }, + "outlierDetection": { + + }, + "commonLbConfig": { + "healthyPanicThreshold": { + + } + }, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "tlsParams": { + + }, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + }, + "matchSubjectAltNames": [ + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/google" + } + ] + } + }, + "sni": "google.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + }, { "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", "name": "local_app", @@ -138,6 +196,64 @@ } ] } + }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "no-endpoints.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "altStatName": "no-endpoints.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": { + + }, + "resourceApiVersion": "V3" + } + }, + "connectTimeout": "5s", + "circuitBreakers": { + + }, + "outlierDetection": { + + }, + "commonLbConfig": { + "healthyPanicThreshold": { + + } + }, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "tlsParams": { + + }, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + }, + "matchSubjectAltNames": [ + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/no-endpoints" + } + ] + } + }, + "sni": "no-endpoints.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } } ], "typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster", diff --git a/agent/xds/testdata/clusters/transparent-proxy-dial-instances-directly.envoy-1-20-x.golden b/agent/xds/testdata/clusters/transparent-proxy-dial-instances-directly.envoy-1-20-x.golden index 28151072aa..d920c11a4a 100644 --- a/agent/xds/testdata/clusters/transparent-proxy-dial-instances-directly.envoy-1-20-x.golden +++ b/agent/xds/testdata/clusters/transparent-proxy-dial-instances-directly.envoy-1-20-x.golden @@ -114,6 +114,64 @@ } } }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "kafka.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "altStatName": "kafka.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": { + + }, + "resourceApiVersion": "V3" + } + }, + "connectTimeout": "5s", + "circuitBreakers": { + + }, + "outlierDetection": { + + }, + "commonLbConfig": { + "healthyPanicThreshold": { + + } + }, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "tlsParams": { + + }, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + }, + "matchSubjectAltNames": [ + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/kafka" + } + ] + } + }, + "sni": "kafka.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + }, { "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", "name": "local_app", diff --git a/agent/xds/testdata/clusters/transparent-proxy.envoy-1-20-x.golden b/agent/xds/testdata/clusters/transparent-proxy.envoy-1-20-x.golden index 0c20c5a693..d3a37c2ceb 100644 --- a/agent/xds/testdata/clusters/transparent-proxy.envoy-1-20-x.golden +++ b/agent/xds/testdata/clusters/transparent-proxy.envoy-1-20-x.golden @@ -114,6 +114,64 @@ } } }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "google.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "altStatName": "google.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": { + + }, + "resourceApiVersion": "V3" + } + }, + "connectTimeout": "5s", + "circuitBreakers": { + + }, + "outlierDetection": { + + }, + "commonLbConfig": { + "healthyPanicThreshold": { + + } + }, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "tlsParams": { + + }, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + }, + "matchSubjectAltNames": [ + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/google" + } + ] + } + }, + "sni": "google.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + }, { "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", "name": "local_app", @@ -139,6 +197,64 @@ ] } }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "no-endpoints.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "altStatName": "no-endpoints.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": { + + }, + "resourceApiVersion": "V3" + } + }, + "connectTimeout": "5s", + "circuitBreakers": { + + }, + "outlierDetection": { + + }, + "commonLbConfig": { + "healthyPanicThreshold": { + + } + }, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "tlsParams": { + + }, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + }, + "matchSubjectAltNames": [ + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/no-endpoints" + } + ] + } + }, + "sni": "no-endpoints.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + }, { "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", "name": "original-destination", diff --git a/agent/xds/testdata/endpoints/ingress-with-chain-and-overrides.envoy-1-20-x.golden b/agent/xds/testdata/endpoints/ingress-with-chain-and-overrides.envoy-1-20-x.golden deleted file mode 100644 index 5d5c847f45..0000000000 --- a/agent/xds/testdata/endpoints/ingress-with-chain-and-overrides.envoy-1-20-x.golden +++ /dev/null @@ -1,41 +0,0 @@ -{ - "versionInfo": "00000001", - "resources": [ - { - "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", - "clusterName": "78ebd528~db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", - "endpoints": [ - { - "lbEndpoints": [ - { - "endpoint": { - "address": { - "socketAddress": { - "address": "10.10.1.1", - "portValue": 8080 - } - } - }, - "healthStatus": "HEALTHY", - "loadBalancingWeight": 1 - }, - { - "endpoint": { - "address": { - "socketAddress": { - "address": "10.10.1.2", - "portValue": 8080 - } - } - }, - "healthStatus": "HEALTHY", - "loadBalancingWeight": 1 - } - ] - } - ] - } - ], - "typeUrl": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", - "nonce": "00000001" -} \ No newline at end of file diff --git a/agent/xds/testdata/listeners/expose-checks.envoy-1-20-x.golden b/agent/xds/testdata/listeners/expose-checks.envoy-1-20-x.golden index d7d82d07b9..a893884f19 100644 --- a/agent/xds/testdata/listeners/expose-checks.envoy-1-20-x.golden +++ b/agent/xds/testdata/listeners/expose-checks.envoy-1-20-x.golden @@ -107,6 +107,26 @@ "name": "tls", "typedConfig": { "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext", + "commonTlsContext": { + "tlsParams": { + + }, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + } + } + }, "requireClientCertificate": true } } diff --git a/agent/xds/testdata/listeners/expose-paths-local-app-paths.envoy-1-20-x.golden b/agent/xds/testdata/listeners/expose-paths-local-app-paths.envoy-1-20-x.golden index b60e6b8b4e..65bdb30d96 100644 --- a/agent/xds/testdata/listeners/expose-paths-local-app-paths.envoy-1-20-x.golden +++ b/agent/xds/testdata/listeners/expose-paths-local-app-paths.envoy-1-20-x.golden @@ -146,6 +146,26 @@ "name": "tls", "typedConfig": { "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext", + "commonTlsContext": { + "tlsParams": { + + }, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + } + } + }, "requireClientCertificate": true } } diff --git a/agent/xds/testdata/listeners/expose-paths-new-cluster-http2.envoy-1-20-x.golden b/agent/xds/testdata/listeners/expose-paths-new-cluster-http2.envoy-1-20-x.golden index 5356319cea..fb860a535a 100644 --- a/agent/xds/testdata/listeners/expose-paths-new-cluster-http2.envoy-1-20-x.golden +++ b/agent/xds/testdata/listeners/expose-paths-new-cluster-http2.envoy-1-20-x.golden @@ -149,6 +149,26 @@ "name": "tls", "typedConfig": { "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext", + "commonTlsContext": { + "tlsParams": { + + }, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + } + } + }, "requireClientCertificate": true } } diff --git a/agent/xds/testdata/listeners/ingress-gateway-bind-addrs.envoy-1-20-x.golden b/agent/xds/testdata/listeners/ingress-gateway-bind-addrs.envoy-1-20-x.golden index ffbba76efa..dc299d5506 100644 --- a/agent/xds/testdata/listeners/ingress-gateway-bind-addrs.envoy-1-20-x.golden +++ b/agent/xds/testdata/listeners/ingress-gateway-bind-addrs.envoy-1-20-x.golden @@ -3,11 +3,11 @@ "resources": [ { "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", - "name": "db:10.0.0.1:9191", + "name": "db:10.0.0.1:8080", "address": { "socketAddress": { "address": "10.0.0.1", - "portValue": 9191 + "portValue": 8080 } }, "filterChains": [ @@ -28,11 +28,11 @@ }, { "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", - "name": "db:172.16.0.1:9191", + "name": "db:172.16.0.1:8080", "address": { "socketAddress": { "address": "172.16.0.1", - "portValue": 9191 + "portValue": 8080 } }, "filterChains": [ @@ -53,11 +53,11 @@ }, { "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", - "name": "db:8.8.8.8:9191", + "name": "db:8.8.8.8:8080", "address": { "socketAddress": { "address": "8.8.8.8", - "portValue": 9191 + "portValue": 8080 } }, "filterChains": [ diff --git a/agent/xds/testdata/listeners/ingress-gateway.envoy-1-20-x.golden b/agent/xds/testdata/listeners/ingress-gateway.envoy-1-20-x.golden index 668cb3e51a..30994abe6e 100644 --- a/agent/xds/testdata/listeners/ingress-gateway.envoy-1-20-x.golden +++ b/agent/xds/testdata/listeners/ingress-gateway.envoy-1-20-x.golden @@ -3,11 +3,11 @@ "resources": [ { "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", - "name": "db:1.2.3.4:9191", + "name": "db:1.2.3.4:8080", "address": { "socketAddress": { "address": "1.2.3.4", - "portValue": 9191 + "portValue": 8080 } }, "filterChains": [ diff --git a/agent/xds/testdata/listeners/ingress-splitter-with-resolver-redirect.envoy-1-20-x.golden b/agent/xds/testdata/listeners/ingress-splitter-with-resolver-redirect.envoy-1-20-x.golden index 9cd2d22219..4f804bc74f 100644 --- a/agent/xds/testdata/listeners/ingress-splitter-with-resolver-redirect.envoy-1-20-x.golden +++ b/agent/xds/testdata/listeners/ingress-splitter-with-resolver-redirect.envoy-1-20-x.golden @@ -3,11 +3,11 @@ "resources": [ { "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", - "name": "http:1.2.3.4:9191", + "name": "http:1.2.3.4:8080", "address": { "socketAddress": { "address": "1.2.3.4", - "portValue": 9191 + "portValue": 8080 } }, "filterChains": [ @@ -17,7 +17,7 @@ "name": "envoy.filters.network.http_connection_manager", "typedConfig": { "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "statPrefix": "ingress_upstream_9191", + "statPrefix": "ingress_upstream_8080", "rds": { "configSource": { "ads": { @@ -25,7 +25,7 @@ }, "resourceApiVersion": "V3" }, - "routeConfigName": "9191" + "routeConfigName": "8080" }, "httpFilters": [ { diff --git a/agent/xds/testdata/listeners/ingress-with-chain-and-overrides.envoy-1-20-x.golden b/agent/xds/testdata/listeners/ingress-with-chain-and-overrides.envoy-1-20-x.golden deleted file mode 100644 index 22e3261e6f..0000000000 --- a/agent/xds/testdata/listeners/ingress-with-chain-and-overrides.envoy-1-20-x.golden +++ /dev/null @@ -1,63 +0,0 @@ -{ - "versionInfo": "00000001", - "resources": [ - { - "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", - "name": "db:1.2.3.4:9191", - "address": { - "socketAddress": { - "address": "1.2.3.4", - "portValue": 9191 - } - }, - "filterChains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "statPrefix": "upstream.db.default.default.dc1", - "rds": { - "configSource": { - "ads": { - - }, - "resourceApiVersion": "V3" - }, - "routeConfigName": "db" - }, - "httpFilters": [ - { - "name": "envoy.filters.http.grpc_stats", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.grpc_stats.v3.FilterConfig", - "statsForAllMethods": true - } - }, - { - "name": "envoy.filters.http.grpc_http1_bridge" - }, - { - "name": "envoy.filters.http.router" - } - ], - "tracing": { - "randomSampling": { - - } - }, - "http2ProtocolOptions": { - - } - } - } - ] - } - ], - "trafficDirection": "OUTBOUND" - } - ], - "typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener", - "nonce": "00000001" -} \ No newline at end of file diff --git a/agent/xds/testdata/listeners/ingress-with-chain-external-sni.envoy-1-20-x.golden b/agent/xds/testdata/listeners/ingress-with-chain-external-sni.envoy-1-20-x.golden index 668cb3e51a..30994abe6e 100644 --- a/agent/xds/testdata/listeners/ingress-with-chain-external-sni.envoy-1-20-x.golden +++ b/agent/xds/testdata/listeners/ingress-with-chain-external-sni.envoy-1-20-x.golden @@ -3,11 +3,11 @@ "resources": [ { "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", - "name": "db:1.2.3.4:9191", + "name": "db:1.2.3.4:8080", "address": { "socketAddress": { "address": "1.2.3.4", - "portValue": 9191 + "portValue": 8080 } }, "filterChains": [ diff --git a/agent/xds/testdata/listeners/ingress-with-sds-listener-gw-level.envoy-1-20-x.golden b/agent/xds/testdata/listeners/ingress-with-sds-listener-gw-level.envoy-1-20-x.golden index 73042063b1..171b26a5a9 100644 --- a/agent/xds/testdata/listeners/ingress-with-sds-listener-gw-level.envoy-1-20-x.golden +++ b/agent/xds/testdata/listeners/ingress-with-sds-listener-gw-level.envoy-1-20-x.golden @@ -3,11 +3,11 @@ "resources": [ { "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", - "name": "db:1.2.3.4:9191", + "name": "db:1.2.3.4:8080", "address": { "socketAddress": { "address": "1.2.3.4", - "portValue": 9191 + "portValue": 8080 } }, "filterChains": [ diff --git a/agent/xds/testdata/listeners/ingress-with-tcp-chain-failover-through-local-gateway.envoy-1-20-x.golden b/agent/xds/testdata/listeners/ingress-with-tcp-chain-failover-through-local-gateway.envoy-1-20-x.golden index 668cb3e51a..30994abe6e 100644 --- a/agent/xds/testdata/listeners/ingress-with-tcp-chain-failover-through-local-gateway.envoy-1-20-x.golden +++ b/agent/xds/testdata/listeners/ingress-with-tcp-chain-failover-through-local-gateway.envoy-1-20-x.golden @@ -3,11 +3,11 @@ "resources": [ { "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", - "name": "db:1.2.3.4:9191", + "name": "db:1.2.3.4:8080", "address": { "socketAddress": { "address": "1.2.3.4", - "portValue": 9191 + "portValue": 8080 } }, "filterChains": [ diff --git a/agent/xds/testdata/listeners/ingress-with-tcp-chain-failover-through-remote-gateway.envoy-1-20-x.golden b/agent/xds/testdata/listeners/ingress-with-tcp-chain-failover-through-remote-gateway.envoy-1-20-x.golden index 668cb3e51a..30994abe6e 100644 --- a/agent/xds/testdata/listeners/ingress-with-tcp-chain-failover-through-remote-gateway.envoy-1-20-x.golden +++ b/agent/xds/testdata/listeners/ingress-with-tcp-chain-failover-through-remote-gateway.envoy-1-20-x.golden @@ -3,11 +3,11 @@ "resources": [ { "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", - "name": "db:1.2.3.4:9191", + "name": "db:1.2.3.4:8080", "address": { "socketAddress": { "address": "1.2.3.4", - "portValue": 9191 + "portValue": 8080 } }, "filterChains": [ diff --git a/agent/xds/testdata/listeners/ingress-with-tls-listener-cipher-suites.envoy-1-20-x.golden b/agent/xds/testdata/listeners/ingress-with-tls-listener-cipher-suites.envoy-1-20-x.golden index 0288278655..516f4b8a25 100644 --- a/agent/xds/testdata/listeners/ingress-with-tls-listener-cipher-suites.envoy-1-20-x.golden +++ b/agent/xds/testdata/listeners/ingress-with-tls-listener-cipher-suites.envoy-1-20-x.golden @@ -3,11 +3,11 @@ "resources": [ { "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", - "name": "db:1.2.3.4:9191", + "name": "db:1.2.3.4:8080", "address": { "socketAddress": { "address": "1.2.3.4", - "portValue": 9191 + "portValue": 8080 } }, "filterChains": [ diff --git a/agent/xds/testdata/listeners/ingress-with-tls-listener-max-version.envoy-1-20-x.golden b/agent/xds/testdata/listeners/ingress-with-tls-listener-max-version.envoy-1-20-x.golden index 6797b81497..69c8fa3bbb 100644 --- a/agent/xds/testdata/listeners/ingress-with-tls-listener-max-version.envoy-1-20-x.golden +++ b/agent/xds/testdata/listeners/ingress-with-tls-listener-max-version.envoy-1-20-x.golden @@ -3,11 +3,11 @@ "resources": [ { "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", - "name": "db:1.2.3.4:9191", + "name": "db:1.2.3.4:8080", "address": { "socketAddress": { "address": "1.2.3.4", - "portValue": 9191 + "portValue": 8080 } }, "filterChains": [ diff --git a/agent/xds/testdata/listeners/ingress-with-tls-listener-min-version.envoy-1-20-x.golden b/agent/xds/testdata/listeners/ingress-with-tls-listener-min-version.envoy-1-20-x.golden index e052df8dfd..7bec506fad 100644 --- a/agent/xds/testdata/listeners/ingress-with-tls-listener-min-version.envoy-1-20-x.golden +++ b/agent/xds/testdata/listeners/ingress-with-tls-listener-min-version.envoy-1-20-x.golden @@ -3,11 +3,11 @@ "resources": [ { "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", - "name": "db:1.2.3.4:9191", + "name": "db:1.2.3.4:8080", "address": { "socketAddress": { "address": "1.2.3.4", - "portValue": 9191 + "portValue": 8080 } }, "filterChains": [ diff --git a/agent/xds/testdata/listeners/ingress-with-tls-listener.envoy-1-20-x.golden b/agent/xds/testdata/listeners/ingress-with-tls-listener.envoy-1-20-x.golden index f578c2ace9..b635a227e8 100644 --- a/agent/xds/testdata/listeners/ingress-with-tls-listener.envoy-1-20-x.golden +++ b/agent/xds/testdata/listeners/ingress-with-tls-listener.envoy-1-20-x.golden @@ -3,11 +3,11 @@ "resources": [ { "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", - "name": "db:1.2.3.4:9191", + "name": "db:1.2.3.4:8080", "address": { "socketAddress": { "address": "1.2.3.4", - "portValue": 9191 + "portValue": 8080 } }, "filterChains": [ diff --git a/agent/xds/testdata/routes/defaults-no-chain.envoy-1-20-x.golden b/agent/xds/testdata/routes/defaults.envoy-1-20-x.golden similarity index 100% rename from agent/xds/testdata/routes/defaults-no-chain.envoy-1-20-x.golden rename to agent/xds/testdata/routes/defaults.envoy-1-20-x.golden diff --git a/agent/xds/testdata/routes/ingress-lb-in-resolver.envoy-1-20-x.golden b/agent/xds/testdata/routes/ingress-lb-in-resolver.envoy-1-20-x.golden index f2830cf0ca..f2bc276d8e 100644 --- a/agent/xds/testdata/routes/ingress-lb-in-resolver.envoy-1-20-x.golden +++ b/agent/xds/testdata/routes/ingress-lb-in-resolver.envoy-1-20-x.golden @@ -3,13 +3,13 @@ "resources": [ { "@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", - "name": "9191", + "name": "8080", "virtualHosts": [ { "name": "db", "domains": [ "db.ingress.*", - "db.ingress.*:9191" + "db.ingress.*:8080" ], "routes": [ { diff --git a/agent/xds/testdata/routes/ingress-splitter-with-resolver-redirect.envoy-1-20-x.golden b/agent/xds/testdata/routes/ingress-splitter-with-resolver-redirect.envoy-1-20-x.golden index 4353e33b10..4c3fee303c 100644 --- a/agent/xds/testdata/routes/ingress-splitter-with-resolver-redirect.envoy-1-20-x.golden +++ b/agent/xds/testdata/routes/ingress-splitter-with-resolver-redirect.envoy-1-20-x.golden @@ -3,13 +3,13 @@ "resources": [ { "@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", - "name": "9191", + "name": "8080", "virtualHosts": [ { "name": "db", "domains": [ "db.ingress.*", - "db.ingress.*:9191" + "db.ingress.*:8080" ], "routes": [ { diff --git a/agent/xds/testdata/routes/ingress-with-chain-and-overrides.envoy-1-20-x.golden b/agent/xds/testdata/routes/ingress-with-chain-and-overrides.envoy-1-20-x.golden deleted file mode 100644 index 6bbc48e6d1..0000000000 --- a/agent/xds/testdata/routes/ingress-with-chain-and-overrides.envoy-1-20-x.golden +++ /dev/null @@ -1,7 +0,0 @@ -{ - "versionInfo": "00000001", - "resources": [ - ], - "typeUrl": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", - "nonce": "00000001" -} \ No newline at end of file diff --git a/agent/xds/testdata/routes/ingress-with-chain-and-router-header-manip.envoy-1-20-x.golden b/agent/xds/testdata/routes/ingress-with-chain-and-router-header-manip.envoy-1-20-x.golden index 649b37240f..4f4ade54cb 100644 --- a/agent/xds/testdata/routes/ingress-with-chain-and-router-header-manip.envoy-1-20-x.golden +++ b/agent/xds/testdata/routes/ingress-with-chain-and-router-header-manip.envoy-1-20-x.golden @@ -3,13 +3,13 @@ "resources": [ { "@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", - "name": "9191", + "name": "8080", "virtualHosts": [ { "name": "db", "domains": [ "db.ingress.*", - "db.ingress.*:9191" + "db.ingress.*:8080" ], "routes": [ { diff --git a/agent/xds/testdata/routes/ingress-with-chain-and-router.envoy-1-20-x.golden b/agent/xds/testdata/routes/ingress-with-chain-and-router.envoy-1-20-x.golden index ddd97143d8..06a8dcc840 100644 --- a/agent/xds/testdata/routes/ingress-with-chain-and-router.envoy-1-20-x.golden +++ b/agent/xds/testdata/routes/ingress-with-chain-and-router.envoy-1-20-x.golden @@ -3,13 +3,13 @@ "resources": [ { "@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", - "name": "9191", + "name": "8080", "virtualHosts": [ { "name": "db", "domains": [ "db.ingress.*", - "db.ingress.*:9191" + "db.ingress.*:8080" ], "routes": [ { diff --git a/agent/xds/testdata/routes/ingress-with-chain-and-splitter.envoy-1-20-x.golden b/agent/xds/testdata/routes/ingress-with-chain-and-splitter.envoy-1-20-x.golden index 225d4ab914..820de54764 100644 --- a/agent/xds/testdata/routes/ingress-with-chain-and-splitter.envoy-1-20-x.golden +++ b/agent/xds/testdata/routes/ingress-with-chain-and-splitter.envoy-1-20-x.golden @@ -3,13 +3,13 @@ "resources": [ { "@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", - "name": "9191", + "name": "8080", "virtualHosts": [ { "name": "db", "domains": [ "db.ingress.*", - "db.ingress.*:9191" + "db.ingress.*:8080" ], "routes": [ { diff --git a/agent/xds/testdata/routes/ingress-with-grpc-router.envoy-1-20-x.golden b/agent/xds/testdata/routes/ingress-with-grpc-router.envoy-1-20-x.golden index d8ed06629c..bc6d021fbb 100644 --- a/agent/xds/testdata/routes/ingress-with-grpc-router.envoy-1-20-x.golden +++ b/agent/xds/testdata/routes/ingress-with-grpc-router.envoy-1-20-x.golden @@ -3,13 +3,13 @@ "resources": [ { "@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", - "name": "9191", + "name": "8080", "virtualHosts": [ { "name": "db", "domains": [ "db.ingress.*", - "db.ingress.*:9191" + "db.ingress.*:8080" ], "routes": [ { diff --git a/agent/xds/xds_protocol_helpers_test.go b/agent/xds/xds_protocol_helpers_test.go index 54084fddfa..3ffc6c831b 100644 --- a/agent/xds/xds_protocol_helpers_test.go +++ b/agent/xds/xds_protocol_helpers_test.go @@ -42,7 +42,7 @@ func newTestSnapshot( dbServiceProtocol string, additionalEntries ...structs.ConfigEntry, ) *proxycfg.ConfigSnapshot { - snap := proxycfg.TestConfigSnapshotDiscoveryChainDefaultWithEntries(t, additionalEntries...) + snap := proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", nil, nil, additionalEntries...) snap.ConnectProxy.PreparedQueryEndpoints = map[proxycfg.UpstreamID]structs.CheckServiceNodes{ UID("prepared_query:geo-cache"): proxycfg.TestPreparedQueryNodes(t, "geo-cache"), }