From 3a87485ca102e11dda3a4eabfd50dee894e4095f Mon Sep 17 00:00:00 2001 From: Myasnikov Daniil Date: Tue, 28 Apr 2026 11:43:02 +0500 Subject: [PATCH] fix(installer): bootstrap cozy-system via --create-namespace + label hook Removes the chart's `Namespace cozy-system` resource and replaces it with a pre-install/pre-upgrade Job hook (cozy-system-labeler) that patches the required labels onto the namespace after `--create-namespace` creates it. Why: helm v3 has a known chicken-and-egg with charts that ship their own Namespace: - WITH `--create-namespace` on the install command, helm pre-creates the namespace via plain kubectl-create (no helm meta annotations); the chart's own Namespace apply then fails with `already exists`. - WITHOUT `--create-namespace`, helm fails immediately because it cannot write its release-secret to a non-existent namespace. Until now this was hidden by the 3x retry on `Install Cozystack` in `.github/workflows/pull-requests.yaml`: first attempt always fails with the conflict, second attempt sees the existing failed release and takes the upgrade code path which patch-merges instead of strict-create. Reproducible on every cold install. Surfaced cleanly when retries on the install step were dropped. After this change: - install commands use `helm upgrade --install --namespace cozy-system --create-namespace`. Standard pattern, matches kube-prometheus-stack / argo-cd / cert-manager / others. - the pre-install hook (SA + ClusterRole + ClusterRoleBinding + Job) patches `cozystack.io/system=true` and `pod-security.kubernetes.io/enforce=privileged` onto the namespace before main resources apply. - hook-delete-policy=before-hook-creation,hook-succeeded so the RBAC surface only exists during install/upgrade. Verified end-to-end on a kind cluster: cold install in 3.3s, upgrade idempotent, cleanup clean. Image pinned to `alpine/k8s:1.32.0` for the hook (small, public, includes kubectl). Signed-off-by: Myasnikov Daniil --- .../templates/cozy-system-labels.yaml | 82 +++++++++++++++++++ .../templates/cozystack-operator.yaml | 10 --- 2 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 packages/core/installer/templates/cozy-system-labels.yaml diff --git a/packages/core/installer/templates/cozy-system-labels.yaml b/packages/core/installer/templates/cozy-system-labels.yaml new file mode 100644 index 00000000..1ea3e5eb --- /dev/null +++ b/packages/core/installer/templates/cozy-system-labels.yaml @@ -0,0 +1,82 @@ +{{/* + Pre-install/upgrade hook that labels the cozy-system namespace with the + PodSecurity and cozystack identity labels. The chart no longer ships a + Namespace resource (helm v3 cannot adopt a namespace pre-created by + --create-namespace because it lacks helm meta annotations; without + --create-namespace, helm fails to write its release-secret because the + namespace does not exist yet — chicken-and-egg). + Install commands now use --create-namespace to bootstrap the namespace, + and this hook patches the labels the operator depends on (notably + pod-security.kubernetes.io/enforce=privileged for the host-network + operator pod). +*/}} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: cozy-system-labeler + namespace: cozy-system + annotations: + helm.sh/hook: pre-install,pre-upgrade + helm.sh/hook-weight: "-200" + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: cozy-system-labeler + annotations: + helm.sh/hook: pre-install,pre-upgrade + helm.sh/hook-weight: "-180" + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded +rules: +- apiGroups: [""] + resources: ["namespaces"] + resourceNames: ["cozy-system"] + verbs: ["get", "patch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: cozy-system-labeler + annotations: + helm.sh/hook: pre-install,pre-upgrade + helm.sh/hook-weight: "-160" + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded +subjects: +- kind: ServiceAccount + name: cozy-system-labeler + namespace: cozy-system +roleRef: + kind: ClusterRole + name: cozy-system-labeler + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: cozy-system-labeler + namespace: cozy-system + annotations: + helm.sh/hook: pre-install,pre-upgrade + helm.sh/hook-weight: "-100" + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded +spec: + backoffLimit: 3 + ttlSecondsAfterFinished: 60 + template: + spec: + serviceAccountName: cozy-system-labeler + restartPolicy: OnFailure + containers: + - name: kubectl + image: alpine/k8s:1.32.0 + imagePullPolicy: IfNotPresent + command: + - kubectl + - label + - --overwrite + - namespace + - cozy-system + - cozystack.io/system=true + - pod-security.kubernetes.io/enforce=privileged diff --git a/packages/core/installer/templates/cozystack-operator.yaml b/packages/core/installer/templates/cozystack-operator.yaml index 8389aca4..9891f7c9 100644 --- a/packages/core/installer/templates/cozystack-operator.yaml +++ b/packages/core/installer/templates/cozystack-operator.yaml @@ -4,16 +4,6 @@ {{- end -}} --- apiVersion: v1 -kind: Namespace -metadata: - name: cozy-system - labels: - cozystack.io/system: "true" - pod-security.kubernetes.io/enforce: privileged - annotations: - helm.sh/resource-policy: keep ---- -apiVersion: v1 kind: ServiceAccount metadata: name: cozystack