Add PVC retention policy to WorkerPool

SC
3d ago
+284-4

What type of PR is this?

/kind api

What this PR does / why we need it:

buildStatefulSet sets volumeClaimTemplates on a WorkerPool's StatefulSet but no persistentVolumeClaimRetentionPolicy, so Kubernetes uses its default of Retain/Retain. When a WorkerPool is scaled down or deleted, its workspace PVCs, and the disks behind them, stay around until someone deletes them by hand. A cluster that resizes or recreates pools often builds up a lot of orphaned volumes this way.

This PR adds an optional WorkerPool.spec.persistentVolumeClaimRetentionPolicy (the standard apps/v1 StatefulSetPersistentVolumeClaimRetentionPolicy):

spec:
  persistentVolumeClaimRetentionPolicy:
    whenDeleted: Delete   # Retain | Delete
    whenScaled: Delete    # Retain | Delete
  • The policy is set on the StatefulSet when it is created.
  • reconcileStatefulSet also updates the policy on an existing StatefulSet when it changes. The StatefulSet field is mutable. When comparing, a nil policy or an empty field counts as Retain, which matches what the API server fills in, so a defaulted StatefulSet is not updated again on every reconcile.
  • CEL rules on the CRD reject any value other than Retain or Delete when the WorkerPool is admitted. Without them, a bad value would only show up later, as a failed StatefulSet create.
  • docs/reference.md documents both fields.

Backward compatibility: the field is optional and has no default. If it is unset, the StatefulSet gets no policy and Kubernetes keeps its Retain/Retain default, so existing WorkerPools behave exactly as before. If a policy is set and later removed, the StatefulSet goes back to the API server default (Retain).

Which issue(s) this PR is related to:

N/A

Special notes for your reviewer:

  • I embedded the upstream appsv1 type, as the WorkerPool API already does with corev1.PersistentVolumeClaimSpec, rather than adding a Kelos-owned copy. Validation comes from the two CEL rules.
  • Tests:
    • Unit tests (internal/controller/workerpool_controller_test.go):
      • an unset field gives a nil policy on create
      • a set policy is copied to the StatefulSet on create
      • a changed policy updates an existing StatefulSet. I checked that this test fails when the new update branch is disabled.
      • the comparison treats nil or empty as Retain
    • Integration tests (test/integration/workerpool_test.go, envtest):
      • the policy reaches the StatefulSet and is updated when the WorkerPool changes
      • an invalid value is rejected with the CEL message
    • Ran locally:
      • make update
      • make verify
      • go test ./internal/controller/ ./api/...
      • the WorkerPool integration specs (-ginkgo.focus)
    • I did not run e2e locally.

Does this PR introduce a user-facing change?

WorkerPool supports an optional `spec.persistentVolumeClaimRetentionPolicy` (`whenDeleted` / `whenScaled`: `Retain` or `Delete`), which is passed to the worker StatefulSet so that worker PVCs can be deleted when the pool is scaled down or deleted. Leaving it unset keeps the existing behavior of retaining PVCs.

🤖 Generated with Claude Code


Summary by cubic

Adds an optional spec.persistentVolumeClaimRetentionPolicy to WorkerPool so worker PVCs can be deleted when the pool is scaled down or deleted. Previously the StatefulSet had no policy, so Kubernetes retained PVCs indefinitely.

  • The policy is copied to the worker StatefulSet on create and updates existing StatefulSets when changed.
  • Unset or empty fields default to Retain, matching Kubernetes' default, so existing WorkerPools behave exactly as before.
  • CEL validation on the CRD rejects values other than Retain or Delete.
  • docs/reference.md documents both fields.