diff --git a/.github/workflows/helm_ci.yml b/.github/workflows/helm_ci.yml index 7b2ec01db..5c5dd31ca 100644 --- a/.github/workflows/helm_ci.yml +++ b/.github/workflows/helm_ci.yml @@ -697,6 +697,92 @@ jobs: echo "$BOOL_FALSE" | grep -q -- '-status Suspended' || { echo "FAIL: bool false versioning did not Suspend the bucket"; exit 1; } echo "Bucket versioning: YAML bool false suspends consistently with string \"false\"" + echo "" + echo "=== Testing hook Job labels ===" + # The hook Jobs were the only pods in the chart without the standard + # app.kubernetes.io label set, so nothing label-based could target + # them - NetworkPolicy podSelectors, monitoring, kubectl -l. Assert + # both the Job and its pod template carry the same name/instance/ + # component triple the other components use, and that the triple is + # the hook's own so a per-component selector cannot match it too. + # + # Only the bucket hook is covered: the volume resize hook is gated on + # lookup finding a StatefulSet with a smaller PVC than requested, and + # lookup returns nothing under helm template, so it never renders here. + python3 - "$CHART_DIR" <<'PYEOF' + import subprocess, sys, yaml + + chart = sys.argv[1] + TRIPLE = ("app.kubernetes.io/name", "app.kubernetes.io/instance", + "app.kubernetes.io/component") + + def render(values): + args = ["helm", "template", "test", chart] + for k, v in values.items(): + args += ["--set", f"{k}={v}"] + return subprocess.check_output(args, text=True) + + def docs(manifest): + return [d for d in yaml.safe_load_all(manifest) if d] + + def hook_job(manifest): + for d in docs(manifest): + if d.get("kind") == "Job" and d["metadata"]["name"].endswith("-bucket-hook"): + return d + return None + + def triple(labels): + return {k: labels.get(k) for k in TRIPLE} + + modes = { + "s3": {"s3.enabled": "true", + "s3.createBuckets[0].name": "b"}, + "filer.s3": {"filer.s3.enabled": "true", + "filer.s3.createBuckets[0].name": "b"}, + "allInOne": {"allInOne.enabled": "true", + "allInOne.s3.enabled": "true", + "allInOne.s3.createBuckets[0].name": "b"}, + } + + failed = [] + for mode, values in modes.items(): + before = len(failed) + out = render(values) + job = hook_job(out) + if job is None: + failed.append(f"{mode}: bucket hook Job not rendered") + continue + pod_labels = job["spec"]["template"]["metadata"].get("labels", {}) + for where, labels in (("Job", job["metadata"].get("labels", {})), + ("pod", pod_labels)): + missing = [k for k in TRIPLE if not labels.get(k)] + if missing: + failed.append(f"{mode}: bucket hook {where} has no {missing}, " + "nothing can select it") + component = pod_labels.get("app.kubernetes.io/component") + if component != "bucket-hook": + failed.append(f"{mode}: bucket hook pod component is {component!r}, " + "expected 'bucket-hook'") + # The triple must not also match another component's pods, or a + # selector meant for that component would pull the hook pod in. + for d in docs(out): + if d.get("kind") not in ("Deployment", "StatefulSet"): + continue + other = d["spec"]["template"]["metadata"].get("labels", {}) + if triple(other) == triple(pod_labels): + failed.append(f"{mode}: bucket hook pod shares its label triple " + f"with {d['metadata']['name']}") + if len(failed) == before: + print(f"{mode}: bucket hook Job and pod carry a distinct label triple") + + if failed: + print("\nFAIL:", file=sys.stderr) + for f in failed: + print(f" - {f}", file=sys.stderr) + sys.exit(1) + PYEOF + echo "Hook Job label tests passed" + echo "All template rendering tests passed!" - name: Create kind cluster diff --git a/k8s/charts/seaweedfs/templates/shared/post-install-bucket-hook.yaml b/k8s/charts/seaweedfs/templates/shared/post-install-bucket-hook.yaml index eb19ed672..8f6cd287f 100644 --- a/k8s/charts/seaweedfs/templates/shared/post-install-bucket-hook.yaml +++ b/k8s/charts/seaweedfs/templates/shared/post-install-bucket-hook.yaml @@ -47,8 +47,11 @@ kind: Job metadata: name: "{{ $.Release.Name }}-bucket-hook" labels: + app.kubernetes.io/name: {{ template "seaweedfs.name" . }} + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} app.kubernetes.io/managed-by: {{ .Release.Service | quote }} app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/component: bucket-hook annotations: "helm.sh/hook": post-install,post-upgrade "helm.sh/hook-weight": "-5" @@ -58,8 +61,11 @@ spec: metadata: name: "{{ .Release.Name }}" labels: + app.kubernetes.io/name: {{ template "seaweedfs.name" . }} + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} app.kubernetes.io/managed-by: {{ .Release.Service | quote }} app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/component: bucket-hook spec: restartPolicy: Never {{- if .Values.filer.podSecurityContext.enabled }} diff --git a/k8s/charts/seaweedfs/templates/volume/volume-resize-hook.yaml b/k8s/charts/seaweedfs/templates/volume/volume-resize-hook.yaml index 313bc61fc..161528533 100644 --- a/k8s/charts/seaweedfs/templates/volume/volume-resize-hook.yaml +++ b/k8s/charts/seaweedfs/templates/volume/volume-resize-hook.yaml @@ -53,6 +53,12 @@ apiVersion: batch/v1 kind: Job metadata: name: "{{ $seaweedfsName }}-volume-resize-hook" + labels: + app.kubernetes.io/name: {{ template "seaweedfs.name" . }} + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: volume-resize-hook annotations: helm.sh/hook: pre-install,pre-upgrade helm.sh/hook-weight: "0" @@ -60,6 +66,13 @@ metadata: spec: backoffLimit: 1 template: + metadata: + labels: + app.kubernetes.io/name: {{ template "seaweedfs.name" . }} + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: volume-resize-hook spec: serviceAccountName: {{ $seaweedfsName }}-volume-resize-hook restartPolicy: Never