From d9c9c6eaddf2ff5cbb988fe124ed7458dc5e794b Mon Sep 17 00:00:00 2001 From: Pratapa Lakshmi Date: Tue, 23 Jun 2026 14:34:18 +0530 Subject: [PATCH 1/3] fix(plane-enterprise): decouple ingress controller type from class name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ingress templates used `ingress.ingressClass` to decide both the resource kind (standard Ingress vs Traefik IngressRoute) and the class name string. As a result `ingress.yaml` rendered only for the exact value `nginx` and `ingress-traefik.yaml` only for values prefixed `traefik` — any other class name (a Traefik alias, or `nginx-new`) matched neither template and the ingress was silently dropped. Introduce `ingress.controller` to select the resource kind, decoupled from the free-form class name. Only `traefik` selects the IngressRoute; any other value selects a standard Ingress. Empty falls back to the previous auto-detection from `ingressClass`, so existing installs are unaffected. Also fix a pre-existing latent bug in ingress.yaml where `len` of a nil `ingress_annotations` threw a hard render error; switched to `with`. Bumps chart 2.6.2 -> 2.6.3. Co-Authored-By: Claude Opus 4.8 (1M context) --- charts/plane-enterprise/Chart.yaml | 2 +- charts/plane-enterprise/README.md | 54 +++++++++++++------ .../plane-enterprise/templates/_helpers.tpl | 30 +++++++++++ .../templates/ingress-traefik.yaml | 2 +- .../plane-enterprise/templates/ingress.yaml | 6 +-- charts/plane-enterprise/values.yaml | 19 +++++-- 6 files changed, 90 insertions(+), 23 deletions(-) diff --git a/charts/plane-enterprise/Chart.yaml b/charts/plane-enterprise/Chart.yaml index 5a4c975..a84088a 100644 --- a/charts/plane-enterprise/Chart.yaml +++ b/charts/plane-enterprise/Chart.yaml @@ -5,7 +5,7 @@ description: Meet Plane. An Enterprise software development tool to manage issue type: application -version: 2.6.2 +version: 2.6.3 appVersion: "2.6.3" home: https://plane.so/ diff --git a/charts/plane-enterprise/README.md b/charts/plane-enterprise/README.md index 7cb1497..f99b4ed 100644 --- a/charts/plane-enterprise/README.md +++ b/charts/plane-enterprise/README.md @@ -27,24 +27,44 @@ If you plan to use Traefik as your ingress controller, install it before deployi ## Migrating the Ingress Controller -The chart selects between two ingress templates based on `ingress.ingressClass`: - -| `ingressClass` value | Template rendered | Resource kind | -| ------------------------------ | -------------------------------- | ---------------------------------- | -| `traefik` (or starts with it) | `templates/ingress-traefik.yaml` | `traefik.io/v1alpha1 IngressRoute` | -| Any other value (e.g. `nginx`) | `templates/ingress.yaml` | `networking.k8s.io/v1 Ingress` | - -The default value is `"traefik"`. If you are switching to a standard ingress controller such as nginx, follow the migration steps below. +The chart renders one of two ingress templates. **Which one** is chosen by the +*controller type* — kept separate from the *class name* so atypical class names +(e.g. `nginx-new`, or a Traefik-named alias served by a standard controller) work +without changing template selection. + +`ingress.controller` selects the resource kind: + +| `ingress.controller` value | Template rendered | Resource kind | +| ------------------------------------- | -------------------------------- | ---------------------------------- | +| `traefik` | `templates/ingress-traefik.yaml` | `traefik.io/v1alpha1 IngressRoute` | +| Any other value (`nginx`, `f5`, ...) | `templates/ingress.yaml` | `networking.k8s.io/v1 Ingress` | + +> The standard `Ingress` path covers **any** standard Kubernetes ingress controller — +> ingress-nginx, F5 NGINX, HAProxy, etc. The value is just a selector; it is not +> written anywhere. The actual class name comes from `ingress.ingressClass` +> (`spec.ingressClassName`), which can be any string your controller exposes. + +If `ingress.controller` is left empty, the controller is **auto-detected** from +`ingress.ingressClass` for backward compatibility: a value starting with `traefik` +selects Traefik, anything else selects the standard `Ingress`. Set +`ingress.controller` explicitly to override this — for example, to serve a standard +`Ingress` whose `ingressClassName` happens to start with `traefik`, or to use a +non-`nginx` class name like `nginx-new` (which previously was silently dropped). + +The default is a Traefik `IngressRoute` (`ingressClass: traefik`, controller +auto-detected). If you are switching to a standard ingress controller, follow the +migration steps below. ### Switching from Traefik to a standard Ingress controller (e.g. nginx) 1. **Install your target ingress controller** if it is not already running. -2. **Update `ingress.ingressClass`** in your `values.yaml`: +2. **Set `ingress.controller` and `ingress.ingressClass`** in your `values.yaml`: ```yaml ingress: - ingressClass: "nginx" # or whichever class your controller exposes + controller: "nginx" # selects the standard Ingress template (any value but "traefik") + ingressClass: "nginx" # spec.ingressClassName — whichever class your controller exposes (e.g. "nginx-new") ``` 3. **Run `helm upgrade`**: @@ -69,11 +89,12 @@ The default value is `"traefik"`. If you are switching to a standard ingress con 1. **Install Traefik** with CRD support enabled (see [Installing Traefik Ingress Controller](#installing-traefik-ingress-controller-optional) above). -2. **Update `ingress.ingressClass`**: +2. **Set `ingress.controller`**: ```yaml ingress: - ingressClass: "traefik" + controller: "traefik" + ingressClass: "traefik" # unused by the IngressRoute, kept for clarity ``` 3. **Run `helm upgrade`**. The old `Ingress` resource is orphaned — delete it: @@ -87,9 +108,10 @@ The default value is `"traefik"`. If you are switching to a standard ingress con | Value | Default | Effect | | ------------------------------------- | ---------- | ----------------------------------------------------------------------------------------- | | `ingress.enabled` | `true` | Master switch — set to `false` to render neither template. | -| `ingress.ingressClass` | `traefik` | Selects which template is active (see table above). | +| `ingress.controller` | `''` | Selects the resource kind: `traefik` → IngressRoute, anything else → standard `Ingress`. Empty = auto-detect from `ingressClass`. | +| `ingress.ingressClass` | `traefik` | Free-form `spec.ingressClassName` on the standard `Ingress`. Also drives auto-detection when `controller` is empty. Unused by Traefik. | | `ingress.traefik.maxRequestBodyBytes` | `20971520` | Max request body size for Traefik's buffering middleware. Ignored when not using Traefik. | -| `ingress.ingress_annotations` | `{}` | Standard `Ingress` annotations. Ignored when `ingressClass` starts with `traefik`. | +| `ingress.ingress_annotations` | `{}` | Standard `Ingress` annotations (e.g. cert-manager). Rendered only on the standard `Ingress`; ignored when `controller` resolves to `traefik`. | ## Installing Plane @@ -158,6 +180,7 @@ The default value is `"traefik"`. If you are switching to a standard ingress con - `planeVersion: v2.6.3 ` - `license.licenseDomain: ` - `ingress.enabled: ` + - `ingress.controller: ` - `ingress.ingressClass: ` - `env.storageClass: ` @@ -760,7 +783,8 @@ Note: When the email service is enabled, the cert-issuer will be automatically c | ingress.enabled | true | | Ingress setup in kubernetes is a common practice to expose application to the intended audience. Set it to `false` if you are using external ingress providers like `Cloudflare` | | ingress.minioHost | | | Based on above configuration, if you want to expose the `minio` web console to set of users, use this key to set the `host` mapping or leave it as `EMPTY` to not expose interface. | | ingress.rabbitmqHost | | | Based on above configuration, if you want to expose the `rabbitmq` web console to set of users, use this key to set the `host` mapping or leave it as `EMPTY` to not expose interface. | -| ingress.ingressClass | nginx | Yes | Kubernetes cluster setup comes with various options of `ingressClass`. Based on your setup, set this value to the right one (eg. nginx, traefik, etc). Leave it to default in case you are using external ingress provider. | +| ingress.controller | | | Selects the ingress resource kind: `traefik` renders a Traefik `IngressRoute`; any other value (`nginx`, `f5`, `haproxy`, ...) renders a standard `Ingress`. Leave empty to auto-detect from `ingressClass`. Set explicitly for atypical class names (e.g. `nginx-new`) or a Traefik-named alias served by a standard controller. | +| ingress.ingressClass | traefik | Yes | Free-form class name written to the standard `Ingress` `spec.ingressClassName` (eg. nginx, traefik, nginx-new, etc). When `controller` is empty it also drives auto-detection. Unused by the Traefik `IngressRoute`. | | ingress.ingress_annotations | `{ "nginx.ingress.kubernetes.io/proxy-body-size": "5m" }` | | Ingress controllers comes with various configuration options which can be passed as annotations. Setting this value lets you change the default value to user required. | | ssl.createIssuer | false | | Kubernets cluster setup supports creating `issuer` type resource. After deployment, this is step towards creating secure access to the ingress url. Issuer is required for you generate SSL certifiate. Kubernetes can be configured to use any of the certificate authority to generate SSL (depending on CertManager configuration). Set it to `true` to create the issuer. Applicable only when `ingress.enabled=true` | | ssl.issuer | http | | CertManager configuration allows user to create issuers using `http` or any of the other DNS Providers like `cloudflare`, `digitalocean`, etc. As of now Plane supports `http`, `cloudflare`, `digitalocean` | diff --git a/charts/plane-enterprise/templates/_helpers.tpl b/charts/plane-enterprise/templates/_helpers.tpl index 8566725..762e86f 100644 --- a/charts/plane-enterprise/templates/_helpers.tpl +++ b/charts/plane-enterprise/templates/_helpers.tpl @@ -64,6 +64,36 @@ of the local_setup flag's value. {{- end -}} {{- end -}} +{{/* +Selects which ingress template renders, decoupling the controller *type* (which +resource kind to emit) from the ingress *class name* (a free-form string). + +Returns one of: + - "traefik" -> templates/ingress-traefik.yaml emits a traefik.io/v1alpha1 IngressRoute + - "ingress" -> templates/ingress.yaml emits a networking.k8s.io/v1 Ingress + (nginx, F5 NGINX, HAProxy, or any other standard controller) + +Resolution order: + 1. If ingress.controller is set explicitly, it wins. Only the literal value + "traefik" selects the IngressRoute path; any other value (e.g. "nginx", + "f5", "haproxy") selects the standard Ingress path. This lets atypical + class names like "nginx-new" be used without affecting template selection. + 2. Otherwise (legacy behavior) the choice is inferred from ingress.ingressClass: + a value starting with "traefik" selects Traefik, anything else selects the + standard Ingress. Set ingress.controller to override the inference (e.g. a + standard Ingress whose ingressClassName happens to start with "traefik"). +*/}} +{{- define "plane.ingressController" -}} + {{- $c := .Values.ingress.controller | default "" | lower | trim -}} + {{- if $c -}} + {{- if eq $c "traefik" -}}traefik{{- else -}}ingress{{- end -}} + {{- else if hasPrefix "traefik" (.Values.ingress.ingressClass | default "" | lower) -}} + traefik + {{- else -}} + ingress + {{- end -}} +{{- end -}} + {{/* Normalize the deprecated s3SecretName/s3SecretKey into the s3Secrets list format. Returns "true" when airgapped is enabled and at least one CA secret is configured. diff --git a/charts/plane-enterprise/templates/ingress-traefik.yaml b/charts/plane-enterprise/templates/ingress-traefik.yaml index 6e296b3..9865619 100644 --- a/charts/plane-enterprise/templates/ingress-traefik.yaml +++ b/charts/plane-enterprise/templates/ingress-traefik.yaml @@ -1,4 +1,4 @@ -{{- if and .Values.ingress.enabled (hasPrefix "traefik" .Values.ingress.ingressClass) .Values.license.licenseDomain }} +{{- if and .Values.ingress.enabled (eq (include "plane.ingressController" .) "traefik") .Values.license.licenseDomain }} apiVersion: traefik.io/v1alpha1 kind: IngressRoute diff --git a/charts/plane-enterprise/templates/ingress.yaml b/charts/plane-enterprise/templates/ingress.yaml index 65f4c97..8c2d3c0 100644 --- a/charts/plane-enterprise/templates/ingress.yaml +++ b/charts/plane-enterprise/templates/ingress.yaml @@ -1,13 +1,13 @@ -{{- if and .Values.ingress.enabled (eq .Values.ingress.ingressClass "nginx") .Values.license.licenseDomain }} +{{- if and .Values.ingress.enabled (eq (include "plane.ingressController" .) "ingress") .Values.license.licenseDomain }} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: namespace: {{ .Release.Namespace }} name: {{ .Release.Name }}-ingress - {{- if gt (len .Values.ingress.ingress_annotations) 0 }} + {{- with .Values.ingress.ingress_annotations }} annotations: - {{- range $key, $value := .Values.ingress.ingress_annotations }} + {{- range $key, $value := . }} {{ $key }}: {{ $value | quote }} {{- end }} {{- end }} diff --git a/charts/plane-enterprise/values.yaml b/charts/plane-enterprise/values.yaml index 6913c4b..21e9326 100644 --- a/charts/plane-enterprise/values.yaml +++ b/charts/plane-enterprise/values.yaml @@ -38,10 +38,23 @@ ingress: enabled: true minioHost: '' rabbitmqHost: '' + # controller selects WHICH KIND of ingress resource the chart renders, decoupled + # from the class name below. Only the literal value "traefik" emits a Traefik + # IngressRoute CRD; any other value ("nginx", "f5", "haproxy", ...) emits a standard + # networking.k8s.io/v1 Ingress. Leave empty to auto-detect from ingressClass + # (a value starting with "traefik" -> Traefik, otherwise standard Ingress). + # Set this explicitly when your ingressClass is an atypical name (e.g. "nginx-new") + # or a Traefik-named alias that should still be served by a standard Ingress. + controller: '' + # ingressClass is the free-form class name written to the standard Ingress' + # spec.ingressClassName. It no longer constrains which template renders (use + # controller above for that). With controller="traefik" it is unused, since an + # IngressRoute has no class name. ingressClass: 'traefik' - # If using nginx ingress controller, set this to 'nginx' and add the appropriate annotations to set the proxy body size limit. - # These annotations are ONLY rendered when ingressClass is 'nginx' (the nginx Ingress template is gated on - # ingressClass == "nginx"); they have no effect with traefik. Example: + # For standard Ingress controllers (controller != "traefik") set the proxy body + # size / buffer annotations here. These are rendered onto the standard Ingress + # only; they have no effect with Traefik (use ingress.traefik.maxRequestBodyBytes). + # Example for ingress-nginx: # - proxy-body-size: nginx equivalent of traefik's maxRequestBodyBytes (upload size limit). # - proxy-buffer-size: size of the buffer for the response headers from upstream; bump this to avoid # "502 upstream sent too big header" errors. From 68e9fbd1e0112b76659b219f1a228db1c84112ae Mon Sep 17 00:00:00 2001 From: Pratapa Lakshmi Date: Tue, 23 Jun 2026 14:39:03 +0530 Subject: [PATCH 2/3] docs(plane-enterprise): drop Traefik-alias-on-standard-Ingress guidance The ingress.controller feature stays (it implements the F5/nginx-new request). Remove only the README/values examples about serving a Traefik-named class alias via a standard Ingress, which is no longer a needed use case. Co-Authored-By: Claude Opus 4.8 (1M context) --- charts/plane-enterprise/README.md | 10 ++++------ charts/plane-enterprise/values.yaml | 3 +-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/charts/plane-enterprise/README.md b/charts/plane-enterprise/README.md index f99b4ed..b2ecf98 100644 --- a/charts/plane-enterprise/README.md +++ b/charts/plane-enterprise/README.md @@ -29,8 +29,7 @@ If you plan to use Traefik as your ingress controller, install it before deployi The chart renders one of two ingress templates. **Which one** is chosen by the *controller type* — kept separate from the *class name* so atypical class names -(e.g. `nginx-new`, or a Traefik-named alias served by a standard controller) work -without changing template selection. +(e.g. `nginx-new`) work without changing template selection. `ingress.controller` selects the resource kind: @@ -47,9 +46,8 @@ without changing template selection. If `ingress.controller` is left empty, the controller is **auto-detected** from `ingress.ingressClass` for backward compatibility: a value starting with `traefik` selects Traefik, anything else selects the standard `Ingress`. Set -`ingress.controller` explicitly to override this — for example, to serve a standard -`Ingress` whose `ingressClassName` happens to start with `traefik`, or to use a -non-`nginx` class name like `nginx-new` (which previously was silently dropped). +`ingress.controller` explicitly to use a non-`nginx` class name like `nginx-new` +(which previously was silently dropped). The default is a Traefik `IngressRoute` (`ingressClass: traefik`, controller auto-detected). If you are switching to a standard ingress controller, follow the @@ -783,7 +781,7 @@ Note: When the email service is enabled, the cert-issuer will be automatically c | ingress.enabled | true | | Ingress setup in kubernetes is a common practice to expose application to the intended audience. Set it to `false` if you are using external ingress providers like `Cloudflare` | | ingress.minioHost | | | Based on above configuration, if you want to expose the `minio` web console to set of users, use this key to set the `host` mapping or leave it as `EMPTY` to not expose interface. | | ingress.rabbitmqHost | | | Based on above configuration, if you want to expose the `rabbitmq` web console to set of users, use this key to set the `host` mapping or leave it as `EMPTY` to not expose interface. | -| ingress.controller | | | Selects the ingress resource kind: `traefik` renders a Traefik `IngressRoute`; any other value (`nginx`, `f5`, `haproxy`, ...) renders a standard `Ingress`. Leave empty to auto-detect from `ingressClass`. Set explicitly for atypical class names (e.g. `nginx-new`) or a Traefik-named alias served by a standard controller. | +| ingress.controller | | | Selects the ingress resource kind: `traefik` renders a Traefik `IngressRoute`; any other value (`nginx`, `f5`, `haproxy`, ...) renders a standard `Ingress`. Leave empty to auto-detect from `ingressClass`. Set explicitly for atypical class names (e.g. `nginx-new`). | | ingress.ingressClass | traefik | Yes | Free-form class name written to the standard `Ingress` `spec.ingressClassName` (eg. nginx, traefik, nginx-new, etc). When `controller` is empty it also drives auto-detection. Unused by the Traefik `IngressRoute`. | | ingress.ingress_annotations | `{ "nginx.ingress.kubernetes.io/proxy-body-size": "5m" }` | | Ingress controllers comes with various configuration options which can be passed as annotations. Setting this value lets you change the default value to user required. | | ssl.createIssuer | false | | Kubernets cluster setup supports creating `issuer` type resource. After deployment, this is step towards creating secure access to the ingress url. Issuer is required for you generate SSL certifiate. Kubernetes can be configured to use any of the certificate authority to generate SSL (depending on CertManager configuration). Set it to `true` to create the issuer. Applicable only when `ingress.enabled=true` | diff --git a/charts/plane-enterprise/values.yaml b/charts/plane-enterprise/values.yaml index 21e9326..bf45da7 100644 --- a/charts/plane-enterprise/values.yaml +++ b/charts/plane-enterprise/values.yaml @@ -43,8 +43,7 @@ ingress: # IngressRoute CRD; any other value ("nginx", "f5", "haproxy", ...) emits a standard # networking.k8s.io/v1 Ingress. Leave empty to auto-detect from ingressClass # (a value starting with "traefik" -> Traefik, otherwise standard Ingress). - # Set this explicitly when your ingressClass is an atypical name (e.g. "nginx-new") - # or a Traefik-named alias that should still be served by a standard Ingress. + # Set this explicitly when your ingressClass is an atypical name (e.g. "nginx-new"). controller: '' # ingressClass is the free-form class name written to the standard Ingress' # spec.ingressClassName. It no longer constrains which template renders (use From 4d7eb39115a48b289f5e22c8b67caa5d19ea88e1 Mon Sep 17 00:00:00 2001 From: Pratapa Lakshmi Date: Tue, 23 Jun 2026 14:45:27 +0530 Subject: [PATCH 3/3] fix(plane-enterprise): align Middleware gate + questions.yml default - traefik-middleware.yaml: gate on plane.ingressController (matching ingress-traefik.yaml) instead of hasPrefix on ingressClass. Prevents a missing Middleware (and route admission failure) when controller=traefik with a non-traefik ingressClass, and avoids an orphan Middleware in the reverse case. - questions.yml: default ingress.ingressClass to "traefik" to match values.yaml (the authoritative chart default). Co-Authored-By: Claude Opus 4.8 (1M context) --- charts/plane-enterprise/questions.yml | 2 +- charts/plane-enterprise/templates/traefik-middleware.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/plane-enterprise/questions.yml b/charts/plane-enterprise/questions.yml index 7429d97..7f8d184 100644 --- a/charts/plane-enterprise/questions.yml +++ b/charts/plane-enterprise/questions.yml @@ -1420,7 +1420,7 @@ questions: label: "Ingress Classname" type: string required: true - default: "nginx" + default: "traefik" show_if: "ingress.enabled=true" - variable: ssl.createIssuer diff --git a/charts/plane-enterprise/templates/traefik-middleware.yaml b/charts/plane-enterprise/templates/traefik-middleware.yaml index b826321..8a8e345 100644 --- a/charts/plane-enterprise/templates/traefik-middleware.yaml +++ b/charts/plane-enterprise/templates/traefik-middleware.yaml @@ -1,4 +1,4 @@ -{{- if and .Values.ingress.enabled (hasPrefix "traefik" .Values.ingress.ingressClass) }} +{{- if and .Values.ingress.enabled (eq (include "plane.ingressController" .) "traefik") }} apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: