Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tag dependency #355

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
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 ->
KSmigielski marked this conversation as resolved.
Show resolved Hide resolved
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> {
KSmigielski marked this conversation as resolved.
Show resolved Hide resolved
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 })
KSmigielski marked this conversation as resolved.
Show resolved Hide resolved

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())
KSmigielski marked this conversation as resolved.
Show resolved Hide resolved
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