diff --git a/.github/workflows/helm-validate.yml b/.github/workflows/helm-validate.yml index 03072cc1..faed799f 100644 --- a/.github/workflows/helm-validate.yml +++ b/.github/workflows/helm-validate.yml @@ -74,6 +74,19 @@ jobs: - name: Template CRDs chart run: helm template test-release helm/temporal-worker-controller-crds + helm-check-rbac: + name: Check Helm RBAC wasn't manually updated + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Regenerate and diff + run: make manifests && git diff --exit-code helm/temporal-worker-controller/templates/rbac.yaml + helm-validate-succeed: name: All Helm Validations Succeed needs: @@ -81,6 +94,7 @@ jobs: - helm-template - helm-lint-crds - helm-template-crds + - helm-check-rbac runs-on: ubuntu-latest if: always() env: diff --git a/Makefile b/Makefile index 1a61de0e..cd6f54c8 100644 --- a/Makefile +++ b/Makefile @@ -153,6 +153,7 @@ help: ## Display this help. manifests: controller-gen ## Generate ClusterRole and CustomResourceDefinition objects. GOWORK=off GO111MODULE=on $(CONTROLLER_GEN) rbac:roleName=manager-role crd:allowDangerousTypes=true,maxDescLen=0,generateEmbeddedObjectMeta=true paths=./api/... paths=./internal/... paths=./cmd/... \ output:crd:artifacts:config=helm/temporal-worker-controller-crds/templates + python3 hack/sync-rbac-rules.py .PHONY: generate generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. diff --git a/hack/sync-rbac-rules.py b/hack/sync-rbac-rules.py new file mode 100644 index 00000000..f4a6d616 --- /dev/null +++ b/hack/sync-rbac-rules.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +"""Sync rules from config/rbac/role.yaml into the Helm ClusterRole template. + +The Helm template must contain: + # GENERATED RULES BEGIN + ... + # GENERATED RULES END +markers inside its ClusterRole rules section. Everything between the markers +is replaced with the rules from config/rbac/role.yaml (indented by two spaces). +""" +import re +import sys + +ROLE_YAML = "config/rbac/role.yaml" +HELM_RBAC = "helm/temporal-worker-controller/templates/rbac.yaml" +BEGIN_MARKER = " # GENERATED RULES BEGIN" +END_MARKER = " # GENERATED RULES END" + + +def extract_rules_text(path): + with open(path) as f: + content = f.read() + idx = content.find("\nrules:\n") + if idx == -1: + print(f"ERROR: 'rules:' not found in {path}", file=sys.stderr) + sys.exit(1) + rules_body = content[idx + len("\nrules:\n"):] + # Indent lines relative to the `rules:` key in the Helm template. + # controller-gen emits two indentation levels: + # col 0: outer list items (e.g. "- apiGroups:") → add 2 spaces + # col 2: inner list values (e.g. " - events") → add 4 spaces + # col 2: mapping keys (e.g. " resources:") → add 2 spaces + # The extra indent on inner list values matches the style used by the + # hand-authored rules in the Helm template. + lines = rules_body.splitlines(keepends=True) + result = [] + for line in lines: + if not line.strip(): + result.append(line) + elif line.startswith(" - "): + result.append(" " + line) # inner list value: 2 global + 2 extra + else: + result.append(" " + line) # outer list item or mapping key: 2 global + return "".join(result) + + +def update_helm(path, rules_text): + with open(path) as f: + content = f.read() + pattern = re.compile( + r"(" + re.escape(BEGIN_MARKER) + r"[^\n]*\n)(.*?)(" + re.escape(END_MARKER) + r")", + re.DOTALL, + ) + if not pattern.search(content): + print(f"ERROR: markers not found in {path}", file=sys.stderr) + sys.exit(1) + updated = pattern.sub(r"\g<1>" + rules_text + r"\g<3>", content) + with open(path, "w") as f: + f.write(updated) + print(f"Synced RBAC rules from {ROLE_YAML} → {HELM_RBAC}") + + +if __name__ == "__main__": + rules = extract_rules_text(ROLE_YAML) + update_helm(HELM_RBAC, rules) diff --git a/helm/temporal-worker-controller/templates/rbac.yaml b/helm/temporal-worker-controller/templates/rbac.yaml index b73faca5..088652ce 100644 --- a/helm/temporal-worker-controller/templates/rbac.yaml +++ b/helm/temporal-worker-controller/templates/rbac.yaml @@ -63,6 +63,14 @@ kind: ClusterRole metadata: name: {{ .Release.Name }}-{{ .Release.Namespace }}-manager-role rules: + # GENERATED RULES BEGIN - do not edit; run 'make manifests' to update + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch - apiGroups: - "" resources: @@ -89,6 +97,12 @@ rules: - deployments/scale verbs: - update + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create - apiGroups: - temporal.io resources: @@ -119,6 +133,7 @@ rules: - temporal.io resources: - temporalworkerdeployments/status + - workerresourcetemplates/status verbs: - get - patch @@ -130,30 +145,10 @@ rules: verbs: - get - list - - watch - patch - update - - apiGroups: - - temporal.io - resources: - - workerresourcetemplates/status - verbs: - - get - - patch - - update - - apiGroups: - - "" - resources: - - events - verbs: - - create - - patch - - apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create + - watch + # GENERATED RULES END # Rules for managing resources attached via WorkerResourceTemplate. # Driven entirely by workerResourceTemplate.allowedResources in values.yaml. {{- range .Values.workerResourceTemplate.allowedResources }} diff --git a/internal/controller/worker_controller.go b/internal/controller/worker_controller.go index 25f2f533..bdd22c40 100644 --- a/internal/controller/worker_controller.go +++ b/internal/controller/worker_controller.go @@ -95,14 +95,17 @@ type TemporalWorkerDeploymentReconciler struct { MaxDeploymentVersionsIneligibleForDeletion int32 } -//+kubebuilder:rbac:groups=temporal.io,resources=temporalworkerdeployments,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=temporal.io,resources=temporalworkerdeployments/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=temporal.io,resources=temporalworkerdeployments/finalizers,verbs=update -//+kubebuilder:rbac:groups=temporal.io,resources=temporalconnections,verbs=get;list;watch -//+kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch -//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=apps,resources=deployments/scale,verbs=update -// +kubebuilder:rbac:groups=events.k8s.io,resources=events,verbs=create;patch +// +kubebuilder:rbac:groups=temporal.io,resources=temporalworkerdeployments,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=temporal.io,resources=temporalworkerdeployments/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=temporal.io,resources=temporalworkerdeployments/finalizers,verbs=update +// +kubebuilder:rbac:groups=temporal.io,resources=temporalconnections,verbs=get;list;watch +// +kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch +// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=apps,resources=deployments/scale,verbs=update +// +kubebuilder:rbac:groups=core,resources=events,verbs=create;patch +// +kubebuilder:rbac:groups=temporal.io,resources=workerresourcetemplates,verbs=get;list;watch;patch;update +// +kubebuilder:rbac:groups=temporal.io,resources=workerresourcetemplates/status,verbs=get;patch;update +// +kubebuilder:rbac:groups=authorization.k8s.io,resources=subjectaccessreviews,verbs=create // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state.