From 99b57660c34d7fd93e5eb14aec2d078d08bcd0f9 Mon Sep 17 00:00:00 2001 From: Patrick Rodrigues Date: Tue, 20 Sep 2022 02:46:08 -0300 Subject: [PATCH 01/34] Add webservice to authentication options enumerator --- docs/deployment/bases/clustered/crds/shinyproxy.crd.yaml | 2 ++ docs/deployment/bases/namespaced/crds/shinyproxy.crd.yaml | 2 ++ src/test/resources/crd.yaml | 2 ++ 3 files changed, 6 insertions(+) diff --git a/docs/deployment/bases/clustered/crds/shinyproxy.crd.yaml b/docs/deployment/bases/clustered/crds/shinyproxy.crd.yaml index eaaf3fc..5b017d2 100644 --- a/docs/deployment/bases/clustered/crds/shinyproxy.crd.yaml +++ b/docs/deployment/bases/clustered/crds/shinyproxy.crd.yaml @@ -66,6 +66,7 @@ spec: - openid - saml - social + - webservice - simple - none containerWaitTime: @@ -205,6 +206,7 @@ spec: - openid - saml - social + - webservice - simple - none containerWaitTime: diff --git a/docs/deployment/bases/namespaced/crds/shinyproxy.crd.yaml b/docs/deployment/bases/namespaced/crds/shinyproxy.crd.yaml index eaaf3fc..5b017d2 100644 --- a/docs/deployment/bases/namespaced/crds/shinyproxy.crd.yaml +++ b/docs/deployment/bases/namespaced/crds/shinyproxy.crd.yaml @@ -66,6 +66,7 @@ spec: - openid - saml - social + - webservice - simple - none containerWaitTime: @@ -205,6 +206,7 @@ spec: - openid - saml - social + - webservice - simple - none containerWaitTime: diff --git a/src/test/resources/crd.yaml b/src/test/resources/crd.yaml index eaaf3fc..5b017d2 100644 --- a/src/test/resources/crd.yaml +++ b/src/test/resources/crd.yaml @@ -66,6 +66,7 @@ spec: - openid - saml - social + - webservice - simple - none containerWaitTime: @@ -205,6 +206,7 @@ spec: - openid - saml - social + - webservice - simple - none containerWaitTime: From 63660541d2c1b88e77e00ce34efcaf6d4f46b323 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Thu, 9 Mar 2023 09:05:30 +0100 Subject: [PATCH 02/34] Improve docs --- docs/deployment/README.md | 190 +++++++++++++++++++++++++++++++++----- 1 file changed, 169 insertions(+), 21 deletions(-) diff --git a/docs/deployment/README.md b/docs/deployment/README.md index dcf60b4..36dea65 100644 --- a/docs/deployment/README.md +++ b/docs/deployment/README.md @@ -182,34 +182,182 @@ The `CustomResourceDefinition` of the operator can be found in the and `namespaced` deployments). The following sections of this file are important: -- `spring` config related to Spring, such as the redis connection information -- `proxy` the configuration of ShinyProxy, this is the same configuration as if +- `spring`: config related to Spring, such as the redis connection information +- `proxy`: the configuration of ShinyProxy, this is the same configuration as if you were manually deploying ShinyProxy -- `kubernetesPodTemplateSpecPatches` allows to patch the PodTemplate -- `image` the docker image to use for the ShinyProxy instances -- `fqdn` the FQDN at which the service should be available -- `appNamespaces` a list of namespaces in which apps will be deployed. This is +- `kubernetesPodTemplateSpecPatches`: allows to patch the `PodTemplate` of the + ReplicaSet created by the operator (see + the [example](#modify-the-shinyproxy-pod)) +- `kubernetesIngressPatches`: allows to patch the `Ingress` resources created by + the operator (see the [example](#modify-the-ingress-resource) +- `image`: the docker image to use for the ShinyProxy server ( + e.g. `openanalytics/shinyproxy:3.0.0`) +- `imagePullPolicy`: the pull policy for ShinyProxy Image; the default value is + `IfNotPresent`; valid options are `Never`, `IfNotPresent` and `Always`. +- `fqdn`: the FQDN at which the service should be available, e.g. ` + shinyproxy-demo.local +- `appNamespaces`: a list of namespaces in which apps will be deployed. This is only needed when you change the namespace of an app using the `kubernetes-pod-patches` feature. The namespace of the operator and ShinyProxy instance are automatically included -## Troubleshooting - -You may get the following exception: +## Modify the Ingress Resource + +The ShinyProxy Operator automatically creates an ingress resource for each +ShinyProxy resource you create. This ingress resource points to the correct +Kubernetes service (which is also created by the operator). The created Ingress +resource contains everything that is needed for a working ShinyProxy deployment. +However, in some cases it is required to modify the resource. This can be +achieved using the `kubernetesIngressPatches` field. This field should contain a +string which contains a list of [JSON Patches](https://jsonpatch.com/) to apply +to the Ingress resource. The above examples already include the following patch: + +```yaml +apiVersion: openanalytics.eu/v1 +kind: ShinyProxy +metadata: + name: shinyproxy + namespace: shinyproxy +spec: + proxy: + # ... + kubernetesIngressPatches: | + - op: add + path: /metadata/annotations + value: + nginx.ingress.kubernetes.io/proxy-buffer-size: "128k" + nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/proxy-body-size: 300m + - op: add + path: /spec/ingressClassName + value: nginx + image: openanalytics/shinyproxy:3.0.0 + imagePullPolicy: Always + fqdn: shinyproxy-demo.local +``` -```text -io.fabric8.kubernetes.client.KubernetesClientException: Failure executing: POST at: https://10.96.0.1/apis/networking.k8s.io/v1beta1/namespaces/shinyproxy/ingresses. Message: admission webhook "validate.nginx.ingress.kubernetes.io" denied the request: host "shinyproxy-demo.local" and path "/" is already defined in ingress shinyproxy/nginx-to-skipper-ingress. Received status: Status(apiVersion=v1, code=400, details=null, kind=Status, message=admission webhook "validate.nginx.ingress.kubernetes.io" denied the request: host "shinyproxy-demo.local" and path "/" is already defined in ingress shinyproxy/nginx-to-skipper-ingress, metadata=ListMeta(_continue=null, remainingItemCount=null, resourceVersion=null, selfLink=null, additionalProperties={}), reason=BadRequest, status=Failure, additionalProperties={}). +The first patch adds some additional annotations to the ShinyProx resource. For +example, in order to set up a redirect from HTTP to HTTPS. The second patch +changes the ingressClassName to `nginx`. Any patch is accepted, but make sure +that the resulting Ingress resource still works for the ShinyProxy Deployment. +The ShinyProxy Operator logs the manifest before and after applying the patch, +this can be useful while creating the patches. + +**Note:** the previous section only applies to version 2 of the operator. +Version 1 behaves differently since it used Skipper as (intermediate) ingress +controller. + +## Modify the ShinyProxy Pod + +The Operator automatically creates a ReplicaSet for each ShinyProxy resource you +create. This ReplicaSet contains a `PodTemplate`, which contains all necessary +settings for creating a proper ShinyProxy pod. In a lot of cases, it can be +useful to adapt this `PodTemplate` for the specific context in which ShinyProxy +is running. For example, it's a good idea to specify the resource requests and +limits, or sometimes it's required to add a toleration to the pod. These +modification can be achieved using the `kubernetesPodTemplateSpecPatches` field. +This field should contain a string which contains a list +of [JSON Patches](https://jsonpatch.com/) to apply to the `PodTemplate`. The +above examples already include the following patch: + +```yaml +apiVersion: openanalytics.eu/v1 +kind: ShinyProxy +metadata: + name: shinyproxy + namespace: shinyproxy +spec: + proxy: + # ... + kubernetesPodTemplateSpecPatches: | + - op: add + path: /spec/containers/0/env/- + value: + name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: redis + key: redis-password + - op: add + path: /spec/containers/0/resources + value: + limits: + cpu: 1 + memory: 1Gi + requests: + cpu: 0.5 + memory: 1Gi + - op: add + path: /spec/serviceAccountName + value: shinyproxy-sa + image: openanalytics/shinyproxy:3.0.0 + imagePullPolicy: Always + fqdn: shinyproxy-demo.local ``` -This exception is caused by a -[bug](https://github.com/kubernetes/ingress-nginx/issues/7546) in the latest -(v1.0.0) version of the ingress-nginx controller. There are two workarounds for this bug: +The above configuration contains three patches. The first patch adds an +environment variable with the password used for connecting to the Redis server. +The second patch configures the resource limits and requests of the ShinyProxy +pod. Finally, the last patch configures the `ServiceAccount` of the pod. + +**Note:** it is important when using this feature to not break any existing +configuration of the pod. For example, when you want to mount additional +configmaps, use the following configuration: + +```yaml +apiVersion: openanalytics.eu/v1 +kind: ShinyProxy +metadata: + name: shinyproxy + namespace: shinyproxy +spec: + kubernetesPodTemplateSpecPatches: | + - op: add + path: /spec/volumes/- + value: + name: myconfig + configMap: + name: some-configmnap + - op: add + path: /spec/containers/0/volumeMounts/- + value: + mountPath: /mnt/configmap + name: myconfig + readOnly: true +``` -- use a version of the ingress controller older than v1.0.0 (e.g. v0.49.2) -- remove the validating webhook using, this is **not recommended** since it - removes an important part of how the ingress controller works. However, it is - an easy solution when you are following the above tutorial in minikube: +In this example, the `path` property of the patch always ends with a `-`, this +indicates that the patch adds a new entry to the end of the array +( e.g. `spec/volumes/`). + +The following patch breaks the behavior of the ShinyProxy pod and should +therefore not be used: + +```yaml +# NOTE: this is a demo of a WRONG configuration - do not use +apiVersion: openanalytics.eu/v1 +kind: ShinyProxy +metadata: + name: shinyproxy + namespace: shinyproxy +spec: + kubernetesPodTemplateSpecPatches: | + - op: add + path: /spec/volumes + value: + - name: myconfig + configMap: + name: some-configmnap + - op: add + path: /spec/containers/0/volumeMounts + value: + - mountPath: /mnt/configmap + name: myconfig + readOnly: true +``` - ```bash - kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission - ``` +This patch replaces the existing `/spec/volumes` +and `/spec/containers/0/volumeMounts` arrays of the pod. The ShinyProxy Operator +automatically creates a mount for a configmap which contains the ShinyProxy +configuration. By overriding these mounts, this configmap is not be mounted and +the default (demo) configuration of ShinyProxy is loaded. From edb4ef30f332ae8f4fc534073d4ae29998667c46 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Thu, 9 Mar 2023 09:07:17 +0100 Subject: [PATCH 03/34] Improve docs --- docs/deployment/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/deployment/README.md b/docs/deployment/README.md index 36dea65..317e42b 100644 --- a/docs/deployment/README.md +++ b/docs/deployment/README.md @@ -147,7 +147,7 @@ repository includes examples for many kinds of deployments: ShinyProxy server runs in its own namespace, isolated from the other servers. However, they are managed by a single operator. -- *3-namespaced-hpa-app-ns*: +- *3-namespaced-app-ns*: - Operator-mode: `namespaced` - Operator-namespace: `shinyproxy` - Redis-namespace: `shinyproxy` @@ -189,7 +189,7 @@ important: ReplicaSet created by the operator (see the [example](#modify-the-shinyproxy-pod)) - `kubernetesIngressPatches`: allows to patch the `Ingress` resources created by - the operator (see the [example](#modify-the-ingress-resource) + the operator (see the [example](#modify-the-ingress-resource)) - `image`: the docker image to use for the ShinyProxy server ( e.g. `openanalytics/shinyproxy:3.0.0`) - `imagePullPolicy`: the pull policy for ShinyProxy Image; the default value is From b60334ab13a714934744a4050e19c033514b7ea4 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Tue, 25 Apr 2023 11:35:31 +0200 Subject: [PATCH 04/34] Fix tests --- .../shinyproxyoperator/helpers/ShinyProxyTestInstance.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt index 5122c8e..ea3d207 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt @@ -106,7 +106,7 @@ class ShinyProxyTestInstance(private val namespace: String, assertEquals(mapOf( LabelFactory.APP_LABEL to LabelFactory.APP_LABEL_VALUE, - LabelFactory.REALM_ID_LABEL to sp.metadata.name + LabelFactory.REALM_ID_LABEL to sp.realmId ), ingress.metadata.labels) assertOwnerReferenceIsCorrect(ingress, sp) @@ -131,7 +131,7 @@ class ShinyProxyTestInstance(private val namespace: String, assertEquals(mapOf( LabelFactory.APP_LABEL to LabelFactory.APP_LABEL_VALUE, - LabelFactory.REALM_ID_LABEL to sp.metadata.name, + LabelFactory.REALM_ID_LABEL to sp.realmId, LabelFactory.LATEST_INSTANCE_LABEL to sp.status.latestInstance()!!.hashOfSpec ), service.metadata.labels) @@ -143,7 +143,7 @@ class ShinyProxyTestInstance(private val namespace: String, assertEquals(IntOrString(8080), service.spec.ports[0].targetPort) assertEquals(mapOf( LabelFactory.APP_LABEL to LabelFactory.APP_LABEL_VALUE, - LabelFactory.REALM_ID_LABEL to sp.metadata.name, + LabelFactory.REALM_ID_LABEL to sp.realmId, LabelFactory.INSTANCE_LABEL to sp.status.latestInstance()!!.hashOfSpec // TODO ), service.spec.selector) @@ -223,7 +223,7 @@ class ShinyProxyTestInstance(private val namespace: String, fun assertLabelsAreCorrect(resource: HasMetadata, sp: ShinyProxy) { assertEquals(mapOf( LabelFactory.APP_LABEL to LabelFactory.APP_LABEL_VALUE, - LabelFactory.REALM_ID_LABEL to sp.metadata.name, + LabelFactory.REALM_ID_LABEL to sp.realmId, LabelFactory.INSTANCE_LABEL to hash ), resource.metadata.labels) } From 10596ac0eeea6894c8911e72f3a4e394c123820a Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Tue, 25 Apr 2023 11:22:25 +0200 Subject: [PATCH 05/34] Fix #30475: add anti affinity --- .../components/PodTemplateSpecFactory.kt | 34 ++++ .../shinyproxyoperator/crd/ShinyProxy.kt | 15 ++ .../shinyproxyoperator/MainIntegrationTest.kt | 157 ++++++++++++++++++ .../resources/configs/affinity_required.yaml | 35 ++++ .../resources/configs/required_affinity.yaml | 35 ++++ 5 files changed, 276 insertions(+) create mode 100644 src/test/resources/configs/affinity_required.yaml create mode 100644 src/test/resources/configs/required_affinity.yaml diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/PodTemplateSpecFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/PodTemplateSpecFactory.kt index b505e23..d0505c1 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/PodTemplateSpecFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/PodTemplateSpecFactory.kt @@ -112,6 +112,7 @@ class PodTemplateSpecFactory { .withPeriodSeconds(5) .endStartupProbe() .endContainer() + .withAffinity(createAffinity(shinyProxy, shinyProxyInstance)) .withVolumes(VolumeBuilder() .withName("config-volume") .withConfigMap(ConfigMapVolumeSourceBuilder() @@ -125,5 +126,38 @@ class PodTemplateSpecFactory { return podTemplatePatcher.patch(template, shinyProxy.parsedKubernetesPodTemplateSpecPatches) } + private fun createAffinity(shinyProxy: ShinyProxy, shinyProxyInstance: ShinyProxyInstance): Affinity { + if (shinyProxy.antiAffinityRequired) { + //@formatter:off + return AffinityBuilder() + .withNewPodAntiAffinity() + .addNewRequiredDuringSchedulingIgnoredDuringExecution() + .withNewLabelSelector() + .withMatchLabels(LabelFactory.labelsForShinyProxyInstance(shinyProxy, shinyProxyInstance)) + .endLabelSelector() + .withTopologyKey(shinyProxy.antiAffinityTopologyKey) + .endRequiredDuringSchedulingIgnoredDuringExecution() + .endPodAntiAffinity() + .build() + //@formatter:on + } else { + //@formatter:off + return AffinityBuilder() + .withNewPodAntiAffinity() + .addNewPreferredDuringSchedulingIgnoredDuringExecution() + .withWeight(1) + .withNewPodAffinityTerm() + .withNewLabelSelector() + .withMatchLabels(LabelFactory.labelsForShinyProxyInstance(shinyProxy, shinyProxyInstance)) + .endLabelSelector() + .withTopologyKey(shinyProxy.antiAffinityTopologyKey) + .endPodAffinityTerm() + .endPreferredDuringSchedulingIgnoredDuringExecution() + .endPodAntiAffinity() + .build() + //@formatter:on + } + } + } diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt index b9989db..62ab350 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt @@ -161,6 +161,21 @@ class ShinyProxy : CustomResource(), Namespaced { return@lazy "${metadata.name}-${metadata.namespace}" } + @get:JsonIgnore + val antiAffinityTopologyKey: String by lazy { + if (spec.get("antiAffinityTopologyKey")?.isTextual == true) { + return@lazy spec.get("antiAffinityTopologyKey").textValue() + } + return@lazy "kubernetes.io/hostname" + } + + @get:JsonIgnore + val antiAffinityRequired: Boolean by lazy { + if (spec.get("antiAffinityRequired")?.isBoolean == true) { + return@lazy spec.get("antiAffinityRequired").booleanValue() + } + return@lazy false + } fun logPrefix(shinyProxyInstance: ShinyProxyInstance): String { return "[${metadata.namespace}/${metadata.name}/${shinyProxyInstance.hashOfSpec}]" diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt index 347b550..b057900 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt @@ -20,6 +20,7 @@ */ package eu.openanalytics.shinyproxyoperator +import eu.openanalytics.shinyproxyoperator.components.LabelFactory import eu.openanalytics.shinyproxyoperator.controller.ShinyProxyEvent import eu.openanalytics.shinyproxyoperator.controller.ShinyProxyEventType import eu.openanalytics.shinyproxyoperator.helpers.IntegrationTestBase @@ -36,6 +37,7 @@ import org.junit.jupiter.api.assertThrows import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertNotNull +import kotlin.test.assertNull import kotlin.test.assertTrue class MainIntegrationTest : IntegrationTestBase() { @@ -904,4 +906,159 @@ class MainIntegrationTest : IntegrationTestBase() { spTestInstance.assertInstanceIsCorrect() job.cancel() } + + @Test + fun `operator should have correct antiAffinity defaults`() = + setup(Mode.NAMESPACED) { namespace, shinyProxyClient, namespacedClient, stableClient, operator, reconcileListener, _ -> + // 1. create a SP instance + val spTestInstance = ShinyProxyTestInstance( + namespace, + namespacedClient, + shinyProxyClient, + "simple_config.yaml", + reconcileListener + ) + val sp = spTestInstance.create() + + val (resourceRetriever, shinyProxyLister) = operator.prepare() + + // 3. start the operator and let it do it's work + val job = GlobalScope.launch { + operator.run(resourceRetriever, shinyProxyLister) + } + + // 4. wait until instance is created + spTestInstance.waitForOneReconcile() + + // 5. assert correctness + spTestInstance.assertInstanceIsCorrect() + + // 6. check antiAffinity rules + val replicaSets = stableClient.inNamespace(namespace).apps().replicaSets().list().items + assertEquals(1, replicaSets.size) + val replicaSet = replicaSets.firstOrNull { it.metadata.labels[LabelFactory.INSTANCE_LABEL] == spTestInstance.hash } + assertNotNull(replicaSet) + + assertNotNull(replicaSet.spec.template.spec.affinity) + assertNotNull(replicaSet.spec.template.spec.affinity.podAntiAffinity) + assertNull(replicaSet.spec.template.spec.affinity.podAffinity) + assertNull(replicaSet.spec.template.spec.affinity.nodeAffinity) + assertEquals(0, replicaSet.spec.template.spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution.size) + assertNotNull(replicaSet.spec.template.spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution) + assertEquals(1, replicaSet.spec.template.spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution.size) + val rule = replicaSet.spec.template.spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[0] + assertEquals(1, rule.weight) + assertEquals("kubernetes.io/hostname", rule.podAffinityTerm.topologyKey) + assertEquals(mapOf( + LabelFactory.APP_LABEL to LabelFactory.APP_LABEL_VALUE, + LabelFactory.REALM_ID_LABEL to sp.realmId, + LabelFactory.INSTANCE_LABEL to spTestInstance.hash + ), rule.podAffinityTerm.labelSelector.matchLabels) + + job.cancel() + } + + @Test + fun `operator should have correct antiAffinity when required is true`() = + setup(Mode.NAMESPACED) { namespace, shinyProxyClient, namespacedClient, stableClient, operator, reconcileListener, _ -> + // 1. create a SP instance + val spTestInstance = ShinyProxyTestInstance( + namespace, + namespacedClient, + shinyProxyClient, + "affinity_required.yaml", + reconcileListener + ) + val sp = spTestInstance.create() + + val (resourceRetriever, shinyProxyLister) = operator.prepare() + + // 3. start the operator and let it do it's work + val job = GlobalScope.launch { + operator.run(resourceRetriever, shinyProxyLister) + } + + // 4. wait until instance is created + spTestInstance.waitForOneReconcile() + + // 5. assert correctness + spTestInstance.assertInstanceIsCorrect() + + // 6. check antiAffinity rules + val replicaSets = stableClient.inNamespace(namespace).apps().replicaSets().list().items + assertEquals(1, replicaSets.size) + val replicaSet = replicaSets.firstOrNull { it.metadata.labels[LabelFactory.INSTANCE_LABEL] == spTestInstance.hash } + assertNotNull(replicaSet) + + assertNotNull(replicaSet.spec.template.spec.affinity) + assertNotNull(replicaSet.spec.template.spec.affinity.podAntiAffinity) + assertNull(replicaSet.spec.template.spec.affinity.podAffinity) + assertNull(replicaSet.spec.template.spec.affinity.nodeAffinity) + assertEquals(0, replicaSet.spec.template.spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution.size) + assertEquals(1, replicaSet.spec.template.spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution.size) + assertNotNull(replicaSet.spec.template.spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution) + val rule = replicaSet.spec.template.spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[0] + assertEquals("kubernetes.io/hostname", rule.topologyKey) + assertEquals(mapOf( + LabelFactory.APP_LABEL to LabelFactory.APP_LABEL_VALUE, + LabelFactory.REALM_ID_LABEL to sp.realmId, + LabelFactory.INSTANCE_LABEL to spTestInstance.hash + ), rule.labelSelector.matchLabels) + + job.cancel() + } + + + @Test + fun `operator should have correct antiAffinity when topologyKey is set`() = + setup(Mode.NAMESPACED) { namespace, shinyProxyClient, namespacedClient, stableClient, operator, reconcileListener, _ -> + // 1. create a SP instance + val spTestInstance = ShinyProxyTestInstance( + namespace, + namespacedClient, + shinyProxyClient, + "affinity_toplogykey.yaml", + reconcileListener + ) + val sp = spTestInstance.create() + + val (resourceRetriever, shinyProxyLister) = operator.prepare() + + // 3. start the operator and let it do it's work + val job = GlobalScope.launch { + operator.run(resourceRetriever, shinyProxyLister) + } + + // 4. wait until instance is created + spTestInstance.waitForOneReconcile() + + // 5. assert correctness + spTestInstance.assertInstanceIsCorrect() + + // 6. check antiAffinity rules + val replicaSets = stableClient.inNamespace(namespace).apps().replicaSets().list().items + assertEquals(1, replicaSets.size) + val replicaSet = replicaSets.firstOrNull { it.metadata.labels[LabelFactory.INSTANCE_LABEL] == spTestInstance.hash } + assertNotNull(replicaSet) + + assertNotNull(replicaSet.spec.template.spec.affinity) + assertNotNull(replicaSet.spec.template.spec.affinity.podAntiAffinity) + assertNull(replicaSet.spec.template.spec.affinity.podAffinity) + assertNull(replicaSet.spec.template.spec.affinity.nodeAffinity) + assertEquals(0, replicaSet.spec.template.spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution.size) + assertNotNull(replicaSet.spec.template.spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution) + assertEquals(1, replicaSet.spec.template.spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution.size) + val rule = replicaSet.spec.template.spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[0] + assertEquals(1, rule.weight) + assertEquals("example.com/custom-topology-key", rule.podAffinityTerm.topologyKey) + assertEquals(mapOf( + LabelFactory.APP_LABEL to LabelFactory.APP_LABEL_VALUE, + LabelFactory.REALM_ID_LABEL to sp.realmId, + LabelFactory.INSTANCE_LABEL to spTestInstance.hash + ), rule.podAffinityTerm.labelSelector.matchLabels) + + job.cancel() + } + + } diff --git a/src/test/resources/configs/affinity_required.yaml b/src/test/resources/configs/affinity_required.yaml new file mode 100644 index 0000000..85b94fd --- /dev/null +++ b/src/test/resources/configs/affinity_required.yaml @@ -0,0 +1,35 @@ +apiVersion: openanalytics.eu/v1 +kind: ShinyProxy +metadata: + name: example-shinyproxy +spec: + antiAffinityTopologyKey: example.com/custom-topology-key + fqdn: itest.local + image: openanalytics/shinyproxy:2.6.1 + proxy: + title: Open Analytics Shiny Proxy + logoUrl: http://www.openanalytics.eu/sites/www.openanalytics.eu/themes/oa/logo.png + landingPage: / + heartbeatRate: 10000 + heartbeatTimeout: 60000 + port: 8080 + authentication: simple + containerBackend: kubernetes + kubernetes: + namespace: itest + users: + - name: demo + password: demo + groups: scientists + - name: demo2 + password: demo2 + groups: mathematicians + specs: + - id: 01_hello + displayName: Hello Application + description: Application which demonstrates the basics of a Shiny app + containerCmd: [ "R", "-e", "shinyproxy::run_01_hello()" ] + containerImage: openanalytics/shinyproxy-demo + - id: 06_tabsets + container-cmd: [ "R", "-e", "shinyproxy::run_06_tabsets()" ] + container-image: openanalytics/shinyproxy-demo diff --git a/src/test/resources/configs/required_affinity.yaml b/src/test/resources/configs/required_affinity.yaml new file mode 100644 index 0000000..a48f4c5 --- /dev/null +++ b/src/test/resources/configs/required_affinity.yaml @@ -0,0 +1,35 @@ +apiVersion: openanalytics.eu/v1 +kind: ShinyProxy +metadata: + name: example-shinyproxy +spec: + antiAffinityRequired: true + fqdn: itest.local + image: openanalytics/shinyproxy:2.6.1 + proxy: + title: Open Analytics Shiny Proxy + logoUrl: http://www.openanalytics.eu/sites/www.openanalytics.eu/themes/oa/logo.png + landingPage: / + heartbeatRate: 10000 + heartbeatTimeout: 60000 + port: 8080 + authentication: simple + containerBackend: kubernetes + kubernetes: + namespace: itest + users: + - name: demo + password: demo + groups: scientists + - name: demo2 + password: demo2 + groups: mathematicians + specs: + - id: 01_hello + displayName: Hello Application + description: Application which demonstrates the basics of a Shiny app + containerCmd: [ "R", "-e", "shinyproxy::run_01_hello()" ] + containerImage: openanalytics/shinyproxy-demo + - id: 06_tabsets + container-cmd: [ "R", "-e", "shinyproxy::run_06_tabsets()" ] + container-image: openanalytics/shinyproxy-demo From 9ce880983d5c415b32291ce0532f44884691af55 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Wed, 26 Apr 2023 08:34:25 +0200 Subject: [PATCH 06/34] Fix tests --- src/test/resources/configs/affinity_required.yaml | 2 +- .../{required_affinity.yaml => affinity_toplogykey.yaml} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/test/resources/configs/{required_affinity.yaml => affinity_toplogykey.yaml} (94%) diff --git a/src/test/resources/configs/affinity_required.yaml b/src/test/resources/configs/affinity_required.yaml index 85b94fd..a48f4c5 100644 --- a/src/test/resources/configs/affinity_required.yaml +++ b/src/test/resources/configs/affinity_required.yaml @@ -3,7 +3,7 @@ kind: ShinyProxy metadata: name: example-shinyproxy spec: - antiAffinityTopologyKey: example.com/custom-topology-key + antiAffinityRequired: true fqdn: itest.local image: openanalytics/shinyproxy:2.6.1 proxy: diff --git a/src/test/resources/configs/required_affinity.yaml b/src/test/resources/configs/affinity_toplogykey.yaml similarity index 94% rename from src/test/resources/configs/required_affinity.yaml rename to src/test/resources/configs/affinity_toplogykey.yaml index a48f4c5..85b94fd 100644 --- a/src/test/resources/configs/required_affinity.yaml +++ b/src/test/resources/configs/affinity_toplogykey.yaml @@ -3,7 +3,7 @@ kind: ShinyProxy metadata: name: example-shinyproxy spec: - antiAffinityRequired: true + antiAffinityTopologyKey: example.com/custom-topology-key fqdn: itest.local image: openanalytics/shinyproxy:2.6.1 proxy: From 3a6f36a8e2a2918d41d858285bf3124e5b575226 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Wed, 26 Apr 2023 12:51:09 +0200 Subject: [PATCH 07/34] Fix typo --- .../eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt | 2 +- .../{affinity_toplogykey.yaml => affinity_topologykey.yaml} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/test/resources/configs/{affinity_toplogykey.yaml => affinity_topologykey.yaml} (100%) diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt index b057900..baa7a3f 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt @@ -1017,7 +1017,7 @@ class MainIntegrationTest : IntegrationTestBase() { namespace, namespacedClient, shinyProxyClient, - "affinity_toplogykey.yaml", + "affinity_topologykey.yaml", reconcileListener ) val sp = spTestInstance.create() diff --git a/src/test/resources/configs/affinity_toplogykey.yaml b/src/test/resources/configs/affinity_topologykey.yaml similarity index 100% rename from src/test/resources/configs/affinity_toplogykey.yaml rename to src/test/resources/configs/affinity_topologykey.yaml From 58bac9ce92324d3853b42e562938bb017139320b Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Fri, 4 Aug 2023 16:09:34 +0200 Subject: [PATCH 08/34] Fix #30476: update ingress when required --- .../shinyproxyoperator/Operator.kt | 6 ++- .../components/IngressFactory.kt | 9 ++-- .../controller/IngressController.kt | 52 +++++++++++++++++++ .../controller/ShinyProxyController.kt | 8 +-- .../helpers/ShinyProxyTestInstance.kt | 7 +-- 5 files changed, 69 insertions(+), 13 deletions(-) create mode 100644 src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/IngressController.kt diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/Operator.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/Operator.kt index 83b2922..5e1b642 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/Operator.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/Operator.kt @@ -23,6 +23,7 @@ package eu.openanalytics.shinyproxyoperator import com.fasterxml.jackson.module.kotlin.registerKotlinModule import eu.openanalytics.shinyproxyoperator.controller.IReconcileListener import eu.openanalytics.shinyproxyoperator.controller.IRecyclableChecker +import eu.openanalytics.shinyproxyoperator.controller.IngressController import eu.openanalytics.shinyproxyoperator.controller.PodRetriever import eu.openanalytics.shinyproxyoperator.controller.RecyclableChecker import eu.openanalytics.shinyproxyoperator.controller.ResourceListener @@ -88,6 +89,7 @@ class Operator(client: NamespacedKubernetesClient? = null, private val configMapListener: ResourceListener> private val ingressListener: ResourceListener> private val serviceController: ServiceController + private val ingressController: IngressController private val channel = Channel(10000) val sendChannel: SendChannel = channel // public for tests @@ -157,19 +159,21 @@ class Operator(client: NamespacedKubernetesClient? = null, configMapListener = ResourceListener(sendChannel, this.client.inAnyNamespace().configMaps()) ingressListener = ResourceListener(sendChannel, this.client.inAnyNamespace().network().v1().ingresses()) serviceController = ServiceController(this.client.inAnyNamespace().services()) + ingressController = IngressController(this.client) } else { replicaSetListener = ResourceListener(sendChannel, this.client.inNamespace(namespace).apps().replicaSets()) serviceListener = ResourceListener(sendChannel, this.client.inNamespace(namespace).services()) configMapListener = ResourceListener(sendChannel, this.client.inNamespace(namespace).configMaps()) ingressListener = ResourceListener(sendChannel, this.client.inNamespace(namespace).network().v1().ingresses()) serviceController = ServiceController(this.client.inNamespace(namespace).services()) + ingressController = IngressController(this.client) } } /** * Controllers */ - val shinyProxyController = ShinyProxyController(channel, this.client, shinyProxyClient, serviceController, reconcileListener, this.recyclableChecker) + val shinyProxyController = ShinyProxyController(channel, this.client, shinyProxyClient, serviceController, ingressController, reconcileListener, this.recyclableChecker) private fun _checkCrdExists(name: String, shortName: String) { try { diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt index 6e06ed3..9c87e41 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt @@ -21,6 +21,7 @@ package eu.openanalytics.shinyproxyoperator.components import eu.openanalytics.shinyproxyoperator.crd.ShinyProxy +import eu.openanalytics.shinyproxyoperator.crd.ShinyProxyInstance import io.fabric8.kubernetes.api.model.networking.v1.HTTPIngressPath import io.fabric8.kubernetes.api.model.networking.v1.HTTPIngressPathBuilder import io.fabric8.kubernetes.api.model.networking.v1.IngressBuilder @@ -33,12 +34,14 @@ class IngressFactory(private val kubeClient: KubernetesClient) { private val ingressPatcher = IngressPatcher() - fun create(shinyProxy: ShinyProxy) { + fun create(shinyProxy: ShinyProxy, latestShinyProxyInstance: ShinyProxyInstance) { + val labels = LabelFactory.labelsForShinyProxy(shinyProxy).toMutableMap() + labels[LabelFactory.LATEST_INSTANCE_LABEL] = latestShinyProxyInstance.hashOfSpec //@formatter:off val ingressDefinition = IngressBuilder() .withNewMetadata() .withName(ResourceNameFactory.createNameForIngress(shinyProxy)) - .withLabels(LabelFactory.labelsForShinyProxy(shinyProxy)) + .withLabels(labels) .addNewOwnerReference() .withController(true) .withKind("ShinyProxy") @@ -48,7 +51,7 @@ class IngressFactory(private val kubeClient: KubernetesClient) { .endOwnerReference() .endMetadata() .withNewSpec() - .withIngressClassName("nginx") // TODO + .withIngressClassName("nginx") .addNewRule() .withHost(shinyProxy.fqdn) .withNewHttp() diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/IngressController.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/IngressController.kt new file mode 100644 index 0000000..8c0d7a3 --- /dev/null +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/IngressController.kt @@ -0,0 +1,52 @@ +/** + * ShinyProxy-Operator + * + * Copyright (C) 2021-2023 Open Analytics + * + * =========================================================================== + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Apache License as published by + * The Apache Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Apache License for more details. + * + * You should have received a copy of the Apache License + * along with this program. If not, see + */ +package eu.openanalytics.shinyproxyoperator.controller + +import eu.openanalytics.shinyproxyoperator.components.IngressFactory +import eu.openanalytics.shinyproxyoperator.components.LabelFactory +import eu.openanalytics.shinyproxyoperator.crd.ShinyProxy +import io.fabric8.kubernetes.client.KubernetesClient +import mu.KotlinLogging + +class IngressController( + kubeClient: KubernetesClient +) { + + private val logger = KotlinLogging.logger {} + private val ingressFactory = IngressFactory(kubeClient) + + fun reconcile(resourceRetriever: ResourceRetriever, shinyProxy: ShinyProxy) { + reconcileLatestInstance(resourceRetriever, shinyProxy) + } + + private fun reconcileLatestInstance(resourceRetriever: ResourceRetriever, shinyProxy: ShinyProxy) { + val latestInstance = shinyProxy.status.latestInstance() ?: return + + val ingresses = resourceRetriever.getIngressByLabels(LabelFactory.labelsForShinyProxy(shinyProxy), shinyProxy.metadata.namespace) + val mustBeUpdated = ingresses.isEmpty() || ingresses[0].metadata?.labels?.get(LabelFactory.LATEST_INSTANCE_LABEL) != latestInstance.hashOfSpec + + if (mustBeUpdated) { + logger.debug { "${shinyProxy.logPrefix()} [Component/Ingress] Reconciling" } + ingressFactory.create(shinyProxy, latestInstance) + } + } + +} diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyController.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyController.kt index 16aa7b2..beae9c9 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyController.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyController.kt @@ -22,7 +22,6 @@ package eu.openanalytics.shinyproxyoperator.controller import eu.openanalytics.shinyproxyoperator.ShinyProxyClient import eu.openanalytics.shinyproxyoperator.components.ConfigMapFactory -import eu.openanalytics.shinyproxyoperator.components.IngressFactory import eu.openanalytics.shinyproxyoperator.components.LabelFactory import eu.openanalytics.shinyproxyoperator.components.ReplicaSetFactory import eu.openanalytics.shinyproxyoperator.crd.ShinyProxy @@ -44,12 +43,12 @@ class ShinyProxyController(private val channel: Channel, private val kubernetesClient: KubernetesClient, private val shinyProxyClient: ShinyProxyClient, private val serviceController: ServiceController, + private val ingressController: IngressController, private val reconcileListener: IReconcileListener?, private val recyclableChecker: IRecyclableChecker) { private val configMapFactory = ConfigMapFactory(kubernetesClient) private val replicaSetFactory = ReplicaSetFactory(kubernetesClient) - private val ingressFactory = IngressFactory(kubernetesClient) private val logger = KotlinLogging.logger {} @@ -268,10 +267,7 @@ class ShinyProxyController(private val channel: Channel, serviceController.reconcile(resourceRetriever, updatedShinyProxy) logger.debug { "${shinyProxy.logPrefix(shinyProxyInstance)} [Step 5/$amountOfSteps: Ok] [Component/Service]" } - val ingresses = resourceRetriever.getIngressByLabels(LabelFactory.labelsForShinyProxy(updatedShinyProxy), updatedShinyProxy.metadata.namespace) - if (ingresses.isEmpty()) { - ingressFactory.create(updatedShinyProxy) - } + ingressController.reconcile(resourceRetriever, updatedShinyProxy) logger.debug { "${shinyProxy.logPrefix(shinyProxyInstance)} [Step 6/$amountOfSteps: Ok] [Component/Ingress]" } if (updatedShinyProxyInstance != null) { diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt index ea3d207..1648415 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt @@ -95,10 +95,10 @@ class ShinyProxyTestInstance(private val namespace: String, assertServiceIsCorrect(sp) // check ingress - assertIngressIsCorrect(sp, numInstancesRunning) + assertIngressIsCorrect(sp) } - fun assertIngressIsCorrect(sp: ShinyProxy, numInstancesRunning: Int = 1) { + fun assertIngressIsCorrect(sp: ShinyProxy) { val allIngresses = client.inNamespace(namespace).network().v1().ingresses().list().items assertEquals(1, allIngresses.size) val ingress = allIngresses.firstOrNull { it.metadata.name == "sp-${sp.metadata.name}-ing".take(63) } @@ -106,7 +106,8 @@ class ShinyProxyTestInstance(private val namespace: String, assertEquals(mapOf( LabelFactory.APP_LABEL to LabelFactory.APP_LABEL_VALUE, - LabelFactory.REALM_ID_LABEL to sp.realmId + LabelFactory.REALM_ID_LABEL to sp.realmId, + LabelFactory.LATEST_INSTANCE_LABEL to sp.status.latestInstance()!!.hashOfSpec ), ingress.metadata.labels) assertOwnerReferenceIsCorrect(ingress, sp) From a7d81c2bc4a7d7281fde0084268be425f59ca589 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Mon, 7 Aug 2023 11:29:18 +0200 Subject: [PATCH 09/34] Fix #31108: support additional fqdns --- .../components/IngressFactory.kt | 20 +++-- .../shinyproxyoperator/crd/ShinyProxy.kt | 9 ++- .../shinyproxyoperator/MainIntegrationTest.kt | 74 +++++++++++++++++++ .../resources/configs/additional_fqdns.yaml | 36 +++++++++ 4 files changed, 132 insertions(+), 7 deletions(-) create mode 100644 src/test/resources/configs/additional_fqdns.yaml diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt index 9c87e41..d513b8c 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt @@ -25,6 +25,8 @@ import eu.openanalytics.shinyproxyoperator.crd.ShinyProxyInstance import io.fabric8.kubernetes.api.model.networking.v1.HTTPIngressPath import io.fabric8.kubernetes.api.model.networking.v1.HTTPIngressPathBuilder import io.fabric8.kubernetes.api.model.networking.v1.IngressBuilder +import io.fabric8.kubernetes.api.model.networking.v1.IngressRule +import io.fabric8.kubernetes.api.model.networking.v1.IngressRuleBuilder import io.fabric8.kubernetes.client.KubernetesClient import mu.KotlinLogging @@ -37,7 +39,18 @@ class IngressFactory(private val kubeClient: KubernetesClient) { fun create(shinyProxy: ShinyProxy, latestShinyProxyInstance: ShinyProxyInstance) { val labels = LabelFactory.labelsForShinyProxy(shinyProxy).toMutableMap() labels[LabelFactory.LATEST_INSTANCE_LABEL] = latestShinyProxyInstance.hashOfSpec + //@formatter:off + val fqdns = listOf(shinyProxy.fqdn) + shinyProxy.additionalFqdns + val rules = fqdns.map { host -> + IngressRuleBuilder() + .withHost(host) + .withNewHttp() + .addToPaths(createPathV1(shinyProxy)) + .endHttp() + .build() + } + val ingressDefinition = IngressBuilder() .withNewMetadata() .withName(ResourceNameFactory.createNameForIngress(shinyProxy)) @@ -52,12 +65,7 @@ class IngressFactory(private val kubeClient: KubernetesClient) { .endMetadata() .withNewSpec() .withIngressClassName("nginx") - .addNewRule() - .withHost(shinyProxy.fqdn) - .withNewHttp() - .addToPaths(createPathV1(shinyProxy)) - .endHttp() - .endRule() + .withRules(rules) .endSpec() .build() //@formatter:on diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt index 62ab350..26d0975 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt @@ -41,7 +41,6 @@ class ShinyProxy : CustomResource(), Namespaced { return ShinyProxyStatus() } - @get:JsonIgnore val image: String by lazy { if (spec.get("image")?.isTextual == true) { @@ -66,6 +65,14 @@ class ShinyProxy : CustomResource(), Namespaced { throw IllegalStateException("Cannot create ShinyProxy instance when no FQDN is specified!") } + @get:JsonIgnore + val additionalFqdns: List by lazy { + if (spec.get("additionalFqdns")?.isArray == true) { + return@lazy spec.get("additionalFqdns").elements().asSequence().map { it.textValue() }.toList(); + } + return@lazy listOf() + } + @get:JsonIgnore val replicas: Int by lazy { if (spec.get("replicas")?.isInt == true) { diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt index baa7a3f..42311fc 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt @@ -1060,5 +1060,79 @@ class MainIntegrationTest : IntegrationTestBase() { job.cancel() } + @Test + fun `test additional fqns`() = + setup(Mode.NAMESPACED) { namespace, shinyProxyClient, namespacedClient, _, operator, reconcileListener, _ -> + // 1. create a SP instance + val spTestInstance = ShinyProxyTestInstance( + namespace, + namespacedClient, + shinyProxyClient, + "additional_fqdns.yaml", + reconcileListener + ) + spTestInstance.create() + + val (resourceRetriever, shinyProxyLister) = operator.prepare() + // 2. start the operator and let it do it's work + val job = GlobalScope.launch { + operator.run(resourceRetriever, shinyProxyLister) + } + + // 3. wait until instance is created + spTestInstance.waitForOneReconcile() + + // 4. assert correctness + val sp = spTestInstance.retrieveInstance() + assertNotNull(sp) + val instance = sp.status.instances[0] + assertNotNull(instance) + assertTrue(instance.isLatestInstance) + + // check configmap + spTestInstance.assertConfigMapIsCorrect(sp) + + // check replicaset + spTestInstance.assertReplicaSetIsCorrect(sp) + + // check service + spTestInstance.assertServiceIsCorrect(spTestInstance.retrieveInstance()) + + // check ingress + val allIngresses = namespacedClient.network().v1().ingresses().list().items + assertEquals(1, allIngresses.size) + val ingress = allIngresses.firstOrNull { it.metadata.name == "sp-${sp.metadata.name}-ing".take(63) } + assertNotNull(ingress) + + assertEquals(mapOf( + LabelFactory.APP_LABEL to LabelFactory.APP_LABEL_VALUE, + LabelFactory.REALM_ID_LABEL to sp.realmId, + LabelFactory.LATEST_INSTANCE_LABEL to sp.status.latestInstance()!!.hashOfSpec + ), ingress.metadata.labels) + + assertEquals(2, ingress.spec.rules.size) + val rule1 = ingress.spec.rules[0] + assertNotNull(rule1) + assertEquals(sp.fqdn, rule1.host) + assertEquals(1, rule1.http.paths.size) + val path1 = rule1.http.paths[0] + assertNotNull(path1) + assertEquals(sp.subPath, path1.path) + assertEquals("sp-${sp.metadata.name}-svc".take(63), path1.backend.service.name) + assertEquals(80, path1.backend.service.port.number) + + val rule2 = ingress.spec.rules[1] + assertNotNull(rule2) + assertEquals(sp.additionalFqdns[0], rule2.host) + assertEquals(1, rule2.http.paths.size) + val path2 = rule2.http.paths[0] + assertNotNull(path2) + assertEquals(sp.subPath, path2.path) + assertEquals("sp-${sp.metadata.name}-svc".take(63), path2.backend.service.name) + assertEquals(80, path2.backend.service.port.number) + + job.cancel() + } + } diff --git a/src/test/resources/configs/additional_fqdns.yaml b/src/test/resources/configs/additional_fqdns.yaml new file mode 100644 index 0000000..4f62304 --- /dev/null +++ b/src/test/resources/configs/additional_fqdns.yaml @@ -0,0 +1,36 @@ +apiVersion: openanalytics.eu/v1 +kind: ShinyProxy +metadata: + name: example-shinyproxy +spec: + fqdn: itest.local + additionalFqdns: + - itest2.locals + image: openanalytics/shinyproxy:2.6.1 + proxy: + title: Open Analytics Shiny Proxy + logoUrl: http://www.openanalytics.eu/sites/www.openanalytics.eu/themes/oa/logo.png + landingPage: / + heartbeatRate: 10000 + heartbeatTimeout: 60000 + port: 8080 + authentication: simple + containerBackend: kubernetes + kubernetes: + namespace: itest + users: + - name: demo + password: demo + groups: scientists + - name: demo2 + password: demo2 + groups: mathematicians + specs: + - id: 01_hello + displayName: Hello Application + description: Application which demonstrates the basics of a Shiny app + containerCmd: [ "R", "-e", "shinyproxy::run_01_hello()" ] + containerImage: openanalytics/shinyproxy-demo + - id: 06_tabsets + container-cmd: [ "R", "-e", "shinyproxy::run_06_tabsets()" ] + container-image: openanalytics/shinyproxy-demo From a24270fd3df46634ab671408129262b6fad9b92d Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Mon, 23 Oct 2023 16:55:49 +0200 Subject: [PATCH 10/34] Fix #31334: add version property to shinyproxy pods --- .../components/PodTemplateSpecFactory.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/PodTemplateSpecFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/PodTemplateSpecFactory.kt index d0505c1..30132a2 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/PodTemplateSpecFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/PodTemplateSpecFactory.kt @@ -33,6 +33,12 @@ class PodTemplateSpecFactory { val operator = Operator.getOperatorInstance() + val version = if (shinyProxyInstance.hashOfSpec == shinyProxy.hashOfCurrentSpec) { + System.currentTimeMillis() + } else { + 0 + } + //@formatter:off val template = PodTemplateSpecBuilder() .withNewMetadata() @@ -75,6 +81,10 @@ class PodTemplateSpecFactory { EnvVarBuilder() .withName("PROXY_REALM_ID") .withValue(shinyProxy.realmId) + .build(), + EnvVarBuilder() + .withName("PROXY_VERSION") + .withValue(version.toString()) .build())) .withVolumeMounts(VolumeMountBuilder() .withName("config-volume") From d78810d1770f42da1f54cb80b4ec6ac52cdbf04d Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Thu, 15 Feb 2024 17:08:43 +0100 Subject: [PATCH 11/34] Fix #32380: update dependencies --- pom.xml | 47 ++++++++++--------- .../shinyproxyoperator/KubernetesClient.kt | 44 +++++++++++++++++ .../shinyproxyoperator/Operator.kt | 7 ++- .../components/ConfigMapFactory.kt | 2 +- .../components/IngressFactory.kt | 3 +- .../components/IngressPatcher.kt | 3 -- .../components/ReplicaSetFactory.kt | 2 +- .../components/ResourceNameFactory.kt | 3 +- .../components/ServiceFactory.kt | 2 +- .../controller/ShinyProxyController.kt | 2 +- .../shinyproxyoperator/crd/ShinyProxy.kt | 2 +- .../shinyproxyoperator/MainIntegrationTest.kt | 8 ++-- .../helpers/ChaosInterceptor.kt | 12 ++--- .../helpers/IntegrationTestBase.kt | 22 ++++----- .../helpers/ShinyProxyTestInstance.kt | 5 +- 15 files changed, 102 insertions(+), 62 deletions(-) create mode 100644 src/main/kotlin/eu/openanalytics/shinyproxyoperator/KubernetesClient.kt diff --git a/pom.xml b/pom.xml index d2d0034..3257867 100644 --- a/pom.xml +++ b/pom.xml @@ -1,11 +1,12 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 eu.openanalytics shinyproxy-operator - 2.0.0 + 2.1.0-SNAPSHOT Open Analytics NV @@ -13,15 +14,21 @@ - 11 - 11 - 11 - 6.1.1 - 1.5.20 + 17 + 17 + 17 true - 5.6.0 - 2.17.2 - 4.10.0 + + 2.16.1 + 2.1.3 + 5.10.2 + 1.10.2 + 1.7.3 + 3.0.5 + 1.9.22 + 2.22.1 + 4.12.0 + 6.10.0 @@ -41,7 +48,7 @@ org.jetbrains.kotlinx kotlinx-coroutines-core - 1.5.1 + ${kotlin.coroutines.version} @@ -83,12 +90,12 @@ io.github.microutils kotlin-logging-jvm - 2.0.10 + ${kotlin.logging.version} - javax.json - javax.json-api - 1.1 + jakarta.json + jakarta.json-api + ${jakarta.json-api.version} org.glassfish @@ -98,12 +105,12 @@ com.fasterxml.jackson.datatype jackson-datatype-jsr353 - 2.13.3 + ${jackson.version} com.fasterxml.jackson.module jackson-module-kotlin - 2.13.3 + ${jackson.version} @@ -116,13 +123,13 @@ org.junit.platform junit-platform-launcher - 1.7.0 + ${junit.platform.version} test org.junit.platform junit-platform-commons - 1.7.2 + ${junit.platform.version} test @@ -242,8 +249,6 @@ 3.0 - - check-copyright-headers package diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/KubernetesClient.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/KubernetesClient.kt new file mode 100644 index 0000000..175f6d8 --- /dev/null +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/KubernetesClient.kt @@ -0,0 +1,44 @@ +/** + * ShinyProxy-Operator + * + * Copyright (C) 2021-2023 Open Analytics + * + * =========================================================================== + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Apache License as published by + * The Apache Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Apache License for more details. + * + * You should have received a copy of the Apache License + * along with this program. If not, see + */ +package eu.openanalytics.shinyproxyoperator + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.registerKotlinModule +import io.fabric8.kubernetes.client.KubernetesClientBuilder +import io.fabric8.kubernetes.client.NamespacedKubernetesClient +import io.fabric8.kubernetes.client.http.HttpClient +import io.fabric8.kubernetes.client.utils.KubernetesSerialization + + +fun createKubernetesClient(httpClientFactory: HttpClient.Factory? = null): NamespacedKubernetesClient { + val objectMapper = ObjectMapper() + objectMapper.registerKotlinModule() + val kubernetesSerialization = KubernetesSerialization(objectMapper, true); + val builder = KubernetesClientBuilder() + + if (httpClientFactory != null) { + builder.withHttpClientFactory(httpClientFactory) + } + + return builder.withKubernetesSerialization(kubernetesSerialization) + .build() + .adapt(NamespacedKubernetesClient::class.java) +} diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/Operator.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/Operator.kt index 5e1b642..27f3dda 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/Operator.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/Operator.kt @@ -41,7 +41,6 @@ import io.fabric8.kubernetes.api.model.apps.ReplicaSet import io.fabric8.kubernetes.api.model.apps.ReplicaSetList import io.fabric8.kubernetes.api.model.networking.v1.Ingress import io.fabric8.kubernetes.api.model.networking.v1.IngressList -import io.fabric8.kubernetes.client.DefaultKubernetesClient import io.fabric8.kubernetes.client.KubernetesClientException import io.fabric8.kubernetes.client.NamespacedKubernetesClient import io.fabric8.kubernetes.client.dsl.Resource @@ -77,9 +76,9 @@ class Operator(client: NamespacedKubernetesClient? = null, val probeFailureThreshold: Int val probeTimeout: Int val startupProbeInitialDelay: Int - val processMaxLifetime: Long - val podRetriever: PodRetriever + private val processMaxLifetime: Long + private val podRetriever: PodRetriever private val shinyProxyClient: ShinyProxyClient private val recyclableChecker: IRecyclableChecker @@ -103,7 +102,7 @@ class Operator(client: NamespacedKubernetesClient? = null, if (client != null) { this.client = client } else { - this.client = DefaultKubernetesClient() + this.client = createKubernetesClient() } this.mode = readConfigValue(mode, Mode.CLUSTERED, "SPO_MODE") { diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ConfigMapFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ConfigMapFactory.kt index 36ad56f..b7f97bb 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ConfigMapFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ConfigMapFactory.kt @@ -55,7 +55,7 @@ class ConfigMapFactory(private val kubeClient: KubernetesClient) { .addToData("application.yml", shinyProxy.specAsYaml) .build() //@formatter:on - val createdConfigMap = kubeClient.configMaps().inNamespace(shinyProxy.metadata.namespace).createOrReplace(configMapDefinition) + val createdConfigMap = kubeClient.configMaps().inNamespace(shinyProxy.metadata.namespace).resource(configMapDefinition).serverSideApply() logger.debug { "${shinyProxy.logPrefix(shinyProxyInstance)} [Component/ConfigMap] Created ${createdConfigMap.metadata.name}" } } diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt index d513b8c..491a114 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt @@ -25,7 +25,6 @@ import eu.openanalytics.shinyproxyoperator.crd.ShinyProxyInstance import io.fabric8.kubernetes.api.model.networking.v1.HTTPIngressPath import io.fabric8.kubernetes.api.model.networking.v1.HTTPIngressPathBuilder import io.fabric8.kubernetes.api.model.networking.v1.IngressBuilder -import io.fabric8.kubernetes.api.model.networking.v1.IngressRule import io.fabric8.kubernetes.api.model.networking.v1.IngressRuleBuilder import io.fabric8.kubernetes.client.KubernetesClient import mu.KotlinLogging @@ -71,7 +70,7 @@ class IngressFactory(private val kubeClient: KubernetesClient) { //@formatter:on val patchedIngress = ingressPatcher.patch(ingressDefinition, shinyProxy.parsedIngressPatches) - val createdIngress = kubeClient.network().v1().ingresses().inNamespace(shinyProxy.metadata.namespace).resource(patchedIngress).createOrReplace() + val createdIngress = kubeClient.network().v1().ingresses().inNamespace(shinyProxy.metadata.namespace).resource(patchedIngress).serverSideApply() logger.debug { "${shinyProxy.logPrefix()} [Component/Ingress] Created ${createdIngress.metadata.name}" } } diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressPatcher.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressPatcher.kt index 3585a4a..5b858fc 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressPatcher.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressPatcher.kt @@ -23,9 +23,6 @@ package eu.openanalytics.shinyproxyoperator.components import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.dataformat.yaml.YAMLFactory import com.fasterxml.jackson.datatype.jsr353.JSR353Module -import io.fabric8.kubernetes.api.model.HTTPGetAction -import io.fabric8.kubernetes.api.model.IntOrString -import io.fabric8.kubernetes.api.model.PodTemplateSpec import io.fabric8.kubernetes.api.model.networking.v1.Ingress import mu.KotlinLogging import javax.json.JsonPatch diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ReplicaSetFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ReplicaSetFactory.kt index ca73edb..6983561 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ReplicaSetFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ReplicaSetFactory.kt @@ -58,7 +58,7 @@ class ReplicaSetFactory(private val kubeClient: KubernetesClient) { .build() //@formatter:on - val createdReplicaSet = kubeClient.apps().replicaSets().inNamespace(shinyProxy.metadata.namespace).createOrReplace(replicaSetDefinition) + val createdReplicaSet = kubeClient.apps().replicaSets().inNamespace(shinyProxy.metadata.namespace).resource(replicaSetDefinition).serverSideApply() logger.debug { "${shinyProxy.logPrefix(shinyProxyInstance)} [Component/ReplicaSet] Created ${createdReplicaSet.metadata.name}" } } diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ResourceNameFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ResourceNameFactory.kt index 5356384..d4b51aa 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ResourceNameFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ResourceNameFactory.kt @@ -22,11 +22,10 @@ package eu.openanalytics.shinyproxyoperator.components import eu.openanalytics.shinyproxyoperator.crd.ShinyProxy import eu.openanalytics.shinyproxyoperator.crd.ShinyProxyInstance -import mu.KotlinLogging object ResourceNameFactory { - const val KUBE_RESOURCE_NAME_MAX_LENGTH = 63 + private const val KUBE_RESOURCE_NAME_MAX_LENGTH = 63 fun createNameForConfigMap(shinyProxy: ShinyProxy, shinyProxyInstance: ShinyProxyInstance): String { return "sp-${shinyProxy.metadata.name}-cm-${shinyProxyInstance.hashOfSpec}".take(KUBE_RESOURCE_NAME_MAX_LENGTH) diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ServiceFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ServiceFactory.kt index 3fcdd7d..e6509bc 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ServiceFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ServiceFactory.kt @@ -63,7 +63,7 @@ class ServiceFactory(private val serviceClient: MixedOperation, fun tryUpdateStatus() { val freshShinyProxy = refreshShinyProxy(shinyProxy) updater(freshShinyProxy) - shinyProxyClient.inNamespace(shinyProxy.metadata.namespace).updateStatus(freshShinyProxy) + shinyProxyClient.inNamespace(shinyProxy.metadata.namespace).resource(freshShinyProxy).updateStatus() } for (i in 1..5) { diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt index 26d0975..92ae910 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt @@ -68,7 +68,7 @@ class ShinyProxy : CustomResource(), Namespaced { @get:JsonIgnore val additionalFqdns: List by lazy { if (spec.get("additionalFqdns")?.isArray == true) { - return@lazy spec.get("additionalFqdns").elements().asSequence().map { it.textValue() }.toList(); + return@lazy spec.get("additionalFqdns").elements().asSequence().map { it.textValue() }.toList() } return@lazy listOf() } diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt index 42311fc..40bb1ae 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt @@ -289,16 +289,16 @@ class MainIntegrationTest : IntegrationTestBase() { assertEquals(null, templateSpec.containers[0].startupProbe) // changed by patch - assertEquals(4, templateSpec.containers[0].env.size) // changed by patch + assertEquals(5, templateSpec.containers[0].env.size) // changed by patch assertNotNull(templateSpec.containers[0].env.firstOrNull { it.name == "SP_KUBE_POD_UID" }) assertNotNull(templateSpec.containers[0].env.firstOrNull { it.name == "SP_KUBE_POD_NAME" }) assertNotNull(templateSpec.containers[0].env.firstOrNull { it.name == "TEST_VAR" }) assertEquals("TEST_VALUE", templateSpec.containers[0].env.firstOrNull { it.name == "TEST_VAR" }?.value) assertNotNull(templateSpec.containers[0].env.firstOrNull { it.name == "PROXY_REALM_ID" }) - assertEquals( - sp.metadata.name + '-' + sp.metadata.namespace, + assertEquals(sp.metadata.name + '-' + sp.metadata.namespace, templateSpec.containers[0].env.firstOrNull { it.name == "PROXY_REALM_ID" }?.value ) + assertNotNull(templateSpec.containers[0].env.firstOrNull { it.name == "PROXY_VERSION" }) // check service spTestInstance.assertServiceIsCorrect(spTestInstance.retrieveInstance()) @@ -880,7 +880,7 @@ class MainIntegrationTest : IntegrationTestBase() { fun `operator should properly handle 409 conflicts by replacing the resource`() = setup(Mode.NAMESPACED) { namespace, shinyProxyClient, namespacedClient, stableClient, operator, reconcileListener, _ -> // 1. create conflicting resources - stableClient.load(this.javaClass.getResourceAsStream("/config/conflict.yaml")).createOrReplace() + stableClient.load(this.javaClass.getResourceAsStream("/config/conflict.yaml")).serverSideApply() // 2. create a SP instance val spTestInstance = ShinyProxyTestInstance( diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ChaosInterceptor.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ChaosInterceptor.kt index b0b0260..073d165 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ChaosInterceptor.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ChaosInterceptor.kt @@ -20,12 +20,11 @@ */ package eu.openanalytics.shinyproxyoperator.helpers +import eu.openanalytics.shinyproxyoperator.createKubernetesClient import io.fabric8.kubernetes.api.model.StatusBuilder -import io.fabric8.kubernetes.client.ConfigBuilder -import io.fabric8.kubernetes.client.DefaultKubernetesClient import io.fabric8.kubernetes.client.KubernetesClientException +import io.fabric8.kubernetes.client.NamespacedKubernetesClient import io.fabric8.kubernetes.client.okhttp.OkHttpClientFactory -import io.fabric8.kubernetes.client.utils.HttpClientUtils import mu.KotlinLogging import okhttp3.Interceptor import okhttp3.OkHttpClient @@ -61,17 +60,14 @@ class ChaosInterceptor : Interceptor { } companion object { - fun createChaosKubernetesClient(): DefaultKubernetesClient { + fun createChaosKubernetesClient(): NamespacedKubernetesClient { val factory = object: OkHttpClientFactory() { override fun additionalConfig(builder: OkHttpClient.Builder) { builder.addInterceptor(ChaosInterceptor()) } } - val config = ConfigBuilder().build() - val httpClient = factory.createHttpClient(config).newBuilder().build(); - - return DefaultKubernetesClient(httpClient, config) + return createKubernetesClient(factory) } } diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/IntegrationTestBase.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/IntegrationTestBase.kt index 57d443b..4bd6bbc 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/IntegrationTestBase.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/IntegrationTestBase.kt @@ -25,9 +25,9 @@ import eu.openanalytics.shinyproxyoperator.Operator import eu.openanalytics.shinyproxyoperator.ShinyProxyClient import eu.openanalytics.shinyproxyoperator.components.LabelFactory import eu.openanalytics.shinyproxyoperator.crd.ShinyProxy +import eu.openanalytics.shinyproxyoperator.createKubernetesClient import io.fabric8.kubernetes.api.model.NamespaceBuilder import io.fabric8.kubernetes.api.model.PodList -import io.fabric8.kubernetes.client.DefaultKubernetesClient import io.fabric8.kubernetes.client.KubernetesClient import io.fabric8.kubernetes.client.KubernetesClientException import io.fabric8.kubernetes.client.NamespacedKubernetesClient @@ -49,11 +49,11 @@ abstract class IntegrationTestBase { protected val chaosEnabled = System.getenv("SPO_TEST_CHAOS") != null - private val stableClient = DefaultKubernetesClient() - private val chaosClient: DefaultKubernetesClient = if (chaosEnabled) { + private val stableClient: NamespacedKubernetesClient = createKubernetesClient() + private val chaosClient: NamespacedKubernetesClient = if (chaosEnabled) { ChaosInterceptor.createChaosKubernetesClient() } else { - DefaultKubernetesClient() + createKubernetesClient() } @AfterEach @@ -70,9 +70,9 @@ abstract class IntegrationTestBase { @AfterAll fun cleanupCRD() { runBlocking { - val client = DefaultKubernetesClient() + val client = createKubernetesClient() val crd = client.apiextensions().v1().customResourceDefinitions().load(this.javaClass.getResource("/crd.yaml")).get() - client.apiextensions().v1().customResourceDefinitions().delete(crd) + client.apiextensions().v1().customResourceDefinitions().resource(crd).delete() delay(2000) while (client.apiextensions().v1().customResourceDefinitions().list().items.firstOrNull { it.spec.group == "openanalytics.eu" && it.spec.names.plural == "shinyproxies" } != null) { @@ -95,8 +95,7 @@ abstract class IntegrationTestBase { // 2. create the CRD if (!crdExists()) { - val crd = stableClient.apiextensions().v1().customResourceDefinitions().load(this.javaClass.getResource("/crd.yaml")).get() - stableClient.apiextensions().v1().customResourceDefinitions().createOrReplace(crd) + stableClient.apiextensions().v1().customResourceDefinitions().load(this.javaClass.getResource("/crd.yaml")).serverSideApply() } // 3. create the operator @@ -121,7 +120,7 @@ abstract class IntegrationTestBase { // 5. remove all instances try { for (sp in shinyProxyClient.inNamespace(namespace).list().items) { - shinyProxyClient.delete(sp) + shinyProxyClient.resource(sp).delete() } } catch (e: KubernetesClientException) { // no SP created @@ -137,11 +136,12 @@ abstract class IntegrationTestBase { private fun createNamespaces() { for (managedNamespace in managedNamespaces) { - stableClient.namespaces().create(NamespaceBuilder() + stableClient.namespaces().resource(NamespaceBuilder() .withNewMetadata() .withName(managedNamespace) .endMetadata() .build()) + .serverSideApply() } } @@ -203,7 +203,7 @@ abstract class IntegrationTestBase { } private fun setupServiceAccount() { - stableClient.load(this.javaClass.getResourceAsStream("/configs/serviceaccount.yaml")).createOrReplace() + stableClient.load(this.javaClass.getResourceAsStream("/configs/serviceaccount.yaml")).serverSideApply() } protected fun getPodsForInstance(instanceHash: String): PodList? { diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt index 1648415..f973ee9 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt @@ -47,7 +47,7 @@ class ShinyProxyTestInstance(private val namespace: String, private val objectMapper = ObjectMapper().registerKotlinModule() fun create(): ShinyProxy { - val sp: ShinyProxy = shinyProxyClient.inNamespace(namespace).load(this.javaClass.getResourceAsStream("/configs/$fileName")).createOrReplace() + val sp: ShinyProxy = shinyProxyClient.inNamespace(namespace).load(this.javaClass.getResourceAsStream("/configs/$fileName")).serverSideApply() hash = sp.hashOfCurrentSpec // assert that it has been created @@ -187,10 +187,11 @@ class ShinyProxyTestInstance(private val namespace: String, assertEquals(sp.image, templateSpec.containers[0].image) assertEquals(sp.imagePullPolicy, templateSpec.containers[0].imagePullPolicy) - assertEquals(3, templateSpec.containers[0].env.size) + assertEquals(4, templateSpec.containers[0].env.size) assertNotNull(templateSpec.containers[0].env.firstOrNull { it.name == "SP_KUBE_POD_UID" }) assertNotNull(templateSpec.containers[0].env.firstOrNull { it.name == "SP_KUBE_POD_NAME" }) assertNotNull(templateSpec.containers[0].env.firstOrNull { it.name == "PROXY_REALM_ID" }) + assertNotNull(templateSpec.containers[0].env.firstOrNull { it.name == "PROXY_VERSION" }) assertEquals(sp.metadata.name + '-'+ sp.metadata.namespace, templateSpec.containers[0].env.firstOrNull { it.name == "PROXY_REALM_ID" }?.value) assertEquals(1, templateSpec.containers[0].volumeMounts.size) From 092690ec141c8b284daf4609c245c09a8a77177b Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Thu, 15 Feb 2024 17:10:25 +0100 Subject: [PATCH 12/34] Fix #32380: update license headers --- LICENSE_HEADER | 2 +- .../eu/openanalytics/shinyproxyoperator/KubernetesClient.kt | 2 +- src/main/kotlin/eu/openanalytics/shinyproxyoperator/Operator.kt | 2 +- .../shinyproxyoperator/components/ConfigMapFactory.kt | 2 +- .../shinyproxyoperator/components/IngressFactory.kt | 2 +- .../shinyproxyoperator/components/IngressPatcher.kt | 2 +- .../openanalytics/shinyproxyoperator/components/LabelFactory.kt | 2 +- .../shinyproxyoperator/components/PodTemplateSpecFactory.kt | 2 +- .../shinyproxyoperator/components/PodTemplateSpecPatcher.kt | 2 +- .../shinyproxyoperator/components/ReplicaSetFactory.kt | 2 +- .../shinyproxyoperator/components/ResourceNameFactory.kt | 2 +- .../shinyproxyoperator/components/ServiceFactory.kt | 2 +- .../shinyproxyoperator/controller/IReconcileListener.kt | 2 +- .../shinyproxyoperator/controller/IRecyclableChecker.kt | 2 +- .../shinyproxyoperator/controller/IngressController.kt | 2 +- .../openanalytics/shinyproxyoperator/controller/PodRetriever.kt | 2 +- .../shinyproxyoperator/controller/RecyclableChecker.kt | 2 +- .../shinyproxyoperator/controller/ResourceListener.kt | 2 +- .../shinyproxyoperator/controller/ResourceRetriever.kt | 2 +- .../shinyproxyoperator/controller/ServiceController.kt | 2 +- .../shinyproxyoperator/controller/ShinyProxyController.kt | 2 +- .../shinyproxyoperator/controller/ShinyProxyEvent.kt | 2 +- .../shinyproxyoperator/controller/ShinyProxyEventType.kt | 2 +- .../shinyproxyoperator/controller/ShinyProxyListener.kt | 2 +- .../eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt | 2 +- .../openanalytics/shinyproxyoperator/crd/ShinyProxyInstance.kt | 2 +- .../eu/openanalytics/shinyproxyoperator/crd/ShinyProxyStatus.kt | 2 +- .../kotlin/eu/openanalytics/shinyproxyoperator/extensions.kt | 2 +- .../eu/openanalytics/shinyproxyoperator/isInManagedNamespace.kt | 2 +- src/main/kotlin/eu/openanalytics/shinyproxyoperator/main.kt | 2 +- src/main/kotlin/eu/openanalytics/shinyproxyoperator/retry.kt | 2 +- .../eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt | 2 +- .../shinyproxyoperator/helpers/ChaosInterceptor.kt | 2 +- .../shinyproxyoperator/helpers/IntegrationTestBase.kt | 2 +- .../shinyproxyoperator/helpers/MockRecyclablecheckler.kt | 2 +- .../shinyproxyoperator/helpers/ReconcileListener.kt | 2 +- .../shinyproxyoperator/helpers/ShinyProxyTestInstance.kt | 2 +- .../shinyproxyoperator/helpers/junit/TestExecutionListener.kt | 2 +- 38 files changed, 38 insertions(+), 38 deletions(-) diff --git a/LICENSE_HEADER b/LICENSE_HEADER index 505d8dc..7679cf4 100755 --- a/LICENSE_HEADER +++ b/LICENSE_HEADER @@ -1,6 +1,6 @@ ShinyProxy-Operator -Copyright (C) 2021-2023 Open Analytics +Copyright (C) 2021-2024 Open Analytics =========================================================================== diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/KubernetesClient.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/KubernetesClient.kt index 175f6d8..140793f 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/KubernetesClient.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/KubernetesClient.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/Operator.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/Operator.kt index 27f3dda..a8bb94c 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/Operator.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/Operator.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ConfigMapFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ConfigMapFactory.kt index b7f97bb..156e808 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ConfigMapFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ConfigMapFactory.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt index 491a114..c5a99fe 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressPatcher.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressPatcher.kt index 5b858fc..681a1c6 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressPatcher.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressPatcher.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/LabelFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/LabelFactory.kt index efea108..5ef8d8d 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/LabelFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/LabelFactory.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/PodTemplateSpecFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/PodTemplateSpecFactory.kt index 30132a2..74a6a50 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/PodTemplateSpecFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/PodTemplateSpecFactory.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/PodTemplateSpecPatcher.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/PodTemplateSpecPatcher.kt index d9a7286..ed1a3ec 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/PodTemplateSpecPatcher.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/PodTemplateSpecPatcher.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ReplicaSetFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ReplicaSetFactory.kt index 6983561..0755a56 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ReplicaSetFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ReplicaSetFactory.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ResourceNameFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ResourceNameFactory.kt index d4b51aa..bc7c410 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ResourceNameFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ResourceNameFactory.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ServiceFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ServiceFactory.kt index e6509bc..0461e70 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ServiceFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ServiceFactory.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/IReconcileListener.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/IReconcileListener.kt index 5678975..29efdde 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/IReconcileListener.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/IReconcileListener.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/IRecyclableChecker.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/IRecyclableChecker.kt index 979545b..5a5fba4 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/IRecyclableChecker.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/IRecyclableChecker.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/IngressController.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/IngressController.kt index 8c0d7a3..67f4864 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/IngressController.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/IngressController.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/PodRetriever.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/PodRetriever.kt index c6fef0d..832caeb 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/PodRetriever.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/PodRetriever.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/RecyclableChecker.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/RecyclableChecker.kt index d8d10db..233ffe8 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/RecyclableChecker.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/RecyclableChecker.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ResourceListener.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ResourceListener.kt index a3d2417..de36d13 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ResourceListener.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ResourceListener.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ResourceRetriever.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ResourceRetriever.kt index 763d304..f5f0097 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ResourceRetriever.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ResourceRetriever.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ServiceController.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ServiceController.kt index f0d7dda..f9e9d26 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ServiceController.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ServiceController.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyController.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyController.kt index 4efc149..cc60408 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyController.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyController.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyEvent.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyEvent.kt index 29dc64f..adf4f6a 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyEvent.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyEvent.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyEventType.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyEventType.kt index ba59232..5bee4fb 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyEventType.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyEventType.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyListener.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyListener.kt index 997809f..858725e 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyListener.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyListener.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt index 92ae910..bf9a52e 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyInstance.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyInstance.kt index a275885..377260d 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyInstance.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyInstance.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyStatus.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyStatus.kt index ac18b9d..f35c351 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyStatus.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyStatus.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/extensions.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/extensions.kt index 4c2ad52..f417c94 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/extensions.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/extensions.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/isInManagedNamespace.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/isInManagedNamespace.kt index 0130176..5d6216d 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/isInManagedNamespace.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/isInManagedNamespace.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/main.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/main.kt index 054317d..91d1eae 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/main.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/main.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/retry.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/retry.kt index daf917b..85cb19f 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/retry.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/retry.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt index 40bb1ae..06e97e8 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ChaosInterceptor.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ChaosInterceptor.kt index 073d165..ee698a2 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ChaosInterceptor.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ChaosInterceptor.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/IntegrationTestBase.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/IntegrationTestBase.kt index 4bd6bbc..50026ba 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/IntegrationTestBase.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/IntegrationTestBase.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/MockRecyclablecheckler.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/MockRecyclablecheckler.kt index 01ebc6e..83551e8 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/MockRecyclablecheckler.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/MockRecyclablecheckler.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ReconcileListener.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ReconcileListener.kt index 8257d5c..3168200 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ReconcileListener.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ReconcileListener.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt index f973ee9..4a69ce4 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/junit/TestExecutionListener.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/junit/TestExecutionListener.kt index 3e38f84..accb2cb 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/junit/TestExecutionListener.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/junit/TestExecutionListener.kt @@ -1,7 +1,7 @@ /** * ShinyProxy-Operator * - * Copyright (C) 2021-2023 Open Analytics + * Copyright (C) 2021-2024 Open Analytics * * =========================================================================== * From d44ea5161e9c40619722b2c6891c228865baf95c Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Fri, 16 Feb 2024 15:12:41 +0100 Subject: [PATCH 13/34] Fix #32380: fix tests --- .../openanalytics/shinyproxyoperator/MainIntegrationTest.kt | 2 +- src/test/resources/configs/conflict.yaml | 6 +++--- src/test/resources/configs/simple_config_with_patches.yaml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt index 06e97e8..2f571ca 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt @@ -880,7 +880,7 @@ class MainIntegrationTest : IntegrationTestBase() { fun `operator should properly handle 409 conflicts by replacing the resource`() = setup(Mode.NAMESPACED) { namespace, shinyProxyClient, namespacedClient, stableClient, operator, reconcileListener, _ -> // 1. create conflicting resources - stableClient.load(this.javaClass.getResourceAsStream("/config/conflict.yaml")).serverSideApply() + stableClient.load(this.javaClass.getResourceAsStream("/configs/conflict.yaml")).serverSideApply() // 2. create a SP instance val spTestInstance = ShinyProxyTestInstance( diff --git a/src/test/resources/configs/conflict.yaml b/src/test/resources/configs/conflict.yaml index 4685676..462e623 100644 --- a/src/test/resources/configs/conflict.yaml +++ b/src/test/resources/configs/conflict.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: ConfigMap metadata: - name: sp-example-shinyproxy-cm-abfc24c726e9e87ea7c633384f2a6599352490 + name: sp-example-shinyproxy-cm-502467f68c769f1a6a3f641603ec053e15a350 namespace: itest data: application.yml: | @@ -10,7 +10,7 @@ data: apiVersion: v1 kind: Service metadata: - name: sp-example-shinyproxy-svc-abfc24c726e9e87ea7c633384f2a659935249 + name: sp-example-shinyproxy-svc namespace: itest spec: type: ClusterIP @@ -25,7 +25,7 @@ spec: apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: sp-example-shinyproxy-ing-abfc24c726e9e87ea7c633384f2a659935249 + name: sp-example-shinyproxy-ing namespace: itest spec: rules: diff --git a/src/test/resources/configs/simple_config_with_patches.yaml b/src/test/resources/configs/simple_config_with_patches.yaml index e29c91f..26efe23 100644 --- a/src/test/resources/configs/simple_config_with_patches.yaml +++ b/src/test/resources/configs/simple_config_with_patches.yaml @@ -44,7 +44,7 @@ spec: failureThreshold: 1 httpGet: path: /actuator/health/liveness - port: 9090 + port: "9090" scheme: HTTP periodSeconds: 1 initialDelaySeconds: 30 @@ -56,7 +56,7 @@ spec: failureThreshold: 1 httpGet: path: /actuator/health/readiness - port: 9090 + port: "9090" scheme: HTTP periodSeconds: 1 initialDelaySeconds: 30 From 3e8fcfdb21d4752e915244eee8b4f69ce66b131a Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Fri, 16 Feb 2024 15:16:01 +0100 Subject: [PATCH 14/34] Fix #32380: update k8s in gha workflow --- .github/workflows/workflows.yaml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/workflows.yaml b/.github/workflows/workflows.yaml index 2bfe58f..ecfe0dc 100644 --- a/.github/workflows/workflows.yaml +++ b/.github/workflows/workflows.yaml @@ -10,10 +10,12 @@ jobs: matrix: java: [ 11 ] kubernetes: - - 'v1.22.17' - - 'v1.23.15' - - 'v1.24.9' - - 'v1.25.5' + - 'v1.24.17' + - 'v1.25.15' + - 'v1.26.14' + - 'v1.27.11' + - 'v1.28.7' + - 'v1.29.2' steps: - uses: actions/checkout@v2 From c7905e564c948a67db71f0e39da543cbaf661c19 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Fri, 16 Feb 2024 15:25:52 +0100 Subject: [PATCH 15/34] Fix #32380: update gha actions --- .github/workflows/workflows.yaml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/workflows.yaml b/.github/workflows/workflows.yaml index ecfe0dc..92c863e 100644 --- a/.github/workflows/workflows.yaml +++ b/.github/workflows/workflows.yaml @@ -18,26 +18,25 @@ jobs: - 'v1.29.2' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up JDK uses: actions/setup-java@v1 with: java-version: ${{ matrix.java }} - name: Cache Maven packages - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} restore-keys: ${{ runner.os }}-m2 - name: Setup Minikube - uses: manusa/actions-setup-minikube@v2.7.2 + uses: manusa/actions-setup-minikube@v2.10.0 with: - minikube version: 'v1.28.0' + minikube version: 'v1.32.0' kubernetes version: ${{ matrix.kubernetes }} github token: ${{ secrets.GITHUB_TOKEN }} - name: Pull images run: | - minikube image pull ledfan/shinyproxy:3.0.0-SNAPSHOT-20230116.114943 minikube image pull openanalytics/shinyproxy-demo minikube image pull curlimages/curl:latest - name: Build with Maven From a62a68ad32819a513428b1637fc4c4aef0ec8860 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Thu, 6 Apr 2023 14:19:03 +0200 Subject: [PATCH 16/34] Improve docs --- docs/deployment/README.md | 6 +++--- .../1-namespaced/resources/shinyproxy.shinyproxy.yaml | 2 +- .../shinyproxy-dept2/resources/shinyproxy.shinyproxy.yaml | 2 +- .../shinyproxy/resources/shinyproxy.shinyproxy.yaml | 2 +- .../shinyproxy/resources/shinyproxy.shinyproxy.yaml | 2 +- .../resources/shinyproxy1.shinyproxy.yaml | 2 +- .../resources/shinyproxy2.shinyproxy.yaml | 4 +--- .../resources/shinyproxy3.shinyproxy.yaml | 4 +--- 8 files changed, 10 insertions(+), 14 deletions(-) diff --git a/docs/deployment/README.md b/docs/deployment/README.md index 317e42b..c9b06c0 100644 --- a/docs/deployment/README.md +++ b/docs/deployment/README.md @@ -191,7 +191,7 @@ important: - `kubernetesIngressPatches`: allows to patch the `Ingress` resources created by the operator (see the [example](#modify-the-ingress-resource)) - `image`: the docker image to use for the ShinyProxy server ( - e.g. `openanalytics/shinyproxy:3.0.0`) + e.g. `openanalytics/shinyproxy:3.0.1`) - `imagePullPolicy`: the pull policy for ShinyProxy Image; the default value is `IfNotPresent`; valid options are `Never`, `IfNotPresent` and `Always`. - `fqdn`: the FQDN at which the service should be available, e.g. ` @@ -231,7 +231,7 @@ spec: - op: add path: /spec/ingressClassName value: nginx - image: openanalytics/shinyproxy:3.0.0 + image: openanalytics/shinyproxy:3.0.1 imagePullPolicy: Always fqdn: shinyproxy-demo.local ``` @@ -290,7 +290,7 @@ spec: - op: add path: /spec/serviceAccountName value: shinyproxy-sa - image: openanalytics/shinyproxy:3.0.0 + image: openanalytics/shinyproxy:3.0.1 imagePullPolicy: Always fqdn: shinyproxy-demo.local ``` diff --git a/docs/deployment/overlays/1-namespaced/resources/shinyproxy.shinyproxy.yaml b/docs/deployment/overlays/1-namespaced/resources/shinyproxy.shinyproxy.yaml index 4ff4c65..289a4f3 100644 --- a/docs/deployment/overlays/1-namespaced/resources/shinyproxy.shinyproxy.yaml +++ b/docs/deployment/overlays/1-namespaced/resources/shinyproxy.shinyproxy.yaml @@ -88,6 +88,6 @@ spec: - op: add path: /spec/ingressClassName value: nginx - image: openanalytics/shinyproxy:3.0.0 + image: openanalytics/shinyproxy:3.0.1 imagePullPolicy: Always fqdn: shinyproxy-demo.local diff --git a/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/shinyproxy.shinyproxy.yaml b/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/shinyproxy.shinyproxy.yaml index 75dbca0..c1d4927 100644 --- a/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/shinyproxy.shinyproxy.yaml +++ b/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/shinyproxy.shinyproxy.yaml @@ -87,6 +87,6 @@ spec: - op: add path: /spec/ingressClassName value: nginx - image: openanalytics/shinyproxy:3.0.0 + image: openanalytics/shinyproxy:3.0.1 imagePullPolicy: Always fqdn: shinyproxy-demo2.local diff --git a/docs/deployment/overlays/2-clustered/shinyproxy/resources/shinyproxy.shinyproxy.yaml b/docs/deployment/overlays/2-clustered/shinyproxy/resources/shinyproxy.shinyproxy.yaml index a0ea13f..587d58d 100644 --- a/docs/deployment/overlays/2-clustered/shinyproxy/resources/shinyproxy.shinyproxy.yaml +++ b/docs/deployment/overlays/2-clustered/shinyproxy/resources/shinyproxy.shinyproxy.yaml @@ -87,6 +87,6 @@ spec: - op: add path: /spec/ingressClassName value: nginx - image: openanalytics/shinyproxy:3.0.0 + image: openanalytics/shinyproxy:3.0.1 imagePullPolicy: Always fqdn: shinyproxy-demo.local diff --git a/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/resources/shinyproxy.shinyproxy.yaml b/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/resources/shinyproxy.shinyproxy.yaml index c0388aa..62061a8 100644 --- a/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/resources/shinyproxy.shinyproxy.yaml +++ b/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/resources/shinyproxy.shinyproxy.yaml @@ -92,7 +92,7 @@ spec: - op: add path: /spec/ingressClassName value: nginx - image: openanalytics/shinyproxy:3.0.0 + image: openanalytics/shinyproxy:3.0.1 imagePullPolicy: Always fqdn: shinyproxy-demo.local appNamespaces: diff --git a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy1.shinyproxy.yaml b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy1.shinyproxy.yaml index 9e00faa..60f304f 100644 --- a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy1.shinyproxy.yaml +++ b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy1.shinyproxy.yaml @@ -90,6 +90,6 @@ spec: - op: add path: /spec/ingressClassName value: nginx - image: openanalytics/shinyproxy:3.0.0 + image: openanalytics/shinyproxy:3.0.1 imagePullPolicy: Always fqdn: shinyproxy-demo.local diff --git a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy2.shinyproxy.yaml b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy2.shinyproxy.yaml index 9987150..2ebb867 100644 --- a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy2.shinyproxy.yaml +++ b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy2.shinyproxy.yaml @@ -13,8 +13,6 @@ spec: spring: session: store-type: redis - redis: - configure-action: none redis: host: redis password: ${REDIS_PASSWORD} @@ -93,6 +91,6 @@ spec: - op: add path: /spec/ingressClassName value: nginx - image: openanalytics/shinyproxy:3.0.0 + image: openanalytics/shinyproxy:3.0.1 imagePullPolicy: Always fqdn: shinyproxy-demo.local diff --git a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy3.shinyproxy.yaml b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy3.shinyproxy.yaml index 1b57588..f38eea5 100644 --- a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy3.shinyproxy.yaml +++ b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy3.shinyproxy.yaml @@ -13,8 +13,6 @@ spec: spring: session: store-type: redis - redis: - configure-action: none redis: host: redis password: ${REDIS_PASSWORD} @@ -93,6 +91,6 @@ spec: - op: add path: /spec/ingressClassName value: nginx - image: openanalytics/shinyproxy:3.0.0 + image: openanalytics/shinyproxy:3.0.1 imagePullPolicy: Always fqdn: shinyproxy-demo.local From cb978d5a129b94a4c1bcf5fa9d10b73b54610ab4 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Thu, 6 Apr 2023 14:22:56 +0200 Subject: [PATCH 17/34] Update README --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ae512cd..01a1861 100644 --- a/README.md +++ b/README.md @@ -174,7 +174,15 @@ Be aware of these changes when updating to version 2.0.0: required to set up Redis Sentinel (based on the bitnamai Redis helm chart). The best way to update to ShinyProxy 2.0.0 is by creating a fresh deployment of -the operator and migrating users to this new deployment. +the operator and migrating users to this new deployment. The following changes +need to be made to the ShinyProxy configuration file: + +- add the property `proxy.store-mode: Redis` +- add the property `proxy.stop-proxies-on-shutdown: false` +- optionally add the + property [`kubernetesIngressPatches`](docs/deployment#modify-the-ingress-resource) + in order to customize the ingress created by the operator. +- update the ShinyProxy image to `openanalytics/shinyproxy:3.0.1` ## Java Version From f4158506a8f3b927035e3545a4f26be61f2ecae5 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Thu, 6 Apr 2023 15:03:35 +0200 Subject: [PATCH 18/34] Improve examples --- docs/deployment/README.md | 72 +++++++++++++------ .../overlays/1-namespaced/kustomization.yaml | 6 +- .../resources/shinyproxy.shinyproxy.yaml | 6 ++ .../2-clustered/redis/kustomization.yaml | 2 +- .../shinyproxy-dept2/kustomization.yaml | 2 +- .../resources/shinyproxy.shinyproxy.yaml | 6 ++ .../shinyproxy-operator/kustomization.yaml | 2 +- .../2-clustered/shinyproxy/kustomization.yaml | 2 +- .../resources/shinyproxy.shinyproxy.yaml | 12 ++++ .../shinyproxy/kustomization.yaml | 6 +- .../resources/shinyproxy.shinyproxy.yaml | 6 ++ .../4-namespaced-multi/kustomization.yaml | 6 +- .../resources/shinyproxy1.shinyproxy.yaml | 6 ++ .../resources/shinyproxy2.shinyproxy.yaml | 6 ++ .../resources/shinyproxy3.shinyproxy.yaml | 6 ++ 15 files changed, 112 insertions(+), 34 deletions(-) diff --git a/docs/deployment/README.md b/docs/deployment/README.md index c9b06c0..c33e72a 100644 --- a/docs/deployment/README.md +++ b/docs/deployment/README.md @@ -84,12 +84,35 @@ ShinyProxy operator on minikube. ``` 6. Once all deployments are finished, you can access ShinyProxy at - `shinyproxy-demo.local`. + `shinyproxy-demo.local`. You will get a security warning from your browser + because if the invalid (self-signed) certificate. You can safely bypass this + warning during this example. 7. Wait until the ShinyProxy instance is fully started. (before you will see a `Not Found` page). 8. Try to launch an application and keep this application running. -9. Change something in the `resources/shinyproxy.shinyproxy.yaml` file (e.g. - the `title` property) and then run: +9. Change something in the `resources/shinyproxy.shinyproxy.yaml` file. For + example change the `title` property and instruct the operator to create two + ShinyProxy replicas: + + ```yaml + apiVersion: openanalytics.eu/v1 + kind: ShinyProxy + metadata: + name: shinyproxy + namespace: shinyproxy + spec: + # ... + proxy: + store-mode: Redis + stop-proxies-on-shutdown: false + title: ShinyProxy 2 # <- MAKE THE CHANGE HERE + # ... + replicas: 2 # <- ADD THIS LINE + image: openanalytics/shinyproxy:3.0.1 + imagePullPolicy: Always + fqdn: shinyproxy-demo.local + ``` +10. Apply this change using `kubectl`: ```bash kubectl apply -f resources/shinyproxy.shinyproxy.yaml @@ -101,20 +124,19 @@ ShinyProxy operator on minikube. Websocket connections. New requests will immediately be handled by the new server as soon as it is ready. Try going to the main page of ShinyProxy and check whether the change your made has been applied. -10. Try the other examples: - - ```bash - kubectl delete namespace/shinyproxy - kubectl delete namespace/shinyproxy-operator # may fail - kubectl delete namespace/shinyproxy-dept2 # may fail - kubectl delete namespace/my-namespace # may fail - kubectl delete namespace/redis # may fail - kubectl delete namespace/skipper # may fail - kubectl delete -n default ingress/nginx-to-skipper-ingress # may fail - kubectl delete -n skipper ingress/nginx-to-skipper-ingress # may fail - cd directory_of_example - kustomize build . | kubectl apply -f - - ``` +11. Try the other examples. The following commands first remove the current + example, next you can open another example (e.g. `2-clustered`) and deploy + it using `kubectl`: + +```bash +kubectl delete namespace/shinyproxy +kubectl delete namespace/shinyproxy-operator # may fail +kubectl delete namespace/shinyproxy-dept2 # may fail +kubectl delete namespace/my-namespace # may fail +kubectl delete namespace/redis # may fail +cd ../2-clustered +kustomize build . | kubectl apply -f - +``` ## Overview of examples @@ -231,6 +253,12 @@ spec: - op: add path: /spec/ingressClassName value: nginx + - op: add + path: /spec/tls + value: + - hosts: + - shinyproxy-demo.local + # secretName: example # uncomment and change this line if needed image: openanalytics/shinyproxy:3.0.1 imagePullPolicy: Always fqdn: shinyproxy-demo.local @@ -238,10 +266,12 @@ spec: The first patch adds some additional annotations to the ShinyProx resource. For example, in order to set up a redirect from HTTP to HTTPS. The second patch -changes the ingressClassName to `nginx`. Any patch is accepted, but make sure -that the resulting Ingress resource still works for the ShinyProxy Deployment. -The ShinyProxy Operator logs the manifest before and after applying the patch, -this can be useful while creating the patches. +changes the ingressClassName to `nginx`. Finally, the last patch configures TLS +for the ingress resource. In a production environment, you can uncomment the +line with the `secretName` to refer to a proper secret. Any patch is accepted, +but make sure that the resulting Ingress resource still works for the ShinyProxy +Deployment. The ShinyProxy Operator logs the manifest before and after applying +the patch, this can be useful while creating the patches. **Note:** the previous section only applies to version 2 of the operator. Version 1 behaves differently since it used Skipper as (intermediate) ingress diff --git a/docs/deployment/overlays/1-namespaced/kustomization.yaml b/docs/deployment/overlays/1-namespaced/kustomization.yaml index 09bb86d..c81e966 100644 --- a/docs/deployment/overlays/1-namespaced/kustomization.yaml +++ b/docs/deployment/overlays/1-namespaced/kustomization.yaml @@ -3,8 +3,8 @@ kind: Kustomization namespace: shinyproxy resources: - - ../../bases/redis-sentinel - - ../../bases/namespaced - - ../../bases/shinyproxy + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/redis-sentinel?ref=v2.0.0 + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/namespaced?ref=v2.0.0 + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/shinyproxy?ref=v2.0.0 - resources/shinyproxy.namespace.yaml - resources/shinyproxy.shinyproxy.yaml diff --git a/docs/deployment/overlays/1-namespaced/resources/shinyproxy.shinyproxy.yaml b/docs/deployment/overlays/1-namespaced/resources/shinyproxy.shinyproxy.yaml index 289a4f3..6c101cf 100644 --- a/docs/deployment/overlays/1-namespaced/resources/shinyproxy.shinyproxy.yaml +++ b/docs/deployment/overlays/1-namespaced/resources/shinyproxy.shinyproxy.yaml @@ -88,6 +88,12 @@ spec: - op: add path: /spec/ingressClassName value: nginx + - op: add + path: /spec/tls + value: + - hosts: + - shinyproxy-demo.local + # secretName: example # uncomment and change this line if needed image: openanalytics/shinyproxy:3.0.1 imagePullPolicy: Always fqdn: shinyproxy-demo.local diff --git a/docs/deployment/overlays/2-clustered/redis/kustomization.yaml b/docs/deployment/overlays/2-clustered/redis/kustomization.yaml index 093c2aa..a76b976 100644 --- a/docs/deployment/overlays/2-clustered/redis/kustomization.yaml +++ b/docs/deployment/overlays/2-clustered/redis/kustomization.yaml @@ -3,7 +3,7 @@ kind: Kustomization namespace: redis resources: - - ../../../bases/redis-sentinel + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/redis-sentinel?ref=v2.0.0 - resources/redis.namespace.yaml patchesStrategicMerge: diff --git a/docs/deployment/overlays/2-clustered/shinyproxy-dept2/kustomization.yaml b/docs/deployment/overlays/2-clustered/shinyproxy-dept2/kustomization.yaml index 91eca23..8dec7e3 100644 --- a/docs/deployment/overlays/2-clustered/shinyproxy-dept2/kustomization.yaml +++ b/docs/deployment/overlays/2-clustered/shinyproxy-dept2/kustomization.yaml @@ -3,7 +3,7 @@ kind: Kustomization namespace: shinyproxy-dept2 resources: - - ../../../bases/shinyproxy + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/shinyproxy?ref=v2.0.0 - resources/redis.secret.yaml - resources/shinyproxy.shinyproxy.yaml - resources/shinyproxy.namespace.yaml diff --git a/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/shinyproxy.shinyproxy.yaml b/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/shinyproxy.shinyproxy.yaml index c1d4927..45a2901 100644 --- a/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/shinyproxy.shinyproxy.yaml +++ b/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/shinyproxy.shinyproxy.yaml @@ -87,6 +87,12 @@ spec: - op: add path: /spec/ingressClassName value: nginx + - op: add + path: /spec/tls + value: + - hosts: + - shinyproxy-demo2.local + # secretName: example # uncomment and change this line if needed image: openanalytics/shinyproxy:3.0.1 imagePullPolicy: Always fqdn: shinyproxy-demo2.local diff --git a/docs/deployment/overlays/2-clustered/shinyproxy-operator/kustomization.yaml b/docs/deployment/overlays/2-clustered/shinyproxy-operator/kustomization.yaml index 4d57e8f..c2f6122 100644 --- a/docs/deployment/overlays/2-clustered/shinyproxy-operator/kustomization.yaml +++ b/docs/deployment/overlays/2-clustered/shinyproxy-operator/kustomization.yaml @@ -3,5 +3,5 @@ kind: Kustomization namespace: shinyproxy-operator resources: - - ../../../bases/clustered + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/clustered?ref=v2.0.0 - resources/shinyproxy-operator.namespace.yaml diff --git a/docs/deployment/overlays/2-clustered/shinyproxy/kustomization.yaml b/docs/deployment/overlays/2-clustered/shinyproxy/kustomization.yaml index ac121e9..6d426d7 100644 --- a/docs/deployment/overlays/2-clustered/shinyproxy/kustomization.yaml +++ b/docs/deployment/overlays/2-clustered/shinyproxy/kustomization.yaml @@ -3,7 +3,7 @@ kind: Kustomization namespace: shinyproxy resources: - - ../../../bases/shinyproxy + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/shinyproxy?ref=v2.0.0 - resources/redis.secret.yaml - resources/shinyproxy.shinyproxy.yaml - resources/shinyproxy.namespace.yaml diff --git a/docs/deployment/overlays/2-clustered/shinyproxy/resources/shinyproxy.shinyproxy.yaml b/docs/deployment/overlays/2-clustered/shinyproxy/resources/shinyproxy.shinyproxy.yaml index 587d58d..e710567 100644 --- a/docs/deployment/overlays/2-clustered/shinyproxy/resources/shinyproxy.shinyproxy.yaml +++ b/docs/deployment/overlays/2-clustered/shinyproxy/resources/shinyproxy.shinyproxy.yaml @@ -87,6 +87,18 @@ spec: - op: add path: /spec/ingressClassName value: nginx + - op: add + path: /spec/tls + value: + - hosts: + - shinyproxy-demo.local + # secretName: example # uncomment and change this line if needed + - op: add + path: /spec/tls + value: + - hosts: + - shinyproxy-demo.local + # secretName: example # uncomment and change this line if needed image: openanalytics/shinyproxy:3.0.1 imagePullPolicy: Always fqdn: shinyproxy-demo.local diff --git a/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/kustomization.yaml b/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/kustomization.yaml index 4cb6642..c81e966 100644 --- a/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/kustomization.yaml +++ b/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/kustomization.yaml @@ -3,8 +3,8 @@ kind: Kustomization namespace: shinyproxy resources: - - ../../../bases/redis-sentinel - - ../../../bases/namespaced - - ../../../bases/shinyproxy + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/redis-sentinel?ref=v2.0.0 + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/namespaced?ref=v2.0.0 + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/shinyproxy?ref=v2.0.0 - resources/shinyproxy.namespace.yaml - resources/shinyproxy.shinyproxy.yaml diff --git a/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/resources/shinyproxy.shinyproxy.yaml b/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/resources/shinyproxy.shinyproxy.yaml index 62061a8..f739a01 100644 --- a/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/resources/shinyproxy.shinyproxy.yaml +++ b/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/resources/shinyproxy.shinyproxy.yaml @@ -92,6 +92,12 @@ spec: - op: add path: /spec/ingressClassName value: nginx + - op: add + path: /spec/tls + value: + - hosts: + - shinyproxy-demo.local + # secretName: example # uncomment and change this line if needed image: openanalytics/shinyproxy:3.0.1 imagePullPolicy: Always fqdn: shinyproxy-demo.local diff --git a/docs/deployment/overlays/4-namespaced-multi/kustomization.yaml b/docs/deployment/overlays/4-namespaced-multi/kustomization.yaml index 4e2a56a..63f190b 100644 --- a/docs/deployment/overlays/4-namespaced-multi/kustomization.yaml +++ b/docs/deployment/overlays/4-namespaced-multi/kustomization.yaml @@ -3,9 +3,9 @@ kind: Kustomization namespace: shinyproxy resources: - - ../../bases/redis-sentinel - - ../../bases/namespaced - - ../../bases/shinyproxy + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/redis-sentinel?ref=v2.0.0 + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/namespaced?ref=v2.0.0 + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/shinyproxy?ref=v2.0.0 - resources/shinyproxy.namespace.yaml - resources/shinyproxy1.shinyproxy.yaml - resources/shinyproxy2.shinyproxy.yaml diff --git a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy1.shinyproxy.yaml b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy1.shinyproxy.yaml index 60f304f..b4ae244 100644 --- a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy1.shinyproxy.yaml +++ b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy1.shinyproxy.yaml @@ -90,6 +90,12 @@ spec: - op: add path: /spec/ingressClassName value: nginx + - op: add + path: /spec/tls + value: + - hosts: + - shinyproxy-demo.local + # secretName: example # uncomment and change this line if needed image: openanalytics/shinyproxy:3.0.1 imagePullPolicy: Always fqdn: shinyproxy-demo.local diff --git a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy2.shinyproxy.yaml b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy2.shinyproxy.yaml index 2ebb867..1b40be6 100644 --- a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy2.shinyproxy.yaml +++ b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy2.shinyproxy.yaml @@ -91,6 +91,12 @@ spec: - op: add path: /spec/ingressClassName value: nginx + - op: add + path: /spec/tls + value: + - hosts: + - shinyproxy-demo.local + # secretName: example # uncomment and change this line if needed image: openanalytics/shinyproxy:3.0.1 imagePullPolicy: Always fqdn: shinyproxy-demo.local diff --git a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy3.shinyproxy.yaml b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy3.shinyproxy.yaml index f38eea5..7f8d5c9 100644 --- a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy3.shinyproxy.yaml +++ b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy3.shinyproxy.yaml @@ -91,6 +91,12 @@ spec: - op: add path: /spec/ingressClassName value: nginx + - op: add + path: /spec/tls + value: + - hosts: + - shinyproxy-demo.local + # secretName: example # uncomment and change this line if needed image: openanalytics/shinyproxy:3.0.1 imagePullPolicy: Always fqdn: shinyproxy-demo.local From 0a114f3319ab0842f1c2a7b08916fbe5efa9ca09 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Thu, 6 Apr 2023 15:43:43 +0200 Subject: [PATCH 19/34] Add instructions to change the redis password --- docs/deployment/README.md | 6 ++++++ docs/deployment/bases/redis-sentinel/kustomization.yml | 5 ++--- docs/deployment/overlays/1-namespaced/kustomization.yaml | 3 +++ .../overlays/1-namespaced/patches/redis.secret.yaml | 8 ++++++++ .../overlays/2-clustered/redis/kustomization.yaml | 4 ++-- .../2-clustered/redis/resources/redis.secret.yaml | 2 +- .../shinyproxy-dept2/resources/redis.secret.yaml | 2 +- .../2-clustered/shinyproxy/resources/redis.secret.yaml | 2 +- .../3-namespaced-app-ns/shinyproxy/kustomization.yaml | 3 +++ .../shinyproxy/patches/redis.secret.yaml | 8 ++++++++ .../overlays/4-namespaced-multi/kustomization.yaml | 3 +++ .../overlays/4-namespaced-multi/patches/redis.secret.yaml | 8 ++++++++ 12 files changed, 46 insertions(+), 8 deletions(-) create mode 100644 docs/deployment/overlays/1-namespaced/patches/redis.secret.yaml create mode 100644 docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/patches/redis.secret.yaml create mode 100644 docs/deployment/overlays/4-namespaced-multi/patches/redis.secret.yaml diff --git a/docs/deployment/README.md b/docs/deployment/README.md index c33e72a..21c1474 100644 --- a/docs/deployment/README.md +++ b/docs/deployment/README.md @@ -25,6 +25,12 @@ and dependencies of the operator. Redis is run in a high-available way. It is also possible to use a Redis server provided by cloud providers. + **Note:** when deploying to production, it is important to change the password + used to secure Redis. Each example (see below) already changes the + password `mySecurePassword12`. For an example see + the [`overlays/1-namespaced/patches/redis.secret.yaml`](overlays/1-namespaced/patches/redis.secret.yaml) + file. + ## Tutorial using minikube This section provides a step-by-step tutorial on the basic deployment of the diff --git a/docs/deployment/bases/redis-sentinel/kustomization.yml b/docs/deployment/bases/redis-sentinel/kustomization.yml index b2db78e..68ecc30 100644 --- a/docs/deployment/bases/redis-sentinel/kustomization.yml +++ b/docs/deployment/bases/redis-sentinel/kustomization.yml @@ -1,4 +1,3 @@ - apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization @@ -6,5 +5,5 @@ resources: - resources/redis-sentinel.yaml - resources/redis.pdb.yaml -patchesStrategicMerge: - - patches/redis-node.statefulset.yaml +patches: + - path: patches/redis-node.statefulset.yaml diff --git a/docs/deployment/overlays/1-namespaced/kustomization.yaml b/docs/deployment/overlays/1-namespaced/kustomization.yaml index c81e966..2d867d2 100644 --- a/docs/deployment/overlays/1-namespaced/kustomization.yaml +++ b/docs/deployment/overlays/1-namespaced/kustomization.yaml @@ -8,3 +8,6 @@ resources: - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/shinyproxy?ref=v2.0.0 - resources/shinyproxy.namespace.yaml - resources/shinyproxy.shinyproxy.yaml + +patches: + - path: patches/redis.secret.yaml diff --git a/docs/deployment/overlays/1-namespaced/patches/redis.secret.yaml b/docs/deployment/overlays/1-namespaced/patches/redis.secret.yaml new file mode 100644 index 0000000..54d278e --- /dev/null +++ b/docs/deployment/overlays/1-namespaced/patches/redis.secret.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + name: redis +type: Opaque +data: + # IMPORTANT: replace this password! + redis-password: bXlTZWN1cmVQYXNzd29yZDEy diff --git a/docs/deployment/overlays/2-clustered/redis/kustomization.yaml b/docs/deployment/overlays/2-clustered/redis/kustomization.yaml index a76b976..21fdab3 100644 --- a/docs/deployment/overlays/2-clustered/redis/kustomization.yaml +++ b/docs/deployment/overlays/2-clustered/redis/kustomization.yaml @@ -6,5 +6,5 @@ resources: - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/redis-sentinel?ref=v2.0.0 - resources/redis.namespace.yaml -patchesStrategicMerge: - - resources/redis.secret.yaml +patches: + - path: resources/redis.secret.yaml diff --git a/docs/deployment/overlays/2-clustered/redis/resources/redis.secret.yaml b/docs/deployment/overlays/2-clustered/redis/resources/redis.secret.yaml index d8323b1..54d278e 100644 --- a/docs/deployment/overlays/2-clustered/redis/resources/redis.secret.yaml +++ b/docs/deployment/overlays/2-clustered/redis/resources/redis.secret.yaml @@ -5,4 +5,4 @@ metadata: type: Opaque data: # IMPORTANT: replace this password! - redis-password: bXlTZWN1cmVQYXNzd29yZA== + redis-password: bXlTZWN1cmVQYXNzd29yZDEy diff --git a/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/redis.secret.yaml b/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/redis.secret.yaml index d8323b1..54d278e 100644 --- a/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/redis.secret.yaml +++ b/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/redis.secret.yaml @@ -5,4 +5,4 @@ metadata: type: Opaque data: # IMPORTANT: replace this password! - redis-password: bXlTZWN1cmVQYXNzd29yZA== + redis-password: bXlTZWN1cmVQYXNzd29yZDEy diff --git a/docs/deployment/overlays/2-clustered/shinyproxy/resources/redis.secret.yaml b/docs/deployment/overlays/2-clustered/shinyproxy/resources/redis.secret.yaml index d8323b1..54d278e 100644 --- a/docs/deployment/overlays/2-clustered/shinyproxy/resources/redis.secret.yaml +++ b/docs/deployment/overlays/2-clustered/shinyproxy/resources/redis.secret.yaml @@ -5,4 +5,4 @@ metadata: type: Opaque data: # IMPORTANT: replace this password! - redis-password: bXlTZWN1cmVQYXNzd29yZA== + redis-password: bXlTZWN1cmVQYXNzd29yZDEy diff --git a/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/kustomization.yaml b/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/kustomization.yaml index c81e966..2d867d2 100644 --- a/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/kustomization.yaml +++ b/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/kustomization.yaml @@ -8,3 +8,6 @@ resources: - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/shinyproxy?ref=v2.0.0 - resources/shinyproxy.namespace.yaml - resources/shinyproxy.shinyproxy.yaml + +patches: + - path: patches/redis.secret.yaml diff --git a/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/patches/redis.secret.yaml b/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/patches/redis.secret.yaml new file mode 100644 index 0000000..54d278e --- /dev/null +++ b/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/patches/redis.secret.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + name: redis +type: Opaque +data: + # IMPORTANT: replace this password! + redis-password: bXlTZWN1cmVQYXNzd29yZDEy diff --git a/docs/deployment/overlays/4-namespaced-multi/kustomization.yaml b/docs/deployment/overlays/4-namespaced-multi/kustomization.yaml index 63f190b..9154db6 100644 --- a/docs/deployment/overlays/4-namespaced-multi/kustomization.yaml +++ b/docs/deployment/overlays/4-namespaced-multi/kustomization.yaml @@ -10,3 +10,6 @@ resources: - resources/shinyproxy1.shinyproxy.yaml - resources/shinyproxy2.shinyproxy.yaml - resources/shinyproxy3.shinyproxy.yaml + +patches: + - path: patches/redis.secret.yaml diff --git a/docs/deployment/overlays/4-namespaced-multi/patches/redis.secret.yaml b/docs/deployment/overlays/4-namespaced-multi/patches/redis.secret.yaml new file mode 100644 index 0000000..54d278e --- /dev/null +++ b/docs/deployment/overlays/4-namespaced-multi/patches/redis.secret.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + name: redis +type: Opaque +data: + # IMPORTANT: replace this password! + redis-password: bXlTZWN1cmVQYXNzd29yZDEy From 9538318a33b904d05b0e79e396ce8458adfc2820 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Fri, 7 Apr 2023 16:03:34 +0200 Subject: [PATCH 20/34] Fix typo --- docs/deployment/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/deployment/README.md b/docs/deployment/README.md index 21c1474..955dd56 100644 --- a/docs/deployment/README.md +++ b/docs/deployment/README.md @@ -27,7 +27,7 @@ and dependencies of the operator. **Note:** when deploying to production, it is important to change the password used to secure Redis. Each example (see below) already changes the - password `mySecurePassword12`. For an example see + password to `mySecurePassword12`. For an example see the [`overlays/1-namespaced/patches/redis.secret.yaml`](overlays/1-namespaced/patches/redis.secret.yaml) file. From ea8cf224d7ae605a5166e477c2ea0e85966729dc Mon Sep 17 00:00:00 2001 From: bsauvajon Date: Tue, 9 May 2023 15:34:45 +0200 Subject: [PATCH 21/34] Add kubernetesServicePatches to shinyproxy definition --- .../components/ServiceFactory.kt | 5 +- .../components/ServicePatcher.kt | 71 +++++++++++++++++++ .../shinyproxyoperator/crd/ShinyProxy.kt | 17 +++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ServicePatcher.kt diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ServiceFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ServiceFactory.kt index 0461e70..57a704e 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ServiceFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ServiceFactory.kt @@ -34,6 +34,8 @@ class ServiceFactory(private val serviceClient: MixedOperation + */ +package eu.openanalytics.shinyproxyoperator.components + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory +import com.fasterxml.jackson.datatype.jsr353.JSR353Module +import io.fabric8.kubernetes.api.model.HTTPGetAction +import io.fabric8.kubernetes.api.model.IntOrString +import io.fabric8.kubernetes.api.model.PodTemplateSpec +import io.fabric8.kubernetes.api.model.Service +import io.fabric8.kubernetes.api.model.ServicePort +import mu.KotlinLogging +import javax.json.JsonPatch +import javax.json.JsonStructure + +class ServicePatcher { + private val mapper = ObjectMapper(YAMLFactory()) + private val logger = KotlinLogging.logger { } + + init { + mapper.registerModule(JSR353Module()) + } + + /** + * Applies a JsonPatch to the given Ingress. + */ + fun patch(service: Service, patch: JsonPatch?): Service { + if (patch == null) { + return service + } + + logger.debug { "Original Service (before applying patches): ${mapper.writeValueAsString(service)}" } + + // 1. convert Service to javax.json.JsonValue object. + // This conversion does not actually convert to a string, but some internal + // representation of Jackson. + val podAsJsonValue: JsonStructure = mapper.convertValue(service, JsonStructure::class.java) + // 2. apply patch + val patchedPodAsJsonValue: JsonStructure = patch.apply(podAsJsonValue) + // 3. convert back to a Service + val patchedService = mapper.convertValue(patchedPodAsJsonValue, Service::class.java) + + for (port in patchedService.spec.ports) { + port.setTargetPort(IntOrString(port.targetPort.strVal.toIntOrNull())) + } + + logger.debug { "Patched Service (after applying patches): ${mapper.writeValueAsString(patchedService)}" } + + return patchedService + } + +} diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt index bf9a52e..921d039 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt @@ -145,6 +145,23 @@ class ShinyProxy : CustomResource(), Namespaced { return@lazy null } + @get:JsonIgnore + val parsedServicePatches: JsonPatch? by lazy { + if (spec.get("kubernetesServicePatches")?.isTextual == true) { + try { + // convert the raw YAML string into a JsonPatch + val yamlReader = ObjectMapper(YAMLFactory()) + yamlReader.registerModule(JSR353Module()) + return@lazy yamlReader.readValue(spec.get("kubernetesServicePatches").textValue(), JsonPatch::class.java) + } catch (exception: Exception) { + exception.printStackTrace() // log the exception for easier debugging + throw exception + } + + } + return@lazy null + } + @get:JsonIgnore val subPath: String by lazy { if (spec.get("server")?.get("servlet")?.get("context-path")?.isTextual == true) { From ed2aed0c363ddbd5bc6f160819bfe6a642d28346 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Mon, 19 Feb 2024 09:12:45 +0100 Subject: [PATCH 22/34] Add IntOrStringDeserializer --- .../IntOrStringDeserializer.kt | 43 +++++++++++ .../components/IngressFactory.kt | 2 +- .../components/IngressPatcher.kt | 63 ---------------- .../{PodTemplateSpecPatcher.kt => Patcher.kt} | 74 ++++++++++++++----- .../components/PodTemplateSpecFactory.kt | 2 +- .../components/ServiceFactory.kt | 2 +- .../components/ServicePatcher.kt | 71 ------------------ .../configs/simple_config_with_patches.yaml | 4 +- 8 files changed, 105 insertions(+), 156 deletions(-) create mode 100644 src/main/kotlin/eu/openanalytics/shinyproxyoperator/IntOrStringDeserializer.kt delete mode 100644 src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressPatcher.kt rename src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/{PodTemplateSpecPatcher.kt => Patcher.kt} (50%) delete mode 100644 src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ServicePatcher.kt diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/IntOrStringDeserializer.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/IntOrStringDeserializer.kt new file mode 100644 index 0000000..832c45b --- /dev/null +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/IntOrStringDeserializer.kt @@ -0,0 +1,43 @@ +/** + * ShinyProxy-Operator + * + * Copyright (C) 2021-2024 Open Analytics + * + * =========================================================================== + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Apache License as published by + * The Apache Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Apache License for more details. + * + * You should have received a copy of the Apache License + * along with this program. If not, see + */ +package eu.openanalytics.shinyproxyoperator + +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import io.fabric8.kubernetes.api.model.IntOrString + +class IntOrStringDeserializer : StdDeserializer(IntOrString::class.java) { + + override fun deserialize(jsonParser: JsonParser, deserializationContext: DeserializationContext): IntOrString { + val node = jsonParser.codec.readTree(jsonParser) + val value = node.asText() + + val asInt = value?.toIntOrNull() + if (asInt != null) { + return IntOrString(asInt) + } + + return IntOrString(value) + } + +} diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt index c5a99fe..6ef1c9a 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt @@ -33,7 +33,7 @@ class IngressFactory(private val kubeClient: KubernetesClient) { private val logger = KotlinLogging.logger {} - private val ingressPatcher = IngressPatcher() + private val ingressPatcher = Patcher() fun create(shinyProxy: ShinyProxy, latestShinyProxyInstance: ShinyProxyInstance) { val labels = LabelFactory.labelsForShinyProxy(shinyProxy).toMutableMap() diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressPatcher.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressPatcher.kt deleted file mode 100644 index 681a1c6..0000000 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressPatcher.kt +++ /dev/null @@ -1,63 +0,0 @@ -/** - * ShinyProxy-Operator - * - * Copyright (C) 2021-2024 Open Analytics - * - * =========================================================================== - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Apache License as published by - * The Apache Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Apache License for more details. - * - * You should have received a copy of the Apache License - * along with this program. If not, see - */ -package eu.openanalytics.shinyproxyoperator.components - -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory -import com.fasterxml.jackson.datatype.jsr353.JSR353Module -import io.fabric8.kubernetes.api.model.networking.v1.Ingress -import mu.KotlinLogging -import javax.json.JsonPatch -import javax.json.JsonStructure - -class IngressPatcher { - private val mapper = ObjectMapper(YAMLFactory()) - private val logger = KotlinLogging.logger { } - - init { - mapper.registerModule(JSR353Module()) - } - - /** - * Applies a JsonPatch to the given Ingress. - */ - fun patch(ingress: Ingress, patch: JsonPatch?): Ingress { - if (patch == null) { - return ingress - } - - logger.debug { "Original Ingress (before applying patches): ${mapper.writeValueAsString(ingress)}" } - - // 1. convert PodTemplate to javax.json.JsonValue object. - // This conversion does not actually convert to a string, but some internal - // representation of Jackson. - val podAsJsonValue: JsonStructure = mapper.convertValue(ingress, JsonStructure::class.java) - // 2. apply patch - val patchedPodAsJsonValue: JsonStructure = patch.apply(podAsJsonValue) - // 3. convert back to a PodTemplate - val patchesIngress = mapper.convertValue(patchedPodAsJsonValue, Ingress::class.java) - - logger.debug { "Patched Ingress (after applying patches): ${mapper.writeValueAsString(patchesIngress)}" } - - return patchesIngress - } - -} diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/PodTemplateSpecPatcher.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/Patcher.kt similarity index 50% rename from src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/PodTemplateSpecPatcher.kt rename to src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/Patcher.kt index ed1a3ec..26fd001 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/PodTemplateSpecPatcher.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/Patcher.kt @@ -21,21 +21,76 @@ package eu.openanalytics.shinyproxyoperator.components import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.dataformat.yaml.YAMLFactory import com.fasterxml.jackson.datatype.jsr353.JSR353Module -import io.fabric8.kubernetes.api.model.HTTPGetAction +import eu.openanalytics.shinyproxyoperator.IntOrStringDeserializer import io.fabric8.kubernetes.api.model.IntOrString import io.fabric8.kubernetes.api.model.PodTemplateSpec +import io.fabric8.kubernetes.api.model.Service +import io.fabric8.kubernetes.api.model.networking.v1.Ingress import mu.KotlinLogging import javax.json.JsonPatch import javax.json.JsonStructure -class PodTemplateSpecPatcher { + +class Patcher { private val mapper = ObjectMapper(YAMLFactory()) private val logger = KotlinLogging.logger { } init { mapper.registerModule(JSR353Module()) + val module = SimpleModule() + module.addDeserializer(IntOrString::class.java, IntOrStringDeserializer()) + mapper.registerModule(module) + } + + /** + * Applies a JsonPatch to the given Service. + */ + fun patch(service: Service, patch: JsonPatch?): Service { + if (patch == null) { + return service + } + + logger.debug { "Original Service (before applying patches): ${mapper.writeValueAsString(service)}" } + + // 1. convert Service to javax.json.JsonValue object. + // This conversion does not actually convert to a string, but some internal + // representation of Jackson. + val podAsJsonValue: JsonStructure = mapper.convertValue(service, JsonStructure::class.java) + // 2. apply patch + val patchedAsJsonValue: JsonStructure = patch.apply(podAsJsonValue) + // 3. convert back to a Service + val patchedService = mapper.convertValue(patchedAsJsonValue, Service::class.java) + + logger.debug { "Patched Service (after applying patches): ${mapper.writeValueAsString(patchedService)}" } + + return patchedService + } + + /** + * Applies a JsonPatch to the given Ingress. + */ + fun patch(ingress: Ingress, patch: JsonPatch?): Ingress { + if (patch == null) { + return ingress + } + + logger.debug { "Original Ingress (before applying patches): ${mapper.writeValueAsString(ingress)}" } + + // 1. convert PodTemplate to javax.json.JsonValue object. + // This conversion does not actually convert to a string, but some internal + // representation of Jackson. + val podAsJsonValue: JsonStructure = mapper.convertValue(ingress, JsonStructure::class.java) + // 2. apply patch + val patchedAsJsonValue: JsonStructure = patch.apply(podAsJsonValue) + // 3. convert back to a PodTemplate + val patchedIngress = mapper.convertValue(patchedAsJsonValue, Ingress::class.java) + + logger.debug { "Patched Ingress (after applying patches): ${mapper.writeValueAsString(patchedIngress)}" } + + return patchedIngress } /** @@ -57,24 +112,9 @@ class PodTemplateSpecPatcher { // 3. convert back to a PodTemplate val patchedPodTemplateSpec = mapper.convertValue(patchedPodAsJsonValue, PodTemplateSpec::class.java) - for (container in patchedPodTemplateSpec.spec.containers) { - container.livenessProbe?.httpGet?.let { patchHttpGet(it) } - container.readinessProbe?.httpGet?.let { patchHttpGet(it) } - container.startupProbe?.httpGet?.let { patchHttpGet(it) } - } - logger.debug { "Patched PodTemplateSpec (after applying patches): ${mapper.writeValueAsString(patchedPodTemplateSpec)}" } return patchedPodTemplateSpec } - private fun patchHttpGet(httpGet: HTTPGetAction) { - if (httpGet.port.intVal == null) { - val asInt = httpGet.port.strVal.toIntOrNull() - if (asInt != null) { - httpGet.port = IntOrString(asInt) - } - } - } - } diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/PodTemplateSpecFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/PodTemplateSpecFactory.kt index 74a6a50..831d691 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/PodTemplateSpecFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/PodTemplateSpecFactory.kt @@ -27,7 +27,7 @@ import io.fabric8.kubernetes.api.model.* class PodTemplateSpecFactory { - private val podTemplatePatcher = PodTemplateSpecPatcher() + private val podTemplatePatcher = Patcher() fun create(shinyProxy: ShinyProxy, shinyProxyInstance: ShinyProxyInstance): PodTemplateSpec { diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ServiceFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ServiceFactory.kt index 57a704e..c1b2ffa 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ServiceFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ServiceFactory.kt @@ -34,7 +34,7 @@ class ServiceFactory(private val serviceClient: MixedOperation - */ -package eu.openanalytics.shinyproxyoperator.components - -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory -import com.fasterxml.jackson.datatype.jsr353.JSR353Module -import io.fabric8.kubernetes.api.model.HTTPGetAction -import io.fabric8.kubernetes.api.model.IntOrString -import io.fabric8.kubernetes.api.model.PodTemplateSpec -import io.fabric8.kubernetes.api.model.Service -import io.fabric8.kubernetes.api.model.ServicePort -import mu.KotlinLogging -import javax.json.JsonPatch -import javax.json.JsonStructure - -class ServicePatcher { - private val mapper = ObjectMapper(YAMLFactory()) - private val logger = KotlinLogging.logger { } - - init { - mapper.registerModule(JSR353Module()) - } - - /** - * Applies a JsonPatch to the given Ingress. - */ - fun patch(service: Service, patch: JsonPatch?): Service { - if (patch == null) { - return service - } - - logger.debug { "Original Service (before applying patches): ${mapper.writeValueAsString(service)}" } - - // 1. convert Service to javax.json.JsonValue object. - // This conversion does not actually convert to a string, but some internal - // representation of Jackson. - val podAsJsonValue: JsonStructure = mapper.convertValue(service, JsonStructure::class.java) - // 2. apply patch - val patchedPodAsJsonValue: JsonStructure = patch.apply(podAsJsonValue) - // 3. convert back to a Service - val patchedService = mapper.convertValue(patchedPodAsJsonValue, Service::class.java) - - for (port in patchedService.spec.ports) { - port.setTargetPort(IntOrString(port.targetPort.strVal.toIntOrNull())) - } - - logger.debug { "Patched Service (after applying patches): ${mapper.writeValueAsString(patchedService)}" } - - return patchedService - } - -} diff --git a/src/test/resources/configs/simple_config_with_patches.yaml b/src/test/resources/configs/simple_config_with_patches.yaml index 26efe23..e29c91f 100644 --- a/src/test/resources/configs/simple_config_with_patches.yaml +++ b/src/test/resources/configs/simple_config_with_patches.yaml @@ -44,7 +44,7 @@ spec: failureThreshold: 1 httpGet: path: /actuator/health/liveness - port: "9090" + port: 9090 scheme: HTTP periodSeconds: 1 initialDelaySeconds: 30 @@ -56,7 +56,7 @@ spec: failureThreshold: 1 httpGet: path: /actuator/health/readiness - port: "9090" + port: 9090 scheme: HTTP periodSeconds: 1 initialDelaySeconds: 30 From 767e1408173e86eb1cb17db4702625612bd4b004 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Mon, 19 Feb 2024 09:13:41 +0100 Subject: [PATCH 23/34] Add tests for ingress and service patch --- .../shinyproxyoperator/MainIntegrationTest.kt | 83 ++++++++++++++++++- .../helpers/IntegrationTestBase.kt | 20 +++-- .../resources/configs/additional_fqdns.yaml | 2 +- .../resources/configs/affinity_required.yaml | 2 +- .../configs/affinity_topologykey.yaml | 2 +- .../resources/configs/serviceaccount.yaml | 3 + src/test/resources/configs/simple_config.yaml | 2 +- .../configs/simple_config_clustered.yaml | 2 +- .../simple_config_multiple_namespaces.yaml | 2 +- .../configs/simple_config_subpath1.yaml | 2 +- .../configs/simple_config_subpath2.yaml | 2 +- .../configs/simple_config_updated.yaml | 2 +- .../simple_config_with_ingress_patches.yaml | 41 +++++++++ .../configs/simple_config_with_patches.yaml | 2 +- .../simple_config_with_service_patches.yaml | 39 +++++++++ 15 files changed, 187 insertions(+), 19 deletions(-) create mode 100644 src/test/resources/configs/simple_config_with_ingress_patches.yaml create mode 100644 src/test/resources/configs/simple_config_with_service_patches.yaml diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt index 2f571ca..4aa0e74 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt @@ -210,7 +210,7 @@ class MainIntegrationTest : IntegrationTestBase() { @Test fun `sp in other namespaced should be ignored when using namespaced mode`() = - setup(Mode.NAMESPACED) { namespace, shinyProxyClient, namespacedClient, stableClient, operator, reconcileListener, _-> + setup(Mode.NAMESPACED) { namespace, shinyProxyClient, namespacedClient, stableClient, operator, reconcileListener, _ -> // 1. create a SP instance in other namespace val spTestInstance = ShinyProxyTestInstance( "itest-2", @@ -494,7 +494,7 @@ class MainIntegrationTest : IntegrationTestBase() { @Test fun `configuration with subpath not ending in slash`() = - setup(Mode.NAMESPACED) { namespace, shinyProxyClient, namespacedClient, stableClient, operator, reconcileListener , _-> + setup(Mode.NAMESPACED) { namespace, shinyProxyClient, namespacedClient, stableClient, operator, reconcileListener, _ -> // 1. create a SP instance val spTestInstance = ShinyProxyTestInstance( namespace, @@ -1092,7 +1092,7 @@ class MainIntegrationTest : IntegrationTestBase() { // check configmap spTestInstance.assertConfigMapIsCorrect(sp) - // check replicaset + // check replicaset spTestInstance.assertReplicaSetIsCorrect(sp) // check service @@ -1134,5 +1134,82 @@ class MainIntegrationTest : IntegrationTestBase() { job.cancel() } + @Test + fun `ingress patch`() = + setup(Mode.NAMESPACED) { namespace, shinyProxyClient, namespacedClient, _, operator, reconcileListener, _ -> + // 1. create a SP instance + val spTestInstance = ShinyProxyTestInstance( + namespace, + namespacedClient, + shinyProxyClient, + "simple_config_with_ingress_patches.yaml", + reconcileListener + ) + spTestInstance.create() + + val (resourceRetriever, shinyProxyLister) = operator.prepare() + // 2. start the operator and let it do it's work + val job = GlobalScope.launch { + operator.run(resourceRetriever, shinyProxyLister) + } + + // 3. wait until instance is created + spTestInstance.waitForOneReconcile() + + // 4. assert correctness + spTestInstance.assertInstanceIsCorrect() + val sp = spTestInstance.retrieveInstance() + + val allIngresses = namespacedClient.network().v1().ingresses().list().items + assertEquals(1, allIngresses.size) + val ingress = allIngresses.firstOrNull { it.metadata.name == "sp-${sp.metadata.name}-ing".take(63) } + assertNotNull(ingress) + + assertEquals(mapOf( + "nginx.ingress.kubernetes.io/proxy-buffer-size" to "128k", + "nginx.ingress.kubernetes.io/ssl-redirect" to "true", + "nginx.ingress.kubernetes.io/proxy-body-size" to "300m" + ), ingress.metadata.annotations) + + job.cancel() + } + + @Test + fun `service patch`() = + setup(Mode.NAMESPACED) { namespace, shinyProxyClient, namespacedClient, _, operator, reconcileListener, _ -> + // 1. create a SP instance + val spTestInstance = ShinyProxyTestInstance( + namespace, + namespacedClient, + shinyProxyClient, + "simple_config_with_service_patches.yaml", + reconcileListener + ) + spTestInstance.create() + + val (resourceRetriever, shinyProxyLister) = operator.prepare() + // 2. start the operator and let it do it's work + val job = GlobalScope.launch { + operator.run(resourceRetriever, shinyProxyLister) + } + + // 3. wait until instance is created + spTestInstance.waitForOneReconcile() + + // 4. assert correctness + spTestInstance.assertInstanceIsCorrect() + val sp = spTestInstance.retrieveInstance() + + val services = namespacedClient.inNamespace(namespace).services().list().items + assertEquals(1, services.size) + val service = services.firstOrNull { it.metadata.name == "sp-${sp.metadata.name}-svc".take(63) } + assertNotNull(service) + + assertEquals(mapOf( + "my-service-ingress-patch" to "abc" + ), service.metadata.annotations) + + job.cancel() + } } diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/IntegrationTestBase.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/IntegrationTestBase.kt index 50026ba..4e8e787 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/IntegrationTestBase.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/IntegrationTestBase.kt @@ -26,6 +26,7 @@ import eu.openanalytics.shinyproxyoperator.ShinyProxyClient import eu.openanalytics.shinyproxyoperator.components.LabelFactory import eu.openanalytics.shinyproxyoperator.crd.ShinyProxy import eu.openanalytics.shinyproxyoperator.createKubernetesClient +import eu.openanalytics.shinyproxyoperator.logger import io.fabric8.kubernetes.api.model.NamespaceBuilder import io.fabric8.kubernetes.api.model.PodList import io.fabric8.kubernetes.client.KubernetesClient @@ -184,15 +185,22 @@ abstract class IntegrationTestBase { val oldNumApps = getPodsForInstance(instance.hash)?.items?.filter { it.status.phase.equals("Running") }?.size ?: 0 val serviceName = "sp-${shinyProxy.metadata.name}-svc".take(63) stableClient.run().inNamespace(shinyProxy.metadata.namespace) - .withRunConfig(RunConfigBuilder() - .withName("itest-curl-helper") - .withImage("curlimages/curl") - .withArgs("-X", "POST", "-u", "demo:demo", "${serviceName}/api/proxy/01_hello") - .withRestartPolicy("Never") - .build()) + .withNewRunConfig() + .withName("itest-curl-helper") + .withImage("curlimages/curl") + .withArgs("-X", "POST", "-u", "demo:demo", "${serviceName}/api/proxy/01_hello") + .withRestartPolicy("Never") .done() + delay(5_000) withTimeout(60_000) { while (true) { + try { + logger.info { "Pod: ${stableClient.kubernetesSerialization.asJson(stableClient.pods().inNamespace(shinyProxy.metadata.namespace).withName("itest-curl-helper").get())}" } + logger.info { "Pod: ${stableClient.pods().inNamespace(shinyProxy.metadata.namespace).withName("itest-curl-helper").log}" } + } catch (e: Throwable) { + logger. info { e } + } + val newNumApps = getPodsForInstance(instance.hash)?.items?.filter { it.status.phase.equals("Running") }?.size ?: continue if (newNumApps > oldNumApps) { break diff --git a/src/test/resources/configs/additional_fqdns.yaml b/src/test/resources/configs/additional_fqdns.yaml index 4f62304..27435a4 100644 --- a/src/test/resources/configs/additional_fqdns.yaml +++ b/src/test/resources/configs/additional_fqdns.yaml @@ -6,7 +6,7 @@ spec: fqdn: itest.local additionalFqdns: - itest2.locals - image: openanalytics/shinyproxy:2.6.1 + image: openanalytics/shinyproxy-snapshot:3.1.0-SNAPSHOT-20240219.093646 proxy: title: Open Analytics Shiny Proxy logoUrl: http://www.openanalytics.eu/sites/www.openanalytics.eu/themes/oa/logo.png diff --git a/src/test/resources/configs/affinity_required.yaml b/src/test/resources/configs/affinity_required.yaml index a48f4c5..af8f790 100644 --- a/src/test/resources/configs/affinity_required.yaml +++ b/src/test/resources/configs/affinity_required.yaml @@ -5,7 +5,7 @@ metadata: spec: antiAffinityRequired: true fqdn: itest.local - image: openanalytics/shinyproxy:2.6.1 + image: openanalytics/shinyproxy-snapshot:3.1.0-SNAPSHOT-20240219.093646 proxy: title: Open Analytics Shiny Proxy logoUrl: http://www.openanalytics.eu/sites/www.openanalytics.eu/themes/oa/logo.png diff --git a/src/test/resources/configs/affinity_topologykey.yaml b/src/test/resources/configs/affinity_topologykey.yaml index 85b94fd..af8c132 100644 --- a/src/test/resources/configs/affinity_topologykey.yaml +++ b/src/test/resources/configs/affinity_topologykey.yaml @@ -5,7 +5,7 @@ metadata: spec: antiAffinityTopologyKey: example.com/custom-topology-key fqdn: itest.local - image: openanalytics/shinyproxy:2.6.1 + image: openanalytics/shinyproxy-snapshot:3.1.0-SNAPSHOT-20240219.093646 proxy: title: Open Analytics Shiny Proxy logoUrl: http://www.openanalytics.eu/sites/www.openanalytics.eu/themes/oa/logo.png diff --git a/src/test/resources/configs/serviceaccount.yaml b/src/test/resources/configs/serviceaccount.yaml index 3af5647..9871d6a 100644 --- a/src/test/resources/configs/serviceaccount.yaml +++ b/src/test/resources/configs/serviceaccount.yaml @@ -7,6 +7,9 @@ rules: - apiGroups: [ "" ] resources: [ "pods" ] verbs: [ "get", "list", "watch", "create", "update", "patch", "delete" ] + - apiGroups: [ "" ] + resources: [ "services" ] + verbs: [ "get", "list", "watch", "create", "update", "patch", "delete" ] --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 diff --git a/src/test/resources/configs/simple_config.yaml b/src/test/resources/configs/simple_config.yaml index 5da9036..481563c 100644 --- a/src/test/resources/configs/simple_config.yaml +++ b/src/test/resources/configs/simple_config.yaml @@ -4,7 +4,7 @@ metadata: name: example-shinyproxy spec: fqdn: itest.local - image: openanalytics/shinyproxy:2.6.1 + image: openanalytics/shinyproxy-snapshot:3.1.0-SNAPSHOT-20240219.093646 proxy: title: Open Analytics Shiny Proxy logoUrl: http://www.openanalytics.eu/sites/www.openanalytics.eu/themes/oa/logo.png diff --git a/src/test/resources/configs/simple_config_clustered.yaml b/src/test/resources/configs/simple_config_clustered.yaml index 0b0319e..10b5ade 100644 --- a/src/test/resources/configs/simple_config_clustered.yaml +++ b/src/test/resources/configs/simple_config_clustered.yaml @@ -4,7 +4,7 @@ metadata: name: example-shinyproxy spec: fqdn: itest2.local - image: openanalytics/shinyproxy:2.6.1 + image: openanalytics/shinyproxy-snapshot:3.1.0-SNAPSHOT-20240219.093646 proxy: title: Open Analytics Shiny Proxy logoUrl: http://www.openanalytics.eu/sites/www.openanalytics.eu/themes/oa/logo.png diff --git a/src/test/resources/configs/simple_config_multiple_namespaces.yaml b/src/test/resources/configs/simple_config_multiple_namespaces.yaml index 3cc5f57..5571abc 100644 --- a/src/test/resources/configs/simple_config_multiple_namespaces.yaml +++ b/src/test/resources/configs/simple_config_multiple_namespaces.yaml @@ -4,7 +4,7 @@ metadata: name: example-shinyproxy spec: fqdn: itest.local - image: openanalytics/shinyproxy:2.6.1 + image: openanalytics/shinyproxy-snapshot:3.1.0-SNAPSHOT-20240219.093646 proxy: title: Open Analytics Shiny Proxy logoUrl: http://www.openanalytics.eu/sites/www.openanalytics.eu/themes/oa/logo.png diff --git a/src/test/resources/configs/simple_config_subpath1.yaml b/src/test/resources/configs/simple_config_subpath1.yaml index cc604d5..dbd0804 100644 --- a/src/test/resources/configs/simple_config_subpath1.yaml +++ b/src/test/resources/configs/simple_config_subpath1.yaml @@ -4,7 +4,7 @@ metadata: name: example-shinyproxy spec: fqdn: itest.local - image: openanalytics/shinyproxy:2.6.1 + image: openanalytics/shinyproxy-snapshot:3.1.0-SNAPSHOT-20240219.093646 proxy: title: Open Analytics Shiny Proxy logoUrl: http://www.openanalytics.eu/sites/www.openanalytics.eu/themes/oa/logo.png diff --git a/src/test/resources/configs/simple_config_subpath2.yaml b/src/test/resources/configs/simple_config_subpath2.yaml index 96efe4d..34155b2 100644 --- a/src/test/resources/configs/simple_config_subpath2.yaml +++ b/src/test/resources/configs/simple_config_subpath2.yaml @@ -4,7 +4,7 @@ metadata: name: example-shinyproxy spec: fqdn: itest.local - image: openanalytics/shinyproxy:2.6.1 + image: openanalytics/shinyproxy-snapshot:3.1.0-SNAPSHOT-20240219.093646 proxy: title: Open Analytics Shiny Proxy logoUrl: http://www.openanalytics.eu/sites/www.openanalytics.eu/themes/oa/logo.png diff --git a/src/test/resources/configs/simple_config_updated.yaml b/src/test/resources/configs/simple_config_updated.yaml index c719b07..2a165d0 100644 --- a/src/test/resources/configs/simple_config_updated.yaml +++ b/src/test/resources/configs/simple_config_updated.yaml @@ -4,7 +4,7 @@ metadata: name: example-shinyproxy spec: fqdn: itest.local - image: openanalytics/shinyproxy:2.6.1 + image: openanalytics/shinyproxy-snapshot:3.1.0-SNAPSHOT-20240219.093646 proxy: title: Open Analytics Shiny Proxy 2 logoUrl: http://www.openanalytics.eu/sites/www.openanalytics.eu/themes/oa/logo.png diff --git a/src/test/resources/configs/simple_config_with_ingress_patches.yaml b/src/test/resources/configs/simple_config_with_ingress_patches.yaml new file mode 100644 index 0000000..29c284c --- /dev/null +++ b/src/test/resources/configs/simple_config_with_ingress_patches.yaml @@ -0,0 +1,41 @@ +apiVersion: openanalytics.eu/v1 +kind: ShinyProxy +metadata: + name: example-shinyproxy +spec: + fqdn: itest.local + image: openanalytics/shinyproxy-snapshot:3.1.0-SNAPSHOT-20240219.093646 + proxy: + title: Open Analytics Shiny Proxy + logoUrl: http://www.openanalytics.eu/sites/www.openanalytics.eu/themes/oa/logo.png + landingPage: / + heartbeatRate: 10000 + heartbeatTimeout: 60000 + port: 8080 + authentication: simple + containerBackend: kubernetes + kubernetes: + namespace: itest + users: + - name: demo + password: demo + groups: scientists + - name: demo2 + password: demo2 + groups: mathematicians + specs: + - id: 01_hello + displayName: Hello Application + description: Application which demonstrates the basics of a Shiny app + containerCmd: [ "R", "-e", "shinyproxy::run_01_hello()" ] + containerImage: openanalytics/shinyproxy-demo + - id: 06_tabsets + container-cmd: [ "R", "-e", "shinyproxy::run_06_tabsets()" ] + container-image: openanalytics/shinyproxy-demo + kubernetesIngressPatches: | + - op: add + path: /metadata/annotations + value: + nginx.ingress.kubernetes.io/proxy-buffer-size: "128k" + nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/proxy-body-size: 300m diff --git a/src/test/resources/configs/simple_config_with_patches.yaml b/src/test/resources/configs/simple_config_with_patches.yaml index e29c91f..1007e44 100644 --- a/src/test/resources/configs/simple_config_with_patches.yaml +++ b/src/test/resources/configs/simple_config_with_patches.yaml @@ -4,7 +4,7 @@ metadata: name: example-shinyproxy spec: fqdn: itest.local - image: openanalytics/shinyproxy:2.6.1 + image: openanalytics/shinyproxy-snapshot:3.1.0-SNAPSHOT-20240219.093646 proxy: title: Open Analytics Shiny Proxy logoUrl: http://www.openanalytics.eu/sites/www.openanalytics.eu/themes/oa/logo.png diff --git a/src/test/resources/configs/simple_config_with_service_patches.yaml b/src/test/resources/configs/simple_config_with_service_patches.yaml new file mode 100644 index 0000000..e48b8a4 --- /dev/null +++ b/src/test/resources/configs/simple_config_with_service_patches.yaml @@ -0,0 +1,39 @@ +apiVersion: openanalytics.eu/v1 +kind: ShinyProxy +metadata: + name: example-shinyproxy +spec: + fqdn: itest.local + image: openanalytics/shinyproxy-snapshot:3.1.0-SNAPSHOT-20240219.093646 + proxy: + title: Open Analytics Shiny Proxy + logoUrl: http://www.openanalytics.eu/sites/www.openanalytics.eu/themes/oa/logo.png + landingPage: / + heartbeatRate: 10000 + heartbeatTimeout: 60000 + port: 8080 + authentication: simple + containerBackend: kubernetes + kubernetes: + namespace: itest + users: + - name: demo + password: demo + groups: scientists + - name: demo2 + password: demo2 + groups: mathematicians + specs: + - id: 01_hello + displayName: Hello Application + description: Application which demonstrates the basics of a Shiny app + containerCmd: [ "R", "-e", "shinyproxy::run_01_hello()" ] + containerImage: openanalytics/shinyproxy-demo + - id: 06_tabsets + container-cmd: [ "R", "-e", "shinyproxy::run_06_tabsets()" ] + container-image: openanalytics/shinyproxy-demo + kubernetesServicePatches: | + - op: add + path: /metadata/annotations + value: + my-service-ingress-patch: "abc" From 04bd3fe30f2ed4af58f88359c1a843566eb11a2e Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Tue, 20 Feb 2024 13:06:27 +0100 Subject: [PATCH 24/34] Fix #32402: create new ShinyProxy server when going back to old config --- .../bases/clustered/crds/shinyproxy.crd.yaml | 3 +- .../bases/namespaced/crds/shinyproxy.crd.yaml | 3 +- .../components/LabelFactory.kt | 4 +- .../components/ResourceNameFactory.kt | 4 +- .../controller/PodRetriever.kt | 6 +- .../controller/ShinyProxyController.kt | 20 +++-- .../shinyproxyoperator/crd/ShinyProxy.kt | 3 +- .../crd/ShinyProxyInstance.kt | 2 +- .../crd/ShinyProxyStatus.kt | 2 +- .../shinyproxyoperator/MainIntegrationTest.kt | 74 +++++++++---------- .../helpers/IntegrationTestBase.kt | 11 ++- .../helpers/ShinyProxyTestInstance.kt | 53 ++++++++----- src/test/resources/configs/conflict.yaml | 2 +- src/test/resources/configs/simple_config.yaml | 4 +- .../configs/simple_config_updated.yaml | 4 +- src/test/resources/crd.yaml | 3 +- 16 files changed, 116 insertions(+), 82 deletions(-) diff --git a/docs/deployment/bases/clustered/crds/shinyproxy.crd.yaml b/docs/deployment/bases/clustered/crds/shinyproxy.crd.yaml index 5b017d2..f461d86 100644 --- a/docs/deployment/bases/clustered/crds/shinyproxy.crd.yaml +++ b/docs/deployment/bases/clustered/crds/shinyproxy.crd.yaml @@ -148,6 +148,8 @@ spec: type: string isLatestInstance: type: boolean + revision: + type: integer subresources: status: {} - name: v1alpha1 @@ -290,4 +292,3 @@ spec: type: boolean subresources: status: {} - diff --git a/docs/deployment/bases/namespaced/crds/shinyproxy.crd.yaml b/docs/deployment/bases/namespaced/crds/shinyproxy.crd.yaml index 5b017d2..f461d86 100644 --- a/docs/deployment/bases/namespaced/crds/shinyproxy.crd.yaml +++ b/docs/deployment/bases/namespaced/crds/shinyproxy.crd.yaml @@ -148,6 +148,8 @@ spec: type: string isLatestInstance: type: boolean + revision: + type: integer subresources: status: {} - name: v1alpha1 @@ -290,4 +292,3 @@ spec: type: boolean subresources: status: {} - diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/LabelFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/LabelFactory.kt index 5ef8d8d..bc1c98c 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/LabelFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/LabelFactory.kt @@ -30,7 +30,8 @@ object LabelFactory { return mapOf( APP_LABEL to APP_LABEL_VALUE, REALM_ID_LABEL to shinyProxy.realmId, - INSTANCE_LABEL to hashOfSpec + INSTANCE_LABEL to hashOfSpec, + REVISION_LABEL to shinyProxyInstance.revision.toString() ) } @@ -45,6 +46,7 @@ object LabelFactory { const val APP_LABEL_VALUE = "shinyproxy" const val REALM_ID_LABEL = "openanalytics.eu/sp-realm-id" const val INSTANCE_LABEL = "openanalytics.eu/sp-instance" + const val REVISION_LABEL = "openanalytics.eu/sp-instance-revision" const val LATEST_INSTANCE_LABEL = "openanalytics.eu/sp-latest-instance" const val PROXIED_APP = "openanalytics.eu/sp-proxied-app" diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ResourceNameFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ResourceNameFactory.kt index bc7c410..58df159 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ResourceNameFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ResourceNameFactory.kt @@ -32,11 +32,11 @@ object ResourceNameFactory { } fun createNameForPod(shinyProxy: ShinyProxy, shinyProxyInstance: ShinyProxyInstance): String { - return "sp-${shinyProxy.metadata.name}-pod-${shinyProxyInstance.hashOfSpec}".take(KUBE_RESOURCE_NAME_MAX_LENGTH) + return "sp-${shinyProxy.metadata.name}-pod-${shinyProxyInstance.revision}-${shinyProxyInstance.hashOfSpec}".take(KUBE_RESOURCE_NAME_MAX_LENGTH) } fun createNameForReplicaSet(shinyProxy: ShinyProxy, shinyProxyInstance: ShinyProxyInstance): String { - return "sp-${shinyProxy.metadata.name}-rs-${shinyProxyInstance.hashOfSpec}".take(KUBE_RESOURCE_NAME_MAX_LENGTH) + return "sp-${shinyProxy.metadata.name}-rs-${shinyProxyInstance.revision}-${shinyProxyInstance.hashOfSpec}".take(KUBE_RESOURCE_NAME_MAX_LENGTH) } fun createNameForService(shinyProxy: ShinyProxy): String { diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/PodRetriever.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/PodRetriever.kt index 832caeb..f811294 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/PodRetriever.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/PodRetriever.kt @@ -29,11 +29,7 @@ import io.fabric8.kubernetes.client.NamespacedKubernetesClient class PodRetriever(private val client: NamespacedKubernetesClient) { fun getShinyProxyPods(shinyProxy: ShinyProxy, shinyProxyInstance: ShinyProxyInstance): List { - val labels = mapOf( - LabelFactory.APP_LABEL to LabelFactory.APP_LABEL_VALUE, - LabelFactory.INSTANCE_LABEL to shinyProxyInstance.hashOfSpec - ) - return client.pods().inNamespace(shinyProxy.metadata.namespace).withLabels(labels).list().items + return client.pods().inNamespace(shinyProxy.metadata.namespace).withLabels(LabelFactory.labelsForShinyProxyInstance(shinyProxy, shinyProxyInstance)).list().items } } diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyController.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyController.kt index cc60408..b8160b1 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyController.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyController.kt @@ -130,19 +130,23 @@ class ShinyProxyController(private val channel: Channel, if (existingInstance != null && existingInstance.isLatestInstance) { logger.warn { "${shinyProxy.logPrefix(existingInstance)} Trying to create new instance which already exists and is the latest instance" } return existingInstance - } else if (existingInstance != null && !existingInstance.isLatestInstance) { + } + + val revision = if (existingInstance != null) { logger.info { "${shinyProxy.logPrefix(existingInstance)} Trying to create new instance which already exists and is not the latest instance. Therefore this instance will become the latest again" } // reconcile will take care of making this the latest instance again - return existingInstance + existingInstance.revision + 1 + } else { + 0 } // create new instance and add it to the list of instances // initial the instance is not the latest. Only when the ReplicaSet is created and fully running // the latestInstance marker will change to the new instance. - val newInstance = ShinyProxyInstance(shinyProxy.hashOfCurrentSpec, false) + val newInstance = ShinyProxyInstance(shinyProxy.hashOfCurrentSpec, false, revision) updateStatus(shinyProxy) { // Extra check, if this check is positive we have some bug - val checkExistingInstance = it.status.instances.firstOrNull { instance -> instance.hashOfSpec == newInstance.hashOfSpec } + val checkExistingInstance = it.status.instances.firstOrNull { instance -> instance.hashOfSpec == newInstance.hashOfSpec && instance.revision == newInstance.revision } if (checkExistingInstance != null) { // status has already been updated (e.g. after an HTTP 409 Conflict response) // remove the existing instance and add the new one, to ensure that all values are correct. @@ -189,8 +193,7 @@ class ShinyProxyController(private val channel: Channel, } private fun updateLatestMarker(shinyProxy: ShinyProxy, shinyProxyInstance: ShinyProxyInstance) { - val latestInstance = shinyProxy.status.instances.firstOrNull { it.hashOfSpec == shinyProxy.hashOfCurrentSpec } - ?: return + val latestInstance = shinyProxy.status.getInstanceByHash(shinyProxy.hashOfCurrentSpec) ?: return if (latestInstance.isLatestInstance) { // already updated marker return @@ -204,7 +207,7 @@ class ShinyProxyController(private val channel: Channel, updateStatus(shinyProxy) { it.status.instances.forEach { inst -> inst.isLatestInstance = false } - it.status.instances.first { inst -> inst.hashOfSpec == latestInstance.hashOfSpec }.isLatestInstance = true + it.status.getInstanceByHash(latestInstance.hashOfSpec)?.isLatestInstance = true } } @@ -282,7 +285,8 @@ class ShinyProxyController(private val channel: Channel, // take a copy of the list to check to prevent concurrent modification val instancesToCheck = shinyProxy.status.instances.toList() for (shinyProxyInstance in instancesToCheck) { - if (shinyProxyInstance.isLatestInstance || shinyProxyInstance.hashOfSpec == shinyProxy.hashOfCurrentSpec) { + val latestRevision = shinyProxy.status.getInstanceByHash(shinyProxyInstance.hashOfSpec)?.revision ?: 0 + if (shinyProxyInstance.isLatestInstance || (shinyProxyInstance.hashOfSpec == shinyProxy.hashOfCurrentSpec && shinyProxyInstance.revision >= latestRevision)) { // shinyProxyInstance is either the latest or the soon to be latest instance continue } diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt index 921d039..ae2d5ec 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxy.kt @@ -33,6 +33,7 @@ import io.fabric8.kubernetes.model.annotation.Group import io.fabric8.kubernetes.model.annotation.Version import javax.json.JsonPatch + @Version("v1") @Group("openanalytics.eu") class ShinyProxy : CustomResource(), Namespaced { @@ -202,7 +203,7 @@ class ShinyProxy : CustomResource(), Namespaced { } fun logPrefix(shinyProxyInstance: ShinyProxyInstance): String { - return "[${metadata.namespace}/${metadata.name}/${shinyProxyInstance.hashOfSpec}]" + return "[${metadata.namespace}/${metadata.name}/${shinyProxyInstance.hashOfSpec}/${shinyProxyInstance.revision}]" } fun logPrefix(): String { diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyInstance.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyInstance.kt index 377260d..3d41841 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyInstance.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyInstance.kt @@ -20,4 +20,4 @@ */ package eu.openanalytics.shinyproxyoperator.crd -data class ShinyProxyInstance(val hashOfSpec: String, var isLatestInstance: Boolean) \ No newline at end of file +data class ShinyProxyInstance(val hashOfSpec: String, var isLatestInstance: Boolean, val revision: Int = 0) diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyStatus.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyStatus.kt index f35c351..e2cce22 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyStatus.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyStatus.kt @@ -29,7 +29,7 @@ import io.fabric8.kubernetes.api.model.KubernetesResource data class ShinyProxyStatus(val instances: ArrayList = arrayListOf()) : KubernetesResource { fun getInstanceByHash(hash: String): ShinyProxyInstance? { - return instances.find { it.hashOfSpec == hash } + return instances.filter { it.hashOfSpec == hash }.maxByOrNull { it.revision } } fun latestInstance(): ShinyProxyInstance? { diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt index 4aa0e74..b868162 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt @@ -165,19 +165,26 @@ class MainIntegrationTest : IntegrationTestBase() { spTestInstance.assertInstanceIsCorrect() // 5. Delete Replicaset -> reconcile -> assert it is still ok + val replicaSetName = "sp-${sp.metadata.name}-rs-0-${spTestInstance.hash}".take(63) executeAsyncAfter100ms { - stableClient.apps().replicaSets() - .withName("sp-${sp.metadata.name}-rs-${spTestInstance.hash}".take(63)).delete() + getAndDelete(stableClient.apps().replicaSets().withName(replicaSetName)) logger.info { "Deleted ReplicaSet" } } spTestInstance.waitForReconcileCycle() logger.info { "Reconciled after deleting RS" } + // wait for replicaset to be ready + withTimeout(50_000) { + while (stableClient.apps().replicaSets().withName(replicaSetName)?.get()?.status?.readyReplicas != 1){ + logger.info { "Replicaset not yet ready" } + delay(1000) + } + } + logger.info { "Replicaset ready" } spTestInstance.assertInstanceIsCorrect() // 6. Delete ConfigMap -> reconcile -> assert it is still ok executeAsyncAfter100ms { - stableClient.configMaps().withName("sp-${sp.metadata.name}-cm-${spTestInstance.hash}".take(63)) - .delete() + getAndDelete(stableClient.configMaps().withName("sp-${sp.metadata.name}-cm-${spTestInstance.hash}".take(63))) logger.info { "Deleted ConfigMap" } } spTestInstance.waitForOneReconcile() @@ -186,8 +193,7 @@ class MainIntegrationTest : IntegrationTestBase() { // 7. Delete Service -> reconcile -> assert it is still ok executeAsyncAfter100ms { - stableClient.services().withName("sp-${sp.metadata.name}-svc".take(63)) - .delete() + getAndDelete(stableClient.services().withName("sp-${sp.metadata.name}-svc".take(63))) logger.info { "Deleted Service" } } spTestInstance.waitForOneReconcile() @@ -196,8 +202,7 @@ class MainIntegrationTest : IntegrationTestBase() { // 8. Delete Ingress -> reconcile -> assert it is still ok executeAsyncAfter100ms { - stableClient.network().v1().ingresses() - .withName("sp-${sp.metadata.name}-ing".take(63)).delete() + getAndDelete(stableClient.network().v1().ingresses().withName("sp-${sp.metadata.name}-ing".take(63))) logger.info { "Deleted Ingress" } } spTestInstance.waitForReconcileCycle() @@ -354,12 +359,7 @@ class MainIntegrationTest : IntegrationTestBase() { recyclableChecker.isRecyclable = true // 8. wait for delete to happen - while (stableClient.pods().withName("sp-${sp.metadata.name}-pod-${spTestInstanceOriginal.hash}".take(63)).get() != null - || stableClient.configMaps().withName("sp-${sp.metadata.name}-cm-${spTestInstanceOriginal.hash}".take(63)).get() != null - || stableClient.services().withName("sp-${sp.metadata.name}-svc-${spTestInstanceOriginal.hash}".take(63)).get() != null) { - delay(1000) - logger.debug { "Pod still exists!" } - } + spTestInstanceOriginal.waitForDeletion(sp) // 9. assert correctness spTestInstanceUpdated.assertInstanceIsCorrect() @@ -427,12 +427,7 @@ class MainIntegrationTest : IntegrationTestBase() { recyclableChecker.isRecyclable = true // 10. wait for delete to happen - while (stableClient.pods().withName("sp-${sp.metadata.name}-pod-${spTestInstanceOriginal.hash}".take(63)).get() != null - || stableClient.configMaps().withName("sp-${sp.metadata.name}-cm-${spTestInstanceOriginal.hash}".take(63)).get() != null - || stableClient.services().withName("sp-${sp.metadata.name}-svc-${spTestInstanceOriginal.hash}".take(63)).get() != null) { - delay(1000) - logger.debug { "Pod still exists!" } - } + spTestInstanceOriginal.waitForDeletion(sp) // 11. assert older instance does not exist anymore assertThrows("Instance not found") { @@ -636,12 +631,7 @@ class MainIntegrationTest : IntegrationTestBase() { spTestInstanceUpdated.waitForOneReconcile() // 7. wait for delete to happen - while (stableClient.pods().withName("sp-${sp.metadata.name}-pod-${spTestInstanceOriginal.hash}".take(63)).get() != null - || stableClient.configMaps().withName("sp-${sp.metadata.name}-cm-${spTestInstanceOriginal.hash}".take(63)).get() != null - || stableClient.services().withName("sp-${sp.metadata.name}-svc-${spTestInstanceOriginal.hash}".take(63)).get() != null) { - delay(1000) - logger.debug { "Pod still exists!" } - } + spTestInstanceOriginal.waitForDeletion(sp) // 8. assert correctness spTestInstanceUpdated.assertInstanceIsCorrect() @@ -712,8 +702,8 @@ class MainIntegrationTest : IntegrationTestBase() { @Test fun `restore old config version`() = - // idea of test: launch instance A, update config to get instance B, and the update config again - // using the same config as A, resulting in instance A' (which is the same instance as A, as A was never removed!) + // idea of test: launch instance A, update config to get instance B, and the update config again + // the operator will start a new instance, with an increased revision setup(Mode.NAMESPACED) { namespace, shinyProxyClient, namespacedClient, stableClient, operator, reconcileListener, recyclableChecker -> // 1. create a SP instance val instanceA = ShinyProxyTestInstance( @@ -776,22 +766,25 @@ class MainIntegrationTest : IntegrationTestBase() { // 10. wait until instance is created instanceAPrime.waitForOneReconcile() - // 11. wait for delete to happen + // 11. wait for delete of instance A to happen recyclableChecker.isRecyclable = true - while (stableClient.pods().withName("sp-${spB.metadata.name}-pod-${instanceB.hash}".take(63)).get() != null - || stableClient.configMaps().withName("sp-${spB.metadata.name}-cm-${instanceB.hash}".take(63)).get() != null - || stableClient.services().withName("sp-${spB.metadata.name}-svc-${instanceB.hash}".take(63)).get() != null) { - delay(1000) - logger.debug { "Pod still exists!" } + instanceA.waitForDeletion(spA) + + // 12. assert instance A does not exists anymore + assertThrows("Instance not found") { + instanceA.retrieveInstance(0) } - // 12. assert instance B does not exists anymore + // 13. wait for delete of instance B to happen + instanceB.waitForDeletion(spB) + + // 14. assert instance B does not exists anymore assertThrows("Instance not found") { instanceB.retrieveInstance() } // 13. assert instance A' is correct - instanceAPrime.assertInstanceIsCorrect(1, true) + instanceAPrime.assertInstanceIsCorrect(1, true, 1) job.cancel() @@ -952,7 +945,8 @@ class MainIntegrationTest : IntegrationTestBase() { assertEquals(mapOf( LabelFactory.APP_LABEL to LabelFactory.APP_LABEL_VALUE, LabelFactory.REALM_ID_LABEL to sp.realmId, - LabelFactory.INSTANCE_LABEL to spTestInstance.hash + LabelFactory.INSTANCE_LABEL to spTestInstance.hash, + LabelFactory.REVISION_LABEL to "0" ), rule.podAffinityTerm.labelSelector.matchLabels) job.cancel() @@ -1002,7 +996,8 @@ class MainIntegrationTest : IntegrationTestBase() { assertEquals(mapOf( LabelFactory.APP_LABEL to LabelFactory.APP_LABEL_VALUE, LabelFactory.REALM_ID_LABEL to sp.realmId, - LabelFactory.INSTANCE_LABEL to spTestInstance.hash + LabelFactory.INSTANCE_LABEL to spTestInstance.hash, + LabelFactory.REVISION_LABEL to "0" ), rule.labelSelector.matchLabels) job.cancel() @@ -1054,7 +1049,8 @@ class MainIntegrationTest : IntegrationTestBase() { assertEquals(mapOf( LabelFactory.APP_LABEL to LabelFactory.APP_LABEL_VALUE, LabelFactory.REALM_ID_LABEL to sp.realmId, - LabelFactory.INSTANCE_LABEL to spTestInstance.hash + LabelFactory.INSTANCE_LABEL to spTestInstance.hash, + LabelFactory.REVISION_LABEL to "0" ), rule.podAffinityTerm.labelSelector.matchLabels) job.cancel() diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/IntegrationTestBase.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/IntegrationTestBase.kt index 4e8e787..8f48676 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/IntegrationTestBase.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/IntegrationTestBase.kt @@ -29,10 +29,12 @@ import eu.openanalytics.shinyproxyoperator.createKubernetesClient import eu.openanalytics.shinyproxyoperator.logger import io.fabric8.kubernetes.api.model.NamespaceBuilder import io.fabric8.kubernetes.api.model.PodList +import io.fabric8.kubernetes.api.model.apps.ReplicaSet import io.fabric8.kubernetes.client.KubernetesClient import io.fabric8.kubernetes.client.KubernetesClientException import io.fabric8.kubernetes.client.NamespacedKubernetesClient -import io.fabric8.kubernetes.client.extended.run.RunConfigBuilder +import io.fabric8.kubernetes.client.dsl.Resource +import io.fabric8.kubernetes.client.dsl.RollableScalableResource import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Job import kotlinx.coroutines.delay @@ -221,6 +223,13 @@ abstract class IntegrationTestBase { )).list() } + protected fun getAndDelete(resource: Resource) { + if (resource.get() == null) { + throw IllegalStateException("Trying to delete resource but it does not exist!") + } + resource.delete() + } + } fun KubernetesClient.isStartupProbesSupported(): Boolean { diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt index 4a69ce4..ece172a 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt @@ -24,13 +24,16 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.registerKotlinModule import eu.openanalytics.shinyproxyoperator.ShinyProxyClient import eu.openanalytics.shinyproxyoperator.components.LabelFactory +import eu.openanalytics.shinyproxyoperator.components.ResourceNameFactory import eu.openanalytics.shinyproxyoperator.crd.ShinyProxy import eu.openanalytics.shinyproxyoperator.crd.ShinyProxyInstance +import eu.openanalytics.shinyproxyoperator.logger import io.fabric8.kubernetes.api.model.HasMetadata import io.fabric8.kubernetes.api.model.IntOrString import io.fabric8.kubernetes.client.NamespacedKubernetesClient import io.fabric8.kubernetes.client.readiness.Readiness import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.delay import kotlinx.coroutines.withTimeout import kotlin.test.assertEquals import kotlin.test.assertNotEquals @@ -55,6 +58,10 @@ class ShinyProxyTestInstance(private val namespace: String, return sp } + fun instance(sp: ShinyProxy, revision: Int = 0): ShinyProxyInstance { + return sp.status.instances.firstOrNull { it.hashOfSpec == hash && it.revision == revision } ?: error("ShinyProxyInstance with hash $hash not found") + } + suspend fun waitForOneReconcile(): ShinyProxyInstance { return withTimeout(120_000) { reconcileListener.waitForNextReconcile(hash).await() @@ -77,8 +84,18 @@ class ShinyProxyTestInstance(private val namespace: String, } } - fun assertInstanceIsCorrect(numInstancesRunning: Int = 1, isLatest: Boolean = true) { - val sp = retrieveInstance() + suspend fun waitForDeletion(sp: ShinyProxy, revision: Int = 0) { + val instance = ShinyProxyInstance(hash, false, revision) + while (client.apps().replicaSets().withName(ResourceNameFactory.createNameForReplicaSet(sp, instance)).get() != null + || client.configMaps().withName(ResourceNameFactory.createNameForReplicaSet(sp, instance)).get() != null) { + delay(1000) + logger.debug { "Pod still exists ${hash}!" } + } + } + + + fun assertInstanceIsCorrect(numInstancesRunning: Int = 1, isLatest: Boolean = true, revision: Int = 0) { + val sp = retrieveInstance(revision) assertNotNull(sp) val instance = sp.status.instances.firstOrNull { it.hashOfSpec == hash } assertNotNull(instance) @@ -86,13 +103,13 @@ class ShinyProxyTestInstance(private val namespace: String, assertEquals(numInstancesRunning, sp.status.instances.size) // check configmap - assertConfigMapIsCorrect(sp, numInstancesRunning, isLatest) + assertConfigMapIsCorrect(sp, numInstancesRunning, isLatest, revision) // check replicaset - assertReplicaSetIsCorrect(sp, numInstancesRunning) + assertReplicaSetIsCorrect(sp, numInstancesRunning, revision) // check service - assertServiceIsCorrect(sp) + assertServiceIsCorrect(sp, revision) // check ingress assertIngressIsCorrect(sp) @@ -124,7 +141,7 @@ class ShinyProxyTestInstance(private val namespace: String, assertEquals(80, path.backend.service.port.number) } - fun assertServiceIsCorrect(sp: ShinyProxy) { + fun assertServiceIsCorrect(sp: ShinyProxy, revision: Int = 0) { val services = client.inNamespace(namespace).services().list().items assertEquals(1, services.size) val service = services.firstOrNull { it.metadata.name == "sp-${sp.metadata.name}-svc".take(63) } @@ -145,12 +162,13 @@ class ShinyProxyTestInstance(private val namespace: String, assertEquals(mapOf( LabelFactory.APP_LABEL to LabelFactory.APP_LABEL_VALUE, LabelFactory.REALM_ID_LABEL to sp.realmId, - LabelFactory.INSTANCE_LABEL to sp.status.latestInstance()!!.hashOfSpec // TODO + LabelFactory.INSTANCE_LABEL to sp.status.latestInstance()!!.hashOfSpec, + LabelFactory.REVISION_LABEL to revision.toString() ), service.spec.selector) } - fun assertConfigMapIsCorrect(sp: ShinyProxy, numInstancesRunning: Int = 1, isLatest: Boolean = true) { + fun assertConfigMapIsCorrect(sp: ShinyProxy, numInstancesRunning: Int = 1, isLatest: Boolean = true, revision: Int = 0) { val configMaps = client.inNamespace(namespace).configMaps().list().items.filter { it.metadata.name != "kube-root-ca.crt" } assertEquals(numInstancesRunning, configMaps.size) val configMap = configMaps.firstOrNull { it.metadata.labels[LabelFactory.INSTANCE_LABEL] == hash } @@ -163,13 +181,13 @@ class ShinyProxyTestInstance(private val namespace: String, assertNotEquals(sp.specAsYaml, configMap.data["application.yml"]) } - assertLabelsAreCorrect(configMap, sp) + assertLabelsAreCorrect(configMap, sp, revision) assertOwnerReferenceIsCorrect(configMap, sp) // assertTrue(configMap.immutable) // TODO make the configmap immutable? } - fun assertReplicaSetIsCorrect(sp: ShinyProxy, numInstancesRunning: Int = 1) { + fun assertReplicaSetIsCorrect(sp: ShinyProxy, numInstancesRunning: Int = 1, revision: Int = 0) { val replicaSets = client.inNamespace(namespace).apps().replicaSets().list().items assertEquals(numInstancesRunning, replicaSets.size) val replicaSet = replicaSets.firstOrNull { it.metadata.labels[LabelFactory.INSTANCE_LABEL] == hash } @@ -177,8 +195,8 @@ class ShinyProxyTestInstance(private val namespace: String, assertEquals(1, replicaSet.status.replicas) assertEquals(1, replicaSet.status.readyReplicas) assertEquals(1, replicaSet.status.availableReplicas) - assertEquals("sp-${sp.metadata.name}-rs-${hash}".take(63), replicaSet.metadata.name) - assertLabelsAreCorrect(replicaSet, sp) + assertEquals("sp-${sp.metadata.name}-rs-${revision}-${hash}".take(63), replicaSet.metadata.name) + assertLabelsAreCorrect(replicaSet, sp, revision) assertOwnerReferenceIsCorrect(replicaSet, sp) val templateSpec = replicaSet.spec.template.spec @@ -192,7 +210,7 @@ class ShinyProxyTestInstance(private val namespace: String, assertNotNull(templateSpec.containers[0].env.firstOrNull { it.name == "SP_KUBE_POD_NAME" }) assertNotNull(templateSpec.containers[0].env.firstOrNull { it.name == "PROXY_REALM_ID" }) assertNotNull(templateSpec.containers[0].env.firstOrNull { it.name == "PROXY_VERSION" }) - assertEquals(sp.metadata.name + '-'+ sp.metadata.namespace, templateSpec.containers[0].env.firstOrNull { it.name == "PROXY_REALM_ID" }?.value) + assertEquals(sp.metadata.name + '-' + sp.metadata.namespace, templateSpec.containers[0].env.firstOrNull { it.name == "PROXY_REALM_ID" }?.value) assertEquals(1, templateSpec.containers[0].volumeMounts.size) assertEquals("config-volume", templateSpec.containers[0].volumeMounts[0].name) @@ -222,11 +240,12 @@ class ShinyProxyTestInstance(private val namespace: String, assertTrue(Readiness.getInstance().isReady(replicaSet)) } - fun assertLabelsAreCorrect(resource: HasMetadata, sp: ShinyProxy) { + fun assertLabelsAreCorrect(resource: HasMetadata, sp: ShinyProxy, revision: Int) { assertEquals(mapOf( LabelFactory.APP_LABEL to LabelFactory.APP_LABEL_VALUE, LabelFactory.REALM_ID_LABEL to sp.realmId, - LabelFactory.INSTANCE_LABEL to hash + LabelFactory.INSTANCE_LABEL to hash, + LabelFactory.REVISION_LABEL to revision.toString() ), resource.metadata.labels) } @@ -239,9 +258,9 @@ class ShinyProxyTestInstance(private val namespace: String, assertEquals(sp.metadata.uid, resource.metadata.ownerReferences[0].uid) } - fun retrieveInstance(): ShinyProxy { + fun retrieveInstance(revision: Int = 0): ShinyProxy { for (sp in shinyProxyClient.inNamespace(namespace).list().items) { - if (sp != null && sp.status.instances.find { it.hashOfSpec == hash } != null) { + if (sp != null && sp.status.instances.find { it.hashOfSpec == hash && it.revision == revision } != null) { return sp } } diff --git a/src/test/resources/configs/conflict.yaml b/src/test/resources/configs/conflict.yaml index 462e623..d9598f8 100644 --- a/src/test/resources/configs/conflict.yaml +++ b/src/test/resources/configs/conflict.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: ConfigMap metadata: - name: sp-example-shinyproxy-cm-502467f68c769f1a6a3f641603ec053e15a350 + name: sp-example-shinyproxy-cm-f5b84adc3f917d9256a0c779fd53080f13033e namespace: itest data: application.yml: | diff --git a/src/test/resources/configs/simple_config.yaml b/src/test/resources/configs/simple_config.yaml index 481563c..f7fda83 100644 --- a/src/test/resources/configs/simple_config.yaml +++ b/src/test/resources/configs/simple_config.yaml @@ -10,12 +10,14 @@ spec: logoUrl: http://www.openanalytics.eu/sites/www.openanalytics.eu/themes/oa/logo.png landingPage: / heartbeatRate: 10000 - heartbeatTimeout: 60000 + heartbeatTimeout: -1 port: 8080 authentication: simple containerBackend: kubernetes + stop-proxies-on-shutdown: false kubernetes: namespace: itest + internal-networking: true users: - name: demo password: demo diff --git a/src/test/resources/configs/simple_config_updated.yaml b/src/test/resources/configs/simple_config_updated.yaml index 2a165d0..33d85e6 100644 --- a/src/test/resources/configs/simple_config_updated.yaml +++ b/src/test/resources/configs/simple_config_updated.yaml @@ -10,12 +10,14 @@ spec: logoUrl: http://www.openanalytics.eu/sites/www.openanalytics.eu/themes/oa/logo.png landingPage: / heartbeatRate: 10000 - heartbeatTimeout: 60000 + heartbeatTimeout: -1 port: 8080 authentication: simple containerBackend: kubernetes + stop-proxies-on-shutdown: false kubernetes: namespace: itest + internal-networking: true users: - name: demo password: demo diff --git a/src/test/resources/crd.yaml b/src/test/resources/crd.yaml index 5b017d2..f461d86 100644 --- a/src/test/resources/crd.yaml +++ b/src/test/resources/crd.yaml @@ -148,6 +148,8 @@ spec: type: string isLatestInstance: type: boolean + revision: + type: integer subresources: status: {} - name: v1alpha1 @@ -290,4 +292,3 @@ spec: type: boolean subresources: status: {} - From 7a7e8453b9280756a6d182cc10302e3a09e30a0c Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Tue, 20 Feb 2024 13:30:24 +0100 Subject: [PATCH 25/34] Fix #30477: improve instance obsolete check --- .../controller/IRecyclableChecker.kt | 2 +- .../controller/RecyclableChecker.kt | 35 +++++++++++++------ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/IRecyclableChecker.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/IRecyclableChecker.kt index 5a5fba4..053d8e2 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/IRecyclableChecker.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/IRecyclableChecker.kt @@ -25,6 +25,6 @@ import eu.openanalytics.shinyproxyoperator.crd.ShinyProxyInstance interface IRecyclableChecker { - fun isInstanceRecyclable(shinyProxy: ShinyProxy, shinyProxyInstance: ShinyProxyInstance): Boolean + suspend fun isInstanceRecyclable(shinyProxy: ShinyProxy, shinyProxyInstance: ShinyProxyInstance): Boolean } diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/RecyclableChecker.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/RecyclableChecker.kt index 233ffe8..760eeeb 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/RecyclableChecker.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/RecyclableChecker.kt @@ -25,6 +25,7 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.registerKotlinModule import eu.openanalytics.shinyproxyoperator.crd.ShinyProxy import eu.openanalytics.shinyproxyoperator.crd.ShinyProxyInstance +import kotlinx.coroutines.delay import mu.KotlinLogging import okhttp3.OkHttpClient import okhttp3.Request @@ -46,21 +47,33 @@ class RecyclableChecker( data class Response(@JsonProperty("isRecyclable") val isRecyclable: Boolean, @JsonProperty("activeConnections") val activeConnections: Int) - override fun isInstanceRecyclable(shinyProxy: ShinyProxy, shinyProxyInstance: ShinyProxyInstance): Boolean { + override suspend fun isInstanceRecyclable(shinyProxy: ShinyProxy, shinyProxyInstance: ShinyProxyInstance): Boolean { val pods = podRetriever.getShinyProxyPods(shinyProxy, shinyProxyInstance) for (pod in pods) { for (i in 1..5) { - val resp = checkServer(pod.status.podIP) - if (resp == null) { - // no response received, try to check again - logger.warn { "${shinyProxy.logPrefix(shinyProxyInstance)} unreachable for recyclable check (using ${pod.status.podIP})" } - Thread.sleep(500) - continue - } - if (!resp.isRecyclable) { - logger.info { "${shinyProxy.logPrefix(shinyProxyInstance)} Replica is not recyclable." } - return false + try { + val podIP: String? = pod.status.podIP + if (podIP == null) { + // no response received, try to check again + logger.warn { "${shinyProxy.logPrefix(shinyProxyInstance)} no ip found during recyclable check" } + delay(500) + continue + } + val resp = checkServer(pod.status.podIP) + if (resp == null) { + // no response received, try to check again + logger.warn { "${shinyProxy.logPrefix(shinyProxyInstance)} unreachable for recyclable check (using ${pod.status.podIP})" } + delay(500) + continue + } + if (!resp.isRecyclable) { + logger.info { "${shinyProxy.logPrefix(shinyProxyInstance)} Replica is not recyclable." } + return false + } + } catch (e: Throwable) { + logger.warn(e) { "${shinyProxy.logPrefix(shinyProxyInstance)} exception during recyclable check" } + delay(500) } } } From 386bca2b927fbe2ee33f0c78782bf50e397f2741 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Tue, 20 Feb 2024 15:27:31 +0100 Subject: [PATCH 26/34] Ref #32380: improve tests --- .github/workflows/workflows.yaml | 5 +++-- .../resources/shinyproxy.shinyproxy.yaml | 4 ++-- .../shinyproxy/resources/shinyproxy.shinyproxy.yaml | 4 ++-- .../shinyproxy/resources/shinyproxy.shinyproxy.yaml | 4 ++-- .../resources/shinyproxy1.shinyproxy.yaml | 4 ++-- .../resources/shinyproxy2.shinyproxy.yaml | 4 ++-- .../resources/shinyproxy3.shinyproxy.yaml | 4 ++-- .../shinyproxyoperator/helpers/MockRecyclablecheckler.kt | 2 +- .../helpers/junit/TestExecutionListener.kt | 9 +++++++-- src/test/resources/configs/additional_fqdns.yaml | 4 ++-- src/test/resources/configs/affinity_required.yaml | 4 ++-- src/test/resources/configs/affinity_topologykey.yaml | 4 ++-- src/test/resources/configs/conflict.yaml | 2 +- src/test/resources/configs/simple_config.yaml | 5 +++-- src/test/resources/configs/simple_config_clustered.yaml | 4 ++-- .../configs/simple_config_multiple_namespaces.yaml | 4 ++-- src/test/resources/configs/simple_config_subpath1.yaml | 4 ++-- src/test/resources/configs/simple_config_subpath2.yaml | 4 ++-- src/test/resources/configs/simple_config_updated.yaml | 5 +++-- .../configs/simple_config_with_ingress_patches.yaml | 4 ++-- .../resources/configs/simple_config_with_patches.yaml | 4 ++-- .../configs/simple_config_with_service_patches.yaml | 4 ++-- 22 files changed, 50 insertions(+), 42 deletions(-) diff --git a/.github/workflows/workflows.yaml b/.github/workflows/workflows.yaml index 92c863e..8277141 100644 --- a/.github/workflows/workflows.yaml +++ b/.github/workflows/workflows.yaml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - java: [ 11 ] + java: [ 17 ] kubernetes: - 'v1.24.17' - 'v1.25.15' @@ -35,9 +35,10 @@ jobs: minikube version: 'v1.32.0' kubernetes version: ${{ matrix.kubernetes }} github token: ${{ secrets.GITHUB_TOKEN }} + driver: docker - name: Pull images run: | - minikube image pull openanalytics/shinyproxy-demo + minikube image pull openanalytics/shinyproxy-integration-test-app minikube image pull curlimages/curl:latest - name: Build with Maven run: mvn -B -U clean install -DskipTests diff --git a/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/shinyproxy.shinyproxy.yaml b/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/shinyproxy.shinyproxy.yaml index 45a2901..9575a9b 100644 --- a/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/shinyproxy.shinyproxy.yaml +++ b/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/shinyproxy.shinyproxy.yaml @@ -42,11 +42,11 @@ spec: display-name: Hello Application description: Application which demonstrates the basics of a Shiny app container-cmd: ["R", "-e", "shinyproxy::run_01_hello()"] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app access-groups: [scientists, mathematicians] - id: 06_tabsets container-cmd: ["R", "-e", "shinyproxy::run_06_tabsets()"] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app access-groups: scientists - id: rstudio displayName: RStudio diff --git a/docs/deployment/overlays/2-clustered/shinyproxy/resources/shinyproxy.shinyproxy.yaml b/docs/deployment/overlays/2-clustered/shinyproxy/resources/shinyproxy.shinyproxy.yaml index e710567..fbcb126 100644 --- a/docs/deployment/overlays/2-clustered/shinyproxy/resources/shinyproxy.shinyproxy.yaml +++ b/docs/deployment/overlays/2-clustered/shinyproxy/resources/shinyproxy.shinyproxy.yaml @@ -42,11 +42,11 @@ spec: display-name: Hello Application description: Application which demonstrates the basics of a Shiny app container-cmd: ["R", "-e", "shinyproxy::run_01_hello()"] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app access-groups: [scientists, mathematicians] - id: 06_tabsets container-cmd: ["R", "-e", "shinyproxy::run_06_tabsets()"] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app access-groups: scientists - id: rstudio displayName: RStudio diff --git a/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/resources/shinyproxy.shinyproxy.yaml b/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/resources/shinyproxy.shinyproxy.yaml index f739a01..3c79e75 100644 --- a/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/resources/shinyproxy.shinyproxy.yaml +++ b/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/resources/shinyproxy.shinyproxy.yaml @@ -43,7 +43,7 @@ spec: display-name: Hello Application description: Application which demonstrates the basics of a Shiny app container-cmd: [ "R", "-e", "shinyproxy::run_01_hello()" ] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app access-groups: [ scientists, mathematicians ] kubernetes-pod-patches: | - op: replace @@ -51,7 +51,7 @@ spec: value: my-namespace - id: 06_tabsets container-cmd: ["R", "-e", "shinyproxy::run_06_tabsets()"] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app access-groups: scientists - id: rstudio displayName: RStudio diff --git a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy1.shinyproxy.yaml b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy1.shinyproxy.yaml index b4ae244..add98b3 100644 --- a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy1.shinyproxy.yaml +++ b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy1.shinyproxy.yaml @@ -45,11 +45,11 @@ spec: display-name: Hello Application description: Application which demonstrates the basics of a Shiny app container-cmd: ["R", "-e", "shinyproxy::run_01_hello()"] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app access-groups: [scientists, mathematicians] - id: 06_tabsets container-cmd: ["R", "-e", "shinyproxy::run_06_tabsets()"] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app access-groups: scientists - id: rstudio displayName: RStudio diff --git a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy2.shinyproxy.yaml b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy2.shinyproxy.yaml index 1b40be6..ca77a10 100644 --- a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy2.shinyproxy.yaml +++ b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy2.shinyproxy.yaml @@ -46,11 +46,11 @@ spec: display-name: Hello Application description: Application which demonstrates the basics of a Shiny app container-cmd: ["R", "-e", "shinyproxy::run_01_hello()"] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app access-groups: [scientists, mathematicians] - id: 06_tabsets container-cmd: ["R", "-e", "shinyproxy::run_06_tabsets()"] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app access-groups: scientists - id: rstudio displayName: RStudio diff --git a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy3.shinyproxy.yaml b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy3.shinyproxy.yaml index 7f8d5c9..82c9f01 100644 --- a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy3.shinyproxy.yaml +++ b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy3.shinyproxy.yaml @@ -46,11 +46,11 @@ spec: display-name: Hello Application description: Application which demonstrates the basics of a Shiny app container-cmd: ["R", "-e", "shinyproxy::run_01_hello()"] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app access-groups: [scientists, mathematicians] - id: 06_tabsets container-cmd: ["R", "-e", "shinyproxy::run_06_tabsets()"] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app access-groups: scientists - id: rstudio displayName: RStudio diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/MockRecyclablecheckler.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/MockRecyclablecheckler.kt index 83551e8..2194301 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/MockRecyclablecheckler.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/MockRecyclablecheckler.kt @@ -30,7 +30,7 @@ class MockRecyclableChecker : IRecyclableChecker { @Volatile var isRecyclable: Boolean = false - override fun isInstanceRecyclable(shinyProxy: ShinyProxy, shinyProxyInstance: ShinyProxyInstance): Boolean { + override suspend fun isInstanceRecyclable(shinyProxy: ShinyProxy, shinyProxyInstance: ShinyProxyInstance): Boolean { return isRecyclable } diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/junit/TestExecutionListener.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/junit/TestExecutionListener.kt index accb2cb..b6bdbad 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/junit/TestExecutionListener.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/junit/TestExecutionListener.kt @@ -29,7 +29,9 @@ class TestExecutionListener : SummaryGeneratingListener() { init { Runtime.getRuntime().addShutdownHook(Thread { - summary.printTo(PrintWriter(System.out)) + if (summary != null) { + summary.printTo(PrintWriter(System.out)) + } }) } @@ -57,7 +59,10 @@ class TestExecutionListener : SummaryGeneratingListener() { println() println("\t\t--> Finished test \"${testIdentifier.displayName}\": $testExecutionResult") + if (testExecutionResult.throwable.isPresent) { + testExecutionResult.throwable.get().printStackTrace() + } println() } -} \ No newline at end of file +} diff --git a/src/test/resources/configs/additional_fqdns.yaml b/src/test/resources/configs/additional_fqdns.yaml index 27435a4..e38e328 100644 --- a/src/test/resources/configs/additional_fqdns.yaml +++ b/src/test/resources/configs/additional_fqdns.yaml @@ -30,7 +30,7 @@ spec: displayName: Hello Application description: Application which demonstrates the basics of a Shiny app containerCmd: [ "R", "-e", "shinyproxy::run_01_hello()" ] - containerImage: openanalytics/shinyproxy-demo + containerImage: openanalytics/shinyproxy-integration-test-app - id: 06_tabsets container-cmd: [ "R", "-e", "shinyproxy::run_06_tabsets()" ] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app diff --git a/src/test/resources/configs/affinity_required.yaml b/src/test/resources/configs/affinity_required.yaml index af8f790..1f34175 100644 --- a/src/test/resources/configs/affinity_required.yaml +++ b/src/test/resources/configs/affinity_required.yaml @@ -29,7 +29,7 @@ spec: displayName: Hello Application description: Application which demonstrates the basics of a Shiny app containerCmd: [ "R", "-e", "shinyproxy::run_01_hello()" ] - containerImage: openanalytics/shinyproxy-demo + containerImage: openanalytics/shinyproxy-integration-test-app - id: 06_tabsets container-cmd: [ "R", "-e", "shinyproxy::run_06_tabsets()" ] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app diff --git a/src/test/resources/configs/affinity_topologykey.yaml b/src/test/resources/configs/affinity_topologykey.yaml index af8c132..d5d7463 100644 --- a/src/test/resources/configs/affinity_topologykey.yaml +++ b/src/test/resources/configs/affinity_topologykey.yaml @@ -29,7 +29,7 @@ spec: displayName: Hello Application description: Application which demonstrates the basics of a Shiny app containerCmd: [ "R", "-e", "shinyproxy::run_01_hello()" ] - containerImage: openanalytics/shinyproxy-demo + containerImage: openanalytics/shinyproxy-integration-test-app - id: 06_tabsets container-cmd: [ "R", "-e", "shinyproxy::run_06_tabsets()" ] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app diff --git a/src/test/resources/configs/conflict.yaml b/src/test/resources/configs/conflict.yaml index d9598f8..1f22bb3 100644 --- a/src/test/resources/configs/conflict.yaml +++ b/src/test/resources/configs/conflict.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: ConfigMap metadata: - name: sp-example-shinyproxy-cm-f5b84adc3f917d9256a0c779fd53080f13033e + name: sp-example-shinyproxy-cm-a2460e4e35bfd14f72413e02ac6acfd0d33d7e namespace: itest data: application.yml: | diff --git a/src/test/resources/configs/simple_config.yaml b/src/test/resources/configs/simple_config.yaml index f7fda83..7cf44a1 100644 --- a/src/test/resources/configs/simple_config.yaml +++ b/src/test/resources/configs/simple_config.yaml @@ -15,6 +15,7 @@ spec: authentication: simple containerBackend: kubernetes stop-proxies-on-shutdown: false + default-stop-proxy-on-logout: false kubernetes: namespace: itest internal-networking: true @@ -30,7 +31,7 @@ spec: displayName: Hello Application description: Application which demonstrates the basics of a Shiny app containerCmd: [ "R", "-e", "shinyproxy::run_01_hello()" ] - containerImage: openanalytics/shinyproxy-demo + containerImage: openanalytics/shinyproxy-integration-test-app - id: 06_tabsets container-cmd: [ "R", "-e", "shinyproxy::run_06_tabsets()" ] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app diff --git a/src/test/resources/configs/simple_config_clustered.yaml b/src/test/resources/configs/simple_config_clustered.yaml index 10b5ade..26abd3c 100644 --- a/src/test/resources/configs/simple_config_clustered.yaml +++ b/src/test/resources/configs/simple_config_clustered.yaml @@ -28,7 +28,7 @@ spec: displayName: Hello Application description: Application which demonstrates the basics of a Shiny app containerCmd: [ "R", "-e", "shinyproxy::run_01_hello()" ] - containerImage: openanalytics/shinyproxy-demo + containerImage: openanalytics/shinyproxy-integration-test-app - id: 06_tabsets container-cmd: [ "R", "-e", "shinyproxy::run_06_tabsets()" ] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app diff --git a/src/test/resources/configs/simple_config_multiple_namespaces.yaml b/src/test/resources/configs/simple_config_multiple_namespaces.yaml index 5571abc..e6cb87c 100644 --- a/src/test/resources/configs/simple_config_multiple_namespaces.yaml +++ b/src/test/resources/configs/simple_config_multiple_namespaces.yaml @@ -28,13 +28,13 @@ spec: displayName: Hello Application description: Application which demonstrates the basics of a Shiny app containerCmd: [ "R", "-e", "shinyproxy::run_01_hello()" ] - containerImage: openanalytics/shinyproxy-demo + containerImage: openanalytics/shinyproxy-integration-test-app kubernetes-pod-patches: | - op: replace path: /metadata/namespace value: my-namespace - id: 06_tabsets container-cmd: [ "R", "-e", "shinyproxy::run_06_tabsets()" ] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app appNamespaces: - my-namespace diff --git a/src/test/resources/configs/simple_config_subpath1.yaml b/src/test/resources/configs/simple_config_subpath1.yaml index dbd0804..139b369 100644 --- a/src/test/resources/configs/simple_config_subpath1.yaml +++ b/src/test/resources/configs/simple_config_subpath1.yaml @@ -28,10 +28,10 @@ spec: displayName: Hello Application description: Application which demonstrates the basics of a Shiny app containerCmd: [ "R", "-e", "shinyproxy::run_01_hello()" ] - containerImage: openanalytics/shinyproxy-demo + containerImage: openanalytics/shinyproxy-integration-test-app - id: 06_tabsets container-cmd: [ "R", "-e", "shinyproxy::run_06_tabsets()" ] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app server: servlet: context-path: /sub-path diff --git a/src/test/resources/configs/simple_config_subpath2.yaml b/src/test/resources/configs/simple_config_subpath2.yaml index 34155b2..91ee027 100644 --- a/src/test/resources/configs/simple_config_subpath2.yaml +++ b/src/test/resources/configs/simple_config_subpath2.yaml @@ -28,10 +28,10 @@ spec: displayName: Hello Application description: Application which demonstrates the basics of a Shiny app containerCmd: [ "R", "-e", "shinyproxy::run_01_hello()" ] - containerImage: openanalytics/shinyproxy-demo + containerImage: openanalytics/shinyproxy-integration-test-app - id: 06_tabsets container-cmd: [ "R", "-e", "shinyproxy::run_06_tabsets()" ] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app server: servlet: context-path: /sub-path/ diff --git a/src/test/resources/configs/simple_config_updated.yaml b/src/test/resources/configs/simple_config_updated.yaml index 33d85e6..b7de94f 100644 --- a/src/test/resources/configs/simple_config_updated.yaml +++ b/src/test/resources/configs/simple_config_updated.yaml @@ -15,6 +15,7 @@ spec: authentication: simple containerBackend: kubernetes stop-proxies-on-shutdown: false + default-stop-proxy-on-logout: false kubernetes: namespace: itest internal-networking: true @@ -30,7 +31,7 @@ spec: displayName: Hello Application description: Application which demonstrates the basics of a Shiny app containerCmd: [ "R", "-e", "shinyproxy::run_01_hello()" ] - containerImage: openanalytics/shinyproxy-demo + containerImage: openanalytics/shinyproxy-integration-test-app - id: 06_tabsets container-cmd: [ "R", "-e", "shinyproxy::run_06_tabsets()" ] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app diff --git a/src/test/resources/configs/simple_config_with_ingress_patches.yaml b/src/test/resources/configs/simple_config_with_ingress_patches.yaml index 29c284c..bf68f69 100644 --- a/src/test/resources/configs/simple_config_with_ingress_patches.yaml +++ b/src/test/resources/configs/simple_config_with_ingress_patches.yaml @@ -28,10 +28,10 @@ spec: displayName: Hello Application description: Application which demonstrates the basics of a Shiny app containerCmd: [ "R", "-e", "shinyproxy::run_01_hello()" ] - containerImage: openanalytics/shinyproxy-demo + containerImage: openanalytics/shinyproxy-integration-test-app - id: 06_tabsets container-cmd: [ "R", "-e", "shinyproxy::run_06_tabsets()" ] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app kubernetesIngressPatches: | - op: add path: /metadata/annotations diff --git a/src/test/resources/configs/simple_config_with_patches.yaml b/src/test/resources/configs/simple_config_with_patches.yaml index 1007e44..6434bf0 100644 --- a/src/test/resources/configs/simple_config_with_patches.yaml +++ b/src/test/resources/configs/simple_config_with_patches.yaml @@ -28,10 +28,10 @@ spec: displayName: Hello Application description: Application which demonstrates the basics of a Shiny app containerCmd: [ "R", "-e", "shinyproxy::run_01_hello()" ] - containerImage: openanalytics/shinyproxy-demo + containerImage: openanalytics/shinyproxy-integration-test-app - id: 06_tabsets container-cmd: [ "R", "-e", "shinyproxy::run_06_tabsets()" ] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app kubernetesPodTemplateSpecPatches: | - op: add path: /spec/containers/0/env/- diff --git a/src/test/resources/configs/simple_config_with_service_patches.yaml b/src/test/resources/configs/simple_config_with_service_patches.yaml index e48b8a4..1adf70f 100644 --- a/src/test/resources/configs/simple_config_with_service_patches.yaml +++ b/src/test/resources/configs/simple_config_with_service_patches.yaml @@ -28,10 +28,10 @@ spec: displayName: Hello Application description: Application which demonstrates the basics of a Shiny app containerCmd: [ "R", "-e", "shinyproxy::run_01_hello()" ] - containerImage: openanalytics/shinyproxy-demo + containerImage: openanalytics/shinyproxy-integration-test-app - id: 06_tabsets container-cmd: [ "R", "-e", "shinyproxy::run_06_tabsets()" ] - container-image: openanalytics/shinyproxy-demo + container-image: openanalytics/shinyproxy-integration-test-app kubernetesServicePatches: | - op: add path: /metadata/annotations From eebd3595b4bc0f47cf4825fbb8d76e726df68454 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Mon, 26 Feb 2024 15:02:44 +0100 Subject: [PATCH 27/34] Fix #30945: add nginx.org/websocket-services --- .../components/IngressFactory.kt | 1 + .../shinyproxyoperator/MainIntegrationTest.kt | 22 ++++++++++++++++++- .../helpers/ShinyProxyTestInstance.kt | 6 +++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt index 6ef1c9a..dff7025 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt @@ -54,6 +54,7 @@ class IngressFactory(private val kubeClient: KubernetesClient) { .withNewMetadata() .withName(ResourceNameFactory.createNameForIngress(shinyProxy)) .withLabels(labels) + .withAnnotations(mapOf("nginx.org/websocket-services" to ResourceNameFactory.createNameForService(shinyProxy))) .addNewOwnerReference() .withController(true) .withKind("ShinyProxy") diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt index b868162..f15c880 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt @@ -1153,14 +1153,34 @@ class MainIntegrationTest : IntegrationTestBase() { spTestInstance.waitForOneReconcile() // 4. assert correctness - spTestInstance.assertInstanceIsCorrect() val sp = spTestInstance.retrieveInstance() + assertNotNull(sp) + val instance = sp.status.instances[0] + assertNotNull(instance) + assertTrue(instance.isLatestInstance) + + // check configmap + spTestInstance.assertConfigMapIsCorrect(sp) + + // check replicaset + spTestInstance.assertReplicaSetIsCorrect(sp) + // check service + spTestInstance.assertServiceIsCorrect(spTestInstance.retrieveInstance()) + + // check ingress val allIngresses = namespacedClient.network().v1().ingresses().list().items assertEquals(1, allIngresses.size) val ingress = allIngresses.firstOrNull { it.metadata.name == "sp-${sp.metadata.name}-ing".take(63) } assertNotNull(ingress) + assertEquals(mapOf( + LabelFactory.APP_LABEL to LabelFactory.APP_LABEL_VALUE, + LabelFactory.REALM_ID_LABEL to sp.realmId, + LabelFactory.LATEST_INSTANCE_LABEL to sp.status.latestInstance()!!.hashOfSpec + ), ingress.metadata.labels) + + // nginx.org annotation was replaced assertEquals(mapOf( "nginx.ingress.kubernetes.io/proxy-buffer-size" to "128k", "nginx.ingress.kubernetes.io/ssl-redirect" to "true", diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt index ece172a..4d7d93c 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/ShinyProxyTestInstance.kt @@ -127,6 +127,12 @@ class ShinyProxyTestInstance(private val namespace: String, LabelFactory.LATEST_INSTANCE_LABEL to sp.status.latestInstance()!!.hashOfSpec ), ingress.metadata.labels) + assertEquals(mapOf( + "nginx.org/websocket-services" to "sp-${sp.metadata.name}-svc".take(63), + ), + ingress.metadata.annotations + ) + assertOwnerReferenceIsCorrect(ingress, sp) assertEquals(1, ingress.spec.rules.size) From 698925514572114550e35e908ad3228ea1787c28 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Mon, 26 Feb 2024 16:35:24 +0100 Subject: [PATCH 28/34] Fix all tests failing when one fails --- .../shinyproxyoperator/MainIntegrationTest.kt | 80 +++++-------------- .../helpers/IntegrationTestBase.kt | 6 ++ .../helpers/junit/TestExecutionListener.kt | 4 +- 3 files changed, 30 insertions(+), 60 deletions(-) diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt index f15c880..33fa627 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/MainIntegrationTest.kt @@ -27,7 +27,6 @@ import eu.openanalytics.shinyproxyoperator.helpers.IntegrationTestBase import eu.openanalytics.shinyproxyoperator.helpers.ShinyProxyTestInstance import io.fabric8.kubernetes.api.model.IntOrString import io.fabric8.kubernetes.client.readiness.Readiness -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeout @@ -97,7 +96,7 @@ class MainIntegrationTest : IntegrationTestBase() { assertTrue(checked) // actually checked that ingress wasn't created when the ReplicaSet wasn't ready yet - val job = GlobalScope.launch { + scope.launch { // let the operator finish its business operator.run(resourceRetriever, shinyProxyLister) } @@ -107,7 +106,6 @@ class MainIntegrationTest : IntegrationTestBase() { // 5. assert correctness spTestInstance.assertInstanceIsCorrect() - job.cancel() } @Test @@ -125,7 +123,7 @@ class MainIntegrationTest : IntegrationTestBase() { val (resourceRetriever, shinyProxyLister) = operator.prepare() // 2. start the operator and let it do it's work - val job = GlobalScope.launch { + scope.launch { operator.run(resourceRetriever, shinyProxyLister) } @@ -134,7 +132,6 @@ class MainIntegrationTest : IntegrationTestBase() { // 4. assert correctness spTestInstance.assertInstanceIsCorrect() - job.cancel() } @Test @@ -153,7 +150,7 @@ class MainIntegrationTest : IntegrationTestBase() { // 2. start the operator and let it do it's work val (resourceRetriever, shinyProxyLister) = operator.prepare() - val job = GlobalScope.launch { + scope.launch { operator.run(resourceRetriever, shinyProxyLister) } @@ -208,9 +205,6 @@ class MainIntegrationTest : IntegrationTestBase() { spTestInstance.waitForReconcileCycle() spTestInstance.assertInstanceIsCorrect() logger.info { "Reconciled after deleting Ingress" } - - job.cancel() - logger.info { "Operator stopped" } } @Test @@ -228,7 +222,7 @@ class MainIntegrationTest : IntegrationTestBase() { // 2. start the operator and let it do it's work val (resourceRetriever, shinyProxyLister) = operator.prepare() - val job = GlobalScope.launch { + scope.launch { operator.run(resourceRetriever, shinyProxyLister) } @@ -237,8 +231,6 @@ class MainIntegrationTest : IntegrationTestBase() { // assert that there are no ReplicaSets created assertEquals(0, stableClient.apps().replicaSets().list().items.size) - - job.cancel() } @Test @@ -256,7 +248,7 @@ class MainIntegrationTest : IntegrationTestBase() { // 2. start the operator and let it do it's work val (resourceRetriever, shinyProxyLister) = operator.prepare() - val job = GlobalScope.launch { + scope.launch { operator.run(resourceRetriever, shinyProxyLister) } @@ -310,8 +302,6 @@ class MainIntegrationTest : IntegrationTestBase() { // check ingress spTestInstance.assertIngressIsCorrect(spTestInstance.retrieveInstance()) - - job.cancel() } @Test @@ -330,7 +320,7 @@ class MainIntegrationTest : IntegrationTestBase() { // 2. start the operator and let it do it's work val (resourceRetriever, shinyProxyLister) = operator.prepare() - val job = GlobalScope.launch { + scope.launch { operator.run(resourceRetriever, shinyProxyLister) } @@ -368,8 +358,6 @@ class MainIntegrationTest : IntegrationTestBase() { assertThrows("Instance not found") { spTestInstanceOriginal.retrieveInstance() } - - job.cancel() } @Test @@ -388,7 +376,7 @@ class MainIntegrationTest : IntegrationTestBase() { // 2. start the operator and let it do it's work val (resourceRetriever, shinyProxyLister) = operator.prepare() - val job = GlobalScope.launch { + scope.launch { operator.run(resourceRetriever, shinyProxyLister) } @@ -439,8 +427,6 @@ class MainIntegrationTest : IntegrationTestBase() { // 13. assert app still exists assertEquals(1, getPodsForInstance(spTestInstanceOriginal.hash)?.items?.size) - - job.cancel() } @Test @@ -458,7 +444,7 @@ class MainIntegrationTest : IntegrationTestBase() { // 2. start the operator and let it do it's work val (resourceRetriever, shinyProxyLister) = operator.prepare() - val job = GlobalScope.launch { + scope.launch { operator.run(resourceRetriever, shinyProxyLister) } @@ -483,8 +469,6 @@ class MainIntegrationTest : IntegrationTestBase() { // 7. assert correctness spTestInstance2.assertInstanceIsCorrect() - - job.cancel() } @Test @@ -502,7 +486,7 @@ class MainIntegrationTest : IntegrationTestBase() { // 2. start the operator and let it do it's work val (resourceRetriever, shinyProxyLister) = operator.prepare() - val job = GlobalScope.launch { + scope.launch { operator.run(resourceRetriever, shinyProxyLister) } @@ -516,8 +500,6 @@ class MainIntegrationTest : IntegrationTestBase() { val ingresses = namespacedClient.inNamespace(namespace).network().v1().ingresses().list().items assertEquals(1, ingresses.size) assertTrue(ingresses.get(0).spec.rules.get(0).http.paths.get(0).path.endsWith("/")); - - job.cancel() } @@ -536,7 +518,7 @@ class MainIntegrationTest : IntegrationTestBase() { // 2. start the operator and let it do it's work val (resourceRetriever, shinyProxyLister) = operator.prepare() - val job = GlobalScope.launch { + scope.launch { operator.run(resourceRetriever, shinyProxyLister) } @@ -551,8 +533,6 @@ class MainIntegrationTest : IntegrationTestBase() { val ingresses = namespacedClient.inNamespace(namespace).network().v1().ingresses().list().items assertEquals(1, ingresses.size) assertTrue(ingresses.get(0).spec.rules.get(0).http.paths.get(0).path.endsWith("/")); - - job.cancel() } /** @@ -574,7 +554,7 @@ class MainIntegrationTest : IntegrationTestBase() { // 2. start the operator and let it do it's work val (resourceRetriever, shinyProxyLister) = operator.prepare() - val job = GlobalScope.launch { + val job = scope.launch { operator.run(resourceRetriever, shinyProxyLister) } @@ -640,8 +620,6 @@ class MainIntegrationTest : IntegrationTestBase() { assertThrows("Instance not found") { spTestInstanceOriginal.retrieveInstance() } - - job.cancel() } @Test @@ -662,7 +640,7 @@ class MainIntegrationTest : IntegrationTestBase() { val (resourceRetriever, shinyProxyLister) = operator.prepare() // 2. start the operator and let it do it's work - val job = GlobalScope.launch { + val job = scope.launch { operator.run(resourceRetriever, shinyProxyLister) } @@ -677,7 +655,7 @@ class MainIntegrationTest : IntegrationTestBase() { val instance = spTestInstance.retrieveInstance().status.instances.first() // 5. schedule reconcile directly after deleting - GlobalScope.launch { + scope.launch { repeat(10) { delay(10) logger.debug { "Trying to trigger bug, by triggering reconcile with old status" } @@ -717,7 +695,7 @@ class MainIntegrationTest : IntegrationTestBase() { val (resourceRetriever, shinyProxyLister) = operator.prepare() // 2. start the operator and let it do it's work - val job = GlobalScope.launch { + scope.launch { operator.run(resourceRetriever, shinyProxyLister) } @@ -785,9 +763,6 @@ class MainIntegrationTest : IntegrationTestBase() { // 13. assert instance A' is correct instanceAPrime.assertInstanceIsCorrect(1, true, 1) - - job.cancel() - } // see #25154 @@ -888,7 +863,7 @@ class MainIntegrationTest : IntegrationTestBase() { val (resourceRetriever, shinyProxyLister) = operator.prepare() // 3. start the operator and let it do it's work - val job = GlobalScope.launch { + scope.launch { operator.run(resourceRetriever, shinyProxyLister) } @@ -897,7 +872,6 @@ class MainIntegrationTest : IntegrationTestBase() { // 5. assert correctness spTestInstance.assertInstanceIsCorrect() - job.cancel() } @Test @@ -916,7 +890,7 @@ class MainIntegrationTest : IntegrationTestBase() { val (resourceRetriever, shinyProxyLister) = operator.prepare() // 3. start the operator and let it do it's work - val job = GlobalScope.launch { + scope.launch { operator.run(resourceRetriever, shinyProxyLister) } @@ -948,8 +922,6 @@ class MainIntegrationTest : IntegrationTestBase() { LabelFactory.INSTANCE_LABEL to spTestInstance.hash, LabelFactory.REVISION_LABEL to "0" ), rule.podAffinityTerm.labelSelector.matchLabels) - - job.cancel() } @Test @@ -968,7 +940,7 @@ class MainIntegrationTest : IntegrationTestBase() { val (resourceRetriever, shinyProxyLister) = operator.prepare() // 3. start the operator and let it do it's work - val job = GlobalScope.launch { + scope.launch { operator.run(resourceRetriever, shinyProxyLister) } @@ -999,8 +971,6 @@ class MainIntegrationTest : IntegrationTestBase() { LabelFactory.INSTANCE_LABEL to spTestInstance.hash, LabelFactory.REVISION_LABEL to "0" ), rule.labelSelector.matchLabels) - - job.cancel() } @@ -1020,7 +990,7 @@ class MainIntegrationTest : IntegrationTestBase() { val (resourceRetriever, shinyProxyLister) = operator.prepare() // 3. start the operator and let it do it's work - val job = GlobalScope.launch { + scope.launch { operator.run(resourceRetriever, shinyProxyLister) } @@ -1052,8 +1022,6 @@ class MainIntegrationTest : IntegrationTestBase() { LabelFactory.INSTANCE_LABEL to spTestInstance.hash, LabelFactory.REVISION_LABEL to "0" ), rule.podAffinityTerm.labelSelector.matchLabels) - - job.cancel() } @Test @@ -1071,7 +1039,7 @@ class MainIntegrationTest : IntegrationTestBase() { val (resourceRetriever, shinyProxyLister) = operator.prepare() // 2. start the operator and let it do it's work - val job = GlobalScope.launch { + scope.launch { operator.run(resourceRetriever, shinyProxyLister) } @@ -1126,8 +1094,6 @@ class MainIntegrationTest : IntegrationTestBase() { assertEquals(sp.subPath, path2.path) assertEquals("sp-${sp.metadata.name}-svc".take(63), path2.backend.service.name) assertEquals(80, path2.backend.service.port.number) - - job.cancel() } @Test @@ -1145,7 +1111,7 @@ class MainIntegrationTest : IntegrationTestBase() { val (resourceRetriever, shinyProxyLister) = operator.prepare() // 2. start the operator and let it do it's work - val job = GlobalScope.launch { + scope.launch { operator.run(resourceRetriever, shinyProxyLister) } @@ -1186,8 +1152,6 @@ class MainIntegrationTest : IntegrationTestBase() { "nginx.ingress.kubernetes.io/ssl-redirect" to "true", "nginx.ingress.kubernetes.io/proxy-body-size" to "300m" ), ingress.metadata.annotations) - - job.cancel() } @Test @@ -1205,7 +1169,7 @@ class MainIntegrationTest : IntegrationTestBase() { val (resourceRetriever, shinyProxyLister) = operator.prepare() // 2. start the operator and let it do it's work - val job = GlobalScope.launch { + scope.launch { operator.run(resourceRetriever, shinyProxyLister) } @@ -1224,8 +1188,6 @@ class MainIntegrationTest : IntegrationTestBase() { assertEquals(mapOf( "my-service-ingress-patch" to "abc" ), service.metadata.annotations) - - job.cancel() } } diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/IntegrationTestBase.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/IntegrationTestBase.kt index 8f48676..23d4226 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/IntegrationTestBase.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/IntegrationTestBase.kt @@ -35,8 +35,11 @@ import io.fabric8.kubernetes.client.KubernetesClientException import io.fabric8.kubernetes.client.NamespacedKubernetesClient import io.fabric8.kubernetes.client.dsl.Resource import io.fabric8.kubernetes.client.dsl.RollableScalableResource +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Job +import kotlinx.coroutines.cancel import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking @@ -59,9 +62,12 @@ abstract class IntegrationTestBase { createKubernetesClient() } + protected val scope = CoroutineScope(Dispatchers.Default) + @AfterEach fun cleanup() { runBlocking { + scope.cancel() deleteNamespaces() stableClient.httpClient.close() } diff --git a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/junit/TestExecutionListener.kt b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/junit/TestExecutionListener.kt index b6bdbad..43cd5dc 100644 --- a/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/junit/TestExecutionListener.kt +++ b/src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/junit/TestExecutionListener.kt @@ -60,7 +60,9 @@ class TestExecutionListener : SummaryGeneratingListener() { println() println("\t\t--> Finished test \"${testIdentifier.displayName}\": $testExecutionResult") if (testExecutionResult.throwable.isPresent) { - testExecutionResult.throwable.get().printStackTrace() + println() + print("\t\t--> ") + println(testExecutionResult.throwable.get().stackTraceToString()) } println() } From f5679925f750a724c4a32c80a25a59efdab1be01 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Fri, 12 Apr 2024 17:28:47 +0200 Subject: [PATCH 29/34] Ref #32454: use forceConflicts for SSA --- .../shinyproxyoperator/components/ConfigMapFactory.kt | 2 +- .../shinyproxyoperator/components/IngressFactory.kt | 2 +- .../shinyproxyoperator/components/ReplicaSetFactory.kt | 2 +- .../shinyproxyoperator/components/ServiceFactory.kt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ConfigMapFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ConfigMapFactory.kt index 156e808..558bfa3 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ConfigMapFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ConfigMapFactory.kt @@ -55,7 +55,7 @@ class ConfigMapFactory(private val kubeClient: KubernetesClient) { .addToData("application.yml", shinyProxy.specAsYaml) .build() //@formatter:on - val createdConfigMap = kubeClient.configMaps().inNamespace(shinyProxy.metadata.namespace).resource(configMapDefinition).serverSideApply() + val createdConfigMap = kubeClient.configMaps().inNamespace(shinyProxy.metadata.namespace).resource(configMapDefinition).forceConflicts().serverSideApply() logger.debug { "${shinyProxy.logPrefix(shinyProxyInstance)} [Component/ConfigMap] Created ${createdConfigMap.metadata.name}" } } diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt index dff7025..2e7c422 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/IngressFactory.kt @@ -71,7 +71,7 @@ class IngressFactory(private val kubeClient: KubernetesClient) { //@formatter:on val patchedIngress = ingressPatcher.patch(ingressDefinition, shinyProxy.parsedIngressPatches) - val createdIngress = kubeClient.network().v1().ingresses().inNamespace(shinyProxy.metadata.namespace).resource(patchedIngress).serverSideApply() + val createdIngress = kubeClient.network().v1().ingresses().inNamespace(shinyProxy.metadata.namespace).resource(patchedIngress).forceConflicts().serverSideApply() logger.debug { "${shinyProxy.logPrefix()} [Component/Ingress] Created ${createdIngress.metadata.name}" } } diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ReplicaSetFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ReplicaSetFactory.kt index 0755a56..1b567eb 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ReplicaSetFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ReplicaSetFactory.kt @@ -58,7 +58,7 @@ class ReplicaSetFactory(private val kubeClient: KubernetesClient) { .build() //@formatter:on - val createdReplicaSet = kubeClient.apps().replicaSets().inNamespace(shinyProxy.metadata.namespace).resource(replicaSetDefinition).serverSideApply() + val createdReplicaSet = kubeClient.apps().replicaSets().inNamespace(shinyProxy.metadata.namespace).resource(replicaSetDefinition).forceConflicts().serverSideApply() logger.debug { "${shinyProxy.logPrefix(shinyProxyInstance)} [Component/ReplicaSet] Created ${createdReplicaSet.metadata.name}" } } diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ServiceFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ServiceFactory.kt index c1b2ffa..4078502 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ServiceFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/ServiceFactory.kt @@ -66,7 +66,7 @@ class ServiceFactory(private val serviceClient: MixedOperation Date: Fri, 19 Apr 2024 14:11:34 +0200 Subject: [PATCH 30/34] Fix orphaned resources during operator update --- .../shinyproxyoperator/components/LabelFactory.kt | 8 ++++++-- .../shinyproxyoperator/controller/ShinyProxyController.kt | 7 ++----- .../shinyproxyoperator/crd/ShinyProxyInstance.kt | 2 +- .../shinyproxyoperator/crd/ShinyProxyStatus.kt | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/LabelFactory.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/LabelFactory.kt index bc1c98c..31a555d 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/LabelFactory.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/components/LabelFactory.kt @@ -27,12 +27,16 @@ object LabelFactory { fun labelsForShinyProxyInstance(shinyProxy: ShinyProxy, shinyProxyInstance: ShinyProxyInstance): Map { val hashOfSpec = shinyProxyInstance.hashOfSpec - return mapOf( + val labels = hashMapOf( APP_LABEL to APP_LABEL_VALUE, REALM_ID_LABEL to shinyProxy.realmId, INSTANCE_LABEL to hashOfSpec, - REVISION_LABEL to shinyProxyInstance.revision.toString() ) + if (shinyProxyInstance.revision != null) { + // only match on revision label, if a revision is set, ensure backwards compatibility + labels[REVISION_LABEL] = shinyProxyInstance.revision.toString() + } + return labels } fun labelsForShinyProxy(shinyProxy: ShinyProxy): Map { diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyController.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyController.kt index b8160b1..9696d10 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyController.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/controller/ShinyProxyController.kt @@ -135,7 +135,7 @@ class ShinyProxyController(private val channel: Channel, val revision = if (existingInstance != null) { logger.info { "${shinyProxy.logPrefix(existingInstance)} Trying to create new instance which already exists and is not the latest instance. Therefore this instance will become the latest again" } // reconcile will take care of making this the latest instance again - existingInstance.revision + 1 + (existingInstance.revision ?: 0) + 1 } else { 0 } @@ -286,7 +286,7 @@ class ShinyProxyController(private val channel: Channel, val instancesToCheck = shinyProxy.status.instances.toList() for (shinyProxyInstance in instancesToCheck) { val latestRevision = shinyProxy.status.getInstanceByHash(shinyProxyInstance.hashOfSpec)?.revision ?: 0 - if (shinyProxyInstance.isLatestInstance || (shinyProxyInstance.hashOfSpec == shinyProxy.hashOfCurrentSpec && shinyProxyInstance.revision >= latestRevision)) { + if (shinyProxyInstance.isLatestInstance || (shinyProxyInstance.hashOfSpec == shinyProxy.hashOfCurrentSpec && shinyProxyInstance.revision != null && shinyProxyInstance.revision >= latestRevision)) { // shinyProxyInstance is either the latest or the soon to be latest instance continue } @@ -321,9 +321,6 @@ class ShinyProxyController(private val channel: Channel, delay(30_000) logger.info { "${shinyProxy.logPrefix(shinyProxyInstance)} DeleteSingleShinyProxyInstance [Step 3/3]: Delete resources" } - for (service in resourceRetriever.getServiceByLabels(LabelFactory.labelsForShinyProxyInstance(shinyProxy, shinyProxyInstance), shinyProxy.metadata.namespace)) { - kubernetesClient.resource(service).delete() - } for (replicaSet in resourceRetriever.getReplicaSetByLabels(LabelFactory.labelsForShinyProxyInstance(shinyProxy, shinyProxyInstance), shinyProxy.metadata.namespace)) { kubernetesClient.resource(replicaSet).delete() } diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyInstance.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyInstance.kt index 3d41841..a34e89f 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyInstance.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyInstance.kt @@ -20,4 +20,4 @@ */ package eu.openanalytics.shinyproxyoperator.crd -data class ShinyProxyInstance(val hashOfSpec: String, var isLatestInstance: Boolean, val revision: Int = 0) +data class ShinyProxyInstance(val hashOfSpec: String, var isLatestInstance: Boolean, val revision: Int? = null) diff --git a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyStatus.kt b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyStatus.kt index e2cce22..27f22b5 100644 --- a/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyStatus.kt +++ b/src/main/kotlin/eu/openanalytics/shinyproxyoperator/crd/ShinyProxyStatus.kt @@ -29,7 +29,7 @@ import io.fabric8.kubernetes.api.model.KubernetesResource data class ShinyProxyStatus(val instances: ArrayList = arrayListOf()) : KubernetesResource { fun getInstanceByHash(hash: String): ShinyProxyInstance? { - return instances.filter { it.hashOfSpec == hash }.maxByOrNull { it.revision } + return instances.filter { it.hashOfSpec == hash }.maxByOrNull { it.revision ?: 0 } } fun latestInstance(): ShinyProxyInstance? { From a4566ba3490ba423d0b1bb940a86dfc2a2f9c795 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Tue, 7 May 2024 11:30:32 +0200 Subject: [PATCH 31/34] Update docs for v2.1.0 --- README.md | 75 +++++---- docs/deployment/README.md | 151 ++++++++++++------ .../overlays/1-namespaced/kustomization.yaml | 6 +- .../resources/shinyproxy.shinyproxy.yaml | 2 +- .../2-clustered/redis/kustomization.yaml | 2 +- .../shinyproxy-dept2/kustomization.yaml | 2 +- .../resources/shinyproxy.shinyproxy.yaml | 2 +- .../shinyproxy-operator/kustomization.yaml | 2 +- .../2-clustered/shinyproxy/kustomization.yaml | 2 +- .../resources/shinyproxy.shinyproxy.yaml | 2 +- .../shinyproxy/kustomization.yaml | 6 +- .../resources/shinyproxy.shinyproxy.yaml | 2 +- .../4-namespaced-multi/kustomization.yaml | 6 +- .../resources/shinyproxy1.shinyproxy.yaml | 2 +- .../resources/shinyproxy2.shinyproxy.yaml | 2 +- .../resources/shinyproxy3.shinyproxy.yaml | 2 +- 16 files changed, 166 insertions(+), 100 deletions(-) diff --git a/README.md b/README.md index 01a1861..d76ff2e 100644 --- a/README.md +++ b/README.md @@ -2,19 +2,19 @@ Easily run ShinyProxy on a Kubernetes cluster -**(c) Copyright Open Analytics NV, 2020-2023 - Apache License 2.0** +**(c) Copyright Open Analytics NV, 2020-2024 - Apache License 2.0** ## Why? Deploying and managing ShinyProxy can get complex when many apps are used, especially when the configuration of ShinyProxy is often updated. -When restarting a running ShinyProxy instance (in order to update its configuration), -users will face a disconnect from their running applications. The only solution -to guarantee that users do not lose their connection to running apps, is to keep -the current instance alive when updating ShinyProxy's configuration. However, -manually keeping track of these instances would be too cumbersome and should -therefore be automated. +When restarting a running ShinyProxy instance (in order to update its +configuration), users will face a disconnect from their running applications. +The only solution to guarantee that users do not lose their connection to +running apps, is to keep the current instance alive when updating ShinyProxy's +configuration. However, manually keeping track of these instances would be too +cumbersome and should therefore be automated. The ShinyProxy operator for Kubernetes is able to manage multiple ShinyProxy instances and their configuration. @@ -88,13 +88,13 @@ start with the `SPO` prefix, meaning **S**hiny**P**roxy**O**perator. probe. By default, this is 60 seconds. - `SPO_LOG_LEVEL`: configures the log level of the operator, may be one of the following: - - `OFF`: disables logging - - `ERROR` - - `WARN` - - `INFO` - - `DEBUG`: default (may change) - - `TRACE` - - `ALL`: enables all logging + - `OFF`: disables logging + - `ERROR` + - `WARN` + - `INFO` + - `DEBUG`: default (may change) + - `TRACE` + - `ALL`: enables all logging Note: in our deployments where startup probes aren't supported we have success with the following configuration: @@ -113,20 +113,23 @@ ShinyProxy and the operator for the best experience. | ShinyProxy Version | Minimum operator version | Maximum operator version (inclusive) | |--------------------|----------------------------------|--------------------------------------| -| 3.0.0 | 2.0.0 | TBD | +| 3.1.0 | 2.1.0 | TBD | +| 3.0.0 | 2.0.0 | TBD (works with 2.1.0) | | 2.6.0 | 1.0.0 | 1.1.0 | | 2.5.0 | `0.0.1-SNAPSHOT-20210302.095930` | `0.0.1-SNAPSHOT-20210607.070151` | | 2.4.3 | `0.0.1-SNAPSHOT-20201215.112635` | `0.0.1-SNAPSHOT-20201215.112635` | ## Kubernetes versions -| | k8s 1.25.x | k8s 1.24.x | k8s 1.23.x | k8s 1.22.x | k8s >= v1.21.3 | k8s <= v1.21.2 | k8s >= 1.20.10 | k8s <= v1.20.9 | v1.19 | <= v1.18 | -|-------|------------|------------|-------------|------------|----------------|----------------|----------------|----------------|-------|----------| -| 2.0.0 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓¹ | ✓ | ✓¹ | ✓ | - | +| | k8s 1.29.x | k8s 1.28.x | k8s 1.27.x | k8s 1.26.x | k8s 1.25.x | k8s 1.24.x | k8s 1.23.x | k8s 1.22.x | k8s >= v1.21.3 | k8s <= v1.21.2 | k8s >= 1.20.10 | k8s <= v1.20.9 | v1.19 | <= v1.18 | +|-------|------------|------------|------------|------------|------------|------------|------------|------------|----------------|----------------|----------------|----------------|-------|----------| +| 2.1.0 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓¹ | ✓ | ✓¹ | ✓ | - | +| 2.0.0 | ✓² | ✓² | ✓² | ✓² | ✓² | ✓² | ✓ | ✓ | ✓ | ✓¹ | ✓ | ✓¹ | ✓ | - | **Note:** - ¹ requires a workaround, see below. +- ² version 2.0.0 supports these Kubernetes versions, but might stop syncing after some time, this issue is solved in version 2.1.0 ### Workaround for bug in Kubernetes @@ -153,18 +156,18 @@ Be aware of these changes when updating to version 2.0.0: started, all new requests will be handled by the new server, including requests for existing apps. Only existing websocket connections will stay open on the old servers. This has multiple benefits: - - when a new server is started, users will immediately use and see the - configuration of that new server. In other words, if a new configuration - includes a new app, this app is immediately available to all users (even if - they are using apps started on older servers) - - there is no longer a process of transferring users to new servers. Both the - forced method and the manual method (where users have to click a button) are - removed. Users will immediately use the new configuration. - - apps can be run for a (very) long time, even if frequently updating the - configuration and without having many old servers. Old servers are removed - as soon as no websocket connections are running on that server. - - Skipper is no longer a dependency of the operator. There is no benefit in - using with version two of the operator. + - when a new server is started, users will immediately use and see the + configuration of that new server. In other words, if a new configuration + includes a new app, this app is immediately available to all users (even + if they are using apps started on older servers) + - there is no longer a process of transferring users to new servers. Both + the forced method and the manual method (where users have to click a + button) are removed. Users will immediately use the new configuration. + - apps can be run for a (very) long time, even if frequently updating the + configuration and without having many old servers. Old servers are removed + as soon as no websocket connections are running on that server. + - Skipper is no longer a dependency of the operator. There is no benefit in + using with version two of the operator. - the operator now requires ShinyProxy to store the active proxies in Redis. Therefore, since this release Redis takes a more critical role. When running Redis inside Kubernetes, it is therefore best practice to use Redis Sentinel. @@ -182,8 +185,16 @@ need to be made to the ShinyProxy configuration file: - optionally add the property [`kubernetesIngressPatches`](docs/deployment#modify-the-ingress-resource) in order to customize the ingress created by the operator. -- update the ShinyProxy image to `openanalytics/shinyproxy:3.0.1` +- update the ShinyProxy image to `openanalytics/shinyproxy:3.1.0` + +### Update to 2.1.0 + +The [ShinyProxy CRD](docs/deployment/bases/namespaced/crds/shinyproxy.crd.yaml) +has been updated in version 2.1.0, it is important to update the CRD in your +cluster. Running the [deployment commands](docs/deployment/) is enough. The CRD +can be updated while ShinyProxy and the ShinyProxy operator are running in the +cluster. ## Java Version -This project requires JDK 11. +This project requires JDK 17. diff --git a/docs/deployment/README.md b/docs/deployment/README.md index 955dd56..94adca3 100644 --- a/docs/deployment/README.md +++ b/docs/deployment/README.md @@ -13,9 +13,9 @@ and dependencies of the operator. servers. - **ShinyProxy**: the ShinyProxy servers, these host the Shiny apps. You do not need to create these servers manually, since these are created by the - operator. Instead, you define which servers to create, and the operator creates - all necessary Kubernetes resources, without affecting any existing server or - causing downtime. + operator. Instead, you define which servers to create, and the operator + creates all necessary Kubernetes resources, without affecting any existing + server or causing downtime. - **Redis**: Redis is used by ShinyProxy (not by the operator) to implement [session and app persistence](https://shinyproxy.io/documentation/configuration/#session-and-app-persistence). This ensures that when a ShinyProxy server is replaced, the user is still @@ -26,8 +26,8 @@ and dependencies of the operator. server provided by cloud providers. **Note:** when deploying to production, it is important to change the password - used to secure Redis. Each example (see below) already changes the - password to `mySecurePassword12`. For an example see + used to secure Redis. Each example (see below) already changes the password + to `mySecurePassword12`. For an example see the [`overlays/1-namespaced/patches/redis.secret.yaml`](overlays/1-namespaced/patches/redis.secret.yaml) file. @@ -82,7 +82,8 @@ ShinyProxy operator on minikube. minikube ip ``` - Next, add the following entries to `/etc/hosts`, replacing `MINIKUBE_IP` by the output of the previous command; + Next, add the following entries to `/etc/hosts`, replacing `MINIKUBE_IP` by + the output of the previous command; ```text MINIKUBE_IP shinyproxy-demo.local @@ -114,22 +115,23 @@ ShinyProxy operator on minikube. title: ShinyProxy 2 # <- MAKE THE CHANGE HERE # ... replicas: 2 # <- ADD THIS LINE - image: openanalytics/shinyproxy:3.0.1 + image: openanalytics/shinyproxy:3.1.0 imagePullPolicy: Always fqdn: shinyproxy-demo.local ``` 10. Apply this change using `kubectl`: - ```bash - kubectl apply -f resources/shinyproxy.shinyproxy.yaml - ``` +```bash +kubectl apply -f resources/shinyproxy.shinyproxy.yaml +``` + +The operator now deploys a new ShinyProxy instance. The old instance will be +kept intact as long as a Websocket connection is active on the old instance. The +old instance will automatically be removed once it no longer has any open +Websocket connections. New requests will immediately be handled by the new +server as soon as it is ready. Try going to the main page of ShinyProxy and +check whether the change your made has been applied. - The operator now deploys a new ShinyProxy instance. The old instance will be - kept intact as long as a Websocket connection is active on the old instance. - The old instance will automatically be removed once it no longer has any open - Websocket connections. New requests will immediately be handled by the new - server as soon as it is ready. Try going to the main page of ShinyProxy and - check whether the change your made has been applied. 11. Try the other examples. The following commands first remove the current example, next you can open another example (e.g. `2-clustered`) and deploy it using `kubectl`: @@ -150,37 +152,37 @@ The Operator is designed to be flexible and fit many type of deployments. This repository includes examples for many kinds of deployments: - *1-namespaced*: - - Operator-mode: `namespaced` - - Operator-namespace: `shinyproxy` - - Redis-namespace: `shinyproxy` - - ShinyProxy-namespace: `shinyproxy` - - URLs: `https://shinyproxy-demo.local` + - Operator-mode: `namespaced` + - Operator-namespace: `shinyproxy` + - Redis-namespace: `shinyproxy` + - ShinyProxy-namespace: `shinyproxy` + - URLs: `https://shinyproxy-demo.local` This is a very simple deployment of the operator, where everything runs in the same namespace. - *2-clustered*: - - Operator-mode: `clustered` - - Operator-namespace: `shinyproxy-operator` - - Redis-namespace: `redis` - - ShinyProxy-namespace: `shinyproxy` and `shinyproxy-dept2` - - URLs: - - `https://shinyproxy-demo.local` - - `https://shinyproxy-demo2.local` + - Operator-mode: `clustered` + - Operator-namespace: `shinyproxy-operator` + - Redis-namespace: `redis` + - ShinyProxy-namespace: `shinyproxy` and `shinyproxy-dept2` + - URLs: + - `https://shinyproxy-demo.local` + - `https://shinyproxy-demo2.local` In this example, the operator runs in `clustered` mode. Therefore, the operator will look into all namespaces for `ShinyProxy` resources and deploy - these resources in their respective namespace. This example also demonstrates how - the Operator can be used in a multi-tenancy or multi-realm way. Each + these resources in their respective namespace. This example also demonstrates + how the Operator can be used in a multi-tenancy or multi-realm way. Each ShinyProxy server runs in its own namespace, isolated from the other servers. However, they are managed by a single operator. - *3-namespaced-app-ns*: - - Operator-mode: `namespaced` - - Operator-namespace: `shinyproxy` - - Redis-namespace: `shinyproxy` - - ShinyProxy-namespace: `shinyproxy` - - URLs: `https://shinyproxy-demo.local` + - Operator-mode: `namespaced` + - Operator-namespace: `shinyproxy` + - Redis-namespace: `shinyproxy` + - ShinyProxy-namespace: `shinyproxy` + - URLs: `https://shinyproxy-demo.local` Similar to example 1, however, the `01_hello` app will now run in the `my-namespace` namespace instead of the `shinyproxy` namespace. In addition to @@ -189,14 +191,14 @@ repository includes examples for many kinds of deployments: the `ServiceAccount` of the ShinyProxy server. - *4-namespaced-multi*: - - Operator-mode: `namespaced` - - Operator-namespace: `shinyproxy` - - Redis-namespace: `shinyproxy` - - ShinyProxy-namespace: `shinyproxy` - - URLs: - - `https://shinyproxy-demo.local/shinyproxy1/` - - `https://shinyproxy-demo.local/shinyproxy2/` - - `https://shinyproxy-demo.local/shinyproxy3/` + - Operator-mode: `namespaced` + - Operator-namespace: `shinyproxy` + - Redis-namespace: `shinyproxy` + - ShinyProxy-namespace: `shinyproxy` + - URLs: + - `https://shinyproxy-demo.local/shinyproxy1/` + - `https://shinyproxy-demo.local/shinyproxy2/` + - `https://shinyproxy-demo.local/shinyproxy3/` Based on the second example, this example shows how multi-tenancy can be achieved using sub-paths instead of multiple domain names. Each ShinyProxy @@ -218,16 +220,24 @@ important: the [example](#modify-the-shinyproxy-pod)) - `kubernetesIngressPatches`: allows to patch the `Ingress` resources created by the operator (see the [example](#modify-the-ingress-resource)) +- `kubernetesServicePatches`: allows to patch the `Service` resources created by + the operator (see the [example](#modify-the-service-resource)) - `image`: the docker image to use for the ShinyProxy server ( - e.g. `openanalytics/shinyproxy:3.0.1`) + e.g. `openanalytics/shinyproxy:3.1.0`) - `imagePullPolicy`: the pull policy for ShinyProxy Image; the default value is `IfNotPresent`; valid options are `Never`, `IfNotPresent` and `Always`. - `fqdn`: the FQDN at which the service should be available, e.g. ` shinyproxy-demo.local +- `additionalFqdns`: (optional) a list of additional FQDNs that can be used to + access this ShinyProxy server - `appNamespaces`: a list of namespaces in which apps will be deployed. This is only needed when you change the namespace of an app using the `kubernetes-pod-patches` feature. The namespace of the operator and ShinyProxy instance are automatically included +- `antiAffinityTopologyKey`: the topology key to use in + the [anti-affinity](#anti-affinity) configuration of the ShinyProxy pods +- `antiAffinityRequired`: if enabled, the [anti-affinity](#anti-affinity) + configuration rules are `required` instead of `preferred` ## Modify the Ingress Resource @@ -248,7 +258,7 @@ metadata: namespace: shinyproxy spec: proxy: - # ... + # ... kubernetesIngressPatches: | - op: add path: /metadata/annotations @@ -265,7 +275,7 @@ spec: - hosts: - shinyproxy-demo.local # secretName: example # uncomment and change this line if needed - image: openanalytics/shinyproxy:3.0.1 + image: openanalytics/shinyproxy:3.1.0 imagePullPolicy: Always fqdn: shinyproxy-demo.local ``` @@ -304,7 +314,7 @@ metadata: namespace: shinyproxy spec: proxy: - # ... + # ... kubernetesPodTemplateSpecPatches: | - op: add path: /spec/containers/0/env/- @@ -326,7 +336,7 @@ spec: - op: add path: /spec/serviceAccountName value: shinyproxy-sa - image: openanalytics/shinyproxy:3.0.1 + image: openanalytics/shinyproxy:3.1.0 imagePullPolicy: Always fqdn: shinyproxy-demo.local ``` @@ -397,3 +407,48 @@ and `/spec/containers/0/volumeMounts` arrays of the pod. The ShinyProxy Operator automatically creates a mount for a configmap which contains the ShinyProxy configuration. By overriding these mounts, this configmap is not be mounted and the default (demo) configuration of ShinyProxy is loaded. + +## Modify the Service Resource + +The ShinyProxy Operator automatically creates a Service resource for each +ShinyProxy resource you create. The created Service resource contains everything +that is needed for a working ShinyProxy deployment. However, in some cases it is +required to modify the resource. This can be achieved using +the `kubernetesServicePatches` field. This field should contain a string which +contains a list of [JSON Patches](https://jsonpatch.com/) to apply to the +Service resource. For example: + +```yaml +apiVersion: openanalytics.eu/v1 +kind: ShinyProxy +metadata: + name: shinyproxy + namespace: shinyproxy +spec: + proxy: + # ... + kubernetesServicePatches: | + - op: add + path: /metadata/annotations + value: + my-annotation: my-value + image: openanalytics/shinyproxy:3.1.0 + imagePullPolicy: Always + fqdn: shinyproxy-demo.local +``` + +This example patch adds the annotation `my-annotation: my-value` to the Service +resource created by the operator. + +## Anti-affinity + +Starting with version 2.1.0, the operator automatically +adds [anti-affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/) +rules, such that Kubernetes will try to not schedule multiple ShinyProxy +replicas on the same Kubernetes node. Note that this only has effect when +running multiple replicas of ShinyProxy. If Kubernetes is unable to satisfy the +requirement, it will still schedule multiple replicas on the same node. This +behavior can be changed by setting `antiAffinityRequired` to `true` in your +ShinyProxy configuration. It is also possible to change the topology, by setting +the `antiAffinityTopologyKey`, e.g. to not run multiple replicas in the same +availability zone you can set this property to `topology.kubernetes.io/zone `. diff --git a/docs/deployment/overlays/1-namespaced/kustomization.yaml b/docs/deployment/overlays/1-namespaced/kustomization.yaml index 2d867d2..561578a 100644 --- a/docs/deployment/overlays/1-namespaced/kustomization.yaml +++ b/docs/deployment/overlays/1-namespaced/kustomization.yaml @@ -3,9 +3,9 @@ kind: Kustomization namespace: shinyproxy resources: - - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/redis-sentinel?ref=v2.0.0 - - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/namespaced?ref=v2.0.0 - - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/shinyproxy?ref=v2.0.0 + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/redis-sentinel?ref=v2.1.0 + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/namespaced?ref=v2.1.0 + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/shinyproxy?ref=v2.1.0 - resources/shinyproxy.namespace.yaml - resources/shinyproxy.shinyproxy.yaml diff --git a/docs/deployment/overlays/1-namespaced/resources/shinyproxy.shinyproxy.yaml b/docs/deployment/overlays/1-namespaced/resources/shinyproxy.shinyproxy.yaml index 6c101cf..3b67397 100644 --- a/docs/deployment/overlays/1-namespaced/resources/shinyproxy.shinyproxy.yaml +++ b/docs/deployment/overlays/1-namespaced/resources/shinyproxy.shinyproxy.yaml @@ -94,6 +94,6 @@ spec: - hosts: - shinyproxy-demo.local # secretName: example # uncomment and change this line if needed - image: openanalytics/shinyproxy:3.0.1 + image: openanalytics/shinyproxy:3.1.0 imagePullPolicy: Always fqdn: shinyproxy-demo.local diff --git a/docs/deployment/overlays/2-clustered/redis/kustomization.yaml b/docs/deployment/overlays/2-clustered/redis/kustomization.yaml index 21fdab3..dd26944 100644 --- a/docs/deployment/overlays/2-clustered/redis/kustomization.yaml +++ b/docs/deployment/overlays/2-clustered/redis/kustomization.yaml @@ -3,7 +3,7 @@ kind: Kustomization namespace: redis resources: - - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/redis-sentinel?ref=v2.0.0 + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/redis-sentinel?ref=v2.1.0 - resources/redis.namespace.yaml patches: diff --git a/docs/deployment/overlays/2-clustered/shinyproxy-dept2/kustomization.yaml b/docs/deployment/overlays/2-clustered/shinyproxy-dept2/kustomization.yaml index 8dec7e3..51930c3 100644 --- a/docs/deployment/overlays/2-clustered/shinyproxy-dept2/kustomization.yaml +++ b/docs/deployment/overlays/2-clustered/shinyproxy-dept2/kustomization.yaml @@ -3,7 +3,7 @@ kind: Kustomization namespace: shinyproxy-dept2 resources: - - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/shinyproxy?ref=v2.0.0 + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/shinyproxy?ref=v2.1.0 - resources/redis.secret.yaml - resources/shinyproxy.shinyproxy.yaml - resources/shinyproxy.namespace.yaml diff --git a/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/shinyproxy.shinyproxy.yaml b/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/shinyproxy.shinyproxy.yaml index 9575a9b..333db03 100644 --- a/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/shinyproxy.shinyproxy.yaml +++ b/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/shinyproxy.shinyproxy.yaml @@ -93,6 +93,6 @@ spec: - hosts: - shinyproxy-demo2.local # secretName: example # uncomment and change this line if needed - image: openanalytics/shinyproxy:3.0.1 + image: openanalytics/shinyproxy:3.1.0 imagePullPolicy: Always fqdn: shinyproxy-demo2.local diff --git a/docs/deployment/overlays/2-clustered/shinyproxy-operator/kustomization.yaml b/docs/deployment/overlays/2-clustered/shinyproxy-operator/kustomization.yaml index c2f6122..91c1cf9 100644 --- a/docs/deployment/overlays/2-clustered/shinyproxy-operator/kustomization.yaml +++ b/docs/deployment/overlays/2-clustered/shinyproxy-operator/kustomization.yaml @@ -3,5 +3,5 @@ kind: Kustomization namespace: shinyproxy-operator resources: - - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/clustered?ref=v2.0.0 + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/clustered?ref=v2.1.0 - resources/shinyproxy-operator.namespace.yaml diff --git a/docs/deployment/overlays/2-clustered/shinyproxy/kustomization.yaml b/docs/deployment/overlays/2-clustered/shinyproxy/kustomization.yaml index 6d426d7..932757a 100644 --- a/docs/deployment/overlays/2-clustered/shinyproxy/kustomization.yaml +++ b/docs/deployment/overlays/2-clustered/shinyproxy/kustomization.yaml @@ -3,7 +3,7 @@ kind: Kustomization namespace: shinyproxy resources: - - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/shinyproxy?ref=v2.0.0 + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/shinyproxy?ref=v2.1.0 - resources/redis.secret.yaml - resources/shinyproxy.shinyproxy.yaml - resources/shinyproxy.namespace.yaml diff --git a/docs/deployment/overlays/2-clustered/shinyproxy/resources/shinyproxy.shinyproxy.yaml b/docs/deployment/overlays/2-clustered/shinyproxy/resources/shinyproxy.shinyproxy.yaml index fbcb126..62c60d3 100644 --- a/docs/deployment/overlays/2-clustered/shinyproxy/resources/shinyproxy.shinyproxy.yaml +++ b/docs/deployment/overlays/2-clustered/shinyproxy/resources/shinyproxy.shinyproxy.yaml @@ -99,6 +99,6 @@ spec: - hosts: - shinyproxy-demo.local # secretName: example # uncomment and change this line if needed - image: openanalytics/shinyproxy:3.0.1 + image: openanalytics/shinyproxy:3.1.0 imagePullPolicy: Always fqdn: shinyproxy-demo.local diff --git a/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/kustomization.yaml b/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/kustomization.yaml index 2d867d2..561578a 100644 --- a/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/kustomization.yaml +++ b/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/kustomization.yaml @@ -3,9 +3,9 @@ kind: Kustomization namespace: shinyproxy resources: - - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/redis-sentinel?ref=v2.0.0 - - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/namespaced?ref=v2.0.0 - - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/shinyproxy?ref=v2.0.0 + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/redis-sentinel?ref=v2.1.0 + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/namespaced?ref=v2.1.0 + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/shinyproxy?ref=v2.1.0 - resources/shinyproxy.namespace.yaml - resources/shinyproxy.shinyproxy.yaml diff --git a/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/resources/shinyproxy.shinyproxy.yaml b/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/resources/shinyproxy.shinyproxy.yaml index 3c79e75..5a36bd5 100644 --- a/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/resources/shinyproxy.shinyproxy.yaml +++ b/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/resources/shinyproxy.shinyproxy.yaml @@ -98,7 +98,7 @@ spec: - hosts: - shinyproxy-demo.local # secretName: example # uncomment and change this line if needed - image: openanalytics/shinyproxy:3.0.1 + image: openanalytics/shinyproxy:3.1.0 imagePullPolicy: Always fqdn: shinyproxy-demo.local appNamespaces: diff --git a/docs/deployment/overlays/4-namespaced-multi/kustomization.yaml b/docs/deployment/overlays/4-namespaced-multi/kustomization.yaml index 9154db6..405f678 100644 --- a/docs/deployment/overlays/4-namespaced-multi/kustomization.yaml +++ b/docs/deployment/overlays/4-namespaced-multi/kustomization.yaml @@ -3,9 +3,9 @@ kind: Kustomization namespace: shinyproxy resources: - - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/redis-sentinel?ref=v2.0.0 - - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/namespaced?ref=v2.0.0 - - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/shinyproxy?ref=v2.0.0 + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/redis-sentinel?ref=v2.1.0 + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/namespaced?ref=v2.1.0 + - github.com/openanalytics/shinyproxy-operator/docs/deployment/bases/shinyproxy?ref=v2.1.0 - resources/shinyproxy.namespace.yaml - resources/shinyproxy1.shinyproxy.yaml - resources/shinyproxy2.shinyproxy.yaml diff --git a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy1.shinyproxy.yaml b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy1.shinyproxy.yaml index add98b3..72e513b 100644 --- a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy1.shinyproxy.yaml +++ b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy1.shinyproxy.yaml @@ -96,6 +96,6 @@ spec: - hosts: - shinyproxy-demo.local # secretName: example # uncomment and change this line if needed - image: openanalytics/shinyproxy:3.0.1 + image: openanalytics/shinyproxy:3.1.0 imagePullPolicy: Always fqdn: shinyproxy-demo.local diff --git a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy2.shinyproxy.yaml b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy2.shinyproxy.yaml index ca77a10..659692a 100644 --- a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy2.shinyproxy.yaml +++ b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy2.shinyproxy.yaml @@ -97,6 +97,6 @@ spec: - hosts: - shinyproxy-demo.local # secretName: example # uncomment and change this line if needed - image: openanalytics/shinyproxy:3.0.1 + image: openanalytics/shinyproxy:3.1.0 imagePullPolicy: Always fqdn: shinyproxy-demo.local diff --git a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy3.shinyproxy.yaml b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy3.shinyproxy.yaml index 82c9f01..c9489f1 100644 --- a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy3.shinyproxy.yaml +++ b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy3.shinyproxy.yaml @@ -97,6 +97,6 @@ spec: - hosts: - shinyproxy-demo.local # secretName: example # uncomment and change this line if needed - image: openanalytics/shinyproxy:3.0.1 + image: openanalytics/shinyproxy:3.1.0 imagePullPolicy: Always fqdn: shinyproxy-demo.local From 912b0022154d91288b1a9225e7ebd9e560e2b29a Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Tue, 7 May 2024 11:33:56 +0200 Subject: [PATCH 32/34] Bump version number --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3257867..1dbe99d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ eu.openanalytics shinyproxy-operator - 2.1.0-SNAPSHOT + 2.1.0 Open Analytics NV From f19e5163887e5b43e6abebb6947566bf141ea422 Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Tue, 7 May 2024 12:11:44 +0200 Subject: [PATCH 33/34] Fix examples --- .../resources/shinyproxy.shinyproxy.yaml | 11 ++++++----- .../resources/shinyproxy.shinyproxy.yaml | 15 ++++++++------- .../resources/shinyproxy.shinyproxy.yaml | 15 ++++++++------- .../resources/shinyproxy.shinyproxy.yaml | 15 ++++++++------- .../resources/shinyproxy1.shinyproxy.yaml | 15 ++++++++------- .../resources/shinyproxy2.shinyproxy.yaml | 17 +++++++++-------- .../resources/shinyproxy3.shinyproxy.yaml | 17 +++++++++-------- 7 files changed, 56 insertions(+), 49 deletions(-) diff --git a/docs/deployment/overlays/1-namespaced/resources/shinyproxy.shinyproxy.yaml b/docs/deployment/overlays/1-namespaced/resources/shinyproxy.shinyproxy.yaml index 3b67397..6d3687a 100644 --- a/docs/deployment/overlays/1-namespaced/resources/shinyproxy.shinyproxy.yaml +++ b/docs/deployment/overlays/1-namespaced/resources/shinyproxy.shinyproxy.yaml @@ -11,12 +11,13 @@ spec: spring: session: store-type: redis - redis: - password: ${REDIS_PASSWORD} - sentinel: - master: shinyproxy + data: + redis: password: ${REDIS_PASSWORD} - nodes: redis-node-0.redis-headless:26379, redis-node-1.redis-headless:26379, redis-node-2.redis-headless:26379 + sentinel: + master: shinyproxy + password: ${REDIS_PASSWORD} + nodes: redis-node-0.redis-headless:26379, redis-node-1.redis-headless:26379, redis-node-2.redis-headless:26379 proxy: store-mode: Redis stop-proxies-on-shutdown: false diff --git a/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/shinyproxy.shinyproxy.yaml b/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/shinyproxy.shinyproxy.yaml index 333db03..01ed78b 100644 --- a/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/shinyproxy.shinyproxy.yaml +++ b/docs/deployment/overlays/2-clustered/shinyproxy-dept2/resources/shinyproxy.shinyproxy.yaml @@ -10,12 +10,13 @@ spec: spring: session: store-type: redis - redis: - password: ${REDIS_PASSWORD} - sentinel: - master: shinyproxy + data: + redis: password: ${REDIS_PASSWORD} - nodes: redis-node-0.redis-headless.redis:26379, redis-node-1.redis-headless.redis:26379, redis-node-2.redis-headless.redis:26379 + sentinel: + master: shinyproxy + password: ${REDIS_PASSWORD} + nodes: redis-node-0.redis-headless.redis:26379, redis-node-1.redis-headless.redis:26379, redis-node-2.redis-headless.redis:26379 proxy: store-mode: Redis stop-proxies-on-shutdown: false @@ -42,11 +43,11 @@ spec: display-name: Hello Application description: Application which demonstrates the basics of a Shiny app container-cmd: ["R", "-e", "shinyproxy::run_01_hello()"] - container-image: openanalytics/shinyproxy-integration-test-app + container-image: openanalytics/shinyproxy-demo access-groups: [scientists, mathematicians] - id: 06_tabsets container-cmd: ["R", "-e", "shinyproxy::run_06_tabsets()"] - container-image: openanalytics/shinyproxy-integration-test-app + container-image: openanalytics/shinyproxy-demo access-groups: scientists - id: rstudio displayName: RStudio diff --git a/docs/deployment/overlays/2-clustered/shinyproxy/resources/shinyproxy.shinyproxy.yaml b/docs/deployment/overlays/2-clustered/shinyproxy/resources/shinyproxy.shinyproxy.yaml index 62c60d3..db0dd12 100644 --- a/docs/deployment/overlays/2-clustered/shinyproxy/resources/shinyproxy.shinyproxy.yaml +++ b/docs/deployment/overlays/2-clustered/shinyproxy/resources/shinyproxy.shinyproxy.yaml @@ -10,12 +10,13 @@ spec: spring: session: store-type: redis - redis: - password: ${REDIS_PASSWORD} - sentinel: - master: shinyproxy + data: + redis: password: ${REDIS_PASSWORD} - nodes: redis-node-0.redis-headless.redis:26379, redis-node-1.redis-headless.redis:26379, redis-node-2.redis-headless.redis:26379 + sentinel: + master: shinyproxy + password: ${REDIS_PASSWORD} + nodes: redis-node-0.redis-headless.redis:26379, redis-node-1.redis-headless.redis:26379, redis-node-2.redis-headless.redis:26379 proxy: store-mode: Redis stop-proxies-on-shutdown: false @@ -42,11 +43,11 @@ spec: display-name: Hello Application description: Application which demonstrates the basics of a Shiny app container-cmd: ["R", "-e", "shinyproxy::run_01_hello()"] - container-image: openanalytics/shinyproxy-integration-test-app + container-image: openanalytics/shinyproxy-demo access-groups: [scientists, mathematicians] - id: 06_tabsets container-cmd: ["R", "-e", "shinyproxy::run_06_tabsets()"] - container-image: openanalytics/shinyproxy-integration-test-app + container-image: openanalytics/shinyproxy-demo access-groups: scientists - id: rstudio displayName: RStudio diff --git a/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/resources/shinyproxy.shinyproxy.yaml b/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/resources/shinyproxy.shinyproxy.yaml index 5a36bd5..4f5d266 100644 --- a/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/resources/shinyproxy.shinyproxy.yaml +++ b/docs/deployment/overlays/3-namespaced-app-ns/shinyproxy/resources/shinyproxy.shinyproxy.yaml @@ -11,12 +11,13 @@ spec: spring: session: store-type: redis - redis: - password: ${REDIS_PASSWORD} - sentinel: - master: shinyproxy + data: + redis: password: ${REDIS_PASSWORD} - nodes: redis-node-0.redis-headless:26379, redis-node-1.redis-headless:26379, redis-node-2.redis-headless:26379 + sentinel: + master: shinyproxy + password: ${REDIS_PASSWORD} + nodes: redis-node-0.redis-headless:26379, redis-node-1.redis-headless:26379, redis-node-2.redis-headless:26379 proxy: store-mode: Redis stop-proxies-on-shutdown: false @@ -43,7 +44,7 @@ spec: display-name: Hello Application description: Application which demonstrates the basics of a Shiny app container-cmd: [ "R", "-e", "shinyproxy::run_01_hello()" ] - container-image: openanalytics/shinyproxy-integration-test-app + container-image: openanalytics/shinyproxy-demo access-groups: [ scientists, mathematicians ] kubernetes-pod-patches: | - op: replace @@ -51,7 +52,7 @@ spec: value: my-namespace - id: 06_tabsets container-cmd: ["R", "-e", "shinyproxy::run_06_tabsets()"] - container-image: openanalytics/shinyproxy-integration-test-app + container-image: openanalytics/shinyproxy-demo access-groups: scientists - id: rstudio displayName: RStudio diff --git a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy1.shinyproxy.yaml b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy1.shinyproxy.yaml index 72e513b..4cae4e5 100644 --- a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy1.shinyproxy.yaml +++ b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy1.shinyproxy.yaml @@ -13,12 +13,13 @@ spec: spring: session: store-type: redis - redis: - password: ${REDIS_PASSWORD} - sentinel: - master: shinyproxy + data: + redis: password: ${REDIS_PASSWORD} - nodes: redis-node-0.redis-headless:26379, redis-node-1.redis-headless:26379, redis-node-2.redis-headless:26379 + sentinel: + master: shinyproxy + password: ${REDIS_PASSWORD} + nodes: redis-node-0.redis-headless:26379, redis-node-1.redis-headless:26379, redis-node-2.redis-headless:26379 proxy: store-mode: Redis stop-proxies-on-shutdown: false @@ -45,11 +46,11 @@ spec: display-name: Hello Application description: Application which demonstrates the basics of a Shiny app container-cmd: ["R", "-e", "shinyproxy::run_01_hello()"] - container-image: openanalytics/shinyproxy-integration-test-app + container-image: openanalytics/shinyproxy-demo access-groups: [scientists, mathematicians] - id: 06_tabsets container-cmd: ["R", "-e", "shinyproxy::run_06_tabsets()"] - container-image: openanalytics/shinyproxy-integration-test-app + container-image: openanalytics/shinyproxy-demo access-groups: scientists - id: rstudio displayName: RStudio diff --git a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy2.shinyproxy.yaml b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy2.shinyproxy.yaml index 659692a..4010f9b 100644 --- a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy2.shinyproxy.yaml +++ b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy2.shinyproxy.yaml @@ -13,13 +13,14 @@ spec: spring: session: store-type: redis - redis: - host: redis - password: ${REDIS_PASSWORD} - sentinel: - master: shinyproxy + data: + redis: + host: redis password: ${REDIS_PASSWORD} - nodes: redis-node-0.redis-headless:26379, redis-node-1.redis-headless:26379, redis-node-2.redis-headless:26379 + sentinel: + master: shinyproxy + password: ${REDIS_PASSWORD} + nodes: redis-node-0.redis-headless:26379, redis-node-1.redis-headless:26379, redis-node-2.redis-headless:26379 proxy: store-mode: Redis stop-proxies-on-shutdown: false @@ -46,11 +47,11 @@ spec: display-name: Hello Application description: Application which demonstrates the basics of a Shiny app container-cmd: ["R", "-e", "shinyproxy::run_01_hello()"] - container-image: openanalytics/shinyproxy-integration-test-app + container-image: openanalytics/shinyproxy-demo access-groups: [scientists, mathematicians] - id: 06_tabsets container-cmd: ["R", "-e", "shinyproxy::run_06_tabsets()"] - container-image: openanalytics/shinyproxy-integration-test-app + container-image: openanalytics/shinyproxy-demo access-groups: scientists - id: rstudio displayName: RStudio diff --git a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy3.shinyproxy.yaml b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy3.shinyproxy.yaml index c9489f1..3f6ef7a 100644 --- a/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy3.shinyproxy.yaml +++ b/docs/deployment/overlays/4-namespaced-multi/resources/shinyproxy3.shinyproxy.yaml @@ -13,13 +13,14 @@ spec: spring: session: store-type: redis - redis: - host: redis - password: ${REDIS_PASSWORD} - sentinel: - master: shinyproxy + data: + redis: + host: redis password: ${REDIS_PASSWORD} - nodes: redis-node-0.redis-headless:26379, redis-node-1.redis-headless:26379, redis-node-2.redis-headless:26379 + sentinel: + master: shinyproxy + password: ${REDIS_PASSWORD} + nodes: redis-node-0.redis-headless:26379, redis-node-1.redis-headless:26379, redis-node-2.redis-headless:26379 proxy: store-mode: Redis stop-proxies-on-shutdown: false @@ -46,11 +47,11 @@ spec: display-name: Hello Application description: Application which demonstrates the basics of a Shiny app container-cmd: ["R", "-e", "shinyproxy::run_01_hello()"] - container-image: openanalytics/shinyproxy-integration-test-app + container-image: openanalytics/shinyproxy-demo access-groups: [scientists, mathematicians] - id: 06_tabsets container-cmd: ["R", "-e", "shinyproxy::run_06_tabsets()"] - container-image: openanalytics/shinyproxy-integration-test-app + container-image: openanalytics/shinyproxy-demo access-groups: scientists - id: rstudio displayName: RStudio From bae31f325b96c51546e19d1fc66ee17d8a0117ba Mon Sep 17 00:00:00 2001 From: Tobia De Koninck Date: Tue, 7 May 2024 12:12:26 +0200 Subject: [PATCH 34/34] Fix examples --- .../clustered/resources/shinyproxy-operator.deployment.yaml | 2 +- .../namespaced/resources/shinyproxy-operator.deployment.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/deployment/bases/clustered/resources/shinyproxy-operator.deployment.yaml b/docs/deployment/bases/clustered/resources/shinyproxy-operator.deployment.yaml index 874b4a8..967c497 100644 --- a/docs/deployment/bases/clustered/resources/shinyproxy-operator.deployment.yaml +++ b/docs/deployment/bases/clustered/resources/shinyproxy-operator.deployment.yaml @@ -19,7 +19,7 @@ spec: spec: containers: - name: shinyproxy-operator - image: openanalytics/shinyproxy-operator:2.0.0 + image: openanalytics/shinyproxy-operator:2.1.0 imagePullPolicy: Always env: - name: SPO_MODE diff --git a/docs/deployment/bases/namespaced/resources/shinyproxy-operator.deployment.yaml b/docs/deployment/bases/namespaced/resources/shinyproxy-operator.deployment.yaml index 0e27638..f834e99 100644 --- a/docs/deployment/bases/namespaced/resources/shinyproxy-operator.deployment.yaml +++ b/docs/deployment/bases/namespaced/resources/shinyproxy-operator.deployment.yaml @@ -19,7 +19,7 @@ spec: spec: containers: - name: shinyproxy-operator - image: openanalytics/shinyproxy-operator:2.0.0 + image: openanalytics/shinyproxy-operator:2.1.0 imagePullPolicy: Always env: - name: SPO_MODE