Skip to content

Commit 58bac9c

Browse files
committed
Fix #30476: update ingress when required
1 parent 3a6f36a commit 58bac9c

5 files changed

Lines changed: 69 additions & 13 deletions

File tree

src/main/kotlin/eu/openanalytics/shinyproxyoperator/Operator.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ package eu.openanalytics.shinyproxyoperator
2323
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
2424
import eu.openanalytics.shinyproxyoperator.controller.IReconcileListener
2525
import eu.openanalytics.shinyproxyoperator.controller.IRecyclableChecker
26+
import eu.openanalytics.shinyproxyoperator.controller.IngressController
2627
import eu.openanalytics.shinyproxyoperator.controller.PodRetriever
2728
import eu.openanalytics.shinyproxyoperator.controller.RecyclableChecker
2829
import eu.openanalytics.shinyproxyoperator.controller.ResourceListener
@@ -88,6 +89,7 @@ class Operator(client: NamespacedKubernetesClient? = null,
8889
private val configMapListener: ResourceListener<ConfigMap, ConfigMapList, Resource<ConfigMap>>
8990
private val ingressListener: ResourceListener<Ingress, IngressList, Resource<Ingress>>
9091
private val serviceController: ServiceController
92+
private val ingressController: IngressController
9193

9294
private val channel = Channel<ShinyProxyEvent>(10000)
9395
val sendChannel: SendChannel<ShinyProxyEvent> = channel // public for tests
@@ -157,19 +159,21 @@ class Operator(client: NamespacedKubernetesClient? = null,
157159
configMapListener = ResourceListener(sendChannel, this.client.inAnyNamespace().configMaps())
158160
ingressListener = ResourceListener(sendChannel, this.client.inAnyNamespace().network().v1().ingresses())
159161
serviceController = ServiceController(this.client.inAnyNamespace().services())
162+
ingressController = IngressController(this.client)
160163
} else {
161164
replicaSetListener = ResourceListener(sendChannel, this.client.inNamespace(namespace).apps().replicaSets())
162165
serviceListener = ResourceListener(sendChannel, this.client.inNamespace(namespace).services())
163166
configMapListener = ResourceListener(sendChannel, this.client.inNamespace(namespace).configMaps())
164167
ingressListener = ResourceListener(sendChannel, this.client.inNamespace(namespace).network().v1().ingresses())
165168
serviceController = ServiceController(this.client.inNamespace(namespace).services())
169+
ingressController = IngressController(this.client)
166170
}
167171
}
168172

169173
/**
170174
* Controllers
171175
*/
172-
val shinyProxyController = ShinyProxyController(channel, this.client, shinyProxyClient, serviceController, reconcileListener, this.recyclableChecker)
176+
val shinyProxyController = ShinyProxyController(channel, this.client, shinyProxyClient, serviceController, ingressController, reconcileListener, this.recyclableChecker)
173177

174178
private fun _checkCrdExists(name: String, shortName: String) {
175179
try {

src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
package eu.openanalytics.shinyproxyoperator.components
2222

2323
import eu.openanalytics.shinyproxyoperator.crd.ShinyProxy
24+
import eu.openanalytics.shinyproxyoperator.crd.ShinyProxyInstance
2425
import io.fabric8.kubernetes.api.model.networking.v1.HTTPIngressPath
2526
import io.fabric8.kubernetes.api.model.networking.v1.HTTPIngressPathBuilder
2627
import io.fabric8.kubernetes.api.model.networking.v1.IngressBuilder
@@ -33,12 +34,14 @@ class IngressFactory(private val kubeClient: KubernetesClient) {
3334

3435
private val ingressPatcher = IngressPatcher()
3536

36-
fun create(shinyProxy: ShinyProxy) {
37+
fun create(shinyProxy: ShinyProxy, latestShinyProxyInstance: ShinyProxyInstance) {
38+
val labels = LabelFactory.labelsForShinyProxy(shinyProxy).toMutableMap()
39+
labels[LabelFactory.LATEST_INSTANCE_LABEL] = latestShinyProxyInstance.hashOfSpec
3740
//@formatter:off
3841
val ingressDefinition = IngressBuilder()
3942
.withNewMetadata()
4043
.withName(ResourceNameFactory.createNameForIngress(shinyProxy))
41-
.withLabels<String, String>(LabelFactory.labelsForShinyProxy(shinyProxy))
44+
.withLabels<String, String>(labels)
4245
.addNewOwnerReference()
4346
.withController(true)
4447
.withKind("ShinyProxy")
@@ -48,7 +51,7 @@ class IngressFactory(private val kubeClient: KubernetesClient) {
4851
.endOwnerReference()
4952
.endMetadata()
5053
.withNewSpec()
51-
.withIngressClassName("nginx") // TODO
54+
.withIngressClassName("nginx")
5255
.addNewRule()
5356
.withHost(shinyProxy.fqdn)
5457
.withNewHttp()
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* ShinyProxy-Operator
3+
*
4+
* Copyright (C) 2021-2023 Open Analytics
5+
*
6+
* ===========================================================================
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the Apache License as published by
10+
* The Apache Software Foundation, either version 2 of the License, or
11+
* (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* Apache License for more details.
17+
*
18+
* You should have received a copy of the Apache License
19+
* along with this program. If not, see <http://www.apache.org/licenses/>
20+
*/
21+
package eu.openanalytics.shinyproxyoperator.controller
22+
23+
import eu.openanalytics.shinyproxyoperator.components.IngressFactory
24+
import eu.openanalytics.shinyproxyoperator.components.LabelFactory
25+
import eu.openanalytics.shinyproxyoperator.crd.ShinyProxy
26+
import io.fabric8.kubernetes.client.KubernetesClient
27+
import mu.KotlinLogging
28+
29+
class IngressController(
30+
kubeClient: KubernetesClient
31+
) {
32+
33+
private val logger = KotlinLogging.logger {}
34+
private val ingressFactory = IngressFactory(kubeClient)
35+
36+
fun reconcile(resourceRetriever: ResourceRetriever, shinyProxy: ShinyProxy) {
37+
reconcileLatestInstance(resourceRetriever, shinyProxy)
38+
}
39+
40+
private fun reconcileLatestInstance(resourceRetriever: ResourceRetriever, shinyProxy: ShinyProxy) {
41+
val latestInstance = shinyProxy.status.latestInstance() ?: return
42+
43+
val ingresses = resourceRetriever.getIngressByLabels(LabelFactory.labelsForShinyProxy(shinyProxy), shinyProxy.metadata.namespace)
44+
val mustBeUpdated = ingresses.isEmpty() || ingresses[0].metadata?.labels?.get(LabelFactory.LATEST_INSTANCE_LABEL) != latestInstance.hashOfSpec
45+
46+
if (mustBeUpdated) {
47+
logger.debug { "${shinyProxy.logPrefix()} [Component/Ingress] Reconciling" }
48+
ingressFactory.create(shinyProxy, latestInstance)
49+
}
50+
}
51+
52+
}

src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyController.kt

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ package eu.openanalytics.shinyproxyoperator.controller
2222

2323
import eu.openanalytics.shinyproxyoperator.ShinyProxyClient
2424
import eu.openanalytics.shinyproxyoperator.components.ConfigMapFactory
25-
import eu.openanalytics.shinyproxyoperator.components.IngressFactory
2625
import eu.openanalytics.shinyproxyoperator.components.LabelFactory
2726
import eu.openanalytics.shinyproxyoperator.components.ReplicaSetFactory
2827
import eu.openanalytics.shinyproxyoperator.crd.ShinyProxy
@@ -44,12 +43,12 @@ class ShinyProxyController(private val channel: Channel<ShinyProxyEvent>,
4443
private val kubernetesClient: KubernetesClient,
4544
private val shinyProxyClient: ShinyProxyClient,
4645
private val serviceController: ServiceController,
46+
private val ingressController: IngressController,
4747
private val reconcileListener: IReconcileListener?,
4848
private val recyclableChecker: IRecyclableChecker) {
4949

5050
private val configMapFactory = ConfigMapFactory(kubernetesClient)
5151
private val replicaSetFactory = ReplicaSetFactory(kubernetesClient)
52-
private val ingressFactory = IngressFactory(kubernetesClient)
5352

5453
private val logger = KotlinLogging.logger {}
5554

@@ -268,10 +267,7 @@ class ShinyProxyController(private val channel: Channel<ShinyProxyEvent>,
268267
serviceController.reconcile(resourceRetriever, updatedShinyProxy)
269268
logger.debug { "${shinyProxy.logPrefix(shinyProxyInstance)} [Step 5/$amountOfSteps: Ok] [Component/Service]" }
270269

271-
val ingresses = resourceRetriever.getIngressByLabels(LabelFactory.labelsForShinyProxy(updatedShinyProxy), updatedShinyProxy.metadata.namespace)
272-
if (ingresses.isEmpty()) {
273-
ingressFactory.create(updatedShinyProxy)
274-
}
270+
ingressController.reconcile(resourceRetriever, updatedShinyProxy)
275271
logger.debug { "${shinyProxy.logPrefix(shinyProxyInstance)} [Step 6/$amountOfSteps: Ok] [Component/Ingress]" }
276272

277273
if (updatedShinyProxyInstance != null) {

src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,18 +95,19 @@ class ShinyProxyTestInstance(private val namespace: String,
9595
assertServiceIsCorrect(sp)
9696

9797
// check ingress
98-
assertIngressIsCorrect(sp, numInstancesRunning)
98+
assertIngressIsCorrect(sp)
9999
}
100100

101-
fun assertIngressIsCorrect(sp: ShinyProxy, numInstancesRunning: Int = 1) {
101+
fun assertIngressIsCorrect(sp: ShinyProxy) {
102102
val allIngresses = client.inNamespace(namespace).network().v1().ingresses().list().items
103103
assertEquals(1, allIngresses.size)
104104
val ingress = allIngresses.firstOrNull { it.metadata.name == "sp-${sp.metadata.name}-ing".take(63) }
105105
assertNotNull(ingress)
106106

107107
assertEquals(mapOf(
108108
LabelFactory.APP_LABEL to LabelFactory.APP_LABEL_VALUE,
109-
LabelFactory.REALM_ID_LABEL to sp.realmId
109+
LabelFactory.REALM_ID_LABEL to sp.realmId,
110+
LabelFactory.LATEST_INSTANCE_LABEL to sp.status.latestInstance()!!.hashOfSpec
110111
), ingress.metadata.labels)
111112

112113
assertOwnerReferenceIsCorrect(ingress, sp)

0 commit comments

Comments
 (0)