Skip to content

Commit

Permalink
custom routes
Browse files Browse the repository at this point in the history
  • Loading branch information
KSmigielski committed Oct 17, 2024
1 parent f43cedc commit 980e439
Show file tree
Hide file tree
Showing 21 changed files with 286 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ class RoutesProperties {
var admin = AdminRouteProperties()
var status = StatusRouteProperties()
var authorization = AuthorizationProperties()
var customs = emptyList<CustomRuteProperties>()
}

class ClusterOutlierDetectionProperties {
Expand Down Expand Up @@ -260,6 +261,12 @@ class AuthorizationProperties {
var unauthorizedResponseMessage = "You have to be authorized"
}

class CustomRuteProperties {
var enabled = false
var cluster = "custom"
var path = StringMatcher()
}

class ServiceTagsProperties {
var enabled = false
var metadataKey = "tag"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ class AdminRoutesFactory(
HttpMethod.POST
)

private val adminRoutes = guardAccessWithDisableHeader() +
generateSecuredAdminRoutes() +
listOfNotNull(
adminPostRoute.authorized.takeIf { properties.admin.publicAccessEnabled },
adminPostRoute.unauthorized.takeIf { properties.admin.publicAccessEnabled },
adminRoute.takeIf { properties.admin.publicAccessEnabled },
adminRedirectRoute.takeIf { properties.admin.publicAccessEnabled }
)

private fun generateSecuredAdminRoutes(): List<Route> {
return properties.admin.securedPaths
.flatMap {
Expand All @@ -55,16 +64,7 @@ class AdminRoutesFactory(
}
}

fun generateAdminRoutes(): List<Route> {
return guardAccessWithDisableHeader() +
generateSecuredAdminRoutes() +
listOfNotNull(
adminPostRoute.authorized.takeIf { properties.admin.publicAccessEnabled },
adminPostRoute.unauthorized.takeIf { properties.admin.publicAccessEnabled },
adminRoute.takeIf { properties.admin.publicAccessEnabled },
adminRedirectRoute.takeIf { properties.admin.publicAccessEnabled }
)
}
fun generateAdminRoutes() = adminRoutes

private fun createAuthorizedRoute(
pathPrefix: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package pl.allegro.tech.servicemesh.envoycontrol.snapshot.resource.routes

import io.envoyproxy.envoy.config.route.v3.Route
import io.envoyproxy.envoy.config.route.v3.RouteAction
import io.envoyproxy.envoy.config.route.v3.RouteMatch
import io.envoyproxy.envoy.type.matcher.v3.RegexMatcher
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.RoutesProperties
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.StringMatcherType

class CustomRoutesFactory(properties: RoutesProperties) {

val routes: List<Route> = properties.customs.filter { it.enabled }.map {
val matcher = when(it.path.type) {
StringMatcherType.REGEX -> RouteMatch.newBuilder()
.setSafeRegex(
RegexMatcher.newBuilder()
.setRegex(it.path.value)
.setGoogleRe2(RegexMatcher.GoogleRE2.getDefaultInstance())
)
StringMatcherType.EXACT -> RouteMatch.newBuilder().setPath(it.path.value)
StringMatcherType.PREFIX -> RouteMatch.newBuilder().setPrefix(it.path.value)
}
RouteMatch.newBuilder()
Route.newBuilder()
.setName(it.cluster)
.setRoute(RouteAction.newBuilder()
.setCluster(it.cluster)
)
.setMatch(matcher)
.build()
}

fun generateCustomRoutes() = routes


}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ class EnvoyIngressRoutesFactory(
envoyHttpFilters: EnvoyHttpFilters = EnvoyHttpFilters.emptyFilters,
private val currentZone: String
) {

private val adminRoutesFactory = AdminRoutesFactory(properties.routes)
private val customRoutesFactory = CustomRoutesFactory(properties.routes)
private val allClients = setOf(
ClientWithSelector.create(properties.incomingPermissions.tlsAuthentication.wildcardClientIdentifier)
)
Expand Down Expand Up @@ -231,13 +232,12 @@ class EnvoyIngressRoutesFactory(
emptyList()
}

val adminRoutesFactory = AdminRoutesFactory(properties.routes)

val virtualHost = VirtualHost.newBuilder()
.setName("secured_local_service")
.addDomains("*")
.addAllVirtualClusters(virtualClusters)
.addAllRoutes(adminRoutesFactory.generateAdminRoutes())
.addAllRoutes(customRoutesFactory.generateCustomRoutes())
.addAllRoutes(generateSecuredIngressRoutes(proxySettings, group))
.also {
if (properties.localService.retryPolicy.default.enabled) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,20 @@ import pl.allegro.tech.servicemesh.envoycontrol.groups.hasStatusVirtualClusters
import pl.allegro.tech.servicemesh.envoycontrol.groups.ingressRoute
import pl.allegro.tech.servicemesh.envoycontrol.groups.matchingOnAnyMethod
import pl.allegro.tech.servicemesh.envoycontrol.groups.matchingOnMethod
import pl.allegro.tech.servicemesh.envoycontrol.groups.matchingOnPrefix
import pl.allegro.tech.servicemesh.envoycontrol.groups.matchingRetryPolicy
import pl.allegro.tech.servicemesh.envoycontrol.groups.pathMatcher
import pl.allegro.tech.servicemesh.envoycontrol.groups.prefixPathMatcher
import pl.allegro.tech.servicemesh.envoycontrol.groups.publicAccess
import pl.allegro.tech.servicemesh.envoycontrol.groups.toCluster
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.CustomRuteProperties
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.EndpointMatch
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.LocalRetryPoliciesProperties
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.LocalRetryPolicyProperties
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.SecuredRoute
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.SnapshotProperties
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.StringMatcher
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.StringMatcherType
import java.time.Duration

internal class EnvoyIngressRoutesFactoryTest {
Expand Down Expand Up @@ -95,6 +101,14 @@ internal class EnvoyIngressRoutesFactoryTest {
pathPrefix = "/config_dump"
method = "GET"
})
routes.customs = listOf(CustomRuteProperties().apply {
enabled = true
cluster = "wrapper"
path = StringMatcher().apply {
type = StringMatcherType.PREFIX
value = "/status/wrapper/"
}
})
}, currentZone = currentZone)
val responseTimeout = Durations.fromSeconds(777)
val idleTimeout = Durations.fromSeconds(61)
Expand Down Expand Up @@ -144,6 +158,12 @@ internal class EnvoyIngressRoutesFactoryTest {
hasOneDomain("*")
hasOnlyRoutesInOrder(
*adminRoutes,
{
matchingOnPrefix("/status/wrapper/")
.toCluster("wrapper")
.publicAccess()

},
{
ingressRoute()
matchingOnMethod("GET")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package pl.allegro.tech.servicemesh.envoycontrol

import okhttp3.Headers.Companion.toHeaders
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody
import okhttp3.Response
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.Arguments
import org.junit.jupiter.params.provider.MethodSource
import pl.allegro.tech.servicemesh.envoycontrol.config.consul.ConsulExtension
import pl.allegro.tech.servicemesh.envoycontrol.config.envoy.EnvoyExtension
import pl.allegro.tech.servicemesh.envoycontrol.config.envoycontrol.EnvoyControlExtension
import pl.allegro.tech.servicemesh.envoycontrol.config.service.EchoServiceExtension
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.CustomRuteProperties
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.SecuredRoute
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.StringMatcher
import pl.allegro.tech.servicemesh.envoycontrol.snapshot.StringMatcherType
import java.util.stream.Stream

internal class CustomRouteTest {
companion object {

private val properties = mapOf(
"envoy-control.envoy.snapshot.routes.customs" to listOf(CustomRuteProperties().apply {
enabled = true
cluster = "wrapper"
path = StringMatcher().apply {
type = StringMatcherType.PREFIX
value = "/status/wrapper/"
}
}),
)

@JvmField
@RegisterExtension
val consul = ConsulExtension()

@JvmField
@RegisterExtension
val envoyControl = EnvoyControlExtension(consul, properties)

@JvmField
@RegisterExtension
val service = EchoServiceExtension()

@JvmField
@RegisterExtension
val wrapper = EchoServiceExtension()

@JvmField
@RegisterExtension
val envoy = EnvoyExtension(envoyControl, service, wrapperService = wrapper)
}
@Test
fun `should redirect to wrapper`() {
// when
val response = envoy.ingressOperations.callLocalService(
endpoint = "/status/wrapper/prometheus"
)
// then
assertThat(response.isSuccessful).isTrue()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ class EnvoyContainer(
private val envoyControl1XdsPort: Int,
private val envoyControl2XdsPort: Int = envoyControl1XdsPort,
private val logLevel: String = "info",
image: String = DEFAULT_IMAGE
image: String = DEFAULT_IMAGE,
private val wrapperServiceIp: () -> String = {"127.0.0.1"},
) : SSLGenericContainer<EnvoyContainer>(
dockerfileBuilder = DockerfileBuilder()
.from(image)
Expand Down Expand Up @@ -72,14 +73,15 @@ class EnvoyContainer(

withCommand(
"/bin/sh", "/usr/local/bin/launch_envoy.sh",
Integer.toString(envoyControl1XdsPort),
Integer.toString(envoyControl2XdsPort),
envoyControl1XdsPort.toString(),
envoyControl2XdsPort.toString(),
CONFIG_DEST,
localServiceIp(),
config.trustedCa,
config.certificateChain,
config.privateKey,
config.serviceName,
wrapperServiceIp(),
"--config-yaml", config.configOverride,
"-l", logLevel
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import java.util.concurrent.TimeUnit
class EnvoyExtension(
private val envoyControl: EnvoyControlExtensionBase,
private val localService: ServiceExtension<*>? = null,
config: EnvoyConfig = RandomConfigFile
config: EnvoyConfig = RandomConfigFile,
private val wrapperService: ServiceExtension<*>? = null
) : BeforeAllCallback, AfterAllCallback, AfterEachCallback {

companion object {
Expand All @@ -31,7 +32,8 @@ class EnvoyExtension(
val container: EnvoyContainer = EnvoyContainer(
config,
{ localService?.container()?.ipAddress() ?: "127.0.0.1" },
envoyControl.app.grpcPort
envoyControl.app.grpcPort,
wrapperServiceIp = { wrapperService?.container()?.ipAddress() ?: "127.0.0.1" },
).withNetwork(Network.SHARED)

val ingressOperations: IngressOperations = IngressOperations(container)
Expand Down
11 changes: 11 additions & 0 deletions envoy-control-tests/src/main/resources/envoy/bad_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,17 @@ static_resources:
port_value: 10000
connect_timeout:
seconds: 1
- name: wrapper
type: STATIC
load_assignment:
cluster_name: wrapper
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: WRAPPER_SERVICE_IP
port_value: 5678
listeners:
- name: default_listener
address:
Expand Down
11 changes: 11 additions & 0 deletions envoy-control-tests/src/main/resources/envoy/config_ads.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,14 @@ static_resources:
port_value: 10000
connect_timeout:
seconds: 1
- name: wrapper
type: STATIC
load_assignment:
cluster_name: wrapper
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: WRAPPER_SERVICE_IP
port_value: 5678
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,14 @@ static_resources:
port_value: 10000
connect_timeout:
seconds: 1
- name: wrapper
type: STATIC
load_assignment:
cluster_name: wrapper
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: WRAPPER_SERVICE_IP
port_value: 5678
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,17 @@ static_resources:
port_value: 10000
connect_timeout:
seconds: 1
- name: wrapper
type: STATIC
load_assignment:
cluster_name: wrapper
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: WRAPPER_SERVICE_IP
port_value: 5678
listeners:
- name: default_listener
address:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,14 @@ static_resources:
port_value: 10000
connect_timeout:
seconds: 1
- name: wrapper
type: STATIC
load_assignment:
cluster_name: wrapper
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: WRAPPER_SERVICE_IP
port_value: 5678
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,14 @@ static_resources:
port_value: 10000
connect_timeout:
seconds: 1
- name: wrapper
type: STATIC
load_assignment:
cluster_name: wrapper
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: WRAPPER_SERVICE_IP
port_value: 5678
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,14 @@ static_resources:
port_value: 10000
connect_timeout:
seconds: 1
- name: wrapper
type: STATIC
load_assignment:
cluster_name: wrapper
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: WRAPPER_SERVICE_IP
port_value: 5678
Loading

0 comments on commit 980e439

Please sign in to comment.