@@ -15,6 +15,7 @@ import pl.allegro.tech.servicemesh.envoycontrol.groups.Group
1515import pl.allegro.tech.servicemesh.envoycontrol.groups.IncomingRateLimitEndpoint
1616import pl.allegro.tech.servicemesh.envoycontrol.groups.ServicesGroup
1717import pl.allegro.tech.servicemesh.envoycontrol.groups.orDefault
18+ import pl.allegro.tech.servicemesh.envoycontrol.logger
1819import pl.allegro.tech.servicemesh.envoycontrol.services.MultiClusterState
1920import pl.allegro.tech.servicemesh.envoycontrol.services.ServiceInstance
2021import pl.allegro.tech.servicemesh.envoycontrol.services.ServiceInstances
@@ -38,6 +39,7 @@ class EnvoySnapshotFactory(
3839
3940 companion object {
4041 const val DEFAULT_HTTP_PORT = 80
42+ private val logger by logger()
4143 }
4244
4345 fun newSnapshot (
@@ -111,6 +113,7 @@ class EnvoySnapshotFactory(
111113 val removedClusters = previous - current.keys
112114 current + removedClusters
113115 }
116+
114117 false -> current
115118 }
116119 }
@@ -156,24 +159,26 @@ class EnvoySnapshotFactory(
156159 return newSnapshotForGroup
157160 }
158161
159- private fun getDomainRouteSpecifications (group : Group ): Map <DomainRoutesGrouper , Collection <RouteSpecification >> {
162+ private fun getDomainRouteSpecifications (
163+ group : Group
164+ ): Map <DomainRoutesGrouper , Collection <RouteSpecification >> {
160165 return group.proxySettings.outgoing.getDomainDependencies().groupBy(
161166 { DomainRoutesGrouper (it.getPort(), it.useSsl()) },
162167 {
163- RouteSpecification (
168+ StandardRouteSpecification (
164169 clusterName = it.getClusterName(),
165170 routeDomains = listOf (it.getRouteDomain()),
166- settings = it.settings
171+ settings = it.settings,
167172 )
168173 }
169174 )
170175 }
171176
172177 private fun getDomainPatternRouteSpecifications (group : Group ): RouteSpecification {
173- return RouteSpecification (
178+ return StandardRouteSpecification (
174179 clusterName = properties.dynamicForwardProxy.clusterName,
175180 routeDomains = group.proxySettings.outgoing.getDomainPatternDependencies().map { it.domainPattern },
176- settings = group.proxySettings.outgoing.defaultServiceSettings
181+ settings = group.proxySettings.outgoing.defaultServiceSettings,
177182 )
178183 }
179184
@@ -182,30 +187,67 @@ class EnvoySnapshotFactory(
182187 globalSnapshot : GlobalSnapshot
183188 ): Collection <RouteSpecification > {
184189 val definedServicesRoutes = group.proxySettings.outgoing.getServiceDependencies().map {
185- RouteSpecification (
190+ buildRouteSpecification (
186191 clusterName = it.service,
187192 routeDomains = listOf (it.service) + getServiceWithCustomDomain(it.service),
188- settings = it.settings
193+ settings = it.settings,
194+ group.serviceName,
195+ globalSnapshot
189196 )
190197 }
191198 return when (group) {
192199 is ServicesGroup -> {
193200 definedServicesRoutes
194201 }
202+
195203 is AllServicesGroup -> {
196204 val servicesNames = group.proxySettings.outgoing.getServiceDependencies().map { it.service }.toSet()
197205 val allServicesRoutes = globalSnapshot.allServicesNames.subtract(servicesNames).map {
198- RouteSpecification (
206+ buildRouteSpecification (
199207 clusterName = it,
200208 routeDomains = listOf (it) + getServiceWithCustomDomain(it),
201- settings = group.proxySettings.outgoing.defaultServiceSettings
209+ settings = group.proxySettings.outgoing.defaultServiceSettings,
210+ group.serviceName,
211+ globalSnapshot
202212 )
203213 }
204214 allServicesRoutes + definedServicesRoutes
205215 }
206216 }
207217 }
208218
219+ private fun buildRouteSpecification (
220+ clusterName : String ,
221+ routeDomains : List <String >,
222+ settings : DependencySettings ,
223+ serviceName : String ,
224+ globalSnapshot : GlobalSnapshot ,
225+ ): RouteSpecification {
226+ val trafficSplitting = properties.loadBalancing.trafficSplitting
227+ val weights = trafficSplitting.serviceByWeightsProperties[serviceName]
228+ val enabledForDependency = globalSnapshot.endpoints[clusterName]?.endpointsList
229+ ?.any { e -> trafficSplitting.zoneName == e.locality.zone }
230+ ? : false
231+ return if (weights != null && enabledForDependency) {
232+ logger.debug(
233+ " Building traffic splitting route spec, weights: $weights , " +
234+ " serviceName: $serviceName , clusterName: $clusterName , "
235+ )
236+ WeightRouteSpecification (
237+ clusterName,
238+ routeDomains,
239+ settings,
240+ weights
241+ )
242+ } else {
243+ StandardRouteSpecification (
244+ clusterName,
245+ routeDomains,
246+ settings
247+ )
248+ }
249+ }
250+
209251 private fun getServiceWithCustomDomain (it : String ): List <String > {
210252 return if (properties.egress.domains.isNotEmpty()) {
211253 properties.egress.domains.map { domain -> " $it$domain " }
@@ -217,7 +259,7 @@ class EnvoySnapshotFactory(
217259 private fun getServicesEndpointsForGroup (
218260 rateLimitEndpoints : List <IncomingRateLimitEndpoint >,
219261 globalSnapshot : GlobalSnapshot ,
220- egressRouteSpecifications : Collection <RouteSpecification >
262+ egressRouteSpecifications : List <RouteSpecification >
221263 ): List <ClusterLoadAssignment > {
222264 val egressLoadAssignments = egressRouteSpecifications.mapNotNull { routeSpec ->
223265 globalSnapshot.endpoints[routeSpec.clusterName]?.let { endpoints ->
@@ -226,27 +268,30 @@ class EnvoySnapshotFactory(
226268 // endpointsFactory.filterEndpoints() can use this cache to prevent computing the same
227269 // ClusterLoadAssignments many times - it may reduce MEM, CPU and latency if some serviceTags are
228270 // commonly used
229- endpointsFactory.filterEndpoints(endpoints, routeSpec.settings.routingPolicy)
271+ routeSpec.clusterName to endpointsFactory.filterEndpoints(endpoints, routeSpec.settings.routingPolicy)
230272 }
231- }
273+ }.toMap()
232274
233275 val rateLimitClusters =
234276 if (rateLimitEndpoints.isNotEmpty()) listOf (properties.rateLimit.serviceName) else emptyList()
235277 val rateLimitLoadAssignments = rateLimitClusters.mapNotNull { name -> globalSnapshot.endpoints[name] }
236-
237- return egressLoadAssignments + rateLimitLoadAssignments
278+ val secondaryLoadAssignments = endpointsFactory.getSecondaryClusterEndpoints(
279+ egressLoadAssignments,
280+ egressRouteSpecifications
281+ )
282+ return egressLoadAssignments.values.toList() + rateLimitLoadAssignments + secondaryLoadAssignments
238283 }
239284
240285 private fun newSnapshotForGroup (
241286 group : Group ,
242287 globalSnapshot : GlobalSnapshot
243288 ): Snapshot {
244-
245289 // TODO(dj): This is where serious refactoring needs to be done
246290 val egressDomainRouteSpecifications = getDomainRouteSpecifications(group)
247291 val egressServiceRouteSpecification = getServiceRouteSpecifications(group, globalSnapshot)
248292 val egressRouteSpecification = egressServiceRouteSpecification +
249- egressDomainRouteSpecifications.values.flatten().toSet() + getDomainPatternRouteSpecifications(group)
293+ egressDomainRouteSpecifications.values.flatten().toSet() +
294+ getDomainPatternRouteSpecifications(group)
250295
251296 val clusters: List <Cluster > =
252297 clustersFactory.getClustersForGroup(group, globalSnapshot)
@@ -272,7 +317,6 @@ class EnvoySnapshotFactory(
272317 )
273318 )
274319 }
275-
276320 val listeners = if (properties.dynamicListeners.enabled) {
277321 listenersFactory.createListeners(group, globalSnapshot)
278322 } else {
@@ -281,11 +325,12 @@ class EnvoySnapshotFactory(
281325
282326 // TODO(dj): endpoints depends on prerequisite of routes -> but only to extract clusterName,
283327 // which is present only in services (not domains) so it could be implemented differently.
284- val endpoints = getServicesEndpointsForGroup(group.proxySettings.incoming.rateLimitEndpoints, globalSnapshot,
285- egressRouteSpecification)
328+ val endpoints = getServicesEndpointsForGroup(
329+ group.proxySettings.incoming.rateLimitEndpoints, globalSnapshot,
330+ egressRouteSpecification
331+ )
286332
287333 val version = snapshotsVersions.version(group, clusters, endpoints, listeners, routes)
288-
289334 return createSnapshot(
290335 clusters = clusters,
291336 clustersVersion = version.clusters,
@@ -372,8 +417,21 @@ data class ClusterConfiguration(
372417 val http2Enabled : Boolean
373418)
374419
375- class RouteSpecification (
376- val clusterName : String ,
377- val routeDomains : List <String >,
378- val settings : DependencySettings
379- )
420+ sealed class RouteSpecification {
421+ abstract val clusterName: String
422+ abstract val routeDomains: List <String >
423+ abstract val settings: DependencySettings
424+ }
425+
426+ data class StandardRouteSpecification (
427+ override val clusterName : String ,
428+ override val routeDomains : List <String >,
429+ override val settings : DependencySettings ,
430+ ) : RouteSpecification()
431+
432+ data class WeightRouteSpecification (
433+ override val clusterName : String ,
434+ override val routeDomains : List <String >,
435+ override val settings : DependencySettings ,
436+ val clusterWeights : ZoneWeights ,
437+ ) : RouteSpecification()
0 commit comments