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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
## [Unreleased]

### Changed
- Add possibility to set dependency to group of services by tag mechanism
- flaky test fixed


## [0.19.26]

### Changed
Expand Down
4 changes: 2 additions & 2 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ Property

## Permissions
Property | Description | Default value
-------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------
-------------------------------------------------------------------------------------------------------------------------------------------------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ---------
**envoy-control.envoy.snapshot.incoming-permissions.enabled** | Enable incoming permissions | false
**envoy-control.envoy.snapshot.incoming-permissions.client-identity-headers** | Headers that identify the client calling the endpoint. In most cases `client-identity-header` should include `service-name-header` value to correctly identify other services in the mesh. | [ x-service-name ]
**envoy-control.envoy.snapshot.incoming-permissions.clients-allowed-to-all-endpoints** | Client names which are allowed to even call service if incoming permissions are enabled. | empty list
Expand Down Expand Up @@ -127,7 +127,7 @@ Property
**envoy-control.envoy.snapshot.outgoing-permissions.services-allowed-to-use-wildcard** | Services that are allowed to have wildcard in outgoing.dependency field | empty set
**envoy-control.envoy.snapshot.outgoing-permissions.rbac.clients-lists.default-clients-list** | List of clients which will be applied to each rbac policy, if none of the lists defined in `custom-clients-lists` have been matched | empty list
**envoy-control.envoy.snapshot.outgoing-permissions.rbac.clients-lists.custom-clients-lists** | Lists of clients which will be applied to each rbac policy, only if key for defined list is present in clients for defined endpoint | empty map

**envoy-control.envoy.snapshot.outgoing-permissions.tag-prefix** | Value that specify which tags are allowed to be used in dependencies by prefix | empty string
## Load Balancing
Property | Description | Default value
------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------
Expand Down
1 change: 1 addition & 0 deletions docs/features/permissions.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ metadata:
- service: service-b
handleInternalRedirect: true
- domain: http://www.example.com
- tag: tag-a
incoming:
endpoints:
- path: /example
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import pl.allegro.tech.servicemesh.envoycontrol.server.callbacks.MetricsDiscover
import pl.allegro.tech.servicemesh.envoycontrol.services.MultiClusterState
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.EnvoySnapshotFactory
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.NoopSnapshotChangeAuditor
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.RouteSpecificationFactory
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.SnapshotChangeAuditor
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.SnapshotUpdater
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.SnapshotsVersions
Expand Down Expand Up @@ -167,7 +168,10 @@ class ControlPlane private constructor(
val snapshotProperties = properties.envoy.snapshot
val envoySnapshotFactory = EnvoySnapshotFactory(
ingressRoutesFactory = EnvoyIngressRoutesFactory(snapshotProperties, envoyHttpFilters),
egressRoutesFactory = EnvoyEgressRoutesFactory(snapshotProperties),
egressRoutesFactory = EnvoyEgressRoutesFactory(
snapshotProperties.egress,
snapshotProperties.incomingPermissions
),
clustersFactory = EnvoyClustersFactory(snapshotProperties),
endpointsFactory = EnvoyEndpointsFactory(
snapshotProperties, ServiceTagMetadataGenerator(snapshotProperties.routing.serviceTags)
Expand All @@ -176,6 +180,7 @@ class ControlPlane private constructor(
snapshotProperties,
envoyHttpFilters
),
routeSpecificationFactory = RouteSpecificationFactory(snapshotProperties),
// Remember when LDS change we have to send RDS again
snapshotsVersions = snapshotsVersions,
properties = snapshotProperties,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,13 @@ fun Value?.toHeaderFilter(default: String? = null): HeaderFilterSettings? {
}
}

private class RawDependency(val service: String?, val domain: String?, val domainPattern: String?, val value: Value)
private class RawDependency(
val service: String?,
val domain: String?,
val domainPattern: String?,
val tag: String?,
val value: Value
)

fun Value?.toOutgoing(properties: SnapshotProperties): Outgoing {
val allServiceDependenciesIdentifier = properties.outgoingPermissions.allServicesDependencies.identifier
Expand Down Expand Up @@ -133,10 +139,19 @@ fun Value?.toOutgoing(properties: SnapshotProperties): Outgoing {
val domainPatterns = rawDependencies.filter { it.domainPattern != null }
.onEach { validateDomainPatternFormat(it) }
.map { DomainPatternDependency(it.domainPattern.orEmpty(), it.value.toSettings(defaultSettingsFromProperties)) }

val tags = rawDependencies.filter { it.tag != null }
.map {
TagDependency(
tag = it.tag!!,
settings = it.value.toSettings(allServicesDefaultSettings)
)
}
return Outgoing(
serviceDependencies = services,
domainDependencies = domains,
domainPatternDependencies = domainPatterns,
tagDependencies = tags,
defaultServiceSettings = allServicesDefaultSettings,
allServicesDependencies = allServicesDependencies != null
)
Expand All @@ -147,6 +162,7 @@ private fun toRawDependency(it: Value): RawDependency {
val service = it.field("service")?.stringValue
val domain = it.field("domain")?.stringValue
val domainPattern = it.field("domainPattern")?.stringValue
val tag = it.field("tag")?.stringValue
var count = 0
if (!service.isNullOrBlank()) {
count += 1
Expand All @@ -157,6 +173,9 @@ private fun toRawDependency(it: Value): RawDependency {
if (!domainPattern.isNullOrBlank()) {
count += 1
}
if (!tag.isNullOrBlank()) {
count += 1
}
if (count != 1) {
throw NodeMetadataValidationException(
"Define one of: 'service', 'domain' or 'domainPattern' as an outgoing dependency"
Expand All @@ -166,6 +185,7 @@ private fun toRawDependency(it: Value): RawDependency {
service = service,
domain = domain,
domainPattern = domainPattern,
tag = tag,
value = it
)
}
Expand Down Expand Up @@ -487,27 +507,30 @@ data class Outgoing(
private val serviceDependencies: List<ServiceDependency> = emptyList(),
private val domainDependencies: List<DomainDependency> = emptyList(),
private val domainPatternDependencies: List<DomainPatternDependency> = emptyList(),
private val tagDependencies: List<TagDependency> = emptyList(),
val allServicesDependencies: Boolean = false,
val defaultServiceSettings: DependencySettings = DependencySettings()
) {

// not declared in primary constructor to exclude from equals(), copy(), etc.
private val deduplicatedDomainDependencies: List<DomainDependency> = domainDependencies
.map { it.domain to it }
.toMap().values.toList()
private val deduplicatedDomainDependencies: List<DomainDependency> =
domainDependencies.associateBy { it.domain }.values.toList()

private val deduplicatedServiceDependencies: List<ServiceDependency> =
serviceDependencies.associateBy { it.service }.values.toList()

private val deduplicatedServiceDependencies: List<ServiceDependency> = serviceDependencies
.map { it.service to it }
.toMap().values.toList()
private val deduplicatedDomainPatternDependencies: List<DomainPatternDependency> =
domainPatternDependencies.associateBy { it.domainPattern }.values.toList()

private val deduplicatedDomainPatternDependencies: List<DomainPatternDependency> = domainPatternDependencies
.map { it.domainPattern to it }
.toMap().values.toList()
private val deduplicatedTagDependency: List<TagDependency> =
tagDependencies.associateBy { it.tag }.values.toList()
KSmigielski marked this conversation as resolved.
Show resolved Hide resolved

fun getDomainDependencies(): List<DomainDependency> = deduplicatedDomainDependencies
fun getServiceDependencies(): List<ServiceDependency> = deduplicatedServiceDependencies
fun getDomainPatternDependencies(): List<DomainPatternDependency> = deduplicatedDomainPatternDependencies

fun getTagDependencies(): List<TagDependency> = deduplicatedTagDependency

data class TimeoutPolicy(
val idleTimeout: Duration? = null,
val connectionIdleTimeout: Duration? = null,
Expand Down Expand Up @@ -569,6 +592,20 @@ data class DomainPatternDependency(
override fun useSsl() = DEFAULT_HTTPS_POLICY
}

data class TagDependency(
val tag: String,
val settings: DependencySettings = DependencySettings()
) : Dependency {
companion object {
private const val DEFAULT_HTTP_PORT = 80
private const val DEFAULT_HTTPS_POLICY = false
}

override fun getPort() = DEFAULT_HTTP_PORT
KSmigielski marked this conversation as resolved.
Show resolved Hide resolved

override fun useSsl() = DEFAULT_HTTPS_POLICY
}

data class DependencySettings(
val handleInternalRedirect: Boolean = false,
val timeoutPolicy: Outgoing.TimeoutPolicy = Outgoing.TimeoutPolicy(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ class AllDependenciesValidationException(serviceName: String?) : NodeMetadataVal
"Blocked service $serviceName from using all dependencies. Only defined services can use all dependencies"
)

class TagDependencyValidationException(serviceName: String?, tags: List<String>) : NodeMetadataValidationException(
"Blocked service $serviceName from using tag dependencies $tags. Only allowed tags are supported."
)

class WildcardPrincipalValidationException(serviceName: String?) : NodeMetadataValidationException(
"Blocked service $serviceName from allowing everyone in incoming permissions. " +
"Only defined services can use that."
Expand Down Expand Up @@ -99,17 +103,26 @@ class NodeMetadataValidator(
if (!properties.outgoingPermissions.enabled) {
return
}
validateEndpointPermissionsMethods(metadata)
if (hasAllServicesDependencies(metadata) && !isAllowedToHaveAllServiceDependencies(metadata)) {
throw AllDependenciesValidationException(metadata.serviceName)
}
if (properties.outgoingPermissions.tagPrefix.isNotBlank()) {
val unsupportedTags = metadata.proxySettings.outgoing.getTagDependencies()
.filter { !it.tag.startsWith(properties.outgoingPermissions.tagPrefix) }
.map { it.tag }
if (unsupportedTags.isNotEmpty()) {
throw TagDependencyValidationException(metadata.serviceName, unsupportedTags)
}
}
}

private fun validateIncomingEndpoints(metadata: NodeMetadata) {
if (!properties.incomingPermissions.enabled) {
return
}

validateEndpointPermissionsMethods(metadata)

metadata.proxySettings.incoming.endpoints.forEach { incomingEndpoint ->
val clients = incomingEndpoint.clients.map { it.name }

Expand Down
Loading