From fec665f6f6dbe5beb771bbe3b27746b7d546bb8e Mon Sep 17 00:00:00 2001 From: "nastassia.dailidava" Date: Mon, 5 Feb 2024 21:21:57 +0100 Subject: [PATCH] #564 Implemented adding a header for locality weighted load balancing --- CHANGELOG.md | 4 + .../snapshot/SnapshotProperties.kt | 1 + .../listeners/filters/EnvoyDefaultFilters.kt | 26 ++++-- .../listeners/filters/EnvoyHttpFilters.kt | 3 +- .../listeners/filters/LuaFilterFactory.kt | 43 ++++++--- .../lua/ingress_current_zone_header.lua | 11 +++ .../envoycontrol/EnvoySnapshotFactoryTest.kt | 2 +- .../filters/EnvoyDefaultFiltersTest.kt | 6 +- .../listeners/filters/LuaFilterFactoryTest.kt | 37 ++++++-- .../infrastructure/ControlPlaneConfig.kt | 10 +- .../ingress_current_zone_header_spec.lua | 92 +++++++++++++++++++ 11 files changed, 202 insertions(+), 33 deletions(-) create mode 100644 envoy-control-core/src/main/resources/lua/ingress_current_zone_header.lua create mode 100644 envoy-control-tests/src/main/resources/lua_spec/ingress_current_zone_header_spec.lua diff --git a/CHANGELOG.md b/CHANGELOG.md index 4071f6718..2dc168411 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ Lists all changes with user impact. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). +## [0.20.11] +### Changed +- Implemented adding a header for locality weighted load balancing + ## [0.20.10] ### Changed - Implemented locality weighted load balancing diff --git a/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/SnapshotProperties.kt b/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/SnapshotProperties.kt index 312df86dc..913e8ba43 100644 --- a/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/SnapshotProperties.kt +++ b/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/SnapshotProperties.kt @@ -164,6 +164,7 @@ class CanaryProperties { class TrafficSplittingProperties { var zoneName = "" + var headerName = "" var weightsByService: Map = mapOf() } diff --git a/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/EnvoyDefaultFilters.kt b/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/EnvoyDefaultFilters.kt index e766458cf..85d6aa305 100644 --- a/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/EnvoyDefaultFilters.kt +++ b/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/EnvoyDefaultFilters.kt @@ -10,16 +10,15 @@ import pl.allegro.tech.servicemesh.envoycontrol.snapshot.SnapshotProperties class EnvoyDefaultFilters( private val snapshotProperties: SnapshotProperties, - private val customLuaMetadata: LuaMetadataProperty.StructPropertyLua + private val customLuaMetadata: LuaMetadataProperty.StructPropertyLua, + private val currentZone: String ) { private val rbacFilterFactory = RBACFilterFactory( snapshotProperties.incomingPermissions, snapshotProperties.routes.status, jwtProperties = snapshotProperties.jwt ) - private val luaFilterFactory = LuaFilterFactory( - snapshotProperties.incomingPermissions - ) + private val luaFilterFactory = LuaFilterFactory(snapshotProperties) private val jwtFilterFactory = JwtFilterFactory( snapshotProperties.jwt ) @@ -68,6 +67,10 @@ class EnvoyDefaultFilters( defaultHeaderToMetadataFilter, defaultServiceTagFilter, defaultEnvoyRouterHttpFilter ) + val defaultCurrentZoneHeaderFilter = { _: Group, _: GlobalSnapshot -> + luaFilterFactory.ingressCurrentZoneHeaderFilter() + } + /** * Order matters: * * defaultClientNameHeaderFilter has to be before defaultRbacLoggingFilter, because the latter consumes results of @@ -101,7 +104,8 @@ class EnvoyDefaultFilters( val preFilters = listOf( defaultClientNameHeaderFilter, defaultAuthorizationHeaderFilter, - defaultJwtHttpFilter + defaultJwtHttpFilter, + defaultCurrentZoneHeaderFilter ) val postFilters = listOf( defaultRbacLoggingFilter, @@ -113,7 +117,9 @@ class EnvoyDefaultFilters( return preFilters + filters.toList() + postFilters } - val defaultIngressMetadata = { group: Group -> luaFilterFactory.ingressScriptsMetadata(group, customLuaMetadata) } + val defaultIngressMetadata = { group: Group -> + luaFilterFactory.ingressScriptsMetadata(group, customLuaMetadata, currentZone) + } private fun headerToMetadataConfig( rules: List, @@ -144,8 +150,12 @@ class EnvoyDefaultFilters( private fun envoyRouterHttpFilter(): HttpFilter = HttpFilter .newBuilder() .setName("envoy.filters.http.router") - .setTypedConfig(Any.pack(Router.newBuilder() - .build())) + .setTypedConfig( + Any.pack( + Router.newBuilder() + .build() + ) + ) .build() private fun headerToMetadataHttpFilter(headerToMetadataConfig: Config.Builder): HttpFilter { diff --git a/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/EnvoyHttpFilters.kt b/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/EnvoyHttpFilters.kt index 705585072..8f073051f 100644 --- a/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/EnvoyHttpFilters.kt +++ b/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/EnvoyHttpFilters.kt @@ -15,9 +15,10 @@ class EnvoyHttpFilters( fun defaultFilters( snapshotProperties: SnapshotProperties, + localDatacenter: String, customLuaMetadata: LuaMetadataProperty.StructPropertyLua = LuaMetadataProperty.StructPropertyLua() ): EnvoyHttpFilters { - val defaultFilters = EnvoyDefaultFilters(snapshotProperties, customLuaMetadata) + val defaultFilters = EnvoyDefaultFilters(snapshotProperties, customLuaMetadata, localDatacenter) return EnvoyHttpFilters( defaultFilters.ingressFilters(), defaultFilters.defaultEgressFilters, diff --git a/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/LuaFilterFactory.kt b/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/LuaFilterFactory.kt index 28219de5f..d6191d429 100644 --- a/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/LuaFilterFactory.kt +++ b/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/LuaFilterFactory.kt @@ -8,17 +8,17 @@ import io.envoyproxy.envoy.config.core.v3.Metadata import io.envoyproxy.envoy.extensions.filters.http.lua.v3.Lua import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter import pl.allegro.tech.servicemesh.envoycontrol.groups.Group -import pl.allegro.tech.servicemesh.envoycontrol.snapshot.IncomingPermissionsProperties +import pl.allegro.tech.servicemesh.envoycontrol.snapshot.SnapshotProperties import pl.allegro.tech.servicemesh.envoycontrol.snapshot.resource.listeners.filters.LuaMetadataProperty.ListPropertyLua import pl.allegro.tech.servicemesh.envoycontrol.snapshot.resource.listeners.filters.LuaMetadataProperty.StringPropertyLua import pl.allegro.tech.servicemesh.envoycontrol.snapshot.resource.listeners.filters.LuaMetadataProperty.StructPropertyLua -class LuaFilterFactory(private val incomingPermissionsProperties: IncomingPermissionsProperties) { +class LuaFilterFactory(private val snapshotProperties: SnapshotProperties) { private val ingressRbacLoggingScript: String = this::class.java.classLoader .getResource("lua/ingress_rbac_logging.lua")!!.readText() - private val ingressRbacLoggingFilter: HttpFilter? = if (incomingPermissionsProperties.enabled) { + private val ingressRbacLoggingFilter: HttpFilter? = if (snapshotProperties.incomingPermissions.enabled) { HttpFilter.newBuilder() .setName("envoy.lua") .setTypedConfig(Any.pack(Lua.newBuilder().setInlineCode(ingressRbacLoggingScript).build())) @@ -27,7 +27,7 @@ class LuaFilterFactory(private val incomingPermissionsProperties: IncomingPermis null } - private val trustedClientIdentityHeader = incomingPermissionsProperties.trustedClientIdentityHeader + private val trustedClientIdentityHeader = snapshotProperties.incomingPermissions.trustedClientIdentityHeader fun ingressRbacLoggingFilter(group: Group): HttpFilter? = ingressRbacLoggingFilter.takeIf { group.proxySettings.incoming.permissionsEnabled } @@ -41,33 +41,52 @@ class LuaFilterFactory(private val incomingPermissionsProperties: IncomingPermis .setTypedConfig(Any.pack(Lua.newBuilder().setInlineCode(ingressClientNameHeaderScript).build())) .build() - private val sanUriWildcardRegexForLua = SanUriMatcherFactory(incomingPermissionsProperties.tlsAuthentication) + private val ingressCurrentZoneHeaderScript: String = this::class.java.classLoader + .getResource("lua/ingress_current_zone_header.lua")!!.readText() + + private val ingressCurrentZoneHeaderFilter: HttpFilter = + HttpFilter.newBuilder() + .setName("ingress.client.lua") + .setTypedConfig(Any.pack(Lua.newBuilder().setInlineCode(ingressCurrentZoneHeaderScript).build())) + .build() + + private val sanUriWildcardRegexForLua = SanUriMatcherFactory( + snapshotProperties.incomingPermissions.tlsAuthentication + ) .sanUriWildcardRegexForLua - fun ingressScriptsMetadata(group: Group, customLuaMetadata: StructPropertyLua = StructPropertyLua()): Metadata { + fun ingressScriptsMetadata( + group: Group, + customLuaMetadata: StructPropertyLua = StructPropertyLua(), + currentZone: String + ): Metadata { val metadata = StructPropertyLua( "client_identity_headers" to ListPropertyLua( - incomingPermissionsProperties + snapshotProperties.incomingPermissions .clientIdentityHeaders.map(::StringPropertyLua) ), "request_id_headers" to ListPropertyLua( - incomingPermissionsProperties.requestIdentificationHeaders.map( + snapshotProperties.incomingPermissions.requestIdentificationHeaders.map( ::StringPropertyLua ) ), "trusted_client_identity_header" to StringPropertyLua(trustedClientIdentityHeader), "san_uri_lua_pattern" to StringPropertyLua(sanUriWildcardRegexForLua), "clients_allowed_to_all_endpoints" to ListPropertyLua( - incomingPermissionsProperties.clientsAllowedToAllEndpoints.map( + snapshotProperties.incomingPermissions.clientsAllowedToAllEndpoints.map( ::StringPropertyLua ) ), "service_name" to StringPropertyLua(group.serviceName), "discovery_service_name" to StringPropertyLua(group.discoveryServiceName ?: ""), "rbac_headers_to_log" to ListPropertyLua( - incomingPermissionsProperties.headersToLogInRbac.map(::StringPropertyLua) + snapshotProperties.incomingPermissions.headersToLogInRbac.map(::StringPropertyLua) + ), + "traffic_splitting_zone_header_name" to StringPropertyLua( + snapshotProperties.loadBalancing.trafficSplitting.headerName ), - ) + customLuaMetadata + "current_zone" to StringPropertyLua(currentZone) + ) + customLuaMetadata return Metadata.newBuilder() .putFilterMetadata("envoy.filters.http.lua", metadata.toValue().structValue) .build() @@ -75,6 +94,8 @@ class LuaFilterFactory(private val incomingPermissionsProperties: IncomingPermis fun ingressClientNameHeaderFilter(): HttpFilter? = ingressClientNameHeaderFilter.takeIf { trustedClientIdentityHeader.isNotEmpty() } + + fun ingressCurrentZoneHeaderFilter(): HttpFilter = ingressCurrentZoneHeaderFilter } sealed class LuaMetadataProperty(open val value: T) { diff --git a/envoy-control-core/src/main/resources/lua/ingress_current_zone_header.lua b/envoy-control-core/src/main/resources/lua/ingress_current_zone_header.lua new file mode 100644 index 000000000..ba18457c0 --- /dev/null +++ b/envoy-control-core/src/main/resources/lua/ingress_current_zone_header.lua @@ -0,0 +1,11 @@ +function envoy_on_request(handle) + local header_name = handle:metadata():get("traffic_splitting_zone_header_name") or "" + local current_zone = handle:metadata():get("current_zone") or "" + if header_name == "" then + return + end + handle:headers():add(header_name, current_zone) +end + +function envoy_on_response(handle) +end diff --git a/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/EnvoySnapshotFactoryTest.kt b/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/EnvoySnapshotFactoryTest.kt index da26a343c..c59c5fbbe 100644 --- a/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/EnvoySnapshotFactoryTest.kt +++ b/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/EnvoySnapshotFactoryTest.kt @@ -458,7 +458,7 @@ class EnvoySnapshotFactoryTest { val egressRoutesFactory = EnvoyEgressRoutesFactory(properties) val clustersFactory = EnvoyClustersFactory(properties) val endpointsFactory = EnvoyEndpointsFactory(properties, ServiceTagMetadataGenerator(), CURRENT_ZONE) - val envoyHttpFilters = EnvoyHttpFilters.defaultFilters(properties) + val envoyHttpFilters = EnvoyHttpFilters.defaultFilters(properties, "dc1") val listenersFactory = EnvoyListenersFactory(properties, envoyHttpFilters) val snapshotsVersions = SnapshotsVersions() val meterRegistry: MeterRegistry = SimpleMeterRegistry() diff --git a/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/EnvoyDefaultFiltersTest.kt b/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/EnvoyDefaultFiltersTest.kt index b75ef7314..ee46df0fe 100644 --- a/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/EnvoyDefaultFiltersTest.kt +++ b/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/EnvoyDefaultFiltersTest.kt @@ -11,7 +11,11 @@ import pl.allegro.tech.servicemesh.envoycontrol.snapshot.SnapshotProperties class EnvoyDefaultFiltersTest { - private val defaultFilters = EnvoyDefaultFilters(SnapshotProperties(), LuaMetadataProperty.StructPropertyLua()) + private val defaultFilters = EnvoyDefaultFilters( + SnapshotProperties(), + LuaMetadataProperty.StructPropertyLua(), + "dc1" + ) @Test fun `should create default filters`() { diff --git a/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/LuaFilterFactoryTest.kt b/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/LuaFilterFactoryTest.kt index 901b5dd5a..cc8ce95f2 100644 --- a/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/LuaFilterFactoryTest.kt +++ b/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/LuaFilterFactoryTest.kt @@ -6,13 +6,15 @@ import pl.allegro.tech.servicemesh.envoycontrol.groups.CommunicationMode import pl.allegro.tech.servicemesh.envoycontrol.groups.Group import pl.allegro.tech.servicemesh.envoycontrol.groups.ServicesGroup import pl.allegro.tech.servicemesh.envoycontrol.snapshot.IncomingPermissionsProperties -import pl.allegro.tech.servicemesh.envoycontrol.snapshot.resource.listeners.filters.LuaMetadataProperty.StructPropertyLua -import pl.allegro.tech.servicemesh.envoycontrol.snapshot.resource.listeners.filters.LuaMetadataProperty.ListPropertyLua -import pl.allegro.tech.servicemesh.envoycontrol.snapshot.resource.listeners.filters.LuaMetadataProperty.StringPropertyLua +import pl.allegro.tech.servicemesh.envoycontrol.snapshot.SnapshotProperties import pl.allegro.tech.servicemesh.envoycontrol.snapshot.resource.listeners.filters.LuaMetadataProperty.BooleanPropertyLua +import pl.allegro.tech.servicemesh.envoycontrol.snapshot.resource.listeners.filters.LuaMetadataProperty.ListPropertyLua import pl.allegro.tech.servicemesh.envoycontrol.snapshot.resource.listeners.filters.LuaMetadataProperty.NumberPropertyLua +import pl.allegro.tech.servicemesh.envoycontrol.snapshot.resource.listeners.filters.LuaMetadataProperty.StringPropertyLua +import pl.allegro.tech.servicemesh.envoycontrol.snapshot.resource.listeners.filters.LuaMetadataProperty.StructPropertyLua internal class LuaFilterFactoryTest { + val properties = SnapshotProperties().also { it.incomingPermissions = IncomingPermissionsProperties() } @Test fun `should create metadata with service name and discovery service name`() { @@ -24,10 +26,10 @@ internal class LuaFilterFactoryTest { serviceName = expectedServiceName, discoveryServiceName = expectedDiscoveryServiceName ) - val factory = LuaFilterFactory(IncomingPermissionsProperties()) + val factory = LuaFilterFactory(properties) // when - val metadata = factory.ingressScriptsMetadata(group) + val metadata = factory.ingressScriptsMetadata(group, currentZone = "dc1") val givenServiceName = metadata .getFilterMetadataOrThrow("envoy.filters.http.lua") .getFieldsOrThrow("service_name") @@ -62,10 +64,10 @@ internal class LuaFilterFactoryTest { serviceName = expectedServiceName, discoveryServiceName = expectedDiscoveryServiceName ) - val factory = LuaFilterFactory(IncomingPermissionsProperties()) + val factory = LuaFilterFactory(properties) // when - val luaMetadata = factory.ingressScriptsMetadata(group, customMetadata) + val luaMetadata = factory.ingressScriptsMetadata(group, customMetadata, "dc1") .getFilterMetadataOrThrow("envoy.filters.http.lua").fieldsMap // then @@ -73,4 +75,25 @@ internal class LuaFilterFactoryTest { assertThat(luaMetadata["list-value"]).isEqualTo(customMetadata["list-value"]?.toValue()) assertThat(luaMetadata["count"]).isEqualTo(customMetadata["count"]?.toValue()) } + + @Test + fun `should create metadata with current zone`() { + // given + val expectedServiceName = "service-1" + val expectedDiscoveryServiceName = "consul-service-1" + val expectedCurrentZone = "dc1" + val group: Group = ServicesGroup( + communicationMode = CommunicationMode.XDS, + serviceName = expectedServiceName, + discoveryServiceName = expectedDiscoveryServiceName + ) + val factory = LuaFilterFactory(properties) + + // when + val luaMetadata = factory.ingressScriptsMetadata(group, StructPropertyLua(), "dc1") + .getFilterMetadataOrThrow("envoy.filters.http.lua").fieldsMap + + // then + assertThat(luaMetadata["current_zone"]).isEqualTo(expectedCurrentZone) + } } diff --git a/envoy-control-runner/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/infrastructure/ControlPlaneConfig.kt b/envoy-control-runner/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/infrastructure/ControlPlaneConfig.kt index 6845fcb5a..2470b39e1 100644 --- a/envoy-control-runner/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/infrastructure/ControlPlaneConfig.kt +++ b/envoy-control-runner/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/infrastructure/ControlPlaneConfig.kt @@ -66,11 +66,11 @@ class ControlPlaneConfig { globalStateChanges: GlobalStateChanges, metrics: EnvoyControlMetrics, envoyHttpFilters: EnvoyHttpFilters, - consulProperties: ConsulProperties + localDatacenter: String ): ControlPlane = ControlPlane.builder(properties, meterRegistry) .withMetrics(metrics) - .withCurrentZone(localDatacenter(consulProperties)) + .withCurrentZone(localDatacenter) .withEnvoyHttpFilters(envoyHttpFilters) .build(globalStateChanges.combined()) @@ -163,11 +163,13 @@ class ControlPlaneConfig { @Bean @ConditionalOnMissingBean(EnvoyHttpFilters::class) fun envoyHttpFilters( - properties: EnvoyControlProperties + properties: EnvoyControlProperties, + localDatacenter: String ): EnvoyHttpFilters { - return EnvoyHttpFilters.defaultFilters(properties.envoy.snapshot) + return EnvoyHttpFilters.defaultFilters(properties.envoy.snapshot, localDatacenter) } + @Bean fun localDatacenter(properties: ConsulProperties) = ConsulClient(properties.host, properties.port).agentSelf.value?.config?.datacenter ?: "local" diff --git a/envoy-control-tests/src/main/resources/lua_spec/ingress_current_zone_header_spec.lua b/envoy-control-tests/src/main/resources/lua_spec/ingress_current_zone_header_spec.lua new file mode 100644 index 000000000..accac8e8a --- /dev/null +++ b/envoy-control-tests/src/main/resources/lua_spec/ingress_current_zone_header_spec.lua @@ -0,0 +1,92 @@ +require('ingress_current_zone_header') + +local function handlerMock(headers, metadata) + return { + headers = function() return { + add = function(_, key, value) if headers[key] ~= nil then headers[key] = headers[key] ..","..value else headers[key] = value end end, + get = function(_, key) + assert.is.not_nil(key, "headers:get() called with nil argument") + return headers[key] + end, + remove = function(_, key) headers[key] = nil end + } + end, + metadata = function() return { + get = function(_, key) return metadata[key] end + } + end + } +end + +describe("envoy_on_request:", function() + it("should set current zone header", function() + -- given + local filter_metadata = { + ['traffic_splitting_zone_header_name'] = 'x-current-zone', + ['current_zone'] = 'local-dc' + } + local headers = {} + local handle = handlerMock(headers, filter_metadata) + + -- when + envoy_on_request(handle) + + -- then + assert.are.equal('local-dc', headers['x-current-zone']) + end) + + it("should add zone to existing header", function() + -- given + local filter_metadata = { + ['traffic_splitting_zone_header_name'] = 'x-current-zone', + ['current_zone'] = 'local-dc' + } + local headers = {['x-current-zone'] = 'local-dc-0'} + local handle = handlerMock(headers, filter_metadata) + + -- when + envoy_on_request(handle) + + -- then + assert.are.equal('local-dc-0,local-dc', headers['x-current-zone']) + end) + + it("should not add header if header name isn't specified", function() + -- given + local filter_metadata = { + ['current_zone'] = 'local-dc' + } + local headers = {} + local handle = handlerMock(headers, filter_metadata) + + -- when + envoy_on_request(handle) + + -- then + assert.are.equal(nil, headers['x-current-zone']) + end) + + + it("should add header if zone name is empty", function() + -- given + local filter_metadata = { + ['traffic_splitting_zone_header_name'] = 'x-current-zone', + ['current_zone'] = '' + } + local headers = {} + local handle = handlerMock(headers, filter_metadata) + + -- when + envoy_on_request(handle) + + -- then + assert.are.equal('', headers['x-current-zone']) + end) +end) + + +--[[ +tools: + show spy calls: + require 'pl.pretty'.dump(handle.logInfo.calls, "/dev/stderr") +]] --