From 9ff148b37aa3c18ddc42907feffb33cd33e1618b Mon Sep 17 00:00:00 2001 From: astro-stan <36302090+astro-stan@users.noreply.github.com> Date: Sun, 5 Oct 2025 21:52:52 +0100 Subject: [PATCH] feat(common): Improve traefik service integration (#40293) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Description** This PR improves the traefik service integration by: - Adds ability to generate [ServersTransport](https://doc.traefik.io/traefik/reference/routing-configuration/kubernetes/crd/http/serverstransport/) resources - Adds docs regarding the traefik service integration to the website ⚒️ Fixes # NA **⚙️ Type of change** - [X] ⚙️ Feature/App addition - [ ] 🪛 Bugfix - [ ] ⚠️ Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] 🔃 Refactor of current code - [X] 📜 Documentation Changes **🧪 How Has This Been Tested?** What I have tested: - ServersTransport generation - `traefik.ingress.kubernetes.io/service.serversscheme` and `traefik.ingress.kubernetes.io/service.serverstransport` service annotations generation - Traefik: - talks over HTTPS when `traefik.ingress.kubernetes.io/service.serversscheme: "https"` is set on the service - skips TLS verification when `insecureSkipVerify: true` - Successfully verifies TLS when `serverName`, `rootCAs` and `insecureSkipVerify` are set appropriately **📃 Notes:** 1. Previously service docs were missing any kind of documentation regarding available integrations. This PR only adds docs about the traefik integration. Other integrations such as metallb, and cillium remain undocumented as they are not relevant to this PR. 2. To keep this PR small, I have only added the most commonly used [ServersTransport configuration options](https://doc.traefik.io/traefik/reference/routing-configuration/kubernetes/crd/http/serverstransport/#configuration-options). However, this can easily be extended with more options in the future. **✔️ Checklist:** - [X] ⚖️ My code follows the style guidelines of this project - [X] 👀 I have performed a self-review of my own code - [X] #️⃣ I have commented my code, particularly in hard-to-understand areas - [X] 📄 I have made changes to the documentation - [X] 🧪 I have added tests to this description that prove my fix is effective or that my feature works - [X] ⬆️ I increased versions for any altered app according to semantic versioning - [X] I made sure the title starts with `feat(chart-name):`, `fix(chart-name):`, `chore(chart-name):`, `docs(chart-name):` or `fix(docs):` **➕ App addition** If this PR is an app addition please make sure you have done the following. - [ ] 🖼️ I have added an icon in the Chart's root directory called `icon.png` --- _Please don't blindly check all the boxes. Read them and only check those that apply. Those checkboxes are there for the reviewer to see what is this all about and the status of this PR with a quick glance._ --------- Signed-off-by: astro-stan <36302090+astro-stan@users.noreply.github.com> --- charts/library/common-test/Chart.yaml | 1 - .../service/traefik_integration_test.yaml | 222 +++++++++++ charts/library/common/Chart.yaml | 3 +- .../common/templates/class/_service.tpl | 9 +- .../_middleware.tpl | 0 .../templates/class/traefik/_rootCARefs.tpl | 76 ++++ .../class/traefik/_serverstransport.tpl | 45 +++ .../lib/service/integrations/_traefik.tpl | 39 +- .../templates/lib/traefik/_validation.tpl | 20 + .../common/templates/loader/_apply.tpl | 3 + .../templates/spawner/traefik/_middleware.tpl | 2 +- .../spawner/traefik/_serverstransport.tpl | 61 +++ charts/library/common/values.yaml | 13 + .../src/content/docs/common/service/index.md | 47 +++ .../common/service/integrations/traefik.md | 363 ++++++++++++++++++ 15 files changed, 891 insertions(+), 13 deletions(-) create mode 100644 charts/library/common-test/tests/service/traefik_integration_test.yaml rename charts/library/common/templates/class/{traefik-middleware => traefik}/_middleware.tpl (100%) create mode 100644 charts/library/common/templates/class/traefik/_rootCARefs.tpl create mode 100644 charts/library/common/templates/class/traefik/_serverstransport.tpl create mode 100644 charts/library/common/templates/spawner/traefik/_serverstransport.tpl create mode 100644 website/src/content/docs/common/service/integrations/traefik.md diff --git a/charts/library/common-test/Chart.yaml b/charts/library/common-test/Chart.yaml index 4d01967c83c..ecb6fb07034 100644 --- a/charts/library/common-test/Chart.yaml +++ b/charts/library/common-test/Chart.yaml @@ -37,4 +37,3 @@ sources: - https://github.com/trueforge-org/truecharts/tree/master/charts/library/common-test type: application version: 1.0.0 - diff --git a/charts/library/common-test/tests/service/traefik_integration_test.yaml b/charts/library/common-test/tests/service/traefik_integration_test.yaml new file mode 100644 index 00000000000..de5a8e7e6f5 --- /dev/null +++ b/charts/library/common-test/tests/service/traefik_integration_test.yaml @@ -0,0 +1,222 @@ +suite: service (traefik integration) test +templates: + - common.yaml +release: + name: test-release-name + namespace: test-release-namespace +tests: + - it: should pass with traefik integration enabled + set: + service: + my-service: + enabled: true + primary: true + type: ClusterIP + integrations: + traefik: + enabled: true + ports: &ports + port-name: + enabled: true + primary: true + port: 12345 + protocol: https + workload: &workload + my-workload: + enabled: true + primary: true + type: Deployment + podSpec: {} + asserts: + - documentIndex: &serviceDoc 1 + isKind: + of: Service + - documentIndex: *serviceDoc + isAPIVersion: + of: v1 + - documentIndex: *serviceDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *serviceDoc + isSubset: + path: metadata.annotations + content: + traefik.ingress.kubernetes.io/service.serversscheme: "https" + traefik.ingress.kubernetes.io/service.serverstransport: "test-release-namespace-test-release-name-common-test@kubernetescrd" + - documentIndex: &serversTransportDoc 2 + isKind: + of: ServersTransport + - documentIndex: *serversTransportDoc + isAPIVersion: + of: traefik.io/v1alpha1 + - documentIndex: *serversTransportDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *serversTransportDoc + equal: + path: spec + value: + insecureSkipVerify: false + + - it: should pass with traefik integration and forceTLS enabled + set: + service: + my-service: + enabled: true + primary: true + type: ClusterIP + integrations: + traefik: + enabled: true + forceTLS: true + ports: + port-name: + enabled: true + primary: true + port: 12345 + protocol: http # Not HTTPS, so forceTLS is needed + workload: *workload + asserts: + - documentIndex: *serviceDoc + isKind: + of: Service + - documentIndex: *serviceDoc + isAPIVersion: + of: v1 + - documentIndex: *serviceDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *serviceDoc + isSubset: + path: metadata.annotations + content: + traefik.ingress.kubernetes.io/service.serversscheme: "https" + traefik.ingress.kubernetes.io/service.serverstransport: "test-release-namespace-test-release-name-common-test@kubernetescrd" + - documentIndex: *serversTransportDoc + isKind: + of: ServersTransport + - documentIndex: *serversTransportDoc + isAPIVersion: + of: traefik.io/v1alpha1 + - documentIndex: *serversTransportDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *serversTransportDoc + equal: + path: spec + value: + insecureSkipVerify: false + + - it: should pass with traefik integration and insecureSkipVerify enabled + set: + service: + my-service: + enabled: true + primary: true + type: ClusterIP + integrations: + traefik: + enabled: true + insecureSkipVerify: true + ports: *ports + workload: *workload + asserts: + - documentIndex: *serviceDoc + isKind: + of: Service + - documentIndex: *serviceDoc + isAPIVersion: + of: v1 + - documentIndex: *serviceDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *serviceDoc + isSubset: + path: metadata.annotations + content: + traefik.ingress.kubernetes.io/service.serversscheme: "https" + traefik.ingress.kubernetes.io/service.serverstransport: "test-release-namespace-test-release-name-common-test@kubernetescrd" + - documentIndex: *serversTransportDoc + isKind: + of: ServersTransport + - documentIndex: *serversTransportDoc + isAPIVersion: + of: traefik.io/v1alpha1 + - documentIndex: *serversTransportDoc + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *serversTransportDoc + equal: + path: spec + value: + insecureSkipVerify: true + + - it: should pass with traefik integration enabled, serverName and rootCA secret + set: + configmap: + my-map: + enabled: true + data: + ca.crt: |- + -----BEGIN CERTIFICATE----- + ... + -----END CERTIFICATE----- + service: + my-service: + enabled: true + primary: true + type: ClusterIP + integrations: + traefik: + enabled: true + serverName: "my.domain.com" + rootCAs: + - secretRef: + name: my-domain-ca-secret + expandObjectName: false + - configMapRef: + name: my-map + ports: *ports + workload: *workload + asserts: + - documentIndex: &serviceDocWithRootCAs 2 + isKind: + of: Service + - documentIndex: *serviceDocWithRootCAs + isAPIVersion: + of: v1 + - documentIndex: *serviceDocWithRootCAs + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *serviceDocWithRootCAs + isSubset: + path: metadata.annotations + content: + traefik.ingress.kubernetes.io/service.serversscheme: "https" + traefik.ingress.kubernetes.io/service.serverstransport: "test-release-namespace-test-release-name-common-test@kubernetescrd" + - documentIndex: &serversTransportDocWithRootCAs 3 + isKind: + of: ServersTransport + - documentIndex: *serversTransportDocWithRootCAs + isAPIVersion: + of: traefik.io/v1alpha1 + - documentIndex: *serversTransportDocWithRootCAs + equal: + path: metadata.name + value: test-release-name-common-test + - documentIndex: *serversTransportDocWithRootCAs + equal: + path: spec + value: + serverName: "my.domain.com" + insecureSkipVerify: false + rootCAs: + - secret: my-domain-ca-secret + - configMap: test-release-name-common-test-my-map diff --git a/charts/library/common/Chart.yaml b/charts/library/common/Chart.yaml index 85f0f435d48..f4b8d952055 100644 --- a/charts/library/common/Chart.yaml +++ b/charts/library/common/Chart.yaml @@ -50,5 +50,4 @@ sources: - https://hub.docker.com/_/ - https://hub.docker.com/r/mikefarah/yq type: library -version: 28.21.1 - +version: 28.22.0 diff --git a/charts/library/common/templates/class/_service.tpl b/charts/library/common/templates/class/_service.tpl index 0c08e8de6b4..43fa1609102 100644 --- a/charts/library/common/templates/class/_service.tpl +++ b/charts/library/common/templates/class/_service.tpl @@ -15,17 +15,12 @@ objectData: The service data, that will be used to render the Service object. {{- $_ := set $objectData "annotations" ($objectData.annotations | default dict) -}} {{/* Init variables */}} - {{- $hasHTTPSPort := false -}} {{- $hasHostPort := false -}} {{- $hostNetwork := false -}} {{- $podValues := dict -}} {{- range $portName, $port := $objectData.ports -}} {{- if $port.enabled -}} - {{- if eq (tpl ($port.protocol | default "") $rootCtx) "https" -}} - {{- $hasHTTPSPort = true -}} - {{- end -}} - {{- if and (hasKey $port "hostPort") $port.hostPort -}} {{- $hasHostPort = true -}} {{- end -}} @@ -64,9 +59,7 @@ objectData: The service data, that will be used to render the Service object. {{- include "tc.v1.common.lib.service.integration.metallb" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} {{- include "tc.v1.common.lib.service.integration.cilium" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} {{- end -}} - {{- if $hasHTTPSPort -}} - {{- include "tc.v1.common.lib.service.integration.traefik" (dict "rootCtx" $rootCtx "objectData" $objectData) -}} - {{- end }} + {{- include "tc.v1.common.lib.service.integration.traefik" (dict "rootCtx" $rootCtx "objectData" $objectData) }} --- apiVersion: v1 kind: Service diff --git a/charts/library/common/templates/class/traefik-middleware/_middleware.tpl b/charts/library/common/templates/class/traefik/_middleware.tpl similarity index 100% rename from charts/library/common/templates/class/traefik-middleware/_middleware.tpl rename to charts/library/common/templates/class/traefik/_middleware.tpl diff --git a/charts/library/common/templates/class/traefik/_rootCARefs.tpl b/charts/library/common/templates/class/traefik/_rootCARefs.tpl new file mode 100644 index 00000000000..369b599c4e7 --- /dev/null +++ b/charts/library/common/templates/class/traefik/_rootCARefs.tpl @@ -0,0 +1,76 @@ +{{/* Returns Env From */}} +{{/* Call this template: +{{ include "tc.v1.common.class.traefik.rootCARefs" (dict "rootCtx" $ "objectData" $objectData) }} +rootCtx: The root context of the chart. +objectData: The object data to be used to render the rootCAs section. +*/}} +{{- define "tc.v1.common.class.traefik.rootCARefs" -}} + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + + {{- $refs := (list "configMapRef" "secretRef") -}} + {{- range $rootCAs := $objectData.rootCAs -}} + {{- if and (not $rootCAs.secretRef) (not $rootCAs.configMapRef) -}} + {{- fail (printf "Traefik - ServersTransport - Expected [rootCAs] entry to have one of [%s]" (join ", " $refs)) -}} + {{- end -}} + + {{- if and $rootCAs.secretRef $rootCAs.configMapRef -}} + {{- fail (printf "Traefik - ServersTransport - Expected [rootCAs] entry to have only one of [%s], but got both" (join ", " $refs)) -}} + {{- end -}} + + {{- range $ref := $refs -}} + {{- with (get $rootCAs $ref) -}} + {{- if not .name -}} + {{- fail (printf "Traefik - ServersTransport - Expected non-empty [rootCAs.%s.name]" $ref) -}} + {{- end -}} + + {{- $objectName := tpl .name $rootCtx -}} + + {{- $expandName := (include "tc.v1.common.lib.util.expandName" (dict + "rootCtx" $rootCtx "objectData" . + "name" $ref "caller" "Traefik - ServersTransport" + "key" "rootCAs")) -}} + + {{- if eq $expandName "true" -}} + {{- $object := dict -}} + {{- $source := "" -}} + {{- if eq $ref "configMapRef" -}} + {{- $object = (get $rootCtx.Values.configmap $objectName) -}} + {{- $source = "ConfigMap" -}} + {{- else if eq $ref "secretRef" -}} + {{- $object = (get $rootCtx.Values.secret $objectName) -}} + {{- $source = "Secret" -}} + {{- end -}} + + {{- if not $object -}} + {{- fail (printf "Traefik - ServersTransport - Expected %s [%s] defined in [rootCAs] to exist" $source $objectName) -}} + {{- end -}} + + {{/* A (not necessarily exhaustive) list of keys that are understood by Traefik to contain CAs. Taken from: + * https://github.com/traefik/traefik/blob/6df82676aaf8186215086a1d9e934170fb5db13f/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml + */}} + {{- $mandatoryKeys := list "ca.crt" "tls.ca" "tls.crt" -}} + + {{- $keyFound := false -}} + {{- range $k, $v := $object.data -}} + {{- if has $k $mandatoryKeys -}} + {{- $keyFound = true -}} + {{- end -}} + {{- end -}} + + {{- if not $keyFound -}} + {{- fail (printf "Traefik - ServersTransport - Expected %s [%s] defined in [rootCAs] to have one of [%s] keys" $source $objectName (join ", " $mandatoryKeys)) }} + {{- end }} + + {{- $objectName = (printf "%s-%s" (include "tc.v1.common.lib.chart.names.fullname" $rootCtx) $objectName) -}} + {{- end }} + + {{- if eq $ref "secretRef" }} +- secret: {{ $objectName | quote }} + {{- else }} +- configMap: {{ $objectName | quote }} + {{- end }} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/library/common/templates/class/traefik/_serverstransport.tpl b/charts/library/common/templates/class/traefik/_serverstransport.tpl new file mode 100644 index 00000000000..c0cbcb7fb8d --- /dev/null +++ b/charts/library/common/templates/class/traefik/_serverstransport.tpl @@ -0,0 +1,45 @@ +{{/* Traefik ServersTransport Class */}} +{{/* Call this template: +{{ include "tc.v1.common.class.traefik.serverstransport" (dict "rootCtx" $ "objectData" $objectData) }} + +rootCtx: The root context of the chart. +objectData: + name: The name of the serverstransport. + labels: The labels of the serverstransport. + annotations: The annotations of the serverstransport. + data: The data of the serverstransport. Supported keys: serverName, insecureSkipVerify, rootCAs + namespace: The namespace of the serverstransport. (Optional) +*/}} + +{{- define "tc.v1.common.class.traefik.serverstransport" -}} + + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData }} +--- +apiVersion: traefik.io/v1alpha1 +kind: ServersTransport +metadata: + name: {{ $objectData.name }} + namespace: {{ include "tc.v1.common.lib.metadata.namespace" (dict "rootCtx" $rootCtx "objectData" $objectData "caller" "ServersTransport") }} + {{- $labels := (mustMerge ($objectData.labels | default dict) (include "tc.v1.common.lib.metadata.allLabels" $rootCtx | fromYaml)) -}} + {{- with (include "tc.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "labels" $labels) | trim) }} + labels: + {{- . | nindent 4 }} + {{- end -}} + {{- $annotations := (mustMerge ($objectData.annotations | default dict) (include "tc.v1.common.lib.metadata.allAnnotations" $rootCtx | fromYaml)) -}} + {{- with (include "tc.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "annotations" $annotations) | trim) }} + annotations: + {{- . | nindent 4 }} + {{- end }} +spec: + {{- if $objectData.serverName }} + serverName: {{ $objectData.serverName }} + {{- end }} + insecureSkipVerify: {{ $objectData.insecureSkipVerify | default false }} + {{- if $objectData.rootCAs }} + {{- with (include "tc.v1.common.class.traefik.rootCARefs" (dict "rootCtx" $rootCtx "objectData" $objectData) | trim) }} + rootCAs: + {{- . | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} diff --git a/charts/library/common/templates/lib/service/integrations/_traefik.tpl b/charts/library/common/templates/lib/service/integrations/_traefik.tpl index e8b64b81ed4..9a569825408 100644 --- a/charts/library/common/templates/lib/service/integrations/_traefik.tpl +++ b/charts/library/common/templates/lib/service/integrations/_traefik.tpl @@ -6,7 +6,44 @@ {{- $traefik := $objectData.integrations.traefik -}} {{- if $traefik.enabled -}} - {{- $_ := set $objectData.annotations "traefik.ingress.kubernetes.io/service.serversscheme" "https" -}} + {{- include "tc.v1.common.lib.service.integration.traefik.validate" (dict "objectData" $objectData) -}} + + {{- $forceTLS := $traefik.forceTLS | default false -}} + {{- $hasOnlyHTTPSPorts := (ge (len $objectData.ports) 1) -}} + + {{- range $portName, $port := $objectData.ports -}} + {{- if and $port.enabled (ne (tpl ($port.protocol | default "") $rootCtx) "https") -}} + {{- $hasOnlyHTTPSPorts = false -}} + {{- end -}} + {{- end -}} + + {{- if or $hasOnlyHTTPSPorts $forceTLS -}} + {{- $_ := set $objectData.annotations "traefik.ingress.kubernetes.io/service.serversscheme" "https" -}} + {{- end -}} + + {{/* Add the ServersTransport annotation. */}} + {{- $_ := set $objectData.annotations + "traefik.ingress.kubernetes.io/service.serverstransport" + (printf "%s-%s@kubernetescrd" + (include "tc.v1.common.lib.metadata.namespace" (dict "rootCtx" $rootCtx "objectData" $objectData "caller" "Service")) + $objectData.name) -}} + {{- end -}} {{- end -}} + +{{- define "tc.v1.common.lib.service.integration.traefik.validate" -}} + {{- $objectData := .objectData -}} + + {{- $traefik := $objectData.integrations.traefik -}} + + {{- if not (kindIs "slice" ($traefik.rootCAs | default list)) -}} + {{- fail (printf "Service - Expected [integrations.traefik.rootCAs] to be a [slice], but got [%s]" (kindOf $traefik.rootCAs)) -}} + {{- end -}} + + {{- range $i, $ca := ($traefik.rootCAs | default list) -}} + {{- if not (kindIs "map" $ca) -}} + {{- fail (printf "Service - Expected [integrations.traefik.rootCAs[%d]] to be a [map], but got [%s]" $i (kindOf $ca)) -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/library/common/templates/lib/traefik/_validation.tpl b/charts/library/common/templates/lib/traefik/_validation.tpl index 04426632399..9ed2ff5d76c 100644 --- a/charts/library/common/templates/lib/traefik/_validation.tpl +++ b/charts/library/common/templates/lib/traefik/_validation.tpl @@ -21,3 +21,23 @@ objectData: {{- end -}} {{- end -}} + +{{/* ServersTransport Validation */}} +{{/* Call this template: +{{ include "tc.v1.common.lib.traefik.serverstransport.validation" (dict "objectData" $objectData) -}} +objectData: + labels: The labels of the ServersTransport. + annotations: The annotations of the ServersTransport. + data: The data of the ServersTransport. +*/}} + +{{- define "tc.v1.common.lib.traefik.serverstransport.validation" -}} + {{- $objectData := .objectData -}} + + {{- if $objectData.data -}} + {{- if not (kindIs "map" $objectData.data) -}} + {{- fail (printf "ServersTransport - Expected [data] to be a dictionary, but got [%v]" (kindOf $objectData.data)) -}} + {{- end -}} + {{- end -}} + +{{- end -}} diff --git a/charts/library/common/templates/loader/_apply.tpl b/charts/library/common/templates/loader/_apply.tpl index 20a634d22d9..45444f0d802 100644 --- a/charts/library/common/templates/loader/_apply.tpl +++ b/charts/library/common/templates/loader/_apply.tpl @@ -49,6 +49,9 @@ {{/* Render Middleware(s) */}} {{- include "tc.v1.common.spawner.traefik.middleware" . | nindent 0 -}} + {{/* Render ServersTransport(s) */}} + {{- include "tc.v1.common.spawner.traefik.serverstransport" . | nindent 0 -}} + {{/* Render ingress(s) */}} {{- include "tc.v1.common.spawner.ingress" . | nindent 0 -}} diff --git a/charts/library/common/templates/spawner/traefik/_middleware.tpl b/charts/library/common/templates/spawner/traefik/_middleware.tpl index e45a926817a..3a78d8a6439 100644 --- a/charts/library/common/templates/spawner/traefik/_middleware.tpl +++ b/charts/library/common/templates/spawner/traefik/_middleware.tpl @@ -1,6 +1,6 @@ {{/* Traefik Middleware Spawner */}} {{/* Call this template: -{{ include "tc.v1.common.spawner.configmap" $ -}} +{{ include "tc.v1.common.spawner.traefik.middleware" $ -}} */}} {{- define "tc.v1.common.spawner.traefik.middleware" -}} diff --git a/charts/library/common/templates/spawner/traefik/_serverstransport.tpl b/charts/library/common/templates/spawner/traefik/_serverstransport.tpl new file mode 100644 index 00000000000..334e22b6487 --- /dev/null +++ b/charts/library/common/templates/spawner/traefik/_serverstransport.tpl @@ -0,0 +1,61 @@ +{{/* Traefik ServersTransport Spawner */}} +{{/* Call this template: +{{ include "tc.v1.common.spawner.traefik.serverstransport" $ -}} +*/}} + +{{- define "tc.v1.common.spawner.traefik.serverstransport" -}} + {{- $fullname := include "tc.v1.common.lib.chart.names.fullname" $ -}} + + {{/* Go over all services and get their defined ServersTransports */}} + {{- range $name, $service := .Values.service -}} + {{- $enabled := (include "tc.v1.common.lib.util.enabled" (dict + "rootCtx" $ "objectData" $service + "name" $name "caller" "Service" + "key" "service")) -}} + + {{/* Skip disabled services or services without traefik integration */}} + {{- if ne $enabled "true" -}}{{- continue -}}{{- end -}} + {{- if not $service.integrations -}} + {{- $_ := set $service "integrations" dict -}} + {{- end -}} + {{- if not $service.integrations.traefik -}} + {{- $_ := set $service.integrations "traefik" dict -}} + {{- end -}} + {{- $traefik := $service.integrations.traefik -}} + {{- $enabledTraefikIntegration := "false" -}} + {{- if and (hasKey $traefik "enabled") (kindIs "bool" $traefik.enabled) -}} + {{- $enabledTraefikIntegration = $traefik.enabled | toString -}} + {{- end -}} + {{- if ne $enabledTraefikIntegration "true" }}{{- continue -}}{{- end -}} + + {{/* Init object name */}} + {{- $objectName := $name -}} + + {{- $expandName := (include "tc.v1.common.lib.util.expandName" (dict + "rootCtx" $ "objectData" $service + "name" $name "caller" "Service" + "key" "service")) -}} + + {{- if eq $expandName "true" -}} + {{/* Expand the name of the service if expandName resolves to true */}} + {{- $objectName = $fullname -}} + {{- end -}} + + {{- if and (eq $expandName "true") (not $service.primary) -}} + {{/* If the service is not primary append its name to fullname */}} + {{- $objectName = (printf "%s-%s" $fullname $name) -}} + {{- end -}} + + {{/* Create a copy of the traefik integration dict */}} + {{- $objectData := (mustDeepCopy $traefik) -}} + {{- $_ := set $objectData "name" $objectName -}} + + {{/* Perform validations */}} + {{- include "tc.v1.common.lib.chart.names.validation" (dict "name" $objectName) -}} + {{- include "tc.v1.common.lib.traefik.serverstransport.validation" (dict "objectData" $objectData) -}} + + {{/* Call class to create the object */}} + {{- include "tc.v1.common.class.traefik.serverstransport" (dict "rootCtx" $ "objectData" $objectData) -}} + + {{- end -}} +{{- end -}} diff --git a/charts/library/common/values.yaml b/charts/library/common/values.yaml index 5da2251f16d..66eb81002a7 100644 --- a/charts/library/common/values.yaml +++ b/charts/library/common/values.yaml @@ -269,6 +269,19 @@ service: # # traefik: # enabled: false + ## Optional - ensures `serversscheme: https` annotation is set + # forceTLS: false + ## Optional to make Traefik skip TLS verification when taling to an HTTPS + ## backend service + # insecureSkipVerify: false + ## Optional to specify the hostname to use when talking to a backend service + # serverName: "" + ## Optional - K8s secrets containing CA certs to use when performing TLS + ## verification when taling to a backend service + # rootCAs: + # - secretRef: # OR configMapRef + # name: root-ca-secret + # expandObjectName: true enabled: true primary: true ports: diff --git a/website/src/content/docs/common/service/index.md b/website/src/content/docs/common/service/index.md index 8151ebd8707..8f8453dca14 100644 --- a/website/src/content/docs/common/service/index.md +++ b/website/src/content/docs/common/service/index.md @@ -481,6 +481,53 @@ service: --- +#### `integrations` + +Define the integrations for this service + +| | | +| ---------- | ---------------------------- | +| Key | `service.$name.integrations` | +| Type | `map` | +| Required | ❌ | +| Helm `tpl` | ❌ | +| Default | `{}` | + +Example + +```yaml +service: + service-name: + integrations: {} +``` + +--- + +##### `integrations.traefik` + +Define the traefik integration for this service + +See more details in [Traefik Integration](/common/service/integrations/traefik) + +| | | +| ---------- | ------------------------------------ | +| Key | `service.$name.integrations.traefik` | +| Type | `map` | +| Required | ❌ | +| Helm `tpl` | ❌ | +| Default | `{}` | + +Example + +```yaml +service: + service-name: + integrations: + traefik: {} +``` + +--- + ## Full Examples Full examples can be found under each service type diff --git a/website/src/content/docs/common/service/integrations/traefik.md b/website/src/content/docs/common/service/integrations/traefik.md new file mode 100644 index 00000000000..2163c0cceee --- /dev/null +++ b/website/src/content/docs/common/service/integrations/traefik.md @@ -0,0 +1,363 @@ +--- +title: Traefik Integration +--- + +:::note + +- Examples under each key are only to be used as a placement guide +- See the [Full Examples](/common/service/integrations/traefik#full-examples) +section for complete examples. + +::: + +## Appears in + +- `.Values.service.$name.integration.traefik` + +:::tip + +- Replace references to `$name` with the actual name you want to use. + +::: + +--- + +## `enabled` + +Enables or Disables the traefik integration + +| | | +| ---------- | -------------------------------------------- | +| Key | `service.$name.integrations.traefik.enabled` | +| Type | `bool` | +| Required | ❌ | +| Helm `tpl` | ❌ | +| Default | `false` | + +Example + +```yaml +service: + service-name: + integrations: + traefik: + enabled: true +``` + +--- + +## `forceTLS` + +Force TLS when talking to the backend service + +:::note + +Adds the `traefik.ingress.kubernetes.io/service.serversscheme: "https"` annotation. + +It does that both with this set OR when there is a service with only https ports + +::: + +| | | +| ---------- | --------------------------------------------- | +| Key | `service.$name.integrations.traefik.forceTLS` | +| Type | `bool` | +| Required | ❌ | +| Helm `tpl` | ❌ | +| Default | `false` | + +Example + +```yaml +service: + service-name: + integrations: + traefik: + forceTLS: true +``` + +--- + +## `insecureSkipVerify` + +Skip TLS verification when taling to an HTTPS backend service + +:::note + +Allows talking to HTTPS backend services which use self-signed certs. + +Alternatively you can set a [server name](/common/service/integrations/traefik#servername) +and [root CAs](/common/service/integrations/traefik#rootcas) to use when performing +TLS validation. + +::: + +| | | +| ---------- | ------------------------------------------------------- | +| Key | `service.$name.integrations.traefik.insecureSkipVerify` | +| Type | `bool` | +| Required | ❌ | +| Helm `tpl` | ❌ | +| Default | `false` | + +Example + +```yaml +service: + service-name: + integrations: + traefik: + insecureSkipVerify: false +``` + +--- + +## `serverName` + +Set the hostname to use when talking to a backend service + +| | | +| ---------- | ----------------------------------------------- | +| Key | `service.$name.integrations.traefik.serverName` | +| Type | `string` | +| Required | ❌ | +| Helm `tpl` | ❌ | +| Default | "" | + +Example + +```yaml +service: + service-name: + integrations: + traefik: + serverName: "my.service.com" +``` + +--- + +## `rootCAs` + +List of kubernetes secrets (in the same namespace) containing certificate +authorities to use when performing TLS verification of the backend service. + +:::note + +The secrets must contain a key called `ca.crt`, `tls.crt` or `tls.ca` with the +value being the certificate authority. For more information refer to the +[official documentation](https://doc.traefik.io/traefik/reference/routing-configuration/kubernetes/crd/http/serverstransport/#serverstransport-rootcas) +and [this fixture](https://github.com/traefik/traefik/blob/6df82676aaf8186215086a1d9e934170fb5db13f/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml). + +::: + +| | | +| ---------- | ----------------------------------------------- | +| Key | `service.$name.integrations.traefik.rootCAs` | +| Type | `list` of `map` | +| Required | ❌ | +| Helm `tpl` | ❌ | +| Default | `[]` | + +Example + +```yaml +service: + service-name: + integrations: + traefik: + rootCAs: [] +``` + +--- + +### `rootCAs.secretRef` + +Define the secretRef + +| | | +| ---------- | ------------------------------------------------------------- | +| Key | `service.$name.integrations.traefik.rootCAs[].secretRef` | +| Type | `map` | +| Required | ❌ | +| Helm `tpl` | ❌ | +| Default | `{}` | + +Example + +```yaml +service: + service-name: + integrations: + traefik: + rootCAs: + - secretRef: {} +``` + +--- + +#### `rootCAs.secretRef.name` + +Define the secret name + +:::note + +This will be automatically expanded to `fullname-secret-name`. +You can opt out of this by setting [`expandObjectName`](/common/service/integrations/traefik#rootcassecretrefexpandobjectname) +to `false` + +::: + +| | | +| ---------- | ------------------------------------------------------------------ | +| Key | `service.$name.integrations.traefik.rootCAs[].secretRef.name` | +| Type | `string` | +| Required | ✅ | +| Helm `tpl` | ✅ | +| Default | `""` | + +Example + +```yaml +service: + service-name: + integrations: + traefik: + rootCAs: + - secretRef: + name: secret-name +``` + +--- + +#### `rootCAs.secretRef.expandObjectName` + +Whether to expand (adding the fullname as prefix) the secret name + +| | | +| ---------- | ------------------------------------------------------------------------------ | +| Key | `service.$name.integrations.traefik.rootCAs[].secretRef.expandObjectName` | +| Type | `bool` | +| Required | ❌ | +| Helm `tpl` | ❌ | +| Default | `true` | + +Example + +```yaml +service: + service-name: + integrations: + traefik: + rootCAs: + - secretRef: + name: secret-name + expandObjectName: false +``` + +--- + +### `rootCAs.configMapRef` + +Define the configMapRef + +| | | +| ---------- | ---------------------------------------------------------------- | +| Key | `service.$name.integrations.traefik.rootCAs[].configMapRef` | +| Type | `map` | +| Required | ❌ | +| Helm `tpl` | ❌ | +| Default | `{}` | + +Example + +```yaml +service: + service-name: + integrations: + traefik: + rootCAs: + - configMapRef: {} +``` + +--- + +#### `rootCAs.configMapRef.name` + +Define the configmap name + +:::note + +This will be automatically expanded to `fullname-configmap-name`. +You can opt out of this by setting [`expandObjectName`](/common/service/integrations/traefik#rootcasconfigmaprefexpandobjectname) +to `false` + +::: + +| | | +| ---------- | --------------------------------------------------------------------- | +| Key | `service.$name.integrations.traefik.rootCAs[].configMapRef.name` | +| Type | `string` | +| Required | ✅ | +| Helm `tpl` | ✅ | +| Default | `""` | + +Example + +```yaml +service: + service-name: + integrations: + traefik: + rootCAs: + - configMapRef: + name: configmap-name +``` + +--- + +#### `rootCAs.configMapRef.expandObjectName` + +Whether to expand (adding the fullname as prefix) the configmap name + +| | | +| ---------- | --------------------------------------------------------------------------------- | +| Key | `service.$name.integrations.traefik.rootCAs[].configMapRef.expandObjectName` | +| Type | `bool` | +| Required | ❌ | +| Helm `tpl` | ❌ | +| Default | `true` | + +Example + +```yaml +service: + service-name: + integrations: + traefik: + rootCAs: + - configMapRef: + name: configmap-name + expandObjectName: false +``` + +--- + +## Full Examples + +```yaml +service: + service-name: + integrations: + traefik: + enabled: true + forceTLS: true + insecureSkipVerify: false + serverName: "my.service.com" + rootCAs: + - configMapRef: + name: configmap-name + expandObjectName: false + - secretRef: + name: secret-name + expandObjectName: true +```