docs(kubernetes-support): name what older clusters silently lose - #421
Conversation
Item 8 of #411. The page claimed the operator "relies only on core, long-stable Kubernetes APIs" and "gates on no version-specific features", and marked ~1.23-1.32 as expected to work. That stopped being true when package execution moved to batch/v1 Jobs. The Jobs path depends on podFailurePolicy, the DisruptionTarget pod condition, the FailureTarget Job condition, podReplacementPolicy, the batch.kubernetes.io/* pod labels, suspend and ttlSecondsAfterFinished. An apiserver that does not know a field drops it and returns success, so on an older cluster the operator creates a healthy-looking Job with the field absent and the property it guaranteed simply gone — no error, event, or log line, and nothing surfaces until the case that field existed to handle actually occurs. Replaces the blanket claim with a cumulative table: each row lists what newly stops working below that version, so reading top-down accumulates the losses. Splits the old 1.23-1.32 band into 1.29-1.32 (every field at least beta-on-by-default, losses theoretical) and 1.23-1.28 (real degradation). The sharpest row is below 1.26, and it got sharper with #402: without podFailurePolicy the Ignore-on-DisruptionTarget rule disappears, so evictions and preemptions count toward backoffLimit like genuine failures. At the old unbounded limit that was harmless; at a finite JOB_BACKOFF_LIMIT a couple of unrelated disruptions can park a package that never failed. Version numbers track upstream feature-gate graduation, not measured NodeWright behaviour — none of these clusters are in CI, and the table says so rather than implying a tested promise. Refs #411 (item 8). Signed-off-by: Alex Yuskauskas <ayuskauskas@nvidia.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Enterprise Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe Kubernetes support documentation now includes a version-specific support matrix for Job feature degradation below Kubernetes 1.33. It documents omitted fields, disabled feature gates, and effects on replacement safety, pod discovery, failure handling, disruption handling, deadlines, attempt accounting, cleanup, and suspension. It also updates version guidance for Kubernetes 1.33–1.36 and older clusters. Estimated code review effort: 1 (Trivial) | ~5 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/kubernetes-support.md`:
- Line 33: Update the Kubernetes 1.26 entry to remove the incorrect claim that
JOB_BACKOFF_LIMIT defaults to 3 and causes a finite backoff budget. Document
that JobSpec.BackoffLimit is configured to math.MaxInt32, resulting in
effectively unlimited retries, while preserving the surrounding version-specific
behavior and deadline-log statement.
- Line 31: Update the Kubernetes 1.29 compatibility entry describing
podReplacementPolicy so it no longer guarantees that concurrent executor writes
are only a race without corruption. Either implement synchronization for shared
generation-based copyDir and package writes and add a concurrent replacement
test, or revise the documentation to explicitly state that concurrent
replacements may corrupt data.
- Around line 14-15: Update the Kubernetes compatibility documentation to use
separate version thresholds for podFailurePolicy/DisruptionTarget support and
the deadline FailureTarget signal: retain 1.26 as the beta availability point,
but identify 1.31 as the start of the delayed-terminal FailureTarget behavior.
Revise the support table and the claims near the Kubernetes support overview and
older-cluster limitations so versions 1.29–1.30 are not described as having the
FailureTarget deadline signal.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Enterprise
Run ID: 8bdcdb6c-7c18-4793-8eb1-14eebbebb0bc
📒 Files selected for processing (1)
docs/kubernetes-support.md
Review question on #421 that the row could not survive: does losing the succeeded-container-name lookup mean we stop knowing a stage completed? No, and the row implied otherwise. Completion is read from the Job's Complete condition, never from pods (job_controller.go:164). The container name it finds feeds one variable whose only use is an equality check against InterruptContainerName (job_controller.go:342), and interrupt Jobs source that from the Job's own label rather than a pod. For every other stage it is cosmetic, exactly as the comment above the call says. Listing it as a cost was wrong. The claim that failure evidence stops being retained was backwards too: losing pruneFailedAttempts means archive pods accumulate rather than being trimmed to two, which is a scale problem, not an evidence problem. The row now names the two real costs — unbounded archive accumulation, and the last-logs snapshot never firing for a container that never started — and states plainly that completion is unaffected. Refs #411 (item 8). Signed-off-by: Alex Yuskauskas <ayuskauskas@nvidia.com>
|
Corrected the 1.27 row in 6db86ef — it overstated the cost, in two directions. Completion is not affected. It is read from the Job's
Listing it as a cost was wrong. "Failure evidence stops being retained" was backwards. Losing The row now names the two real costs — unbounded archive accumulation, and the One thing this dug up that is worth a reviewer's eye, and that I am not putting in the doc because it is speculative: After #402, That does not break parking, because the Pod watch is the other writer and it is unaffected — child pods inherit What it does mean is that on those clusters the park loses its backstop and rests entirely on the Pod watch — which is precisely the two-writer invariant documented in #413. Worth knowing, not worth a table row. |
…two unfounded claims CodeRabbit review on #421; all three findings were right. FailureTarget is a later threshold than podFailurePolicy, not the same one. podFailurePolicy and DisruptionTarget are beta-on from 1.26, but 1.29-1.30 report a deadline expiry directly as JobFailed — the delayed-terminal behaviour that raises FailureTarget first arrives in 1.31. Everything the operator hangs off that condition therefore breaks a full five minors earlier than the table claimed, and it is more than the log snapshot: failureTargetStale, the path that surfaces erroring for a Job wedged on an unreachable node, keys on it too. Split into its own row, removed from the 1.26 row, and the intro band split accordingly. The "race, not corruption" line was a guarantee I had no business making. Flag files make re-execution idempotent; they are not a lock and do not order two concurrent cp -r runs into the same copyDir, so a step script can read a file another attempt is mid-way through overwriting. Now described as a possible-corruption configuration. The 1.26 row cited a finite JOB_BACKOFF_LIMIT defaulting to 3. That knob does not exist on this branch — it arrives with #402, and here BackoffLimit is MaxInt32. Rephrased to hold in both regimes rather than forward-referencing an unmerged PR. Also de-duplicated the snapshot loss out of the 1.27 row: the table is cumulative, and by 1.27 it has already been lost at 1.31. Refs #411 (item 8). Signed-off-by: Alex Yuskauskas <ayuskauskas@nvidia.com>
…e overlap safe CodeRabbit review on #422. Three findings; a fourth comment was it confirming the previous round rather than a new issue. NewPauseCmd still said pause "will not interrupt any currently running operations" — the pre-Jobs behaviour. NewEnableCmd said the operator "will resume normal processing after this command", which is untrue while pause is still set. Fixing disable's help last round and leaving its two siblings stale was the same miss the previous review caught, one command over. Both now describe what the Jobs path actually does, pause version-qualified because its stop-strength depends on the operator generation. docs/cli.md asserted pause halts running work and then, fourteen lines later, said older operators let the current run finish. Now version-qualified inline instead of relying on the reader following a pointer. The design doc's paused-legacy note leaned on the agent's flag files as if they made the overlap safe. They do not: they make re-execution idempotent, they are not a lock, and they neither serialize two executors nor order concurrent writes into the shared copyDir. The same overstatement is being corrected on #421. The note now says the overlap is unguarded while the window is open, and names what closing it would take — teaching the in-flight gate to count legacy raw pods. Refs #411 (items 2 and 5a). Signed-off-by: Alex Yuskauskas <ayuskauskas@nvidia.com>
|
All three addressed in 50f3be6. All three were right.
"Race, not corruption" — a guarantee I had no business making. Flag files make re-execution idempotent; they are not a lock and do not order two concurrent The One thing I fixed that none of the three named: the 1.27 row still listed the
|
Refs #411 (item 8). Does not close it. Part of #223, based on
feature/package-as-jobs.The problem
docs/kubernetes-support.mdclaimed the operator "relies only on core, long-stable Kubernetes APIs" and "gates on no version-specific features", and marked ~1.23 – 1.32 as expected to work.That was true before package execution moved to
batch/v1Jobs. It isn't now. The Jobs path depends onpodFailurePolicy, theDisruptionTargetpod condition, theFailureTargetJob condition,podReplacementPolicy, thebatch.kubernetes.io/*pod labels,suspend, andttlSecondsAfterFinished.The failure mode is what makes this worth documenting rather than just fixing a range. An apiserver that doesn't know a field drops it and returns success — it doesn't reject the Job. So on an older cluster the operator creates a healthy-looking Job with the field simply absent, the property it guaranteed is gone, and there is no error, event, or log line. Nothing surfaces until the situation that field existed to handle actually occurs.
What this adds
A cumulative table — each row lists what newly stops working below that version, so reading top-down accumulates:
podReplacementPolicy→ two executors can share one hostcopyDirbatch.kubernetes.io/*pod labels → pruning,last-logs, container-name lookup all no-oppodFailurePolicy+DisruptionTarget+FailureTarget→ disruptions count as failuresttlSecondsAfterFinished→ finished Jobs never GC'dspec.suspend→ pause can't stop a running stageThe old 1.23–1.32 band splits into 1.29–1.32 (every field at least beta-on-by-default; losses theoretical) and 1.23–1.28 (real degradation).
The row that matters most, and why it got worse
Below 1.26 the
Ignore-on-DisruptionTargetrule disappears, so evictions, preemptions and taint-manager kills count towardbackoffLimitlike genuine failures.At the old unbounded
backoffLimitthat was harmless. With #402 making it finite (default 3), a couple of unrelated disruptions can exhaust the budget and park a package that never failed. So this degradation is materially sharper than when #411 was filed, which is worth a reviewer's attention.Honesty about the numbers
The versions track upstream feature-gate graduation, not measured NodeWright behaviour — none of these clusters are in CI, and I did not stand any up. #411 explicitly said "please confirm the floor before editing the table", so treat these as needing a maintainer's check against upstream before merge. The page now says as much in a note rather than implying a tested promise.
I also removed the same "core APIs only" claim where it was repeated in the Version Selection Guide, so the two sections can't drift apart.
Testing
Docs only.
markdownlint-cli2 --config ci/.markdownlint-cli2.yaml— 0 issues.