Skip to content

fix(kubernetes): reject re-pointing BatchSandbox.spec.poolRef to another pool - #1434

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

fix(kubernetes): reject re-pointing BatchSandbox.spec.poolRef to another pool#1434
tomsen02 wants to merge 1 commit into
opensandbox-group:mainfrom
tomsen02:fix/batchsandbox-poolref-immutable

Conversation

@tomsen02

@tomsen02 tomsen02 commented Aug 4, 2026

Copy link
Copy Markdown

Summary

Fixes #1433.

Re-pointing a bound BatchSandbox.spec.poolRef from one Pool to another triggered a destructive chain: the previous pool recycled the sandbox's in-use Pod as an orphan (default recycler = delete), while the stale alloc-status annotation made the new pool compute supplement = 0 and never supply a replacement — the sandbox dropped to zero Pods permanently, with status still claiming allocated: 1 / Progressing "Sandbox is being created" and no event explaining why. See #1433 for the full root-cause walkthrough and live-cluster reproduction.

This PR adds a CEL transition rule on BatchSandboxSpec so the API server rejects the re-point at admission time, while keeping every currently-defined transition working:

  • initial bind (unset/"" → name)
  • auto-assign resolution ("*" → name)
  • detach (name → unset/"", used by pause/resume solidify)

Changes:

  • apis/sandbox/v1alpha1/batchsandbox_types.go: XValidation marker on BatchSandboxSpec (spec-level with has() guards so unset↔set transitions are covered too — same lesson as fix(api): mark Sandbox.spec.volumeClaimTemplates immutable via CEL kubernetes-sigs/agent-sandbox#858)
  • config/crd/bases/sandbox.opensandbox.io_batchsandboxes.yaml: regenerated via make manifests
  • charts/opensandbox-controller/templates/crds/batchsandboxes.yaml: chart CRD copy kept in sync
  • internal/controller/batchsandbox_poolref_validation_test.go: envtest regression coverage — re-point is rejected and the existing allocation/Pod stays intact; bind-once and detach are still allowed

Testing

  • Not run (explain why)
  • Unit tests — full kubernetes/ suite via envtest (go test ./... excluding /e2e), including the two new regression specs and the existing "detach GC" spec that guards the allowed transition
  • Integration tests
  • e2e / manual verification — on a live Kind cluster (v1.36.1): before the fix the re-point kills the in-use Pod and permanently starves the sandbox; after applying the updated CRD the same kubectl patch fails fast with spec.poolRef cannot be re-pointed to a different pool; clear it first to detach

Breaking Changes

  • None — every transition the controller performs or documents today remains valid; only the previously-undefined (and destructive) pool-to-pool re-point is rejected. CEL validation rules require Kubernetes ≥1.25 (GA 1.29), consistent with the versions exercised in CI.

Checklist

  • Linked Issue or clearly described motivation
  • Added/updated docs (if needed) — behavior is documented on the spec field itself; happy to add a docs page note if maintainers prefer
  • Added/updated tests (if needed)
  • Security impact considered — none; rule only restricts spec transitions
  • Backward compatibility considered — see Breaking Changes

🤖 Generated with Claude Code

…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>
@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".

required:
- replicas
type: object
x-kubernetes-validations:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Raise the Kubernetes minimum before adding CEL rules

On clusters that this chart still advertises as supported (kubernetes/charts/opensandbox-controller/Chart.yaml:27 is >=1.21.1-0), this CRD feature is not available: Kubernetes only graduated CRD validation rules / x-kubernetes-validations to beta in 1.25 (Kubernetes blog). For 1.21–1.24 installs the poolRef transition guard is not enforced, or the CRD can be rejected under strict field validation, so the re-point bug remains or upgrades fail while still inside the declared support window; please either raise the chart/docs minimum to a CEL-capable Kubernetes version or provide a non-CEL fallback.

Useful? React with 👍 / 👎.

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 for the catch — I verified the details before responding:

  • On Kubernetes < 1.25 the apiextensions API server silently drops the x-kubernetes-validations field when the CRD is applied: the CRD still installs, and there is no upgrade breakage — the rule is simply not enforced there. This matches the CI matrix continuing to pass on 1.21.1 / 1.22.4 / 1.24.4 with this PR.
  • So the failure mode on 1.21–1.24 is "guard absent, behavior identical to today's status quo" (the re-point defect simply remains unfixed on those versions), not a rejected CRD or a failed upgrade.

Given that, this PR intentionally degrades gracefully rather than narrowing the declared support window (kubeVersion: ">=1.21.1-0" spans all three charts and the CI matrix still exercises 1.21–1.24): clusters on 1.25+ get the admission-time guard, older clusters keep exactly their current behavior.

Whether to raise the chart kubeVersion floor to a CEL-capable version is a support-window decision I'd rather leave to maintainers — happy to bump it in this PR, or document the limitation instead, whichever is preferred.

// 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.

@Pangjiping Pangjiping self-assigned this Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component/k8s For kubernetes runtime size/M Denotes a PR that changes 30-99 lines, ignoring generated files.

Projects

None yet

2 participants