Files
cozystack/internal/controller
Aleksei Sviridkin 30302eabe4 feat(tenantgateway): switch Gateway allowedRoutes to label selector
The Gateway's HTTPS / TLS-passthrough listeners now match attached
namespaces by the namespace.cozystack.io/gateway label keyed on the
TenantGateway's own namespace, instead of a static
kubernetes.io/metadata.name In [list] whitelist. This restores
inheritance parity with the legacy ingress flow: child tenants of a
Gateway-owning ancestor publish through the ancestor's Gateway by
default — without their own LB IP / Certificate, and without
operator intervention to add each child by name to platform values.

Two writers populate the label:

* apps/tenant chart namespace.yaml — each tenant namespace gets a
  label pointing at the nearest ancestor that owns a Gateway (self
  if owning, inherited otherwise). The tenant Helm chart is the
  source of truth for tenant-tree labels; the controller never
  touches them (annotation-gated GC).
* cozystack-controller (ensureNamespaceLabels) — patches the label
  onto every namespace in tgw.Spec.AttachedNamespaces, so cozy-*
  system namespaces (cert-manager, monitoring, harbor, …) reach the
  publishing Gateway alongside the tenant tree. Tracks its own
  writes via the cozystack.io/gateway-attached-by annotation; on a
  reconcile where an entry is removed from AttachedNamespaces, the
  label is garbage-collected (annotation present + namespace no
  longer expected → strip both label and annotation).

Also adds the gateway-attach label to tenant-root itself via
cozystack-basics/templates/tenant-root.yaml when
_cluster.gateway-enabled is true, so the Gateway in tenant-root's
own namespace passes its own selector for the controller-owned
http→https redirect HTTPRoute.

Tests (TDD, red→green):
* TestReconcile_HTTPSListenerUsesGatewayLabelSelector pins the new
  MatchLabels shape on HTTPS listeners.
* TestReconcile_LabelsAttachedNamespaces pins the patch path: every
  namespace in spec.AttachedNamespaces (plus tgw.Namespace) carries
  namespace.cozystack.io/gateway=<owner> after reconcile.
* TestReconcile_LabelGCRemovesDroppedAttachedNamespaces pins the GC
  path: removing an entry between reconciles strips the label.
* TestReconcile_LabelGCDoesNotStripHelmOwnedLabels pins the safety
  contract: labels lacking the controller annotation (Helm-owned
  tenant labels) are left alone.
* tenant-root_test.yaml adds the positive case for the conditional
  label render under _cluster.gateway-enabled=true.

The HTTP listener (port 80) keeps the narrower static-name selector
— it allows only the tenant namespace plus cozy-cert-manager (for
HTTP-01 ACME challenges) and must NOT accept routes from app
namespaces or child tenants, since unattended routes on the HTTP
listener serve plaintext and leak credentials.

Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-05-25 18:32:20 +03:00
..