11package app.simplecloud.controller.runtime.envoy
22
3- import app.simplecloud.controller.runtime.droplet.DropletRepository
43import app.simplecloud.droplet.api.droplet.Droplet
54import com.google.protobuf.Any
65import com.google.protobuf.Duration
@@ -9,7 +8,6 @@ import io.envoyproxy.controlplane.cache.ConfigWatcher
98import io.envoyproxy.controlplane.cache.v3.SimpleCache
109import io.envoyproxy.controlplane.cache.v3.Snapshot
1110import io.envoyproxy.envoy.config.cluster.v3.Cluster
12- import io.envoyproxy.envoy.config.cluster.v3.Cluster.EdsClusterConfig
1311import io.envoyproxy.envoy.config.core.v3.*
1412import io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment
1513import io.envoyproxy.envoy.config.endpoint.v3.Endpoint
@@ -19,7 +17,6 @@ import io.envoyproxy.envoy.config.listener.v3.Filter
1917import io.envoyproxy.envoy.config.listener.v3.FilterChain
2018import io.envoyproxy.envoy.config.listener.v3.Listener
2119import io.envoyproxy.envoy.config.route.v3.*
22- import io.envoyproxy.envoy.extensions.filters.http.connect_grpc_bridge.v3.FilterConfig
2320import io.envoyproxy.envoy.extensions.filters.http.grpc_web.v3.GrpcWeb
2421import io.envoyproxy.envoy.extensions.filters.http.router.v3.Router
2522import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
@@ -28,147 +25,217 @@ import io.envoyproxy.envoy.extensions.upstreams.http.v3.HttpProtocolOptions
2825import org.apache.logging.log4j.LogManager
2926import java.util.*
3027
31- /* *
32- * This class handles the remapping of the [DropletRepository] to a [SimpleCache] of [Snapshot]s, which are used by the envoy ADS service.
33- */
34- class DropletCache {
28+ class DropletCache (
29+ private val grpcHost : String ,
30+ private val envoyPort : Int ,
31+ ) {
3532 private val cache = SimpleCache (SimpleCloudNodeGroup ())
3633 private val logger = LogManager .getLogger(DropletCache ::class .java)
3734
38- // Create a new Snapshot by the droplet repository's data
3935 fun update (droplets : List <Droplet >) {
4036 logger.info(" Detected new droplets in DropletRepository, adding to ADS..." )
37+
4138 val clusters = mutableListOf<Cluster >()
42- val listeners = mutableListOf<Listener >()
4339 val clas = mutableListOf<ClusterLoadAssignment >()
44- droplets.forEach {
45- clusters.add(createCluster(it))
46- listeners.add(createListener(it))
47- clas.add(createCLA(it))
40+ val listeners = mutableListOf<Listener >()
41+
42+ droplets.forEach { droplet ->
43+ clusters.add(createCluster(droplet))
44+ clas.add(createCLA(droplet))
45+ listeners.add(createOriginalListener(droplet))
4846 }
47+
48+ listeners.add(createMainListener(droplets))
49+
4950 cache.setSnapshot(
5051 SimpleCloudNodeGroup .GROUP ,
5152 Snapshot .create(
5253 clusters,
5354 clas,
5455 listeners,
55- listOf (), // We don't need routes
56- listOf (), // We don't need secrets
57- UUID .randomUUID()
58- .toString() // This can be anything, used internally for versioning. THIS HAS TO BE DIFFERENT FOR EVERY SNAPSHOT
56+ listOf (),
57+ listOf (),
58+ UUID .randomUUID().toString()
5959 )
6060 )
6161 }
6262
63- // Creates endpoints users can connect with later
64- private fun createListener (it : Droplet ): Listener {
65- return Listener .newBuilder().setName(" ${it.type} -${it.id} " ).setAddress(
66- Address .newBuilder().setSocketAddress(
67- SocketAddress .newBuilder().setProtocol(SocketAddress .Protocol .TCP ).setAddress(" 0.0.0.0" )
68- .setPortValue(it.envoyPort)
63+ private fun createMainListener (droplets : List <Droplet >): Listener {
64+ return Listener .newBuilder()
65+ .setName(" main_listener" )
66+ .setAddress(
67+ Address .newBuilder().setSocketAddress(
68+ SocketAddress .newBuilder()
69+ .setProtocol(SocketAddress .Protocol .TCP )
70+ .setAddress(grpcHost)
71+ .setPortValue(envoyPort)
72+ )
6973 )
70- ).setDefaultFilterChain(createListenerFilterChain( " ${it.type} - ${it.id} " )).build( )
71-
74+ .addFilterChains(createMainFilterChain(droplets) )
75+ .build()
7276 }
7377
74- // Creates load assignments for new droplets (I don't yet know if they need to be called every time?)
75- private fun createCLA (it : Droplet ): ClusterLoadAssignment {
76- return ClusterLoadAssignment .newBuilder().setClusterName(" ${it.type} -${it.id} " )
77- .addEndpoints(
78- LocalityLbEndpoints .newBuilder().addLbEndpoints(
79- LbEndpoint .newBuilder().setEndpoint(
80- Endpoint .newBuilder()
81- .setAddress(
82- Address .newBuilder().setSocketAddress(
83- SocketAddress .newBuilder().setPortValue(it.port).setAddress(it.host)
84- .setProtocol(SocketAddress .Protocol .TCP )
78+ private fun createMainFilterChain (droplets : List <Droplet >): FilterChain {
79+ val routes = droplets.map { droplet ->
80+ Route .newBuilder()
81+ .setMatch(
82+ RouteMatch .newBuilder()
83+ .setPrefix(" /${droplet.id} /" )
84+ )
85+ .setRoute(
86+ RouteAction .newBuilder()
87+ .setCluster(" ${droplet.type} -${droplet.id} " )
88+ .setPrefixRewrite(" /" )
89+ )
90+ .build()
91+ }
92+
93+ return FilterChain .newBuilder()
94+ .addFilters(
95+ Filter .newBuilder()
96+ .setName(" envoy.filters.network.http_connection_manager" )
97+ .setTypedConfig(
98+ Any .pack(
99+ HttpConnectionManager .newBuilder()
100+ .setStatPrefix(" ingress_http" )
101+ .setCodecType(HttpConnectionManager .CodecType .AUTO )
102+ .setRouteConfig(
103+ RouteConfiguration .newBuilder()
104+ .setName(" local_route" )
105+ .addVirtualHosts(
106+ VirtualHost .newBuilder()
107+ .setName(" local_service" )
108+ .addDomains(" *" )
109+ .addAllRoutes(routes)
110+ )
85111 )
86- )
112+ .addHttpFilters(
113+ HttpFilter .newBuilder()
114+ .setName(" envoy.filters.http.grpc_web" )
115+ .setTypedConfig(Any .pack(GrpcWeb .getDefaultInstance()))
116+ )
117+ .addHttpFilters(
118+ HttpFilter .newBuilder()
119+ .setName(" envoy.filters.http.router" )
120+ .setTypedConfig(Any .pack(Router .getDefaultInstance()))
121+ )
122+ .build()
123+ )
87124 )
88- )
89125 )
90126 .build()
91127 }
92128
93- // Creates clusters listening to droplets
94- private fun createCluster (it : Droplet ): Cluster {
95- return Cluster .newBuilder().setName(" ${it.type} -${it.id} " )
96- .setConnectTimeout(Duration .newBuilder().setSeconds(5 ))
97- .setType(Cluster .DiscoveryType .EDS )
98- .setEdsClusterConfig(
99- EdsClusterConfig .newBuilder()
100- .setEdsConfig(ConfigSource .newBuilder().setAds(AggregatedConfigSource .getDefaultInstance()))
101- )
102- .setLbPolicy(Cluster .LbPolicy .ROUND_ROBIN )
103- .setLoadAssignment(
104- ClusterLoadAssignment .newBuilder().setClusterName(" ${it.type} -${it.id} " )
105- .addEndpoints(
106- LocalityLbEndpoints .newBuilder()
107- .addLbEndpoints(
108- LbEndpoint .newBuilder().setEndpoint(
109- Endpoint .newBuilder()
110- .setAddress(
111- Address .newBuilder().setSocketAddress(
112- SocketAddress .newBuilder().setPortValue(it.port).setAddress(it.host)
113- )
114- )
115- )
116- )
117- )
118- ).putTypedExtensionProtocolOptions(
119- " envoy.extensions.upstreams.http.v3.HttpProtocolOptions" , Any .pack(
120- HttpProtocolOptions .newBuilder().setExplicitHttpConfig(
121- HttpProtocolOptions .ExplicitHttpConfig .newBuilder().setHttp2ProtocolOptions(
122- Http2ProtocolOptions .newBuilder().setMaxConcurrentStreams(
123- UInt32Value .of(100 )
124- )
125- )
126- ).build()
129+ private fun createOriginalListener (droplet : Droplet ): Listener {
130+ return Listener .newBuilder()
131+ .setName(" original_listener_${droplet.type} _${droplet.id} " )
132+ .setAddress(
133+ Address .newBuilder().setSocketAddress(
134+ SocketAddress .newBuilder()
135+ .setProtocol(SocketAddress .Protocol .TCP )
136+ .setAddress(" 0.0.0.0" )
137+ .setPortValue(droplet.envoyPort)
127138 )
128139 )
140+ .addFilterChains(createOriginalFilterChain(droplet))
129141 .build()
130-
131142 }
132143
133- // Creates a filter chain that remaps http to grpc
134- private fun createListenerFilterChain (cluster : String ): FilterChain .Builder {
144+ private fun createOriginalFilterChain (droplet : Droplet ): FilterChain {
135145 return FilterChain .newBuilder()
136146 .addFilters(
137- Filter .newBuilder().setName(" envoy.filters.network.http_connection_manager" )
147+ Filter .newBuilder()
148+ .setName(" envoy.filters.network.http_connection_manager" )
138149 .setTypedConfig(
139150 Any .pack(
140- HttpConnectionManager .newBuilder().setStatPrefix(" ingress_http" )
151+ HttpConnectionManager .newBuilder()
152+ .setStatPrefix(" ingress_http" )
141153 .setCodecType(HttpConnectionManager .CodecType .AUTO )
142154 .setRouteConfig(
143- RouteConfiguration .newBuilder().setName(" local_route" )
155+ RouteConfiguration .newBuilder()
156+ .setName(" local_route" )
144157 .addVirtualHosts(
145- VirtualHost .newBuilder().setName(" local_service" ).addDomains(" *" )
158+ VirtualHost .newBuilder()
159+ .setName(" local_service" )
160+ .addDomains(" *" )
146161 .addRoutes(
147- Route .newBuilder().setRoute(
148- RouteAction .newBuilder().setCluster(cluster)
149- .setTimeout(Duration .newBuilder().setSeconds(0 ).setNanos(0 ))
150- ).setMatch(RouteMatch .newBuilder().setPrefix(" /" ))
162+ Route .newBuilder()
163+ .setMatch(
164+ RouteMatch .newBuilder()
165+ .setPrefix(" /" )
166+ )
167+ .setRoute(
168+ RouteAction .newBuilder()
169+ .setCluster(" ${droplet.type} -${droplet.id} " )
170+ )
151171 )
152172 )
153- ).addHttpFilters(
154- HttpFilter .newBuilder().setName(" envoy.filters.http.connect_grpc_bridge" )
155- .setTypedConfig(Any .pack(FilterConfig .getDefaultInstance()))
156- ).addHttpFilters(
157- HttpFilter .newBuilder().setName(" envoy.filters.http.grpc_web" )
173+ )
174+ .addHttpFilters(
175+ HttpFilter .newBuilder()
176+ .setName(" envoy.filters.http.grpc_web" )
158177 .setTypedConfig(Any .pack(GrpcWeb .getDefaultInstance()))
159- ).addHttpFilters(
160- HttpFilter .newBuilder().setName(" envoy.filters.http.router" )
178+ )
179+ .addHttpFilters(
180+ HttpFilter .newBuilder()
181+ .setName(" envoy.filters.http.router" )
161182 .setTypedConfig(Any .pack(Router .getDefaultInstance()))
162183 )
163-
164184 .build()
165185 )
166186 )
167187 )
188+ .build()
189+ }
190+
191+ private fun createCluster (droplet : Droplet ): Cluster {
192+ return Cluster .newBuilder()
193+ .setName(" ${droplet.type} -${droplet.id} " )
194+ .setConnectTimeout(Duration .newBuilder().setSeconds(5 ))
195+ .setType(Cluster .DiscoveryType .STRICT_DNS )
196+ .setLbPolicy(Cluster .LbPolicy .ROUND_ROBIN )
197+ .putTypedExtensionProtocolOptions(
198+ " envoy.extensions.upstreams.http.v3.HttpProtocolOptions" ,
199+ Any .pack(
200+ HttpProtocolOptions .newBuilder()
201+ .setExplicitHttpConfig(
202+ HttpProtocolOptions .ExplicitHttpConfig .newBuilder()
203+ .setHttp2ProtocolOptions(
204+ Http2ProtocolOptions .newBuilder()
205+ .setMaxConcurrentStreams(UInt32Value .of(100 ))
206+ )
207+ ).build()
208+ )
209+ )
210+ .setLoadAssignment(createCLA(droplet))
211+ .build()
212+ }
213+
214+ private fun createCLA (droplet : Droplet ): ClusterLoadAssignment {
215+ return ClusterLoadAssignment .newBuilder()
216+ .setClusterName(" ${droplet.type} -${droplet.id} " )
217+ .addEndpoints(
218+ LocalityLbEndpoints .newBuilder()
219+ .addLbEndpoints(
220+ LbEndpoint .newBuilder()
221+ .setEndpoint(
222+ Endpoint .newBuilder()
223+ .setAddress(
224+ Address .newBuilder()
225+ .setSocketAddress(
226+ SocketAddress .newBuilder()
227+ .setPortValue(droplet.port)
228+ .setAddress(droplet.host)
229+ .setProtocol(SocketAddress .Protocol .TCP )
230+ )
231+ )
232+ )
233+ )
234+ )
235+ .build()
168236 }
169237
170238 fun getCache (): ConfigWatcher {
171239 return cache
172240 }
173-
174- }
241+ }
0 commit comments