Skip to content

Commit d30455d

Browse files
rbac(worker): grant cluster-scoped namespaces list to instant-worker
The orphan_sweep_reconciler's PASS 3 enumerates every instant-deploy-* namespace to find owner-less ones (deployment row / team gone). The instant-worker SA had only get+delete on namespaces, so ListDeployNamespaces was denied with a Forbidden error every sweep -> the job errored -> River retried -> ERROR-level log spam every ~60s. Adds the `list` verb to the existing instant-worker-stack-cleaner ClusterRole (namespaces get+list+delete). Per-app namespaces are created on demand by the api process and cannot be enumerated at RBAC-bind time, so a cluster-scoped list is required. RBAC is additive — safe to apply. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 53388ef commit d30455d

1 file changed

Lines changed: 50 additions & 5 deletions

File tree

k8s/worker-rbac.yaml

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,29 @@ metadata:
77
namespace: instant-infra
88

99
---
10-
# ClusterRole granting the worker permission to delete stack namespaces.
11-
# The worker only deletes namespaces it finds in the DB; the label constraint
12-
# in the code (namespace must start with cfg.KubeNamespaceApps prefix) is an
13-
# extra defence-in-depth guardrail.
10+
# ClusterRole granting the worker permission to list and delete stack
11+
# namespaces.
12+
#
13+
# delete: the team_deletion_executor tears down a deleted team's deploy
14+
# namespaces (it only deletes namespaces it finds in the DB; the label
15+
# constraint in the code — namespace must start with cfg.KubeNamespaceApps
16+
# prefix — is an extra defence-in-depth guardrail).
17+
#
18+
# list: the orphan_sweep_reconciler's PASS 3 (see
19+
# worker/internal/jobs/orphan_sweep_reconciler.go) enumerates every
20+
# instant-deploy-* namespace to find owner-less ones whose backing
21+
# deployment row / team is gone. Per-app namespaces are created on demand
22+
# by the api process, so they cannot be enumerated at RBAC-bind time — a
23+
# cluster-scoped list is required. Without this verb the reconciler's
24+
# ListDeployNamespaces is denied with a Forbidden error every sweep.
1425
apiVersion: rbac.authorization.k8s.io/v1
1526
kind: ClusterRole
1627
metadata:
1728
name: instant-worker-stack-cleaner
1829
rules:
1930
- apiGroups: [""]
2031
resources: ["namespaces"]
21-
verbs: ["get", "delete"]
32+
verbs: ["get", "list", "delete"]
2233

2334
---
2435
# Bind the ClusterRole to the instant-worker ServiceAccount.
@@ -34,3 +45,37 @@ subjects:
3445
- kind: ServiceAccount
3546
name: instant-worker
3647
namespace: instant-infra
48+
49+
---
50+
# ClusterRole granting the worker permission to read Deployment objects across
51+
# all "instant-deploy-*" namespaces. Used by DeployStatusReconciler (see
52+
# worker/internal/jobs/deploy_status_reconcile.go) to reconcile the
53+
# deployments.status DB column from live k8s state every 30s.
54+
#
55+
# This is intentionally cluster-scoped because per-app namespaces are created
56+
# on demand by the api process — we can't enumerate them at RBAC-bind time.
57+
# Read-only get on deployments.apps is the tightest verb set that makes the
58+
# reconciler work; the worker has no need to list/watch/patch and gets none.
59+
apiVersion: rbac.authorization.k8s.io/v1
60+
kind: ClusterRole
61+
metadata:
62+
name: instant-worker-deploy-reader
63+
rules:
64+
- apiGroups: ["apps"]
65+
resources: ["deployments"]
66+
verbs: ["get"]
67+
68+
---
69+
# Bind the deploy-reader ClusterRole to the instant-worker ServiceAccount.
70+
apiVersion: rbac.authorization.k8s.io/v1
71+
kind: ClusterRoleBinding
72+
metadata:
73+
name: instant-worker-deploy-reader
74+
roleRef:
75+
apiGroup: rbac.authorization.k8s.io
76+
kind: ClusterRole
77+
name: instant-worker-deploy-reader
78+
subjects:
79+
- kind: ServiceAccount
80+
name: instant-worker
81+
namespace: instant-infra

0 commit comments

Comments
 (0)