Skip to content

fix(kubernetes): preserve pool allocations when poolRef changes - #1434

Open
tomsen02 wants to merge 3 commits into
opensandbox-group:mainfrom
tomsen02:fix/batchsandbox-poolref-immutable
Open

tomsen02 wants to merge 3 commits into
opensandbox-group:mainfrom
tomsen02:fix/batchsandbox-poolref-immutable

Conversation

@tomsen02

@tomsen02 tomsen02 commented Aug 4, 2026 •

Copy link
Copy Markdown

Revised implementation: 35f192df, pushed to the original fix/batchsandbox-poolref-immutable branch. This PR is still closed because both reopen APIs rejected the request; its Files changed / Checks views currently refer to the previous revision. A maintainer reopen is requested below.

Summary

Addresses #1433 for Pool allocations with a recorded source Pool.

Changing an allocated BatchSandbox from Pool A to Pool B previously made A recycle its in-use Pods, while stale allocation state prevented B from replacing them. The controller now keeps the existing allocation attached to the Pool recorded in alloc-status, including after controller restart. It reports a PoolRefUpdateRejected condition and Warning event when the requested reference differs, including a change back to "*".

The requested spec stays visible for the user to correct; the API update is accepted rather than rejected at admission. Restoring the recorded reference clears the condition. Initial binding, unallocated auto-assignment, and the existing explicit-detach/pause-resume flow remain supported.

The implementation removes the CEL transition rule from both generated and Helm CRDs, retaining Kubernetes 1.22 compatibility without introducing a webhook. Pool indexing, event routing, allocation sync/recovery, and finalizer cleanup consistently use the committed source. Annotation keys and JSON shapes are unchanged.

Legacy records without poolRef retain existing behavior until the existing Pool-controller backfill verifies and upgrades them. This does not add live migration between Pools or an admission-time immutability guarantee.

Testing

  • Reproduced the destructive orphan-recycle path with CEL removed before applying the controller fix.
  • make test: complete Kubernetes unit/envtest suite passed (Kubernetes 1.33.0 assets), including existing pause/resume, detach, and legacy-backfill tests.
  • Kubernetes v1.22.4 API server + etcd, with real local controllers: the focused BatchSandbox poolRef guard integration specs passed. Both generated and Helm-rendered BatchSandbox CRDs also passed server-side dry-run apply on v1.22.4.
  • make lint: passed; Helm lint/template and documentation build passed.
  • Added unit coverage for restart recovery, allocation resync, Pool indexing, repeated warnings, wildcard re-pointing, and detach; added a Core E2E regression for preserving the original Pod across pool-to-pool and pool-to-wildcard changes.
  • E2E package compiles. Full Kind E2E could not run locally because node startup failed before Kubernetes started (missing systemd/cgroup readiness log); the repository CI matrix remains the full-cluster compatibility check.

Compatibility

No CEL rules or new admission component. The CRD change adds one status-condition enum value. The guard applies to committed Pool allocations; it does not claim to reject every possible pre-allocation spec transition.

AI assistance (OpenAI Codex) was used for this revision and local validation.

@github-actions github-actions Bot added component/k8s For kubernetes runtime size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Aug 4, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bb966c92ea

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread kubernetes/charts/opensandbox-controller/templates/crds/batchsandboxes.yaml Outdated
// detach; re-pointing a bound sandbox to a different pool is rejected because
// the previous pool would recycle the in-use pods while the stale allocation
// record blocks the new pool from supplying replacements.
// +kubebuilder:validation:XValidation:rule="!has(oldSelf.poolRef) || size(oldSelf.poolRef) == 0 || oldSelf.poolRef == '*' || !has(self.poolRef) || size(self.poolRef) == 0 || self.poolRef == oldSelf.poolRef",message="spec.poolRef cannot be re-pointed to a different pool; clear it first to detach"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: this rule raises the project Kubernetes compatibility floor to >= 1.25, conflicting with what the repo claims and tests today.

x-kubernetes-validations with oldSelf (transition rules) requires Kubernetes >= 1.25 to be accepted and enforced (GA in 1.29). On 1.23/1.24 it is behind the ValidationRules feature gate (off by default), and on < 1.23 the field is rejected outright — the CRD cannot be installed on those clusters at all.

But this repo currently:

  • Runs e2e on Kind 1.21.1 / 1.22.4 / 1.24.4 in CI (.github/workflows/kubernetes-test.yml), and the e2e suite installs the CRDs via make install (test/e2e/e2e_test.go) — those legs will fail at CRD apply.
  • Declares kubeVersion: ">=1.21.1-0" in charts/opensandbox-controller/Chart.yaml, says "Kubernetes 1.21.1+" in the chart README, and defaults local e2e to KIND_K8S_VERSION=v1.22.4 (kubernetes/Makefile).

So the "Breaking Changes: None" claim in the PR description is not accurate — this effectively drops support for < 1.25 clusters. We need a maintainer decision before merge:

  1. Raise the documented/CI support floor to >= 1.25 (update Chart.yaml kubeVersion, chart README, CI e2e matrix, local e2e default), or
  2. Enforce the invariant with a version-agnostic mechanism (validating admission webhook, or a controller-side guard that rejects/events on re-pointing).

The rule logic itself is correct for the documented transitions (initial bind, "*" -> name write-back, detach, no-op); the concern is purely deployment-surface compatibility.

@tomsen02 tomsen02 Aug 12, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — you're right. My earlier statement that the older-version CI matrix continued to pass was not supported; this PR has only run the auto-label check.

I also checked the existing validation infrastructure. OpenSandbox does not currently enable a validating webhook, so replacing the CEL rule with one would add a new deployment surface (webhook service/certificates and Helm/Kustomize wiring). A controller-side rollback cannot reject the update at admission time and may race with the old pool's orphan cleanup.

I agree that raising the Kubernetes support floor should not be done implicitly in this bug fix. Would you prefer introducing validating-webhook support for this invariant, or handling pool-to-pool re-pointing as an explicit safe controller transition? I'm happy to revise the PR once the intended direction is clear.

AI usage disclosure: I used OpenAI Codex to inspect the repository's existing validation/controller paths and help draft this response; I reviewed the conclusions before posting.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the pushback — I went back and verified against the Kubernetes source, and you're right. On 1.21/1.22 the XValidations field doesn't exist in the apiextensions v1 types (it was added in 1.23), so x-kubernetes-validations is silently dropped during decoding and the CRD applies cleanly — my earlier claim that the CRD cannot be installed on those clusters was wrong. On 1.23/1.24 the field is accepted but not enforced (alpha feature gate, off by default); enforcement starts at 1.25 (beta, on by default; GA in 1.29). So on 1.22 everything does run, exactly as you described: guard absent, behavior unchanged.

What remains is the softer version of my concern — on <1.25 clusters the fix silently doesn't protect, while the chart still advertises kubeVersion: ">=1.21.1-0" — which is a support-window / documentation question rather than a compatibility break. Let me think it over and follow up.

@Pangjiping Pangjiping self-assigned this Aug 10, 2026
…her pool

Re-pointing a bound BatchSandbox from pool A to pool B made pool A recycle
the in-use pod as an orphan while the stale alloc-status annotation kept
pool B from supplying a replacement, permanently starving the sandbox with
no event explaining why.

Add a CEL transition rule on BatchSandboxSpec so the API server rejects
the re-point while still allowing the defined transitions: initial bind,
auto-assign resolution ("*" -> name), and detach (clear poolRef). Sync the
generated CRD and the Helm chart copy, and add envtest regression coverage
for both the rejected and the allowed transitions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Pangjiping
Pangjiping force-pushed the fix/batchsandbox-poolref-immutable branch from bb966c9 to e5bb6b4 Compare September 14, 2026 11:01
@Pangjiping

Copy link
Copy Markdown
Collaborator

Closing for now: this PR breaks Kubernetes 1.22 compatibility, which we need to keep supporting.

Problem

The PR adds CEL validation rules (x-kubernetes-validations) to the BatchSandbox CRD (both in the Helm chart template and config/crd/bases) to enforce poolRef immutability. CEL rules on CRDs require Kubernetes >= 1.25, so on v1.22 the apiserver rejects the CRD entirely:

error validating "STDIN": error validating data:
ValidationError(CustomResourceDefinition.spec.versions[0].schema.openAPIV3Schema.properties.spec):
unknown field "x-kubernetes-validations" in ...JSONSchemaProps

This is why the E2E Controller E2E Core (v1.22.4) / Controller E2E PauseResume (v1.22.4) jobs fail: make install fails to apply the CRD, the Manager [BeforeAll] spec fails, and all 48 remaining specs get skipped. The Kubernetes CI aggregate job then fails as a gate.

Impact

This is not just CI noise — once merged, any 1.22 cluster cannot apply the CRD at all, so the controller cannot be installed there. There is also no "CEL + webhook fallback" hybrid that restores 1.22 support, because the CRD manifest itself becomes unappliable.

Path forward

To keep 1.22 compatibility, poolRef immutability should be enforced at the controller/webhook layer instead (reject the update in code and surface an error/event), without embedding CEL rules in the CRD schema. Feel free to re-open with that approach — thanks for working on this!

@Pangjiping Pangjiping closed this Sep 15, 2026
@tomsen02 tomsen02 changed the title fix(kubernetes): reject re-pointing BatchSandbox.spec.poolRef to another pool fix(kubernetes): preserve pool allocations when poolRef changes Sep 16, 2026
@tomsen02

Copy link
Copy Markdown
Author

The Kubernetes 1.22 compatibility feedback is addressed in 35f192df, pushed to this PR's original branch.

The revision removes CEL from both CRDs and uses the existing allocation's recorded source Pool across indexing, routing, sync, recovery, and cleanup. Unsupported pool-to-pool or pool-to-* changes retain the original Pods and report a PoolRefUpdateRejected condition / Warning event. The API update itself is accepted. Explicit detach remains supported; legacy records rely on the existing verified backfill. The description documents these boundaries.

Validation passed: complete make test, lint, Helm validation, docs build, and focused race coverage. I also ran the controller regression specs against an isolated v1.22.4 API server + etcd, and server-side validated both generated and Helm-rendered CRDs there. This checks the compatibility failure directly. The new full-cluster E2E regression compiles; local Kind node startup failed before Kubernetes started, so the full E2E matrix still needs CI.

Could a maintainer reopen this original PR so it picks up the updated branch and runs CI? gh pr reopen returned Could not open the pull request; REST returned HTTP 422 without a specific validation reason. Reopening at the original closing head also failed. The tested revision is restored on the branch. I have not opened a replacement PR.

AI assistance (OpenAI Codex) was used for this revision and validation.

@Pangjiping Pangjiping reopened this Sep 16, 2026
@Pangjiping
Pangjiping requested a review from jwx0925 as a code owner September 16, 2026 02:04
@github-actions github-actions Bot added documentation Improvements or additions to documentation size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Sep 16, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component/k8s For kubernetes runtime documentation Improvements or additions to documentation size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants