diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 856d897..c47d76f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,6 +5,8 @@ repos: - id: helm-docs args: - --sort-values-order=file + - --chart-search-root=charts + exclude: ^\.claude/ - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 diff --git a/charts/platform/CHANGELOG.md b/charts/platform/CHANGELOG.md index 8973ec7..9a45ba3 100644 --- a/charts/platform/CHANGELOG.md +++ b/charts/platform/CHANGELOG.md @@ -5,12 +5,32 @@ All notable changes to this chart will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.33.0] - 2026-04-30 + +### Added + +- **Enhancement**: allow global configuration of Ingress options. A new `global.ingress` block (`enabled`, `path`, `defaultPathType`, `ingressClassName`, `annotations`, `extraLabels`, `tls`) lets cluster-wide Ingress defaults be set once at the parent and propagate to every subchart, removing the need to repeat controller-wide config per subchart. `enabled` is OR-merged; scalar fields fall back to global when local is unset; `annotations` and `extraLabels` are merged with local winning on key collision; `tls` is concatenated (useful for a single wildcard certificate across all services). +- Add `seqera.ingress.host` template helper in each chart's `_helpers.tpl` returning that chart's primary domain. Lets users write `external-dns.alpha.kubernetes.io/hostname: '{{ include "seqera.ingress.host" . }}'` once in `global.ingress.annotations` and have it resolve to the correct host per chart at render time, without hard-coding hostnames. +- Add `docs/conventions/ingress.md` documenting the Ingress conventions used across charts. + +### Changed + +- Update bitnami/common to 2.39.0 +- **BREAKING**: Default `ingress.defaultPathType` is now `Prefix` (was `ImplementationSpecific`). With the previous default and the chart's default `path: "/"`, routing behavior depended on the ingress controller — NGINX treated it as a prefix match, AWS ALB required `/*` for the same effect, GKE applied its own interpretation. The result was the same chart and values producing different routing across clusters. `Prefix` is part of the Kubernetes Ingress spec and produces consistent prefix-match semantics across NGINX, Traefik, AWS ALB, and most modern controllers, giving users a predictable out-of-the-box experience. Users whose controller still requires `ImplementationSpecific` (e.g. older GKE) can set `global.ingress.defaultPathType: ImplementationSpecific` once at the parent. +- Update `examples/ingress-configurations/*` to drop now-redundant `defaultPathType: Prefix` overrides and showcase `global.ingress.ingressClassName` in `nginx-cert-manager.yaml`. + ## [0.32.7] - 2026-04-30 ### Changed - Bump `seqera-common` to 2.1.2, `wave` to 0.1.2, `agent-backend` to 0.4.11, `mcp` to 0.3.6, `pipeline-optimization` to 2.0.5, `portal-web` to 0.2.8, `studios` to 1.2.15 (Redis init container log message now reports `auth set` or `auth not set`) +## [0.32.5] - 2026-04-30 + +### Changed + +- Bump `seqera-common` dependency to 2.1.1, `wave` subchart to 0.1.1, `agent-backend` to 0.4.9, `mcp` to 0.3.4, `pipeline-optimization` to 2.0.4, `portal-web` to 0.2.6, `studios` to 1.2.13 to pick up the Redis init container fix that no longer logs the password + ## [0.32.6] - 2026-04-30 ### Changed diff --git a/charts/platform/Chart.lock b/charts/platform/Chart.lock index 82ae1dc..ad4e2df 100644 --- a/charts/platform/Chart.lock +++ b/charts/platform/Chart.lock @@ -1,27 +1,27 @@ dependencies: - name: common repository: oci://registry-1.docker.io/bitnamicharts - version: 2.38.0 + version: 2.39.0 - name: seqera-common repository: file://../seqera-common version: 2.1.2 - name: pipeline-optimization repository: file://charts/pipeline-optimization - version: 2.0.5 + version: 2.0.6 - name: studios repository: file://charts/studios - version: 1.2.15 + version: 1.3.0 - name: wave repository: file://charts/wave - version: 0.1.2 + version: 0.2.0 - name: mcp repository: file://charts/mcp - version: 0.3.6 + version: 0.4.0 - name: agent-backend repository: file://charts/agent-backend - version: 0.4.11 + version: 0.5.0 - name: portal-web repository: file://charts/portal-web - version: 0.2.8 -digest: sha256:27521223337ca1b5d2afbb18b9cd356f53e6693e30ce7e0cc70cf35c5ae3c569 -generated: "2026-04-30T17:17:57.223873+02:00" + version: 0.3.0 +digest: sha256:91713efc8b064ff4d302d405bf0f27ead8297706ebfada779ef5d15fd7419a6b +generated: "2026-05-05T14:25:51.292458674+02:00" diff --git a/charts/platform/Chart.yaml b/charts/platform/Chart.yaml index ff96aa1..e2331d1 100644 --- a/charts/platform/Chart.yaml +++ b/charts/platform/Chart.yaml @@ -37,7 +37,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.32.7 +version: 0.33.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to @@ -69,25 +69,25 @@ dependencies: - studios condition: studios.enabled - name: wave - version: "0.1.x" + version: "0.2.x" repository: "file://charts/wave" tags: - wave condition: wave.enabled - name: mcp - version: "0.3.x" + version: "0.4.x" repository: "file://charts/mcp" tags: - mcp condition: mcp.enabled - name: agent-backend - version: "0.4.x" + version: "0.5.x" repository: "file://charts/agent-backend" tags: - agent-backend condition: agent-backend.enabled - name: portal-web - version: "0.2.x" + version: "0.3.x" repository: "file://charts/portal-web" tags: - portal-web diff --git a/charts/platform/README.md b/charts/platform/README.md index 9ccad18..8e1ea9b 100644 --- a/charts/platform/README.md +++ b/charts/platform/README.md @@ -2,7 +2,7 @@ A Helm chart to deploy Seqera Platform (also referred to as Tower) on Kubernetes. -![Version: 0.32.7](https://img.shields.io/badge/Version-0.32.7-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v25.3.4](https://img.shields.io/badge/AppVersion-v25.3.4-informational?style=flat-square) +![Version: 0.33.0](https://img.shields.io/badge/Version-0.33.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v25.3.4](https://img.shields.io/badge/AppVersion-v25.3.4-informational?style=flat-square) > [!WARNING] > This chart is currently still in development and breaking changes are expected. @@ -41,7 +41,7 @@ To install the chart with the release name `my-release`: ```console helm install my-release oci://public.cr.seqera.io/charts/platform \ - --version 0.32.7 \ + --version 0.33.0 \ --namespace my-namespace \ --create-namespace ``` @@ -63,12 +63,12 @@ When upgrading between versions, please refer to the [CHANGELOG.md](CHANGELOG.md | Repository | Name | Version | |------------|------|---------| | file://../seqera-common | seqera-common | 2.x.x | -| file://charts/agent-backend | agent-backend | 0.4.x | -| file://charts/mcp | mcp | 0.3.x | +| file://charts/agent-backend | agent-backend | 0.5.x | +| file://charts/mcp | mcp | 0.4.x | | file://charts/pipeline-optimization | pipeline-optimization | 2.x.x | -| file://charts/portal-web | portal-web | 0.2.x | +| file://charts/portal-web | portal-web | 0.3.x | | file://charts/studios | studios | 1.x.x | -| file://charts/wave | wave | 0.1.x | +| file://charts/wave | wave | 0.2.x | | oci://registry-1.docker.io/bitnamicharts | common | 2.x.x | ## Values @@ -85,6 +85,13 @@ When upgrading between versions, please refer to the [CHANGELOG.md](CHANGELOG.md | global.mcpDomain | string | `"{{ printf \"mcp.%s\" .Values.global.platformExternalDomain }}"` | Domain where Seqera MCP listens. Evaluated as a template | | global.agentBackendDomain | string | `"{{ printf \"ai-api.%s\" .Values.global.platformExternalDomain }}"` | Domain where the Agent Backend service listens. Evaluated as a template | | global.portalWebDomain | string | `"{{ printf \"ai.%s\" .Values.global.platformExternalDomain }}"` | Domain where the Portal Web frontend listens. Evaluated as a template | +| global.ingress.enabled | bool | `false` | Enable Ingress for the parent chart and every subchart that exposes one. Each chart's local `ingress.enabled` is OR'd with this — set this to `true` to turn on all Ingresses in one switch | +| global.ingress.path | string | `"/"` | Default path applied to ingress rules when a chart's local `ingress.path` is not set. AWS ALB users should override to `/*`. | +| global.ingress.defaultPathType | string | `"Prefix"` | Default path type applied to ingress rules when a chart's local `ingress.defaultPathType` is not set. `Prefix` works for nginx, traefik, AWS ALB, and most modern controllers. Override to `ImplementationSpecific` only if your controller requires it (e.g. older GKE). | +| global.ingress.ingressClassName | string | `""` | Default ingress class name applied to ingress rules when a chart's local `ingress.ingressClassName` is not set. Replaces the deprecated `kubernetes.io/ingress.class` annotation | +| global.ingress.annotations | object | `{}` | Annotations merged into every chart's Ingress (e.g. cert-manager issuer, NGINX `proxy-body-size`, ALB SSL config). Local `ingress.annotations` wins on key collision. Evaluated as a template | +| global.ingress.extraLabels | object | `{}` | Extra labels merged into every chart's Ingress. Local `ingress.extraLabels` wins on key collision. Evaluated as a template | +| global.ingress.tls | list | `[]` | TLS entries concatenated with each chart's local `ingress.tls`. Useful for a single wildcard cert that covers all services. Evaluated as a template | | global.imageCredentials | list | `[]` | Optional credentials to log in and fetch images from a private registry. These credentials are shared with all the subcharts automatically | | global.imageCredentialsSecrets | list | `[]` | Optional list of existing Secrets containing image pull credentials to use for pulling images from private registries. These Secrets are shared with all the subcharts automatically | | global.azure.images.platformBackend.registry | string | `nil` | Image registry for the Platform backend image deployed on Azure. Example: `myregistry.azurecr.io`. Evaluated as a template | @@ -426,14 +433,14 @@ When upgrading between versions, please refer to the [CHANGELOG.md](CHANGELOG.md | serviceAccount.imagePullSecretNames | list | `[]` | Names of Secrets containing credentials to pull images from registries | | serviceAccount.automountServiceAccountToken | bool | `false` | Automount service account token when the service account is generated | | ingress.enabled | bool | `false` | Enable ingress for Platform | -| ingress.path | string | `"/"` | Path for the main ingress rule Note: this needs to be set to '/*' to be used with AWS ALB ingress controller | -| ingress.contentPath | string | `"/"` | Path for the content domain ingress rule Note: this needs to be set to '/*' to be used with AWS ALB ingress controller | -| ingress.defaultPathType | string | `"ImplementationSpecific"` | Default path type for the Ingress | +| ingress.path | string | `""` | Path for the main ingress rule. When empty, falls back to `global.ingress.path` | +| ingress.contentPath | string | `"/"` | Path for the content domain ingress rule | +| ingress.defaultPathType | string | `""` | Default path type for the Ingress. When empty, falls back to `global.ingress.defaultPathType` | | ingress.defaultBackend | object | `{}` | Configure the default service for the ingress (evaluated as template) Important: make sure only one defaultBackend is defined across the k8s cluster: if the ingress doesn't reconcile successfully, 'describe ingress ' will report problems | | ingress.extraHosts | list | `[]` | Additional hosts you want to include. Evaluated as a template | | ingress.annotations | object | `{}` | Ingress annotations specific to your load balancer. Evaluated as a template | | ingress.extraLabels | object | `{}` | Additional labels for the ingress object. Evaluated as a template | -| ingress.ingressClassName | string | `""` | Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`) | +| ingress.ingressClassName | string | `""` | Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`). When empty, falls back to `global.ingress.ingressClassName` | | ingress.tls | list | `[]` | TLS configuration. Evaluated as a template | | extraDeploy | list | `[]` | Array of extra objects to deploy with the release | | commonAnnotations | object | `{}` | Annotations to add to all deployed objects | diff --git a/charts/platform/charts/agent-backend/CHANGELOG.md b/charts/platform/charts/agent-backend/CHANGELOG.md index 4de0c95..cbd32ae 100644 --- a/charts/platform/charts/agent-backend/CHANGELOG.md +++ b/charts/platform/charts/agent-backend/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to this chart will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.0] - 2026-05-05 + +- **Enhancement**: allow global configuration of Ingress options. A new `global.ingress` block (`enabled`, `path`, `defaultPathType`, `ingressClassName`, `annotations`, `extraLabels`, `tls`) lets cluster-wide Ingress defaults be set once at the parent and propagate to every subchart, removing the need to repeat controller-wide config per subchart. `enabled` is OR-merged; scalar fields fall back to global when local is unset; `annotations` and `extraLabels` are merged with local winning on key collision; `tls` is concatenated (useful for a single wildcard certificate across all services). +- Add `seqera.ingress.host` template helper in each chart's `_helpers.tpl` returning that chart's primary domain. Lets users write `external-dns.alpha.kubernetes.io/hostname: '{{ include "seqera.ingress.host" . }}'` once in `global.ingress.annotations` and have it resolve to the correct host per chart at render time, without hard-coding hostnames. +- Add `docs/conventions/ingress.md` documenting the Ingress conventions used across charts. + +### Changed + +- Update bitnami/common to 2.39.0 +- **BREAKING**: Default `ingress.defaultPathType` is now `Prefix` (was `ImplementationSpecific`). With the previous default and the chart's default `path: "/"`, routing behavior depended on the ingress controller — NGINX treated it as a prefix match, AWS ALB required `/*` for the same effect, GKE applied its own interpretation. The result was the same chart and values producing different routing across clusters. `Prefix` is part of the Kubernetes Ingress spec and produces consistent prefix-match semantics across NGINX, Traefik, AWS ALB, and most modern controllers, giving users a predictable out-of-the-box experience. Users whose controller still requires `ImplementationSpecific` (e.g. older GKE) can set `global.ingress.defaultPathType: ImplementationSpecific` once at the parent. + ## [0.4.11] - 2026-04-30 ### Changed diff --git a/charts/platform/charts/agent-backend/Chart.lock b/charts/platform/charts/agent-backend/Chart.lock index e1ffec4..38f0400 100644 --- a/charts/platform/charts/agent-backend/Chart.lock +++ b/charts/platform/charts/agent-backend/Chart.lock @@ -1,9 +1,9 @@ dependencies: - name: common repository: oci://registry-1.docker.io/bitnamicharts - version: 2.38.0 + version: 2.39.0 - name: seqera-common repository: file://../../../seqera-common version: 2.1.2 -digest: sha256:4d1319075460f2901b92a5aa964db5992673e65275005b33671b597179c47c83 -generated: "2026-04-30T17:18:03.74270588+02:00" +digest: sha256:3eac4511073f329f614981ce9b91e6cdda035aa007a3aeecd7069958c9ede539 +generated: "2026-05-05T14:17:14.074321992+02:00" diff --git a/charts/platform/charts/agent-backend/Chart.yaml b/charts/platform/charts/agent-backend/Chart.yaml index 2aae5aa..c45ccfe 100644 --- a/charts/platform/charts/agent-backend/Chart.yaml +++ b/charts/platform/charts/agent-backend/Chart.yaml @@ -19,7 +19,7 @@ apiVersion: v2 name: agent-backend description: Backend service for Seqera CLI AI capabilities type: application -version: 0.4.11 +version: 0.5.0 appVersion: "2.0.0" maintainers: - name: Seqera Labs diff --git a/charts/platform/charts/agent-backend/README.md b/charts/platform/charts/agent-backend/README.md index 8484af4..7f80824 100644 --- a/charts/platform/charts/agent-backend/README.md +++ b/charts/platform/charts/agent-backend/README.md @@ -2,7 +2,7 @@ Backend service for Seqera CLI AI capabilities -![Version: 0.4.11](https://img.shields.io/badge/Version-0.4.11-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 2.0.0](https://img.shields.io/badge/AppVersion-2.0.0-informational?style=flat-square) +![Version: 0.5.0](https://img.shields.io/badge/Version-0.5.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 2.0.0](https://img.shields.io/badge/AppVersion-2.0.0-informational?style=flat-square) > [!WARNING] > This chart is currently still in development and breaking changes are expected. @@ -39,7 +39,7 @@ To install the chart with the release name `my-release`: ```console helm install my-release oci://public.cr.seqera.io/charts/agent-backend \ - --version 0.4.11 \ + --version 0.5.0 \ --namespace my-namespace \ --create-namespace ``` @@ -66,6 +66,13 @@ When upgrading between versions, please refer to the [CHANGELOG.md](CHANGELOG.md | global.platformServicePort | string | `""` | Seqera Platform Service port. Required when deploying this subchart standalone. When deploying as part of the parent `platform` umbrella chart, this value is inherited from the parent chart's `global` section | | global.agentBackendDomain | string | `"{{ printf \"ai-api.%s\" .Values.global.platformExternalDomain }}"` | Domain where the Agent Backend service listens. Evaluated as a template | | global.mcpDomain | string | `"{{ printf \"mcp.%s\" .Values.global.platformExternalDomain }}"` | Domain where Seqera MCP listens. Evaluated as a template | +| global.ingress.enabled | bool | `false` | Enable Ingress for this chart. OR'd with the chart's local `ingress.enabled` so setting this once at the parent enables all subchart Ingresses. | +| global.ingress.path | string | `"/"` | Default path applied to ingress rules when `ingress.path` is not set. AWS ALB users should override to `/*`. | +| global.ingress.defaultPathType | string | `"Prefix"` | Default path type applied to ingress rules when `ingress.defaultPathType` is not set. `Prefix` works for nginx, traefik, AWS ALB, and most modern controllers. | +| global.ingress.ingressClassName | string | `""` | Default ingress class name applied when `ingress.ingressClassName` is not set | +| global.ingress.annotations | object | `{}` | Annotations merged into the Ingress. Local `ingress.annotations` wins on key collision. Evaluated as a template | +| global.ingress.extraLabels | object | `{}` | Extra labels merged into the Ingress. Local `ingress.extraLabels` wins on key collision. Evaluated as a template | +| global.ingress.tls | list | `[]` | TLS entries concatenated with the local `ingress.tls`. Evaluated as a template | | global.imageCredentials | list | `[]` | Optional credentials to log in and fetch images from a private registry. These credentials are shared with all the subcharts automatically | | global.imageCredentialsSecrets | list | `[]` | Optional list of existing Secrets containing image pull credentials to use for pulling images from private registries. These Secrets are shared with all the subcharts automatically | | database.host | string | `""` | MySQL database hostname | @@ -196,13 +203,13 @@ When upgrading between versions, please refer to the [CHANGELOG.md](CHANGELOG.md | serviceAccount.imagePullSecretNames | list | `[]` | Names of Secrets containing credentials to pull images from registries | | serviceAccount.automountServiceAccountToken | bool | `true` | Automatically mount service account token | | ingress.enabled | bool | `false` | Enable ingress for Agent Backend | -| ingress.path | string | `"/"` | Path for the main ingress rule Note: this needs to be set to '/*' to be used with AWS ALB ingress controller | -| ingress.defaultPathType | string | `"ImplementationSpecific"` | Default path type for the Ingress | +| ingress.path | string | `""` | Path for the main ingress rule. When empty, falls back to `global.ingress.path` | +| ingress.defaultPathType | string | `""` | Default path type for the Ingress. When empty, falls back to `global.ingress.defaultPathType` | | ingress.defaultBackend | object | `{}` | Configure the default service for the ingress (evaluated as template) Important: make sure only one defaultBackend is defined across the k8s cluster: if the ingress doesn't reconcile successfully, 'describe ingress ' will report problems | | ingress.extraHosts | list | `[]` | Additional hosts you want to include. Evaluated as a template | | ingress.annotations | object | `{}` | Ingress annotations specific to your load balancer. Evaluated as a template | | ingress.extraLabels | object | `{}` | Additional labels for the ingress object. Evaluated as a template | -| ingress.ingressClassName | string | `""` | Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`) | +| ingress.ingressClassName | string | `""` | Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`). When empty, falls back to `global.ingress.ingressClassName` | | ingress.tls | list | `[]` | TLS configuration. Evaluated as a template | | extraDeploy | list | `[]` | Array of extra objects to deploy with the release | | commonAnnotations | object | `{}` | Annotations to add to all deployed objects | diff --git a/charts/platform/charts/agent-backend/templates/_helpers.tpl b/charts/platform/charts/agent-backend/templates/_helpers.tpl index 9e25fd6..20df8e2 100644 --- a/charts/platform/charts/agent-backend/templates/_helpers.tpl +++ b/charts/platform/charts/agent-backend/templates/_helpers.tpl @@ -126,3 +126,12 @@ Result is base64-encoded (for use in Secret data). {{- end -}} {{- end -}} {{- end -}} + +{{/* +Return this chart's primary ingress hostname. See parent platform chart's `_helpers.tpl` for +usage notes — `'{{ include "seqera.ingress.host" . }}'` in `global.ingress.annotations` resolves +to each chart's own domain at render time. +*/}} +{{- define "seqera.ingress.host" -}} +{{- tpl .Values.global.agentBackendDomain . -}} +{{- end -}} diff --git a/charts/platform/charts/agent-backend/templates/ingress.yaml b/charts/platform/charts/agent-backend/templates/ingress.yaml index 05981f5..80a9538 100644 --- a/charts/platform/charts/agent-backend/templates/ingress.yaml +++ b/charts/platform/charts/agent-backend/templates/ingress.yaml @@ -17,12 +17,16 @@ limitations under the License. */}} -{{ if .Values.ingress.enabled }} +{{ if or .Values.ingress.enabled .Values.global.ingress.enabled }} {{- $commonLabels := include "common.labels.standard" (dict "customLabels" .Values.commonLabels "context" $) | fromYaml -}} - {{- $mergedlabels := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.extraLabels $commonLabels) "context" .) -}} + {{- $mergedlabels := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.extraLabels .Values.global.ingress.extraLabels $commonLabels) "context" .) -}} {{- $labels := include "common.tplvalues.render" (dict "value" $mergedlabels "context" $) -}} - {{- $mergedAnnotations := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.annotations .Values.commonAnnotations) "context" .) -}} + {{- $mergedAnnotations := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.annotations .Values.global.ingress.annotations .Values.commonAnnotations) "context" .) -}} {{- $annotations := include "common.tplvalues.render" (dict "value" $mergedAnnotations "context" $) -}} + {{- $defaultPathType := default .Values.global.ingress.defaultPathType .Values.ingress.defaultPathType -}} + {{- $path := default .Values.global.ingress.path .Values.ingress.path -}} + {{- $ingressClassName := default .Values.global.ingress.ingressClassName .Values.ingress.ingressClassName -}} + {{- $tls := concat (default (list) .Values.ingress.tls) (default (list) .Values.global.ingress.tls) -}} apiVersion: {{ include "common.capabilities.ingress.apiVersion" . }} kind: Ingress @@ -36,11 +40,11 @@ spec: defaultBackend: {{- include "seqera.tplvalues.render" (dict "value" . "context" $) | nindent 4 }} {{- end }} - {{- if .Values.ingress.ingressClassName }} - ingressClassName: {{ .Values.ingress.ingressClassName | quote }} + {{- if $ingressClassName }} + ingressClassName: {{ $ingressClassName | quote }} {{- end }} - {{- with .Values.ingress.tls }} + {{- with $tls }} tls: {{- include "common.tplvalues.render" (dict "value" . "context" $) | nindent 4 }} {{- end }} @@ -49,8 +53,8 @@ spec: - host: {{ tpl .Values.global.agentBackendDomain . | quote }} http: paths: - - path: {{ .Values.ingress.path | quote }} - pathType: {{ .Values.ingress.defaultPathType }} + - path: {{ $path | quote }} + pathType: {{ $defaultPathType }} backend: service: name: {{ (include "common.names.fullname" .) | quote }} @@ -62,7 +66,7 @@ spec: paths: {{- range .paths }} - path: {{ .path }} - pathType: {{ .pathType | default $.Values.ingress.defaultPathType }} + pathType: {{ .pathType | default $defaultPathType }} backend: service: name: {{ tpl .serviceName $ | quote }} diff --git a/charts/platform/charts/agent-backend/tests/__snapshot__/ingress_test.yaml.snap b/charts/platform/charts/agent-backend/tests/__snapshot__/ingress_test.yaml.snap index 71d4f69..4b7b4b4 100644 --- a/charts/platform/charts/agent-backend/tests/__snapshot__/ingress_test.yaml.snap +++ b/charts/platform/charts/agent-backend/tests/__snapshot__/ingress_test.yaml.snap @@ -23,7 +23,7 @@ should render when ingress.enabled is true: port: number: 80 path: / - pathType: ImplementationSpecific + pathType: Prefix - host: agent.example.com http: paths: @@ -33,4 +33,4 @@ should render when ingress.enabled is true: port: number: 8002 path: / - pathType: ImplementationSpecific + pathType: Prefix diff --git a/charts/platform/charts/agent-backend/values.yaml b/charts/platform/charts/agent-backend/values.yaml index 87fb5a4..2e868c6 100644 --- a/charts/platform/charts/agent-backend/values.yaml +++ b/charts/platform/charts/agent-backend/values.yaml @@ -37,6 +37,29 @@ global: # -- Domain where Seqera MCP listens. Evaluated as a template mcpDomain: '{{ printf "mcp.%s" .Values.global.platformExternalDomain }}' + # Ingress defaults shared across the parent chart and all subcharts. Each subchart's local + # `ingress.*` value takes precedence when set; otherwise the global is used. + ingress: + # -- Enable Ingress for this chart. OR'd with the chart's local `ingress.enabled` so + # setting this once at the parent enables all subchart Ingresses. + enabled: false + # -- Default path applied to ingress rules when `ingress.path` is not set. + # AWS ALB users should override to `/*`. + path: "/" + # -- Default path type applied to ingress rules when `ingress.defaultPathType` is not set. + # `Prefix` works for nginx, traefik, AWS ALB, and most modern controllers. + defaultPathType: "Prefix" + # -- Default ingress class name applied when `ingress.ingressClassName` is not set + ingressClassName: "" + # -- Annotations merged into the Ingress. Local `ingress.annotations` wins on key collision. + # Evaluated as a template + annotations: {} + # -- Extra labels merged into the Ingress. Local `ingress.extraLabels` wins on key collision. + # Evaluated as a template + extraLabels: {} + # -- TLS entries concatenated with the local `ingress.tls`. Evaluated as a template + tls: [] + # -- Optional credentials to log in and fetch images from a private registry. These credentials # are shared with all the subcharts automatically imageCredentials: [] @@ -486,12 +509,11 @@ ingress: # -- Enable ingress for Agent Backend enabled: false - # -- Path for the main ingress rule - # Note: this needs to be set to '/*' to be used with AWS ALB ingress controller - path: "/" + # -- Path for the main ingress rule. When empty, falls back to `global.ingress.path` + path: "" - # -- Default path type for the Ingress - defaultPathType: "ImplementationSpecific" + # -- Default path type for the Ingress. When empty, falls back to `global.ingress.defaultPathType` + defaultPathType: "" # -- Configure the default service for the ingress (evaluated as template) # Important: make sure only one defaultBackend is defined across the k8s cluster: if the @@ -523,7 +545,8 @@ ingress: annotations: {} # -- Additional labels for the ingress object. Evaluated as a template extraLabels: {} - # -- Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`) + # -- Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`). + # When empty, falls back to `global.ingress.ingressClassName` ingressClassName: "" # -- TLS configuration. Evaluated as a template tls: [] diff --git a/charts/platform/charts/mcp/CHANGELOG.md b/charts/platform/charts/mcp/CHANGELOG.md index 9e60069..6eacbff 100644 --- a/charts/platform/charts/mcp/CHANGELOG.md +++ b/charts/platform/charts/mcp/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to this chart will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.4.0] - 2026-05-05 + +- **Enhancement**: allow global configuration of Ingress options. A new `global.ingress` block (`enabled`, `path`, `defaultPathType`, `ingressClassName`, `annotations`, `extraLabels`, `tls`) lets cluster-wide Ingress defaults be set once at the parent and propagate to every subchart, removing the need to repeat controller-wide config per subchart. `enabled` is OR-merged; scalar fields fall back to global when local is unset; `annotations` and `extraLabels` are merged with local winning on key collision; `tls` is concatenated (useful for a single wildcard certificate across all services). +- Add `seqera.ingress.host` template helper in each chart's `_helpers.tpl` returning that chart's primary domain. Lets users write `external-dns.alpha.kubernetes.io/hostname: '{{ include "seqera.ingress.host" . }}'` once in `global.ingress.annotations` and have it resolve to the correct host per chart at render time, without hard-coding hostnames. +- Add `docs/conventions/ingress.md` documenting the Ingress conventions used across charts. + +### Changed + +- Update bitnami/common to 2.39.0 +- **BREAKING**: Default `ingress.defaultPathType` is now `Prefix` (was `ImplementationSpecific`). With the previous default and the chart's default `path: "/"`, routing behavior depended on the ingress controller — NGINX treated it as a prefix match, AWS ALB required `/*` for the same effect, GKE applied its own interpretation. The result was the same chart and values producing different routing across clusters. `Prefix` is part of the Kubernetes Ingress spec and produces consistent prefix-match semantics across NGINX, Traefik, AWS ALB, and most modern controllers, giving users a predictable out-of-the-box experience. Users whose controller still requires `ImplementationSpecific` (e.g. older GKE) can set `global.ingress.defaultPathType: ImplementationSpecific` once at the parent. + ## [0.3.6] - 2026-04-30 ### Changed diff --git a/charts/platform/charts/mcp/Chart.lock b/charts/platform/charts/mcp/Chart.lock index 1380ea2..bdfe380 100644 --- a/charts/platform/charts/mcp/Chart.lock +++ b/charts/platform/charts/mcp/Chart.lock @@ -1,9 +1,9 @@ dependencies: - name: common repository: oci://registry-1.docker.io/bitnamicharts - version: 2.38.0 + version: 2.39.0 - name: seqera-common repository: file://../../../seqera-common version: 2.1.2 -digest: sha256:66f5f1fb1ad2a6fd814da84d51f11c2a3f10633e1ba8d23f72eab6294ab68d49 -generated: "2026-04-30T17:18:10.359219514+02:00" +digest: sha256:b66829bad11b646116b1ac6fc712849c4231dfbb6641380100f3282e06ab9a39 +generated: "2026-05-05T14:17:42.984593108+02:00" diff --git a/charts/platform/charts/mcp/Chart.yaml b/charts/platform/charts/mcp/Chart.yaml index 1a8fd7c..cec87d7 100644 --- a/charts/platform/charts/mcp/Chart.yaml +++ b/charts/platform/charts/mcp/Chart.yaml @@ -22,7 +22,7 @@ description: | Wave container provisioning, bioinformatics data, and nf-core modules through intelligent RAG-based natural language interactions. type: application -version: 0.3.6 +version: 0.4.0 appVersion: "1.1.0" maintainers: - name: Seqera Labs diff --git a/charts/platform/charts/mcp/README.md b/charts/platform/charts/mcp/README.md index f26e856..e549b75 100644 --- a/charts/platform/charts/mcp/README.md +++ b/charts/platform/charts/mcp/README.md @@ -4,7 +4,7 @@ A Model Context Protocol (MCP) server that provides comprehensive access to the Wave container provisioning, bioinformatics data, and nf-core modules through intelligent RAG-based natural language interactions. -![Version: 0.3.6](https://img.shields.io/badge/Version-0.3.6-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.1.0](https://img.shields.io/badge/AppVersion-1.1.0-informational?style=flat-square) +![Version: 0.4.0](https://img.shields.io/badge/Version-0.4.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.1.0](https://img.shields.io/badge/AppVersion-1.1.0-informational?style=flat-square) > [!WARNING] > This chart is currently still in development and breaking changes are expected. @@ -38,7 +38,7 @@ To install the chart with the release name `my-release`: ```console helm install my-release oci://public.cr.seqera.io/charts/mcp \ - --version 0.3.6 \ + --version 0.4.0 \ --namespace my-namespace \ --create-namespace ``` @@ -64,6 +64,13 @@ When upgrading between versions, please refer to the [CHANGELOG.md](CHANGELOG.md | global.platformServiceAddress | string | `""` | Seqera Platform Service name: can be the internal Kubernetes hostname or an external ingress hostname. Evaluated as a template. Required when deploying this subchart standalone. When deploying as part of the parent `platform` umbrella chart, this value is inherited from the parent chart's `global` section | | global.platformServicePort | string | `""` | Seqera Platform Service port. Required when deploying this subchart standalone. When deploying as part of the parent `platform` umbrella chart, this value is inherited from the parent chart's `global` section | | global.mcpDomain | string | `"{{ printf \"mcp.%s\" .Values.global.platformExternalDomain }}"` | Domain where Seqera MCP listens. Evaluated as a template. Note: The OAuth redirect URL is automatically derived by appending /oauth/callback to the domain | +| global.ingress.enabled | bool | `false` | Enable Ingress for this chart. OR'd with the chart's local `ingress.enabled` so setting this once at the parent enables all subchart Ingresses. | +| global.ingress.path | string | `"/"` | Default path applied to ingress rules when `ingress.path` is not set. AWS ALB users should override to `/*`. | +| global.ingress.defaultPathType | string | `"Prefix"` | Default path type applied to ingress rules when `ingress.defaultPathType` is not set. `Prefix` works for nginx, traefik, AWS ALB, and most modern controllers. | +| global.ingress.ingressClassName | string | `""` | Default ingress class name applied when `ingress.ingressClassName` is not set | +| global.ingress.annotations | object | `{}` | Annotations merged into the Ingress. Local `ingress.annotations` wins on key collision. Evaluated as a template | +| global.ingress.extraLabels | object | `{}` | Extra labels merged into the Ingress. Local `ingress.extraLabels` wins on key collision. Evaluated as a template | +| global.ingress.tls | list | `[]` | TLS entries concatenated with the local `ingress.tls`. Evaluated as a template | | global.imageCredentials | list | `[]` | Optional credentials to log in and fetch images from a private registry. These credentials are shared with all the subcharts automatically | | global.imageCredentialsSecrets | list | `[]` | Optional list of existing Secrets containing image pull credentials to use for pulling images from private registries. These Secrets are shared with all the subcharts automatically | | micronautEnvironments | list | `["oauth-platform"]` | List of Micronaut environments to enable. Evaluated as a template | @@ -156,13 +163,13 @@ When upgrading between versions, please refer to the [CHANGELOG.md](CHANGELOG.md | serviceAccount.imagePullSecretNames | list | `[]` | Names of Secrets containing credentials to pull images from registries | | serviceAccount.automountServiceAccountToken | bool | `true` | Automatically mount service account token | | ingress.enabled | bool | `false` | Enable ingress for MCP | -| ingress.path | string | `"/"` | Path for the main ingress rule Note: this needs to be set to '/*' to be used with AWS ALB ingress controller | -| ingress.defaultPathType | string | `"ImplementationSpecific"` | Default path type for the Ingress | +| ingress.path | string | `""` | Path for the main ingress rule. When empty, falls back to `global.ingress.path` | +| ingress.defaultPathType | string | `""` | Default path type for the Ingress. When empty, falls back to `global.ingress.defaultPathType` | | ingress.defaultBackend | object | `{}` | Configure the default service for the ingress (evaluated as template) Important: make sure only one defaultBackend is defined across the k8s cluster: if the ingress doesn't reconcile successfully, 'describe ingress ' will report problems | | ingress.extraHosts | list | `[]` | Additional hosts you want to include. Evaluated as a template | | ingress.annotations | object | `{}` | Ingress annotations specific to your load balancer. Evaluated as a template | | ingress.extraLabels | object | `{}` | Additional labels for the ingress object. Evaluated as a template | -| ingress.ingressClassName | string | `""` | Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`) | +| ingress.ingressClassName | string | `""` | Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`). When empty, falls back to `global.ingress.ingressClassName` | | ingress.tls | list | `[]` | TLS configuration. Evaluated as a template | | extraDeploy | list | `[]` | Array of extra objects to deploy with the release | | commonAnnotations | object | `{}` | Annotations to add to all deployed objects | diff --git a/charts/platform/charts/mcp/templates/_helpers.tpl b/charts/platform/charts/mcp/templates/_helpers.tpl index 41f1738..fedfd68 100644 --- a/charts/platform/charts/mcp/templates/_helpers.tpl +++ b/charts/platform/charts/mcp/templates/_helpers.tpl @@ -71,3 +71,12 @@ Return the name of the secret containing the OIDC client registration token. {{- printf "MCP_OAUTH_INITIAL_ACCESS_TOKEN" -}} {{- end -}} {{- end -}} + +{{/* +Return this chart's primary ingress hostname. See parent platform chart's `_helpers.tpl` for +usage notes — `'{{ include "seqera.ingress.host" . }}'` in `global.ingress.annotations` resolves +to each chart's own domain at render time. +*/}} +{{- define "seqera.ingress.host" -}} +{{- tpl .Values.global.mcpDomain . -}} +{{- end -}} diff --git a/charts/platform/charts/mcp/templates/ingress.yaml b/charts/platform/charts/mcp/templates/ingress.yaml index 4743878..9cdc80c 100644 --- a/charts/platform/charts/mcp/templates/ingress.yaml +++ b/charts/platform/charts/mcp/templates/ingress.yaml @@ -17,12 +17,16 @@ limitations under the License. */}} -{{ if .Values.ingress.enabled }} +{{ if or .Values.ingress.enabled .Values.global.ingress.enabled }} {{- $commonLabels := include "common.labels.standard" (dict "customLabels" .Values.commonLabels "context" $) | fromYaml -}} - {{- $mergedlabels := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.extraLabels $commonLabels) "context" .) -}} + {{- $mergedlabels := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.extraLabels .Values.global.ingress.extraLabels $commonLabels) "context" .) -}} {{- $labels := include "common.tplvalues.render" (dict "value" $mergedlabels "context" $) -}} - {{- $mergedAnnotations := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.annotations .Values.commonAnnotations) "context" .) -}} + {{- $mergedAnnotations := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.annotations .Values.global.ingress.annotations .Values.commonAnnotations) "context" .) -}} {{- $annotations := include "common.tplvalues.render" (dict "value" $mergedAnnotations "context" $) -}} + {{- $defaultPathType := default .Values.global.ingress.defaultPathType .Values.ingress.defaultPathType -}} + {{- $path := default .Values.global.ingress.path .Values.ingress.path -}} + {{- $ingressClassName := default .Values.global.ingress.ingressClassName .Values.ingress.ingressClassName -}} + {{- $tls := concat (default (list) .Values.ingress.tls) (default (list) .Values.global.ingress.tls) -}} apiVersion: {{ include "common.capabilities.ingress.apiVersion" . }} kind: Ingress @@ -36,11 +40,11 @@ spec: defaultBackend: {{- include "seqera.tplvalues.render" (dict "value" . "context" $) | nindent 4 }} {{- end }} - {{- if .Values.ingress.ingressClassName }} - ingressClassName: {{ .Values.ingress.ingressClassName | quote }} + {{- if $ingressClassName }} + ingressClassName: {{ $ingressClassName | quote }} {{- end }} - {{- with .Values.ingress.tls }} + {{- with $tls }} tls: {{- include "common.tplvalues.render" (dict "value" . "context" $) | nindent 4 }} {{- end }} @@ -49,8 +53,8 @@ spec: - host: {{ tpl .Values.global.mcpDomain . | quote }} http: paths: - - path: {{ .Values.ingress.path | quote }} - pathType: {{ .Values.ingress.defaultPathType }} + - path: {{ $path | quote }} + pathType: {{ $defaultPathType }} backend: service: name: {{ (include "common.names.fullname" .) | quote }} @@ -62,7 +66,7 @@ spec: paths: {{- range .paths }} - path: {{ .path }} - pathType: {{ .pathType | default $.Values.ingress.defaultPathType }} + pathType: {{ .pathType | default $defaultPathType }} backend: service: name: {{ tpl .serviceName $ | quote }} diff --git a/charts/platform/charts/mcp/tests/__snapshot__/ingress_test.yaml.snap b/charts/platform/charts/mcp/tests/__snapshot__/ingress_test.yaml.snap index 406b41e..f07a85d 100644 --- a/charts/platform/charts/mcp/tests/__snapshot__/ingress_test.yaml.snap +++ b/charts/platform/charts/mcp/tests/__snapshot__/ingress_test.yaml.snap @@ -23,7 +23,7 @@ should render when ingress.enabled is true: port: number: 6010 path: / - pathType: ImplementationSpecific + pathType: Prefix - host: mcp2.example.com http: paths: @@ -33,4 +33,4 @@ should render when ingress.enabled is true: port: number: 8002 path: / - pathType: ImplementationSpecific + pathType: Prefix diff --git a/charts/platform/charts/mcp/values.yaml b/charts/platform/charts/mcp/values.yaml index 0aedde9..9754a55 100644 --- a/charts/platform/charts/mcp/values.yaml +++ b/charts/platform/charts/mcp/values.yaml @@ -35,6 +35,29 @@ global: # Note: The OAuth redirect URL is automatically derived by appending /oauth/callback to the domain mcpDomain: '{{ printf "mcp.%s" .Values.global.platformExternalDomain }}' + # Ingress defaults shared across the parent chart and all subcharts. Each subchart's local + # `ingress.*` value takes precedence when set; otherwise the global is used. + ingress: + # -- Enable Ingress for this chart. OR'd with the chart's local `ingress.enabled` so + # setting this once at the parent enables all subchart Ingresses. + enabled: false + # -- Default path applied to ingress rules when `ingress.path` is not set. + # AWS ALB users should override to `/*`. + path: "/" + # -- Default path type applied to ingress rules when `ingress.defaultPathType` is not set. + # `Prefix` works for nginx, traefik, AWS ALB, and most modern controllers. + defaultPathType: "Prefix" + # -- Default ingress class name applied when `ingress.ingressClassName` is not set + ingressClassName: "" + # -- Annotations merged into the Ingress. Local `ingress.annotations` wins on key collision. + # Evaluated as a template + annotations: {} + # -- Extra labels merged into the Ingress. Local `ingress.extraLabels` wins on key collision. + # Evaluated as a template + extraLabels: {} + # -- TLS entries concatenated with the local `ingress.tls`. Evaluated as a template + tls: [] + # -- Optional credentials to log in and fetch images from a private registry. These credentials # are shared with all the subcharts automatically imageCredentials: [] @@ -379,12 +402,11 @@ ingress: # -- Enable ingress for MCP enabled: false - # -- Path for the main ingress rule - # Note: this needs to be set to '/*' to be used with AWS ALB ingress controller - path: "/" + # -- Path for the main ingress rule. When empty, falls back to `global.ingress.path` + path: "" - # -- Default path type for the Ingress - defaultPathType: "ImplementationSpecific" + # -- Default path type for the Ingress. When empty, falls back to `global.ingress.defaultPathType` + defaultPathType: "" # -- Configure the default service for the ingress (evaluated as template) # Important: make sure only one defaultBackend is defined across the k8s cluster: if the @@ -416,7 +438,8 @@ ingress: annotations: {} # -- Additional labels for the ingress object. Evaluated as a template extraLabels: {} - # -- Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`) + # -- Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`). + # When empty, falls back to `global.ingress.ingressClassName` ingressClassName: "" # -- TLS configuration. Evaluated as a template tls: [] diff --git a/charts/platform/charts/pipeline-optimization/CHANGELOG.md b/charts/platform/charts/pipeline-optimization/CHANGELOG.md index 2f8b86c..b5499a7 100644 --- a/charts/platform/charts/pipeline-optimization/CHANGELOG.md +++ b/charts/platform/charts/pipeline-optimization/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this chart will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.0.6] - 2026-05-05 + +### Changed + +- Update bitnami/common to 2.39.0 + ## [2.0.5] - 2026-04-30 ### Changed diff --git a/charts/platform/charts/pipeline-optimization/Chart.lock b/charts/platform/charts/pipeline-optimization/Chart.lock index c40033e..3cb4bbc 100644 --- a/charts/platform/charts/pipeline-optimization/Chart.lock +++ b/charts/platform/charts/pipeline-optimization/Chart.lock @@ -1,9 +1,9 @@ dependencies: - name: common repository: oci://registry-1.docker.io/bitnamicharts - version: 2.38.0 + version: 2.39.0 - name: seqera-common repository: file://../../../seqera-common version: 2.1.2 -digest: sha256:4d1319075460f2901b92a5aa964db5992673e65275005b33671b597179c47c83 -generated: "2026-04-30T17:18:17.504134609+02:00" +digest: sha256:3eac4511073f329f614981ce9b91e6cdda035aa007a3aeecd7069958c9ede539 +generated: "2026-05-05T14:18:13.372479516+02:00" diff --git a/charts/platform/charts/pipeline-optimization/Chart.yaml b/charts/platform/charts/pipeline-optimization/Chart.yaml index 28ba120..82b479f 100644 --- a/charts/platform/charts/pipeline-optimization/Chart.yaml +++ b/charts/platform/charts/pipeline-optimization/Chart.yaml @@ -23,7 +23,7 @@ maintainers: email: devops@seqera.io url: https://seqera.io type: application -version: 2.0.5 +version: 2.0.6 appVersion: "0.4.13" dependencies: - name: common diff --git a/charts/platform/charts/pipeline-optimization/README.md b/charts/platform/charts/pipeline-optimization/README.md index 883e6f2..1264c84 100644 --- a/charts/platform/charts/pipeline-optimization/README.md +++ b/charts/platform/charts/pipeline-optimization/README.md @@ -2,7 +2,7 @@ A Helm chart to deploy the Seqera Pipeline Optimization service (referred to as Groundswell in Platform configuration files). -![Version: 2.0.5](https://img.shields.io/badge/Version-2.0.5-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.4.13](https://img.shields.io/badge/AppVersion-0.4.13-informational?style=flat-square) +![Version: 2.0.6](https://img.shields.io/badge/Version-2.0.6-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.4.13](https://img.shields.io/badge/AppVersion-0.4.13-informational?style=flat-square) > [!WARNING] > This chart is currently still in development and breaking changes are expected. @@ -37,7 +37,7 @@ To install the chart with the release name `my-release`: ```console helm install my-release oci://public.cr.seqera.io/charts/pipeline-optimization \ - --version 2.0.5 \ + --version 2.0.6 \ --namespace my-namespace \ --create-namespace ``` diff --git a/charts/platform/charts/portal-web/CHANGELOG.md b/charts/platform/charts/portal-web/CHANGELOG.md index d4f5d98..719dd35 100644 --- a/charts/platform/charts/portal-web/CHANGELOG.md +++ b/charts/platform/charts/portal-web/CHANGELOG.md @@ -4,6 +4,17 @@ All notable changes to this chart will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [0.3.0] - 2026-05-05 + +- **Enhancement**: allow global configuration of Ingress options. A new `global.ingress` block (`enabled`, `path`, `defaultPathType`, `ingressClassName`, `annotations`, `extraLabels`, `tls`) lets cluster-wide Ingress defaults be set once at the parent and propagate to every subchart, removing the need to repeat controller-wide config per subchart. `enabled` is OR-merged; scalar fields fall back to global when local is unset; `annotations` and `extraLabels` are merged with local winning on key collision; `tls` is concatenated (useful for a single wildcard certificate across all services). +- Add `seqera.ingress.host` template helper in each chart's `_helpers.tpl` returning that chart's primary domain. Lets users write `external-dns.alpha.kubernetes.io/hostname: '{{ include "seqera.ingress.host" . }}'` once in `global.ingress.annotations` and have it resolve to the correct host per chart at render time, without hard-coding hostnames. +- Add `docs/conventions/ingress.md` documenting the Ingress conventions used across charts. + +### Changed + +- Update bitnami/common to 2.39.0 +- **BREAKING**: Default `ingress.defaultPathType` is now `Prefix` (was `ImplementationSpecific`). With the previous default and the chart's default `path: "/"`, routing behavior depended on the ingress controller — NGINX treated it as a prefix match, AWS ALB required `/*` for the same effect, GKE applied its own interpretation. The result was the same chart and values producing different routing across clusters. `Prefix` is part of the Kubernetes Ingress spec and produces consistent prefix-match semantics across NGINX, Traefik, AWS ALB, and most modern controllers, giving users a predictable out-of-the-box experience. Users whose controller still requires `ImplementationSpecific` (e.g. older GKE) can set `global.ingress.defaultPathType: ImplementationSpecific` once at the parent. + ## [0.2.8] - 2026-04-30 ### Changed diff --git a/charts/platform/charts/portal-web/Chart.lock b/charts/platform/charts/portal-web/Chart.lock index bcf2227..a94fb88 100644 --- a/charts/platform/charts/portal-web/Chart.lock +++ b/charts/platform/charts/portal-web/Chart.lock @@ -1,9 +1,9 @@ dependencies: - name: common repository: oci://registry-1.docker.io/bitnamicharts - version: 2.38.0 + version: 2.39.0 - name: seqera-common repository: file://../../../seqera-common version: 2.1.2 -digest: sha256:66f5f1fb1ad2a6fd814da84d51f11c2a3f10633e1ba8d23f72eab6294ab68d49 -generated: "2026-04-30T17:18:24.090883307+02:00" +digest: sha256:b66829bad11b646116b1ac6fc712849c4231dfbb6641380100f3282e06ab9a39 +generated: "2026-05-05T14:18:41.428348035+02:00" diff --git a/charts/platform/charts/portal-web/Chart.yaml b/charts/platform/charts/portal-web/Chart.yaml index e60bfc5..e4e4665 100644 --- a/charts/platform/charts/portal-web/Chart.yaml +++ b/charts/platform/charts/portal-web/Chart.yaml @@ -19,7 +19,7 @@ apiVersion: v2 name: portal-web description: Portal web frontend for Seqera Platform type: application -version: 0.2.8 +version: 0.3.0 appVersion: "0.1.0" maintainers: - name: Seqera Labs diff --git a/charts/platform/charts/portal-web/README.md b/charts/platform/charts/portal-web/README.md index b7f062c..7d6fb02 100644 --- a/charts/platform/charts/portal-web/README.md +++ b/charts/platform/charts/portal-web/README.md @@ -2,7 +2,7 @@ Portal web frontend for Seqera Platform -![Version: 0.2.8](https://img.shields.io/badge/Version-0.2.8-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.1.0](https://img.shields.io/badge/AppVersion-0.1.0-informational?style=flat-square) +![Version: 0.3.0](https://img.shields.io/badge/Version-0.3.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.1.0](https://img.shields.io/badge/AppVersion-0.1.0-informational?style=flat-square) > [!WARNING] > This chart is currently still in development and breaking changes are expected. @@ -32,7 +32,7 @@ To install the chart with the release name `my-release`: ```console helm install my-release oci://public.cr.seqera.io/charts/portal-web \ - --version 0.2.8 \ + --version 0.3.0 \ --namespace my-namespace \ --create-namespace ``` @@ -59,6 +59,13 @@ When upgrading between versions, please refer to the [CHANGELOG.md](CHANGELOG.md | global.platformServicePort | string | `""` | Seqera Platform Service port. Required when deploying this subchart standalone. When deploying as part of the parent `platform` umbrella chart, this value is inherited from the parent chart's `global` section | | global.agentBackendDomain | string | `"{{ printf \"ai-api.%s\" .Values.global.platformExternalDomain }}"` | Domain where the Agent Backend service listens. Evaluated as a template | | global.portalWebDomain | string | `"{{ printf \"ai.%s\" .Values.global.platformExternalDomain }}"` | Domain where the Portal Web frontend listens. Evaluated as a template | +| global.ingress.enabled | bool | `false` | Enable Ingress for this chart. OR'd with the chart's local `ingress.enabled` so setting this once at the parent enables all subchart Ingresses. | +| global.ingress.path | string | `"/"` | Default path applied to ingress rules when `ingress.path` is not set. AWS ALB users should override to `/*`. | +| global.ingress.defaultPathType | string | `"Prefix"` | Default path type applied to ingress rules when `ingress.defaultPathType` is not set. `Prefix` works for nginx, traefik, AWS ALB, and most modern controllers. | +| global.ingress.ingressClassName | string | `""` | Default ingress class name applied when `ingress.ingressClassName` is not set | +| global.ingress.annotations | object | `{}` | Annotations merged into the Ingress. Local `ingress.annotations` wins on key collision. Evaluated as a template | +| global.ingress.extraLabels | object | `{}` | Extra labels merged into the Ingress. Local `ingress.extraLabels` wins on key collision. Evaluated as a template | +| global.ingress.tls | list | `[]` | TLS entries concatenated with the local `ingress.tls`. Evaluated as a template | | global.imageCredentials | list | `[]` | Optional credentials to log in and fetch images from a private registry. These credentials are shared with all the subcharts automatically | | global.imageCredentialsSecrets | list | `[]` | Optional list of existing Secrets containing image pull credentials to use for pulling images from private registries. These Secrets are shared with all the subcharts automatically | | image.registry | string | `""` | Container image registry | @@ -126,13 +133,13 @@ When upgrading between versions, please refer to the [CHANGELOG.md](CHANGELOG.md | serviceAccount.imagePullSecretNames | list | `[]` | Names of Secrets containing credentials to pull images from registries | | serviceAccount.automountServiceAccountToken | bool | `false` | Automatically mount service account token | | ingress.enabled | bool | `false` | Enable ingress for Portal Web | -| ingress.path | string | `"/"` | Path for the main ingress rule Note: this needs to be set to '/*' to be used with AWS ALB ingress controller | -| ingress.defaultPathType | string | `"ImplementationSpecific"` | Default path type for the Ingress | +| ingress.path | string | `""` | Path for the main ingress rule. When empty, falls back to `global.ingress.path` | +| ingress.defaultPathType | string | `""` | Default path type for the Ingress. When empty, falls back to `global.ingress.defaultPathType` | | ingress.defaultBackend | object | `{}` | Configure the default service for the ingress (evaluated as template) Important: make sure only one defaultBackend is defined across the k8s cluster: if the ingress doesn't reconcile successfully, 'describe ingress ' will report problems | | ingress.extraHosts | list | `[]` | Additional hosts you want to include. Evaluated as a template | | ingress.annotations | object | `{}` | Ingress annotations specific to your load balancer. Evaluated as a template | | ingress.extraLabels | object | `{}` | Additional labels for the ingress object. Evaluated as a template | -| ingress.ingressClassName | string | `""` | Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`) | +| ingress.ingressClassName | string | `""` | Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`). When empty, falls back to `global.ingress.ingressClassName` | | ingress.tls | list | `[]` | TLS configuration. Evaluated as a template | | extraDeploy | list | `[]` | Array of extra objects to deploy with the release | | commonAnnotations | object | `{}` | Annotations to add to all deployed objects | diff --git a/charts/platform/charts/portal-web/templates/_helpers.tpl b/charts/platform/charts/portal-web/templates/_helpers.tpl index 2610858..5400e7a 100644 --- a/charts/platform/charts/portal-web/templates/_helpers.tpl +++ b/charts/platform/charts/portal-web/templates/_helpers.tpl @@ -23,3 +23,12 @@ Create the name of the service account to use {{- define "portal-web.serviceAccountName" -}} {{- default (printf "%s-sa" (include "common.names.fullname" .)) .Values.serviceAccount.name -}} {{- end }} + +{{/* +Return this chart's primary ingress hostname. See parent platform chart's `_helpers.tpl` for +usage notes — `'{{ include "seqera.ingress.host" . }}'` in `global.ingress.annotations` resolves +to each chart's own domain at render time. +*/}} +{{- define "seqera.ingress.host" -}} +{{- tpl .Values.global.portalWebDomain . -}} +{{- end -}} diff --git a/charts/platform/charts/portal-web/templates/ingress.yaml b/charts/platform/charts/portal-web/templates/ingress.yaml index 7de275f..79d0303 100644 --- a/charts/platform/charts/portal-web/templates/ingress.yaml +++ b/charts/platform/charts/portal-web/templates/ingress.yaml @@ -17,12 +17,16 @@ limitations under the License. */}} -{{ if .Values.ingress.enabled }} +{{ if or .Values.ingress.enabled .Values.global.ingress.enabled }} {{- $commonLabels := include "common.labels.standard" (dict "customLabels" .Values.commonLabels "context" $) | fromYaml -}} - {{- $mergedlabels := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.extraLabels $commonLabels) "context" .) -}} + {{- $mergedlabels := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.extraLabels .Values.global.ingress.extraLabels $commonLabels) "context" .) -}} {{- $labels := include "common.tplvalues.render" (dict "value" $mergedlabels "context" $) -}} - {{- $mergedAnnotations := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.annotations .Values.commonAnnotations) "context" .) -}} + {{- $mergedAnnotations := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.annotations .Values.global.ingress.annotations .Values.commonAnnotations) "context" .) -}} {{- $annotations := include "common.tplvalues.render" (dict "value" $mergedAnnotations "context" $) -}} + {{- $defaultPathType := default .Values.global.ingress.defaultPathType .Values.ingress.defaultPathType -}} + {{- $path := default .Values.global.ingress.path .Values.ingress.path -}} + {{- $ingressClassName := default .Values.global.ingress.ingressClassName .Values.ingress.ingressClassName -}} + {{- $tls := concat (default (list) .Values.ingress.tls) (default (list) .Values.global.ingress.tls) -}} apiVersion: {{ include "common.capabilities.ingress.apiVersion" . }} kind: Ingress @@ -36,11 +40,11 @@ spec: defaultBackend: {{- include "seqera.tplvalues.render" (dict "value" . "context" $) | nindent 4 }} {{- end }} - {{- if .Values.ingress.ingressClassName }} - ingressClassName: {{ .Values.ingress.ingressClassName | quote }} + {{- if $ingressClassName }} + ingressClassName: {{ $ingressClassName | quote }} {{- end }} - {{- with .Values.ingress.tls }} + {{- with $tls }} tls: {{- include "common.tplvalues.render" (dict "value" . "context" $) | nindent 4 }} {{- end }} @@ -48,8 +52,8 @@ spec: - host: {{ tpl .Values.global.portalWebDomain . | quote }} http: paths: - - path: {{ .Values.ingress.path | quote }} - pathType: {{ .Values.ingress.defaultPathType }} + - path: {{ $path | quote }} + pathType: {{ $defaultPathType }} backend: service: name: {{ (include "common.names.fullname" .) | quote }} @@ -61,7 +65,7 @@ spec: paths: {{- range .paths }} - path: {{ .path }} - pathType: {{ .pathType | default $.Values.ingress.defaultPathType }} + pathType: {{ .pathType | default $defaultPathType }} backend: service: name: {{ tpl .serviceName $ | quote }} diff --git a/charts/platform/charts/portal-web/tests/__snapshot__/ingress_test.yaml.snap b/charts/platform/charts/portal-web/tests/__snapshot__/ingress_test.yaml.snap index f8abec5..fe5cab5 100644 --- a/charts/platform/charts/portal-web/tests/__snapshot__/ingress_test.yaml.snap +++ b/charts/platform/charts/portal-web/tests/__snapshot__/ingress_test.yaml.snap @@ -23,4 +23,4 @@ should render when ingress.enabled is true: port: number: 80 path: / - pathType: ImplementationSpecific + pathType: Prefix diff --git a/charts/platform/charts/portal-web/values.yaml b/charts/platform/charts/portal-web/values.yaml index 21e0fa1..3a3612b 100644 --- a/charts/platform/charts/portal-web/values.yaml +++ b/charts/platform/charts/portal-web/values.yaml @@ -37,6 +37,29 @@ global: # -- Domain where the Portal Web frontend listens. Evaluated as a template portalWebDomain: '{{ printf "ai.%s" .Values.global.platformExternalDomain }}' + # Ingress defaults shared across the parent chart and all subcharts. Each subchart's local + # `ingress.*` value takes precedence when set; otherwise the global is used. + ingress: + # -- Enable Ingress for this chart. OR'd with the chart's local `ingress.enabled` so + # setting this once at the parent enables all subchart Ingresses. + enabled: false + # -- Default path applied to ingress rules when `ingress.path` is not set. + # AWS ALB users should override to `/*`. + path: "/" + # -- Default path type applied to ingress rules when `ingress.defaultPathType` is not set. + # `Prefix` works for nginx, traefik, AWS ALB, and most modern controllers. + defaultPathType: "Prefix" + # -- Default ingress class name applied when `ingress.ingressClassName` is not set + ingressClassName: "" + # -- Annotations merged into the Ingress. Local `ingress.annotations` wins on key collision. + # Evaluated as a template + annotations: {} + # -- Extra labels merged into the Ingress. Local `ingress.extraLabels` wins on key collision. + # Evaluated as a template + extraLabels: {} + # -- TLS entries concatenated with the local `ingress.tls`. Evaluated as a template + tls: [] + # -- Optional credentials to log in and fetch images from a private registry. These credentials # are shared with all the subcharts automatically imageCredentials: [] @@ -273,12 +296,11 @@ ingress: # -- Enable ingress for Portal Web enabled: false - # -- Path for the main ingress rule - # Note: this needs to be set to '/*' to be used with AWS ALB ingress controller - path: "/" + # -- Path for the main ingress rule. When empty, falls back to `global.ingress.path` + path: "" - # -- Default path type for the Ingress - defaultPathType: "ImplementationSpecific" + # -- Default path type for the Ingress. When empty, falls back to `global.ingress.defaultPathType` + defaultPathType: "" # -- Configure the default service for the ingress (evaluated as template) # Important: make sure only one defaultBackend is defined across the k8s cluster: if the @@ -299,7 +321,8 @@ ingress: annotations: {} # -- Additional labels for the ingress object. Evaluated as a template extraLabels: {} - # -- Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`) + # -- Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`). + # When empty, falls back to `global.ingress.ingressClassName` ingressClassName: "" # -- TLS configuration. Evaluated as a template tls: [] diff --git a/charts/platform/charts/studios/CHANGELOG.md b/charts/platform/charts/studios/CHANGELOG.md index ed573ac..c33b88c 100644 --- a/charts/platform/charts/studios/CHANGELOG.md +++ b/charts/platform/charts/studios/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to this chart will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.3.0] - 2026-05-05 + +- **Enhancement**: allow global configuration of Ingress options. A new `global.ingress` block (`enabled`, `path`, `defaultPathType`, `ingressClassName`, `annotations`, `extraLabels`, `tls`) lets cluster-wide Ingress defaults be set once at the parent and propagate to every subchart, removing the need to repeat controller-wide config per subchart. `enabled` is OR-merged; scalar fields fall back to global when local is unset; `annotations` and `extraLabels` are merged with local winning on key collision; `tls` is concatenated (useful for a single wildcard certificate across all services). +- Add `seqera.ingress.host` template helper in each chart's `_helpers.tpl` returning that chart's primary domain. Lets users write `external-dns.alpha.kubernetes.io/hostname: '{{ include "seqera.ingress.host" . }}'` once in `global.ingress.annotations` and have it resolve to the correct host per chart at render time, without hard-coding hostnames. +- Add `docs/conventions/ingress.md` documenting the Ingress conventions used across charts. + +### Changed + +- Update bitnami/common to 2.39.0 +- **BREAKING**: Default `ingress.defaultPathType` is now `Prefix` (was `ImplementationSpecific`). With the previous default and the chart's default `path: "/"`, routing behavior depended on the ingress controller — NGINX treated it as a prefix match, AWS ALB required `/*` for the same effect, GKE applied its own interpretation. The result was the same chart and values producing different routing across clusters. `Prefix` is part of the Kubernetes Ingress spec and produces consistent prefix-match semantics across NGINX, Traefik, AWS ALB, and most modern controllers, giving users a predictable out-of-the-box experience. Users whose controller still requires `ImplementationSpecific` (e.g. older GKE) can set `global.ingress.defaultPathType: ImplementationSpecific` once at the parent. + ## [1.2.15] - 2026-04-30 ### Changed diff --git a/charts/platform/charts/studios/Chart.lock b/charts/platform/charts/studios/Chart.lock index 5a46ecb..d4e0d45 100644 --- a/charts/platform/charts/studios/Chart.lock +++ b/charts/platform/charts/studios/Chart.lock @@ -1,9 +1,9 @@ dependencies: - name: common repository: oci://registry-1.docker.io/bitnamicharts - version: 2.38.0 + version: 2.39.0 - name: seqera-common repository: file://../../../seqera-common version: 2.1.2 -digest: sha256:4d1319075460f2901b92a5aa964db5992673e65275005b33671b597179c47c83 -generated: "2026-04-30T17:18:31.198120058+02:00" +digest: sha256:3eac4511073f329f614981ce9b91e6cdda035aa007a3aeecd7069958c9ede539 +generated: "2026-05-05T14:19:18.00542025+02:00" diff --git a/charts/platform/charts/studios/Chart.yaml b/charts/platform/charts/studios/Chart.yaml index 2cc80ec..3463702 100644 --- a/charts/platform/charts/studios/Chart.yaml +++ b/charts/platform/charts/studios/Chart.yaml @@ -21,7 +21,7 @@ name: studios description: Studios is a unified platform for interactive analysis sources: - https://github.com/seqeralabs/helm-charts/tree/main/seqera/charts/studios -version: 1.2.15 +version: 1.3.0 appVersion: "0.11.0" maintainers: - name: Seqera Labs diff --git a/charts/platform/charts/studios/README.md b/charts/platform/charts/studios/README.md index 08a9b59..aaeaaed 100644 --- a/charts/platform/charts/studios/README.md +++ b/charts/platform/charts/studios/README.md @@ -2,7 +2,7 @@ Studios is a unified platform for interactive analysis -![Version: 1.2.15](https://img.shields.io/badge/Version-1.2.15-informational?style=flat-square) ![AppVersion: 0.11.0](https://img.shields.io/badge/AppVersion-0.11.0-informational?style=flat-square) +![Version: 1.3.0](https://img.shields.io/badge/Version-1.3.0-informational?style=flat-square) ![AppVersion: 0.11.0](https://img.shields.io/badge/AppVersion-0.11.0-informational?style=flat-square) > [!WARNING] > This chart is currently still in development and breaking changes are expected. @@ -41,7 +41,7 @@ To install the chart with the release name `my-release`: ```console helm install my-release oci://public.cr.seqera.io/charts/studios \ - --version 1.2.15 \ + --version 1.3.0 \ --namespace my-namespace \ --create-namespace ``` @@ -74,6 +74,13 @@ When upgrading between versions, please refer to the [CHANGELOG.md](CHANGELOG.md | global.platformServicePort | string | `""` | Seqera Platform Service port. Required when deploying this subchart standalone. When deploying as part of the parent `platform` umbrella chart, this value is inherited from the parent chart's `global` section | | global.studiosDomain | string | `"{{ printf \"studios.%s\" .Values.global.platformExternalDomain }}"` | Domain where the Studios service listens. Make sure the TLS certificate covers this and its wildcard subdomains. Evaluated as a template | | global.studiosConnectionUrl | string | `"{{ printf \"https://connect.%s\" (tpl .Values.global.studiosDomain $) }}"` | Base URL for Studios connections: can be any value, since each session will use a unique subdomain under `.global.studiosDomain` anyway to connect. Evaluated as a template | +| global.ingress.enabled | bool | `false` | Enable Ingress for this chart. OR'd with the chart's local `ingress.enabled` so setting this once at the parent enables all subchart Ingresses. | +| global.ingress.path | string | `"/"` | Default path applied to ingress rules when `ingress.path` is not set. AWS ALB users should override to `/*`. | +| global.ingress.defaultPathType | string | `"Prefix"` | Default path type applied to ingress rules when `ingress.defaultPathType` is not set. `Prefix` works for nginx, traefik, AWS ALB, and most modern controllers. | +| global.ingress.ingressClassName | string | `""` | Default ingress class name applied when `ingress.ingressClassName` is not set | +| global.ingress.annotations | object | `{}` | Annotations merged into the Ingress. Local `ingress.annotations` wins on key collision. Evaluated as a template | +| global.ingress.extraLabels | object | `{}` | Extra labels merged into the Ingress. Local `ingress.extraLabels` wins on key collision. Evaluated as a template | +| global.ingress.tls | list | `[]` | TLS entries concatenated with the local `ingress.tls`. Evaluated as a template | | global.imageCredentials | list | `[]` | Optional credentials to log in and fetch images from a private registry. These credentials are shared with all the subcharts automatically | | global.imageCredentialsSecrets | list | `[]` | Optional list of existing Secrets containing image pull credentials to use for pulling images from private registries. These Secrets are shared with all the subcharts automatically | | redis.host | string | `""` | Redis hostname | @@ -178,13 +185,13 @@ When upgrading between versions, please refer to the [CHANGELOG.md](CHANGELOG.md | serviceAccount.imagePullSecretNames | list | `[]` | Names of Secrets containing credentials to pull images from registries | | serviceAccount.automountServiceAccountToken | bool | `true` | Automount service account token when the ServiceAccount is generated | | ingress.enabled | bool | `false` | Enable ingress for Studios Proxy | -| ingress.path | string | `"/"` | Path for the main ingress rule Note: this needs to be set to '/*' to be used with AWS ALB ingress controller | -| ingress.defaultPathType | string | `"ImplementationSpecific"` | Default path type for the Ingress | +| ingress.path | string | `""` | Path for the main ingress rule. When empty, falls back to `global.ingress.path` | +| ingress.defaultPathType | string | `""` | Default path type for the Ingress. When empty, falls back to `global.ingress.defaultPathType` | | ingress.defaultBackend | object | `{}` | Configure the default service for the ingress (evaluated as template) Important: make sure only one defaultBackend is defined across the k8s cluster: if the ingress doesn't reconcile successfully, 'describe ingress ' will report problems | | ingress.extraHosts | list | `[]` | Additional hosts you want to include. Evaluated as a template | | ingress.annotations | object | `{}` | Ingress annotations specific to your load balancer. Evaluated as a template | | ingress.extraLabels | object | `{}` | Additional labels for the ingress object. Evaluated as a template | -| ingress.ingressClassName | string | `""` | Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`) | +| ingress.ingressClassName | string | `""` | Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`). When empty, falls back to `global.ingress.ingressClassName` | | ingress.tls | list | `[]` | TLS configuration. Evaluated as a template | | extraDeploy | list | `[]` | Array of extra objects to deploy with the release | | commonAnnotations | object | `{}` | Annotations to add to all deployed objects | diff --git a/charts/platform/charts/studios/templates/_helpers.tpl b/charts/platform/charts/studios/templates/_helpers.tpl index a3d27b1..137c03a 100644 --- a/charts/platform/charts/studios/templates/_helpers.tpl +++ b/charts/platform/charts/studios/templates/_helpers.tpl @@ -104,3 +104,13 @@ key: {{ include "studios.oidcToken.secretKey" $studiosContext }} {{- printf "CONNECT_OIDC_CLIENT_REGISTRATION_TOKEN" -}} {{- end -}} {{- end -}} + +{{/* +Return this chart's primary ingress hostname. See parent platform chart's `_helpers.tpl` for +usage notes — `'{{ include "seqera.ingress.host" . }}'` in `global.ingress.annotations` resolves +to each chart's own domain at render time. Returns the bare studios domain — users wanting the +wildcard form can write `'*.{{ include "seqera.ingress.host" . }}'`. +*/}} +{{- define "seqera.ingress.host" -}} +{{- tpl .Values.global.studiosDomain . -}} +{{- end -}} diff --git a/charts/platform/charts/studios/templates/ingress.yaml b/charts/platform/charts/studios/templates/ingress.yaml index aa20d17..c17ae3c 100644 --- a/charts/platform/charts/studios/templates/ingress.yaml +++ b/charts/platform/charts/studios/templates/ingress.yaml @@ -17,12 +17,16 @@ limitations under the License. */}} -{{ if .Values.ingress.enabled }} +{{ if or .Values.ingress.enabled .Values.global.ingress.enabled }} {{- $commonLabels := include "common.labels.standard" (dict "customLabels" .Values.commonLabels "context" $) | fromYaml -}} - {{- $mergedlabels := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.extraLabels $commonLabels) "context" .) -}} + {{- $mergedlabels := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.extraLabels .Values.global.ingress.extraLabels $commonLabels) "context" .) -}} {{- $labels := include "common.tplvalues.render" (dict "value" $mergedlabels "context" $) -}} - {{- $mergedAnnotations := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.annotations .Values.commonAnnotations) "context" .) -}} + {{- $mergedAnnotations := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.annotations .Values.global.ingress.annotations .Values.commonAnnotations) "context" .) -}} {{- $annotations := include "common.tplvalues.render" (dict "value" $mergedAnnotations "context" $) -}} + {{- $defaultPathType := default .Values.global.ingress.defaultPathType .Values.ingress.defaultPathType -}} + {{- $path := default .Values.global.ingress.path .Values.ingress.path -}} + {{- $ingressClassName := default .Values.global.ingress.ingressClassName .Values.ingress.ingressClassName -}} + {{- $tls := concat (default (list) .Values.ingress.tls) (default (list) .Values.global.ingress.tls) -}} apiVersion: {{ include "common.capabilities.ingress.apiVersion" . }} kind: Ingress @@ -36,11 +40,11 @@ spec: defaultBackend: {{- include "seqera.tplvalues.render" (dict "value" . "context" $) | nindent 4 }} {{- end }} - {{- if .Values.ingress.ingressClassName }} - ingressClassName: {{ .Values.ingress.ingressClassName | quote }} + {{- if $ingressClassName }} + ingressClassName: {{ $ingressClassName | quote }} {{- end }} - {{- with .Values.ingress.tls }} + {{- with $tls }} tls: {{- include "common.tplvalues.render" (dict "value" . "context" $) | nindent 4 }} {{- end }} @@ -49,8 +53,8 @@ spec: - host: '{{ printf "*.%s" (tpl .Values.global.studiosDomain $) }}' http: paths: - - path: {{ .Values.ingress.path | quote }} - pathType: {{ .Values.ingress.defaultPathType }} + - path: {{ $path | quote }} + pathType: {{ $defaultPathType }} backend: service: name: {{ printf "%s-proxy" (include "common.names.fullname" .) | quote }} @@ -63,7 +67,7 @@ spec: paths: {{- range .paths }} - path: {{ .path }} - pathType: {{ .pathType | default $.Values.ingress.defaultPathType }} + pathType: {{ .pathType | default $defaultPathType }} backend: service: name: {{ tpl .serviceName $ | quote }} diff --git a/charts/platform/charts/studios/tests/__snapshot__/ingress_test.yaml.snap b/charts/platform/charts/studios/tests/__snapshot__/ingress_test.yaml.snap index a0b93a3..0d769c5 100644 --- a/charts/platform/charts/studios/tests/__snapshot__/ingress_test.yaml.snap +++ b/charts/platform/charts/studios/tests/__snapshot__/ingress_test.yaml.snap @@ -9,7 +9,7 @@ should configure extra hosts with multiple paths: port: number: 80 path: / - pathType: ImplementationSpecific + pathType: Prefix - host: api.example.com http: paths: @@ -57,7 +57,7 @@ should render complete ingress with all features: port: number: 80 path: / - pathType: ImplementationSpecific + pathType: Prefix - host: admin.example.com http: paths: @@ -97,4 +97,4 @@ should render ingress when enabled with defaults: port: number: 80 path: / - pathType: ImplementationSpecific + pathType: Prefix diff --git a/charts/platform/charts/studios/tests/ingress_test.yaml b/charts/platform/charts/studios/tests/ingress_test.yaml index 76ba3f0..c4b1131 100644 --- a/charts/platform/charts/studios/tests/ingress_test.yaml +++ b/charts/platform/charts/studios/tests/ingress_test.yaml @@ -112,7 +112,7 @@ tests: value: "*.studios.example.com" - equal: path: spec.rules[0].http.paths[0].pathType - value: "ImplementationSpecific" + value: "Prefix" - equal: path: spec.rules[0].http.paths[0].path value: "/" @@ -467,7 +467,7 @@ tests: asserts: - equal: path: spec.rules[1].http.paths[0].pathType - value: "ImplementationSpecific" + value: "Prefix" - it: should handle proxy.service.http.port as integer set: diff --git a/charts/platform/charts/studios/values.yaml b/charts/platform/charts/studios/values.yaml index e25a461..fc391df 100644 --- a/charts/platform/charts/studios/values.yaml +++ b/charts/platform/charts/studios/values.yaml @@ -38,6 +38,29 @@ global: # subdomain under `.global.studiosDomain` anyway to connect. Evaluated as a template studiosConnectionUrl: '{{ printf "https://connect.%s" (tpl .Values.global.studiosDomain $) }}' + # Ingress defaults shared across the parent chart and all subcharts. Each subchart's local + # `ingress.*` value takes precedence when set; otherwise the global is used. + ingress: + # -- Enable Ingress for this chart. OR'd with the chart's local `ingress.enabled` so + # setting this once at the parent enables all subchart Ingresses. + enabled: false + # -- Default path applied to ingress rules when `ingress.path` is not set. + # AWS ALB users should override to `/*`. + path: "/" + # -- Default path type applied to ingress rules when `ingress.defaultPathType` is not set. + # `Prefix` works for nginx, traefik, AWS ALB, and most modern controllers. + defaultPathType: "Prefix" + # -- Default ingress class name applied when `ingress.ingressClassName` is not set + ingressClassName: "" + # -- Annotations merged into the Ingress. Local `ingress.annotations` wins on key collision. + # Evaluated as a template + annotations: {} + # -- Extra labels merged into the Ingress. Local `ingress.extraLabels` wins on key collision. + # Evaluated as a template + extraLabels: {} + # -- TLS entries concatenated with the local `ingress.tls`. Evaluated as a template + tls: [] + # -- Optional credentials to log in and fetch images from a private registry. These credentials # are shared with all the subcharts automatically imageCredentials: [] @@ -462,12 +485,11 @@ ingress: # -- Enable ingress for Studios Proxy enabled: false - # -- Path for the main ingress rule - # Note: this needs to be set to '/*' to be used with AWS ALB ingress controller - path: "/" + # -- Path for the main ingress rule. When empty, falls back to `global.ingress.path` + path: "" - # -- Default path type for the Ingress - defaultPathType: "ImplementationSpecific" + # -- Default path type for the Ingress. When empty, falls back to `global.ingress.defaultPathType` + defaultPathType: "" # -- Configure the default service for the ingress (evaluated as template) # Important: make sure only one defaultBackend is defined across the k8s cluster: if the @@ -499,7 +521,8 @@ ingress: annotations: {} # -- Additional labels for the ingress object. Evaluated as a template extraLabels: {} - # -- Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`) + # -- Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`). + # When empty, falls back to `global.ingress.ingressClassName` ingressClassName: "" # -- TLS configuration. Evaluated as a template tls: [] diff --git a/charts/platform/charts/wave/CHANGELOG.md b/charts/platform/charts/wave/CHANGELOG.md index 1a71e4c..df4ce88 100644 --- a/charts/platform/charts/wave/CHANGELOG.md +++ b/charts/platform/charts/wave/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to this chart will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.2.0] - 2026-05-05 + +- **Enhancement**: allow global configuration of Ingress options. A new `global.ingress` block (`enabled`, `path`, `defaultPathType`, `ingressClassName`, `annotations`, `extraLabels`, `tls`) lets cluster-wide Ingress defaults be set once at the parent and propagate to every subchart, removing the need to repeat controller-wide config per subchart. `enabled` is OR-merged; scalar fields fall back to global when local is unset; `annotations` and `extraLabels` are merged with local winning on key collision; `tls` is concatenated (useful for a single wildcard certificate across all services). +- Add `seqera.ingress.host` template helper in each chart's `_helpers.tpl` returning that chart's primary domain. Lets users write `external-dns.alpha.kubernetes.io/hostname: '{{ include "seqera.ingress.host" . }}'` once in `global.ingress.annotations` and have it resolve to the correct host per chart at render time, without hard-coding hostnames. +- Add `docs/conventions/ingress.md` documenting the Ingress conventions used across charts. + +### Changed + +- Update bitnami/common to 2.39.0 +- **BREAKING**: Default `ingress.defaultPathType` is now `Prefix` (was `ImplementationSpecific`). With the previous default and the chart's default `path: "/"`, routing behavior depended on the ingress controller — NGINX treated it as a prefix match, AWS ALB required `/*` for the same effect, GKE applied its own interpretation. The result was the same chart and values producing different routing across clusters. `Prefix` is part of the Kubernetes Ingress spec and produces consistent prefix-match semantics across NGINX, Traefik, AWS ALB, and most modern controllers, giving users a predictable out-of-the-box experience. Users whose controller still requires `ImplementationSpecific` (e.g. older GKE) can set `global.ingress.defaultPathType: ImplementationSpecific` once at the parent. + ## [0.1.2] - 2026-04-30 ### Changed diff --git a/charts/platform/charts/wave/Chart.lock b/charts/platform/charts/wave/Chart.lock index 17a9a4a..e032946 100644 --- a/charts/platform/charts/wave/Chart.lock +++ b/charts/platform/charts/wave/Chart.lock @@ -1,9 +1,9 @@ dependencies: - name: common repository: oci://registry-1.docker.io/bitnamicharts - version: 2.38.0 + version: 2.39.0 - name: seqera-common repository: file://../../../seqera-common version: 2.1.2 -digest: sha256:4d1319075460f2901b92a5aa964db5992673e65275005b33671b597179c47c83 -generated: "2026-04-30T17:18:37.887535625+02:00" +digest: sha256:3eac4511073f329f614981ce9b91e6cdda035aa007a3aeecd7069958c9ede539 +generated: "2026-05-05T14:19:45.779728648+02:00" diff --git a/charts/platform/charts/wave/Chart.yaml b/charts/platform/charts/wave/Chart.yaml index 79baf7f..df2f689 100644 --- a/charts/platform/charts/wave/Chart.yaml +++ b/charts/platform/charts/wave/Chart.yaml @@ -19,7 +19,7 @@ apiVersion: v2 name: wave description: Wave type: application -version: 0.1.2 +version: 0.2.0 appVersion: "v1.32.4" maintainers: - name: Seqera Labs diff --git a/charts/platform/charts/wave/README.md b/charts/platform/charts/wave/README.md index c7e5236..4b49174 100644 --- a/charts/platform/charts/wave/README.md +++ b/charts/platform/charts/wave/README.md @@ -2,7 +2,7 @@ Wave -![Version: 0.1.2](https://img.shields.io/badge/Version-0.1.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v1.32.4](https://img.shields.io/badge/AppVersion-v1.32.4-informational?style=flat-square) +![Version: 0.2.0](https://img.shields.io/badge/Version-0.2.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v1.32.4](https://img.shields.io/badge/AppVersion-v1.32.4-informational?style=flat-square) > [!WARNING] > This chart is currently still in development and breaking changes are expected. @@ -35,7 +35,7 @@ To install the chart with the release name `my-release`: ```console helm install my-release oci://public.cr.seqera.io/charts/wave \ - --version 0.1.2 \ + --version 0.2.0 \ --namespace my-namespace \ --create-namespace ``` @@ -59,6 +59,13 @@ When upgrading between versions, please refer to the [CHANGELOG.md](CHANGELOG.md |-----|------|---------|-------------| | global.platformExternalDomain | string | `"example.com"` | Domain where Seqera Platform listens | | global.waveDomain | string | `"{{ printf \"wave.%s\" .Values.global.platformExternalDomain }}"` | Domain where Wave listens. Evaluated as a template | +| global.ingress.enabled | bool | `false` | Enable Ingress for this chart. OR'd with the chart's local `ingress.enabled` so setting this once at the parent enables all subchart Ingresses. | +| global.ingress.path | string | `"/"` | Default path applied to ingress rules when `ingress.path` is not set. AWS ALB users should override to `/*`. | +| global.ingress.defaultPathType | string | `"Prefix"` | Default path type applied to ingress rules when `ingress.defaultPathType` is not set. `Prefix` works for nginx, traefik, AWS ALB, and most modern controllers. | +| global.ingress.ingressClassName | string | `""` | Default ingress class name applied when `ingress.ingressClassName` is not set | +| global.ingress.annotations | object | `{}` | Annotations merged into the Ingress. Local `ingress.annotations` wins on key collision. Evaluated as a template | +| global.ingress.extraLabels | object | `{}` | Extra labels merged into the Ingress. Local `ingress.extraLabels` wins on key collision. Evaluated as a template | +| global.ingress.tls | list | `[]` | TLS entries concatenated with the local `ingress.tls`. Evaluated as a template | | global.imageCredentials | list | `[]` | Optional credentials to log in and fetch images from a private registry. These credentials are shared with all the subcharts automatically | | global.imageCredentialsSecrets | list | `[]` | Optional list of existing Secrets containing image pull credentials to use for pulling images from private registries. These Secrets are shared with all the subcharts automatically | | micronautEnvironments | list | `["postgres","redis","lite"]` | List of Micronaut environments to enable | @@ -169,13 +176,13 @@ When upgrading between versions, please refer to the [CHANGELOG.md](CHANGELOG.md | serviceAccount.imagePullSecretNames | list | `[]` | Names of Secrets containing credentials to pull images from registries | | serviceAccount.automountServiceAccountToken | bool | `true` | Automatically mount service account token | | ingress.enabled | bool | `false` | Enable ingress for Wave | -| ingress.path | string | `"/"` | Path for the main ingress rule Note: this needs to be set to '/*' to be used with AWS ALB ingress controller | -| ingress.defaultPathType | string | `"ImplementationSpecific"` | Default path type for the Ingress | +| ingress.path | string | `""` | Path for the main ingress rule. When empty, falls back to `global.ingress.path` | +| ingress.defaultPathType | string | `""` | Default path type for the Ingress. When empty, falls back to `global.ingress.defaultPathType` | | ingress.defaultBackend | object | `{}` | Configure the default service for the ingress (evaluated as template) Important: make sure only one defaultBackend is defined across the k8s cluster: if the ingress doesn't reconcile successfully, 'describe ingress ' will report problems | | ingress.extraHosts | list | `[]` | Additional hosts you want to include. Evaluated as a template | | ingress.annotations | object | `{}` | Ingress annotations specific to your load balancer. Evaluated as a template | | ingress.extraLabels | object | `{}` | Additional labels for the ingress object. Evaluated as a template | -| ingress.ingressClassName | string | `""` | Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`) | +| ingress.ingressClassName | string | `""` | Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`). When empty, falls back to `global.ingress.ingressClassName` | | ingress.tls | list | `[]` | TLS configuration. Evaluated as a template | | extraDeploy | list | `[]` | Array of extra objects to deploy with the release | | commonAnnotations | object | `{}` | Annotations to add to all deployed objects | diff --git a/charts/platform/charts/wave/templates/_helpers.tpl b/charts/platform/charts/wave/templates/_helpers.tpl index 98fa770..f8b1d44 100644 --- a/charts/platform/charts/wave/templates/_helpers.tpl +++ b/charts/platform/charts/wave/templates/_helpers.tpl @@ -92,3 +92,12 @@ Build the Wave micronaut envs list: always ensure required environments are pres {{- printf "WAVE_REDIS_PASSWORD" -}} {{- end -}} {{- end -}} + +{{/* +Return this chart's primary ingress hostname. See parent platform chart's `_helpers.tpl` for +usage notes — `'{{ include "seqera.ingress.host" . }}'` in `global.ingress.annotations` resolves +to each chart's own domain at render time. +*/}} +{{- define "seqera.ingress.host" -}} +{{- tpl .Values.global.waveDomain . -}} +{{- end -}} diff --git a/charts/platform/charts/wave/templates/ingress.yaml b/charts/platform/charts/wave/templates/ingress.yaml index 9e274d0..9de4d78 100644 --- a/charts/platform/charts/wave/templates/ingress.yaml +++ b/charts/platform/charts/wave/templates/ingress.yaml @@ -17,12 +17,16 @@ limitations under the License. */}} -{{ if .Values.ingress.enabled }} +{{ if or .Values.ingress.enabled .Values.global.ingress.enabled }} {{- $commonLabels := include "common.labels.standard" (dict "customLabels" .Values.commonLabels "context" $) | fromYaml -}} - {{- $mergedlabels := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.extraLabels $commonLabels) "context" .) -}} + {{- $mergedlabels := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.extraLabels .Values.global.ingress.extraLabels $commonLabels) "context" .) -}} {{- $labels := include "common.tplvalues.render" (dict "value" $mergedlabels "context" $) -}} - {{- $mergedAnnotations := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.annotations .Values.commonAnnotations) "context" .) -}} + {{- $mergedAnnotations := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.annotations .Values.global.ingress.annotations .Values.commonAnnotations) "context" .) -}} {{- $annotations := include "common.tplvalues.render" (dict "value" $mergedAnnotations "context" $) -}} + {{- $defaultPathType := default .Values.global.ingress.defaultPathType .Values.ingress.defaultPathType -}} + {{- $path := default .Values.global.ingress.path .Values.ingress.path -}} + {{- $ingressClassName := default .Values.global.ingress.ingressClassName .Values.ingress.ingressClassName -}} + {{- $tls := concat (default (list) .Values.ingress.tls) (default (list) .Values.global.ingress.tls) -}} apiVersion: {{ include "common.capabilities.ingress.apiVersion" . }} kind: Ingress @@ -36,11 +40,11 @@ spec: defaultBackend: {{- include "seqera.tplvalues.render" (dict "value" . "context" $) | nindent 4 }} {{- end }} - {{- if .Values.ingress.ingressClassName }} - ingressClassName: {{ .Values.ingress.ingressClassName | quote }} + {{- if $ingressClassName }} + ingressClassName: {{ $ingressClassName | quote }} {{- end }} - {{- with .Values.ingress.tls }} + {{- with $tls }} tls: {{- include "common.tplvalues.render" (dict "value" . "context" $) | nindent 4 }} {{- end }} @@ -49,8 +53,8 @@ spec: - host: {{ tpl .Values.global.waveDomain . | quote }} http: paths: - - path: {{ .Values.ingress.path | quote }} - pathType: {{ .Values.ingress.defaultPathType }} + - path: {{ $path | quote }} + pathType: {{ $defaultPathType }} backend: service: name: {{ (include "common.names.fullname" .) | quote }} @@ -62,7 +66,7 @@ spec: paths: {{- range .paths }} - path: {{ .path }} - pathType: {{ .pathType | default $.Values.ingress.defaultPathType }} + pathType: {{ .pathType | default $defaultPathType }} backend: service: name: {{ tpl .serviceName $ | quote }} diff --git a/charts/platform/charts/wave/tests/__snapshot__/ingress_test.yaml.snap b/charts/platform/charts/wave/tests/__snapshot__/ingress_test.yaml.snap index 2476cca..edfae37 100644 --- a/charts/platform/charts/wave/tests/__snapshot__/ingress_test.yaml.snap +++ b/charts/platform/charts/wave/tests/__snapshot__/ingress_test.yaml.snap @@ -23,4 +23,4 @@ should render when ingress.enabled is true: port: number: 9090 path: / - pathType: ImplementationSpecific + pathType: Prefix diff --git a/charts/platform/charts/wave/values.yaml b/charts/platform/charts/wave/values.yaml index 81eb060..0db9017 100644 --- a/charts/platform/charts/wave/values.yaml +++ b/charts/platform/charts/wave/values.yaml @@ -24,6 +24,29 @@ global: # -- Domain where Wave listens. Evaluated as a template waveDomain: '{{ printf "wave.%s" .Values.global.platformExternalDomain }}' + # Ingress defaults shared across the parent chart and all subcharts. Each subchart's local + # `ingress.*` value takes precedence when set; otherwise the global is used. + ingress: + # -- Enable Ingress for this chart. OR'd with the chart's local `ingress.enabled` so + # setting this once at the parent enables all subchart Ingresses. + enabled: false + # -- Default path applied to ingress rules when `ingress.path` is not set. + # AWS ALB users should override to `/*`. + path: "/" + # -- Default path type applied to ingress rules when `ingress.defaultPathType` is not set. + # `Prefix` works for nginx, traefik, AWS ALB, and most modern controllers. + defaultPathType: "Prefix" + # -- Default ingress class name applied when `ingress.ingressClassName` is not set + ingressClassName: "" + # -- Annotations merged into the Ingress. Local `ingress.annotations` wins on key collision. + # Evaluated as a template + annotations: {} + # -- Extra labels merged into the Ingress. Local `ingress.extraLabels` wins on key collision. + # Evaluated as a template + extraLabels: {} + # -- TLS entries concatenated with the local `ingress.tls`. Evaluated as a template + tls: [] + # -- Optional credentials to log in and fetch images from a private registry. These credentials # are shared with all the subcharts automatically imageCredentials: [] @@ -398,12 +421,11 @@ ingress: # -- Enable ingress for Wave enabled: false - # -- Path for the main ingress rule - # Note: this needs to be set to '/*' to be used with AWS ALB ingress controller - path: "/" + # -- Path for the main ingress rule. When empty, falls back to `global.ingress.path` + path: "" - # -- Default path type for the Ingress - defaultPathType: "ImplementationSpecific" + # -- Default path type for the Ingress. When empty, falls back to `global.ingress.defaultPathType` + defaultPathType: "" # -- Configure the default service for the ingress (evaluated as template) # Important: make sure only one defaultBackend is defined across the k8s cluster: if the @@ -435,7 +457,8 @@ ingress: annotations: {} # -- Additional labels for the ingress object. Evaluated as a template extraLabels: {} - # -- Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`) + # -- Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`). + # When empty, falls back to `global.ingress.ingressClassName` ingressClassName: "" # -- TLS configuration. Evaluated as a template tls: [] diff --git a/charts/platform/examples/ingress-configurations/README.md b/charts/platform/examples/ingress-configurations/README.md index cedad7c..640b180 100644 --- a/charts/platform/examples/ingress-configurations/README.md +++ b/charts/platform/examples/ingress-configurations/README.md @@ -188,13 +188,41 @@ ingress: secretName: wildcard-tls ``` +### Resolving a chart's hostname in annotations: `seqera.ingress.host` + +Each chart ships a `seqera.ingress.host` template helper that returns its own +primary domain (`global.platformExternalDomain` for platform, +`global.studiosDomain` for studios, etc.). Drop it into +`global.ingress.annotations` once and every chart's Ingress renders with the +right hostname — useful for things like external-dns where the annotation +value needs to match the actual host: + +```yaml +global: + ingress: + annotations: + external-dns.alpha.kubernetes.io/hostname: '{{ include "seqera.ingress.host" . }}' +``` + +For wildcard forms (e.g. studios uses `*.studios.example.com`), compose the +helper: + +```yaml +external-dns.alpha.kubernetes.io/hostname: '*.{{ include "seqera.ingress.host" . }}' +``` + ### Path Types -- `ImplementationSpecific` (default): Ingress controller decides interpretation -- `Prefix`: Matches URL path prefix +- `Prefix` (default): Matches URL path prefix — works for nginx, traefik, AWS ALB, and most modern controllers +- `ImplementationSpecific`: Ingress controller decides interpretation - `Exact`: Exact path match only -For AWS ALB, always use path `/*` and `Prefix` type. +For AWS ALB, set `path: "/*"` (path syntax required by ALB). The default `Prefix` +path type works fine. + +The default can be set once cluster-wide via `global.ingress.defaultPathType`, +which propagates to every chart's Ingress. A chart's local +`ingress.defaultPathType` still wins when set. ### Common Annotations diff --git a/charts/platform/examples/ingress-configurations/aws-alb.yaml b/charts/platform/examples/ingress-configurations/aws-alb.yaml index 30c21e4..93fcc43 100644 --- a/charts/platform/examples/ingress-configurations/aws-alb.yaml +++ b/charts/platform/examples/ingress-configurations/aws-alb.yaml @@ -98,6 +98,3 @@ ingress: # TLS handled by ACM certificate specified in annotations # Don't specify tls section when using ACM certificates tls: [] - - # Use Prefix path type (required for ALB with /*) - defaultPathType: Prefix diff --git a/charts/platform/examples/ingress-configurations/extra-hosts.yaml b/charts/platform/examples/ingress-configurations/extra-hosts.yaml index 2826b17..a3bf8e1 100644 --- a/charts/platform/examples/ingress-configurations/extra-hosts.yaml +++ b/charts/platform/examples/ingress-configurations/extra-hosts.yaml @@ -88,5 +88,3 @@ ingress: - hosts: - user-data.example.com secretName: platform-content-tls - - defaultPathType: Prefix diff --git a/charts/platform/examples/ingress-configurations/nginx-cert-manager.yaml b/charts/platform/examples/ingress-configurations/nginx-cert-manager.yaml index 03e7e98..c8d7ae5 100644 --- a/charts/platform/examples/ingress-configurations/nginx-cert-manager.yaml +++ b/charts/platform/examples/ingress-configurations/nginx-cert-manager.yaml @@ -15,6 +15,17 @@ global: # Separate domain for user-generated content (XSS protection) contentDomain: user-data.example.com + # Cluster-wide ingress defaults. Set once here and every chart's Ingress (platform plus any + # enabled subcharts like studios, wave, mcp) picks them up. Per-chart overrides under + # `.ingress.*` still win when set. + ingress: + ingressClassName: nginx + annotations: + # `seqera.ingress.host` resolves to each chart's own primary domain, so this single + # annotation registers the correct host per service (platform.example.com, + # studios.example.com, wave.example.com, …) without hard-coding hostnames. + external-dns.alpha.kubernetes.io/hostname: '{{ include "seqera.ingress.host" . }}' + platformDatabase: host: mysql.example.com database: platform @@ -37,8 +48,7 @@ backend: ingress: enabled: true - # NGINX ingress class - ingressClassName: nginx + # ingressClassName is set via global.ingress.ingressClassName above so subcharts inherit it. annotations: # cert-manager annotations for automatic TLS certificate provisioning @@ -66,6 +76,3 @@ ingress: - hosts: - user-data.example.com secretName: platform-content-tls - - # Default path type - defaultPathType: Prefix diff --git a/charts/platform/examples/ingress-configurations/traefik.yaml b/charts/platform/examples/ingress-configurations/traefik.yaml index dd0a939..f7579f4 100644 --- a/charts/platform/examples/ingress-configurations/traefik.yaml +++ b/charts/platform/examples/ingress-configurations/traefik.yaml @@ -65,8 +65,6 @@ ingress: - user-data.example.com secretName: platform-content-tls - defaultPathType: Prefix - # Note: For advanced Traefik configurations, consider using IngressRoute CRD # and add it via extraDeploy. Example: # diff --git a/charts/platform/examples/ingress-configurations/wildcard-tls.yaml b/charts/platform/examples/ingress-configurations/wildcard-tls.yaml index d8bd24b..bf158f4 100644 --- a/charts/platform/examples/ingress-configurations/wildcard-tls.yaml +++ b/charts/platform/examples/ingress-configurations/wildcard-tls.yaml @@ -76,5 +76,3 @@ ingress: - platform.example.com - user-data.example.com secretName: wildcard-tls # Single wildcard certificate - - defaultPathType: Prefix diff --git a/charts/platform/templates/_helpers.tpl b/charts/platform/templates/_helpers.tpl index ed89cb9..6822a70 100644 --- a/charts/platform/templates/_helpers.tpl +++ b/charts/platform/templates/_helpers.tpl @@ -490,3 +490,12 @@ true false {{- end -}} {{- end -}} + +{{/* +Return this chart's primary ingress hostname. Use `'{{ include "seqera.ingress.host" . }}'` in +`global.ingress.annotations` (e.g. `external-dns.alpha.kubernetes.io/hostname`) and the correct +host resolves per chart at render time without hard-coding it. +*/}} +{{- define "seqera.ingress.host" -}} +{{- tpl .Values.global.platformExternalDomain . -}} +{{- end -}} diff --git a/charts/platform/templates/ingress.yaml b/charts/platform/templates/ingress.yaml index 8a8e4c9..a2dbdb1 100644 --- a/charts/platform/templates/ingress.yaml +++ b/charts/platform/templates/ingress.yaml @@ -17,12 +17,16 @@ limitations under the License. */}} -{{ if .Values.ingress.enabled }} +{{ if or .Values.ingress.enabled .Values.global.ingress.enabled }} {{- $commonLabels := include "common.labels.standard" (dict "customLabels" .Values.commonLabels "context" $) | fromYaml -}} - {{- $mergedlabels := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.extraLabels $commonLabels) "context" .) -}} + {{- $mergedlabels := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.extraLabels .Values.global.ingress.extraLabels $commonLabels) "context" .) -}} {{- $labels := include "common.tplvalues.render" (dict "value" $mergedlabels "context" $) -}} - {{- $mergedAnnotations := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.annotations .Values.commonAnnotations) "context" .) -}} + {{- $mergedAnnotations := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.annotations .Values.global.ingress.annotations .Values.commonAnnotations) "context" .) -}} {{- $annotations := include "common.tplvalues.render" (dict "value" $mergedAnnotations "context" $) -}} + {{- $defaultPathType := default .Values.global.ingress.defaultPathType .Values.ingress.defaultPathType -}} + {{- $path := default .Values.global.ingress.path .Values.ingress.path -}} + {{- $ingressClassName := default .Values.global.ingress.ingressClassName .Values.ingress.ingressClassName -}} + {{- $tls := concat (default (list) .Values.ingress.tls) (default (list) .Values.global.ingress.tls) -}} apiVersion: {{ include "common.capabilities.ingress.apiVersion" . }} kind: Ingress @@ -36,11 +40,11 @@ spec: defaultBackend: {{- include "seqera.tplvalues.render" (dict "value" . "context" $) | nindent 4 }} {{- end }} - {{- if .Values.ingress.ingressClassName }} - ingressClassName: {{ .Values.ingress.ingressClassName | quote }} + {{- if $ingressClassName }} + ingressClassName: {{ $ingressClassName | quote }} {{- end }} - {{- with .Values.ingress.tls }} + {{- with $tls }} tls: {{- include "common.tplvalues.render" (dict "value" . "context" $) | nindent 4 }} {{- end }} @@ -49,8 +53,8 @@ spec: - host: {{ tpl .Values.global.platformExternalDomain $ | quote }} http: paths: - - path: {{ .Values.ingress.path | quote }} - pathType: {{ .Values.ingress.defaultPathType }} + - path: {{ $path | quote }} + pathType: {{ $defaultPathType }} backend: service: name: {{ printf "%s-frontend" (include "common.names.fullname" .) | quote }} @@ -63,7 +67,7 @@ spec: http: paths: - path: {{ .Values.ingress.contentPath | quote }} - pathType: {{ .Values.ingress.defaultPathType }} + pathType: {{ $defaultPathType }} backend: service: name: {{ printf "%s-frontend" (include "common.names.fullname" .) | quote }} @@ -77,7 +81,7 @@ spec: paths: {{- range .paths }} - path: {{ .path }} - pathType: {{ .pathType | default $.Values.ingress.defaultPathType }} + pathType: {{ .pathType | default $defaultPathType }} backend: service: name: {{ tpl .serviceName $ | quote }} diff --git a/charts/platform/tests/__snapshot__/ingress_test.yaml.snap b/charts/platform/tests/__snapshot__/ingress_test.yaml.snap index dc8ab42..f502137 100644 --- a/charts/platform/tests/__snapshot__/ingress_test.yaml.snap +++ b/charts/platform/tests/__snapshot__/ingress_test.yaml.snap @@ -9,7 +9,7 @@ should configure extra hosts with multiple paths: port: number: 80 path: / - pathType: ImplementationSpecific + pathType: Prefix - host: user-data.example.com http: paths: @@ -19,7 +19,7 @@ should configure extra hosts with multiple paths: port: number: 80 path: / - pathType: ImplementationSpecific + pathType: Prefix - host: api.example.com http: paths: diff --git a/charts/platform/tests/ingress_test.yaml b/charts/platform/tests/ingress_test.yaml index 8799724..22c6f98 100644 --- a/charts/platform/tests/ingress_test.yaml +++ b/charts/platform/tests/ingress_test.yaml @@ -31,6 +31,18 @@ tests: asserts: - hasDocuments: count: 0 +- it: should render ingress when global.ingress.enabled is true even if local is false + set: + ingress: + enabled: false + global: + ingress: + enabled: true + asserts: + - hasDocuments: + count: 1 + - isKind: + of: Ingress - it: should set correct hostname set: ingress: @@ -97,7 +109,7 @@ tests: port: number: 80 path: / - pathType: ImplementationSpecific + pathType: Prefix - it: should not expose user-data. (content) subdomain when not set set: global: @@ -118,7 +130,7 @@ tests: port: number: 80 path: / - pathType: ImplementationSpecific + pathType: Prefix - it: should configure TLS when enabled set: ingress: @@ -231,6 +243,111 @@ tests: - equal: path: spec.rules[1].http.paths[0].pathType value: Prefix +- it: should fall back to global.ingress.defaultPathType when local is empty + set: + ingress: + enabled: true + defaultPathType: "" + global: + ingress: + defaultPathType: Prefix + asserts: + - equal: + path: spec.rules[0].http.paths[0].pathType + value: Prefix + - equal: + path: spec.rules[1].http.paths[0].pathType + value: Prefix +- it: should resolve {{ include "seqera.ingress.host" . }} to the chart's primary domain in annotations + set: + global: + platformExternalDomain: platform.example.com + ingress: + annotations: + external-dns.alpha.kubernetes.io/hostname: '{{ include "seqera.ingress.host" . }}' + ingress: + enabled: true + asserts: + - equal: + path: metadata.annotations["external-dns.alpha.kubernetes.io/hostname"] + value: platform.example.com +- it: should merge global.ingress.annotations with local annotations, local wins on collision + set: + ingress: + enabled: true + annotations: + nginx.ingress.kubernetes.io/proxy-body-size: "1g" + chart-only: "yes" + global: + ingress: + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + nginx.ingress.kubernetes.io/proxy-body-size: "100m" + asserts: + - equal: + path: metadata.annotations + value: + cert-manager.io/cluster-issuer: letsencrypt-prod + chart-only: "yes" + nginx.ingress.kubernetes.io/proxy-body-size: "1g" +- it: should merge global.ingress.extraLabels with local extraLabels, local wins on collision + set: + ingress: + enabled: true + extraLabels: + env: dev + chart: platform + global: + ingress: + extraLabels: + env: prod + team: devops + asserts: + - equal: + path: metadata.labels.env + value: dev + - equal: + path: metadata.labels.team + value: devops + - equal: + path: metadata.labels.chart + value: platform +- it: should concatenate global.ingress.tls with local tls + set: + ingress: + enabled: true + tls: + - hosts: + - platform.example.com + secretName: platform-tls + global: + ingress: + tls: + - hosts: + - "*.example.com" + secretName: wildcard-tls + asserts: + - equal: + path: spec.tls + value: + - hosts: + - platform.example.com + secretName: platform-tls + - hosts: + - "*.example.com" + secretName: wildcard-tls +- it: should prefer local ingress.defaultPathType over global + set: + ingress: + enabled: true + defaultPathType: Exact + global: + ingress: + defaultPathType: Prefix + asserts: + - equal: + path: spec.rules[0].http.paths[0].pathType + value: Exact - it: should use correct API version set: ingress: diff --git a/charts/platform/values.yaml b/charts/platform/values.yaml index 3617c5d..b4ecdc7 100644 --- a/charts/platform/values.yaml +++ b/charts/platform/values.yaml @@ -65,6 +65,35 @@ global: # -- Domain where the Portal Web frontend listens. Evaluated as a template portalWebDomain: '{{ printf "ai.%s" .Values.global.platformExternalDomain }}' + # Ingress defaults shared across the parent chart and all subcharts. Each subchart's local + # `ingress.*` value takes precedence when set; otherwise the global is used. + ingress: + # -- Enable Ingress for the parent chart and every subchart that exposes one. Each chart's + # local `ingress.enabled` is OR'd with this — set this to `true` to turn on all Ingresses + # in one switch + enabled: false + # -- Default path applied to ingress rules when a chart's local `ingress.path` is not set. + # AWS ALB users should override to `/*`. + path: "/" + # -- Default path type applied to ingress rules when a chart's local `ingress.defaultPathType` + # is not set. `Prefix` works for nginx, traefik, AWS ALB, and most modern controllers. + # Override to `ImplementationSpecific` only if your controller requires it (e.g. older GKE). + defaultPathType: "Prefix" + # -- Default ingress class name applied to ingress rules when a chart's local + # `ingress.ingressClassName` is not set. Replaces the deprecated `kubernetes.io/ingress.class` + # annotation + ingressClassName: "" + # -- Annotations merged into every chart's Ingress (e.g. cert-manager issuer, NGINX + # `proxy-body-size`, ALB SSL config). Local `ingress.annotations` wins on key collision. + # Evaluated as a template + annotations: {} + # -- Extra labels merged into every chart's Ingress. Local `ingress.extraLabels` wins on key + # collision. Evaluated as a template + extraLabels: {} + # -- TLS entries concatenated with each chart's local `ingress.tls`. Useful for a single + # wildcard cert that covers all services. Evaluated as a template + tls: [] + # -- Optional credentials to log in and fetch images from a private registry. These credentials # are shared with all the subcharts automatically imageCredentials: [] @@ -1302,16 +1331,14 @@ ingress: # -- Enable ingress for Platform enabled: false - # -- Path for the main ingress rule - # Note: this needs to be set to '/*' to be used with AWS ALB ingress controller - path: "/" + # -- Path for the main ingress rule. When empty, falls back to `global.ingress.path` + path: "" # -- Path for the content domain ingress rule - # Note: this needs to be set to '/*' to be used with AWS ALB ingress controller contentPath: "/" - # -- Default path type for the Ingress - defaultPathType: "ImplementationSpecific" + # -- Default path type for the Ingress. When empty, falls back to `global.ingress.defaultPathType` + defaultPathType: "" # -- Configure the default service for the ingress (evaluated as template) # Important: make sure only one defaultBackend is defined across the k8s cluster: if the @@ -1343,7 +1370,8 @@ ingress: annotations: {} # -- Additional labels for the ingress object. Evaluated as a template extraLabels: {} - # -- Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`) + # -- Name of the ingress class (replaces the deprecated annotation `kubernetes.io/ingress.class`). + # When empty, falls back to `global.ingress.ingressClassName` ingressClassName: "" # -- TLS configuration. Evaluated as a template tls: [] diff --git a/docs/conventions/ingress.md b/docs/conventions/ingress.md new file mode 100644 index 0000000..0d06830 --- /dev/null +++ b/docs/conventions/ingress.md @@ -0,0 +1,281 @@ +# Ingress Conventions + +How Ingress resources are structured across the `platform` chart and its +subcharts (`studios`, `portal-web`, `mcp`, `wave`, `agent-backend`). + +The goal is consistency: every chart that exposes HTTP traffic ships the same +Ingress shape so users only have to learn one set of values. + +## One Ingress per chart + +Each chart that exposes HTTP traffic owns exactly one `templates/ingress.yaml`. +Subcharts do not share an Ingress with the parent — each component is +independently toggleable via its own `ingress.enabled`. + +When adding a new chart that needs HTTP exposure, copy the structure from an +existing template (e.g. `charts/platform/charts/wave/templates/ingress.yaml`) +rather than inventing a new shape. + +## Standard structure + +Every Ingress template follows the same skeleton: + +```gotemplate +{{ if or .Values.ingress.enabled .Values.global.ingress.enabled }} + {{- $commonLabels := include "common.labels.standard" (dict "customLabels" .Values.commonLabels "context" $) | fromYaml -}} + {{- $mergedlabels := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.extraLabels $commonLabels) "context" .) -}} + {{- $labels := include "common.tplvalues.render" (dict "value" $mergedlabels "context" $) -}} + {{- $mergedAnnotations := include "common.tplvalues.merge" (dict "values" (list .Values.ingress.annotations .Values.commonAnnotations) "context" .) -}} + {{- $annotations := include "common.tplvalues.render" (dict "value" $mergedAnnotations "context" $) -}} + {{- $defaultPathType := default .Values.global.ingress.defaultPathType .Values.ingress.defaultPathType -}} + {{- $path := default .Values.global.ingress.path .Values.ingress.path -}} + {{- $ingressClassName := default .Values.global.ingress.ingressClassName .Values.ingress.ingressClassName -}} + +apiVersion: {{ include "common.capabilities.ingress.apiVersion" . }} +kind: Ingress +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- $labels | nindent 4 }} + annotations: {{- $annotations | nindent 4 }} +spec: + {{- with .Values.ingress.defaultBackend }} + defaultBackend: {{- include "seqera.tplvalues.render" (dict "value" . "context" $) | nindent 4 }} + {{- end }} + + {{- if $ingressClassName }} + ingressClassName: {{ $ingressClassName | quote }} + {{- end }} + + {{- with .Values.ingress.tls }} + tls: {{- include "common.tplvalues.render" (dict "value" . "context" $) | nindent 4 }} + {{- end }} + + rules: + - host: {{ tpl .Values.global. . | quote }} + http: + paths: + - path: {{ $path | quote }} + pathType: {{ $defaultPathType }} + backend: + service: + name: {{ (include "common.names.fullname" .) | quote }} + port: + number: {{ tpl (toString .Values.service.http.port) . | int }} + + {{- range .Values.ingress.extraHosts }} + - host: {{ tpl .host $ | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + pathType: {{ .pathType | default $defaultPathType }} + backend: + service: + name: {{ tpl .serviceName $ | quote }} + port: + number: {{ tpl (toString .portNumber) $ | int }} + {{- end }} + {{- end }} +{{- end }} +``` + +The pieces that should not change between charts: + +- **Gate**: `{{ if or .Values.ingress.enabled .Values.global.ingress.enabled }}` — + disabled by default; flipping `global.ingress.enabled: true` enables every + chart's Ingress at once. +- **API version**: resolved via `common.capabilities.ingress.apiVersion`, never + hard-coded. +- **Name / namespace**: `common.names.fullname` and `common.names.namespace`. +- **Labels**: standard labels merged with `commonLabels` and + `ingress.extraLabels`, then `tpl`-rendered. +- **Annotations**: `commonAnnotations` merged with `ingress.annotations`, then + `tpl`-rendered. +- **`defaultBackend`, `ingressClassName`, `tls`**: optional, all rendered + through the same `tpl` helper so values can reference `.Release` etc. +- **`extraHosts`**: every chart accepts the same `extraHosts` shape so users + can add hostnames without forking the chart. + +## Hostnames live in `global` + +Primary hostnames are pulled from `.Values.global.*Domain` so the parent chart +and its subcharts can be wired up without duplicating values. Examples in use: + +| Chart | Host source | +| --------------- | ------------------------------------------------------------------------------ | +| `platform` | `global.platformExternalDomain`, `global.contentDomain` (optional second host) | +| `studios` | `global.studiosDomain` (wildcard `*.`) | +| `portal-web` | `global.portalWebDomain` | +| `wave` | `global.waveDomain` | +| `mcp` | `global.mcpDomain` | +| `agent-backend` | `global.agentBackendDomain` | + +Hosts are always passed through `tpl` so they can reference other values +(e.g. `'{{ printf "*.%s" .Values.global.studiosDomain }}'`). + +When adding a new chart, define a new `global.Domain` value rather than +introducing a chart-local `ingress.host`. + +## Backend service wiring + +The default rule's backend points at the chart's own Service, named via +`common.names.fullname`. Two patterns appear: + +- **Single-service charts** (`portal-web`, `wave`, `mcp`, `agent-backend`): + backend is `{{ include "common.names.fullname" . }}`. +- **Multi-service charts** (`platform`, `studios`): backend is suffixed — + `{{ printf "%s-frontend" (include "common.names.fullname" .) }}` for + `platform`, `-proxy` for `studios`. + +Ports are read from the relevant Service values block (e.g. +`.Values.frontend.service.http.port`) and always rendered with +`{{ tpl (toString ...) . | int }}` so templated ports survive the cast. + +## Cluster-wide ingress defaults via `global.ingress` + +In practice, users who expose one Seqera service via Ingress almost always expose +them all the same way: same controller, same class name, same annotations +(cert-manager, ALB scheme, NGINX body-size limits, etc.). Forcing those values +to be repeated under `platform.ingress.*`, `studios.ingress.*`, `wave.ingress.*`, +and so on is verbose and drift-prone. `global.ingress` lifts the controller-wide +concerns to one place; per-chart overrides remain available for the rare case +where one service genuinely needs different routing. + +Seven fields are controller-wide concerns rather than per-chart settings: + +| Field | Local default | Global default | Resolution | +| ------------------ | ------------- | -------------- | ---------------------------------------------------------------- | +| `enabled` | `false` | `false` | OR — either being `true` enables the Ingress | +| `path` | `""` | `"/"` | Local wins when set; otherwise global. ALB → `"/*"` | +| `defaultPathType` | `""` | `"Prefix"` | Local wins when set; otherwise global | +| `ingressClassName` | `""` | `""` | Local wins when set; otherwise global | +| `annotations` | `{}` | `{}` | Merged; local wins on key collision (e.g. cert-manager, ALB SSL) | +| `extraLabels` | `{}` | `{}` | Merged; local wins on key collision | +| `tls` | `[]` | `[]` | Concatenated — supports a single wildcard cert across charts | + +Set these once at the parent's `global.ingress.*` and every subchart's Ingress +picks them up. Three resolution patterns: + +- **OR** for `enabled` so flipping `global.ingress.enabled: true` turns on + every chart's Ingress in one switch. +- **Local-wins-when-set** for the scalar fields (`path`, `defaultPathType`, + `ingressClassName`) so a chart can opt out of the cluster-wide default + without disabling it for everyone. +- **Merge / concat** for the collection fields (`annotations`, `extraLabels`, + `tls`) so cluster-wide entries (cert-manager issuer, NGINX `proxy-body-size`, + wildcard cert) coexist with chart-specific additions. + +In templates the resolution is done once at the top: + +```gotemplate +{{- $defaultPathType := default .Values.global.ingress.defaultPathType .Values.ingress.defaultPathType -}} +{{- $path := default .Values.global.ingress.path .Values.ingress.path -}} +{{- $ingressClassName := default .Values.global.ingress.ingressClassName .Values.ingress.ingressClassName -}} +{{- $tls := concat (default (list) .Values.ingress.tls) (default (list) .Values.global.ingress.tls) -}} +``` + +`annotations` and `extraLabels` are merged inline as part of the existing +`common.tplvalues.merge` chain — the global is added to the list of sources, +positioned so local entries win on key collision. + +## The `seqera.ingress.host` helper + +Each chart's `_helpers.tpl` defines a template called `seqera.ingress.host` +that returns that chart's primary domain (`global.platformExternalDomain` for +platform, `global.studiosDomain` for studios, etc.). The point of having the +same template name everywhere is that users can write a single annotation in +`global.ingress.annotations` that resolves to the right hostname per chart: + +```yaml +global: + ingress: + annotations: + external-dns.alpha.kubernetes.io/hostname: '{{ include "seqera.ingress.host" . }}' +``` + +When the platform chart renders its Ingress, the include resolves to +`platform.example.com`. When studios renders, the same string resolves to +`studios.example.com`. No hard-coding required, and it composes — for a +wildcard form, write `'\*.{{ include "seqera.ingress.host" . }}'`. + +When adding a new chart, define `seqera.ingress.host` in its `_helpers.tpl` +returning the chart's primary domain via `tpl`: + +```gotemplate +{{- define "seqera.ingress.host" -}} +{{- tpl .Values.global. . -}} +{{- end -}} +``` + +## TLS + +`ingress.tls` is an empty list by default. When set, it is rendered through +`common.tplvalues.render` so users can template hostnames: + +```yaml +ingress: + tls: + - hosts: + - "{{ .Values.global.platformExternalDomain }}" + secretName: my-tls +``` + +TLS secrets are **not** generated by the chart — bring your own (typically +managed by cert-manager or external-secrets). + +## Annotations and ingress class + +- `ingressClassName` is the supported way to pick a controller. Do **not** use + the deprecated `kubernetes.io/ingress.class` annotation. Set it once at + `global.ingress.ingressClassName`; per-chart overrides are rare. +- `ingress.annotations` is merged with `commonAnnotations`. Controller-specific + config (ALB, nginx, etc.) goes here. + +## Required values shape + +Every chart's `values.yaml` must expose this block under `ingress:`: + +```yaml +ingress: + enabled: false + path: "" # falls back to global.ingress.path + defaultPathType: "" # falls back to global.ingress.defaultPathType + ingressClassName: "" # falls back to global.ingress.ingressClassName + defaultBackend: {} + extraHosts: [] + annotations: {} + extraLabels: {} + tls: [] +``` + +And in `global:`: + +```yaml +global: + ingress: + enabled: false + path: "/" + defaultPathType: "Prefix" + ingressClassName: "" + annotations: {} + extraLabels: {} + tls: [] +``` + +Keep the `# --` helm-docs comments on each field — the README is generated +from them. + +## Testing + +Every Ingress template has a matching `tests/ingress_test.yaml`. Tests should +cover at minimum: + +- Resource is **not** rendered when `ingress.enabled: false`. +- A `matchSnapshot` for the default-enabled rendering. +- Targeted `equal` assertions for `ingressClassName`, `tls`, `extraHosts`, and + any chart-specific host logic (e.g. the optional `contentDomain` rule in + `platform`, the wildcard host in `studios`). + +Follow the test rules in [CLAUDE.md](../../CLAUDE.md) — assert on whole +maps/lists, not individual fields.