From b719cf67e3428ccf0d7f52dcec19584df93f432d Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Thu, 23 Apr 2026 19:55:59 +0300 Subject: [PATCH] feat(tenant,harbor,bucket): wire per-tenant apps through the tenant Gateway MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tenants can now opt their own apps into Gateway API without touching the publishing tenant. The mechanism mirrors the existing _namespace.ingress contract exactly. packages/apps/tenant/templates/namespace.yaml: - Compute a new $gateway value: if the tenant sets .Values.gateway it becomes the tenant's own namespace, otherwise it inherits from the parent namespace (empty means no Gateway anywhere in the chain). - Ship $gateway out through the cozystack-values Secret under _namespace.gateway and through a namespace label namespace.cozystack.io/gateway. packages/apps/harbor and packages/system/bucket: - Existing Ingress wrapped in '{{ if not $gateway }}' so it only renders when no Gateway is attached anywhere up the tenant chain. - New templates/httproute.yaml render when $gateway is non-empty: apiVersion gateway.networking.k8s.io/v1 HTTPRoute, parentRef to the 'cozystack' Gateway in whichever namespace $gateway points at, hostname . (harbor) or . (bucket), backendRef to the existing Service on its current port. Per-tenant resolution: a tenant with .Values.gateway=true gets its own Gateway in its own namespace, and harbor/bucket deployed inside that tenant attach to that namespace's Gateway — no cross-namespace references, no coupling to the publishing tenant. Default remains ingress-nginx: tenants that do not opt in keep rendering Ingress verbatim. Assisted-By: Claude Signed-off-by: Aleksei Sviridkin --- packages/apps/harbor/templates/httproute.yaml | 21 +++++++++++++++++++ packages/apps/harbor/templates/ingress.yaml | 3 +++ packages/apps/tenant/templates/namespace.yaml | 7 +++++++ .../system/bucket/templates/httproute.yaml | 20 ++++++++++++++++++ packages/system/bucket/templates/ingress.yaml | 3 +++ 5 files changed, 54 insertions(+) create mode 100644 packages/apps/harbor/templates/httproute.yaml create mode 100644 packages/system/bucket/templates/httproute.yaml diff --git a/packages/apps/harbor/templates/httproute.yaml b/packages/apps/harbor/templates/httproute.yaml new file mode 100644 index 00000000..972f0464 --- /dev/null +++ b/packages/apps/harbor/templates/httproute.yaml @@ -0,0 +1,21 @@ +{{- $gateway := .Values._namespace.gateway | default "" }} +{{- $host := .Values._namespace.host }} +{{- $harborHost := .Values.host | default (printf "%s.%s" .Release.Name $host) }} +{{- if $gateway }} +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: {{ .Release.Name }} +spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: cozystack + namespace: {{ $gateway }} + hostnames: + - {{ $harborHost | quote }} + rules: + - backendRefs: + - name: {{ .Release.Name }} + port: 80 +{{- end }} diff --git a/packages/apps/harbor/templates/ingress.yaml b/packages/apps/harbor/templates/ingress.yaml index 70933f5f..a8ac6463 100644 --- a/packages/apps/harbor/templates/ingress.yaml +++ b/packages/apps/harbor/templates/ingress.yaml @@ -1,8 +1,10 @@ {{- $ingress := .Values._namespace.ingress }} +{{- $gateway := .Values._namespace.gateway | default "" }} {{- $host := .Values._namespace.host }} {{- $harborHost := .Values.host | default (printf "%s.%s" .Release.Name $host) }} {{- $solver := (index .Values._cluster "solver") | default "http01" }} {{- $clusterIssuer := (index .Values._cluster "issuer-name") | default "letsencrypt-prod" }} +{{- if not $gateway }} --- apiVersion: networking.k8s.io/v1 kind: Ingress @@ -35,3 +37,4 @@ spec: name: {{ .Release.Name }} port: number: 80 +{{- end }} diff --git a/packages/apps/tenant/templates/namespace.yaml b/packages/apps/tenant/templates/namespace.yaml index dfb83730..0e884934 100644 --- a/packages/apps/tenant/templates/namespace.yaml +++ b/packages/apps/tenant/templates/namespace.yaml @@ -29,6 +29,11 @@ {{- $ingress = $tenantName }} {{- end }} +{{- $gateway := $parentNamespace.gateway | default "" }} +{{- if .Values.gateway }} +{{- $gateway = $tenantName }} +{{- end }} + {{- $monitoring := $parentNamespace.monitoring | default "" }} {{- if .Values.monitoring }} {{- $monitoring = $tenantName }} @@ -61,6 +66,7 @@ metadata: {{/* Labels for network policies */}} namespace.cozystack.io/etcd: {{ $etcd | quote }} namespace.cozystack.io/ingress: {{ $ingress | quote }} + namespace.cozystack.io/gateway: {{ $gateway | quote }} namespace.cozystack.io/monitoring: {{ $monitoring | quote }} namespace.cozystack.io/seaweedfs: {{ $seaweedfs | quote }} namespace.cozystack.io/host: {{ $computedHost | quote }} @@ -92,6 +98,7 @@ stringData: _namespace: etcd: {{ $etcd | quote }} ingress: {{ $ingress | quote }} + gateway: {{ $gateway | quote }} monitoring: {{ $monitoring | quote }} seaweedfs: {{ $seaweedfs | quote }} host: {{ $computedHost | quote }} diff --git a/packages/system/bucket/templates/httproute.yaml b/packages/system/bucket/templates/httproute.yaml new file mode 100644 index 00000000..8e6fae51 --- /dev/null +++ b/packages/system/bucket/templates/httproute.yaml @@ -0,0 +1,20 @@ +{{- $host := .Values._namespace.host }} +{{- $gateway := .Values._namespace.gateway | default "" }} +{{- if $gateway }} +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: {{ .Values.bucketName }}-ui +spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: cozystack + namespace: {{ $gateway }} + hostnames: + - {{ printf "%s.%s" .Values.bucketName $host | quote }} + rules: + - backendRefs: + - name: {{ .Values.bucketName }}-ui + port: 8080 +{{- end }} diff --git a/packages/system/bucket/templates/ingress.yaml b/packages/system/bucket/templates/ingress.yaml index b7ffaf8b..c2d80647 100644 --- a/packages/system/bucket/templates/ingress.yaml +++ b/packages/system/bucket/templates/ingress.yaml @@ -1,7 +1,9 @@ {{- $host := .Values._namespace.host }} {{- $ingress := .Values._namespace.ingress }} +{{- $gateway := .Values._namespace.gateway | default "" }} {{- $solver := (index .Values._cluster "solver") | default "http01" }} {{- $clusterIssuer := (index .Values._cluster "issuer-name") | default "letsencrypt-prod" }} +{{- if not $gateway }} apiVersion: networking.k8s.io/v1 kind: Ingress @@ -32,3 +34,4 @@ spec: name: {{ .Values.bucketName }}-ui port: number: 8080 +{{- end }}