Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,9 @@ class NodeMetadataValidator(
val unsupportedTags = metadata.proxySettings.outgoing.getTagDependencies()
.filter { !it.tag.startsWith(properties.outgoingPermissions.tagPrefix) }
.map { it.tag }
throw TagDependencyValidationException(metadata.serviceName, unsupportedTags)
if (unsupportedTags.isNotEmpty()) {
throw TagDependencyValidationException(metadata.serviceName, unsupportedTags)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,9 @@ class EnvoySnapshotFactory(
}
}

fun getSnapshotForGroup(group: Group, globalSnapshot: GlobalSnapshot): Snapshot {
val groupSample = Timer.start(meterRegistry)

val newSnapshotForGroup = newSnapshotForGroup(group, globalSnapshot)
groupSample.stop(meterRegistry.timer("snapshot-factory.get-snapshot-for-group.time"))
return newSnapshotForGroup
}
fun getSnapshotForGroup(group: Group, globalSnapshot: GlobalSnapshot): Snapshot =
meterRegistry.timer("snapshot-factory.get-snapshot-for-group.time")
.record<Snapshot> { newSnapshotForGroup(group, globalSnapshot) }

private fun getServicesEndpointsForGroup(
rateLimitEndpoints: List<IncomingRateLimitEndpoint>,
Expand Down Expand Up @@ -333,39 +329,31 @@ class RouteSpecificationFactory(
)
}
val servicesNames = group.proxySettings.outgoing.getServiceDependencies().map { it.service }.toSet()
val definedTagsRoutes = group.proxySettings.outgoing.getTagDependencies().flatMap { tagDependency ->
globalSnapshot.tags
.filterKeys { !servicesNames.contains(it) }
.filterValues { it.contains(tagDependency.tag) }
.map { RouteSpecification(
clusterName = it.key,
routeDomains = listOf(it.key) + getServiceWithCustomDomain(it.key),
settings = tagDependency.settings
) }
}.fold(emptyMap<String, RouteSpecification>()) {
acc, routeSpecification ->
if (acc.containsKey(routeSpecification.clusterName)) {
acc
} else {
acc.plus(routeSpecification.clusterName to routeSpecification)
}
}
val definedTagsRoutes = globalSnapshot
.getTagsForDependency(group.proxySettings.outgoing) { servicesName, tagDependency ->
RouteSpecification(
clusterName = servicesName,
routeDomains = listOf(servicesName) + getServiceWithCustomDomain(servicesName),
settings = tagDependency.settings
)
}.distinctBy { it.clusterName }

return when (group) {
is ServicesGroup -> {
definedServicesRoutes + definedTagsRoutes.values
definedServicesRoutes + definedTagsRoutes
}
is AllServicesGroup -> {
val allServicesRoutes = globalSnapshot.allServicesNames
.subtract(servicesNames)
.subtract(definedTagsRoutes.keys)
.subtract(definedTagsRoutes.map { it.clusterName }.toSet())
.map {
RouteSpecification(
clusterName = it,
routeDomains = listOf(it) + getServiceWithCustomDomain(it),
settings = group.proxySettings.outgoing.defaultServiceSettings
)
}
allServicesRoutes + definedServicesRoutes + definedTagsRoutes.values
allServicesRoutes + definedServicesRoutes + definedTagsRoutes
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package pl.allegro.tech.servicemesh.envoycontrol.snapshot
import io.envoyproxy.controlplane.cache.SnapshotResources
import io.envoyproxy.envoy.config.cluster.v3.Cluster
import io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment
import pl.allegro.tech.servicemesh.envoycontrol.groups.Outgoing
import pl.allegro.tech.servicemesh.envoycontrol.groups.TagDependency

data class GlobalSnapshot(
val clusters: Map<String, Cluster>,
Expand All @@ -11,7 +13,18 @@ data class GlobalSnapshot(
val clusterConfigurations: Map<String, ClusterConfiguration>,
val securedClusters: Map<String, Cluster>,
val tags: Map<String, Set<String>>
)
) {
fun <T> getTagsForDependency(
outgoing: Outgoing,
mapper: (String, TagDependency) -> T): List<T> {
val serviceDependencies = outgoing.getServiceDependencies().map { it.service }.toSet()
return outgoing.getTagDependencies().flatMap { tagDependency ->
tags.filterKeys { !serviceDependencies.contains(it) }
.filterValues { it.contains(tagDependency.tag) }
.map { mapper(it.key, tagDependency) }
}
}
}

@Suppress("LongParameterList")
fun globalSnapshot(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,36 +191,30 @@ class EnvoyClustersFactory(
globalSnapshot.clusters
}

val serviceDependencies = group.proxySettings.outgoing.getServiceDependencies().mapNotNull {
createClusterForGroup(it.settings, clusters[it.service])
}
val servicesNames = group.proxySettings.outgoing.getServiceDependencies().map { it.service }.toSet()

val tagDependencies = group.proxySettings.outgoing.getTagDependencies().flatMap { tagDependency ->
globalSnapshot.tags
.filterKeys { !servicesNames.contains(it) }
.filterValues { it.contains(tagDependency.tag) }
.map {
it.key to createClusterForGroup(tagDependency.settings, clusters[it.key])
}.toMap().asIterable()
}.fold(emptyMap<String, Cluster>()) {
acc, entry ->
if (acc.containsKey(entry.key) || entry.value == null) {
acc
} else {
acc.plus(entry.key to entry.value!!)
}
}
val serviceSettings = group.proxySettings.outgoing.getServiceDependencies()
.associateBy({ it.service }, { it.settings })

val serviceFromTagSettings = globalSnapshot
.getTagsForDependency(group.proxySettings.outgoing) { serviceName, tagDependency ->
serviceName to tagDependency.settings
}.reversed().associateBy({ it.first }, { it.second })

val clustersForGroup = when (group) {
is ServicesGroup -> serviceDependencies + tagDependencies.values
is ServicesGroup -> serviceSettings.mapNotNull {
createClusterForGroup(it.value, clusters[it.key])
} + serviceFromTagSettings.mapNotNull {
createClusterForGroup(it.value, clusters[it.key])
}
is AllServicesGroup -> {
globalSnapshot.allServicesNames
.subtract(servicesNames)
.subtract(tagDependencies.keys)
.mapNotNull {
createClusterForGroup(group.proxySettings.outgoing.defaultServiceSettings, clusters[it])
} + serviceDependencies + tagDependencies.values
val settings = serviceSettings[it] ?: serviceFromTagSettings[it]
if (settings != null && settings.timeoutPolicy.connectionIdleTimeout != null) {
createClusterForGroup(settings, clusters[it])
} else {
createClusterForGroup(group.proxySettings.outgoing.defaultServiceSettings, clusters[it])
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import pl.allegro.tech.servicemesh.envoycontrol.services.Locality
import pl.allegro.tech.servicemesh.envoycontrol.services.MultiClusterState
import pl.allegro.tech.servicemesh.envoycontrol.services.ServiceInstance
import pl.allegro.tech.servicemesh.envoycontrol.services.ServiceInstances
import pl.allegro.tech.servicemesh.envoycontrol.services.ServiceName
import pl.allegro.tech.servicemesh.envoycontrol.services.ServicesState
import java.util.concurrent.ConcurrentHashMap

Expand Down Expand Up @@ -54,7 +53,7 @@ class EnvoySnapshotFactoryTest {
val tagPrefix = ""
val serviceTagsCluster1 = mapOf("abc" to setOf("uws", "poc"), "xyz" to setOf("uj"), "qwerty" to setOf())
val serviceTagsCluster2 = mapOf("abc" to setOf("lkj"), "xyz" to setOf(), "qwerty" to setOf("ban"))
val state: MultiClusterState = MultiClusterState(listOf(
val state = MultiClusterState(listOf(
ClusterState(serviceState(serviceTagsCluster1), Locality.LOCAL, "cluster"),
ClusterState(serviceState(serviceTagsCluster2), Locality.LOCAL, "cluster2")
))
Expand All @@ -71,13 +70,9 @@ class EnvoySnapshotFactoryTest {
}

private fun serviceState(servicesTags: Map<String, Set<String>>): ServicesState {
val map: ConcurrentHashMap<ServiceName, ServiceInstances> = ConcurrentHashMap()
servicesTags.forEach {
val instances = listOf(1, 2, 3).map { id ->
ServiceInstance("${it.key}-$id", it.value, null, null)
}.toSet()
map[it.key] = ServiceInstances(it.key, instances)
}
return ServicesState(map)
val servicesInstances = servicesTags.map {
it.key to setOf(ServiceInstance("${it.key}-1", it.value, null, null))
}.associateTo(ConcurrentHashMap()) { it.first to ServiceInstances(it.first, it.second) }
return ServicesState(servicesInstances)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1310,7 +1310,10 @@ class SnapshotUpdaterTest {
private fun snapshotFactory(snapshotProperties: SnapshotProperties, meterRegistry: MeterRegistry) =
EnvoySnapshotFactory(
ingressRoutesFactory = EnvoyIngressRoutesFactory(snapshotProperties),
egressRoutesFactory = EnvoyEgressRoutesFactory(snapshotProperties),
egressRoutesFactory = EnvoyEgressRoutesFactory(
snapshotProperties.egress,
snapshotProperties.incomingPermissions
),
clustersFactory = EnvoyClustersFactory(snapshotProperties),
endpointsFactory = EnvoyEndpointsFactory(
snapshotProperties, ServiceTagMetadataGenerator(snapshotProperties.routing.serviceTags)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,9 @@ class EnvoyClusterFactoryTest {
)
)
val services = listOf("service-A", "service-B", "service-C")
val globalSnapshot = GlobalSnapshot(
clusters = createClusters(properties, services),
allServicesNames = services.toSet(),
endpoints = emptyMap(),
clusterConfigurations = emptyMap(),
securedClusters = emptyMap(),
tags = emptyMap()
val globalSnapshot = buildGlobalSnapshot(
services = services,
properties = properties
)

// when
Expand All @@ -77,12 +73,9 @@ class EnvoyClusterFactoryTest {
)
)
val services = listOf("service-A", "service-B", "service-C")
val globalSnapshot = GlobalSnapshot(
clusters = createClusters(properties, services),
allServicesNames = services.toSet(),
endpoints = emptyMap(),
clusterConfigurations = emptyMap(),
securedClusters = emptyMap(),
val globalSnapshot = buildGlobalSnapshot(
services = services,
properties = properties,
tags = mapOf("service-A" to setOf("tag"), "service-C" to setOf("tag"))
)

Expand All @@ -101,7 +94,7 @@ class EnvoyClusterFactoryTest {
}

@Test
fun `should return clusters from tag dependency with `() {
fun `should return clusters from tag dependency with keeping order`() {
// given
val properties = SnapshotProperties()
val factory = EnvoyClustersFactory(properties)
Expand All @@ -115,12 +108,9 @@ class EnvoyClusterFactoryTest {
)
)
val services = listOf("service-A", "service-B", "service-C")
val globalSnapshot = GlobalSnapshot(
clusters = createClusters(properties, services),
allServicesNames = services.toSet(),
endpoints = emptyMap(),
clusterConfigurations = emptyMap(),
securedClusters = emptyMap(),
val globalSnapshot = buildGlobalSnapshot(
services = services,
properties = properties,
tags = mapOf("service-A" to setOf("tag-1"), "service-C" to setOf("tag-1", "tag-2"), "service-B" to setOf("tag-2"))
)

Expand Down Expand Up @@ -153,19 +143,16 @@ class EnvoyClusterFactoryTest {
serviceDependency("service-A", 44)
),
tagDependencies = listOf(
tagDependency("tag-1", 33)
tagDependency("tag", 33)
)
)
)
)
val services = listOf("service-A", "service-B", "service-C")
val globalSnapshot = GlobalSnapshot(
clusters = createClusters(properties, services),
allServicesNames = services.toSet(),
endpoints = emptyMap(),
clusterConfigurations = emptyMap(),
securedClusters = emptyMap(),
tags = mapOf("service-A" to setOf("tag-1"), "service-C" to setOf("tag-1"))
val globalSnapshot = buildGlobalSnapshot(
services = services,
properties = properties,
tags = mapOf("service-A" to setOf("tag"), "service-C" to setOf("tag"))
)

// when
Expand All @@ -189,13 +176,9 @@ class EnvoyClusterFactoryTest {
val factory = EnvoyClustersFactory(properties)
val group = allServicesGroup
val services = listOf("service-A", "service-B", "service-C")
val globalSnapshot = GlobalSnapshot(
clusters = createClusters(properties, services),
allServicesNames = services.toSet(),
endpoints = emptyMap(),
clusterConfigurations = emptyMap(),
securedClusters = emptyMap(),
tags = emptyMap()
val globalSnapshot = buildGlobalSnapshot(
services = services,
properties = properties
)

// when
Expand All @@ -222,13 +205,9 @@ class EnvoyClusterFactoryTest {
)
)
val services = listOf("service-A", "service-B", "service-C")
val globalSnapshot = GlobalSnapshot(
clusters = createClusters(properties, services),
allServicesNames = services.toSet(),
endpoints = emptyMap(),
clusterConfigurations = emptyMap(),
securedClusters = emptyMap(),
tags = emptyMap()
val globalSnapshot = buildGlobalSnapshot(
services = services,
properties = properties
)

// when
Expand Down Expand Up @@ -257,14 +236,10 @@ class EnvoyClusterFactoryTest {
)
)
val services = listOf("service-A", "service-B", "service-C")
val globalSnapshot = GlobalSnapshot(
clusters = createClusters(properties, services),
allServicesNames = services.toSet(),
endpoints = emptyMap(),
clusterConfigurations = emptyMap(),
securedClusters = emptyMap(),
tags = mapOf("service-A" to setOf("tag"))
)
val globalSnapshot = buildGlobalSnapshot(
services = services,
properties = properties,
tags = mapOf("service-A" to setOf("tag")))

// when
val clustersForGroup = factory.getClustersForGroup(group, globalSnapshot)
Expand All @@ -279,6 +254,19 @@ class EnvoyClusterFactoryTest {
}
}

private fun buildGlobalSnapshot(
services: Collection<String> = emptyList(),
properties: SnapshotProperties = SnapshotProperties(),
tags: Map<String, Set<String>> = emptyMap()
) = GlobalSnapshot(
clusters = createClusters(properties, services.toList()),
allServicesNames = services.toSet(),
endpoints = emptyMap(),
clusterConfigurations = emptyMap(),
securedClusters = emptyMap(),
tags = tags
)

private fun List<Cluster>.assertServiceCluster(name: String): ObjectAssert<Cluster> {
return assertThat(this)
.filteredOn { it.name == name }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class EnvoyListenersFactoryTest {
}

@Test
fun `should return egress http proxy virtual listener listener with service dependency`() {
fun `should return egress http proxy virtual listener with service dependency`() {
// given
val properties = SnapshotProperties()
val factory = EnvoyListenersFactory(properties, EnvoyHttpFilters(emptyList(), emptyList()))
Expand Down Expand Up @@ -66,7 +66,7 @@ class EnvoyListenersFactoryTest {
}

@Test
fun `should return egress http proxy virtual listener listener with tag dependency`() {
fun `should return egress http proxy virtual listener with tag dependency`() {
// given
val properties = SnapshotProperties()
val factory = EnvoyListenersFactory(properties, EnvoyHttpFilters(emptyList(), emptyList()))
Expand Down
Loading