Skip to content

Converge generated-resource cleanup when a downstream is already absent #5527

Open

Description

@devantler

🤖 Generated by the Agentic Engineer

Delivered on: 2026-10-31
Measure on: 2026-11-08

Resume condition: No earlier than 2026-11-01 at 22:55 UTC, collect at least 24 hours of attributable observations on the accepted controller image and a fresh Coroot window. Rebind the image and uninterrupted observation window if the Pods or image change. A controlled or independently observed cleanup of an already-absent generated resource is still required; ordinary deletion log lines and zero errors do not establish that case. If any required evidence is absent, keep this issue open and record the remaining condition. Do not restart resources or induce a production deletion to manufacture the observation.

The controller delivery is complete through #5638, merged as 9b00677e2a52082035cf89c54486de27f1ecadc9. Both replicas were independently read back ready on the accepted image with zero restarts on 1 November. This card returns to Ready while the observation window matures; the original acceptance criteria and first-start history remain unchanged.

Problem and evidence

The current Kyverno background controller reports a failed downstream deletion when Kubernetes says the generated object is already absent. A fresh read-only observation on 28 October at 17:29:54 UTC captured this error during ordinary resource replacement; Coroot consequently reports a background-controller log error.

The deployed and latest stable release is v1.19.2. Its pkg/background/gpol/dynamic_watcher.go treats every deletion error as a failure and retains the cached downstream identity. In the already-absent case, the intended state has been reached, but that stale cache entry survives. This is a cache-reconciliation defect to reproduce, not a reason to lower log severity or hide arbitrary failures.

Hypothesis and smallest useful change

If an authenticated Kubernetes NotFound response completes downstream cleanup and removes only the matching cached identity, repeated cleanup converges without a false failure. Permission errors, conflicts and unrelated failures must remain visible and retain retryable state. Preserve policy/trigger ownership and deletion identity safety.

Acceptance

  • Reproduce through the actual WatchManager/client boundary: already-absent downstreams clear their exact cache entry; wrapped NotFound is recognized; unrelated errors retain the entry and report failure; another policy/trigger is untouched. Cover each affected deletion path and repeat cleanup.
  • Implement the smallest reviewed correction at the owning source/compatibility-image surface. No RBAC increase, policy exception, log filter or blanket error suppression.
  • Verify the real Linux controller package and published executable, provenance and signature before any separately reviewed image adoption. Prove existing image-verification boundaries still reject unrelated publishers.
  • After normal protected delivery, bind the running image and controller identities, verify retained generation/deletion behavior and observe a fresh Coroot/log window. Source tests and an absent object alone do not establish production recovery.

Part of #2043.

Activity

  1. self-assigned this
    on Oct 28, 2026
  2. added theissue type on Oct 28, 2026
  3. moved this to 🏃🏻‍♂️ In Progress in 🌊 Project Boardon Oct 28, 2026
  4. added a commit that references this issue on Oct 28, 2026
  5. devantler commented on Oct 29, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    The first signed candidate from #5532 is published, but it is not accepted for image adoption. Its executable identifies the release source as a pseudo-version, and stripped symbols prevent the binary scanner from establishing package reachability. Signature, compiler and help-command checks passed; those do not complete the security acceptance.

    I am correcting the producer's release-identity and executable-audit checks before any separately reviewed adoption. The candidate remains held; no advisory, signature boundary or log check is waived. Part of #5527.

  6. devantler commented on Oct 31, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    Published image read back from the registry after #5630, and accepted as an adoption candidate. It is not adopted yet.

    Image: ghcr.io/devantler-tech/platform-kyverno-background@sha256:327d72f865bb10108b19ce2510e3244104872632aa0063283da5e69034b1f1fa (amd64 sha256:1024d675…), from run 41233039072.

    Check Result
    Signature cosign verify finds one signature for the main-branch publisher identity
    Compiler go1.26.10
    Release identity kyverno v1.19.2, vcs.modified=false
    Symbols Kept: 204,173, including 46 from pkg/background/gpol
    OpenPGP package Not compiled in
    Binary scan Symbol level, exactly GO-2025-3615 GO-2026-5337 GO-2026-5594 GO-2026-5932

    These are the four records the Dockerfile accepts. This read came from the image's patched layer on its own.

    A wrong first reading, in case someone repeats it: my first read used crane export piped to tar, and it reported go1.26.9, v1.19.2+dirty and no symbols. That was the upstream binary from the layer underneath. The two layers name the same file differently (ko-app/… and /ko-app/…), so the export lists both and tar keeps the older one. The running image is not affected: the patched layer is on top.

    Still open on this issue: a separately reviewed adoption, then running-image identity and a fresh log window after delivery.

  7. devantler commented on Oct 31, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    The patched controller is running in production since #5638 (merge-group deploy run 41248257178). This issue stays open: the log window so far is too short to show the fix worked.

    Check, read 2026-10-31 22:57Z Result
    Running image Both Pods report sha256:327d72f8…, the digest accepted above
    Version the binary logs v1.19.2-downstream-cleanup.1-aa…
    Pods 2/2 ready, no restarts, started about 22:45Z
    Error-level log lines since start 0 across both Pods (about 1,400 lines in about 12 minutes)

    That the pull succeeded also shows the Talos rule from #5635 accepted this image on those two nodes. It says nothing about the other nodes.

    Still owed:

    • A longer log and Coroot window. Twelve minutes without an error proves little, because I did not establish that a generated object was deleted in that time, and that is the only path this fix changes.
    • The case in the acceptance criteria: a generated resource replaced or removed on the running controller, with cleanup converging and no false failure.
  8. devantler commented on Nov 1, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    Log window read 00:18Z on 1 November, about 1.5 hours on the patched controller. The changed code path now ran, with no false failure. This issue stays open.

    Check Result
    Pods Both still the 22:45Z Pods, 0 restarts
    Error-level lines since start 0 of about 5,800 lines across both Pods
    "Deleting downstream resources" 74 times on the active Pod, 37 each for auto-vpa-statefulset and auto-vpa-daemonset, the deletion path this fix changes
    "Not found" text in the logs 0 lines

    What this does not show: the patched path clears an already-absent object silently, so these logs cannot tell how many of the 74 runs met that case. I did not measure the old image's error rate over a comparable window, so this is not a before-and-after comparison.

    Still owed: the Coroot window, and a controlled or observed case where a generated object is already gone when cleanup runs. A clean log window of at least 24 hours can be read from about 22:45Z today.

  9. added 2 commits that reference this issue on Nov 1, 2026
  10. moved this from 🏃🏻‍♂️ In Progress to 🫴 Ready in 🌊 Project Boardon Nov 1, 2026
  11. added a commit that references this issue on Nov 1, 2026
  12. devantler commented on Nov 1, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    The controller repair is protected-merged in #5638, and a fresh read-only check finds both replicas ready on the accepted image with zero restarts. The observation period is still shorter than the required 24 hours; the earlier deletion log entries also do not prove cleanup of an already-absent resource.

    I recorded the real delivery date and a continuation no earlier than 1 November, 22:55 UTC, then returned this merged check to Ready. At that checkpoint, collect an attributable log and Coroot window and the missing already-absent-resource evidence; otherwise keep the issue open. The original acceptance criteria and flow history are preserved. No source, live resource, scan schedule or security control changed.

  13. devantler commented on Nov 1, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    Checkpoint at about 23:05 UTC on 1 November. This stays open: the case it needs has not happened yet.

    • Image and uptime: both replicas have run on the accepted image since 31 October at about 22:45 UTC, with zero restarts. That is the full 24 hours.
    • The original failure: in the last 25 hours of logs, neither replica shows a failed deletion of an already-absent object. This proves little, because the logs also show no cleanup of an already-absent object at all, and that is the case this issue has to observe.
    • Other errors: one replica logged 5 errors between 16:46 and 21:46 UTC, all routine update conflicts on generated autoscaler objects. They are a different signature and are not this issue.
    • Not read: the Coroot window. I read logs only in this pass.

    Still needed: one independently observed cleanup of an already-absent resource, plus a Coroot window. I did not induce a deletion, as this issue's resume condition forbids. Next read is no earlier than 8 November, and the observation window restarts if the Pods or the image change.

  14. devantler commented on Nov 1, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    Checkpoint read at 23:05 UTC on 1 November. The 24-hour Pod window is met, but this issue stays open: two required pieces of evidence are still missing.

    Check Result
    Pods and image Same two Pods since 22:45 UTC on 31 October, both on the accepted digest sha256:327d72f8…, 0 restarts, ready. That is about 24 hours 20 minutes uninterrupted
    Inactive Pod 522 log lines covering the full 24 hours, 0 errors, no deletion activity
    Active Pod Retained logs start at 13:47 UTC on 1 November, so they cover only the last 9 hours 17 minutes, not 24 hours
    Deletion path, active Pod 587 "deleting downstream resources" lines, 0 lines containing "not found", 0 failed deletion lines
    Errors, active Pod 5, and none is the error this issue tracks. All are update conflicts while generating resources, not deletions

    Closest real event: at 20:10:15 UTC the controller logged a generated resource deleted, added and deleted again in one second, with cleanup for related autoscaler policies running alongside it and no error. I did not confirm what caused it. This is a candidate only. The patched path clears an absent object silently, so the logs cannot show that any deletion actually met an absent object.

    Still owed:

    1. A fresh Coroot window. I did not read Coroot in this run.
    2. An observed or controlled cleanup of an already-absent resource. Waiting will not produce this from the current logs, because the fixed path writes nothing in that case. The next useful step is a controlled run of this exact image on a throwaway local cluster. Production stays out of it, as this issue requires.

    The 5 conflict errors are a separate finding, which I am filing as its own issue. I did not measure the old image over a comparable window, so I can't say whether they are new. No source, live resource or security control changed.

    Reconciled with the checkpoint above, which another run posted within the same second: the next read stays no earlier than 8 November. On log coverage, this comment is the accurate one: the active Pod retains only the last 9 hours 17 minutes, so "the last 25 hours" holds only for the inactive Pod. Issue #5705 tracks the conflicts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Fields

Priority

None yet

Effort

None yet

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions