diff --git a/.changelog/16274.txt b/.changelog/16274.txt deleted file mode 100644 index 983d33b195998..0000000000000 --- a/.changelog/16274.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:improvement -connect: Bump Envoy 1.22.5 to 1.22.7, 1.23.2 to 1.23.4, 1.24.0 to 1.24.2, add 1.25.1, remove 1.21.5 -``` diff --git a/.changelog/16292.txt b/.changelog/16292.txt deleted file mode 100644 index 085fe7fd07c27..0000000000000 --- a/.changelog/16292.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:feature -server: added server side RPC requests global read/write rate-limiter. -``` diff --git a/.circleci/config.yml b/.circleci/config.yml index 7dd57d1bbe994..dae806a625bdb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -23,10 +23,10 @@ references: BASH_ENV: .circleci/bash_env.sh GO_VERSION: 1.19.4 envoy-versions: &supported_envoy_versions - - &default_envoy_version "1.22.7" - - "1.23.4" - - "1.24.2" - - "1.25.1" + - &default_envoy_version "1.21.5" + - "1.22.5" + - "1.23.2" + - "1.24.0" nomad-versions: &supported_nomad_versions - &default_nomad_version "1.3.3" - "1.2.10" diff --git a/.github/workflows/nightly-test-1.15.x.yaml b/.github/workflows/nightly-test-1.11.x.yaml similarity index 98% rename from .github/workflows/nightly-test-1.15.x.yaml rename to .github/workflows/nightly-test-1.11.x.yaml index 18fe5466f0091..cd913d4eca492 100644 --- a/.github/workflows/nightly-test-1.15.x.yaml +++ b/.github/workflows/nightly-test-1.11.x.yaml @@ -1,4 +1,4 @@ -name: Nightly Test 1.15.x +name: Nightly Test 1.11.x on: schedule: - cron: '0 4 * * *' @@ -6,8 +6,8 @@ on: env: EMBER_PARTITION_TOTAL: 4 # Has to be changed in tandem with the matrix.partition - BRANCH: "release/1.15.x" - BRANCH_NAME: "release/1.15.x" # Used for naming artifacts + BRANCH: "release/1.11.x" + BRANCH_NAME: "release-1.11.x" # Used for naming artifacts jobs: frontend-test-workspace-node: diff --git a/agent/proxycfg/testing_api_gateway.go b/agent/proxycfg/testing_api_gateway.go deleted file mode 100644 index 8a9e113f76edc..0000000000000 --- a/agent/proxycfg/testing_api_gateway.go +++ /dev/null @@ -1,145 +0,0 @@ -package proxycfg - -import ( - "fmt" - "github.com/hashicorp/consul/agent/connect" - "github.com/hashicorp/consul/agent/consul/discoverychain" - "github.com/mitchellh/go-testing-interface" - - "github.com/hashicorp/consul/agent/structs" -) - -func TestConfigSnapshotAPIGateway( - t testing.T, - variation string, - nsFn func(ns *structs.NodeService), - configFn func(entry *structs.APIGatewayConfigEntry, boundEntry *structs.BoundAPIGatewayConfigEntry), - routes []structs.BoundRoute, - extraUpdates []UpdateEvent, - additionalEntries ...structs.ConfigEntry, -) *ConfigSnapshot { - roots, placeholderLeaf := TestCerts(t) - - entry := &structs.APIGatewayConfigEntry{ - Kind: structs.APIGateway, - Name: "api-gateway", - } - boundEntry := &structs.BoundAPIGatewayConfigEntry{ - Kind: structs.BoundAPIGateway, - Name: "api-gateway", - } - - if configFn != nil { - configFn(entry, boundEntry) - } - - baseEvents := []UpdateEvent{ - { - CorrelationID: rootsWatchID, - Result: roots, - }, - { - CorrelationID: leafWatchID, - Result: placeholderLeaf, - }, - { - CorrelationID: gatewayConfigWatchID, - Result: &structs.ConfigEntryResponse{ - Entry: entry, - }, - }, - { - CorrelationID: gatewayConfigWatchID, - Result: &structs.ConfigEntryResponse{ - Entry: boundEntry, - }, - }, - } - - for _, route := range routes { - // Add the watch event for the route. - watch := UpdateEvent{ - CorrelationID: routeConfigWatchID, - Result: &structs.ConfigEntryResponse{ - Entry: route, - }, - } - baseEvents = append(baseEvents, watch) - - // Add the watch event for the discovery chain. - entries := []structs.ConfigEntry{ - &structs.ProxyConfigEntry{ - Kind: structs.ProxyDefaults, - Name: structs.ProxyConfigGlobal, - Config: map[string]interface{}{ - "protocol": route.GetProtocol(), - }, - }, - &structs.ServiceResolverConfigEntry{ - Kind: structs.ServiceResolver, - Name: "api-gateway", - }, - } - - // Add a discovery chain watch event for each service. - for _, serviceName := range route.GetServiceNames() { - discoChain := UpdateEvent{ - CorrelationID: fmt.Sprintf("discovery-chain:%s", UpstreamIDString("", "", serviceName.Name, &serviceName.EnterpriseMeta, "")), - Result: &structs.DiscoveryChainResponse{ - Chain: discoverychain.TestCompileConfigEntries(t, serviceName.Name, "default", "default", "dc1", connect.TestClusterID+".consul", nil, entries...), - }, - } - baseEvents = append(baseEvents, discoChain) - } - } - - upstreams := structs.TestUpstreams(t) - - baseEvents = testSpliceEvents(baseEvents, setupTestVariationConfigEntriesAndSnapshot( - t, variation, upstreams, additionalEntries..., - )) - - return testConfigSnapshotFixture(t, &structs.NodeService{ - Kind: structs.ServiceKindAPIGateway, - Service: "api-gateway", - Address: "1.2.3.4", - Meta: nil, - TaggedAddresses: nil, - }, nsFn, nil, testSpliceEvents(baseEvents, extraUpdates)) -} - -// TestConfigSnapshotAPIGateway_NilConfigEntry is used to test when -// the update event for the config entry returns nil -// since this always happens on the first watch if it doesn't exist. -func TestConfigSnapshotAPIGateway_NilConfigEntry( - t testing.T, -) *ConfigSnapshot { - roots, _ := TestCerts(t) - - baseEvents := []UpdateEvent{ - { - CorrelationID: rootsWatchID, - Result: roots, - }, - { - CorrelationID: gatewayConfigWatchID, - Result: &structs.ConfigEntryResponse{ - Entry: nil, // The first watch on a config entry will return nil if the config entry doesn't exist. - }, - }, - { - CorrelationID: gatewayConfigWatchID, - Result: &structs.ConfigEntryResponse{ - Entry: nil, // The first watch on a config entry will return nil if the config entry doesn't exist. - }, - }, - } - - return testConfigSnapshotFixture(t, &structs.NodeService{ - Kind: structs.ServiceKindAPIGateway, - Service: "api-gateway", - Address: "1.2.3.4", - Meta: nil, - TaggedAddresses: nil, - }, nil, nil, testSpliceEvents(baseEvents, nil)) -} diff --git a/agent/xds/listeners_test.go b/agent/xds/listeners_test.go index e4e7c84575146..fffe3dc342dfd 100644 --- a/agent/xds/listeners_test.go +++ b/agent/xds/listeners_test.go @@ -527,211 +527,6 @@ func TestListenersFromSnapshot(t *testing.T) { }, nil) }, }, - { - name: "api-gateway", - create: func(t testinf.T) *proxycfg.ConfigSnapshot { - return proxycfg.TestConfigSnapshotAPIGateway(t, "default", nil, nil, nil, nil) - }, - }, - { - name: "api-gateway-nil-config-entry", - create: func(t testinf.T) *proxycfg.ConfigSnapshot { - return proxycfg.TestConfigSnapshotAPIGateway_NilConfigEntry(t) - }, - }, - { - name: "api-gateway-tcp-listener", - create: func(t testinf.T) *proxycfg.ConfigSnapshot { - return proxycfg.TestConfigSnapshotAPIGateway(t, "default", nil, func(entry *structs.APIGatewayConfigEntry, bound *structs.BoundAPIGatewayConfigEntry) { - entry.Listeners = []structs.APIGatewayListener{ - { - Name: "listener", - Protocol: structs.ListenerProtocolTCP, - Port: 8080, - }, - } - bound.Listeners = []structs.BoundAPIGatewayListener{ - { - Name: "listener", - }, - } - }, nil, nil) - }, - }, - { - name: "api-gateway-tcp-listener-with-tcp-route", - create: func(t testinf.T) *proxycfg.ConfigSnapshot { - return proxycfg.TestConfigSnapshotAPIGateway(t, "default", nil, func(entry *structs.APIGatewayConfigEntry, bound *structs.BoundAPIGatewayConfigEntry) { - entry.Listeners = []structs.APIGatewayListener{ - { - Name: "listener", - Protocol: structs.ListenerProtocolTCP, - Port: 8080, - }, - } - bound.Listeners = []structs.BoundAPIGatewayListener{ - { - Name: "listener", - Routes: []structs.ResourceReference{ - { - Name: "tcp-route", - Kind: structs.TCPRoute, - }, - }, - }, - } - - }, []structs.BoundRoute{ - &structs.TCPRouteConfigEntry{ - Name: "tcp-route", - Kind: structs.TCPRoute, - Parents: []structs.ResourceReference{ - { - Kind: structs.APIGateway, - Name: "api-gateway", - }, - }, - Services: []structs.TCPService{ - {Name: "tcp-service"}, - }, - }, - }, nil) - }, - }, - { - name: "api-gateway-http-listener", - create: func(t testinf.T) *proxycfg.ConfigSnapshot { - return proxycfg.TestConfigSnapshotAPIGateway(t, "default", nil, func(entry *structs.APIGatewayConfigEntry, bound *structs.BoundAPIGatewayConfigEntry) { - entry.Listeners = []structs.APIGatewayListener{ - { - Name: "listener", - Protocol: structs.ListenerProtocolHTTP, - Port: 8080, - }, - } - bound.Listeners = []structs.BoundAPIGatewayListener{ - { - Name: "listener", - Routes: []structs.ResourceReference{}, - }, - } - }, nil, nil) - }, - }, - { - name: "api-gateway-http-listener-with-http-route", - create: func(t testinf.T) *proxycfg.ConfigSnapshot { - return proxycfg.TestConfigSnapshotAPIGateway(t, "default", nil, func(entry *structs.APIGatewayConfigEntry, bound *structs.BoundAPIGatewayConfigEntry) { - entry.Listeners = []structs.APIGatewayListener{ - { - Name: "listener", - Protocol: structs.ListenerProtocolHTTP, - Port: 8080, - }, - } - bound.Listeners = []structs.BoundAPIGatewayListener{ - { - Name: "listener", - Routes: []structs.ResourceReference{ - { - Name: "http-route", - Kind: structs.HTTPRoute, - }, - }, - }, - } - }, []structs.BoundRoute{ - &structs.HTTPRouteConfigEntry{ - Name: "http-route", - Kind: structs.HTTPRoute, - Parents: []structs.ResourceReference{ - { - Kind: structs.APIGateway, - Name: "api-gateway", - }, - }, - Rules: []structs.HTTPRouteRule{ - { - Services: []structs.HTTPService{ - {Name: "http-service"}, - }, - }, - }, - }, - }, nil) - }, - }, - { - name: "api-gateway-tcp-listener-with-tcp-and-http-route", - create: func(t testinf.T) *proxycfg.ConfigSnapshot { - return proxycfg.TestConfigSnapshotAPIGateway(t, "default", nil, func(entry *structs.APIGatewayConfigEntry, bound *structs.BoundAPIGatewayConfigEntry) { - entry.Listeners = []structs.APIGatewayListener{ - { - Name: "listener-tcp", - Protocol: structs.ListenerProtocolTCP, - Port: 8080, - }, - { - Name: "listener-http", - Protocol: structs.ListenerProtocolHTTP, - Port: 8081, - }, - } - bound.Listeners = []structs.BoundAPIGatewayListener{ - { - Name: "listener-tcp", - Routes: []structs.ResourceReference{ - { - Name: "tcp-route", - Kind: structs.TCPRoute, - }, - }, - }, - { - Name: "listener-http", - Routes: []structs.ResourceReference{ - { - Name: "http-route", - Kind: structs.HTTPRoute, - }, - }, - }, - } - - }, []structs.BoundRoute{ - &structs.TCPRouteConfigEntry{ - Name: "tcp-route", - Kind: structs.TCPRoute, - Parents: []structs.ResourceReference{ - { - Kind: structs.APIGateway, - Name: "api-gateway", - }, - }, - Services: []structs.TCPService{ - {Name: "tcp-service"}, - }, - }, - &structs.HTTPRouteConfigEntry{ - Name: "http-route", - Kind: structs.HTTPRoute, - Parents: []structs.ResourceReference{ - { - Kind: structs.APIGateway, - Name: "api-gateway", - }, - }, - Rules: []structs.HTTPRouteRule{ - { - Services: []structs.HTTPService{ - {Name: "http-service"}, - }, - }, - }, - }, - }, nil) - }, - }, { name: "ingress-gateway", create: func(t testinf.T) *proxycfg.ConfigSnapshot { diff --git a/agent/xds/testdata/listeners/api-gateway-http-listener-with-http-route.latest.golden b/agent/xds/testdata/listeners/api-gateway-http-listener-with-http-route.latest.golden deleted file mode 100644 index 0bf31dc66240b..0000000000000 --- a/agent/xds/testdata/listeners/api-gateway-http-listener-with-http-route.latest.golden +++ /dev/null @@ -1,49 +0,0 @@ -{ - "versionInfo": "00000001", - "resources": [ - { - "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", - "name": "http:1.2.3.4:8080", - "address": { - "socketAddress": { - "address": "1.2.3.4", - "portValue": 8080 - } - }, - "filterChains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "statPrefix": "ingress_upstream_8080", - "rds": { - "configSource": { - "ads": {}, - "resourceApiVersion": "V3" - }, - "routeConfigName": "8080" - }, - "httpFilters": [ - { - "name": "envoy.filters.http.router", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - } - ], - "tracing": { - "randomSampling": {} - } - } - } - ] - } - ], - "trafficDirection": "OUTBOUND" - } - ], - "typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener", - "nonce": "00000001" -} \ No newline at end of file diff --git a/agent/xds/testdata/listeners/api-gateway-http-listener.latest.golden b/agent/xds/testdata/listeners/api-gateway-http-listener.latest.golden deleted file mode 100644 index 53b67bb37300e..0000000000000 --- a/agent/xds/testdata/listeners/api-gateway-http-listener.latest.golden +++ /dev/null @@ -1,5 +0,0 @@ -{ - "versionInfo": "00000001", - "typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener", - "nonce": "00000001" -} \ No newline at end of file diff --git a/agent/xds/testdata/listeners/api-gateway-nil-config-entry.latest.golden b/agent/xds/testdata/listeners/api-gateway-nil-config-entry.latest.golden deleted file mode 100644 index 53b67bb37300e..0000000000000 --- a/agent/xds/testdata/listeners/api-gateway-nil-config-entry.latest.golden +++ /dev/null @@ -1,5 +0,0 @@ -{ - "versionInfo": "00000001", - "typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener", - "nonce": "00000001" -} \ No newline at end of file diff --git a/agent/xds/testdata/listeners/api-gateway-tcp-listener-with-tcp-and-http-route.latest.golden b/agent/xds/testdata/listeners/api-gateway-tcp-listener-with-tcp-and-http-route.latest.golden deleted file mode 100644 index 8da750a592962..0000000000000 --- a/agent/xds/testdata/listeners/api-gateway-tcp-listener-with-tcp-and-http-route.latest.golden +++ /dev/null @@ -1,74 +0,0 @@ -{ - "versionInfo": "00000001", - "resources": [ - { - "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", - "name": "http:1.2.3.4:8081", - "address": { - "socketAddress": { - "address": "1.2.3.4", - "portValue": 8081 - } - }, - "filterChains": [ - { - "filters": [ - { - "name": "envoy.filters.network.http_connection_manager", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", - "statPrefix": "ingress_upstream_8081", - "rds": { - "configSource": { - "ads": {}, - "resourceApiVersion": "V3" - }, - "routeConfigName": "8081" - }, - "httpFilters": [ - { - "name": "envoy.filters.http.router", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" - } - } - ], - "tracing": { - "randomSampling": {} - } - } - } - ] - } - ], - "trafficDirection": "OUTBOUND" - }, - { - "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", - "name": "tcp-service:1.2.3.4:8080", - "address": { - "socketAddress": { - "address": "1.2.3.4", - "portValue": 8080 - } - }, - "filterChains": [ - { - "filters": [ - { - "name": "envoy.filters.network.tcp_proxy", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", - "statPrefix": "upstream.tcp-service.default.default.dc1", - "cluster": "tcp-service.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" - } - } - ] - } - ], - "trafficDirection": "OUTBOUND" - } - ], - "typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener", - "nonce": "00000001" -} \ No newline at end of file diff --git a/agent/xds/testdata/listeners/api-gateway-tcp-listener-with-tcp-route.latest.golden b/agent/xds/testdata/listeners/api-gateway-tcp-listener-with-tcp-route.latest.golden deleted file mode 100644 index 12f6c29726d5e..0000000000000 --- a/agent/xds/testdata/listeners/api-gateway-tcp-listener-with-tcp-route.latest.golden +++ /dev/null @@ -1,32 +0,0 @@ -{ - "versionInfo": "00000001", - "resources": [ - { - "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", - "name": "tcp-service:1.2.3.4:8080", - "address": { - "socketAddress": { - "address": "1.2.3.4", - "portValue": 8080 - } - }, - "filterChains": [ - { - "filters": [ - { - "name": "envoy.filters.network.tcp_proxy", - "typedConfig": { - "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", - "statPrefix": "upstream.tcp-service.default.default.dc1", - "cluster": "tcp-service.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" - } - } - ] - } - ], - "trafficDirection": "OUTBOUND" - } - ], - "typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener", - "nonce": "00000001" -} \ No newline at end of file diff --git a/agent/xds/testdata/listeners/api-gateway-tcp-listener.latest.golden b/agent/xds/testdata/listeners/api-gateway-tcp-listener.latest.golden deleted file mode 100644 index 53b67bb37300e..0000000000000 --- a/agent/xds/testdata/listeners/api-gateway-tcp-listener.latest.golden +++ /dev/null @@ -1,5 +0,0 @@ -{ - "versionInfo": "00000001", - "typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener", - "nonce": "00000001" -} \ No newline at end of file diff --git a/agent/xds/testdata/listeners/api-gateway-tcp-listeners.latest.golden b/agent/xds/testdata/listeners/api-gateway-tcp-listeners.latest.golden deleted file mode 100644 index d2d839adf9567..0000000000000 --- a/agent/xds/testdata/listeners/api-gateway-tcp-listeners.latest.golden +++ /dev/null @@ -1,5 +0,0 @@ -{ - "versionInfo": "00000001", - "typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener", - "nonce": "00000001" -} \ No newline at end of file diff --git a/agent/xds/testdata/listeners/api-gateway.latest.golden b/agent/xds/testdata/listeners/api-gateway.latest.golden deleted file mode 100644 index d2d839adf9567..0000000000000 --- a/agent/xds/testdata/listeners/api-gateway.latest.golden +++ /dev/null @@ -1,5 +0,0 @@ -{ - "versionInfo": "00000001", - "typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener", - "nonce": "00000001" -} \ No newline at end of file diff --git a/envoyextensions/xdscommon/envoy_versioning_test.go b/envoyextensions/xdscommon/envoy_versioning_test.go index e20a2ca8ceefa..833e3014ebeec 100644 --- a/envoyextensions/xdscommon/envoy_versioning_test.go +++ b/envoyextensions/xdscommon/envoy_versioning_test.go @@ -121,7 +121,6 @@ func TestDetermineSupportedProxyFeaturesFromString(t *testing.T) { "1.18.6": {expectErr: "Envoy 1.18.6 " + errTooOld}, "1.19.5": {expectErr: "Envoy 1.19.5 " + errTooOld}, "1.20.7": {expectErr: "Envoy 1.20.7 " + errTooOld}, - "1.21.5": {expectErr: "Envoy 1.21.5 " + errTooOld}, } // Insert a bunch of valid versions. @@ -136,10 +135,10 @@ func TestDetermineSupportedProxyFeaturesFromString(t *testing.T) { } */ for _, v := range []string{ + "1.21.0", "1.21.1", "1.21.2", "1.21.3", "1.21.4", "1.21.5", "1.22.0", "1.22.1", "1.22.2", "1.22.3", "1.22.4", "1.22.5", - "1.23.0", "1.23.1", "1.23.2", "1.23.3", "1.23.4", - "1.24.0", "1.24.1", "1.24.2", - "1.25.0", "1.25.1", + "1.23.0", "1.23.1", "1.23.2", + "1.24.0", } { cases[v] = testcase{expect: SupportedProxyFeatures{}} } diff --git a/envoyextensions/xdscommon/proxysupport.go b/envoyextensions/xdscommon/proxysupport.go index bedc0608bfd39..963e0dba0c2aa 100644 --- a/envoyextensions/xdscommon/proxysupport.go +++ b/envoyextensions/xdscommon/proxysupport.go @@ -9,10 +9,10 @@ import "strings" // // see: https://www.consul.io/docs/connect/proxies/envoy#supported-versions var EnvoyVersions = []string{ - "1.25.1", - "1.24.2", - "1.23.4", + "1.24.0", + "1.23.2", "1.22.5", + "1.21.5", } // UnsupportedEnvoyVersions lists any unsupported Envoy versions (mainly minor versions) that fall diff --git a/test/integration/connect/envoy/case-api-gateway-tcp-conflicted/setup.sh b/test/integration/connect/envoy/case-api-gateway-tcp-conflicted/setup.sh index bb9baacbb0f91..2394ab23621ed 100644 --- a/test/integration/connect/envoy/case-api-gateway-tcp-conflicted/setup.sh +++ b/test/integration/connect/envoy/case-api-gateway-tcp-conflicted/setup.sh @@ -38,7 +38,6 @@ services = [ ] parents = [ { - kind = "api-gateway" name = "api-gateway" } ] @@ -48,4 +47,4 @@ register_services primary gen_envoy_bootstrap api-gateway 20000 primary true gen_envoy_bootstrap s1 19000 -gen_envoy_bootstrap s2 19001 +gen_envoy_bootstrap s2 19001 \ No newline at end of file diff --git a/test/integration/connect/envoy/case-api-gateway-tcp-simple/setup.sh b/test/integration/connect/envoy/case-api-gateway-tcp-simple/setup.sh index 56a86166d4c04..fd4f474abfdcc 100644 --- a/test/integration/connect/envoy/case-api-gateway-tcp-simple/setup.sh +++ b/test/integration/connect/envoy/case-api-gateway-tcp-simple/setup.sh @@ -47,7 +47,6 @@ parents = [ { name = "api-gateway" sectionName = "listener-two" - kind = "api-gateway" } ] ' @@ -74,4 +73,4 @@ register_services primary gen_envoy_bootstrap api-gateway 20000 primary true gen_envoy_bootstrap s1 19000 -gen_envoy_bootstrap s2 19001 +gen_envoy_bootstrap s2 19001 \ No newline at end of file diff --git a/test/integration/connect/envoy/case-api-gateway-tcp-simple/verify.bats b/test/integration/connect/envoy/case-api-gateway-tcp-simple/verify.bats index e96f473be4f4a..51ed646bd6bca 100644 --- a/test/integration/connect/envoy/case-api-gateway-tcp-simple/verify.bats +++ b/test/integration/connect/envoy/case-api-gateway-tcp-simple/verify.bats @@ -29,4 +29,4 @@ load helpers @test "api gateway should get an intentions error connecting to s2 via configured port" { run retry_default must_fail_tcp_connection localhost:9998 -} +} \ No newline at end of file diff --git a/test/integration/consul-container/libs/assert/envoy.go b/test/integration/consul-container/libs/assert/envoy.go index 6713c4fb6490d..e62118c4f1d8a 100644 --- a/test/integration/consul-container/libs/assert/envoy.go +++ b/test/integration/consul-container/libs/assert/envoy.go @@ -127,7 +127,7 @@ func AssertEnvoyMetricAtLeast(t *testing.T, adminPort int, prefix, metric string err error ) failer := func() *retry.Timer { - return &retry.Timer{Timeout: 60 * time.Second, Wait: 500 * time.Millisecond} + return &retry.Timer{Timeout: 30 * time.Second, Wait: 500 * time.Millisecond} } retry.RunWith(failer(), t, func(r *retry.R) { diff --git a/test/integration/consul-container/libs/service/connect.go b/test/integration/consul-container/libs/service/connect.go index b5a8087d2d67f..ac4907d4e583e 100644 --- a/test/integration/consul-container/libs/service/connect.go +++ b/test/integration/consul-container/libs/service/connect.go @@ -109,13 +109,6 @@ func (g ConnectContainer) Start() error { return g.container.Start(g.ctx) } -func (g ConnectContainer) Stop() error { - if g.container == nil { - return fmt.Errorf("container has not been initialized") - } - return g.container.Stop(context.Background(), nil) -} - func (g ConnectContainer) Terminate() error { return cluster.TerminateContainer(g.ctx, g.container, true) } diff --git a/test/integration/consul-container/libs/service/examples.go b/test/integration/consul-container/libs/service/examples.go index da075f5aec9c9..9d95f6e9099f7 100644 --- a/test/integration/consul-container/libs/service/examples.go +++ b/test/integration/consul-container/libs/service/examples.go @@ -101,13 +101,6 @@ func (g exampleContainer) Start() error { return g.container.Start(context.Background()) } -func (g exampleContainer) Stop() error { - if g.container == nil { - return fmt.Errorf("container has not been initialized") - } - return g.container.Stop(context.Background(), nil) -} - func (c exampleContainer) Terminate() error { return cluster.TerminateContainer(c.ctx, c.container, true) } diff --git a/test/integration/consul-container/libs/service/gateway.go b/test/integration/consul-container/libs/service/gateway.go index 70897fc7b0992..5fb3a36184b4a 100644 --- a/test/integration/consul-container/libs/service/gateway.go +++ b/test/integration/consul-container/libs/service/gateway.go @@ -86,13 +86,6 @@ func (g gatewayContainer) Start() error { return g.container.Start(context.Background()) } -func (g gatewayContainer) Stop() error { - if g.container == nil { - return fmt.Errorf("container has not been initialized") - } - return g.container.Stop(context.Background(), nil) -} - func (c gatewayContainer) Terminate() error { return cluster.TerminateContainer(c.ctx, c.container, true) } diff --git a/test/integration/consul-container/libs/service/service.go b/test/integration/consul-container/libs/service/service.go index 99da558226904..57a3539a64123 100644 --- a/test/integration/consul-container/libs/service/service.go +++ b/test/integration/consul-container/libs/service/service.go @@ -18,7 +18,6 @@ type Service interface { GetName() string GetServiceName() string Start() (err error) - Stop() (err error) Terminate() error Restart() error GetStatus() (string, error) diff --git a/test/integration/consul-container/libs/topology/peering_topology.go b/test/integration/consul-container/libs/topology/peering_topology.go index ba36978c72f43..1c764c45c53cd 100644 --- a/test/integration/consul-container/libs/topology/peering_topology.go +++ b/test/integration/consul-container/libs/topology/peering_topology.go @@ -41,7 +41,6 @@ type BuiltCluster struct { func BasicPeeringTwoClustersSetup( t *testing.T, consulVersion string, - peeringThroughMeshgateway bool, ) (*BuiltCluster, *BuiltCluster) { // acceptingCluster, acceptingCtx, acceptingClient := NewPeeringCluster(t, "dc1", 3, consulVersion, true) acceptingCluster, acceptingCtx, acceptingClient := NewPeeringCluster(t, 3, &libcluster.BuildOptions{ @@ -54,38 +53,6 @@ func BasicPeeringTwoClustersSetup( ConsulVersion: consulVersion, InjectAutoEncryption: true, }) - - // Create the mesh gateway for dataplane traffic and peering control plane traffic (if enabled) - acceptingClusterGateway, err := libservice.NewGatewayService(context.Background(), "mesh", "mesh", acceptingCluster.Clients()[0]) - require.NoError(t, err) - dialingClusterGateway, err := libservice.NewGatewayService(context.Background(), "mesh", "mesh", dialingCluster.Clients()[0]) - require.NoError(t, err) - - // Enable peering control plane traffic through mesh gateway - if peeringThroughMeshgateway { - req := &api.MeshConfigEntry{ - Peering: &api.PeeringMeshConfig{ - PeerThroughMeshGateways: true, - }, - } - configCluster := func(cli *api.Client) error { - libassert.CatalogServiceExists(t, cli, "mesh") - ok, _, err := cli.ConfigEntries().Set(req, &api.WriteOptions{}) - if !ok { - return fmt.Errorf("config entry is not set") - } - - if err != nil { - return fmt.Errorf("error writing config entry: %s", err) - } - return nil - } - err = configCluster(dialingClient) - require.NoError(t, err) - err = configCluster(acceptingClient) - require.NoError(t, err) - } - require.NoError(t, dialingCluster.PeerWithCluster(acceptingClient, AcceptingPeerName, DialingPeerName)) libassert.PeeringStatus(t, acceptingClient, AcceptingPeerName, api.PeeringStateActive) @@ -93,6 +60,7 @@ func BasicPeeringTwoClustersSetup( // Register an static-server service in acceptingCluster and export to dialing cluster var serverService, serverSidecarService libservice.Service + var acceptingClusterGateway libservice.Service { clientNode := acceptingCluster.Clients()[0] @@ -113,10 +81,15 @@ func BasicPeeringTwoClustersSetup( libassert.CatalogServiceExists(t, acceptingClient, "static-server-sidecar-proxy") require.NoError(t, serverService.Export("default", AcceptingPeerName, acceptingClient)) + + // Create the mesh gateway for dataplane traffic + acceptingClusterGateway, err = libservice.NewGatewayService(context.Background(), "mesh", "mesh", clientNode) + require.NoError(t, err) } // Register an static-client service in dialing cluster and set upstream to static-server service var clientSidecarService *libservice.ConnectContainer + var dialingClusterGateway libservice.Service { clientNode := dialingCluster.Clients()[0] @@ -127,6 +100,9 @@ func BasicPeeringTwoClustersSetup( libassert.CatalogServiceExists(t, dialingClient, "static-client-sidecar-proxy") + // Create the mesh gateway for dataplane traffic + dialingClusterGateway, err = libservice.NewGatewayService(context.Background(), "mesh", "mesh", clientNode) + require.NoError(t, err) } _, adminPort := clientSidecarService.GetAdminAddr() diff --git a/test/integration/consul-container/test/peering/rotate_server_and_ca_then_fail_test.go b/test/integration/consul-container/test/peering/rotate_server_and_ca_then_fail_test.go index bbac9cc034019..223effa449b26 100644 --- a/test/integration/consul-container/test/peering/rotate_server_and_ca_then_fail_test.go +++ b/test/integration/consul-container/test/peering/rotate_server_and_ca_then_fail_test.go @@ -50,7 +50,7 @@ import ( func TestPeering_RotateServerAndCAThenFail_(t *testing.T) { t.Parallel() - accepting, dialing := libtopology.BasicPeeringTwoClustersSetup(t, utils.TargetVersion, false) + accepting, dialing := libtopology.BasicPeeringTwoClustersSetup(t, utils.TargetVersion) var ( acceptingCluster = accepting.Cluster dialingCluster = dialing.Cluster diff --git a/test/integration/consul-container/test/troubleshoot/troubleshoot_test.go b/test/integration/consul-container/test/troubleshoot/troubleshoot_upstream_test.go similarity index 100% rename from test/integration/consul-container/test/troubleshoot/troubleshoot_test.go rename to test/integration/consul-container/test/troubleshoot/troubleshoot_upstream_test.go diff --git a/test/integration/consul-container/test/upgrade/peering_control_plane_mgw_test.go b/test/integration/consul-container/test/upgrade/peering_control_plane_mgw_test.go index 5ccba95677391..f4112b6f6b83e 100644 --- a/test/integration/consul-container/test/upgrade/peering_control_plane_mgw_test.go +++ b/test/integration/consul-container/test/upgrade/peering_control_plane_mgw_test.go @@ -42,7 +42,7 @@ func TestPeering_Upgrade_ControlPlane_MGW(t *testing.T) { } run := func(t *testing.T, tc testcase) { - accepting, dialing := libtopology.BasicPeeringTwoClustersSetup(t, tc.oldversion, true) + accepting, dialing := libtopology.BasicPeeringTwoClustersSetup(t, tc.oldversion) var ( acceptingCluster = accepting.Cluster dialingCluster = dialing.Cluster @@ -54,6 +54,19 @@ func TestPeering_Upgrade_ControlPlane_MGW(t *testing.T) { acceptingClient, err := acceptingCluster.GetClient(nil, false) require.NoError(t, err) + // Enable peering control plane traffic through mesh gateway + req := &api.MeshConfigEntry{ + Peering: &api.PeeringMeshConfig{ + PeerThroughMeshGateways: true, + }, + } + ok, _, err := dialingClient.ConfigEntries().Set(req, &api.WriteOptions{}) + require.True(t, ok) + require.NoError(t, err) + ok, _, err = acceptingClient.ConfigEntries().Set(req, &api.WriteOptions{}) + require.True(t, ok) + require.NoError(t, err) + // Verify control plane endpoints and traffic in gateway _, gatewayAdminPort := dialing.Gateway.GetAdminAddr() libassert.AssertUpstreamEndpointStatus(t, gatewayAdminPort, "server.dc1.peering", "HEALTHY", 1) @@ -61,9 +74,6 @@ func TestPeering_Upgrade_ControlPlane_MGW(t *testing.T) { libassert.AssertEnvoyMetricAtLeast(t, gatewayAdminPort, "cluster.static-server.default.default.accepting-to-dialer.external", "upstream_cx_total", 1) - libassert.AssertEnvoyMetricAtLeast(t, gatewayAdminPort, - "cluster.server.dc1.peering", - "upstream_cx_total", 1) // Upgrade the accepting cluster and assert peering is still ACTIVE require.NoError(t, acceptingCluster.StandardUpgrade(t, context.Background(), tc.targetVersion)) @@ -80,12 +90,11 @@ func TestPeering_Upgrade_ControlPlane_MGW(t *testing.T) { // - Register a new static-client service in dialing cluster and // - set upstream to static-server service in peered cluster - // Stop the accepting gateway and restart dialing gateway - // to force peering control plane traffic through dialing mesh gateway - require.NoError(t, accepting.Gateway.Stop()) + // Restart the gateway & proxy sidecar require.NoError(t, dialing.Gateway.Restart()) + require.NoError(t, dialing.Container.Restart()) - // Restarted dialing gateway should not have any measurement on data plane traffic + // Restarted gateway should not have any measurement on data plane traffic libassert.AssertEnvoyMetricAtMost(t, gatewayAdminPort, "cluster.static-server.default.default.accepting-to-dialer.external", "upstream_cx_total", 0) @@ -93,7 +102,6 @@ func TestPeering_Upgrade_ControlPlane_MGW(t *testing.T) { libassert.AssertEnvoyMetricAtLeast(t, gatewayAdminPort, "cluster.server.dc1.peering", "upstream_cx_total", 1) - require.NoError(t, accepting.Gateway.Start()) clientSidecarService, err := libservice.CreateAndRegisterStaticClientSidecar(dialingCluster.Servers()[0], libtopology.DialingPeerName, true) require.NoError(t, err) diff --git a/test/integration/consul-container/test/upgrade/peering_http_test.go b/test/integration/consul-container/test/upgrade/peering_http_test.go index fe91f76530988..aec03a3edb41c 100644 --- a/test/integration/consul-container/test/upgrade/peering_http_test.go +++ b/test/integration/consul-container/test/upgrade/peering_http_test.go @@ -99,7 +99,7 @@ func TestPeering_UpgradeToTarget_fromLatest(t *testing.T) { } run := func(t *testing.T, tc testcase) { - accepting, dialing := libtopology.BasicPeeringTwoClustersSetup(t, tc.oldversion, false) + accepting, dialing := libtopology.BasicPeeringTwoClustersSetup(t, tc.oldversion) var ( acceptingCluster = accepting.Cluster dialingCluster = dialing.Cluster diff --git a/version/VERSION b/version/VERSION index 1f0d2f335194a..0dec25d15b37f 100644 --- a/version/VERSION +++ b/version/VERSION @@ -1 +1 @@ -1.16.0-dev +1.15.0-dev \ No newline at end of file diff --git a/website/content/docs/connect/config-entries/service-splitter.mdx b/website/content/docs/connect/config-entries/service-splitter.mdx index 34ea9597e2185..4386fba281bcd 100644 --- a/website/content/docs/connect/config-entries/service-splitter.mdx +++ b/website/content/docs/connect/config-entries/service-splitter.mdx @@ -1,575 +1,54 @@ ---- +--- layout: docs -page_title: Service Splitter Configuration Entry Reference -description: >- - Service splitter configuration entries are L7 traffic management tools for redirecting requests for a service to - multiple instances. Learn how to write `service-splitter` config entries in HCL or YAML with a specification - reference, configuration model, a complete example, and example code by use case. +page_title: Service Splitter - Configuration Entry Reference +description: >- + The service splitter configuration entry kind defines how to divide service mesh traffic between service instances. Use the reference guide to learn about `""service-splitter""` config entry parameters and how it can be used for traffic management behaviors like canary rollouts, blue green deployment, and load balancing across environments. --- -# Service Splitter Configuration Reference - -This reference page describes the structure and contents of service splitter configuration entries. Configure and apply service splitters to redirect a percentage of incoming traffic requests for a service to one or more specific service instances. - -## Configuration model - -The following list outlines field hierarchy, language-specific data types, and requirements in a service splitter configuration entry. Click on a property name to view additional details, including default values. - - - - - -- [`Kind`](#kind): string | required -- [`Name`](#name): string | required -- [`Namespace`](#namespace): string -- [`Partition`](#partition): string -- [`Meta`](#meta): map -- [`Splits`](#splits): map | required - - [`Weight`](#splits-weight): number | required - - [`Service`](#splits-service): string | required - - [`ServiceSubset`](#splits-servicesubset): string - - [`Namespace`](#splits-namespace): string - - [`Partition`](#splits-partition): string - - [`RequestHeaders`](#splits-requestheaders): map - - [`Add`](#splits-requestheaders): map - - [`Set`](#splits-requestheaders): map - - [`Remove`](#splits-requestheaders): map - - [`ResponseHeaders`](#splits-responseheaders): map - - [`Add`](#splits-responseheaders): map - - [`Set`](#splits-responseheaders): map - - [`Remove`](#splits-responseheaders): map - - - - - -- [`apiVersion`](#apiversion): string | required -- [`kind`](#kind): string | required -- [`metadata`](#metadata): object | required - - [`name`](#metadata-name): string | required - - [`namespace`](#metadata-namespace): string | optional -- [`spec`](#spec): object | required - - [`splits`](#spec-splits): list | required - - [`weight`](#spec-splits-weight): float32 | required - - [`service`](#spec-splits-service): string | required - - [`serviceSubset`](#spec-splits-servicesubset): string - - [`namespace`](#spec-splits-namespace): string - - [`partition`](#spec-splits-partition): string - - [`requestHeaders`](#spec-splits-requestheaders): HTTPHeaderModifiers - - [`add`](#spec-splits-requestheaders): map - - [`set`](#spec-splits-requestheaders): map - - [`remove`](#spec-splits-requestheaders): map - - [`responseHeaders`](#spec-splits-responseheaders): HTTPHeaderModifiers - - [`add`](#spec-splits-responseheaders): map - - [`set`](#spec-splits-responseheaders): map - - [`remove`](#spec-splits-responseheaders): map - - - - -## Complete configuration - -When every field is defined, a service splitter configuration entry has the following form: - - - - - -```hcl -Kind = "service-splitter" ## string | required -Name = "config-entry-name" ## string | required -Namespace = "main" ## string -Partition = "partition" ## string -Meta = { ## map - key = "value" -} -Splits = [ ## list | required - { ## map - Weight = 90 ## number | required - Service = "service" ## string - ServiceSubset = "v1" ## string - Namespace = "target-namespace" ## string - Partition = "target-partition" ## string - RequestHeaders = { ## map - Set = { - "X-Web-Version" : "from-v1" - } - } - ResponseHeaders = { ## map - Set = { - "X-Web-Version" : "to-v1" - } - } - }, - { - Weight = 10 - Service = "service" - ServiceSubset = "v2" - Namespace = "target-namespace" - Partition = "target-partition" - RequestHeaders = { - Set = { - "X-Web-Version" : "from-v2" - } - } - ResponseHeaders = { - Set = { - "X-Web-Version" : "to-v2" - } - } - } -] -``` - - - - - -```json -{ - "Kind" : "service-splitter", ## string | required - "Name" : "config-entry-name", ## string | required - "Namespace" : "main", ## string - "Partition" : "partition", ## string - "Meta" : { ## map - "_key_" : "_value_" - }, - "Splits" : [ ## list | required - { ## map - "Weight" : 90, ## number | required - "Service" : "service", ## string - "ServiceSubset" : "v1", ## string - "Namespace" : "target-namespace", ## string - "Partition" : "target-partition", ## string - "RequestHeaders" : { ## map - "Set" : { - "X-Web-Version": "from-v1" - } - }, - "ResponseHeaders" : { ## map - "Set" : { - "X-Web-Version": "to-v1" - } - } - }, - { - "Weight" : 10, - "Service" : "service", - "ServiceSubset" : "v2", - "Namespace" : "target-namespace", - "Partition" : "target-partition", - "RequestHeaders" : { - "Set" : { - "X-Web-Version": "from-v2" - } - }, - "ResponseHeaders" : { - "Set" : { - "X-Web-Version": "to-v2" - } - } - } - ] -} -``` - - - - - -```yaml -apiVersion: consul.hashicorp.com/v1alpha1 # string | required -kind: ServiceSplitter # string | required -metadata: # object | required - name: config-entry-name # string | required - namespace: main # string -spec: - splits: # list - - weight: 90 # floating point | required - service: service # string - serviceSubset: v1 # string - namespace: target-namespace # string - partition: target-partition # string - requestHeaders: - set: - x-web-version: from-v1 # string - responseHeaders: - set: - x-web-version: to-v1 # string - - weight: 10 - service: service - serviceSubset: v2 - namespace: target-namespace - partition: target-partition - requestHeaders: - set: - x-web-version: from-v2 - responseHeaders: - set: - x-web-version: to-v2 -``` - - - - - -## Specification - -This section provides details about the fields you can configure in the service splitter configuration entry. - - - - - -### `Kind` - -Specifies the type of configuration entry to implement. - -#### Values - -- Default: none -- This field is required. -- Data type: String value that must be set to `service-splitter`. - -### `Name` - -Specifies a name for the configuration entry. The name is metadata that you can use to reference the configuration entry when performing Consul operations, such as applying a configuration entry to a specific cluster. - -#### Values - -- Default: Defaults to the name of the node after writing the entry to the Consul server. -- This field is required. -- Data type: String - - -### `Namespace` - -Specifies the [namespace](/consul/docs/enterprise/namespaces) to apply the configuration entry. - -#### Values - -- Default: None -- Data type: String - -### `Partition` - -Specifies the [admin partition](/consul/docs/enterprise/admin-partitions) to apply the configuration entry. - -#### Values - -- Default: `Default` -- Data type: String - -### `Meta` - -Specifies key-value pairs to add to the KV store. - -#### Values - -- Default: none -- Data type: Map of one or more key-value pairs - - keys: String - - values: String, integer, or float - -### `Splits` - -Defines how much traffic to send to sets of service instances during a traffic split. - -#### Values - -- Default: None -- This field is required. -- Data type: list of objects that can contain the following fields: - - `Weight`: The sum of weights for a set of service instances must add up to 100. - - `Service`: This field is required. - - `ServiceSubset` - - `Namespace` - - `Partition` - - `RequestHeaders` - - `ResponseHeaders` - -### `Splits[].Weight` - -Specifies the percentage of traffic sent to the set of service instances specified in the [`Service`](#service) field. Each weight must be a floating integer between `0` and `100`. The smallest representable value is `.01`. The sum of weights across all splits must add up to `100`. - -#### Values - -- Default: `null` -- This field is required. -- Data type: Floating number from `.01` to `100`. - -### `Splits[].Service` - -Specifies the name of the service to resolve. - -#### Values - -- Default: Inherits the value of the [`Name`](#name) field. -- Data type: String - -### `Splits[].ServiceSubset` - -Specifies a subset of the service to resolve. A service subset assigns a name to a specific subset of discoverable service instances within a datacenter, such as `version2` or `canary`. All services have an unnamed default subset that returns all healthy instances. - -You can define service subsets in a [service resolver configuration entry](/consul/docs/connect/config-entries/service-resolver), which are referenced by their names throughout the other configuration entries. This field overrides the default subset value in the service resolver configuration entry. - -#### Values - -- Default: If empty, the `split` uses the default subset. -- Data type: String - -### `Splits[].Namespace` - -Specifies the [namespace](/consul/docs/enterprise/namespaces) to use in the FQDN when resolving the service. - -#### Values - -- Default: Inherits the value of [`Namespace`](#Namespace) from the top-level of the configuration entry. -- Data type: String - -### `Splits[].Partition` - -Specifies the [admin partition](/consul/docs/enterprise/admin-partitions) to use in the FQDN when resolving the service. - -#### Values - -- Default: By default, the `service-splitter` uses the [admin partition specified in the top-level configuration entry](#partition). -- Data type: String - -### `Splits[].RequestHeaders` - -Specifies a set of HTTP-specific header modification rules applied to requests routed with the service split. You cannot configure request headers if the listener protocol is set to `tcp`. Refer to [Set HTTP Headers](#set-http-headers) for an example configuration. - -#### Values - -- Default: None -- Values: Object containing one or more fields that define header modification rules - - `Add`: Map of one or more key-value pairs - - `Set`: Map of one or more key-value pairs - - `Remove`: Map of one or more key-value pairs - -The following table describes how to configure values for request headers: - -| Rule | Description | Type | -| --- | --- | --- | -| `Add` | Defines a set of key-value pairs to add to the header. Use header names as the keys. Header names are not case-sensitive. If header values with the same name already exist, the value is appended and Consul applies both headers. You can [use variable placeholders](#use-variable-placeholders). | map of strings | -| `Set` | Defines a set of key-value pairs to add to the request header or to replace existing header values with. Use header names as the keys. Header names are not case-sensitive. If header values with the same names already exist, Consul replaces the header values. You can [use variable placeholders](#use-variable-placeholders). | map of strings | -| `Remove` | Defines an list of headers to remove. Consul removes only headers containing exact matches. Header names are not case-sensitive. | list of strings | - -#### Use variable placeholders - -For `Add` and `Set`, if the service is configured to use Envoy as the proxy, the value may contain variables to interpolate dynamic metadata into the value. For example, using the variable `%DOWNSTREAM_REMOTE_ADDRESS%` in your configuration entry allows you to pass a value that is generated when the split occurs. - - -### `Splits[].ResponseHeaders` - -Specifies a set of HTTP-specific header modification rules applied to responses routed with the service split. You cannot configure request headers if the listener protocol is set to `tcp`. Refer to [Set HTTP Headers](#set-http-headers) for an example configuration. - -#### Values - -- Default: None -- Values: Object containing one or more fields that define header modification rules - - `Add`: Map of one or more string key-value pairs - - `Set`: Map of one or more string key-value pairs - - `Remove`: Map of one or more string key-value pairs - -The following table describes how to configure values for response headers: - -| Rule | Description | Type | -| --- | --- | --- | -| `Add` | Defines a set of key-value pairs to add to the header. Use header names as the keys. Header names are not case-sensitive. If header values with the same name already exist, the value is appended and Consul applies both headers. You can [use variable placeholders](#use-variable-placeholders). | map of strings | -| `Set` | Defines a set of key-value pairs to add to the request header or to replace existing header values with. Use header names as the keys. Header names are not case-sensitive. If header values with the same names already exist, Consul replaces the header values. You can [use variable placeholders](#use-variable-placeholders). | map of strings | -| `Remove` | Defines an list of headers to remove. Consul removes only headers containing exact matches. Header names are not case-sensitive. | list of strings | - -#### Use variable placeholders - -For `Add` and `Set`, if the service is configured to use Envoy as the proxy, the value may contain variables to interpolate dynamic metadata into the value. For example, using the variable `%DOWNSTREAM_REMOTE_ADDRESS%` in your configuration entry allows you to pass a value that is generated when the split occurs. - - - - - -### `apiVersion` - -Kubernetes-only parameter that specifies the version of the Consul API that the configuration entry maps to Kubernetes configurations. The value must be `consul.hashicorp.com/v1alpha1`. - -### `kind` - -Specifies the type of configuration entry to implement. - -#### Values - -- Default: none -- This field is required. -- Data type: String value that must be set to `serviceSplitter`. - -### `metadata.name` - -Specifies a name for the configuration entry. The name is metadata that you can use to reference the configuration entry when performing Consul operations, such as applying a configuration entry to a specific cluster. - -#### Values - -- Default: Inherits name from the host node -- This field is required. -- Data type: String - - -### `metadata.namespace` - -Specifies the Consul namespace to use for resolving the service. You can map Consul namespaces to Kubernetes Namespaces in different ways. Refer to [Custom Resource Definitions (CRDs) for Consul on Kubernetes](/consul/docs/k8s/crds#consul-enterprise) for additional information. - -#### Values - -- Default: None -- Data type: String - -### `spec` - -Kubernetes-only field that contains all of the configurations for service splitter pods. - -#### Values - -- Default: none -- This field is required. -- Data type: Object containing [`spec.splits`](#spec-splits) configuration - -### `spec.meta` - -Specifies key-value pairs to add to the KV store. - -#### Values - -- Default: none -- Data type: Map of one or more key-value pairs - - keys: String - - values: String, integer, or float - -### `spec.splits` - -Defines how much traffic to send to sets of service instances during a traffic split. +# Service Splitter Configuration Entry -#### Values +-> **v1.8.4+:** On Kubernetes, the `ServiceSplitter` custom resource is supported in Consul versions 1.8.4+.
+**v1.6.0+:** On other platforms, this config entry is supported in Consul versions 1.6.0+. -- Default: None -- This field is required. -- Data type: list of objects that can contain the following fields: - - `weight`: The sum of weights for a set of service instances. The total defined value must add up to 100. - - `service`: This field is required. - - `serviceSubset` - - `namespace` - - `partition` - - `requestHeaders` - - `responseHeaders` +The `service-splitter` config entry kind (`ServiceSplitter` on Kubernetes) controls how to split incoming Connect +requests 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 of codebase migration). -### `spec.splits[].weight` +If no splitter config is defined for a service it is assumed 100% of traffic +flows to a service with the same name and discovery continues on to the +resolution stage. -Specifies the percentage of traffic sent to the set of service instances specified in the [`spec.splits.service`](#spec-splits-service) field. Each weight must be a floating integer between `0` and `100`. The smallest representable value is `.01`. The sum of weights across all splits must add up to `100`. +## Interaction with other Config Entries -#### Values +- Service splitter config entries are a component of [L7 Traffic + Management](/consul/docs/connect/l7-traffic). -- Default: `null` -- This field is required. -- Data type: Floating integer from `.01` to `100` +- Service splitter config entries are restricted to only services that define + their protocol as http-based via a corresponding + [`service-defaults`](/consul/docs/connect/config-entries/service-defaults) config + entry or globally via + [`proxy-defaults`](/consul/docs/connect/config-entries/proxy-defaults) . -### `spec.splits[].service` +- Any split destination that specifies a different `Service` field and omits + the `ServiceSubset` field is eligible for further splitting should a splitter + be configured for that other service, otherwise resolution proceeds according + to any configured + [`service-resolver`](/consul/docs/connect/config-entries/service-resolver). -Specifies the name of the service to resolve. +## UI -#### Values +Once a `service-splitter` is successfully entered, you can view it in the UI. Service routers, service splitters, and service resolvers can all be viewed by clicking on your service then switching to the _routing_ tab. -- Default: The service matching the configuration entry [`meta.name`](#metadata-name) field. -- Data type: String +![screenshot of service splitter in the UI](/img/l7-routing/Splitter.png) -### `spec.splits[].serviceSubset` - -Specifies a subset of the service to resolve. This field overrides the `DefaultSubset`. - -#### Values - -- Default: Inherits the name of the default subset. -- Data type: String - -### `spec.splits[].namespace` - -Specifies the [namespace](/consul/docs/enterprise/namespaces) to use when resolving the service. - -#### Values - -- Default: The namespace specified in the top-level configuration entry. -- Data type: String - -### `spec.splits[].partition` - -Specifies which [admin partition](/consul/docs/enterprise/admin-partitions) to use in the FQDN when resolving the service. - -#### Values - -- Default: `default` -- Data type: String - -### `spec.splits[].requestHeaders` - -Specifies a set of HTTP-specific header modification rules applied to requests routed with the service split. You cannot configure request headers if the listener protocol is set to `tcp`. Refer to [Set HTTP Headers](#set-http-headers) for an example configuration. - -#### Values - -- Default: None -- Values: Object containing one or more fields that define header modification rules - - `add`: Map of one or more key-value pairs - - `set`: Map of one or more key-value pairs - - `remove`: Map of one or more key-value pairs - -The following table describes how to configure values for request headers: - -| Rule | Description | Type | -| --- | --- | --- | -| `add` | Defines a set of key-value pairs to add to the header. Use header names as the keys. Header names are not case-sensitive. If header values with the same name already exist, the value is appended and Consul applies both headers. You can [use variable placeholders](#use-variable-placeholders). | map of strings | -| `set` | Defines a set of key-value pairs to add to the request header or to replace existing header values with. Use header names as the keys. Header names are not case-sensitive. If header values with the same names already exist, Consul replaces the header values. You can [use variable placeholders](#use-variable-placeholders). | map of strings | -| `remove` | Defines an list of headers to remove. Consul removes only headers containing exact matches. Header names are not case-sensitive. | list of strings | - -#### Use variable placeholders - -For `add` and `set`, if the service is configured to use Envoy as the proxy, the value may contain variables to interpolate dynamic metadata into the value. For example, using the variable `%DOWNSTREAM_REMOTE_ADDRESS%` in your configuration entry allows you to pass a value that is generated when the split occurs. - -### `spec.splits[].responseHeaders` - -Specifies a set of HTTP-specific header modification rules applied to responses routed with the service split. You cannot configure request headers if the listener protocol is set to `tcp`. Refer to [Set HTTP Headers](#set-http-headers) for an example configuration. - -#### Values - -- Default: None -- Values: Object containing one or more fields that define header modification rules - - `add`: Map of one or more string key-value pairs - - `set`: Map of one or more string key-value pairs - - `remove`: Map of one or more string key-value pairs - -The following table describes how to configure values for response headers: - -| Rule | Description | Type | -| --- | --- | --- | -| `add` | Defines a set of key-value pairs to add to the header. Use header names as the keys. Header names are not case-sensitive. If header values with the same name already exist, the value is appended and Consul applies both headers. You can [use variable placeholders](#use-variable-placeholders). | map of strings | -| `set` | Defines a set of key-value pairs to add to the request header or to replace existing header values with. Use header names as the keys. Header names are not case-sensitive. If header values with the same names already exist, Consul replaces the header values. You can [use variable placeholders](#use-variable-placeholders). | map of strings | -| `remove` | Defines an list of headers to remove. Consul removes only headers containing exact matches. Header names are not case-sensitive. | list of strings | - -#### Use variable placeholders - -For `add` and `set`, if the service is configured to use Envoy as the proxy, the value may contain variables to interpolate dynamic metadata into the value. For example, using the variable `%DOWNSTREAM_REMOTE_ADDRESS%` in your configuration entry allows you to pass a value that is generated when the split occurs. - -
- -
- -## Examples - -The following examples demonstrate common service splitter configuration patterns for specific use cases. +## Sample Config Entries ### Two subsets of same service Split traffic between two subsets of the same service: - - - + ```hcl Kind = "service-splitter" @@ -586,9 +65,18 @@ Splits = [ ] ``` - - - +```yaml +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceSplitter +metadata: + name: web +spec: + splits: + - weight: 90 + serviceSubset: v1 + - weight: 10 + serviceSubset: v2 +``` ```json { @@ -607,34 +95,13 @@ Splits = [ } ``` - - - - -```yaml -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceSplitter -metadata: - name: web -spec: - splits: - - weight: 90 - serviceSubset: v1 - - weight: 10 - serviceSubset: v2 -``` - - - - + ### Two different services Split traffic between two services: - - - + ```hcl Kind = "service-splitter" @@ -651,9 +118,18 @@ Splits = [ ] ``` - - - +```yaml +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceSplitter +metadata: + name: web +spec: + splits: + - weight: 50 + # will default to service with same name as config entry ("web") + - weight: 50 + service: web-rewrite +``` ```json { @@ -671,35 +147,14 @@ Splits = [ } ``` - - - - -```yaml -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceSplitter -metadata: - name: web -spec: - splits: - - weight: 50 - # defaults to the service with same name as the configuration entry ("web") - - weight: 50 - service: web-rewrite -``` - - - - + ### Set HTTP Headers Split traffic between two subsets with extra headers added so clients can tell which version: - - - + ```hcl Kind = "service-splitter" @@ -726,9 +181,24 @@ Splits = [ ] ``` - - - +```yaml +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceSplitter +metadata: + name: web +spec: + splits: + - weight: 90 + serviceSubset: v1 + responseHeaders: + set: + x-web-version: v1 + - weight: 10 + serviceSubset: v2 + responseHeaders: + set: + x-web-version: v2 +``` ```json { @@ -757,31 +227,136 @@ Splits = [ } ``` - + +## Available Fields +', + yaml: false, + }, + { + name: 'Namespace', + type: `string: "default"`, + enterprise: true, + description: + 'Specifies the namespace to which the configuration entry will apply.', + yaml: false, + }, + { + name: 'Partition', + type: `string: "default"`, + enterprise: true, + description: + 'Specifies the admin partition to which the configuration entry will apply.', + yaml: false, + }, + { + name: 'Meta', + type: 'map: nil', + description: + 'Specifies arbitrary KV metadata pairs. Added in Consul 1.8.4.', + yaml: false, + }, + { + name: 'metadata', + children: [ + { + name: 'name', + description: 'Set to the name of the service being configured.', + }, + { + name: 'namespace', + description: + 'If running Consul Open Source, the namespace is ignored (see [Kubernetes Namespaces in Consul OSS](/consul/docs/k8s/crds#consul-oss)). If running Consul Enterprise see [Kubernetes Namespaces in Consul Enterprise](/consul/docs/k8s/crds#consul-enterprise) for more details.', + }, + ], + hcl: false, + }, + { + name: 'Splits', + type: 'array', + description: + 'Defines how much traffic to send to which set of service instances during a traffic split. The sum of weights across all splits must add up to 100.', + children: [ + { + name: 'weight', + type: 'float32: 0', + description: + 'A value between 0 and 100 reflecting what portion of traffic should be directed to this split. The smallest representable weight is 1/10000 or .01%', + }, + { + name: 'Service', + type: 'string: ""', + description: 'The service to resolve instead of the default.', + }, + { + name: 'ServiceSubset', + type: 'string: ""', + description: { + hcl: + "A named subset of the given service to resolve instead of one defined as that service's `DefaultSubset`. If empty the default subset is used.", + yaml: + "A named subset of the given service to resolve instead of one defined as that service's `defaultSubset`. If empty the default subset is used.", + }, + }, + { + name: 'Namespace', + enterprise: true, + type: 'string: ""', + description: + 'The namespace to resolve the service from instead of the current namespace. If empty, the current namespace is used.', + }, + { + name: 'Partition', + enterprise: true, + type: 'string: ""', + description: + 'The admin partition to resolve the service from instead of the current partition. If empty, the current partition is used.', + }, + { + name: 'RequestHeaders', + type: 'HTTPHeaderModifiers: ', + description: `A set of [HTTP-specific header modification rules](/consul/docs/connect/config-entries/service-router#httpheadermodifiers) + that will be applied to requests routed to this split. + This cannot be used with a \`tcp\` listener.`, + }, + { + name: 'ResponseHeaders', + type: 'HTTPHeaderModifiers: ', + description: `A set of [HTTP-specific header modification rules](/consul/docs/connect/config-entries/service-router#httpheadermodifiers) + that will be applied to responses from this split. + This cannot be used with a \`tcp\` listener.`, + }, + ], + }, + ]} +/> - +## ACLs -```yaml -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceSplitter -metadata: - name: web -spec: - splits: - - weight: 90 - serviceSubset: v1 - responseHeaders: - set: - x-web-version: v1 - - weight: 10 - serviceSubset: v2 - responseHeaders: - set: - x-web-version: v2 -``` +Configuration entries may be protected by [ACLs](/consul/docs/security/acl). + +Reading a `service-splitter` config entry requires `service:read` on the resource. - +Creating, updating, or deleting a `service-splitter` config entry requires +`service:write` on the resource and `service:read` on any other service referenced by +name in these fields: - \ No newline at end of file +- [`Splits[].Service`](#service) diff --git a/website/content/docs/connect/proxies/envoy.mdx b/website/content/docs/connect/proxies/envoy.mdx index b47639dc765ea..19e98a1367653 100644 --- a/website/content/docs/connect/proxies/envoy.mdx +++ b/website/content/docs/connect/proxies/envoy.mdx @@ -39,7 +39,6 @@ Consul supports **four major Envoy releases** at the beginning of each major Con | Consul Version | Compatible Envoy Versions | | ------------------- | -----------------------------------------------------------------------------------| -| 1.15.x | 1.25.1, 1.24.2, 1.23.4, 1.22.5 | | 1.14.x | 1.24.0, 1.23.1, 1.22.5, 1.21.5 | | 1.13.x | 1.23.1, 1.22.5, 1.21.5, 1.20.7 | | 1.12.x | 1.22.5, 1.21.5, 1.20.7, 1.19.5 | @@ -53,7 +52,6 @@ Consul Dataplane is a feature introduced in Consul v1.14. Because each version o | Consul Version | Consul Dataplane Version | Bundled Envoy Version | | ------------------- | ------------------------ | ---------------------- | -| 1.15.x | 1.1.x | 1.25.x | | 1.14.x | 1.0.x | 1.24.x | ## Getting Started