Skip to content

Commit 4c32aaf

Browse files
PROM-6104 | establish x-service-tag-preference on ingress (#454)
1 parent 096de43 commit 4c32aaf

File tree

7 files changed

+125
-21
lines changed

7 files changed

+125
-21
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
Lists all changes with user impact.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
55

6+
## [0.22.13]
7+
### Changed
8+
- establish x-service-tag-preference on ingress: use the default one if it's more specific, otherwise the request one
9+
610
## [0.22.12]
711
### Changed
812
- add service tag preference routing

envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/EnvoyDefaultFilters.kt

+7-20
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ class EnvoyDefaultFilters(
3535
private val defaultHeaderToMetadataConfig = headerToMetadataConfig(defaultServiceTagHeaderToMetadataFilterRules)
3636
private val headerToMetadataHttpFilter = headerToMetadataHttpFilter(defaultHeaderToMetadataConfig)
3737
private val defaultHeaderToMetadataFilter = { _: Group, _: GlobalSnapshot -> headerToMetadataHttpFilter }
38-
private val defaultServiceTagFilters = serviceTagFilterFactory.egressFilters()
3938
private val envoyRouterHttpFilter = envoyRouterHttpFilter()
4039

4140
/**
@@ -74,6 +73,9 @@ class EnvoyDefaultFilters(
7473
compressionFilterFactory.brotliCompressionFilter(group)
7574
}
7675

76+
val defaultServiceTagFilters = serviceTagFilterFactory.egressFilters()
77+
val defaultIngressServiceTagFilters = serviceTagFilterFactory.ingressFilters()
78+
7779
val defaultEgressFilters: List<HttpFilterFactory> = listOf(
7880
defaultHeaderToMetadataFilter,
7981
*defaultServiceTagFilters,
@@ -87,32 +89,16 @@ class EnvoyDefaultFilters(
8789
}
8890

8991
/**
92+
* Creates a list of http filters with provided user filters. It respects order for crucial default filters.
93+
* This function is a recommended way to create list of ingress filters.
94+
*
9095
* Order matters:
9196
* * defaultClientNameHeaderFilter has to be before defaultRbacLoggingFilter, because the latter consumes results of
9297
* the former
9398
* * defaultRbacLoggingFilter has to be before defaultRbacFilter, otherwise unauthorised requests will not be
9499
* logged, because the RBAC filter will stop filter chain execution and subsequent filters will not process
95100
* the request
96101
* * defaultEnvoyRouterHttpFilter - router filter should be always the last filter.
97-
*
98-
* Instead of it, use:
99-
* @see ingressFilters()
100-
*/
101-
@Deprecated("It becomes deprecated, because user has no option to add new filters.")
102-
val defaultIngressFilters = listOf(
103-
defaultClientNameHeaderFilter,
104-
defaultAuthorizationHeaderFilter,
105-
defaultJwtHttpFilter,
106-
defaultRbacLoggingFilter,
107-
defaultRbacFilter,
108-
defaultRateLimitLuaFilter,
109-
defaultRateLimitFilter,
110-
defaultEnvoyRouterHttpFilter
111-
)
112-
113-
/**
114-
* Creates a list of http filters with provided user filters. It respects order for crucial default filters.
115-
* This function is a recommended way to create list of ingress filters.
116102
*/
117103
fun ingressFilters(vararg filters: (Group, GlobalSnapshot) -> HttpFilter?):
118104
List<(Group, GlobalSnapshot) -> HttpFilter?> {
@@ -127,6 +113,7 @@ class EnvoyDefaultFilters(
127113
defaultRbacFilter,
128114
defaultRateLimitLuaFilter,
129115
defaultRateLimitFilter,
116+
*defaultIngressServiceTagFilters,
130117
defaultEnvoyRouterHttpFilter
131118
)
132119
return preFilters + filters.toList() + postFilters

envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/ServiceTagFilterFactory.kt

+21
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,24 @@ class ServiceTagFilterFactory(private val properties: ServiceTagsProperties) {
3434
{ _, _ -> luaEgressAutoServiceTagsFilter }
3535
)
3636

37+
fun ingressFilters(): Array<HttpFilterFactory> = arrayOf(
38+
{ group: Group, _ -> luaIngressServiceTagPreferenceFilter(group) },
39+
)
40+
3741
private fun luaEgressServiceTagPreferenceFilter(group: Group): HttpFilter? =
3842
if (properties.preferenceRouting.isEnabledFor(group.serviceName)) {
3943
luaEgressServiceTagPreferenceFilter
4044
} else {
4145
null
4246
}
4347

48+
private fun luaIngressServiceTagPreferenceFilter(group: Group): HttpFilter? =
49+
if (properties.preferenceRouting.isEnabledFor(group.serviceName)) {
50+
luaIngressServiceTagPreferenceFilter
51+
} else {
52+
null
53+
}
54+
4455
private val luaEgressAutoServiceTagsFilter: HttpFilter? =
4556
if (properties.shouldRejectRequestsWithDuplicatedAutoServiceTag()) {
4657
createLuaFilter(
@@ -67,4 +78,14 @@ class ServiceTagFilterFactory(private val properties: ServiceTagsProperties) {
6778
(properties.preferenceRouting.fallbackToAny.enableForServicesWithDefaultPreferenceEqualTo ?: "")
6879
)
6980
)
81+
82+
private val luaIngressServiceTagPreferenceFilter: HttpFilter = createLuaFilter(
83+
luaFile = "lua/ingress_service_tag_preference.lua",
84+
filterName = "ingress.lua.service_tag_preference",
85+
variables = mapOf(
86+
"SERVICE_TAG_PREFERENCE_HEADER" to properties.preferenceRouting.header,
87+
"DEFAULT_SERVICE_TAG_PREFERENCE_ENV" to properties.preferenceRouting.defaultPreferenceEnv,
88+
"DEFAULT_SERVICE_TAG_PREFERENCE_FALLBACK" to properties.preferenceRouting.defaultPreferenceFallback
89+
)
90+
)
7091
}

envoy-control-core/src/main/resources/lua/egress_service_tag_preference.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ if fallbackToAnyIfDefaultPreferenceEqualTo ~= "" then
99
end
1010
end
1111

12-
function parseServiceTagPreferenceToFallbackList(preferenceString)
12+
local parseServiceTagPreferenceToFallbackList = function(preferenceString)
1313
local fallbackList = {}
1414
local i = 1
1515
for tag in string.gmatch(preferenceString, "[^|]+") do
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
local defaultServiceTagPreference = os.getenv("%DEFAULT_SERVICE_TAG_PREFERENCE_ENV%") or "%DEFAULT_SERVICE_TAG_PREFERENCE_FALLBACK%"
2+
local defaultServiceTagPreferenceLength = #defaultServiceTagPreference
3+
4+
local isSuffixOfDefaultPreference = function(requestPreference)
5+
local requestPreferenceLength = #requestPreference
6+
if requestPreferenceLength >= defaultServiceTagPreferenceLength then
7+
return false
8+
end
9+
return string.sub(defaultServiceTagPreference, -requestPreferenceLength - 1) == "|" .. requestPreference
10+
end
11+
12+
function envoy_on_request(handle)
13+
local requestPreference = handle:headers():getAtIndex("%SERVICE_TAG_PREFERENCE_HEADER%", 0)
14+
if not requestPreference then
15+
handle:headers():add("%SERVICE_TAG_PREFERENCE_HEADER%", defaultServiceTagPreference)
16+
return
17+
end
18+
if isSuffixOfDefaultPreference(requestPreference) then
19+
handle:headers():replace("%SERVICE_TAG_PREFERENCE_HEADER%", defaultServiceTagPreference)
20+
end
21+
end

envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/EnvoyDefaultFiltersTest.kt

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class EnvoyDefaultFiltersTest {
2828
defaultFilters.defaultRbacFilter,
2929
defaultFilters.defaultRateLimitLuaFilter,
3030
defaultFilters.defaultRateLimitFilter,
31+
*defaultFilters.defaultIngressServiceTagFilters,
3132
defaultFilters.defaultEnvoyRouterHttpFilter
3233
)
3334

@@ -57,6 +58,7 @@ class EnvoyDefaultFiltersTest {
5758
defaultFilters.defaultRbacFilter,
5859
defaultFilters.defaultRateLimitLuaFilter,
5960
defaultFilters.defaultRateLimitFilter,
61+
*defaultFilters.defaultIngressServiceTagFilters,
6062
defaultFilters.defaultEnvoyRouterHttpFilter
6163
)
6264

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package pl.allegro.tech.servicemesh.envoycontrol.routing
2+
3+
import okhttp3.Headers.Companion.headersOf
4+
import okhttp3.Headers.Companion.toHeaders
5+
import org.assertj.core.api.Assertions.assertThat
6+
import org.junit.jupiter.api.Test
7+
import org.junit.jupiter.api.extension.RegisterExtension
8+
import pl.allegro.tech.servicemesh.envoycontrol.config.consul.ConsulExtension
9+
import pl.allegro.tech.servicemesh.envoycontrol.config.envoy.EnvoyExtension
10+
import pl.allegro.tech.servicemesh.envoycontrol.config.envoycontrol.EnvoyControlExtension
11+
import pl.allegro.tech.servicemesh.envoycontrol.config.service.HttpsEchoExtension
12+
import pl.allegro.tech.servicemesh.envoycontrol.config.service.asHttpsEchoResponse
13+
14+
class ServiceTagPreferenceIngressTest {
15+
16+
companion object {
17+
@RegisterExtension
18+
val consul = ConsulExtension()
19+
20+
@RegisterExtension
21+
val envoyControl = EnvoyControlExtension(
22+
consul = consul, mapOf(
23+
"envoy-control.envoy.snapshot.routing.service-tags.enabled" to true,
24+
"envoy-control.envoy.snapshot.routing.service-tags.preference-routing.header" to "x-service-tag-preference",
25+
"envoy-control.envoy.snapshot.routing.service-tags.preference-routing.default-preference-env" to "DEFAULT_SERVICE_TAG_PREFERENCE",
26+
"envoy-control.envoy.snapshot.routing.service-tags.preference-routing.default-preference-fallback" to "global",
27+
"envoy-control.envoy.snapshot.routing.service-tags.preference-routing.enable-for-all" to true,
28+
)
29+
)
30+
31+
@RegisterExtension
32+
val localService = HttpsEchoExtension()
33+
34+
@RegisterExtension
35+
val envoyVte22 = EnvoyExtension(envoyControl = envoyControl, localService = localService).also {
36+
it.container.withEnv("DEFAULT_SERVICE_TAG_PREFERENCE", "vte22|global")
37+
}
38+
}
39+
40+
@Test
41+
fun `should pass request x-service-tag-preference if it's more specific`() {
42+
envoyVte22.ingressOperations.callLocalService(
43+
endpoint = "/",
44+
headers = mapOf("x-service-tag-preference" to "lvte-1|vte22|global").toHeaders()
45+
).asHttpsEchoResponse().let {
46+
assertThat(it.requestHeaders).containsEntry("x-service-tag-preference", "lvte-1|vte22|global")
47+
}
48+
}
49+
50+
@Test
51+
fun `should pass default service tag preference if it's more specific than the request one`() {
52+
envoyVte22.ingressOperations.callLocalService(
53+
endpoint = "/",
54+
headers = mapOf("x-service-tag-preference" to "global").toHeaders()
55+
).asHttpsEchoResponse().let {
56+
assertThat(it.requestHeaders).containsEntry("x-service-tag-preference", "vte22|global")
57+
}
58+
}
59+
60+
@Test
61+
fun `should pass default service tag preference if request preference is absent`() {
62+
envoyVte22.ingressOperations.callLocalService(
63+
endpoint = "/",
64+
headers = headersOf()
65+
).asHttpsEchoResponse().let {
66+
assertThat(it.requestHeaders).containsEntry("x-service-tag-preference", "vte22|global")
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)