From c839454a1fe7c8ba37b677dcf04e999b679de626 Mon Sep 17 00:00:00 2001 From: Lemon-miaow Date: Tue, 22 Sep 2026 20:18:36 +0800 Subject: [PATCH] fix(manifests): reaper ServiceAccount lives in (and binds from) the Minecraft namespace Follow-up to the CronJob placement fix: a Pod cannot USE a ServiceAccount from another namespace either (live drill: 'error looking up service account minecraft/felis-reaper: serviceaccount not found'). Move the SA and its RoleBinding subject to the Minecraft namespace alongside the CronJob. --- internal/platform/identities.go | 10 +++++----- internal/platform/rbac.go | 11 ++++++++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/internal/platform/identities.go b/internal/platform/identities.go index bd4bd75..d3c4da9 100644 --- a/internal/platform/identities.go +++ b/internal/platform/identities.go @@ -66,11 +66,11 @@ const ( // Params parameterises the install bundle. Namespaces and the registry location // have safe defaults; VelocityCIDRs has none — see the field comment. type Params struct { - // ControlNamespace is where felis-api/operator run. All three SAs live here and - // the RoleBindings' subjects reference them here, even though the Roles they bind - // to live in the minecraft (and build) namespaces. The reaper CronJob alone runs - // in the Minecraft namespace, because a Pod can only mount PVCs from its own - // namespace and its backup PVC is provisioned there. + // ControlNamespace is where felis-api/operator run; their SAs live here and the + // RoleBindings' subjects reference them here, even though the Roles they bind to + // live in the minecraft (and build) namespaces. The reaper alone runs — CronJob + // and SA — in the Minecraft namespace, because a Pod can only mount a PVC and + // use a ServiceAccount from its own namespace, and its backup PVC is there. ControlNamespace string // MinecraftNamespace is where MinecraftServer workloads, their RCON Secrets, // and their world PVCs live. All three identities' minecraft-scoped Roles, and diff --git a/internal/platform/rbac.go b/internal/platform/rbac.go index 740b360..7f49779 100644 --- a/internal/platform/rbac.go +++ b/internal/platform/rbac.go @@ -53,7 +53,9 @@ func ControlPlaneRBAC(p Params) RBAC { }, // Each binding lives in the Role's namespace and names the subject SA in the // control namespace (a RoleBinding may reference an SA from another namespace; - // its roleRef must be a Role in the binding's own namespace). + // its roleRef must be a Role in the binding's own namespace). The reaper + // binding below is the one exception: its CronJob runs in the Minecraft + // namespace, so both the SA and the subject live there. RoleBindings: []*rbacv1.RoleBinding{ bindRole(p.MinecraftNamespace, "felis-api", p.ControlNamespace, SAAPI, ComponentAPI), bindRole(p.BuildNamespace, "felis-api-builds", p.ControlNamespace, SAAPI, ComponentAPI), @@ -62,11 +64,14 @@ func ControlPlaneRBAC(p Params) RBAC { } // The destructive fourth power is conditional on its consumer (see the doc above). if reaperEnabled(p) { + // SAReaper lives in — and its binding subject resolves in — the MINECRAFT + // namespace, because the reaper CronJob runs there (its backup PVC is there; + // a Pod can only mount a PVC and use a ServiceAccount from its own namespace). rbac.ServiceAccounts = append(rbac.ServiceAccounts, - controlPlaneServiceAccount(p.ControlNamespace, SAReaper, ComponentReaper)) + controlPlaneServiceAccount(p.MinecraftNamespace, SAReaper, ComponentReaper)) rbac.Roles = append(rbac.Roles, ReaperRole(p)) rbac.RoleBindings = append(rbac.RoleBindings, - bindRole(p.MinecraftNamespace, "felis-reaper", p.ControlNamespace, SAReaper, ComponentReaper)) + bindRole(p.MinecraftNamespace, "felis-reaper", p.MinecraftNamespace, SAReaper, ComponentReaper)) } return rbac }