fix(manifests): render the retention reaper CronJob into the Minecraft namespace

A Pod can only mount PVCs from its own namespace; the CronJob referenced the
minecraft-namespace backup PVC while being rendered under ControlNamespace, so
it could never schedule — live drill: FailedScheduling 'persistentvolumeclaim
felis-backups not found'. The reaper Role/RoleBinding were already
minecraft-scoped (the objects it touches live there), so the CronJob was the
odd one out. The minecraft felis-config replica (felis setup, backup Job fix)
supplies its config mount.
This commit is contained in:
Lemon-miaow committed 2026-09-22 20:14:19 +08:00
1 parent 0414913bc7
commit e4f2cff532
3 files changed
+19 -7

No files matched your search

+5 -3
View File
@@ -66,9 +66,11 @@ const (
// Params parameterises the install bundle. Namespaces and the registry location // Params parameterises the install bundle. Namespaces and the registry location
// have safe defaults; VelocityCIDRs has none — see the field comment. // have safe defaults; VelocityCIDRs has none — see the field comment.
type Params struct { type Params struct {
// ControlNamespace is where felis-api/operator/reaper run. Their SAs live here // ControlNamespace is where felis-api/operator run. All three SAs live here and
// and the RoleBindings' subjects reference them here, even though the Roles // the RoleBindings' subjects reference them here, even though the Roles they bind
// they bind to live in the minecraft (and build) namespaces. // 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 string ControlNamespace string
// MinecraftNamespace is where MinecraftServer workloads, their RCON Secrets, // MinecraftNamespace is where MinecraftServer workloads, their RCON Secrets,
// and their world PVCs live. All three identities' minecraft-scoped Roles, and // and their world PVCs live. All three identities' minecraft-scoped Roles, and
+9 -2
View File
@@ -472,8 +472,15 @@ func reaperCronJob(p Params) *batchv1.CronJob {
} }
return &batchv1.CronJob{ return &batchv1.CronJob{
TypeMeta: metav1.TypeMeta{APIVersion: "batch/v1", Kind: "CronJob"}, TypeMeta: metav1.TypeMeta{APIVersion: "batch/v1", Kind: "CronJob"},
ObjectMeta: metav1.ObjectMeta{Name: SAReaper, Namespace: p.ControlNamespace, Labels: labels}, // The CronJob lives in the MINECRAFT namespace: a Pod can only mount PVCs
// from its own namespace and the backup PVC is provisioned there alongside
// the backup Jobs. Placed under ControlNamespace it could never schedule
// (FailedScheduling: persistentvolumeclaim not found) in any stock install;
// the reaper Role/RoleBinding were already minecraft-scoped for the same
// reason, and the minecraft felis-config replica (felis setup) supplies the
// config mount.
ObjectMeta: metav1.ObjectMeta{Name: SAReaper, Namespace: p.MinecraftNamespace, Labels: labels},
Spec: batchv1.CronJobSpec{ Spec: batchv1.CronJobSpec{
Schedule: reaperSchedule, Schedule: reaperSchedule,
ConcurrencyPolicy: batchv1.ForbidConcurrent, ConcurrencyPolicy: batchv1.ForbidConcurrent,
+5 -2
View File
@@ -519,8 +519,11 @@ func TestReaperCronJob_Shape(t *testing.T) {
if cj.Name != SAReaper { if cj.Name != SAReaper {
t.Errorf("CronJob name = %q, want %q", cj.Name, SAReaper) t.Errorf("CronJob name = %q, want %q", cj.Name, SAReaper)
} }
if cj.Namespace != p.ControlNamespace { // The CronJob must sit where its PVC lives: a Pod cannot mount a PVC across
t.Errorf("CronJob namespace = %q, want control ns %q", cj.Namespace, p.ControlNamespace) // namespaces, and the backup PVC is provisioned in the Minecraft namespace.
// (Rendered under ControlNamespace it failed to schedule on a live cluster.)
if cj.Namespace != p.MinecraftNamespace {
t.Errorf("CronJob namespace = %q, want minecraft ns %q (its backup PVC's namespace)", cj.Namespace, p.MinecraftNamespace)
} }
spec := cj.Spec spec := cj.Spec