Skip to content

Commit 11de249

Browse files
authored
Merge branch 'master' into tag_dependency
2 parents 95ae0af + 9e000d3 commit 11de249

File tree

7 files changed

+208
-89
lines changed

7 files changed

+208
-89
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
88
### Changed
99
- Add possibility to set dependency to group of services by tag mechanism
1010
- flaky test fixed
11+
- remove duplicated routes
1112

1213

1314
## [0.19.26]

docs/configuration.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ Where `<selector>` is one of the following:
184184
### Outgoing traffic
185185
Property | Description | Default value
186186
--------------------------------------------------------------------------------------------------------| ----------------------------------------------------------------------------------------------------------------------------- | ---------
187+
**envoy-control.envoy.snapshot.retryPolicy.enabled** | Flag which enables default retries | true
187188
**envoy-control.envoy.snapshot.retryPolicy.numberOfRetries** | Number of retries | 1
188189
**envoy-control.envoy.snapshot.retryPolicy.hostSelectionRetryMaxAttempts** | The maximum number of times host selection will be reattempted before request being routed to last selected host | 3
189190
**envoy-control.envoy.snapshot.retryPolicy.retryHostPredicate** | Specifies a collection of RetryHostPredicates that will be consulted when selecting a host for retries | a list with one entry "envoy.retry_host_predicates.previous_hosts"

envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/groups/NodeMetadata.kt

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import io.envoyproxy.controlplane.server.exception.RequestException
88
import io.grpc.Status
99
import pl.allegro.tech.servicemesh.envoycontrol.groups.ClientWithSelector.Companion.decomposeClient
1010
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.AccessLogFiltersProperties
11+
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.CommonHttpProperties
12+
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.RetryPolicyProperties
1113
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.SnapshotProperties
1214
import pl.allegro.tech.servicemesh.envoycontrol.utils.AccessLogFilterParser
1315
import pl.allegro.tech.servicemesh.envoycontrol.utils.ComparisonFilterSettings
@@ -101,30 +103,40 @@ private class RawDependency(
101103
val value: Value
102104
)
103105

106+
private fun defaultRetryPolicy(retryPolicy: RetryPolicyProperties) = if (retryPolicy.enabled) {
107+
RetryPolicy(
108+
retryOn = retryPolicy.retryOn,
109+
numberRetries = retryPolicy.numberOfRetries,
110+
retryHostPredicate = retryPolicy.retryHostPredicate,
111+
hostSelectionRetryMaxAttempts = retryPolicy.hostSelectionRetryMaxAttempts,
112+
rateLimitedRetryBackOff = RateLimitedRetryBackOff(
113+
retryPolicy.rateLimitedRetryBackOff.resetHeaders.map { ResetHeader(it.name, it.format) }
114+
),
115+
retryBackOff = RetryBackOff(
116+
Durations.fromMillis(retryPolicy.retryBackOff.baseInterval.toMillis())
117+
),
118+
)
119+
} else {
120+
null
121+
}
122+
123+
private fun defaultTimeoutPolicy(commonHttpProperties: CommonHttpProperties) = Outgoing.TimeoutPolicy(
124+
idleTimeout = Durations.fromMillis(commonHttpProperties.idleTimeout.toMillis()),
125+
connectionIdleTimeout = Durations.fromMillis(commonHttpProperties.connectionIdleTimeout.toMillis()),
126+
requestTimeout = Durations.fromMillis(commonHttpProperties.requestTimeout.toMillis())
127+
)
128+
129+
private fun defaultDependencySettings(properties: SnapshotProperties) = DependencySettings(
130+
handleInternalRedirect = properties.egress.handleInternalRedirect,
131+
timeoutPolicy = defaultTimeoutPolicy(properties.egress.commonHttp),
132+
retryPolicy = defaultRetryPolicy(properties.retryPolicy)
133+
)
134+
104135
fun Value?.toOutgoing(properties: SnapshotProperties): Outgoing {
105136
val allServiceDependenciesIdentifier = properties.outgoingPermissions.allServicesDependencies.identifier
106137
val rawDependencies = this?.field("dependencies")?.list().orEmpty().map(::toRawDependency)
107138
val allServicesDependencies = toAllServiceDependencies(rawDependencies, allServiceDependenciesIdentifier)
108-
val defaultSettingsFromProperties = DependencySettings(
109-
handleInternalRedirect = properties.egress.handleInternalRedirect,
110-
timeoutPolicy = Outgoing.TimeoutPolicy(
111-
idleTimeout = Durations.fromMillis(properties.egress.commonHttp.idleTimeout.toMillis()),
112-
connectionIdleTimeout = Durations.fromMillis(properties.egress.commonHttp.connectionIdleTimeout.toMillis()),
113-
requestTimeout = Durations.fromMillis(properties.egress.commonHttp.requestTimeout.toMillis())
114-
),
115-
retryPolicy = RetryPolicy(
116-
retryOn = properties.retryPolicy.retryOn,
117-
numberRetries = properties.retryPolicy.numberOfRetries,
118-
retryHostPredicate = properties.retryPolicy.retryHostPredicate,
119-
hostSelectionRetryMaxAttempts = properties.retryPolicy.hostSelectionRetryMaxAttempts,
120-
rateLimitedRetryBackOff = RateLimitedRetryBackOff(
121-
properties.retryPolicy.rateLimitedRetryBackOff.resetHeaders.map { ResetHeader(it.name, it.format) }
122-
),
123-
retryBackOff = RetryBackOff(
124-
Durations.fromMillis(properties.retryPolicy.retryBackOff.baseInterval.toMillis())
125-
),
126-
)
127-
)
139+
val defaultSettingsFromProperties = defaultDependencySettings(properties)
128140
val allServicesDefaultSettings = allServicesDependencies?.value.toSettings(defaultSettingsFromProperties)
129141
val services = rawDependencies.filter { it.service != null && it.service != allServiceDependenciesIdentifier }
130142
.map {
@@ -259,27 +271,27 @@ private fun Value?.toSettings(defaultSettings: DependencySettings): DependencySe
259271
}
260272
}
261273

262-
private fun mapProtoToRetryPolicy(value: Value, defaultRetryPolicy: RetryPolicy): RetryPolicy {
274+
private fun mapProtoToRetryPolicy(value: Value, defaultRetryPolicy: RetryPolicy?): RetryPolicy {
263275
return RetryPolicy(
264-
retryOn = value.field("retryOn")?.listValue?.valuesList?.map { it.stringValue } ?: defaultRetryPolicy.retryOn,
276+
retryOn = value.field("retryOn")?.listValue?.valuesList?.map { it.stringValue } ?: defaultRetryPolicy?.retryOn,
265277
hostSelectionRetryMaxAttempts = value.field("hostSelectionRetryMaxAttempts")?.numberValue?.toLong()
266-
?: defaultRetryPolicy.hostSelectionRetryMaxAttempts,
267-
numberRetries = value.field("numberRetries")?.numberValue?.toInt() ?: defaultRetryPolicy.numberRetries,
278+
?: defaultRetryPolicy?.hostSelectionRetryMaxAttempts,
279+
numberRetries = value.field("numberRetries")?.numberValue?.toInt() ?: defaultRetryPolicy?.numberRetries,
268280
retryHostPredicate = value.field("retryHostPredicate")?.listValue?.valuesList?.map {
269281
RetryHostPredicate(it.field("name")!!.stringValue)
270-
}?.toList() ?: defaultRetryPolicy.retryHostPredicate,
282+
}?.toList() ?: defaultRetryPolicy?.retryHostPredicate,
271283
perTryTimeoutMs = value.field("perTryTimeoutMs")?.numberValue?.toLong(),
272284
retryBackOff = value.field("retryBackOff")?.structValue?.let {
273285
RetryBackOff(
274286
baseInterval = it.fieldsMap["baseInterval"]?.toDuration(),
275287
maxInterval = it.fieldsMap["maxInterval"]?.toDuration()
276288
)
277-
} ?: defaultRetryPolicy.retryBackOff,
289+
} ?: defaultRetryPolicy?.retryBackOff,
278290
rateLimitedRetryBackOff = value.field("rateLimitedRetryBackOff")?.structValue?.let {
279291
RateLimitedRetryBackOff(
280292
it.fieldsMap["resetHeaders"]?.listValue?.valuesList?.mapNotNull(::mapProtoToResetHeader)
281293
)
282-
} ?: defaultRetryPolicy.rateLimitedRetryBackOff,
294+
} ?: defaultRetryPolicy?.rateLimitedRetryBackOff,
283295
retryableStatusCodes = value.field("retryableStatusCodes")?.listValue?.valuesList?.map {
284296
it.numberValue.toInt()
285297
},
@@ -610,11 +622,11 @@ data class DependencySettings(
610622
val handleInternalRedirect: Boolean = false,
611623
val timeoutPolicy: Outgoing.TimeoutPolicy = Outgoing.TimeoutPolicy(),
612624
val rewriteHostHeader: Boolean = false,
613-
val retryPolicy: RetryPolicy = RetryPolicy()
625+
val retryPolicy: RetryPolicy? = RetryPolicy()
614626
)
615627

616628
data class RetryPolicy(
617-
val retryOn: List<String> = emptyList(),
629+
val retryOn: List<String>? = emptyList(),
618630
val hostSelectionRetryMaxAttempts: Long? = null,
619631
val numberRetries: Int? = null,
620632
val retryHostPredicate: List<RetryHostPredicate>? = null,

envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/SnapshotProperties.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ data class RateLimitProperties(
373373
)
374374

375375
data class RetryPolicyProperties(
376+
var enabled: Boolean = true,
376377
var retryOn: List<String> = emptyList(),
377378
var numberOfRetries: Int = 1,
378379
var hostSelectionRetryMaxAttempts: Long = 3,

envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/routes/EnvoyEgressRoutesFactory.kt

Lines changed: 52 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ class EnvoyEgressRoutesFactory(
7474
.setValue("%UPSTREAM_REMOTE_ADDRESS%").build()
7575
)
7676

77+
private val defaultRouteMatch = RouteMatch
78+
.newBuilder()
79+
.setPrefix("/")
80+
.build()
81+
7782
/**
7883
* @see TestResources.createRoute
7984
*/
@@ -86,12 +91,7 @@ class EnvoyEgressRoutesFactory(
8691
val virtualHosts = routes
8792
.filter { it.routeDomains.isNotEmpty() }
8893
.map { routeSpecification ->
89-
addMultipleRoutes(
90-
VirtualHost.newBuilder()
91-
.setName(routeSpecification.clusterName)
92-
.addAllDomains(routeSpecification.routeDomains),
93-
routeSpecification
94-
).build()
94+
buildEgressRoute(routeSpecification)
9595
}
9696

9797
var routeConfiguration = RouteConfiguration.newBuilder()
@@ -122,37 +122,55 @@ class EnvoyEgressRoutesFactory(
122122
return routeConfiguration.build()
123123
}
124124

125-
private fun addMultipleRoutes(
126-
addAllDomains: VirtualHost.Builder,
127-
routeSpecification: RouteSpecification
128-
): VirtualHost.Builder {
129-
routeSpecification.settings.retryPolicy.let {
130-
buildRouteForRetryPolicy(addAllDomains, routeSpecification)
125+
private fun buildEgressRoute(routeSpecification: RouteSpecification): VirtualHost {
126+
val virtualHost = VirtualHost.newBuilder()
127+
.setName(routeSpecification.clusterName)
128+
.addAllDomains(routeSpecification.routeDomains)
129+
val retryPolicy = routeSpecification.settings.retryPolicy
130+
if (retryPolicy != null) {
131+
buildEgressRouteWithRetryPolicy(virtualHost, retryPolicy, routeSpecification)
132+
} else {
133+
virtualHost.addRoutes(
134+
Route.newBuilder()
135+
.setMatch(defaultRouteMatch)
136+
.setRoute(createRouteAction(routeSpecification, shouldAddRetryPolicy = false))
137+
.build()
138+
)
131139
}
132-
buildDefaultRoute(addAllDomains, routeSpecification)
133-
return addAllDomains
140+
return virtualHost.build()
134141
}
135142

136-
private fun buildRouteForRetryPolicy(
137-
addAllDomains: VirtualHost.Builder,
143+
private fun buildEgressRouteWithRetryPolicy(
144+
virtualHost: VirtualHost.Builder,
145+
retryPolicy: EnvoyControlRetryPolicy,
138146
routeSpecification: RouteSpecification
139-
): VirtualHost.Builder? {
140-
val regexAsAString = routeSpecification.settings.retryPolicy.methods?.joinToString(separator = "|")
141-
val routeMatchBuilder = RouteMatch
142-
.newBuilder()
143-
.setPrefix("/")
144-
.also { routeMatcher ->
145-
regexAsAString?.let {
146-
routeMatcher.addHeaders(buildMethodHeaderMatcher(it))
147-
}
148-
}
149-
150-
return addAllDomains.addRoutes(
151-
Route.newBuilder()
152-
.setMatch(routeMatchBuilder.build())
153-
.setRoute(createRouteAction(routeSpecification, shouldAddRetryPolicy = true))
147+
): VirtualHost.Builder {
148+
return if (retryPolicy.methods != null) {
149+
val regexAsAString = retryPolicy.methods.joinToString(separator = "|")
150+
val retryRouteMatch = RouteMatch
151+
.newBuilder()
152+
.setPrefix("/")
153+
.addHeaders(buildMethodHeaderMatcher(regexAsAString))
154154
.build()
155-
)
155+
virtualHost.addRoutes(
156+
Route.newBuilder()
157+
.setMatch(retryRouteMatch)
158+
.setRoute(createRouteAction(routeSpecification, shouldAddRetryPolicy = true))
159+
.build()
160+
).addRoutes(
161+
Route.newBuilder()
162+
.setMatch(defaultRouteMatch)
163+
.setRoute(createRouteAction(routeSpecification, shouldAddRetryPolicy = false))
164+
.build()
165+
)
166+
} else {
167+
virtualHost.addRoutes(
168+
Route.newBuilder()
169+
.setMatch(defaultRouteMatch)
170+
.setRoute(createRouteAction(routeSpecification, shouldAddRetryPolicy = true))
171+
.build()
172+
)
173+
}
156174
}
157175

158176
private fun buildMethodHeaderMatcher(regexAsAString: String) = HeaderMatcher.newBuilder()
@@ -164,23 +182,6 @@ class EnvoyEgressRoutesFactory(
164182
.build()
165183
)
166184

167-
private fun buildDefaultRoute(
168-
addAllDomains: VirtualHost.Builder,
169-
routeSpecification: RouteSpecification
170-
) {
171-
addAllDomains.addRoutes(
172-
Route.newBuilder()
173-
.setMatch(
174-
RouteMatch.newBuilder()
175-
.setPrefix("/")
176-
.build()
177-
)
178-
.setRoute(
179-
createRouteAction(routeSpecification)
180-
).build()
181-
)
182-
}
183-
184185
/**
185186
* @see TestResources.createRoute
186187
*/
@@ -196,11 +197,7 @@ class EnvoyEgressRoutesFactory(
196197
.addAllDomains(routeSpecification.routeDomains)
197198
.addRoutes(
198199
Route.newBuilder()
199-
.setMatch(
200-
RouteMatch.newBuilder()
201-
.setPrefix("/")
202-
.build()
203-
)
200+
.setMatch(defaultRouteMatch)
204201
.setRoute(
205202
createRouteAction(routeSpecification)
206203
).build()
@@ -254,7 +251,7 @@ class RequestPolicyMapper private constructor() {
254251
return retryPolicy?.let { policy ->
255252
val retryPolicyBuilder = RetryPolicy.newBuilder()
256253

257-
policy.retryOn.let { retryPolicyBuilder.setRetryOn(it.joinToString { joined -> joined }) }
254+
policy.retryOn?.let { retryPolicyBuilder.setRetryOn(it.joinToString { joined -> joined }) }
258255
policy.hostSelectionRetryMaxAttempts?.let {
259256
retryPolicyBuilder.setHostSelectionRetryMaxAttempts(it)
260257
}

envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/groups/RoutesAssertions.kt

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ fun RouteConfiguration.hasSingleVirtualHostThat(condition: VirtualHost.() -> Uni
2222
return this
2323
}
2424

25+
fun RouteConfiguration.hasVirtualHostThat(name: String, condition: VirtualHost.() -> Unit): RouteConfiguration {
26+
condition(this.virtualHostsList.find { it.name == name }!!)
27+
return this
28+
}
29+
2530
fun RouteConfiguration.hasRequestHeaderToAdd(key: String, value: String): RouteConfiguration {
2631
assertThat(this.requestHeadersToAddList).anySatisfy {
2732
assertThat(it.header.key).isEqualTo(key)
@@ -136,15 +141,22 @@ fun Route.matchingOnPath(path: String): Route {
136141
return this
137142
}
138143

139-
fun Route.matchingOnHeader(name: String, value: String): Route {
144+
fun Route.matchingOnHeader(name: String, value: String, isRegex: Boolean = false): Route {
145+
val matcher = RegexMatcher.newBuilder().setRegex(value)
146+
.setGoogleRe2(RegexMatcher.GoogleRE2.getDefaultInstance())
147+
.build()
140148
assertThat(this.match.headersList).anyMatch {
141-
it.name == name && it.exactMatch == value
149+
if (isRegex) {
150+
it.name == name && it.safeRegexMatch == matcher
151+
} else {
152+
it.name == name && it.exactMatch == value
153+
}
142154
}
143155
return this
144156
}
145157

146-
fun Route.matchingOnMethod(method: String): Route {
147-
return this.matchingOnHeader(":method", method)
158+
fun Route.matchingOnMethod(method: String, isRegex: Boolean = false): Route {
159+
return this.matchingOnHeader(":method", method, isRegex)
148160
}
149161

150162
fun Route.matchingOnAnyMethod(): Route {
@@ -207,6 +219,10 @@ fun Route.hasNoRetryPolicy() {
207219
assertThat(this.route.retryPolicy).isEqualTo(RetryPolicy.newBuilder().build())
208220
}
209221

222+
fun Route.hasRetryPolicy() {
223+
assertThat(this.route.hasRetryPolicy()).isTrue()
224+
}
225+
210226
fun Route.ingressRoute() {
211227
this.matchingOnPrefix("/")
212228
.publicAccess()

0 commit comments

Comments
 (0)