fix: send propagationPolicy at the top level of DeleteOptions - #28
fix: send propagationPolicy at the top level of DeleteOptions#28abnegate wants to merge 1 commit into
Conversation
delete() nested propagationPolicy and gracePeriodSeconds inside the preconditions map, where the Kubernetes API ignores them. Worse, the refresh() call that followed replaced the resource attributes entirely, so the DeleteOptions body sent to the API was just the refreshed resource relabelled as kind DeleteOptions with no propagationPolicy anywhere. Job deletes therefore fell back to the API default (Orphan), leaving completed pods with no ownerRef and no TTL, which pins PVCs via pvc-protection and permanently wedges dedicated database resizes (DAT-1869). Build the DeleteOptions payload explicitly instead of mutating the resource: propagationPolicy and gracePeriodSeconds at the top level, and preconditions carrying only the resource uid. The resourceVersion precondition is dropped deliberately: it never took effect before, and enforcing it now would introduce conflict failures into every consumer's delete path whenever a controller bumps the resource between refresh and delete. The uid precondition still guards against deleting a recreated resource of the same name. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Greptile SummaryThis PR fixes two stacked bugs in
Confidence Score: 4/5The fix is well-scoped and correct — only The core logic is sound: No files require special attention — both changed files are straightforward and well-covered by the new tests. Important Files Changed
Prompt To Fix All With AIFix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
src/Traits/RunsClusterOperations.php:263-270
The `$propagationPolicy` parameter accepts any string, but the Kubernetes API only recognises `Foreground`, `Background`, and `Orphan`. An invalid value (e.g. a typo like `'foreground'`) is silently forwarded and rejected by the API at request time. Adding a docblock listing the valid values makes the contract clear without requiring a runtime check.
```suggestion
/**
* Build the DeleteOptions payload for the delete operation.
* The propagationPolicy and gracePeriodSeconds fields are top-level
* DeleteOptions fields; only the resource uid belongs in preconditions.
*
* @param string $propagationPolicy One of 'Foreground', 'Background', or 'Orphan'.
*
* @return array<string, mixed>
*/
public function deleteOptions(?int $gracePeriod = null, string $propagationPolicy = 'Foreground'): array
```
### Issue 2 of 2
tests/DeleteOptionsTest.php:76
The `json_decode(json_encode(...))` round-trip is redundant here — `deleteOptions()` already returns a plain PHP array, so the shape can be asserted directly. The round-trip doesn't add safety because `json_encode` of a PHP array is lossless for scalar values, and the assertion on lines 90–91 already guards against resource-attribute leakage.
```suggestion
$payload = $job->deleteOptions(null, 'Foreground');
```
Reviews (1): Last reviewed commit: "fix: send propagationPolicy at the top l..." | Re-trigger Greptile |
| /** | ||
| * Build the DeleteOptions payload for the delete operation. | ||
| * The propagationPolicy and gracePeriodSeconds fields are top-level | ||
| * DeleteOptions fields; only the resource uid belongs in preconditions. | ||
| * | ||
| * @return array<string, mixed> | ||
| */ | ||
| public function deleteOptions(?int $gracePeriod = null, string $propagationPolicy = 'Foreground'): array |
There was a problem hiding this comment.
The
$propagationPolicy parameter accepts any string, but the Kubernetes API only recognises Foreground, Background, and Orphan. An invalid value (e.g. a typo like 'foreground') is silently forwarded and rejected by the API at request time. Adding a docblock listing the valid values makes the contract clear without requiring a runtime check.
| /** | |
| * Build the DeleteOptions payload for the delete operation. | |
| * The propagationPolicy and gracePeriodSeconds fields are top-level | |
| * DeleteOptions fields; only the resource uid belongs in preconditions. | |
| * | |
| * @return array<string, mixed> | |
| */ | |
| public function deleteOptions(?int $gracePeriod = null, string $propagationPolicy = 'Foreground'): array | |
| /** | |
| * Build the DeleteOptions payload for the delete operation. | |
| * The propagationPolicy and gracePeriodSeconds fields are top-level | |
| * DeleteOptions fields; only the resource uid belongs in preconditions. | |
| * | |
| * @param string $propagationPolicy One of 'Foreground', 'Background', or 'Orphan'. | |
| * | |
| * @return array<string, mixed> | |
| */ | |
| public function deleteOptions(?int $gracePeriod = null, string $propagationPolicy = 'Foreground'): array |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/Traits/RunsClusterOperations.php
Line: 263-270
Comment:
The `$propagationPolicy` parameter accepts any string, but the Kubernetes API only recognises `Foreground`, `Background`, and `Orphan`. An invalid value (e.g. a typo like `'foreground'`) is silently forwarded and rejected by the API at request time. Adding a docblock listing the valid values makes the contract clear without requiring a runtime check.
```suggestion
/**
* Build the DeleteOptions payload for the delete operation.
* The propagationPolicy and gracePeriodSeconds fields are top-level
* DeleteOptions fields; only the resource uid belongs in preconditions.
*
* @param string $propagationPolicy One of 'Foreground', 'Background', or 'Orphan'.
*
* @return array<string, mixed>
*/
public function deleteOptions(?int $gracePeriod = null, string $propagationPolicy = 'Foreground'): array
```
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| ], | ||
| ]); | ||
|
|
||
| $payload = json_decode(json_encode($job->deleteOptions(null, 'Foreground')), true); |
There was a problem hiding this comment.
The
json_decode(json_encode(...)) round-trip is redundant here — deleteOptions() already returns a plain PHP array, so the shape can be asserted directly. The round-trip doesn't add safety because json_encode of a PHP array is lossless for scalar values, and the assertion on lines 90–91 already guards against resource-attribute leakage.
| $payload = json_decode(json_encode($job->deleteOptions(null, 'Foreground')), true); | |
| $payload = $job->deleteOptions(null, 'Foreground'); |
Prompt To Fix With AI
This is a comment left during a code review.
Path: tests/DeleteOptionsTest.php
Line: 76
Comment:
The `json_decode(json_encode(...))` round-trip is redundant here — `deleteOptions()` already returns a plain PHP array, so the shape can be asserted directly. The round-trip doesn't add safety because `json_encode` of a PHP array is lossless for scalar values, and the assertion on lines 90–91 already guards against resource-attribute leakage.
```suggestion
$payload = $job->deleteOptions(null, 'Foreground');
```
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Problem
RunsClusterOperations::delete()claims to supportpropagationPolicy, but it never reaches the Kubernetes API. Two stacked bugs:propagationPolicyandgracePeriodSecondswere nested inside thepreconditionsmap, where the API ignores them (preconditions only acceptsuidandresourceVersion).$this->refresh()call that followedsetAttribute('preconditions', ...)replaces the resource attributes entirely (syncWith()assigns$this->attributes), so even the misplaced fields were wiped before serialization. The actual body sent was the refreshed resource relabelled askind: DeleteOptions:{ "apiVersion": "batch/v1", "kind": "DeleteOptions", "metadata": { "name": "reap", "uid": "...", "resourceVersion": "12345", "namespace": "default" } }No
propagationPolicyanywhere, so Job deletes fall back to the API default for batch/v1 (Orphan). This is the root cause of the DAT-1869 reopen: edge's resize reap calls$job->delete(propagationPolicy: 'Foreground'), the delete orphans the Completed pods (no ownerRef, no TTL),pvc-protectionnever releases the claim, and the resize wedges permanently.Fix
Build the DeleteOptions payload explicitly instead of mutating the resource. New body:
{ "apiVersion": "v1", "kind": "DeleteOptions", "propagationPolicy": "Foreground", "preconditions": { "uid": "..." } }propagationPolicyandgracePeriodSeconds(when set) are top-level DeleteOptions fields.preconditionscarries only the resourceuid, guarding against deleting a recreated resource of the same name.resourceVersionprecondition is dropped deliberately: it never took effect before (see bug 2), and enforcing it now would introduce conflict failures into every consumer's delete path whenever a controller bumps the resource betweenrefresh()and the DELETE call.deleteOptions()so the shape is unit-testable without a cluster.Tests
tests/DeleteOptionsTest.phpasserts the payload shape:propagationPolicyat the top level (and not inpreconditions),gracePeriodSecondsat the top level when set,preconditionslimited touidand omitted when the uid is unknown, and no resource attributes leaking into the body.Consumers
Edge's dedicated-database resize reap depends on this fix; the edge-side bump should happen after this merges and a release is tagged.
🤖 Generated with Claude Code