From 52365b157bb418810b6fc2fb65dfb08a39319330 Mon Sep 17 00:00:00 2001 From: "nastassia.dailidava" Date: Thu, 25 Jul 2024 13:23:50 +0200 Subject: [PATCH] allegro-internal/flex-roadmap#687 Added http compression filter configuration (added a white list of services) --- .../snapshot/SnapshotProperties.kt | 5 +- .../filters/CompressionFilterFactory.kt | 11 ++-- .../listeners/filters/EnvoyDefaultFilters.kt | 8 +-- .../envoycontrol/CompressionFilterTest.kt | 57 +++++++++++++------ 4 files changed, 56 insertions(+), 25 deletions(-) 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 2428d9115..6c90d2e1d 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 @@ -46,6 +46,7 @@ class PathNormalizationProperties { var mergeSlashes = true var pathWithEscapedSlashesAction = "KEEP_UNCHANGED" } + class MetricsProperties { var cacheSetSnapshot = false } @@ -65,7 +66,7 @@ class AccessLogProperties { var enabled = false var timeFormat = "%START_TIME(%FT%T.%3fZ)%" var messageFormat = "%PROTOCOL% %REQ(:METHOD)% %REQ(:authority)% %REQ(:PATH)% " + - "%DOWNSTREAM_REMOTE_ADDRESS% -> %UPSTREAM_HOST%" + "%DOWNSTREAM_REMOTE_ADDRESS% -> %UPSTREAM_HOST%" var level = "TRACE" var logger = "envoy.AccessLog" var customFields = mapOf() @@ -131,6 +132,7 @@ class ClientsListsProperties { var defaultClientsList: List = emptyList() var customClientsLists: Map> = mapOf() } + class TlsProtocolProperties { var cipherSuites: List = listOf("ECDHE-ECDSA-AES128-GCM-SHA256", "ECDHE-RSA-AES128-GCM-SHA256") var minimumVersion = TlsParameters.TlsProtocol.TLSv1_2 @@ -385,6 +387,7 @@ class CompressionProperties { var disableOnEtagHeader = true var requestCompressionEnabled = false var responseCompressionEnabled = false + var enableForServices: List = emptyList() } class BrotliProperties { diff --git a/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/CompressionFilterFactory.kt b/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/CompressionFilterFactory.kt index 8519b98ff..2f3cc32ef 100644 --- a/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/CompressionFilterFactory.kt +++ b/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/CompressionFilterFactory.kt @@ -9,6 +9,7 @@ import io.envoyproxy.envoy.extensions.compression.brotli.compressor.v3.Brotli import io.envoyproxy.envoy.extensions.compression.gzip.compressor.v3.Gzip import io.envoyproxy.envoy.extensions.filters.http.compressor.v3.Compressor 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.SnapshotProperties class CompressionFilterFactory(val properties: SnapshotProperties) { @@ -40,14 +41,14 @@ class CompressionFilterFactory(val properties: SnapshotProperties) { properties.compression.gzip.chooseFirst ) - fun gzipCompressionFilter(): HttpFilter? { - return if (properties.compression.gzip.enabled) { + fun gzipCompressionFilter(group: Group): HttpFilter? { + return if (properties.compression.gzip.enabled && group.hasCompressionEnabled()) { gzipCompressionFilter } else null } - fun brotliCompressionFilter(): HttpFilter? { - return if (properties.compression.brotli.enabled) { + fun brotliCompressionFilter(group: Group): HttpFilter? { + return if (properties.compression.brotli.enabled && group.hasCompressionEnabled()) { brotliCompressionFilter } else null } @@ -89,4 +90,6 @@ class CompressionFilterFactory(val properties: SnapshotProperties) { .setDefaultValue(BoolValue.of(defaultValue)) ) .setMinContentLength(UInt32Value.of(properties.compression.minContentLength)) + + private fun Group.hasCompressionEnabled() = properties.compression.enableForServices.contains(this.serviceName) } 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 7a49d2531..07e92eb53 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 @@ -65,12 +65,12 @@ class EnvoyDefaultFilters( authorizationHeaderToMetadataFilter() } - val defaultGzipCompressionFilter = { _: Group, _: GlobalSnapshot -> - compressionFilterFactory.gzipCompressionFilter() + val defaultGzipCompressionFilter = { group: Group, _: GlobalSnapshot -> + compressionFilterFactory.gzipCompressionFilter(group) } - val defaultBrotliCompressionFilter = { _: Group, _: GlobalSnapshot -> - compressionFilterFactory.brotliCompressionFilter() + val defaultBrotliCompressionFilter = { group: Group, _: GlobalSnapshot -> + compressionFilterFactory.brotliCompressionFilter(group) } val defaultEgressFilters = listOf( diff --git a/envoy-control-tests/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/CompressionFilterTest.kt b/envoy-control-tests/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/CompressionFilterTest.kt index a7c900f6b..36a3d19d7 100644 --- a/envoy-control-tests/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/CompressionFilterTest.kt +++ b/envoy-control-tests/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/CompressionFilterTest.kt @@ -6,6 +6,7 @@ import org.assertj.core.api.ObjectAssert import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.RegisterExtension import pl.allegro.tech.servicemesh.envoycontrol.assertions.untilAsserted +import pl.allegro.tech.servicemesh.envoycontrol.config.AdsAllDependencies import pl.allegro.tech.servicemesh.envoycontrol.config.Xds import pl.allegro.tech.servicemesh.envoycontrol.config.consul.ConsulExtension import pl.allegro.tech.servicemesh.envoycontrol.config.envoy.EnvoyExtension @@ -17,6 +18,22 @@ import pl.allegro.tech.servicemesh.envoycontrol.config.service.GenericServiceExt class CompressionFilterTest { companion object { + private const val SERVICE_NAME = "service-1" + private const val DOWNSTREAM_SERVICE_NAME = "echo2" + private const val LONG_STRING = "Workshallmeantheworkofauthorship,whetherinSourceorObjectform," + + "madeavailableundertheLicensesindicatedbyacopyrightnoticethatisincludedinorattachedto" + + "thework(anexampleisprovidedintheAppendixbelow)." + private val serviceConfig = """ + node: + metadata: + proxy_settings: + incoming: + unlistedEndpointsPolicy: log + endpoints: [] + """.trimIndent() + private val config = Xds.copy(configOverride = serviceConfig, serviceName = SERVICE_NAME) + private val unListedServiceConfig = AdsAllDependencies + private val longText = LONG_STRING.repeat(100) @JvmField @RegisterExtension @@ -31,36 +48,32 @@ class CompressionFilterTest { "envoy-control.envoy.snapshot.compression.brotli.enabled" to true, "envoy-control.envoy.snapshot.compression.minContentLength" to 100, "envoy-control.envoy.snapshot.compression.responseCompressionEnabled" to true, + "envoy-control.envoy.snapshot.compression.enableForServices" to listOf(DOWNSTREAM_SERVICE_NAME), + "envoy-control.envoy.snapshot.outgoing-permissions.servicesAllowedToUseWildcard" to "test-service" + ) ) - private const val SERVICE_NAME = "service-1" - private const val LONG_STRING = "Workshallmeantheworkofauthorship,whetherinSourceorObjectform," + - "madeavailableundertheLicensesindicatedbyacopyrightnoticethatisincludedinorattachedto" + - "thework(anexampleisprovidedintheAppendixbelow)." - - private val serviceConfig = """ - node: - metadata: - proxy_settings: - incoming: - unlistedEndpointsPolicy: log - endpoints: [] - """.trimIndent() - private val config = Xds.copy(configOverride = serviceConfig, serviceName = SERVICE_NAME) - private val longText = LONG_STRING.repeat(100) @JvmField @RegisterExtension val service = GenericServiceExtension(EchoContainer(longText)) + @JvmField + @RegisterExtension + val noCompressionService = GenericServiceExtension(EchoContainer(longText)) + @JvmField @RegisterExtension val downstreamService = EchoServiceExtension() @JvmField @RegisterExtension - val downstreamEnvoy = EnvoyExtension(envoyControl, downstreamService, config = Xds) + val downstreamEnvoy = EnvoyExtension(envoyControl, downstreamService, Xds) + + @JvmField + @RegisterExtension + val noCompressionEnvoy = EnvoyExtension(envoyControl, noCompressionService, unListedServiceConfig) @JvmField @RegisterExtension @@ -100,6 +113,18 @@ class CompressionFilterTest { } } + @Test + fun `should not enable compression on unlisted service`() { + consul.server.operations.registerServiceWithEnvoyOnIngress(serviceEnvoy, name = SERVICE_NAME) + noCompressionEnvoy.waitForReadyServices(SERVICE_NAME) + untilAsserted { + val response = + noCompressionEnvoy.egressOperations.callService(SERVICE_NAME, headers = mapOf("accept-encoding" to "gzip")) + println(response.headers.toString()) + assertThat(response).isNotCompressed() + } + } + private fun ObjectAssert.isCompressedWith(encoding: String): ObjectAssert { matches { it.isSuccessful && it.headers.any { x -> x.first == "content-encoding" && x.second == encoding } } return this