From 57c4ed9c0a7dc3eb4e6cb927f28966a26cab92f3 Mon Sep 17 00:00:00 2001 From: yivan-atl <52448429+bianchi2@users.noreply.github.com> Date: Tue, 1 Oct 2024 13:45:10 +1000 Subject: [PATCH] Create a dedicated Synchrony ingress (#883) * Dedicated Synchrony ingress * Dedicated Synchrony ingress * Fix unit tests --------- Co-authored-by: Yevhen Ivantsov --- src/main/charts/confluence/README.md | 4 ++ .../charts/confluence/templates/_helpers.tpl | 9 ++- .../templates/ingress-synchrony.yaml | 41 ++++++++++++++ .../charts/confluence/templates/ingress.yaml | 2 +- src/main/charts/confluence/values.yaml | 19 +++++++ src/test/java/test/IngressTest.java | 55 +++++++++++++++++++ .../bamboo-agent/output.yaml | 2 +- .../expected_helm_output/bamboo/output.yaml | 2 +- .../bitbucket/output.yaml | 4 +- .../confluence/output.yaml | 4 ++ 10 files changed, 135 insertions(+), 7 deletions(-) create mode 100644 src/main/charts/confluence/templates/ingress-synchrony.yaml diff --git a/src/main/charts/confluence/README.md b/src/main/charts/confluence/README.md index e9f1d1903..83e5e2f0a 100644 --- a/src/main/charts/confluence/README.md +++ b/src/main/charts/confluence/README.md @@ -202,6 +202,10 @@ Kubernetes: `>=1.21.x-0` | synchrony.additionalVolumeMounts | list | `[]` | Defines any additional volumes mounts for the Synchrony container. These can refer to existing volumes, or new volumes can be defined via 'volumes.additionalSynchrony'. | | synchrony.containerSecurityContext | object | `{}` | Standard K8s field that holds security configurations that will be applied to a container. https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ | | synchrony.enabled | bool | `false` | Set to 'true' if Synchrony (i.e. collaborative editing) should be enabled. This will result in a separate StatefulSet and Service to be created for Synchrony. If disabled, then collaborative editing will be disabled in Confluence. | +| synchrony.ingress | object | `{"annotations":null,"path":null,"pathType":null}` | If 'synchrony.ingress.path' is defined, a dedicated Synchrony ingress object is created. This is useful if you need to deploy multiple instances of Confluence with Synchrony enabled using the same Ingress hostname and different synchrony paths | +| synchrony.ingress.annotations | string | `nil` | Custom annotations applied to Synchrony ingress | +| synchrony.ingress.path | string | `nil` | Ingress path applied to Synchrony ingress | +| synchrony.ingress.pathType | string | `nil` | Defaults to Prefix, but can be ImplementationSpecific if rewrite target is applied | | synchrony.podAnnotations | object | `{}` | Custom annotations that will be applied to all Synchrony pods. When undefined, default to '.Values.podAnnotations' which are Confluence pod annotations (if defined) | | synchrony.ports.hazelcast | int | `5701` | The port on which the Synchrony container listens for Hazelcast traffic | | synchrony.ports.http | int | `8091` | The port on which the Synchrony container listens for HTTP traffic | diff --git a/src/main/charts/confluence/templates/_helpers.tpl b/src/main/charts/confluence/templates/_helpers.tpl index a2dc1726a..7bd2017b7 100644 --- a/src/main/charts/confluence/templates/_helpers.tpl +++ b/src/main/charts/confluence/templates/_helpers.tpl @@ -198,9 +198,14 @@ Pod labels {{- end -}} {{- define "confluence.sysprop.synchronyServiceUrl" -}} +{{- $synchronyIngressPath := "synchrony" }} +{{- if .Values.synchrony.ingress.path }} +{{- $sanitizePathRegex := "^/|\\(.*" }} +{{- $synchronyIngressPath = regexReplaceAll $sanitizePathRegex .Values.synchrony.ingress.path "" }} +{{- end }} {{- if .Values.synchrony.enabled -}} - {{- if .Values.ingress.https -}}-Dsynchrony.service.url=https://{{ .Values.ingress.host }}/synchrony/v1 - {{- else }}-Dsynchrony.service.url=http://{{ .Values.ingress.host }}/synchrony/v1 + {{- if .Values.ingress.https -}}-Dsynchrony.service.url=https://{{ .Values.ingress.host }}/{{ $synchronyIngressPath }}/v1 + {{- else }}-Dsynchrony.service.url=http://{{ .Values.ingress.host }}/{{ $synchronyIngressPath }}/v1 {{- end }} {{- else -}} -Dsynchrony.btf.disabled=true diff --git a/src/main/charts/confluence/templates/ingress-synchrony.yaml b/src/main/charts/confluence/templates/ingress-synchrony.yaml new file mode 100644 index 000000000..6764ba35b --- /dev/null +++ b/src/main/charts/confluence/templates/ingress-synchrony.yaml @@ -0,0 +1,41 @@ +{{- if and .Values.ingress.create (not .Values.ingress.openShiftRoute) .Values.synchrony.ingress.path }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "common.names.fullname" . }}-synchrony + labels: + {{- include "common.labels.commonLabels" . | nindent 4 }} + annotations: + {{ if .Values.ingress.nginx }} + "nginx.ingress.kubernetes.io/affinity": "cookie" + "nginx.ingress.kubernetes.io/affinity-mode": "persistent" + "nginx.ingress.kubernetes.io/proxy-body-size": {{ .Values.ingress.maxBodySize }} + "nginx.ingress.kubernetes.io/proxy-connect-timeout": {{ .Values.ingress.proxyConnectTimeout|quote }} + "nginx.ingress.kubernetes.io/proxy-read-timeout": {{ .Values.ingress.proxyReadTimeout|quote }} + "nginx.ingress.kubernetes.io/proxy-send-timeout": {{ .Values.ingress.proxySendTimeout|quote }} + {{- end }} + {{- with .Values.synchrony.ingress.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: +{{ if and (.Values.ingress.https) (.Values.ingress.host) }} + tls: + - hosts: + - {{ .Values.ingress.host }} + {{ if .Values.ingress.tlsSecretName }} + secretName: {{ .Values.ingress.tlsSecretName }} + {{ end }} +{{ end }} + ingressClassName: {{ default "nginx" .Values.ingress.className }} + rules: + - host: {{ .Values.ingress.host }} + http: + paths: + - path: {{ .Values.synchrony.ingress.path }} + pathType: {{ .Values.synchrony.ingress.pathType | default "Prefix" }} + backend: + service: + name: {{ include "synchrony.fullname" . }} + port: + number: {{ $.Values.synchrony.service.port }} +{{ end }} diff --git a/src/main/charts/confluence/templates/ingress.yaml b/src/main/charts/confluence/templates/ingress.yaml index 3d364f687..e906aa3d3 100644 --- a/src/main/charts/confluence/templates/ingress.yaml +++ b/src/main/charts/confluence/templates/ingress.yaml @@ -31,7 +31,7 @@ spec: - host: {{ .Values.ingress.host }} http: paths: - {{ if .Values.synchrony.enabled }} + {{ if and .Values.synchrony.enabled (not .Values.synchrony.ingress.path) }} - path: /synchrony pathType: Prefix backend: diff --git a/src/main/charts/confluence/values.yaml b/src/main/charts/confluence/values.yaml index 3e566ef9f..7cb4e7f31 100644 --- a/src/main/charts/confluence/values.yaml +++ b/src/main/charts/confluence/values.yaml @@ -1215,6 +1215,25 @@ synchrony: # annotations: {} + # -- If 'synchrony.ingress.path' is defined, a dedicated Synchrony ingress object is created. + # This is useful if you need to deploy multiple instances of Confluence with Synchrony enabled + # using the same Ingress hostname and different synchrony paths + # + ingress: + + # -- Ingress path applied to Synchrony ingress + # + path: + + # -- Defaults to Prefix, but can be ImplementationSpecific if rewrite target is applied + # + pathType: + + # -- Custom annotations applied to Synchrony ingress + # + annotations: + + securityContextEnabled: true securityContext: diff --git a/src/test/java/test/IngressTest.java b/src/test/java/test/IngressTest.java index b2836b378..480e03938 100644 --- a/src/test/java/test/IngressTest.java +++ b/src/test/java/test/IngressTest.java @@ -521,6 +521,61 @@ void confluence_ingress_path_default_synchronyDisabled(Product product) throws E "/bootstrap"); } + @ParameterizedTest + @EnumSource(value = Product.class, names = "confluence") + void confluence_synchrony_dedicated_ingress(Product product) throws Exception { + final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of( + "synchrony.enabled", "true", + "synchrony.ingress.path", "/custom-synchrony(/|$)(.*)", + "synchrony.ingress.pathType", "ImplementationSpecific", + "synchrony.ingress.annotations.nginx\\.ingress\\.kubernetes\\.io\\/rewrite-target", "/$2", + "ingress.create", "true", + "ingress.host", "myhost.mydomain")); + + final var ingresses = resources.getAll(Kind.Ingress); + Assertions.assertEquals(3, ingresses.size()); + final List ingressPaths = extractAllPaths(ingresses); + org.assertj.core.api.Assertions.assertThat(ingressPaths).containsExactlyInAnyOrder( + "/", + "/setup", + "/bootstrap", + "/custom-synchrony(/|$)(.*)"); + + final var synchronyIngress = resources.get(Kind.Ingress, product.getHelmReleaseName() + "-synchrony"); + final var pathType = synchronyIngress.getSpec().get("rules").get(0).get("http").get("paths").get(0).get("pathType"); + final var annotations = synchronyIngress.getAnnotations().get("nginx.ingress.kubernetes.io/rewrite-target"); + assertThat(pathType).hasTextEqualTo("ImplementationSpecific"); + assertThat(annotations).hasTextContaining("/$2"); + } + + @ParameterizedTest + @EnumSource(value = Product.class, names = "confluence") + void confluence_no_synchrony_path_confluence_ingress(Product product) throws Exception { + final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of( + "synchrony.enabled", "true", + "synchrony.ingress.path", "/custom-synchrony(/|$)(.*)", + "ingress.create", "true", + "ingress.host", "myhost.mydomain")); + + final var confluenceIngress = resources.get(Kind.Ingress, product.getHelmReleaseName()); + final var paths = confluenceIngress.getSpec().get("rules").get(0).get("http").get("paths"); + assert paths.size() == 1; + assertThat(paths.get(0).get("path")).hasTextEqualTo("/"); + } + + @ParameterizedTest + @EnumSource(value = Product.class, names = "confluence") + void confluence_custom_synchrony_context_system_property(Product product) throws Exception { + final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of( + "synchrony.enabled", "true", + "synchrony.ingress.path", "/custom-synchrony(/|$)(.*)", + "ingress.create", "true", + "ingress.host", "myhost.mydomain")); + + final var jvmConfigMap = resources.getConfigMap(product.getHelmReleaseName() + "-jvm-config"); + assertThat(jvmConfigMap.getConfigMapData().path("additional_jvm_args")).hasTextContaining("https://myhost.mydomain/custom-synchrony/v1"); + } + @ParameterizedTest @EnumSource(value = Product.class, names = {"bamboo", "bitbucket", "confluence", "crowd", "jira"}) void ingress_proxy_settings(Product product) throws Exception { diff --git a/src/test/resources/expected_helm_output/bamboo-agent/output.yaml b/src/test/resources/expected_helm_output/bamboo-agent/output.yaml index dfde77f36..0a8792e54 100644 --- a/src/test/resources/expected_helm_output/bamboo-agent/output.yaml +++ b/src/test/resources/expected_helm_output/bamboo-agent/output.yaml @@ -46,7 +46,7 @@ spec: template: metadata: annotations: - checksum/config-jvm: 2d30d799fbc750b2b8db524a4c083578c6ccb6fe80039f28bd942bdd50d140ba + checksum/config-jvm: 7038385ec2965d79622b5c5298b2d382ab8417b6d466bbe7586eebaee220626b labels: app.kubernetes.io/name: bamboo-agent app.kubernetes.io/instance: unittest-bamboo-agent diff --git a/src/test/resources/expected_helm_output/bamboo/output.yaml b/src/test/resources/expected_helm_output/bamboo/output.yaml index b8f293d57..20362e171 100644 --- a/src/test/resources/expected_helm_output/bamboo/output.yaml +++ b/src/test/resources/expected_helm_output/bamboo/output.yaml @@ -450,7 +450,7 @@ spec: template: metadata: annotations: - checksum/config-jvm: 94eeaf84360ab454cdbce033b6e70a9861dd0c5629a8eac368121fa7eda373c9 + checksum/config-jvm: 2426cd1859d23ac35add3b5a19d6840b154cd8f349aed06103bd4df62229115e labels: app.kubernetes.io/name: bamboo app.kubernetes.io/instance: unittest-bamboo diff --git a/src/test/resources/expected_helm_output/bitbucket/output.yaml b/src/test/resources/expected_helm_output/bitbucket/output.yaml index d8d70af62..c97d9a37c 100644 --- a/src/test/resources/expected_helm_output/bitbucket/output.yaml +++ b/src/test/resources/expected_helm_output/bitbucket/output.yaml @@ -593,7 +593,7 @@ spec: template: metadata: annotations: - checksum/config-jvm: e7046ce5cd859d7707a5c4012208e7e0dccdf2e85faf9d83defbed6cc7d3ba36 + checksum/config-jvm: 9b71314a22fc99c88c2425704e324d523ca1f11147a026233fde149e643ad8d6 labels: app.kubernetes.io/name: bitbucket-mesh app.kubernetes.io/instance: unittest-bitbucket @@ -714,7 +714,7 @@ spec: template: metadata: annotations: - checksum/config-jvm: 05c7c39e4f510616cfd852a30e28d85e39f81a279ee9fd52dd3dd07bfa921c31 + checksum/config-jvm: 0e687f22f2386675084265bd7fe2782cd1ab50ef5ece4b70263f98bb7b98f39b labels: app.kubernetes.io/name: bitbucket app.kubernetes.io/instance: unittest-bitbucket diff --git a/src/test/resources/expected_helm_output/confluence/output.yaml b/src/test/resources/expected_helm_output/confluence/output.yaml index 99a627249..6e7342d87 100644 --- a/src/test/resources/expected_helm_output/confluence/output.yaml +++ b/src/test/resources/expected_helm_output/confluence/output.yaml @@ -333,6 +333,10 @@ data: additionalVolumeMounts: [] containerSecurityContext: {} enabled: true + ingress: + annotations: null + path: null + pathType: null podAnnotations: {} ports: hazelcast: 5701