fix(image-refresh): write Pass-0 stale-pin annotations before the restart block - #1039
Conversation
…tart block so a latched flap can't drop them Second half of the split from #1008 (item 1). The #563 flap guard does `WARN + FLAP_KEY + exit 0` once refresh-attempt >= MAX_REFRESH_ATTEMPTS -- BEFORE the digest-record annotate at the end of the tick. The stale-pin CLEARS were batched into that final annotate, so on a tick that is both off-digest (restart_needed=1) and latched they were dropped: a stale-pin clear that never lands leaves a FALSE "pin is stale" finding to persist forever -- a write-only annotation outliving its problem (the class client#824's clear paths fixed) -- and it is dropped on the exact tick refresh is dead, when the finding matters most. Move the stale-pin writes (clears and finding-sets) into their own accumulator (`stale_pin_args`) and annotate them in a bounded, non-fatal call ABOVE the restart block. They touch only the `stale-pin-<image>` keys, never `last-refreshed-<image>-digest`, so writing them before the rollout cannot affect the `recorded == latest` skip logic. The digest record deliberately stays BELOW, after a successful `rollout status` -- annotating it before a failed rollout would freeze the workload on the old image (@shujaatTracebloc on #1008). Tests: - image-refresh-latched-annotate.bats (new): extracts the shipped tail and asserts the stale-pin clear lands while the digest record does NOT on a latched tick (restart_needed=1, attempt >= MAX), and that both land on a healthy tick. - image-refresh-stale-pin.bats: harness updated for the new accumulator. - image_refresh_test.yaml: render guard for the accumulator + before-restart ordering. Bumps chart to 1.9.113. Verified: 47 image-refresh bats + helm unittest 730/730 + make check green; the 9 full-suite bats failures are pre-existing (identical on develop). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
LukasWodka
left a comment
There was a problem hiding this comment.
Verified the mechanism against the parent. On develop the four stale-pin writes (disabled-pin clear, current-pin clear, stale finding set, unpinned clear) all rode annotate_args, whose only annotate sits after the restart block, below the flap guard's exit 0, so an off-digest and latched tick dropped them. On this head they accumulate in stale_pin_args and land in a bounded (--request-timeout=15s), non-fatal (if ! sp_err="$(...)") annotate above if [ "$restart_needed" -eq 1 ], guarded by -n so an empty tick makes no call. Only stale-pin-* keys travel there; last-refreshed-* and digest-applied-* still land after rollout status, so the recorded == latest logic is unchanged. Writing a stale-pin clear before a rollout that later fails is right: pinned images continue before restart_needed is set, so the finding and the rollout concern different images.
Tests, run locally from this head: 18/18 bats (latched-annotate + stale-pin) and 40/40 helm unittest on image_refresh_test.yaml. Mutation-proved: reverting the template hunk reddens both; moving the block below the flap guard reddens the ordering assert; keeping the anchor but re-batching stale_pin_args into annotate_args reddens bats cases 2 and 4 on their own assertions.
One gap, matching the open Bugbot thread: re-batching only the disabled_stale_key clear (template line 594) leaves all three test files green. The render guard names stale_key and unpinned_stale_key but not disabled_stale_key, and the stale-pin bats "disabled-monitor exit clears" case asserts on $output, which contains both the ANNOTATE: and STALEPIN: lines, so it cannot tell the accumulators apart. Please add the third matchRegex/notMatchRegex pair, or assert STALEPIN: specifically in that case, then resolve the thread.
Chart 1.9.112 to 1.9.113 on both version and appVersion. No hunk overlap with #1013 in this template (header comments only), though #1013 will pick up another Chart.yaml conflict on rebase.
Happy to approve once the guard is extended and CI is green.
|
Heads-up on the chart version: #1040 (opened a few minutes ago) also bumps |
saadqbal
left a comment
There was a problem hiding this comment.
Not approving yet only because Bugbot's thread is open — and having checked it, Bugbot is right, so it's worth two lines rather than a dismissal. The production code is correct; this is purely a hole in the guard, so there's no logic bug to hunt.
All four stale-pin writes in the shipped script do go to the accumulator:
594 stale_pin_args=" ... ${disabled_stale_key}-" clear, disabled image
622 stale_pin_args=" ... ${stale_key}-" clear
633 stale_pin_args=" ... ${stale_key}=${pin_latest}" the finding SET
646 stale_pin_args=" ... ${unpinned_stale_key}-" clear, unpinned
But the render guard names only two keys. stale_key gets a positive (stale_pin_args ... ${stale_key}-) and a negative (notMatchRegex on annotate_args ... ${stale_key}); unpinned_stale_key gets both as well. disabled_stale_key appears in neither. And the new image-refresh-latched-annotate.bats has four cases with no mention of disabled at all. So moving line 594 back into annotate_args leaves the positive assertions untouched (they name the other two keys), the negatives untouched (same), and the ordering assertion satisfied (stale_pin_args still exists for the rest) — helm-unittest and both bats stay green while the disabled-image clear is dropped on exactly the latched tick this PR exists to fix. Two assertions mirroring the stale_key pair close it.
That's worth fixing rather than waiving because it's this PR's own thesis one level up: a write that never lands leaves a false finding forever. The guard that stops that happening is itself unguarded on one of its three keys, and the comment above it says its job is to "lock the code shapes".
One smaller thing in the same area, non-blocking. The positive assertions pin the two clears but not the finding-set at 633 — deleting ${stale_key}=${pin_latest} from the accumulator entirely would keep both suites green, since the negative only forbids it riding annotate_args. The behaviour bats assert a clear lands; nothing asserts the set does. Lower stakes than the disabled path (a lost set means a real staleness goes unreported rather than a phantom persisting), but it's the same shape.
The fix itself is right and the reasoning for the split ordering is the part I'd keep: stale-pin keys above the restart block because they can't affect the recorded == latest skip, digest record below because annotating it before a failed rollout would freeze the workload on the old image. That asymmetry is the whole point and it's stated where the next person will look.
|
Status check on the re-review pass: head is still
Production hunk is correct as reviewed; I will approve once those two land and CI is green. |
review) Resolves the Chart.yaml conflict (version + appVersion -> 1.9.115, one above develop's 1.9.114). Bugbot Low + @saadqbal: the render guard locked stale_key and unpinned_stale_key onto stale_pin_args but not disabled_stale_key (the third clear), so re-batching only that write back into annotate_args would drop the disabled-image clear on a latched tick while helm-unittest and both bats stayed green -- this guard's own thesis one level up. Add the disabled_stale_key positive+negative pair, plus a positive for the finding-SET (${stale_key}=${pin_latest}) whose absence let the set be dropped from the accumulator unnoticed. All four stale-pin writes now have both a goes-to-stale_pin_args positive and a not-on-annotate_args negative. Verified: image_refresh helm-unittest 40/40; 47 image-refresh bats; make check green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit dfa180a. Configure here.
|
Re-reviewed
A re-run of the failed jobs (or an empty push) should turn both green; I'll approve as soon as it does. |
…ched-annotation-drop # Conflicts: # client/Chart.yaml
|
bugbot run |
saadqbal
left a comment
There was a problem hiding this comment.
The disabled_stale_key pair is in, and you went one better by covering the finding-SET too — all four stale-pin writes now have a positive and every key has a negative. I checked each pattern against the actual template lines rather than taking the block on faith, and the helm unit tests are green at this head, so the assertions are live rather than decorative.
The four red Ubuntu jobs look like an apt-mirror stall, not you: exit 137 on "Installing curl" at the 12m step bound, every non-Ubuntu distro in the same matrix green, and the job's own annotation says as much. Re-run those four and mutation-check, and I'll approve.
LukasWodka
left a comment
There was a problem hiding this comment.
Re-checked a6bc300: the push is a pure merge of develop (#1045, #1016) with Chart.yaml at 1.9.117 over develop's 1.9.116 — the template hunk and all three test files are byte-identical to dfa180a, so the disabled_stale_key guard pair (asserts on stale_pin_args and annotate_args) and the finding-set positive stand, and image_refresh_test.yaml is 40/40 from this head against the merged develop.
Helm unit tests completed green on this head (run 34575772777), Bugbot found no issues on a6bc300, no open threads, and the four Ubuntu installer jobs that were red on the first attempt were the fleet-wide apt-mirror stall (same jobs red on main and release-train/to-main in the same hour); they are green on the rerun. Approving.
…e this PR was open Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Summary
The #563 flap guard in the restart block does
WARN + FLAP_KEY + exit 0oncerefresh-attempt >= MAX_REFRESH_ATTEMPTS— before the digest-record annotate at the end of the tick. The stale-pin clears were batched into that final annotate, so on a tick that is both off-digest (restart_needed=1) and latched, they were dropped: a stale-pin clear that never lands leaves a false "pin is stale" finding to persist forever (a write-only annotation outliving its problem — the class client#824's clear paths fixed), and it's dropped on the exact tick refresh is dead, when the finding matters most.Fix (as @shujaatTracebloc prescribed on #1008): move the stale-pin writes (clears and finding-sets) into their own accumulator
stale_pin_args, annotated in a bounded, non-fatal call above the restart block. They touch only thestale-pin-<image>keys, neverlast-refreshed-<image>-digest, so writing them before the rollout can't affect therecorded == latestskip logic. The digest record deliberately stays below, after a successfulrollout status— annotating it before a failed rollout would freeze the workload on the old image.Related
Split from #1008 — item 1 (the latched-flap Pass-0 annotation drop). Item 2 (the fresh-install digest gate) merged in #1033. This is the second and final half of that split.
Type of change
Test plan
scripts/tests/image-refresh-latched-annotate.bats(new, 4 cases): extracts the shipped tail from the rendered chart and asserts the stale-pin clear lands while the digest record does not on a latched tick (restart_needed=1,attempt >= MAX), and that both land on a healthy tick — re-batching the writes reddens it.scripts/tests/image-refresh-stale-pin.bats: harness updated for the new accumulator.client/tests/image_refresh_test.yaml: render guard for the accumulator + the before-restart ordering (+1 = 40).helm unittest ./client730/730 +make checkgreen. The 9 full-batsfailures are pre-existing (identical ondevelop: footprint / embed-drift / envelope / doctor).version+appVersion).Checklist
Note
Medium Risk
Changes ordering of cluster annotations on the jobs-manager deployment during image-refresh ticks; behavior is narrow (stale-pin keys only) but affects operational diagnostics when refresh is latched or flapping.
Overview
Fixes a bug where stale-pin diagnostic annotations (
tracebloc.io/stale-pin-*clears and sets) were batched into the same finalannotate_argswrite as digest bookkeeping. On ticks that need a re-image and hit the flap latch (refresh-attempt >= MAX), the guard exits before that final annotate—so stale-pin clears never landed and false “pin is stale” findings could stick around.The cron script now accumulates those writes in
stale_pin_argsand applies them in a separate, non-fatalkubectl annotatebefore the restart/flap block.last-refreshed-*-digestrecording stays after a successful rollout, unchanged.Adds
image-refresh-latched-annotate.bats(latched vs healthy tick behavior), extends helm unittest regex guards for all four stale-pin write paths and ordering, and updates the stale-pin branch harness. Chart 1.9.117.Reviewed by Cursor Bugbot for commit a6bc300. Bugbot is set up for automated code reviews on this repo. Configure here.