diff --git a/agent/consul/state/config_entry.go b/agent/consul/state/config_entry.go index 19f8655f3..5a8335662 100644 --- a/agent/consul/state/config_entry.go +++ b/agent/consul/state/config_entry.go @@ -495,12 +495,7 @@ func (s *Store) readDiscoveryChainConfigEntriesTxn( serviceName string, overrides map[structs.ConfigEntryKindName]structs.ConfigEntry, ) (uint64, *structs.DiscoveryChainConfigEntries, error) { - res := &structs.DiscoveryChainConfigEntries{ - Routers: make(map[string]*structs.ServiceRouterConfigEntry), - Splitters: make(map[string]*structs.ServiceSplitterConfigEntry), - Resolvers: make(map[string]*structs.ServiceResolverConfigEntry), - Services: make(map[string]*structs.ServiceConfigEntry), - } + res := structs.NewDiscoveryChainConfigEntries() // Note that below we always look up splitters and resolvers in pairs, even // in some circumstances where both are not strictly necessary. diff --git a/agent/proxycfg/manager_test.go b/agent/proxycfg/manager_test.go index 20581bd82..a42983a0c 100644 --- a/agent/proxycfg/manager_test.go +++ b/agent/proxycfg/manager_test.go @@ -3,9 +3,11 @@ package proxycfg import ( "log" "os" + "path" "testing" "time" + "github.com/mitchellh/copystructure" "github.com/stretchr/testify/require" "github.com/hashicorp/consul/agent/cache" @@ -39,63 +41,264 @@ func assertLastReqArgs(t *testing.T, types *TestCacheTypes, token string, source } func TestManager_BasicLifecycle(t *testing.T) { - // Use a mocked cache to make life simpler - types := NewTestCacheTypes(t) - c := TestCacheWithTypes(t, types) - - require := require.New(t) - + // Create a bunch of common data for the various test cases. roots, leaf := TestCerts(t) - // Initialize a default group resolver for "db" - dbResolverEntry := &structs.ServiceResolverConfigEntry{ - Kind: structs.ServiceResolver, - Name: "db", - } dbTarget := structs.DiscoveryTarget{ Service: "db", Namespace: "default", Datacenter: "dc1", } - dbResolverNode := &structs.DiscoveryGraphNode{ - Type: structs.DiscoveryGraphNodeTypeGroupResolver, - Name: "db", - GroupResolver: &structs.DiscoveryGroupResolver{ - Definition: dbResolverEntry, - Default: true, - Target: dbTarget, - }, + dbTarget_v1 := structs.DiscoveryTarget{ + Service: "db", + ServiceSubset: "v1", + Namespace: "default", + Datacenter: "dc1", } - dbChain := &structs.CompiledDiscoveryChain{ - ServiceName: "db", - Namespace: "default", - Datacenter: "dc1", - Protocol: "tcp", - Node: dbResolverNode, - GroupResolverNodes: map[structs.DiscoveryTarget]*structs.DiscoveryGraphNode{ - dbTarget: dbResolverNode, - }, - Resolvers: map[string]*structs.ServiceResolverConfigEntry{ - "db": dbResolverEntry, - }, - Targets: []structs.DiscoveryTarget{ - dbTarget, + dbTarget_v2 := structs.DiscoveryTarget{ + Service: "db", + ServiceSubset: "v2", + Namespace: "default", + Datacenter: "dc1", + } + dbDefaultChain := func() *structs.CompiledDiscoveryChain { + return TestCompileConfigEntries(t, "db", "default", "dc1", + &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "db", + }, + ) + } + dbSplitChain := func() *structs.CompiledDiscoveryChain { + return TestCompileConfigEntries(t, "db", "default", "dc1", + &structs.ProxyConfigEntry{ + Kind: structs.ProxyDefaults, + Name: structs.ProxyConfigGlobal, + Config: map[string]interface{}{ + "protocol": "http", + }, + }, + &structs.ServiceResolverConfigEntry{ + Kind: structs.ServiceResolver, + Name: "db", + Subsets: map[string]structs.ServiceResolverSubset{ + "v1": { + Filter: "Service.Meta.version == v1", + }, + "v2": { + Filter: "Service.Meta.version == v2", + }, + }, + }, + &structs.ServiceSplitterConfigEntry{ + Kind: structs.ServiceSplitter, + Name: "db", + Splits: []structs.ServiceSplit{ + {Weight: 60, ServiceSubset: "v1"}, + {Weight: 40, ServiceSubset: "v2"}, + }, + }, + ) + } + webProxy := &structs.NodeService{ + Kind: structs.ServiceKindConnectProxy, + ID: "web-sidecar-proxy", + 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: structs.TestUpstreams(t), }, } - // Setup initial values - types.roots.value.Store(roots) - types.leaf.value.Store(leaf) - types.intentions.value.Store(TestIntentions(t)) - types.health.value.Store( - &structs.IndexedCheckServiceNodes{ - Nodes: TestUpstreamNodes(t), - }) - types.compiledChain.value.Store( - &structs.DiscoveryChainResponse{ - Chain: dbChain, + rootsCacheKey := testGenCacheKey(&structs.DCSpecificRequest{ + Datacenter: "dc1", + QueryOptions: structs.QueryOptions{Token: "my-token"}, + }) + leafCacheKey := testGenCacheKey(&cachetype.ConnectCALeafRequest{ + Datacenter: "dc1", + Token: "my-token", + Service: "web", + }) + intentionCacheKey := testGenCacheKey(&structs.IntentionQueryRequest{ + Datacenter: "dc1", + QueryOptions: structs.QueryOptions{Token: "my-token"}, + Match: &structs.IntentionQueryMatch{ + Type: structs.IntentionMatchDestination, + Entries: []structs.IntentionMatchEntry{ + { + Namespace: structs.IntentionDefaultNamespace, + Name: "web", + }, + }, }, - ) + }) + + dbChainCacheKey := testGenCacheKey(&structs.DiscoveryChainRequest{ + Datacenter: "dc1", + QueryOptions: structs.QueryOptions{Token: "my-token"}, + Name: "db", + }) + + dbHealthCacheKey := testGenCacheKey(&structs.ServiceSpecificRequest{ + Datacenter: "dc1", + QueryOptions: structs.QueryOptions{Token: "my-token", Filter: ""}, + ServiceName: "db", + Connect: true, + }) + db_v1_HealthCacheKey := testGenCacheKey(&structs.ServiceSpecificRequest{ + Datacenter: "dc1", + QueryOptions: structs.QueryOptions{Token: "my-token", + Filter: "Service.Meta.version == v1", + }, + ServiceName: "db", + Connect: true, + }) + db_v2_HealthCacheKey := testGenCacheKey(&structs.ServiceSpecificRequest{ + Datacenter: "dc1", + QueryOptions: structs.QueryOptions{Token: "my-token", + Filter: "Service.Meta.version == v2", + }, + ServiceName: "db", + Connect: true, + }) + + // Create test cases using some of the common data above. + tests := []*testcase_BasicLifecycle{ + { + name: "simple-default-resolver", + setup: func(t *testing.T, types *TestCacheTypes) { + // Note that we deliberately leave the 'geo-cache' prepared query to time out + types.health.Set(dbHealthCacheKey, &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t), + }) + types.compiledChain.Set(dbChainCacheKey, &structs.DiscoveryChainResponse{ + Chain: dbDefaultChain(), + }) + }, + expectSnap: &ConfigSnapshot{ + Kind: structs.ServiceKindConnectProxy, + Service: webProxy.Service, + ProxyID: webProxy.ID, + Address: webProxy.Address, + Port: webProxy.Port, + Proxy: webProxy.Proxy, + TaggedAddresses: make(map[string]structs.ServiceAddress), + Roots: roots, + ConnectProxy: configSnapshotConnectProxy{ + Leaf: leaf, + DiscoveryChain: map[string]*structs.CompiledDiscoveryChain{ + "db": dbDefaultChain(), + }, + WatchedUpstreams: nil, // Clone() clears this out + WatchedUpstreamEndpoints: map[string]map[structs.DiscoveryTarget]structs.CheckServiceNodes{ + "db": { + dbTarget: TestUpstreamNodes(t), + }, + }, + UpstreamEndpoints: map[string]structs.CheckServiceNodes{}, + }, + Datacenter: "dc1", + }, + }, + { + name: "chain-resolver-with-version-split", + setup: func(t *testing.T, types *TestCacheTypes) { + // Note that we deliberately leave the 'geo-cache' prepared query to time out + types.health.Set(db_v1_HealthCacheKey, &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodes(t), + }) + types.health.Set(db_v2_HealthCacheKey, &structs.IndexedCheckServiceNodes{ + Nodes: TestUpstreamNodesAlternate(t), + }) + types.compiledChain.Set(dbChainCacheKey, &structs.DiscoveryChainResponse{ + Chain: dbSplitChain(), + }) + }, + expectSnap: &ConfigSnapshot{ + Kind: structs.ServiceKindConnectProxy, + Service: webProxy.Service, + ProxyID: webProxy.ID, + Address: webProxy.Address, + Port: webProxy.Port, + Proxy: webProxy.Proxy, + TaggedAddresses: make(map[string]structs.ServiceAddress), + Roots: roots, + ConnectProxy: configSnapshotConnectProxy{ + Leaf: leaf, + DiscoveryChain: map[string]*structs.CompiledDiscoveryChain{ + "db": dbSplitChain(), + }, + WatchedUpstreams: nil, // Clone() clears this out + WatchedUpstreamEndpoints: map[string]map[structs.DiscoveryTarget]structs.CheckServiceNodes{ + "db": { + dbTarget_v1: TestUpstreamNodes(t), + dbTarget_v2: TestUpstreamNodesAlternate(t), + }, + }, + UpstreamEndpoints: map[string]structs.CheckServiceNodes{}, + }, + Datacenter: "dc1", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.NotNil(t, tt.setup) + require.NotNil(t, tt.expectSnap) + + // Use a mocked cache to make life simpler + types := NewTestCacheTypes(t) + + // Setup initial values + types.roots.Set(rootsCacheKey, roots) + types.leaf.Set(leafCacheKey, leaf) + types.intentions.Set(intentionCacheKey, TestIntentions(t)) + tt.setup(t, types) + + expectSnapCopy, err := copystructure.Copy(tt.expectSnap) + require.NoError(t, err) + + webProxyCopy, err := copystructure.Copy(webProxy) + require.NoError(t, err) + + testManager_BasicLifecycle(t, tt, types, + rootsCacheKey, leafCacheKey, + roots, leaf, + webProxyCopy.(*structs.NodeService), + expectSnapCopy.(*ConfigSnapshot), + ) + }) + } +} + +type testcase_BasicLifecycle struct { + name string + setup func(t *testing.T, types *TestCacheTypes) + webProxy *structs.NodeService + expectSnap *ConfigSnapshot +} + +func testManager_BasicLifecycle( + t *testing.T, + tt *testcase_BasicLifecycle, + types *TestCacheTypes, + rootsCacheKey, leafCacheKey string, + roots *structs.IndexedCARoots, + leaf *structs.IssuedCert, + webProxy *structs.NodeService, + expectSnap *ConfigSnapshot, +) { + c := TestCacheWithTypes(t, types) + + require := require.New(t) logger := log.New(os.Stderr, "", log.LstdFlags) state := local.NewState(local.Config{}, logger, &token.Store{}) @@ -117,24 +320,6 @@ func TestManager_BasicLifecycle(t *testing.T) { require.NoError(err) }() - // Register a proxy for "web" - webProxy := &structs.NodeService{ - Kind: structs.ServiceKindConnectProxy, - ID: "web-sidecar-proxy", - 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: structs.TestUpstreams(t), - }, - } - // BEFORE we register, we should be able to get a watch channel wCh, cancel := m.Watch(webProxy.ID) defer cancel() @@ -146,30 +331,6 @@ func TestManager_BasicLifecycle(t *testing.T) { // We should see the initial config delivered but not until after the // coalesce timeout - expectSnap := &ConfigSnapshot{ - Kind: structs.ServiceKindConnectProxy, - Service: webProxy.Service, - ProxyID: webProxy.ID, - Address: webProxy.Address, - Port: webProxy.Port, - Proxy: webProxy.Proxy, - TaggedAddresses: make(map[string]structs.ServiceAddress), - Roots: roots, - ConnectProxy: configSnapshotConnectProxy{ - Leaf: leaf, - DiscoveryChain: map[string]*structs.CompiledDiscoveryChain{ - "db": dbChain, - }, - WatchedUpstreams: nil, // Clone() clears this out - WatchedUpstreamEndpoints: map[string]map[structs.DiscoveryTarget]structs.CheckServiceNodes{ - "db": { - dbTarget: TestUpstreamNodes(t), - }, - }, - UpstreamEndpoints: map[string]structs.CheckServiceNodes{}, - }, - Datacenter: "dc1", - } start := time.Now() assertWatchChanRecvs(t, wCh, expectSnap) require.True(time.Since(start) >= coalesceTimeout) @@ -202,7 +363,7 @@ func TestManager_BasicLifecycle(t *testing.T) { // Update roots newRoots, newLeaf := TestCerts(t) newRoots.Roots = append(newRoots.Roots, roots.Roots...) - types.roots.Set(newRoots) + types.roots.Set(rootsCacheKey, newRoots) // Expect new roots in snapshot expectSnap.Roots = newRoots @@ -210,7 +371,7 @@ func TestManager_BasicLifecycle(t *testing.T) { assertWatchChanRecvs(t, wCh2, expectSnap) // Update leaf - types.leaf.Set(newLeaf) + types.leaf.Set(leafCacheKey, newLeaf) // Expect new roots in snapshot expectSnap.ConnectProxy.Leaf = newLeaf @@ -340,3 +501,8 @@ func TestManager_deliverLatest(t *testing.T) { // Check we got the _second_ one require.Equal(snap2, <-ch5) } + +func testGenCacheKey(req cache.Request) string { + info := req.CacheInfo() + return path.Join(info.Key, info.Datacenter) +} diff --git a/agent/proxycfg/state.go b/agent/proxycfg/state.go index 2149a7f74..27e488096 100644 --- a/agent/proxycfg/state.go +++ b/agent/proxycfg/state.go @@ -634,13 +634,24 @@ func (s *state) resetWatchesFromChain( meshGateway = s.proxyCfg.MeshGateway.Mode } + filterExp := subset.Filter + if subset.OnlyPassing { + if filterExp != "" { + // TODO (filtering) - Update to "and all Checks as chk { chk.Status == passing }" + // once the syntax is supported + filterExp = fmt.Sprintf("(%s) and not Checks.Status != passing", filterExp) + } else { + filterExp = "not Checks.Status != passing" + } + } + // TODO(rb): update the health endpoint to allow returning even unhealthy endpoints err = s.watchConnectProxyService( ctx, "upstream-target:"+string(encodedTarget)+":"+id, target.Service, target.Datacenter, - subset.Filter, + filterExp, meshGateway, ) if err != nil { diff --git a/agent/proxycfg/testing.go b/agent/proxycfg/testing.go index 3fbc91b13..4fb38d97a 100644 --- a/agent/proxycfg/testing.go +++ b/agent/proxycfg/testing.go @@ -3,6 +3,7 @@ package proxycfg import ( "context" "fmt" + "path" "sync" "sync/atomic" "time" @@ -10,6 +11,7 @@ import ( "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/mitchellh/go-testing-interface" "github.com/stretchr/testify/require" @@ -155,6 +157,52 @@ func TestUpstreamNodes(t testing.T) structs.CheckServiceNodes { } } +func TestUpstreamNodesDC2(t testing.T) structs.CheckServiceNodes { + return structs.CheckServiceNodes{ + structs.CheckServiceNode{ + Node: &structs.Node{ + ID: "test1", + Node: "test1", + Address: "10.20.1.1", + Datacenter: "dc2", + }, + Service: structs.TestNodeService(t), + }, + structs.CheckServiceNode{ + Node: &structs.Node{ + ID: "test2", + Node: "test2", + Address: "10.20.1.2", + Datacenter: "dc2", + }, + Service: structs.TestNodeService(t), + }, + } +} + +func TestUpstreamNodesAlternate(t testing.T) structs.CheckServiceNodes { + return structs.CheckServiceNodes{ + structs.CheckServiceNode{ + Node: &structs.Node{ + ID: "alt-test1", + Node: "alt-test1", + Address: "10.20.1.1", + Datacenter: "dc1", + }, + Service: structs.TestNodeService(t), + }, + structs.CheckServiceNode{ + Node: &structs.Node{ + ID: "alt-test2", + Node: "alt-test2", + Address: "10.20.1.2", + Datacenter: "dc1", + }, + Service: structs.TestNodeService(t), + }, + } +} + func TestGatewayNodesDC2(t testing.T) structs.CheckServiceNodes { return structs.CheckServiceNodes{ structs.CheckServiceNode{ @@ -379,6 +427,181 @@ func TestConfigSnapshot(t testing.T) *ConfigSnapshot { } } +// TestConfigSnapshotDiscoveryChain returns a fully populated snapshot using a discovery chain +func TestConfigSnapshotDiscoveryChain(t testing.T) *ConfigSnapshot { + return testConfigSnapshotDiscoveryChain(t, "simple") +} + +func TestConfigSnapshotDiscoveryChainWithFailover(t testing.T) *ConfigSnapshot { + return testConfigSnapshotDiscoveryChain(t, "failover") +} + +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 testConfigSnapshotDiscoveryChain(t testing.T, variation string, additionalEntries ...structs.ConfigEntry) *ConfigSnapshot { + roots, leaf := TestCerts(t) + + // Compile a chain. + var entries []structs.ConfigEntry + switch variation { + case "simple": + entries = append(entries, + &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 "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": structs.ServiceResolverSubset{ + Filter: "Service.Meta.version == v1", + }, + "v2": structs.ServiceResolverSubset{ + Filter: "Service.Meta.version == v2", + }, + }, + }, + ) + default: + t.Fatalf("unexpected variation: %q", variation) + return nil + } + + if len(additionalEntries) > 0 { + entries = append(entries, additionalEntries...) + } + + dbChain := TestCompileConfigEntries(t, "db", "default", "dc1", entries...) + + dbTarget := structs.DiscoveryTarget{ + Service: "db", + Namespace: "default", + Datacenter: "dc1", + } + failTarget := structs.DiscoveryTarget{ + Service: "fail", + Namespace: "default", + Datacenter: "dc1", + } + + snap := &ConfigSnapshot{ + Kind: structs.ServiceKindConnectProxy, + Service: "web-sidecar-proxy", + ProxyID: "web-sidecar-proxy", + 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{ + Leaf: leaf, + DiscoveryChain: map[string]*structs.CompiledDiscoveryChain{ + "db": dbChain, + }, + WatchedUpstreamEndpoints: map[string]map[structs.DiscoveryTarget]structs.CheckServiceNodes{ + "db": map[structs.DiscoveryTarget]structs.CheckServiceNodes{ + dbTarget: TestUpstreamNodes(t), + }, + }, + }, + Datacenter: "dc1", + } + + switch variation { + case "simple": + case "failover": + snap.ConnectProxy.WatchedUpstreamEndpoints["db"][failTarget] = + TestUpstreamNodesAlternate(t) + case "splitter-with-resolver-redirect-multidc": + dbTarget_v1_dc1 := structs.DiscoveryTarget{ + Service: "db", + ServiceSubset: "v1", + Namespace: "default", + Datacenter: "dc1", + } + dbTarget_v2_dc2 := structs.DiscoveryTarget{ + Service: "db", + ServiceSubset: "v2", + Namespace: "default", + Datacenter: "dc2", + } + snap.ConnectProxy.WatchedUpstreamEndpoints["db"] = map[structs.DiscoveryTarget]structs.CheckServiceNodes{ + dbTarget_v1_dc1: TestUpstreamNodes(t), + dbTarget_v2_dc2: TestUpstreamNodesDC2(t), + } + default: + t.Fatalf("unexpected variation: %q", variation) + return nil + } + + return snap +} + func TestConfigSnapshotMeshGateway(t testing.T) *ConfigSnapshot { roots, _ := TestCerts(t) return &ConfigSnapshot{ @@ -421,11 +644,33 @@ func TestConfigSnapshotMeshGateway(t testing.T) *ConfigSnapshot { } } +func TestCompileConfigEntries( + t testing.T, + serviceName string, + currentNamespace string, + currentDatacenter string, + entries ...structs.ConfigEntry, +) *structs.CompiledDiscoveryChain { + set := structs.NewDiscoveryChainConfigEntries() + + set.AddEntries(entries...) + + chain, err := discoverychain.Compile(discoverychain.CompileRequest{ + ServiceName: serviceName, + CurrentNamespace: currentNamespace, + CurrentDatacenter: currentDatacenter, + InferDefaults: true, + Entries: set, + }) + require.NoError(t, err) + return chain +} + // ControllableCacheType is a cache.Type that simulates a typical blocking RPC // but lets us control the responses and when they are delivered easily. type ControllableCacheType struct { index uint64 - value atomic.Value + value sync.Map // Need a condvar to trigger all blocking requests (there might be multiple // for same type due to background refresh and timing issues) when values // change. Chans make it nondeterministic which one triggers or need extra @@ -449,9 +694,9 @@ func NewControllableCacheType(t testing.T) *ControllableCacheType { // Set sets the response value to be returned from subsequent cache gets for the // type. -func (ct *ControllableCacheType) Set(value interface{}) { +func (ct *ControllableCacheType) Set(key string, value interface{}) { atomic.AddUint64(&ct.index, 1) - ct.value.Store(value) + ct.value.Store(key, value) ct.triggerMu.Lock() ct.trigger.Broadcast() ct.triggerMu.Unlock() @@ -459,7 +704,6 @@ func (ct *ControllableCacheType) Set(value interface{}) { // Fetch implements cache.Type. It simulates blocking or non-blocking queries. func (ct *ControllableCacheType) Fetch(opts cache.FetchOptions, req cache.Request) (cache.FetchResult, error) { - index := atomic.LoadUint64(&ct.index) ct.lastReq.Store(req) @@ -475,9 +719,12 @@ func (ct *ControllableCacheType) Fetch(opts cache.FetchOptions, req cache.Reques ct.triggerMu.Unlock() } + info := req.CacheInfo() + key := path.Join(info.Key, info.Datacenter) // omit token for testing purposes + // reload index as it probably got bumped index = atomic.LoadUint64(&ct.index) - val := ct.value.Load() + val, _ := ct.value.Load(key) if err, ok := val.(error); ok { return cache.FetchResult{ diff --git a/agent/structs/config_entry_discoverychain.go b/agent/structs/config_entry_discoverychain.go index b8b270c93..369232381 100644 --- a/agent/structs/config_entry_discoverychain.go +++ b/agent/structs/config_entry_discoverychain.go @@ -99,6 +99,8 @@ func (e *ServiceRouterConfigEntry) Validate() error { return fmt.Errorf("Route[%d] should only contain at most one of PathExact, PathPrefix, or PathRegex", i) } + // TODO(rb): do some validation of PathExact and PathPrefix + for j, hdr := range route.Match.HTTP.Header { if hdr.Name == "" { return fmt.Errorf("Route[%d] Header[%d] missing required Name field", i, j) @@ -284,6 +286,10 @@ type ServiceRouteDestination struct { RetryOnStatusCodes []uint32 `json:",omitempty"` } +func (d *ServiceRouteDestination) HasRetryFeatures() bool { + return d.NumRetries > 0 || d.RetryOnConnectFailure || len(d.RetryOnStatusCodes) > 0 +} + // ServiceSplitterConfigEntry defines how incoming requests are split across // different subsets of a single service (like during staged canary rollouts), // or perhaps across different services (like during a v2 rewrite or other type @@ -845,6 +851,15 @@ type DiscoveryChainConfigEntries struct { GlobalProxy *ProxyConfigEntry } +func NewDiscoveryChainConfigEntries() *DiscoveryChainConfigEntries { + return &DiscoveryChainConfigEntries{ + Routers: make(map[string]*ServiceRouterConfigEntry), + Splitters: make(map[string]*ServiceSplitterConfigEntry), + Resolvers: make(map[string]*ServiceResolverConfigEntry), + Services: make(map[string]*ServiceConfigEntry), + } +} + func (e *DiscoveryChainConfigEntries) GetRouter(name string) *ServiceRouterConfigEntry { if e.Routers != nil { return e.Routers[name] @@ -913,6 +928,30 @@ func (e *DiscoveryChainConfigEntries) AddServices(entries ...*ServiceConfigEntry } } +// AddEntries adds generic configs. Convenience function for testing. Panics on +// operator error. +func (e *DiscoveryChainConfigEntries) AddEntries(entries ...ConfigEntry) { + for _, entry := range entries { + switch entry.GetKind() { + case ServiceRouter: + e.AddRouters(entry.(*ServiceRouterConfigEntry)) + case ServiceSplitter: + e.AddSplitters(entry.(*ServiceSplitterConfigEntry)) + case ServiceResolver: + e.AddResolvers(entry.(*ServiceResolverConfigEntry)) + case ServiceDefaults: + e.AddServices(entry.(*ServiceConfigEntry)) + case ProxyDefaults: + if entry.GetName() != ProxyConfigGlobal { + panic("the only supported proxy-defaults name is '" + ProxyConfigGlobal + "'") + } + e.GlobalProxy = entry.(*ProxyConfigEntry) + default: + panic("unhandled config entry kind: " + entry.GetKind()) + } + } +} + func (e *DiscoveryChainConfigEntries) IsEmpty() bool { return e.IsChainEmpty() && len(e.Services) == 0 && e.GlobalProxy == nil } diff --git a/agent/structs/testing_catalog.go b/agent/structs/testing_catalog.go index 84ee019a7..e1f847bf6 100644 --- a/agent/structs/testing_catalog.go +++ b/agent/structs/testing_catalog.go @@ -34,6 +34,7 @@ func TestNodeService(t testing.T) *NodeService { return &NodeService{ Kind: ServiceKindTypical, Service: "web", + Port: 8080, } } diff --git a/agent/xds/clusters.go b/agent/xds/clusters.go index 67bf059aa..a68165f73 100644 --- a/agent/xds/clusters.go +++ b/agent/xds/clusters.go @@ -255,8 +255,6 @@ func (s *Server) makeUpstreamClustersForDiscoveryChain( var out []*envoy.Cluster for target, node := range chain.GroupResolverNodes { groupResolver := node.GroupResolver - // TODO(rb): failover - // Failover *DiscoveryFailover `json:",omitempty"` // sad path sni := TargetSNI(target, cfgSnap) s.Logger.Printf("[DEBUG] xds.clusters - generating cluster for %s", sni) diff --git a/agent/xds/clusters_test.go b/agent/xds/clusters_test.go index 6ef3fa1f5..714dbdb73 100644 --- a/agent/xds/clusters_test.go +++ b/agent/xds/clusters_test.go @@ -100,6 +100,21 @@ func TestClustersFromSnapshot(t *testing.T) { snap.Proxy.Upstreams[0].Config["connect_timeout_ms"] = 2345 }, }, + { + name: "connect-proxy-with-chain", + create: proxycfg.TestConfigSnapshotDiscoveryChain, + setup: nil, + }, + { + name: "connect-proxy-with-chain-and-failover", + create: proxycfg.TestConfigSnapshotDiscoveryChainWithFailover, + setup: nil, + }, + { + name: "splitter-with-resolver-redirect", + create: proxycfg.TestConfigSnapshotDiscoveryChain_SplitterWithResolverRedirectMultiDC, + setup: nil, + }, { name: "mesh-gateway", create: proxycfg.TestConfigSnapshotMeshGateway, diff --git a/agent/xds/endpoints_test.go b/agent/xds/endpoints_test.go index 964ffa56a..1077a739a 100644 --- a/agent/xds/endpoints_test.go +++ b/agent/xds/endpoints_test.go @@ -234,6 +234,39 @@ func Test_endpointsFromSnapshot(t *testing.T) { create: proxycfg.TestConfigSnapshotMeshGateway, setup: nil, }, + { + name: "connect-proxy-with-chain", + create: proxycfg.TestConfigSnapshotDiscoveryChain, + setup: nil, + }, + { + name: "connect-proxy-with-chain-and-failover", + create: proxycfg.TestConfigSnapshotDiscoveryChainWithFailover, + setup: nil, + }, + { + name: "connect-proxy-with-chain-and-sliding-failover", + create: proxycfg.TestConfigSnapshotDiscoveryChainWithFailover, + setup: func(snap *proxycfg.ConfigSnapshot) { + chain := snap.ConnectProxy.DiscoveryChain["db"] + + dbTarget := structs.DiscoveryTarget{ + Service: "db", + Namespace: "default", + Datacenter: "dc1", + } + dbResolverNode := chain.GroupResolverNodes[dbTarget] + + groupResolverFailover := dbResolverNode.GroupResolver.Failover + + groupResolverFailover.Definition.OverprovisioningFactor = 160 + }, + }, + { + name: "splitter-with-resolver-redirect", + create: proxycfg.TestConfigSnapshotDiscoveryChain_SplitterWithResolverRedirectMultiDC, + setup: nil, + }, { name: "mesh-gateway-service-subsets", create: proxycfg.TestConfigSnapshotMeshGateway, diff --git a/agent/xds/listeners.go b/agent/xds/listeners.go index 317246b7b..ddc78ece9 100644 --- a/agent/xds/listeners.go +++ b/agent/xds/listeners.go @@ -427,6 +427,7 @@ func makeTLSInspectorListenerFilter() (envoylistener.ListenerFilter, error) { return envoylistener.ListenerFilter{Name: util.TlsInspector}, nil } +// TODO(rb): should this be dead code? func makeSNIFilterChainMatch(sniMatch string) (*envoylistener.FilterChainMatch, error) { return &envoylistener.FilterChainMatch{ ServerNames: []string{sniMatch}, diff --git a/agent/xds/listeners_test.go b/agent/xds/listeners_test.go index b3f1ab12f..fbfbee5ea 100644 --- a/agent/xds/listeners_test.go +++ b/agent/xds/listeners_test.go @@ -129,6 +129,61 @@ func TestListenersFromSnapshot(t *testing.T) { }) }, }, + { + name: "splitter-with-resolver-redirect", + create: proxycfg.TestConfigSnapshotDiscoveryChain_SplitterWithResolverRedirectMultiDC, + setup: nil, + }, + { + name: "connect-proxy-with-tcp-chain", + create: proxycfg.TestConfigSnapshotDiscoveryChain, + setup: nil, + }, + { + name: "connect-proxy-with-http-chain", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChainWithEntries(t, + &structs.ProxyConfigEntry{ + Kind: structs.ProxyDefaults, + Name: structs.ProxyConfigGlobal, + Config: map[string]interface{}{ + "protocol": "http", + }, + }, + ) + }, + setup: nil, + }, + { + name: "connect-proxy-with-http2-chain", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChainWithEntries(t, + &structs.ProxyConfigEntry{ + Kind: structs.ProxyDefaults, + Name: structs.ProxyConfigGlobal, + Config: map[string]interface{}{ + "protocol": "http2", + }, + }, + ) + }, + setup: nil, + }, + { + name: "connect-proxy-with-grpc-chain", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChainWithEntries(t, + &structs.ProxyConfigEntry{ + Kind: structs.ProxyDefaults, + Name: structs.ProxyConfigGlobal, + Config: map[string]interface{}{ + "protocol": "grpc", + }, + }, + ) + }, + setup: nil, + }, { name: "mesh-gateway", create: proxycfg.TestConfigSnapshotMeshGateway, diff --git a/agent/xds/routes.go b/agent/xds/routes.go index a9f71b5bf..9f4f1befe 100644 --- a/agent/xds/routes.go +++ b/agent/xds/routes.go @@ -79,10 +79,6 @@ func makeUpstreamRouteForDiscoveryChain( for _, discoveryRoute := range chain.Node.Routes { routeMatch := makeRouteMatchForDiscoveryRoute(discoveryRoute, chain.Protocol) - // TODO(rb): handle PrefixRewrite - // TODO(rb): handle RequestTimeout - // TODO(rb): handle Retries - var ( routeAction *envoyroute.Route_Route err error @@ -103,6 +99,41 @@ func makeUpstreamRouteForDiscoveryChain( return nil, fmt.Errorf("unexpected graph node after route %q", next.Type) } + // TODO(rb): Better help handle the envoy case where you need (prefix=/foo/,rewrite=/) and (exact=/foo,rewrite=/) to do a full rewrite + + destination := discoveryRoute.Definition.Destination + if destination != nil { + if destination.PrefixRewrite != "" { + routeAction.Route.PrefixRewrite = destination.PrefixRewrite + } + + if destination.RequestTimeout > 0 { + routeAction.Route.Timeout = &destination.RequestTimeout + } + + if destination.HasRetryFeatures() { + retryPolicy := &envoyroute.RetryPolicy{} + if destination.NumRetries > 0 { + retryPolicy.NumRetries = makeUint32Value(int(destination.NumRetries)) + } + + // The RetryOn magic values come from: https://www.envoyproxy.io/docs/envoy/v1.10.0/configuration/http_filters/router_filter#config-http-filters-router-x-envoy-retry-on + if destination.RetryOnConnectFailure { + retryPolicy.RetryOn = "connect-failure" + } + if len(destination.RetryOnStatusCodes) > 0 { + if retryPolicy.RetryOn != "" { + retryPolicy.RetryOn = ",retriable-status-codes" + } else { + retryPolicy.RetryOn = "retriable-status-codes" + } + retryPolicy.RetriableStatusCodes = destination.RetryOnStatusCodes + } + + routeAction.Route.RetryPolicy = retryPolicy + } + } + routes = append(routes, envoyroute.Route{ Match: routeMatch, Action: routeAction, @@ -171,13 +202,21 @@ func makeRouteMatchForDiscoveryRoute(discoveryRoute *structs.DiscoveryRoute, pro switch { case match.HTTP.PathExact != "": - em.PathSpecifier = &envoyroute.RouteMatch_Path{Path: "/"} + em.PathSpecifier = &envoyroute.RouteMatch_Path{ + Path: match.HTTP.PathExact, + } case match.HTTP.PathPrefix != "": - em.PathSpecifier = &envoyroute.RouteMatch_Prefix{Prefix: "/"} + em.PathSpecifier = &envoyroute.RouteMatch_Prefix{ + Prefix: match.HTTP.PathPrefix, + } case match.HTTP.PathRegex != "": - em.PathSpecifier = &envoyroute.RouteMatch_Regex{Regex: "/"} + em.PathSpecifier = &envoyroute.RouteMatch_Regex{ + Regex: match.HTTP.PathRegex, + } default: - em.PathSpecifier = &envoyroute.RouteMatch_Prefix{Prefix: "/"} + em.PathSpecifier = &envoyroute.RouteMatch_Prefix{ + Prefix: "/", + } } if len(match.HTTP.Header) > 0 { @@ -277,9 +316,10 @@ func makeRouteActionForSplitter(splits []*structs.DiscoverySplit, cfgSnap *proxy target := groupResolver.Target clusterName := TargetSNI(target, cfgSnap) - // TODO(rb): scale up by 100 and adjust total weight + // The smallest representable weight is 1/10000 or .01% but envoy + // deals with integers so scale everything up by 100x. cw := &envoyroute.WeightedCluster_ClusterWeight{ - Weight: makeUint32Value(int(split.Weight)), + Weight: makeUint32Value(int(split.Weight * 100)), Name: clusterName, } @@ -291,7 +331,7 @@ func makeRouteActionForSplitter(splits []*structs.DiscoverySplit, cfgSnap *proxy ClusterSpecifier: &envoyroute.RouteAction_WeightedClusters{ WeightedClusters: &envoyroute.WeightedCluster{ Clusters: clusters, - TotalWeight: makeUint32Value(100), + TotalWeight: makeUint32Value(10000), // scaled up 100% }, }, }, diff --git a/agent/xds/routes_test.go b/agent/xds/routes_test.go new file mode 100644 index 000000000..a5d738a8b --- /dev/null +++ b/agent/xds/routes_test.go @@ -0,0 +1,307 @@ +package xds + +import ( + "path" + "sort" + "testing" + "time" + + envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2" + "github.com/hashicorp/consul/agent/proxycfg" + "github.com/hashicorp/consul/agent/structs" + testinf "github.com/mitchellh/go-testing-interface" + "github.com/stretchr/testify/require" +) + +func TestRoutesFromSnapshot(t *testing.T) { + httpMatch := func(http *structs.ServiceRouteHTTPMatch) *structs.ServiceRouteMatch { + return &structs.ServiceRouteMatch{HTTP: http} + } + httpMatchHeader := func(headers ...structs.ServiceRouteHTTPMatchHeader) *structs.ServiceRouteMatch { + return httpMatch(&structs.ServiceRouteHTTPMatch{ + Header: headers, + }) + } + httpMatchParam := func(params ...structs.ServiceRouteHTTPMatchQueryParam) *structs.ServiceRouteMatch { + return httpMatch(&structs.ServiceRouteHTTPMatch{ + QueryParam: params, + }) + } + toService := func(svc string) *structs.ServiceRouteDestination { + return &structs.ServiceRouteDestination{Service: svc} + } + + 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) + overrideGoldenName string + }{ + { + name: "defaults-no-chain", + create: proxycfg.TestConfigSnapshot, + setup: nil, // Default snapshot + }, + { + name: "connect-proxy-with-chain", + create: proxycfg.TestConfigSnapshotDiscoveryChain, + setup: nil, + }, + { + name: "splitter-with-resolver-redirect", + create: proxycfg.TestConfigSnapshotDiscoveryChain_SplitterWithResolverRedirectMultiDC, + setup: nil, + }, + { + name: "connect-proxy-with-chain-and-splitter", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChainWithEntries(t, + &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"}, + {Weight: 4, Service: "goldilocks-side"}, + {Weight: 0.5, Service: "lil-bit-side"}, + }, + }, + ) + }, + setup: nil, + }, + { + name: "connect-proxy-with-chain-and-router", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChainWithEntries(t, + &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", + 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: httpMatchParam(structs.ServiceRouteHTTPMatchQueryParam{ + Name: "secretparam", + Value: "exact", + }), + Destination: toService("prm-exact"), + }, + { + Match: httpMatchParam(structs.ServiceRouteHTTPMatchQueryParam{ + Name: "secretparam", + Value: "regex", + Regex: true, + }), + Destination: toService("prm-regex"), + }, + { + 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"), + }, + }, + }, + ) + }, + setup: nil, + }, + // TODO(rb): test match stanza skipped for grpc + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + + // Sanity check default with no overrides first + snap := tt.create(t) + + // We need to replace the TLS certs with deterministic ones to make golden + // files workable. Note we don't update these otherwise they'd change + // golden files for every test case and so not be any use! + if snap.ConnectProxy.Leaf != nil { + snap.ConnectProxy.Leaf.CertPEM = golden(t, "test-leaf-cert", "") + snap.ConnectProxy.Leaf.PrivateKeyPEM = golden(t, "test-leaf-key", "") + } + if snap.Roots != nil { + snap.Roots.Roots[0].RootCert = golden(t, "test-root-cert", "") + } + + if tt.setup != nil { + tt.setup(snap) + } + + routes, err := routesFromSnapshot(snap, "my-token") + require.NoError(err) + sort.Slice(routes, func(i, j int) bool { + return routes[i].(*envoy.RouteConfiguration).Name < routes[j].(*envoy.RouteConfiguration).Name + }) + r, err := createResponse(RouteType, "00000001", "00000001", routes) + require.NoError(err) + + gotJSON := responseToJSON(t, r) + + gName := tt.name + if tt.overrideGoldenName != "" { + gName = tt.overrideGoldenName + } + + require.JSONEq(golden(t, path.Join("routes", gName), gotJSON), gotJSON) + }) + } +} diff --git a/agent/xds/server_test.go b/agent/xds/server_test.go index 22481c7fa..b651e8cb1 100644 --- a/agent/xds/server_test.go +++ b/agent/xds/server_test.go @@ -245,7 +245,7 @@ func expectEndpointsJSON(t *testing.T, snap *proxycfg.ConfigSnapshot, token stri "address": { "socketAddress": { "address": "10.10.1.1", - "portValue": 0 + "portValue": 8080 } } }, @@ -257,7 +257,7 @@ func expectEndpointsJSON(t *testing.T, snap *proxycfg.ConfigSnapshot, token stri "address": { "socketAddress": { "address": "10.10.1.2", - "portValue": 0 + "portValue": 8080 } } }, diff --git a/agent/xds/testdata/clusters/connect-proxy-with-chain-and-failover.golden b/agent/xds/testdata/clusters/connect-proxy-with-chain-and-failover.golden new file mode 100644 index 000000000..316e7780f --- /dev/null +++ b/agent/xds/testdata/clusters/connect-proxy-with-chain-and-failover.golden @@ -0,0 +1,116 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.api.v2.Cluster", + "name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "altStatName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": { + + } + } + }, + "connectTimeout": "33s", + "tlsContext": { + "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" + } + } + }, + "sni": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + }, + "outlierDetection": { + + }, + "commonLbConfig": { + "healthyPanicThreshold": { + + } + } + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Cluster", + "name": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": { + + } + } + }, + "connectTimeout": "5s", + "tlsContext": { + "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" + } + } + }, + "sni": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + }, + "outlierDetection": { + + } + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Cluster", + "name": "local_app", + "type": "STATIC", + "connectTimeout": "5s", + "loadAssignment": { + "clusterName": "local_app", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 8080 + } + } + } + } + ] + } + ] + } + } + ], + "typeUrl": "type.googleapis.com/envoy.api.v2.Cluster", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/clusters/connect-proxy-with-chain.golden b/agent/xds/testdata/clusters/connect-proxy-with-chain.golden new file mode 100644 index 000000000..316e7780f --- /dev/null +++ b/agent/xds/testdata/clusters/connect-proxy-with-chain.golden @@ -0,0 +1,116 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.api.v2.Cluster", + "name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "altStatName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": { + + } + } + }, + "connectTimeout": "33s", + "tlsContext": { + "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" + } + } + }, + "sni": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + }, + "outlierDetection": { + + }, + "commonLbConfig": { + "healthyPanicThreshold": { + + } + } + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Cluster", + "name": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": { + + } + } + }, + "connectTimeout": "5s", + "tlsContext": { + "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" + } + } + }, + "sni": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + }, + "outlierDetection": { + + } + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Cluster", + "name": "local_app", + "type": "STATIC", + "connectTimeout": "5s", + "loadAssignment": { + "clusterName": "local_app", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 8080 + } + } + } + } + ] + } + ] + } + } + ], + "typeUrl": "type.googleapis.com/envoy.api.v2.Cluster", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/clusters/splitter-with-resolver-redirect.golden b/agent/xds/testdata/clusters/splitter-with-resolver-redirect.golden new file mode 100644 index 000000000..99c93b187 --- /dev/null +++ b/agent/xds/testdata/clusters/splitter-with-resolver-redirect.golden @@ -0,0 +1,161 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.api.v2.Cluster", + "name": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": { + + } + } + }, + "connectTimeout": "5s", + "tlsContext": { + "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" + } + } + }, + "sni": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + }, + "outlierDetection": { + + } + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Cluster", + "name": "local_app", + "type": "STATIC", + "connectTimeout": "5s", + "loadAssignment": { + "clusterName": "local_app", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 8080 + } + } + } + } + ] + } + ] + } + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Cluster", + "name": "v1.db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "altStatName": "v1.db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": { + + } + } + }, + "connectTimeout": "5s", + "tlsContext": { + "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" + } + } + }, + "sni": "v1.db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + }, + "outlierDetection": { + + }, + "commonLbConfig": { + "healthyPanicThreshold": { + + } + } + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Cluster", + "name": "v2.db.default.dc2.internal.11111111-2222-3333-4444-555555555555.consul", + "altStatName": "v2.db.default.dc2.internal.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": { + + } + } + }, + "connectTimeout": "5s", + "tlsContext": { + "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" + } + } + }, + "sni": "v2.db.default.dc2.internal.11111111-2222-3333-4444-555555555555.consul" + }, + "outlierDetection": { + + }, + "commonLbConfig": { + "healthyPanicThreshold": { + + } + } + } + ], + "typeUrl": "type.googleapis.com/envoy.api.v2.Cluster", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/endpoints/connect-proxy-with-chain-and-failover.golden b/agent/xds/testdata/endpoints/connect-proxy-with-chain-and-failover.golden new file mode 100644 index 000000000..749b3a80c --- /dev/null +++ b/agent/xds/testdata/endpoints/connect-proxy-with-chain-and-failover.golden @@ -0,0 +1,73 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment", + "clusterName": "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 + } + ] + }, + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.20.1.1", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + }, + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.20.1.2", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + } + ], + "priority": 1 + } + ], + "policy": { + "overprovisioningFactor": 100000 + } + } + ], + "typeUrl": "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/endpoints/connect-proxy-with-chain-and-sliding-failover.golden b/agent/xds/testdata/endpoints/connect-proxy-with-chain-and-sliding-failover.golden new file mode 100644 index 000000000..5f7883a63 --- /dev/null +++ b/agent/xds/testdata/endpoints/connect-proxy-with-chain-and-sliding-failover.golden @@ -0,0 +1,73 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment", + "clusterName": "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 + } + ] + }, + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.20.1.1", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + }, + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.20.1.2", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + } + ], + "priority": 1 + } + ], + "policy": { + "overprovisioningFactor": 160 + } + } + ], + "typeUrl": "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/endpoints/connect-proxy-with-chain.golden b/agent/xds/testdata/endpoints/connect-proxy-with-chain.golden new file mode 100644 index 000000000..2acef4c0a --- /dev/null +++ b/agent/xds/testdata/endpoints/connect-proxy-with-chain.golden @@ -0,0 +1,41 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment", + "clusterName": "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.api.v2.ClusterLoadAssignment", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/endpoints/defaults.golden b/agent/xds/testdata/endpoints/defaults.golden index 347b9201e..2acef4c0a 100644 --- a/agent/xds/testdata/endpoints/defaults.golden +++ b/agent/xds/testdata/endpoints/defaults.golden @@ -12,7 +12,7 @@ "address": { "socketAddress": { "address": "10.10.1.1", - "portValue": 0 + "portValue": 8080 } } }, @@ -24,7 +24,7 @@ "address": { "socketAddress": { "address": "10.10.1.2", - "portValue": 0 + "portValue": 8080 } } }, diff --git a/agent/xds/testdata/endpoints/splitter-with-resolver-redirect.golden b/agent/xds/testdata/endpoints/splitter-with-resolver-redirect.golden new file mode 100644 index 000000000..5ca0de82e --- /dev/null +++ b/agent/xds/testdata/endpoints/splitter-with-resolver-redirect.golden @@ -0,0 +1,75 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment", + "clusterName": "v1.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 + } + ] + } + ] + }, + { + "@type": "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment", + "clusterName": "v2.db.default.dc2.internal.11111111-2222-3333-4444-555555555555.consul", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.20.1.1", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + }, + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.20.1.2", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + } + ] + } + ] + } + ], + "typeUrl": "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/listeners/connect-proxy-with-grpc-chain.golden b/agent/xds/testdata/listeners/connect-proxy-with-grpc-chain.golden new file mode 100644 index 000000000..a911cd189 --- /dev/null +++ b/agent/xds/testdata/listeners/connect-proxy-with-grpc-chain.golden @@ -0,0 +1,139 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.api.v2.Listener", + "name": "db:127.0.0.1:9191", + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 9191 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.http_connection_manager", + "config": { + "http2_protocol_options": { + }, + "http_filters": [ + { + "config": { + }, + "name": "envoy.grpc_http1_bridge" + }, + { + "name": "envoy.router" + } + ], + "rds": { + "config_source": { + "ads": { + } + }, + "route_config_name": "db" + }, + "stat_prefix": "upstream_db_grpc", + "tracing": { + "operation_name": "EGRESS", + "random_sampling": { + } + } + } + } + ] + } + ] + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Listener", + "name": "prepared_query:geo-cache:127.10.10.10:8181", + "address": { + "socketAddress": { + "address": "127.10.10.10", + "portValue": 8181 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.tcp_proxy", + "config": { + "cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "stat_prefix": "upstream_prepared_query_geo-cache_tcp" + } + } + ] + } + ] + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Listener", + "name": "public_listener:0.0.0.0:9999", + "address": { + "socketAddress": { + "address": "0.0.0.0", + "portValue": 9999 + } + }, + "filterChains": [ + { + "tlsContext": { + "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 + }, + "filters": [ + { + "name": "envoy.ext_authz", + "config": { + "grpc_service": { + "envoy_grpc": { + "cluster_name": "local_agent" + }, + "initial_metadata": [ + { + "key": "x-consul-token", + "value": "my-token" + } + ] + }, + "stat_prefix": "connect_authz" + } + }, + { + "name": "envoy.tcp_proxy", + "config": { + "cluster": "local_app", + "stat_prefix": "public_listener_tcp" + } + } + ] + } + ] + } + ], + "typeUrl": "type.googleapis.com/envoy.api.v2.Listener", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/listeners/connect-proxy-with-http-chain.golden b/agent/xds/testdata/listeners/connect-proxy-with-http-chain.golden new file mode 100644 index 000000000..ddbdd9914 --- /dev/null +++ b/agent/xds/testdata/listeners/connect-proxy-with-http-chain.golden @@ -0,0 +1,132 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.api.v2.Listener", + "name": "db:127.0.0.1:9191", + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 9191 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.http_connection_manager", + "config": { + "http_filters": [ + { + "name": "envoy.router" + } + ], + "rds": { + "config_source": { + "ads": { + } + }, + "route_config_name": "db" + }, + "stat_prefix": "upstream_db_http", + "tracing": { + "operation_name": "EGRESS", + "random_sampling": { + } + } + } + } + ] + } + ] + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Listener", + "name": "prepared_query:geo-cache:127.10.10.10:8181", + "address": { + "socketAddress": { + "address": "127.10.10.10", + "portValue": 8181 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.tcp_proxy", + "config": { + "cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "stat_prefix": "upstream_prepared_query_geo-cache_tcp" + } + } + ] + } + ] + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Listener", + "name": "public_listener:0.0.0.0:9999", + "address": { + "socketAddress": { + "address": "0.0.0.0", + "portValue": 9999 + } + }, + "filterChains": [ + { + "tlsContext": { + "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 + }, + "filters": [ + { + "name": "envoy.ext_authz", + "config": { + "grpc_service": { + "envoy_grpc": { + "cluster_name": "local_agent" + }, + "initial_metadata": [ + { + "key": "x-consul-token", + "value": "my-token" + } + ] + }, + "stat_prefix": "connect_authz" + } + }, + { + "name": "envoy.tcp_proxy", + "config": { + "cluster": "local_app", + "stat_prefix": "public_listener_tcp" + } + } + ] + } + ] + } + ], + "typeUrl": "type.googleapis.com/envoy.api.v2.Listener", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/listeners/connect-proxy-with-http2-chain.golden b/agent/xds/testdata/listeners/connect-proxy-with-http2-chain.golden new file mode 100644 index 000000000..6994537d4 --- /dev/null +++ b/agent/xds/testdata/listeners/connect-proxy-with-http2-chain.golden @@ -0,0 +1,134 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.api.v2.Listener", + "name": "db:127.0.0.1:9191", + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 9191 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.http_connection_manager", + "config": { + "http2_protocol_options": { + }, + "http_filters": [ + { + "name": "envoy.router" + } + ], + "rds": { + "config_source": { + "ads": { + } + }, + "route_config_name": "db" + }, + "stat_prefix": "upstream_db_http", + "tracing": { + "operation_name": "EGRESS", + "random_sampling": { + } + } + } + } + ] + } + ] + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Listener", + "name": "prepared_query:geo-cache:127.10.10.10:8181", + "address": { + "socketAddress": { + "address": "127.10.10.10", + "portValue": 8181 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.tcp_proxy", + "config": { + "cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "stat_prefix": "upstream_prepared_query_geo-cache_tcp" + } + } + ] + } + ] + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Listener", + "name": "public_listener:0.0.0.0:9999", + "address": { + "socketAddress": { + "address": "0.0.0.0", + "portValue": 9999 + } + }, + "filterChains": [ + { + "tlsContext": { + "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 + }, + "filters": [ + { + "name": "envoy.ext_authz", + "config": { + "grpc_service": { + "envoy_grpc": { + "cluster_name": "local_agent" + }, + "initial_metadata": [ + { + "key": "x-consul-token", + "value": "my-token" + } + ] + }, + "stat_prefix": "connect_authz" + } + }, + { + "name": "envoy.tcp_proxy", + "config": { + "cluster": "local_app", + "stat_prefix": "public_listener_tcp" + } + } + ] + } + ] + } + ], + "typeUrl": "type.googleapis.com/envoy.api.v2.Listener", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/listeners/connect-proxy-with-tcp-chain.golden b/agent/xds/testdata/listeners/connect-proxy-with-tcp-chain.golden new file mode 100644 index 000000000..e83b237df --- /dev/null +++ b/agent/xds/testdata/listeners/connect-proxy-with-tcp-chain.golden @@ -0,0 +1,116 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.api.v2.Listener", + "name": "db:127.0.0.1:9191", + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 9191 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.tcp_proxy", + "config": { + "cluster": "", + "stat_prefix": "upstream_db_tcp" + } + } + ] + } + ] + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Listener", + "name": "prepared_query:geo-cache:127.10.10.10:8181", + "address": { + "socketAddress": { + "address": "127.10.10.10", + "portValue": 8181 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.tcp_proxy", + "config": { + "cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "stat_prefix": "upstream_prepared_query_geo-cache_tcp" + } + } + ] + } + ] + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Listener", + "name": "public_listener:0.0.0.0:9999", + "address": { + "socketAddress": { + "address": "0.0.0.0", + "portValue": 9999 + } + }, + "filterChains": [ + { + "tlsContext": { + "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 + }, + "filters": [ + { + "name": "envoy.ext_authz", + "config": { + "grpc_service": { + "envoy_grpc": { + "cluster_name": "local_agent" + }, + "initial_metadata": [ + { + "key": "x-consul-token", + "value": "my-token" + } + ] + }, + "stat_prefix": "connect_authz" + } + }, + { + "name": "envoy.tcp_proxy", + "config": { + "cluster": "local_app", + "stat_prefix": "public_listener_tcp" + } + } + ] + } + ] + } + ], + "typeUrl": "type.googleapis.com/envoy.api.v2.Listener", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/listeners/splitter-with-resolver-redirect.golden b/agent/xds/testdata/listeners/splitter-with-resolver-redirect.golden new file mode 100644 index 000000000..ddbdd9914 --- /dev/null +++ b/agent/xds/testdata/listeners/splitter-with-resolver-redirect.golden @@ -0,0 +1,132 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.api.v2.Listener", + "name": "db:127.0.0.1:9191", + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 9191 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.http_connection_manager", + "config": { + "http_filters": [ + { + "name": "envoy.router" + } + ], + "rds": { + "config_source": { + "ads": { + } + }, + "route_config_name": "db" + }, + "stat_prefix": "upstream_db_http", + "tracing": { + "operation_name": "EGRESS", + "random_sampling": { + } + } + } + } + ] + } + ] + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Listener", + "name": "prepared_query:geo-cache:127.10.10.10:8181", + "address": { + "socketAddress": { + "address": "127.10.10.10", + "portValue": 8181 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.tcp_proxy", + "config": { + "cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "stat_prefix": "upstream_prepared_query_geo-cache_tcp" + } + } + ] + } + ] + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Listener", + "name": "public_listener:0.0.0.0:9999", + "address": { + "socketAddress": { + "address": "0.0.0.0", + "portValue": 9999 + } + }, + "filterChains": [ + { + "tlsContext": { + "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 + }, + "filters": [ + { + "name": "envoy.ext_authz", + "config": { + "grpc_service": { + "envoy_grpc": { + "cluster_name": "local_agent" + }, + "initial_metadata": [ + { + "key": "x-consul-token", + "value": "my-token" + } + ] + }, + "stat_prefix": "connect_authz" + } + }, + { + "name": "envoy.tcp_proxy", + "config": { + "cluster": "local_app", + "stat_prefix": "public_listener_tcp" + } + } + ] + } + ] + } + ], + "typeUrl": "type.googleapis.com/envoy.api.v2.Listener", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/routes/connect-proxy-with-chain-and-router.golden b/agent/xds/testdata/routes/connect-proxy-with-chain-and-router.golden new file mode 100644 index 000000000..5d2f56c6f --- /dev/null +++ b/agent/xds/testdata/routes/connect-proxy-with-chain-and-router.golden @@ -0,0 +1,289 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration", + "name": "db", + "virtualHosts": [ + { + "name": "db", + "domains": [ + "*" + ], + "routes": [ + { + "match": { + "prefix": "/prefix" + }, + "route": { + "cluster": "prefix.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + }, + { + "match": { + "path": "/exact" + }, + "route": { + "cluster": "exact.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + }, + { + "match": { + "regex": "/regex" + }, + "route": { + "cluster": "regex.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + }, + { + "match": { + "prefix": "/", + "headers": [ + { + "name": "x-debug", + "presentMatch": true + } + ] + }, + "route": { + "cluster": "hdr-present.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + }, + { + "match": { + "prefix": "/", + "headers": [ + { + "name": "x-debug", + "presentMatch": true, + "invertMatch": true + } + ] + }, + "route": { + "cluster": "hdr-not-present.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + }, + { + "match": { + "prefix": "/", + "headers": [ + { + "name": "x-debug", + "exactMatch": "exact" + } + ] + }, + "route": { + "cluster": "hdr-exact.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + }, + { + "match": { + "prefix": "/", + "headers": [ + { + "name": "x-debug", + "prefixMatch": "prefix" + } + ] + }, + "route": { + "cluster": "hdr-prefix.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + }, + { + "match": { + "prefix": "/", + "headers": [ + { + "name": "x-debug", + "suffixMatch": "suffix" + } + ] + }, + "route": { + "cluster": "hdr-suffix.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + }, + { + "match": { + "prefix": "/", + "headers": [ + { + "name": "x-debug", + "regexMatch": "regex" + } + ] + }, + "route": { + "cluster": "hdr-regex.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + }, + { + "match": { + "prefix": "/", + "queryParameters": [ + { + "name": "secretparam", + "value": "exact", + "regex": false + } + ] + }, + "route": { + "cluster": "prm-exact.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + }, + { + "match": { + "prefix": "/", + "queryParameters": [ + { + "name": "secretparam", + "value": "regex", + "regex": true + } + ] + }, + "route": { + "cluster": "prm-regex.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + }, + { + "match": { + "prefix": "/" + }, + "route": { + "cluster": "nil-match.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + }, + { + "match": { + "prefix": "/" + }, + "route": { + "cluster": "empty-match-1.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + }, + { + "match": { + "prefix": "/" + }, + "route": { + "cluster": "empty-match-2.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + }, + { + "match": { + "prefix": "/prefix" + }, + "route": { + "cluster": "prefix-rewrite-1.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "prefixRewrite": "/" + } + }, + { + "match": { + "prefix": "/prefix" + }, + "route": { + "cluster": "prefix-rewrite-2.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "prefixRewrite": "/nested/newlocation" + } + }, + { + "match": { + "prefix": "/timeout" + }, + "route": { + "cluster": "req-timeout.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "timeout": "33s" + } + }, + { + "match": { + "prefix": "/retry-connect" + }, + "route": { + "cluster": "retry-connect.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "retryPolicy": { + "retryOn": "connect-failure", + "numRetries": 15 + } + } + }, + { + "match": { + "prefix": "/retry-codes" + }, + "route": { + "cluster": "retry-codes.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "retryPolicy": { + "retryOn": "retriable-status-codes", + "numRetries": 15, + "retriableStatusCodes": [ + 401, + 409, + 451 + ] + } + } + }, + { + "match": { + "prefix": "/retry-both" + }, + "route": { + "cluster": "retry-both.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "retryPolicy": { + "retryOn": ",retriable-status-codes", + "retriableStatusCodes": [ + 401, + 409, + 451 + ] + } + } + }, + { + "match": { + "prefix": "/split-3-ways" + }, + "route": { + "weightedClusters": { + "clusters": [ + { + "name": "big-side.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "weight": 9550 + }, + { + "name": "goldilocks-side.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "weight": 400 + }, + { + "name": "lil-bit-side.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "weight": 50 + } + ], + "totalWeight": 10000 + } + } + }, + { + "match": { + "prefix": "/" + }, + "route": { + "cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "validateClusters": true + } + ], + "typeUrl": "type.googleapis.com/envoy.api.v2.RouteConfiguration", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/routes/connect-proxy-with-chain-and-splitter.golden b/agent/xds/testdata/routes/connect-proxy-with-chain-and-splitter.golden new file mode 100644 index 000000000..efe364bad --- /dev/null +++ b/agent/xds/testdata/routes/connect-proxy-with-chain-and-splitter.golden @@ -0,0 +1,46 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration", + "name": "db", + "virtualHosts": [ + { + "name": "db", + "domains": [ + "*" + ], + "routes": [ + { + "match": { + "prefix": "/" + }, + "route": { + "weightedClusters": { + "clusters": [ + { + "name": "big-side.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "weight": 9550 + }, + { + "name": "goldilocks-side.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "weight": 400 + }, + { + "name": "lil-bit-side.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "weight": 50 + } + ], + "totalWeight": 10000 + } + } + } + ] + } + ], + "validateClusters": true + } + ], + "typeUrl": "type.googleapis.com/envoy.api.v2.RouteConfiguration", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/routes/connect-proxy-with-chain.golden b/agent/xds/testdata/routes/connect-proxy-with-chain.golden new file mode 100644 index 000000000..a0b6cb832 --- /dev/null +++ b/agent/xds/testdata/routes/connect-proxy-with-chain.golden @@ -0,0 +1,30 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration", + "name": "db", + "virtualHosts": [ + { + "name": "db", + "domains": [ + "*" + ], + "routes": [ + { + "match": { + "prefix": "/" + }, + "route": { + "cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "validateClusters": true + } + ], + "typeUrl": "type.googleapis.com/envoy.api.v2.RouteConfiguration", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/routes/defaults-no-chain.golden b/agent/xds/testdata/routes/defaults-no-chain.golden new file mode 100644 index 000000000..a065cc73d --- /dev/null +++ b/agent/xds/testdata/routes/defaults-no-chain.golden @@ -0,0 +1,7 @@ +{ + "versionInfo": "00000001", + "resources": [ + ], + "typeUrl": "type.googleapis.com/envoy.api.v2.RouteConfiguration", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/routes/splitter-with-resolver-redirect.golden b/agent/xds/testdata/routes/splitter-with-resolver-redirect.golden new file mode 100644 index 000000000..8c2f58a98 --- /dev/null +++ b/agent/xds/testdata/routes/splitter-with-resolver-redirect.golden @@ -0,0 +1,42 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration", + "name": "db", + "virtualHosts": [ + { + "name": "db", + "domains": [ + "*" + ], + "routes": [ + { + "match": { + "prefix": "/" + }, + "route": { + "weightedClusters": { + "clusters": [ + { + "name": "v1.db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "weight": 5000 + }, + { + "name": "v2.db.default.dc2.internal.11111111-2222-3333-4444-555555555555.consul", + "weight": 5000 + } + ], + "totalWeight": 10000 + } + } + } + ] + } + ], + "validateClusters": true + } + ], + "typeUrl": "type.googleapis.com/envoy.api.v2.RouteConfiguration", + "nonce": "00000001" +} \ No newline at end of file