feat(task/observe-image): advance STS status.currentRevision after rollout#379
feat(task/observe-image): advance STS status.currentRevision after rollout#379bdchatham wants to merge 1 commit into
Conversation
…nning-mode update completes SeiNode StatefulSets are generated with updateStrategy: OnDelete (see noderesource.GenerateNodeStatefulSet) because pod replacement is driven by the replace-pod task, not the StatefulSet controller's rolling-update loop. As a side effect, the upstream StatefulSet controller never advances status.currentRevision after a successful rollout — it only does so as part of its own rolling-update step, which never runs under OnDelete. The kube-prometheus-mixin alert KubeStatefulSetUpdateNotRolledOut latches on this mismatch (currentRevision != updateRevision with updatedReplicas == replicas == readyReplicas) and never clears, producing chronic false-positive pages on every SND we cycle. Concrete instance: atlantic-2/archive-0 has been firing since 2026-05-29, and every other SND rolled today entered the same state. Fix: extend ObserveImage. Once UpdatedReplicas confirms rollout is complete and we stamp Status.CurrentImage, also patch the underlying StatefulSet's status.currentRevision = status.updateRevision (and currentReplicas = updatedReplicas). Patch via the status subresource with client.MergeFrom; no-op when revisions already match or when updateRevision is empty (controller hasn't observed the spec yet). This sits exactly where we already know "new pod is up at the new revision" — minimal surface area, no new task in the progression. Adds RBAC for apps/statefulsets/status. Test plan: - New unit tests in observe_image_test.go covering revision advance, no-op when revisions already match, no-op when updateRevision is empty. - Existing observe-image tests continue to pass (no behavioral change for rollout-not-yet-complete paths).
PR SummaryMedium Risk Overview The patch is a no-op when revisions already match or Reviewed by Cursor Bugbot for commit 9b6655e. Bugbot is set up for automated code reviews on this repo. Configure here. |
|
Closing — adversarial review with online verification surfaced that this approach is the wrong remediation. Root cause we hit: SND-managed StatefulSets use Why this PR is wrong:
Correct remediation (separate PR to platform repo): A Will file a follow-up issue tracking the v1.37 upgrade as the removal trigger. |
Problem
SeiNode StatefulSets are generated with
updateStrategy: OnDelete(seeinternal/noderesource/noderesource.go:253) because pod replacement is driven by thereplace-podtask, not the StatefulSet controller's rolling-update loop. The SNDInPlacestrategy is implemented at the controller layer viaReplacePod, not via the StatefulSet controller's own rolling-update step.The upstream StatefulSet controller never advances
status.currentRevisionunderOnDelete— that field is only written as part of its rolling-update step, which we deliberately bypass. After a successful in-place rollout, the StatefulSet sits in a permanent split state:The kube-prometheus-mixin alert
KubeStatefulSetUpdateNotRolledOutlatches on exactly this mismatch and never clears, producing chronic false-positive pages on every SND we cycle.Concrete symptoms (observed today):
atlantic-2/archive-0— firing since 2026-05-29 despite the rollout completing cleanly.Fix
Augment the existing
ObserveImagetask (Option A in the design discussion). OnceUpdatedReplicasconfirms rollout is complete and we stampStatus.CurrentImage/Status.CurrentSidecarImage, also patch the underlying StatefulSet'sstatus.currentRevision = status.updateRevision(andcurrentReplicas = updatedReplicas).client.MergeFrom.updateRevisionis empty (controller hasn't observed the spec yet).apps/statefulsets/status(get;patch;update).Why ObserveImage and not a new task or a periodic sweeper
ApplyStatefulSet -> ApplyService -> ConfigPatch -> ConfigValidate -> ReplacePod -> ObserveImage -> MarkReady). The information we need to advance currentRevision is the same information ObserveImage already computes — adding an 8th task would be one indirection short of pointless.Test plan
internal/task/observe_image_test.go:TestObserveImage_RolloutComplete_AdvancesStatefulSetCurrentRevision— happy path: differing revisions, patch applies.TestObserveImage_RolloutComplete_RevisionsAlreadyMatch_NoOp— already-equal case: no patch attempted, task still completes.TestObserveImage_RolloutComplete_EmptyUpdateRevision_NoOp— race window where StatefulSet controller hasn't observed the spec: do not blank an existing CurrentRevision.go test ./...(excluding e2e) passes cleanly across all packages.make manifestsregeneratedmanifests/role.yamlfor the new RBAC marker; diff is exactly the expected statefulsets/status block.atlantic-2: after deploying a controller bump, trigger an in-place update on an SND, watch forKubeStatefulSetUpdateNotRolledOutto clear on the next Prometheus scrape afterObserveImagereports complete.Scope discipline (per CLAUDE.md)
client.MergeFromfor the external StatefulSet — theMergeFromWithOptimisticLock{}requirement applies to SeiNode/SND/SeiNodeTask own-status writes, not to writes to upstream resources, so this is consistent with the repo convention.