Widen node-stats-mcp deployer Role: add services/configmaps/ingresses/middlewares (CD half-applied on #28, RBAC forbidden) #465

Closed
opened 2026-07-04 07:05:01 +00:00 by coilysiren · 2 comments
Owner

What

Widen the node-stats-mcp deployer Role in deploy/forgejo-runner-deploy.yml so CD can apply everything the node-stats bundle now contains. Today that Role grants only pods (get/list/watch) + deployments/replicasets, but deploy#28 expanded the node-stats-mcp namespace to also hold a Service, a ConfigMap, two Ingresses, and a Traefik Middleware.

Evidence (live CD failure)

The deploy-node-stats-mcp.yml CD run on the #28 push (89d1244) half-applied: Deployments went through, everything else was denied. Verbatim:

services "node-stats-mcp" is forbidden: User "system:serviceaccount:forgejo:deployer" cannot get resource "services" in API group "" in the namespace "node-stats-mcp"
middlewares.traefik.io "node-stats-mcp-authelia" is forbidden: ... cannot get resource "middlewares" in API group "traefik.io" ...
configmaps "node-stats-mcp-meta" is forbidden: ... cannot get resource "configmaps" in API group "" ...
ingresses.networking.k8s.io "node-stats-mcp" is forbidden: ... cannot get resource "ingresses" in API group "networking.k8s.io" ...

The consequence downstream: the node-stats-mcp-meta pod sat in ContainerCreating for 64m with FailedMount: configmap "node-stats-mcp-meta" not found, because RBAC blocked the ConfigMap the meta pod mounts.

The fix

Add rules to the existing node-stats-mcp deployer Role (around line 155 of deploy/forgejo-runner-deploy.yml) covering every kind the bundle applies. Scope to exactly what coilyco-bridge/deploy services/node-stats-mcp/deploy/main.yml declares — currently:

  • "" (core): pods (get/list/watch), services, configmaps
  • apps: deployments, replicasets
  • networking.k8s.io: ingresses
  • traefik.io: middlewares

Give the mutable kinds the same verb set the sibling public-Ingress services use (get, list, watch, create, update, patch, delete). Model on the coilysiren-eco-gnome / coilysiren-eco-app deployer Roles in this same file (they already carry Service/Ingress/ConfigMap/Middleware verbs), not the current node-stats-mcp Role. The RoleBinding already exists and is unchanged.

Coordination note

coilyco-flight-deck/infrastructure#464 (authelia deployer Role) is in flight against this same file concurrently. It adds a new authelia block; this issue edits the existing node-stats-mcp block, so they touch different regions and should 3-way-merge cleanly. If #464 has already landed when you start, rebase onto it; if not, expect to resolve a trivial adjacency at most.

Not in scope

  • The separate CD wrinkle in the same run: ls-remote unavailable (unauthenticated runner?) falling back to the running sha for the image tag. Real but independent - filed/handled separately if it recurs.
  • No change to coilyco-bridge/deploy.

Done when

The node-stats-mcp deployer Role in deploy/forgejo-runner-deploy.yml covers services/configmaps/ingresses/middlewares (+ existing pods/deployments), lints clean, lands on main, and a subsequent deploy-node-stats-mcp.yml CD run applies the full bundle with no Forbidden errors.

Provenance: director surface session, 2026-07-04, configuring deploy#28 (node-stats CD half-applied on RBAC).

## What Widen the **`node-stats-mcp`** `deployer` Role in `deploy/forgejo-runner-deploy.yml` so CD can apply everything the node-stats bundle now contains. Today that Role grants only `pods` (get/list/watch) + `deployments`/`replicasets`, but **deploy#28** expanded the `node-stats-mcp` namespace to also hold a Service, a ConfigMap, two Ingresses, and a Traefik Middleware. ## Evidence (live CD failure) The `deploy-node-stats-mcp.yml` CD run on the #28 push (`89d1244`) half-applied: Deployments went through, everything else was denied. Verbatim: ``` services "node-stats-mcp" is forbidden: User "system:serviceaccount:forgejo:deployer" cannot get resource "services" in API group "" in the namespace "node-stats-mcp" middlewares.traefik.io "node-stats-mcp-authelia" is forbidden: ... cannot get resource "middlewares" in API group "traefik.io" ... configmaps "node-stats-mcp-meta" is forbidden: ... cannot get resource "configmaps" in API group "" ... ingresses.networking.k8s.io "node-stats-mcp" is forbidden: ... cannot get resource "ingresses" in API group "networking.k8s.io" ... ``` The consequence downstream: the `node-stats-mcp-meta` pod sat in `ContainerCreating` for 64m with `FailedMount: configmap "node-stats-mcp-meta" not found`, because RBAC blocked the ConfigMap the meta pod mounts. ## The fix Add rules to the existing `node-stats-mcp` deployer Role (around line 155 of `deploy/forgejo-runner-deploy.yml`) covering every kind the bundle applies. Scope to exactly what `coilyco-bridge/deploy` `services/node-stats-mcp/deploy/main.yml` declares — currently: - `""` (core): `pods` (get/list/watch), `services`, `configmaps` - `apps`: `deployments`, `replicasets` - `networking.k8s.io`: `ingresses` - `traefik.io`: `middlewares` Give the mutable kinds the same verb set the sibling public-Ingress services use (`get, list, watch, create, update, patch, delete`). Model on the **`coilysiren-eco-gnome` / `coilysiren-eco-app`** deployer Roles in this same file (they already carry Service/Ingress/ConfigMap/Middleware verbs), not the current node-stats-mcp Role. The RoleBinding already exists and is unchanged. ## Coordination note `coilyco-flight-deck/infrastructure#464` (authelia deployer Role) is in flight against **this same file** concurrently. It adds a new `authelia` block; this issue edits the existing `node-stats-mcp` block, so they touch different regions and should 3-way-merge cleanly. If #464 has already landed when you start, rebase onto it; if not, expect to resolve a trivial adjacency at most. ## Not in scope - The separate CD wrinkle in the same run: `ls-remote unavailable (unauthenticated runner?)` falling back to the running sha for the image tag. Real but independent - filed/handled separately if it recurs. - No change to `coilyco-bridge/deploy`. ## Done when The `node-stats-mcp` deployer Role in `deploy/forgejo-runner-deploy.yml` covers services/configmaps/ingresses/middlewares (+ existing pods/deployments), lints clean, lands on `main`, and a subsequent `deploy-node-stats-mcp.yml` CD run applies the full bundle with no `Forbidden` errors. Provenance: director surface session, 2026-07-04, configuring deploy#28 (node-stats CD half-applied on RBAC).
Member

🔒 Reserved by ward agent --driver claude — container engineer-claude-infrastructure-465 on host KAI-DESKTOP-TOWER is carrying this issue (reserved 2026-07-04T07:05:19Z). Concurrent ward agent runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); --force overrides.

Do not comment on or edit this issue to steer the run while it is reserved. The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a new issue, dispatched fresh — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494).

— Claude (she/her), via ward agent

<!-- ward-agent-reservation --> 🔒 Reserved by `ward agent --driver claude` — container `engineer-claude-infrastructure-465` on host `KAI-DESKTOP-TOWER` is carrying this issue (reserved 2026-07-04T07:05:19Z). Concurrent `ward agent` runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); `--force` overrides. **Do not comment on or edit this issue to steer the run while it is reserved.** The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a **new issue, dispatched fresh** — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494). <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Author
Owner

WARD-OUTCOME: done - node-stats-mcp deployer Role widened to services/configmaps/ingresses/middlewares, landed on main (be85c09), CI green.

Straightforward one. #464 (authelia) had already landed ahead of me, so the coordination note resolved itself - no adjacency to reconcile, the authelia block sat well clear of the node-stats-mcp region. I modeled the new rules on the authelia block specifically because it already does the clean thing of splitting read-only pods (get/list/watch) out from the mutable kinds, rather than flattening everything to one verb set. So pods stays read-only and services/configmaps/ingresses/middlewares get the full get/list/watch/create/update/patch/delete.

Only thing that fought back was environmental: the pylint pre-commit hook choked trying to write uv's python interpreter into /opt/uv/python (permission denied in this container). Pinning UV_PYTHON_INSTALL_DIR to a writable home dir cleared it, and the hook then passed - my diff is YAML-only so pylint was never actually unhappy with the change.

Confident in the result: the diff mirrors the proven eco/authelia Roles exactly, and the post-push commit status shows both CI / lint and TruffleHog green. The real end-to-end proof is the next deploy-node-stats-mcp.yml CD run applying the full bundle with no Forbidden errors - that runs on the coilyco-bridge/deploy side and I can't trigger it from here, so worth a glance next time #28's bundle redeploys. No follow-ups to file.

WARD-OUTCOME: done - node-stats-mcp deployer Role widened to services/configmaps/ingresses/middlewares, landed on main (be85c09), CI green. Straightforward one. #464 (authelia) had already landed ahead of me, so the coordination note resolved itself - no adjacency to reconcile, the authelia block sat well clear of the node-stats-mcp region. I modeled the new rules on the authelia block specifically because it already does the clean thing of splitting read-only `pods` (get/list/watch) out from the mutable kinds, rather than flattening everything to one verb set. So `pods` stays read-only and services/configmaps/ingresses/middlewares get the full get/list/watch/create/update/patch/delete. Only thing that fought back was environmental: the pylint pre-commit hook choked trying to write uv's python interpreter into `/opt/uv/python` (permission denied in this container). Pinning `UV_PYTHON_INSTALL_DIR` to a writable home dir cleared it, and the hook then passed - my diff is YAML-only so pylint was never actually unhappy with the change. Confident in the result: the diff mirrors the proven eco/authelia Roles exactly, and the post-push commit status shows both `CI / lint` and TruffleHog green. The real end-to-end proof is the next `deploy-node-stats-mcp.yml` CD run applying the full bundle with no Forbidden errors - that runs on the coilyco-bridge/deploy side and I can't trigger it from here, so worth a glance next time #28's bundle redeploys. No follow-ups to file.
Sign in to join this conversation.
No description provided.