Skip to content

Commit

Permalink
Fixed bugs in Proxy and Listener mappers, updated logging message, an…
Browse files Browse the repository at this point in the history
…d enhanced file parsing in FileProxyP rovider (#29)
  • Loading branch information
ayansen authored Jan 10, 2024
1 parent 763f8cc commit 2e46e5c
Show file tree
Hide file tree
Showing 12 changed files with 258 additions and 25 deletions.
4 changes: 3 additions & 1 deletion envoy/configs/test-proxy.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: chained_envoy_hosts
domains:
- "envoy.local"
- "envoy.local:8080"
routes:
- match:
prefix: "/exemplar"
Expand All @@ -13,6 +13,7 @@ routes:
- socketAddress:
address: openapi-exemplar.default.svc
port: 80
protocol: HTTP

mutations:
prefixRewrite: "/"
Expand All @@ -27,5 +28,6 @@ routes:
- socketAddress:
address: www.envoyproxy.io
port: 443
protocol: HTTPS
mutations:
prefixRewrite: "/"
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ayansen.playground.envoy

import DiscoveryServer
import ayansen.playground.envoy.entity.ListenersConfiguration
import ayansen.playground.envoy.entity.ProxyProviderConfigurations
import ayansen.playground.envoy.provider.ProxyProvider
import ayansen.playground.envoy.provider.FileProxyProvider
import io.envoyproxy.controlplane.cache.v3.SimpleCache
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
package ayansen.playground.envoy.entity

import io.envoyproxy.envoy.config.accesslog.v3.AccessLog
import io.envoyproxy.envoy.config.core.v3.ApiConfigSource
import io.envoyproxy.envoy.config.listener.v3.FilterChain
import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter
import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.Rds
import org.springframework.boot.context.properties.ConfigurationProperties


@ConfigurationProperties(prefix = "envoy-listeners")
data class ListenersConfiguration(
var listeners: List<Listener> = emptyList()
) {
data class Listener(
var name: String = "",
var socketAddress: SocketAddress = SocketAddress()
)


data class SocketAddress(
var address: String = "",
var port: Int = 0
)
fun toProtoListeners(): List<io.envoyproxy.envoy.config.listener.v3.Listener> {
return listeners.map {
val listeners = listeners.map {
io.envoyproxy.envoy.config.listener.v3.Listener.newBuilder()
.setName(it.name)
.setAddress(
Expand All @@ -29,6 +42,40 @@ data class ListenersConfiguration(
io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.newBuilder()
.setStatPrefix(it.name)
.setCodecType(io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.CodecType.AUTO)
.addHttpFilters(
HttpFilter.newBuilder().setName("envoy.filters.http.router").build()
)
.addAccessLog(
AccessLog.newBuilder().setName("envoy.access_loggers.file").setTypedConfig(
com.google.protobuf.Any.pack(
io.envoyproxy.envoy.extensions.access_loggers.file.v3.FileAccessLog.newBuilder()
.setPath("/dev/stdout")
.build()
)
).build()
)
.setRds(
Rds.newBuilder()
.setRouteConfigName("chained_envoy_hosts")
.setConfigSource(
io.envoyproxy.envoy.config.core.v3.ConfigSource.newBuilder()
.setResourceApiVersion(io.envoyproxy.envoy.config.core.v3.ApiVersion.V3)
.setApiConfigSource(
ApiConfigSource.newBuilder()
.setApiType(ApiConfigSource.ApiType.GRPC)
.setTransportApiVersion(io.envoyproxy.envoy.config.core.v3.ApiVersion.V3)
.addGrpcServices(
io.envoyproxy.envoy.config.core.v3.GrpcService.newBuilder()
.setEnvoyGrpc(
io.envoyproxy.envoy.config.core.v3.GrpcService.EnvoyGrpc.newBuilder()
.setClusterName("envoy_control_plane")
)
)
)
.build()
)
.build()
)
.build()
)
)
Expand All @@ -37,15 +84,6 @@ data class ListenersConfiguration(
)
.build()
}
return listeners
}
data class Listener(
val name: String,
val socketAddress: SocketAddress
)


data class SocketAddress(
val address: String,
val port: Int
)
}
64 changes: 60 additions & 4 deletions envoy/src/main/kotlin/ayansen/playground/envoy/entity/Proxy.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@ data class Proxy(
val routes: List<Route>
) {

enum class PROTOCOL(val value: String) {
HTTP("HTTP1"),
HTTPS("HTTPS")
}

data class Route(
val match: Match,
val cluster: Cluster,
val mutations: Mutations

)

data class Match(
Expand All @@ -42,7 +46,8 @@ data class Proxy(

data class SocketAddress(
val address: String,
val port: Int
val port: Int,
val protocol: PROTOCOL = PROTOCOL.HTTP
)

fun toProtoRoute(): io.envoyproxy.envoy.config.route.v3.RouteConfiguration {
Expand All @@ -63,6 +68,7 @@ data class Proxy(
.setRoute(
io.envoyproxy.envoy.config.route.v3.RouteAction.newBuilder()
.setCluster(route.cluster.name)
.setPrefixRewrite(route.mutations.prefixRewrite)
)
.build()
}
Expand All @@ -72,6 +78,7 @@ data class Proxy(
)
.build()
}

fun toProtoEndpoints(): List<ClusterLoadAssignment> {
return routes.map {
ClusterLoadAssignment.newBuilder()
Expand All @@ -98,17 +105,66 @@ data class Proxy(
.build()
}
}

fun toProtoClusters(): List<io.envoyproxy.envoy.config.cluster.v3.Cluster> {
return routes.map {
io.envoyproxy.envoy.config.cluster.v3.Cluster.newBuilder()
val cluster = io.envoyproxy.envoy.config.cluster.v3.Cluster.newBuilder()
.setName(it.cluster.name)
.setConnectTimeout(
com.google.protobuf.Duration.newBuilder()
.setSeconds(it.cluster.connectTimeout.toLong())
)
.setDnsLookupFamily(io.envoyproxy.envoy.config.cluster.v3.Cluster.DnsLookupFamily.V4_ONLY)
.setType(io.envoyproxy.envoy.config.cluster.v3.Cluster.DiscoveryType.valueOf(it.cluster.type))
.setLbPolicy(io.envoyproxy.envoy.config.cluster.v3.Cluster.LbPolicy.valueOf(it.cluster.lbPolicy))
.build()
.setLoadAssignment(
ClusterLoadAssignment.newBuilder()
.setClusterName(it.cluster.name)
.addAllEndpoints(
(it.cluster.hosts.map { host ->
LocalityLbEndpoints.newBuilder()
.addLbEndpoints(
LbEndpoint.newBuilder()
.setEndpoint(
Endpoint.newBuilder()
.setAddress(
Address.newBuilder()
.setSocketAddress(
io.envoyproxy.envoy.config.core.v3.SocketAddress.newBuilder()
.setAddress(host.socketAddress.address)
.setPortValue(host.socketAddress.port)
)
)
)
).build()
})
)
.build()
)
if (it.cluster.hosts.first().socketAddress.protocol == PROTOCOL.HTTPS) {
cluster.setTransportSocket(
io.envoyproxy.envoy.config.core.v3.TransportSocket.newBuilder()
.setName("envoy.transport_sockets.tls")
.setTypedConfig(
com.google.protobuf.Any.pack(
io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext.newBuilder()
.setCommonTlsContext(
io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CommonTlsContext.newBuilder()
.setTlsParams(
io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.TlsParameters.newBuilder()
.setTlsMinimumProtocolVersion(io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.TlsParameters.TlsProtocol.TLS_AUTO)
.build()

)
.build()
)
.setSni(it.cluster.hosts.first().socketAddress.address)
.build()
)
)
)
}
cluster.build()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ayansen.playground.envoy
package ayansen.playground.envoy.entity

import org.springframework.boot.context.properties.ConfigurationProperties

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import io.envoyproxy.controlplane.cache.v3.SimpleCache

import kotlinx.coroutines.*
import org.slf4j.LoggerFactory
import java.io.File
import java.nio.file.FileSystems
import java.nio.file.Path
import java.nio.file.StandardWatchEventKinds
Expand Down Expand Up @@ -39,7 +40,7 @@ class FileProxyProvider(

override fun getProxies(): List<Proxy> {
return Path.of(proxyFolderPath).toFile().listFiles()?.map { file ->
parseYamlFile(file.toPath())
parseYamlFile(file)
} ?: emptyList()
}

Expand All @@ -51,8 +52,8 @@ class FileProxyProvider(
throw NotImplementedError("proxy deletion can be done by deleting the file from the folder")
}

private inline fun <reified T> parseYamlFile(path: Path): T {
return mapper.readValue(path.toFile(), T::class.java)
private inline fun <reified T> parseYamlFile(file: File): T {
return mapper.readValue(file, T::class.java)
}

private fun watchForChanges(path: Path) {
Expand Down
11 changes: 6 additions & 5 deletions envoy/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ config-provider:
file:
path: "./configs"

listeners:
- name: http
socketAddress:
address: 0.0.0.0
port: 10000
envoy-listeners:
listeners:
- name: http
socketAddress:
address: 0.0.0.0
port: 10000
17 changes: 17 additions & 0 deletions envoy/src/test/kotlin/ayansen/playground/envoy/Fixtures.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package ayansen.playground.envoy

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import com.fasterxml.jackson.module.kotlin.KotlinModule
import java.io.File

object Fixtures {

val mapper: ObjectMapper = ObjectMapper(YAMLFactory()).apply {
registerModule(KotlinModule.Builder().build())
}

inline fun <reified T> parseYamlFile(file: File): T {
return mapper.readValue(file, T::class.java)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package ayansen.playground.envoy.entity

import ayansen.playground.envoy.Fixtures.parseYamlFile
import org.junit.jupiter.api.Test
import java.io.File
import kotlin.test.assertEquals

class ListenersConfigurationTest {

@Test
fun `toProtoListeners method returns a list of Listener protobuf objects when given a list of ListenerConfiguration objects`() {
// Given
val listeners: ListenersConfiguration =
parseYamlFile(File("src/test/resources/listeners/multiple_http_listeners.yaml"))


// When
val result = listeners.toProtoListeners()

// Then
assertEquals(2, result.size)
listeners.listeners.forEachIndexed { index, listener ->
assertEquals(listener.name, result[index].name)
assertEquals(
listener.socketAddress.address,
result[index].address.socketAddress.address
)
assertEquals(result[index].filterChainsCount, 1)
assertEquals(
listener.socketAddress.port,
result[index].address.socketAddress.portValue
)
}
}
}
41 changes: 41 additions & 0 deletions envoy/src/test/kotlin/ayansen/playground/envoy/entity/ProxyTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package ayansen.playground.envoy.entity


import ayansen.playground.envoy.Fixtures.parseYamlFile
import org.junit.jupiter.api.Test
import java.io.File
import kotlin.test.assertNotNull

class ProxyTest {

@Test
fun `generates a unique cluster for each route in the proxy`() {
val proxy:Proxy = parseYamlFile(File("src/test/resources/proxies/proxy_with_multiple_routes.yaml"))
val clusters = proxy.toProtoClusters()

assert(clusters.size == 2)
assert(clusters[0].name != clusters[1].name)
}

@Test
fun `generates multiple endpoints with the right cluster name for each route in the proxy`() {
val proxy:Proxy = parseYamlFile(File("src/test/resources/proxies/proxy_with_multiple_routes.yaml"))
val clusters = proxy.toProtoClusters()
val endpoints = proxy.toProtoEndpoints()

assert(clusters.size == 2)
assert(endpoints.size == 2)
assert(clusters[0].name != clusters[1].name)
assert(endpoints[0].clusterName == clusters[0].name)
assert(endpoints[1].clusterName == clusters[1].name)
}

@Test
fun `generates a single route configuration for a unique domain name`() {
val proxy:Proxy = parseYamlFile(File("src/test/resources/proxies/proxy_with_multiple_routes.yaml"))

val routeConfiguration = proxy.toProtoRoute()
assertNotNull(routeConfiguration)
}

}
10 changes: 10 additions & 0 deletions envoy/src/test/resources/listeners/multiple_http_listeners.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

listeners:
- name: http
socketAddress:
address: 0.0.0.0
port: 10000
- name: http
socketAddress:
address: 0.0.0.0
port: 10001
Loading

0 comments on commit 2e46e5c

Please sign in to comment.