Repository navigation
feat!: SnapshotPolicy deletion cascade + auto-adoption of discovered snapshots - #272
Merged
Merged
Conversation
Today CONFIG_LABEL is stamped by the schedule controller and by the CLI's `snapshot now`, but a raw-`kubectl apply`'d manual Snapshot with spec.policyRef never gets it — making it invisible to GFS retention, the policy fan-out watch, and the upcoming SnapshotPolicy deletion cascade, all of which select a policy's children by this label. Fix the asymmetry in the mutating webhook instead: on Snapshot CREATE only, stamp kopiur.home-operations.com/config = policyRef.name when the origin is Manual/Scheduled (never Discovered), the ref has a nonempty name targeting the Snapshot's own namespace, and the label isn't already present. The decision is a pure `config_label_stamp` helper so it unit-tests without a cluster; the JSON-patch op mirrors set_spec_field's absent-parent handling for /metadata/labels. Deliberately no controller-side backfill of pre-existing CRs: retro- labeling would make previously-immortal raw-applied manual Snapshots GFS-prunable, which is a silent-data-loss footgun.
…cy, Origin::Adopted, SnapshotAdoption) Lands the full API surface for two upcoming controller/webhook features: - SnapshotPolicy deletion cascade: PolicyDeletePolicy (Retain/Delete), SnapshotPolicySpec.deletion (PolicyDeletionSpec) + effective_on_policy_delete resolver, and PrunedBy::PolicyCascade for the finalizer's release-only path. - Discovered-snapshot auto-adoption (#210): SnapshotAdoption (Adopt/Ignore), CatalogBounds.adoption + SnapshotPolicySpec.adoption + effective_adoption precedence resolver (policy > repo catalog > Adopt), Origin::Adopted, and SnapshotPolicyStatus.adoption (AdoptionSummary) + CatalogStatus.scanRequestHonored. Validators updated to exhaustive Origin matches (no catch-alls): validate_backup_deletion_policy allows any deletionPolicy for Adopted; validate_backup_on_schedule_delete forbids onScheduleDelete for both Discovered and Adopted with an origin-aware message. Wires the new Origin::Adopted variant through every exhaustive match outside crates/api needed to keep the workspace compiling (controller plan.rs/catalog.rs/ build.rs, webhook handlers.rs, CLI snapshots.rs) — adopted rows are treated as managed/produced wherever discovered-vs-produced is distinguished. Behavioral reconciler/webhook logic for both features lands in later milestones. Regenerated deploy/crds, helm chart CRDs, docs/field-reference.md, and the xtask CRD-schema insta snapshots via `cargo xtask gen-all`.
…rune plan Lands the pure decision layer for the SnapshotPolicy deletion cascade (M2), building on M1's PolicyDeletePolicy/PrunedBy::PolicyCascade/Origin::Adopted types: - plan_policy_cascade (snapshot_policy.rs): decides how a SnapshotPolicy deletion cascades onto its Snapshot children under onPolicyDelete Retain/Delete — origin-filtered (Discovered never cascades), terminating children excluded from any live-work set, exhaustive over (terminating, mode). Nothing calls it yet (wired to the policy finalizer in M3). - plan_prune (snapshot/plan.rs) is now variant-aware: exhaustive 3x3 match over (PrunedBy, DeletionPolicy) instead of discarding the prune kind. A policy-cascade prune under an effective Delete policy downgrades to the new DeletionPlan::RetainSnapshotOnPolicyDelete rather than contacting the repository — the loud downgrade that makes the Retain-mode cascade safe. - retain_on_policy_delete executor (snapshot/mod.rs) mirrors retain_on_schedule_delete exactly: releases the finalizer without repo contact, bumps the new kopiur_snapshots_policy_cascade_retained counter, and emits one SnapshotRetainedOnPolicyDelete Warning event via a pure, unit-tested message function that handles both the completed-snapshot and cancelled-mid-flight cases. Extracted execute_deletion_plan out of handle_deletion to keep the latter under the cognitive-complexity ratchet after adding the new match arm. No CRD/schema changes; deploy/crds and docs/field-reference.md untouched (gen-check clean).
…ting-policy fire guard Wires the pure plan_policy_cascade planner (M2) to a new policy-cleanup finalizer that drives the SnapshotPolicy deletion cascade: each pass re-LISTs the policy's Snapshot children, executes up to 50 batched actions (stamp_and_delete, then stamp_only, then delete_only), and releases the finalizer only once a pass's plan is entirely empty. A stamp_only-only pass still requeues rather than releasing in the same pass, so a breaker-held terminating child never wedges removal (it drains on the next, now-empty pass instead). Also closes the SnapshotSchedule refire hole: a policy mid-deletion is now treated exactly like an absent one (Error::MissingDependency), via the new policy_usable predicate, so a schedule never fires a Snapshot into a recipe whose own deletion cascade may already have listed (and so will never account for) that child. - crates/controller/src/consts.rs: POLICY_CLEANUP_FINALIZER - crates/controller/src/io/prune.rs: bare unstamped delete_snapshot helper for the delete_only (external-classified) set - crates/controller/src/metrics.rs: kopiur_policy_cascade_children_deleted counter (mode=retain|delete) - crates/controller/src/snapshot_policy.rs: handle_policy_lifecycle/ handle_policy_deletion wiring, slice_policy_cascade_batch (unit tested) + per-set execute_* helpers - crates/controller/src/snapshot_schedule.rs: policy_usable (unit tested) wired into policy_default_deletion_policy
…nd attempt backoff Adds repo-side honoring of an on-demand catalog-scan-requested-at token (the policy-side stamping lands in a later milestone): a pure scan_requested_due predicate retires the token by EQUALITY against status.catalog.scanRequestHonored (never a lastRefreshAt comparison, which would let an unrelated periodic/ generation scan silently "honor" a request it predates), and rate-limits repeat bootstrap attempts via a new status.catalog.scanRequestAttemptAt field so a pending token against an unreachable backend cannot recreate bootstrap Jobs forever. Threaded as an OR-arm into scan_due/bootstrap_recycle_due/ bootstrap_create_due in both the Repository and ClusterRepository reconcilers; regression tests pin that a token=None reconcile is byte-identical to today. CRDs/RBAC/docs regenerated via cargo xtask gen-all for the new status field.
backup_origin flips from from_label.or(from_status) to from_status.or(from_label) to match the controller's resolve_origin. With adoption in play, a user could otherwise flip a discovered row's metadata origin LABEL to "adopted" and unlock deletionPolicy: Delete at admission while the controller (which only trusts status.origin, a subresource users can't casually write) still treats the row as discovered. Safe at CREATE (status.origin is always absent on a new object, so the label fallback is unchanged) and for existing discovered/produced rows (their admission defaulting never depended on which arm won). Adds 4 route-level tests: adopted status admits Delete, discovered status denies Delete, a flipped label does NOT override a discovered status (the attack this closes), and CREATE with only the label still resolves discovered.
…it only bootstrap launches A Ready object-store repo with a pending catalog-scan-request token never retired it: scan_due's token arm reused the rate-limited scan_requested_due, so the finalize pass scanning a just-succeeded, token-driven bootstrap Job saw the very scanRequestAttemptAt stamp that launched it (still fresh) and refused to scan, wedging the token behind an infinite recycle-create-succeed-no-scan loop. Split the predicate: scan_requested_pending (new, pure equality-retirement, no rate limit) drives scan_due's token arm, so a completed bootstrap with a fresh listing and a pending token always scans exactly once. The rate-limited scan_requested_due (now built on top of scan_requested_pending) still gates bootstrap_recycle_due/bootstrap_create_due, so an unreachable repo still cannot churn Jobs. Applied identically to Repository and ClusterRepository.
…gate in snapshot reconciler Converts reconcile_inner's Discovered-only gate into an exhaustive `match origin` (no `_ =>`) so a new Origin variant cannot compile without a decision. Adopted rows are retention-visible history, not runs: they now pin `phase: Succeeded` + `status.origin: Adopted` and self-heal the snapshot-cleanup finalizer, returning before any backup-run/post-hook/reap/pin machinery, exactly like the existing Discovered arm. The Discovered arm's body is a byte-for-byte relocation into pin_discovered_row; pin_adopted_row mirrors its shape. Both share a new pure predicate (plan::needs_terminal_pin) for the "only pin when unset/divergent" idempotence. Adds plan_deletion coverage locking in that Adopted rows are NOT forced-Retain (unlike Discovered): external deletes flow through the mass-deletion breaker like produced rows, retention prunes always delete, and policy-cascade prunes still retain kopia data. Also adds the first-ever tests for resolve_origin's status-first-over-label precedence.
…ing policies Adds the adoption engine (fixes #210): a discovered Snapshot whose kopia identity exactly matches a live SnapshotPolicy's resolved identity is re-created as an origin: Adopted Snapshot in the policy's namespace and the discovered row deleted, so GFS retention governs (and eventually prunes) it instead of it living in the catalog forever. - New pure module crates/controller/src/adoption.rs: adoption_candidates, plan_adoption (exhaustive over SnapshotAdoption), build_adopted_snapshot, adoption_event_message — full unit matrix. - snapshot_policy::run_adoption wires it in AFTER the retention block: a cluster-wide scoped_api LIST of this repo's discovered rows, structured identity equality, ForeignCluster refusal, create-then-ensure-status-then -delete (409-tolerant, no ownerReference), batch cap 50 in snapshot-id order. - On-demand catalog-scan request stamped on the repository (io::request_catalog_scan) + SnapshotsAdopted/AdoptionScanRequested Normal events + kopiur_snapshots_adopted metric + status.adoption summary. - ResolvedRepository gains catalog (for effective_adoption gating).
Documents the M0-M6 policy-deletion-cascade + snapshot-auto-adoption feature for all three audiences: - docs/backups.md: new "What happens when the policy is deleted" section (onPolicyDelete Retain/Delete, the behavior-change callout, Retain-wins-ties against a simultaneous schedule delete, the Running-child-cancelled case, discovered-rows-never-touched, both escape hatches, and the manual-Snapshot config-label caveat); a retention-section note that adopted rows are pruned exactly like produced ones. - docs/scenarios/adopt-existing-repo.md: extends the scenario past "discovered" with "Delete a policy, then recreate it" — the full delete to clean-CRs/kept-data to on-demand scan request to rediscovered to adopted to retention-resumes flow, plus both adoption opt-outs and the foreign-cluster exclusion. - docs/repositories.md: new `catalog.adoption` section under the catalog — the Adopt/Ignore knob, the policy-wins-over-repo resolution order, the bounded one-time scan-request burst on a repository shared by many policies, and the transiently-stale `discoveredBackupCount` after a wave. - docs/troubleshooting.md: two entries — "My old snapshots were pruned after I recreated a policy" (adoption + GFS is by design, the SnapshotsAdopted event is the paper trail, both opt-outs) and "Adoption didn't happen" (a checklist: adoption knobs, exact-identity mismatch, foreign-cluster exclusion, scan-request pending/backoff, reconcile timing). - deploy/examples/35-policy-deletion-cascade.yaml and 36-policy-recreate-adoption.yaml: apply-ready manifests for the cascade and the recreate/adopt flow, snippet-included from the pages above (example 34's inline-in-topic-page format, not the examples.md ladder). Also fixes a stale event message: retain_on_schedule_delete's Warning Event promised rediscovery "within the repository's catalog refresh interval", which is misleading now that periodicRefresh defaults off. Extracted the message into a pure schedule_cascade_retained_message (mirroring the existing policy_cascade_retained_message), reworded to name the real triggers (a catalog scan: bootstrap, spec change, or a recreated policy's scan request) and the post-rediscovery outcome (default auto-adoption), and added its first unit tests. Every field name, event reason, metric, annotation, and status field cited was checked against crates/api, crates/controller/src/consts.rs, and the actual reconcile/adoption code before writing prose — not against the original milestone plan, which evolved during implementation. mise run test / clippy / gen / gen-check / complexity-check / docs all green; the new examples pass crates/api's examples_match_crd_shapes test. BREAKING CHANGE: deleting a SnapshotPolicy now removes its config-labeled Snapshot CRs by default (onPolicyDelete: Retain — kopia snapshots are kept and rediscoverable/adoptable). Previously the CRs were left dangling.
Adds crates/e2e/tests/adoption.rs (3 scenarios) and appends 3 policy-cascade scenarios to crates/e2e/tests/mass_deletion.rs, proving the full policy-cascade + snapshot-adoption story against a live operator: adoption.rs - policy_recreate_adopts_and_prunes_history: THE #210 acceptance test — delete a policy+schedule over real backdated history (kopia unchanged, finalizer released), recreate with keepLatest:1, and prove hands-off scan-request -> rediscover -> adopt (origin=adopted, config-labeled, Succeeded, lastAdoptedCount>=3 + SnapshotsAdopted event) -> kopia history pruned to the retention spec (the assertion that FAILED before this branch), with a non-matching negative control left as a forced-Retain discovered row. - adoption_opt_out_stays_discovered: catalog.adoption: Ignore leaves matching rows discovered (no adopted rows, no scan-request stamping, empty status.adoption). - cluster_repository_adoption_cross_namespace: discovered row in the workload ns, adopting policy in the operator ns (hostname override) -> adopts cross-namespace (the cluster-wide-LIST guard), converges again after delete/recreate. mass_deletion.rs (scenarios 10-12) - policy_cascade_delete_cleans_crs_keeps_kopia: Retain cascade drains a scheduled + a webhook-config-label-stamped MANUAL child, keeps kopia, re-catalogs, fires SnapshotRetainedOnPolicyDelete, leaves a pre-existing discovered row untouched. - policy_cascade_opt_in_delete_is_breaker_gated: onPolicyDelete: Delete children are breaker-HELD, yet the policy finalizer still RELEASES; ack drains + deletes kopia. - schedule_and_policy_simultaneous_delete_drains_to_retain: back-to-back delete drains every child with no stuck finalizer, no SnapshotDeleteBatch Job, kopia intact. New isolated repo subpaths (consts.rs + mise node-seed) and the kopiur-adopt-crepo bucket back these scenarios.
Greptile SummaryThis PR adds SnapshotPolicy lifecycle cleanup and automatic adoption of matching discovered snapshots. The main changes are:
|
| Filename | Overview |
|---|---|
| crates/controller/src/adoption.rs | Adds adoption candidate extraction, identity matching, foreign-cluster refusal, bounded batches, and adopted Snapshot construction. |
| crates/controller/src/snapshot_policy.rs | Adds policy deletion cascade handling and adoption orchestration after retention. |
| crates/controller/src/snapshot/plan.rs | Extends Snapshot deletion planning for policy-cascade prunes and namespace teardown precedence. |
| crates/controller/src/snapshot/mod.rs | Adds adopted-row steady-state handling with provenance gating. |
| crates/controller/src/catalog.rs | Adds on-demand catalog scan predicates and scan-request retirement behavior. |
| crates/controller/src/repository.rs | Threads catalog scan request honoring and attempt status through Repository reconciliation. |
| crates/controller/src/cluster_repository.rs | Mirrors scan request honoring and attempt handling for ClusterRepository reconciliation. |
| crates/controller/src/io/repo.rs | Adds a helper for adoption-driven catalog scan requests and carries catalog adoption settings in resolved repositories. |
| crates/webhook/src/handlers.rs | Updates Snapshot admission to resolve origin status-first and stamp policy labels for managed Snapshot origins. |
| crates/api/src/snapshot_policy.rs | Adds policy deletion and adoption API fields plus adoption status summary types. |
| crates/api/src/common/cache.rs | Adds SnapshotAdoption settings and effective adoption precedence. |
| crates/api/src/validate/snapshot.rs | Extends validation so adopted snapshots cannot set schedule-deletion behavior. |
Reviews (4): Last reviewed commit: "fix(controller): collision-free adopted ..." | Re-trigger Greptile
…ore rediscovery) The 'pre-existing untouched discovered row' control in policy_cascade_delete_cleans_crs_keeps_kopia deleted the seed Snapshot CR while its identity-matching SEED_POL policy was still LIVE, so the operator CORRECTLY auto-adopted the re-cataloged seed (flipping it out of 'discovered') and the repo_discovered_rows wait timed out. Retire SEED_POL first (Retain cascade keeps the kopia snapshot) and only accept the discovered row once SEED_POL is gone, so no live policy can adopt the control out from under it. Test-fixture fix only; the operator behaved correctly (retain held, adoption fired as designed). Confirmed green on a fresh kind cluster.
…cy-cascade Retain stamp During namespace teardown the SnapshotPolicy cleanup finalizer stamps its live children `pruned-by: policy-cascade` (default onPolicyDelete: Retain). `plan_deletion` routed such a stamped child straight to the quiet-retain downgrade (RetainSnapshotOnPolicyDelete), silently nullifying an EXPLICIT `onNamespaceDelete: Delete` opt-in — so `kubectl delete ns` left the kopia snapshot in the repo instead of reclaiming it (the failing e2e on_namespace_delete_delete_cascades_snapshot reported 1 snapshot). Add `plan_ns_delete` (the ns-terminating Delete arm), exhaustive over PrunedBy: an unstamped OR policy-cascade-stamped child resolves as an external destructive deletion (breaker-gated) — the explicit ns-delete opt-in wins over the implicit cascade stamp. Retention/failed-history operator prunes keep their prune semantics; Orphan/None ns policies stay non-destructive. The retain-wins-ties rule stays scoped to schedule-vs- policy cascade races, not explicit namespace-delete opt-ins. Adds two composite plan_deletion unit tests pinning the contract (the Delete case fails on HEAD; the Orphan/None case guards the preserved non-destructive default).
…cade Delete mode On the policy-cascade branch a config-labeled child is owned by the SnapshotPolicy deletion cascade, so deleting the policy first now quiet-releases the child under the default Retain mode — the old "delete the policy, then delete the Snapshot" flow no longer occurs (the unwrap panic at credential_projection.rs:652 was the now-404 explicit delete). Set `spec.deletion.onPolicyDelete: Delete` so policy deletion issues an UNSTAMPED external delete for the child, whose finalizer must complete the kopia deletion using the PINNED resolved repository + credentialProjection with the recipe gone — exactly the #255 property, now also covering the cascade Delete path end-to-end. The child is removed by the cascade (the test never touches the Snapshot); its released cleanup finalizer is the kopia-side proof (a deletionPolicy: Delete external deletion under the breaker threshold resolves to DeleteSnapshot). Keeps the test name and updates its doc comment to state the new flow.
…n Delete waves During a namespace teardown with the explicit `onNamespaceDelete: Delete` opt-in, the SnapshotPolicy cleanup finalizer stamps its children `pruned-by: policy-cascade`. Their plan correctly resolves to an external destructive DeleteSnapshot (plan_ns_delete -> plan_external), but the mass-deletion breaker never counted or held them, and the batch dispatcher never even fired them: - counts_toward_breaker blanket-exempted ANY pruned-by stamp. - gather_deletion_facts computed breaker_applies in the stable ns_terminating:false form, where a policy-cascade child is a quiet retain. - pending_members (the repo-wide counting/fire set) hardcoded ns_terminating:false, so a policy-cascade child was excluded from the counting set entirely -> uncounted AND unfireable (fire_batch's self-uid bypass only matches members present in the set), so its kopia snapshot was never deleted. - PendingMember.external (== breaker-relevant) was pruned_by().is_none(), so even once included a policy-cascade child was treated as an exempt prune by unacked_breaker_count / fire_breaker_ok. Fix: a single exhaustive PrunedBy classification (plan::breaker_relevant) shared by counts_toward_breaker and the external flag — Retention/FailedHistory stay exempt everywhere; None/PolicyCascade are breaker-relevant. The counting set now evaluates each member's plan in its real (ns_terminating, ns_policy) form: resolve_terminating_namespaces runs over pending_candidate_namespaces (bounded by CRs actually being deleted) before pending_members, and gather_deletion_facts computes breaker_applies in the real form. A policy-cascade child under ns-teardown Delete now counts toward and is held by the per-repo breaker exactly like an unstamped external child (same threshold, same allow-mass-deletion ack release, same DeletionHeld condition/events); Retention/FailedHistory keep their prune semantics; policy-cascade in a live namespace keeps quiet-retain. The repo-level MassDeletionHeld mirror is likewise made ns-aware for consistency. No wedge: an already-stamped held terminating child yields zero policy-cascade work, so handle_policy_deletion still releases the finalizer (guarded by plan_policy_cascade_only_terminating_children_yields_no_live_work_in_either_mode).
`pin_adopted_row` pinned `phase: Succeeded` for ANY CR resolving to `Origin::Adopted` — including a user-applied bare `origin: adopted` label with no controller-written `status.snapshot`. That phantom Succeeded row entered `retention_view` (via the creationTimestamp fallback), claimed a GFS bucket, and displaced a REAL snapshot into the breaker-exempt retention delete set; it also set `has_history`, suppressing a recreated policy's on-demand scan request. Gate the pin on controller-written provenance (`status.snapshot` present, the id `adopt_one`'s create→status-patch flow records) via a new pure predicate `plan::adopted_row_has_provenance`. The genuine adopt flow converges within a pass, so an interim row is only transiently phase-less. Defense in depth: - `retention_view` now requires `status.snapshot` for a Succeeded row to enter GFS — closes the displacement even if a phase ever sneaks in. - `own_snapshot_ids_and_history`'s `origin == Adopted` arm requires the same provenance, so a bare label never counts as history. Also harden `adopt_one`'s create-409 path: verify the occupant carries the expected `SNAPSHOT_ID_LABEL` and a catalog origin label (`adopted`/`discovered`) before overwriting its status — a name-collision stranger is skipped with a warn instead of clobbered (`adopted_row_matches_candidate`, pure + unit-tested). Tests (fail on HEAD's un-gated logic): forged label-only row → not pinned Succeeded, not in retention_view, doesn't set has_history; genuine adopted row (status.snapshot present) → unchanged.
…th (C1) `resolve_terminating_namespaces` mapped a Namespace GET error to "terminating" (`unwrap_or(true)`). Post the plan-in-real-form change, that set feeds PLAN EVALUATION in `pending_members`: on a repo with `onNamespaceDelete: Delete`, a transient read error flipped retain-destined draining children (policy-cascade-stamped, or schedule-cascade Retain) into destructive `DeleteSnapshot` members that `fire_namespace_ok` then admitted; below threshold they enrolled into the next batch Job and deleted real kopia data that Retain promised to keep — in a namespace that is NOT being deleted. Split the resolution into two sets by read confidence: - `confirmed` (`Ok(true)`) — the ONLY set fed to plan evaluation (`pending_members`) and breaker counting-as-terminating. An unreadable namespace now stays retain-form for plan/count, matching the finalizer's self-CR path (`unwrap_or(false)`). - `unreadable` (`Err`) — folded into the FIRE path's ns-Orphan exclusion (`confirmed ∪ unreadable`, so an Orphan peer stays protected) AND excluded from the FIRE set OUTRIGHT via a new `fire_namespace_readable_ok` predicate: a peer whose namespace we cannot confirm terminating is never deleted on the assumption that it is (its own self-fire drains it). Counting such a member toward the breaker stays fine — over-count is safe. Updated the now-wrong Err-direction doc and made the self-CR asymmetry note coherent (both paths treat an unreadable namespace as NOT terminating for plan/count). Also documented that a terminating-ns member whose real plan is `OrphanSnapshot` no longer counts toward the breaker — the alert-mirror softens correctly during Orphan teardowns. Pure tests (fail on HEAD's single-set design): member in an unreadable namespace + `onNamespaceDelete: Delete` → NOT in the fire set and its plan stays retain; confirmed-terminating + Delete → fires and counts as before; unreadable + Orphan → still excluded from fire (pre-existing protection).
The first doc paragraph on `scan_requested_pending` ("...should force a
catalog scan/bootstrap NOW...") was left over from the pre-split function
and describes behavior this predicate no longer has. Remove it; keep the
correct "still PENDING: present, non-empty, not yet retired by TOKEN
EQUALITY" header and the scan_due-vs-scan_requested_due rationale.
…sh suffix Two kopia snapshot ids sharing a >=16-char prefix under the same policy minted the same adopted-row name (<policy>-adopted-<first16(id)>). On HEAD (post-91093e7's adopted_row_matches_candidate guard) this doesn't corrupt status, but permanently wedges the second snapshot: skipped and re-warned on every reconcile pass, its discovered row never cleaned up. Append a short_hash(full snapshot_id) suffix (reusing the existing FNV-1a naming::short_hash idiom, no new hash dep) so the name is collision-free while staying deterministic per (policy, full id) and human-correlatable via the retained first-16 prefix. capped_name still applies for the 63-char cap. The 409-branch ensure-status logic and adopted_row_matches_candidate guard are unchanged (defense in depth). The sibling <repo>-disc-<first16> naming in catalog.rs has the same collision class but predates this branch; left as a follow-up.
1 task
perfectra1n
added a commit
that referenced
this pull request
Jul 20, 2026
Both binaries had never run in CI, so both had rotted silently. Neither failure involves the health probe; they are pre-existing and were only made visible by wiring the orphaned e2e binaries into the shard matrix. leak_cleanup: the fixture could never age into eligibility. The creds sweep kernels refuse to reap anything younger than `CREDS_MIN_AGE_FLOOR_SECS` (15m), a hard floor that deliberately overrides the harness's 30s `KOPIUR_WORK_SPEC_SWEEP_MIN_AGE_SECS` so an admin who zeroes that shared knob cannot get a zero-width guard on the project→create-Job window. A Secret created during the test can therefore never reach 900s inside `default_timeout()` (420s), so the assertion was arithmetically unsatisfiable. Backdate `projected-at` instead — `sweep::creds_age_secs` prefers that annotation over the apiserver-owned `creationTimestamp` — and stamp all three fixtures identically so the two controls still prove they survive on their marker and in-use guard rather than on being too young to consider. The production floor is untouched. cli: `cli_migrate_volsync_kopia` asserted the seeded fork snapshot surfaces with `origin=discovered`, but #272 (policy auto-adoption) now has the migrated SnapshotPolicy adopt any discovered row whose identity matches it — creating an `origin=adopted` row and DELETING the discovered one. The assertion became unsatisfiable the day #272 landed, and nothing caught it because this binary ran in no shard. Select on the repository label and accept either origin, since which one you observe is a race against the adoption pass; the load-bearing assertion is that the fork identity survives the migration intact, which holds under both. Origin is read from the label, not `spec.origin`, which is elided for these rows.
doonga
pushed a commit
to greyrock-labs/home-ops
that referenced
this pull request
Jul 21, 2026
…0.7.5 ➔ 0.8.0) (#107) This PR contains the following updates: | Package | Update | Change | |---|---|---| | [ghcr.io/home-operations/charts/kopiur](https://github.com/home-operations/kopiur) | minor | `0.7.5` → `0.8.0` | --- >⚠️ **Warning** > > Some dependencies could not be looked up. Check the [Dependency Dashboard](issues/11) for more information. --- ### Release Notes <details> <summary>home-operations/kopiur (ghcr.io/home-operations/charts/kopiur)</summary> ### [`v0.8.0`](https://github.com/home-operations/kopiur/blob/HEAD/CHANGELOG.md#080-2026-07-19) [Compare Source](home-operations/kopiur@0.7.5...0.8.0) ##### ⚠ BREAKING CHANGES - SnapshotPolicy deletion cascade + auto-adoption of discovered snapshots ([#​272](home-operations/kopiur#272)) ##### Features - **deps:** update rust crate tokio (1.52.4 → 1.53.0) ([#​264](home-operations/kopiur#264)) ([6cab0c6](home-operations/kopiur@6cab0c6)) - mass-deletion protection (cascade guard, per-repo breaker, batched deletes) ([#​265](home-operations/kopiur#265)) ([038ea6d](home-operations/kopiur@038ea6d)) - SnapshotPolicy deletion cascade + auto-adoption of discovered snapshots ([#​272](home-operations/kopiur#272)) ([9da8c26](home-operations/kopiur@9da8c26)) ##### Bug Fixes - **deps:** update rust crate anyhow (1.0.103 → 1.0.104) ([#​269](home-operations/kopiur#269)) ([4d81496](home-operations/kopiur@4d81496)) - **deps:** update rust crate clap (4.6.1 → 4.6.2) ([#​257](home-operations/kopiur#257)) ([c238df1](home-operations/kopiur@c238df1)) - **deps:** update rust crate futures (0.3.32 → 0.3.33) ([#​268](home-operations/kopiur#268)) ([926b52d](home-operations/kopiur@926b52d)) - **deps:** update rust crate serde (1.0.228 → 1.0.229) ([#​270](home-operations/kopiur#270)) ([5fbd713](home-operations/kopiur@5fbd713)) - **deps:** update rust crate thiserror (2.0.18 → 2.0.19) ([#​271](home-operations/kopiur#271)) ([d83730f](home-operations/kopiur@d83730f)) - **deps:** update rust crate tokio (1.52.3 → 1.52.4) ([#​261](home-operations/kopiur#261)) ([accf6ae](home-operations/kopiur@accf6ae)) - writable backup source ([#​254](home-operations/kopiur#254)), stuck deletion finalizer ([#​255](home-operations/kopiur#255)), kopia epoch parameters ([#​258](home-operations/kopiur#258)) ([#​263](home-operations/kopiur#263)) ([94f9a15](home-operations/kopiur@94f9a15)) ##### Miscellaneous Chores - **krew:** kopiur 0.7.5 manifest ([16d7390](home-operations/kopiur@16d7390)) - **mise:** Update tool aqua:astral-sh/uv (0.11.28 → 0.11.29) ([#​256](home-operations/kopiur#256)) ([f822335](home-operations/kopiur@f822335)) - **mise:** Update tool cosign (3.1.1 → 3.1.2) ([#​267](home-operations/kopiur#267)) ([649ba2e](home-operations/kopiur@649ba2e)) </details> --- ### Configuration 📅 **Schedule**: (in timezone America/New_York) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNjAuMiIsInVwZGF0ZWRJblZlciI6IjQzLjI3Mi4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19--> Reviewed-on: https://git.greyrock.io/greyrock-labs/home-ops/pulls/107
perfectra1n
added a commit
that referenced
this pull request
Jul 22, 2026
The scenario predates policy-cascade auto-adoption (#210/#272): it asserted the retained kopia snapshots persist as origin=discovered rows — but the POLICY deliberately survives a schedule cascade, so rediscovered rows are auto-adopted straight back to it (origin: adopted, spec re-derived from the policy), often before a 3s poll can observe the interim discovered state. The old assertion could only pass by racing the adopter. Now accepts both end states with the correct per-origin invariants: a row still discovered must be FORCED Retain; an adopted row must reference the surviving policy and carry its defaultDeletionPolicy. Cleanup drains the uid-keyed catalog set (adopted rows' Delete at teardown is intended — the scenario-scoped repo goes next, and the wave is under the breaker threshold). Validated locally: scenario timed out at 420s pre-fix (both with and without the recycle fix alone), passes in 141s with recycle fix + this assertion.
eleboucher
pushed a commit
to eleboucher/homelab
that referenced
this pull request
Jul 23, 2026
…0.7.4 ➔ 0.8.0) (#1627) This PR contains the following updates: | Package | Update | Change | |---|---|---| | [ghcr.io/home-operations/charts/kopiur](https://github.com/home-operations/kopiur) | minor | `0.7.4` → `0.8.0` | --- ### Release Notes <details> <summary>home-operations/kopiur (ghcr.io/home-operations/charts/kopiur)</summary> ### [`v0.8.0`](https://github.com/home-operations/kopiur/blob/HEAD/CHANGELOG.md#080-2026-07-19) [Compare Source](home-operations/kopiur@0.7.5...0.8.0) ##### ⚠ BREAKING CHANGES - SnapshotPolicy deletion cascade + auto-adoption of discovered snapshots ([#​272](home-operations/kopiur#272)) ##### Features - **deps:** update rust crate tokio (1.52.4 → 1.53.0) ([#​264](home-operations/kopiur#264)) ([6cab0c6](home-operations/kopiur@6cab0c6)) - mass-deletion protection (cascade guard, per-repo breaker, batched deletes) ([#​265](home-operations/kopiur#265)) ([038ea6d](home-operations/kopiur@038ea6d)) - SnapshotPolicy deletion cascade + auto-adoption of discovered snapshots ([#​272](home-operations/kopiur#272)) ([9da8c26](home-operations/kopiur@9da8c26)) ##### Bug Fixes - **deps:** update rust crate anyhow (1.0.103 → 1.0.104) ([#​269](home-operations/kopiur#269)) ([4d81496](home-operations/kopiur@4d81496)) - **deps:** update rust crate clap (4.6.1 → 4.6.2) ([#​257](home-operations/kopiur#257)) ([c238df1](home-operations/kopiur@c238df1)) - **deps:** update rust crate futures (0.3.32 → 0.3.33) ([#​268](home-operations/kopiur#268)) ([926b52d](home-operations/kopiur@926b52d)) - **deps:** update rust crate serde (1.0.228 → 1.0.229) ([#​270](home-operations/kopiur#270)) ([5fbd713](home-operations/kopiur@5fbd713)) - **deps:** update rust crate thiserror (2.0.18 → 2.0.19) ([#​271](home-operations/kopiur#271)) ([d83730f](home-operations/kopiur@d83730f)) - **deps:** update rust crate tokio (1.52.3 → 1.52.4) ([#​261](home-operations/kopiur#261)) ([accf6ae](home-operations/kopiur@accf6ae)) - writable backup source ([#​254](home-operations/kopiur#254)), stuck deletion finalizer ([#​255](home-operations/kopiur#255)), kopia epoch parameters ([#​258](home-operations/kopiur#258)) ([#​263](home-operations/kopiur#263)) ([94f9a15](home-operations/kopiur@94f9a15)) ##### Miscellaneous Chores - **krew:** kopiur 0.7.5 manifest ([16d7390](home-operations/kopiur@16d7390)) - **mise:** Update tool aqua:astral-sh/uv (0.11.28 → 0.11.29) ([#​256](home-operations/kopiur#256)) ([f822335](home-operations/kopiur@f822335)) - **mise:** Update tool cosign (3.1.1 → 3.1.2) ([#​267](home-operations/kopiur#267)) ([649ba2e](home-operations/kopiur@649ba2e)) ### [`v0.7.5`](https://github.com/home-operations/kopiur/blob/HEAD/CHANGELOG.md#075-2026-07-16) [Compare Source](home-operations/kopiur@0.7.4...0.7.5) ##### Bug Fixes - **readme:** update readme admonition ([987957c](home-operations/kopiur@987957c)) - **secctx:** SecurityContextCompatible is verified, not asserted; inherit merges with explicit ([#​259](home-operations/kopiur#259)) ([cbfd97d](home-operations/kopiur@cbfd97d)) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMDEuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEwMS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19--> Reviewed-on: https://git.erwanleboucher.dev/eleboucher/homelab/pulls/1627
hbjydev
pushed a commit
to hbjydev/phoebe
that referenced
this pull request
Aug 27, 2026
…0.7.3 → 0.10.4) (#273) This PR contains the following updates: | Package | Update | Change | |---|---|---| | [ghcr.io/home-operations/charts/kopiur](https://github.com/home-operations/kopiur) | minor | `0.7.3` → `0.10.4` | --- >⚠️ **Warning** > > Some dependencies could not be looked up. Check the [Dependency Dashboard](issues/141) for more information. --- ### Release Notes <details> <summary>home-operations/kopiur (ghcr.io/home-operations/charts/kopiur)</summary> ### [`v0.10.4`](https://github.com/home-operations/kopiur/blob/HEAD/CHANGELOG.md#0104-2026-08-24) [Compare Source](home-operations/kopiur@0.10.3...0.10.4) ##### Bug Fixes - **chart:** give flowSchema its doc comment and timings a clean schema entry ([#​397](home-operations/kopiur#397)) ([7c8dd3f](home-operations/kopiur@7c8dd3f)) - **mover:** read status.resolved via the /status subresource ([#​408](home-operations/kopiur#408)) ([06fc2f9](home-operations/kopiur@06fc2f9)) ##### Miscellaneous Chores - **github-action:** update action docker/github-builder (v1.16.0 → v1.17.0) ([#​407](home-operations/kopiur#407)) ([b6d7d04](home-operations/kopiur@b6d7d04)) - **mise:** update mise tools ([#​400](home-operations/kopiur#400)) ([b1a83f8](home-operations/kopiur@b1a83f8)) - **mise:** update tool kubectl (1.36.3 → 1.36.4) ([#​405](home-operations/kopiur#405)) ([274e3d6](home-operations/kopiur@274e3d6)) - **mise:** update tool lefthook (2.1.10 → 2.1.11) ([#​406](home-operations/kopiur#406)) ([2ba700d](home-operations/kopiur@2ba700d)) - **mise:** update tool rust (1.97.1 → 1.98.0) ([#​403](home-operations/kopiur#403)) ([c655005](home-operations/kopiur@c655005)) - **mise:** update tool yq (4.53.3 → 4.53.4) ([#​402](home-operations/kopiur#402)) ([d1ed8a7](home-operations/kopiur@d1ed8a7)) - **mise:** update tool yq (4.53.4 → 4.53.6) ([#​404](home-operations/kopiur#404)) ([dd5878c](home-operations/kopiur@dd5878c)) ### [`v0.10.3`](https://github.com/home-operations/kopiur/blob/HEAD/CHANGELOG.md#0103-2026-08-18) [Compare Source](home-operations/kopiur@0.10.2...0.10.3) ##### Features - **repository:** seed a fresh repository from its replica — DR for replicated repositories ([#​380](home-operations/kopiur#380)) ([#​396](home-operations/kopiur#396)) ([74665dc](home-operations/kopiur@74665dc)) ##### Bug Fixes - **rust:** update crate cel (0.14.2 → 0.14.3) ([#​387](home-operations/kopiur#387)) ([bf9aa0c](home-operations/kopiur@bf9aa0c)) - **rust:** update crate http-body-util (0.1.4 → 0.1.5) ([#​381](home-operations/kopiur#381)) ([6354e17](home-operations/kopiur@6354e17)) ##### Miscellaneous Chores - **github-action:** update action jdx/mise-action (v4.2.4 → v4.2.5) ([#​388](home-operations/kopiur#388)) ([1040c9e](home-operations/kopiur@1040c9e)) - **mise:** update tool aqua:astral-sh/uv (0.12.3 → 0.12.4) ([#​390](home-operations/kopiur#390)) ([bf3916c](home-operations/kopiur@bf3916c)) - **mise:** update tool aqua:astral-sh/uv (0.12.4 → 0.12.5) ([#​395](home-operations/kopiur#395)) ([b919878](home-operations/kopiur@b919878)) - **mise:** update tool helm (4.2.3 → 4.2.4) ([#​389](home-operations/kopiur#389)) ([5b134fa](home-operations/kopiur@5b134fa)) - **mise:** Update tool oxfmt (0.62.0 → 0.63.0) ([#​386](home-operations/kopiur#386)) ([65db237](home-operations/kopiur@65db237)) ### [`v0.10.2`](https://github.com/home-operations/kopiur/blob/HEAD/CHANGELOG.md#0102-2026-08-13) [Compare Source](home-operations/kopiur@0.10.1...0.10.2) ##### Performance Improvements - **controller:** store-backed reads, LIST dedup, bounded PVC retries, client-side request metrics ([#​382](home-operations/kopiur#382)) ([#​383](home-operations/kopiur#383)) ([2b07022](home-operations/kopiur@2b07022)) ### [`v0.10.1`](https://github.com/home-operations/kopiur/blob/HEAD/CHANGELOG.md#0101-2026-08-11) [Compare Source](home-operations/kopiur@0.10.0...0.10.1) ##### Bug Fixes - **mise:** something about mise warnings ([#​373](home-operations/kopiur#373)) ([fde8073](home-operations/kopiur@fde8073)) - **rust:** update crate cel (0.14.1 → 0.14.2) ([#​379](home-operations/kopiur#379)) ([4a60221](home-operations/kopiur@4a60221)) - **rust:** update crate futures (0.3.33 → 0.3.34) ([#​378](home-operations/kopiur#378)) ([ff657b8](home-operations/kopiur@ff657b8)) - **rust:** update crate rcgen (0.14.8 → 0.14.9) ([#​377](home-operations/kopiur#377)) ([e3b3b3f](home-operations/kopiur@e3b3b3f)) ##### Miscellaneous Chores - **mise:** Update tool aqua:astral-sh/uv (0.12.2 → 0.12.3) ([#​375](home-operations/kopiur#375)) ([952af6d](home-operations/kopiur@952af6d)) ### [`v0.10.0`](https://github.com/home-operations/kopiur/blob/HEAD/CHANGELOG.md#0100-2026-08-09) [Compare Source](home-operations/kopiur@0.9.5...0.10.0) ##### ⚠ BREAKING CHANGES - **rust:** Update crate base64 (0.22.1 → 0.23.1) ([#​366](home-operations/kopiur#366)) ##### Features - **rust:** Update crate base64 (0.22.1 → 0.23.1) ([#​366](home-operations/kopiur#366)) ([2c6e3e7](home-operations/kopiur@2c6e3e7)) - SnapshotReplication (logical snapshot-level replication) + SnapshotPolicy multi-repository fan-out ([#​370](home-operations/kopiur#370)) ([4e06e42](home-operations/kopiur@4e06e42)) ##### Continuous Integration - **github-action:** Update action Swatinem/rust-cache (v2.9.1 → v2.9.2) ([#​372](home-operations/kopiur#372)) ([e4ce173](home-operations/kopiur@e4ce173)) ##### Miscellaneous Chores - **mise:** Update mise tools ([#​367](home-operations/kopiur#367)) ([d6f82f4](home-operations/kopiur@d6f82f4)) - **mise:** Update tool cosign (3.1.2 → 3.1.3) ([#​371](home-operations/kopiur#371)) ([558b9f4](home-operations/kopiur@558b9f4)) ### [`v0.9.5`](https://github.com/home-operations/kopiur/blob/HEAD/CHANGELOG.md#095-2026-08-08) [Compare Source](home-operations/kopiur@0.9.4...0.9.5) ##### Bug Fixes - deliver s3 tls.caBundleRef to every kopia invocation ([#​365](home-operations/kopiur#365)) ([cda0237](home-operations/kopiur@cda0237)) - doctor misses structurally-blocked work ([#​359](home-operations/kopiur#359)) + phase/gate exhaustiveness ratchet ([#​363](home-operations/kopiur#363)) ([b844974](home-operations/kopiur@b844974)) - **rust:** update crate thiserror (2.0.19 → 2.0.20) ([#​360](home-operations/kopiur#360)) ([4aa646d](home-operations/kopiur@4aa646d)) ##### Continuous Integration - **github-action:** Update action docker/github-builder (v1.15.0 → v1.16.0) ([#​362](home-operations/kopiur#362)) ([db32818](home-operations/kopiur@db32818)) ### [`v0.9.4`](https://github.com/home-operations/kopiur/blob/HEAD/CHANGELOG.md#094-2026-08-08) [Compare Source](home-operations/kopiur@0.9.3...0.9.4) ##### Features - **docs:** Update README.md ([#​358](home-operations/kopiur#358)) ([05d2506](home-operations/kopiur@05d2506)) ##### Bug Fixes - implement pvcSelector/groupBy ([#​346](home-operations/kopiur#346)) and model deduped backups as Unchanged ([#​351](home-operations/kopiur#351)) ([#​354](home-operations/kopiur#354)) ([a2b9ae6](home-operations/kopiur@a2b9ae6)) - **rust:** update crate clap (4.6.5 → 4.6.6) ([#​357](home-operations/kopiur#357)) ([e9ca396](home-operations/kopiur@e9ca396)) ##### Miscellaneous Chores - **mise:** Update tool node (24.18.1 → v24.19.0) ([#​353](home-operations/kopiur#353)) ([2f28c6d](home-operations/kopiur@2f28c6d)) - **mise:** Update tool oxfmt (0.61.0 → 0.62.0) ([#​356](home-operations/kopiur#356)) ([e95df6b](home-operations/kopiur@e95df6b)) ### [`v0.9.3`](https://github.com/home-operations/kopiur/blob/HEAD/CHANGELOG.md#093-2026-08-05) [Compare Source](home-operations/kopiur@0.9.2...0.9.3) ##### Features - repository-level circuit breaker — stop fanning out doomed Jobs when the backend is unreachable ([#​350](home-operations/kopiur#350)) ([5d787b9](home-operations/kopiur@5d787b9)) ##### Bug Fixes - **rust:** update crate base64 (0.23.0 → 0.23.1) ([#​349](home-operations/kopiur#349)) ([11e7aa8](home-operations/kopiur@11e7aa8)) ##### Continuous Integration - **github-action:** Update action jdx/mise-action (v4.2.3 → v4.2.4) ([#​347](home-operations/kopiur#347)) ([1c9cf3b](home-operations/kopiur@1c9cf3b)) ##### Miscellaneous Chores - **renovate:** keep 0.x minors out of automerge ([#​352](home-operations/kopiur#352)) ([95d672a](home-operations/kopiur@95d672a)) ### [`v0.9.2`](https://github.com/home-operations/kopiur/blob/HEAD/CHANGELOG.md#092-2026-08-02) [Compare Source](home-operations/kopiur@0.9.1...0.9.2) ##### Features - **repository:** object-lock blob retention ([#​332](home-operations/kopiur#332)); fix blank volumeSnapshotClassName ([#​344](home-operations/kopiur#344)) ([dab8fdd](home-operations/kopiur@dab8fdd)) ##### Bug Fixes - **ci:** fail the merge gate on cancelled jobs, and cut e2e artifact time ([#​330](home-operations/kopiur#330)) ([1847a8a](home-operations/kopiur@1847a8a)) - **rust:** update crate time (0.3.54 → 0.3.55) ([#​337](home-operations/kopiur#337)) ([ee1b951](home-operations/kopiur@ee1b951)) ##### Continuous Integration - **github-action:** Update action docker/github-builder (v1.14.0 → v1.15.0) ([#​327](home-operations/kopiur#327)) ([f5514fc](home-operations/kopiur@f5514fc)) - **github-action:** Update action docker/login-action (v4.5.1 → v4.5.2) ([#​331](home-operations/kopiur#331)) ([d90efdf](home-operations/kopiur@d90efdf)) - **github-action:** Update action docker/login-action (v4.5.2 → v4.6.0) ([#​339](home-operations/kopiur#339)) ([b783bcc](home-operations/kopiur@b783bcc)) - **github-action:** Update action home-operations/.github/actions/workflow-lint (v1.0.2 → v1.0.3) ([#​343](home-operations/kopiur#343)) ([4af16d5](home-operations/kopiur@4af16d5)) - update shared actions and use self-repository syntax ([#​341](home-operations/kopiur#341)) ([559e23f](home-operations/kopiur@559e23f)) ##### Miscellaneous Chores - **deps:** lock file maintenance (pep621) ([#​334](home-operations/kopiur#334)) ([e19ae0e](home-operations/kopiur@e19ae0e)) - **mise:** Lock file maintenance tool (mise) ([#​335](home-operations/kopiur#335)) ([666afb5](home-operations/kopiur@666afb5)) - **mise:** prune lockfile to used platforms ([#​342](home-operations/kopiur#342)) ([741d6a4](home-operations/kopiur@741d6a4)) - **mise:** Update tool aqua:astral-sh/uv (0.12.0 → 0.12.1) ([#​333](home-operations/kopiur#333)) ([3ed2766](home-operations/kopiur@3ed2766)) - **mise:** Update tool promtool (3.13.1 → 3.13.2) ([#​329](home-operations/kopiur#329)) ([ac1752e](home-operations/kopiur@ac1752e)) - **mise:** Update tool zizmor (1.28.0 → 1.29.0) ([#​340](home-operations/kopiur#340)) ([410dc26](home-operations/kopiur@410dc26)) - **release-please:** standardize the release pull request title pattern ([#​338](home-operations/kopiur#338)) ([21013a3](home-operations/kopiur@21013a3)) - **rust:** lock file maintenance crate (cargo) ([#​336](home-operations/kopiur#336)) ([8606177](home-operations/kopiur@8606177)) ### [`v0.9.1`](https://github.com/home-operations/kopiur/blob/HEAD/CHANGELOG.md#091-2026-07-29) [Compare Source](home-operations/kopiur@0.9.0...0.9.1) ##### Features - **rust:** update crate http (1.4.2 → 1.5.0) ([#​323](home-operations/kopiur#323)) ([cf0b149](home-operations/kopiur@cf0b149)) ##### Bug Fixes - **controller:** survive a slow API server instead of abdicating on the first one ([#​319](home-operations/kopiur#319)) ([#​324](home-operations/kopiur#324)) ([df7e90c](home-operations/kopiur@df7e90c)) - **rust:** update crate rustls (0.23.42 → 0.23.43) ([#​326](home-operations/kopiur#326)) ([2f5123e](home-operations/kopiur@2f5123e)) ##### Tests - **e2e:** retry flaky tests with cargo-nextest ([#​322](home-operations/kopiur#322)) ([558ac85](home-operations/kopiur@558ac85)) ##### Miscellaneous Chores - **mise:** Update tool aqua:astral-sh/uv (0.11.33 → 0.12.0) ([#​320](home-operations/kopiur#320)) ([66ec350](home-operations/kopiur@66ec350)) - **mise:** Update tool node (24.18.0 → v24.18.1) ([#​325](home-operations/kopiur#325)) ([1c833a4](home-operations/kopiur@1c833a4)) ### [`v0.9.0`](https://github.com/home-operations/kopiur/blob/HEAD/CHANGELOG.md#090-2026-07-28) [Compare Source](home-operations/kopiur@0.8.1...0.9.0) ##### ⚠ BREAKING CHANGES - **rust:** Update crate serial\_test (3.5.0 → 4.0.1) ([#​307](home-operations/kopiur#307)) - **secctx:** identity-aware mover merge, recorded snapshot identity, restore inherit-from-snapshot ([#​304](home-operations/kopiur#304)) ##### Features - **rust:** Update crate serial\_test (3.5.0 → 4.0.1) ([#​307](home-operations/kopiur#307)) ([4392e4a](home-operations/kopiur@4392e4a)) - **secctx:** identity-aware mover merge, recorded snapshot identity, restore inherit-from-snapshot ([#​304](home-operations/kopiur#304)) ([0632a29](home-operations/kopiur@0632a29)) ##### Bug Fixes - **rust:** update crate cel (0.14.0 → 0.14.1) ([#​317](home-operations/kopiur#317)) ([00cf539](home-operations/kopiur@00cf539)) - **rust:** update crate schemars (1.2.1 → 1.2.2) ([#​313](home-operations/kopiur#313)) ([b6e6268](home-operations/kopiur@b6e6268)) ##### Build System - **mise:** add actionlint and refresh the lockfile ([#​309](home-operations/kopiur#309)) ([e74c9b3](home-operations/kopiur@e74c9b3)) ##### Continuous Integration - adopt the shared workflow-lint and docs-build actions ([#​310](home-operations/kopiur#310)) ([7d4b329](home-operations/kopiur@7d4b329)) - gate pull requests on Build Success and share the docs build ([#​306](home-operations/kopiur#306)) ([1b693aa](home-operations/kopiur@1b693aa)) - **github-action:** Update action docker/login-action (v4.5.0 → v4.5.1) ([#​312](home-operations/kopiur#312)) ([29d00b1](home-operations/kopiur@29d00b1)) - **github-action:** Update action jdx/mise-action (v4.2.1 → v4.2.2) ([#​311](home-operations/kopiur#311)) ([65bbca4](home-operations/kopiur@65bbca4)) - **github-action:** Update action jdx/mise-action (v4.2.2 → v4.2.3) ([#​315](home-operations/kopiur#315)) ([5f517d4](home-operations/kopiur@5f517d4)) - skip release-please PRs in checks and drop nightly e2e ([#​305](home-operations/kopiur#305)) ([ee6d4f5](home-operations/kopiur@ee6d4f5)) ##### Miscellaneous Chores - **krew:** kopiur 0.8.1 manifest ([84df757](home-operations/kopiur@84df757)) - **mise:** Update tool aqua:astral-sh/uv (0.11.32 → 0.11.33) ([#​318](home-operations/kopiur#318)) ([f63bc3a](home-operations/kopiur@f63bc3a)) - **mise:** Update tool oxfmt (0.60.0 → 0.61.0) ([#​314](home-operations/kopiur#314)) ([bdbf6c6](home-operations/kopiur@bdbf6c6)) - **renovate:** drop the stale automerge overrides ([#​308](home-operations/kopiur#308)) ([c13a3d5](home-operations/kopiur@c13a3d5)) - standardize release-please changelog sections ([#​316](home-operations/kopiur#316)) ([273d0f8](home-operations/kopiur@273d0f8)) ### [`v0.8.1`](https://github.com/home-operations/kopiur/blob/HEAD/CHANGELOG.md#081-2026-07-24) [Compare Source](home-operations/kopiur@0.8.0...0.8.1) ##### Features - **deps:** update rust crate base64 (0.22.1 → 0.23.0) ([#​293](home-operations/kopiur#293)) ([5ab14d5](home-operations/kopiur@5ab14d5)) - **deps:** update rust crate kube (4.0.0 → 4.2.0) ([#​288](home-operations/kopiur#288)) ([b067458](home-operations/kopiur@b067458)) - httpRequest hook headers ([#​290](home-operations/kopiur#290)) + recovery-aware alerts ([#​280](home-operations/kopiur#280)) ([#​296](home-operations/kopiur#296)) ([f388a14](home-operations/kopiur@f388a14)) - **renovate:** automerge if it passes CI ([66f243f](home-operations/kopiur@66f243f)) ##### Bug Fixes - **controller:** survive an API-server outage without exhausting file descriptors ([#​287](home-operations/kopiur#287)) ([ce184b6](home-operations/kopiur@ce184b6)) - **deps:** update rust crate clap (4.6.2 → 4.6.3) ([#​277](home-operations/kopiur#277)) ([e3d4113](home-operations/kopiur@e3d4113)) - **deps:** update rust crate libc (0.2.186 → 0.2.189) ([#​283](home-operations/kopiur#283)) ([2b2e636](home-operations/kopiur@2b2e636)) - **deps:** update rust crate serde\_json (1.0.150 → 1.0.151) ([#​275](home-operations/kopiur#275)) ([1410f42](home-operations/kopiur@1410f42)) - **deps:** update rust crate time (0.3.53 → 0.3.54) ([#​276](home-operations/kopiur#276)) ([ff250ff](home-operations/kopiur@ff250ff)) - **deps:** update rust crate tokio (1.53.0 → 1.53.1) ([#​281](home-operations/kopiur#281)) ([82082e9](home-operations/kopiur@82082e9)) - **helm:** stamp Chart.yaml version on release + document image sub-fields ([#​289](home-operations/kopiur#289)) ([041549a](home-operations/kopiur@041549a)) - **probe:** stamp the health probe at launch so it stops recycling its Job ([#​273](home-operations/kopiur#273)) ([#​278](home-operations/kopiur#278)) ([7fa3657](home-operations/kopiur@7fa3657)) - **release:** use the generic updater for Chart.yaml + stamp README badges ([#​292](home-operations/kopiur#292)) ([f4a3141](home-operations/kopiur@f4a3141)) - retention-aware adoption stops the adopt/prune/rediscover livelock ([#​299](home-operations/kopiur#299)) ([4093f12](home-operations/kopiur@4093f12)) ##### Styles - indent markdown at 2 to match embedded yaml ([#​279](home-operations/kopiur#279)) ([06885e0](home-operations/kopiur@06885e0)) ##### Miscellaneous Chores - **github-release:** Update release helm-unittest/helm-unittest (v1.1.1 → v1.1.2) ([#​301](home-operations/kopiur#301)) ([e62272d](home-operations/kopiur@e62272d)) - **krew:** kopiur 0.8.0 manifest ([2be416f](home-operations/kopiur@2be416f)) - **mise:** Update tool aqua:astral-sh/uv (0.11.29 → 0.11.30) ([#​282](home-operations/kopiur#282)) ([e12f96c](home-operations/kopiur@e12f96c)) - **mise:** Update tool aqua:astral-sh/uv (0.11.30 → 0.11.31) ([#​286](home-operations/kopiur#286)) ([a3f31e6](home-operations/kopiur@a3f31e6)) - **mise:** Update tool aqua:astral-sh/uv (0.11.31 → 0.11.32) ([#​298](home-operations/kopiur#298)) ([9d8c171](home-operations/kopiur@9d8c171)) - **mise:** Update tool kubectl (1.36.2 → 1.36.3) ([#​291](home-operations/kopiur#291)) ([ef290f8](home-operations/kopiur@ef290f8)) - **mise:** Update tool oxfmt (0.59.0 → 0.60.0) ([#​284](home-operations/kopiur#284)) ([ad8a18a](home-operations/kopiur@ad8a18a)) - **mise:** Update tool rust (1.96.1 → 1.97.1) ([#​222](home-operations/kopiur#222)) ([fbeb2fa](home-operations/kopiur@fbeb2fa)) - **mise:** Update tool zizmor (1.27.0 → 1.28.0) ([#​285](home-operations/kopiur#285)) ([9ef35ce](home-operations/kopiur@9ef35ce)) ### [`v0.8.0`](https://github.com/home-operations/kopiur/blob/HEAD/CHANGELOG.md#080-2026-07-19) [Compare Source](home-operations/kopiur@0.7.5...0.8.0) ##### ⚠ BREAKING CHANGES - SnapshotPolicy deletion cascade + auto-adoption of discovered snapshots ([#​272](home-operations/kopiur#272)) ##### Features - **deps:** update rust crate tokio (1.52.4 → 1.53.0) ([#​264](home-operations/kopiur#264)) ([6cab0c6](home-operations/kopiur@6cab0c6)) - mass-deletion protection (cascade guard, per-repo breaker, batched deletes) ([#​265](home-operations/kopiur#265)) ([038ea6d](home-operations/kopiur@038ea6d)) - SnapshotPolicy deletion cascade + auto-adoption of discovered snapshots ([#​272](home-operations/kopiur#272)) ([9da8c26](home-operations/kopiur@9da8c26)) ##### Bug Fixes - **deps:** update rust crate anyhow (1.0.103 → 1.0.104) ([#​269](home-operations/kopiur#269)) ([4d81496](home-operations/kopiur@4d81496)) - **deps:** update rust crate clap (4.6.1 → 4.6.2) ([#​257](home-operations/kopiur#257)) ([c238df1](home-operations/kopiur@c238df1)) - **deps:** update rust crate futures (0.3.32 → 0.3.33) ([#​268](home-operations/kopiur#268)) ([926b52d](home-operations/kopiur@926b52d)) - **deps:** update rust crate serde (1.0.228 → 1.0.229) ([#​270](home-operations/kopiur#270)) ([5fbd713](home-operations/kopiur@5fbd713)) - **deps:** update rust crate thiserror (2.0.18 → 2.0.19) ([#​271](home-operations/kopiur#271)) ([d83730f](home-operations/kopiur@d83730f)) - **deps:** update rust crate tokio (1.52.3 → 1.52.4) ([#​261](home-operations/kopiur#261)) ([accf6ae](home-operations/kopiur@accf6ae)) - writable backup source ([#​254](home-operations/kopiur#254)), stuck deletion finalizer ([#​255](home-operations/kopiur#255)), kopia epoch parameters ([#​258](home-operations/kopiur#258)) ([#​263](home-operations/kopiur#263)) ([94f9a15](home-operations/kopiur@94f9a15)) ##### Miscellaneous Chores - **krew:** kopiur 0.7.5 manifest ([16d7390](home-operations/kopiur@16d7390)) - **mise:** Update tool aqua:astral-sh/uv (0.11.28 → 0.11.29) ([#​256](home-operations/kopiur#256)) ([f822335](home-operations/kopiur@f822335)) - **mise:** Update tool cosign (3.1.1 → 3.1.2) ([#​267](home-operations/kopiur#267)) ([649ba2e](home-operations/kopiur@649ba2e)) ### [`v0.7.5`](https://github.com/home-operations/kopiur/blob/HEAD/CHANGELOG.md#075-2026-07-16) [Compare Source](home-operations/kopiur@0.7.4...0.7.5) ##### Bug Fixes - **readme:** update readme admonition ([987957c](home-operations/kopiur@987957c)) - **secctx:** SecurityContextCompatible is verified, not asserted; inherit merges with explicit ([#​259](home-operations/kopiur#259)) ([cbfd97d](home-operations/kopiur@cbfd97d)) ### [`v0.7.4`](https://github.com/home-operations/kopiur/blob/HEAD/CHANGELOG.md#074-2026-07-15) [Compare Source](home-operations/kopiur@0.7.3...0.7.4) ##### Features - bound Snapshot CR reconcile cost at scale ([#​249](home-operations/kopiur#249)) ([#​253](home-operations/kopiur#253)) ([3697aa9](home-operations/kopiur@3697aa9)) - **docs:** clean up admonitions in docs ([71c5d85](home-operations/kopiur@71c5d85)) - multi-cluster shared repository support — cluster identity, foreign-aware discovery, collision-free maintenance leases ([#​251](home-operations/kopiur#251)) ([a4593d8](home-operations/kopiur@a4593d8)) ##### Bug Fixes - batch of six open-issue fixes ([#​248](home-operations/kopiur#248) [#​238](home-operations/kopiur#238) [#​245](home-operations/kopiur#245) [#​196](home-operations/kopiur#196) [#​237](home-operations/kopiur#237) [#​250](home-operations/kopiur#250)) ([#​252](home-operations/kopiur#252)) ([045a5a5](home-operations/kopiur@045a5a5)) - **deps:** update rust crate http-body-util (0.1.3 → 0.1.4) ([#​244](home-operations/kopiur#244)) ([4878006](home-operations/kopiur@4878006)) - **deps:** update rust crate rustls (0.23.41 → 0.23.42) ([#​243](home-operations/kopiur#243)) ([13b1dc4](home-operations/kopiur@13b1dc4)) ##### Miscellaneous Chores - **krew:** kopiur 0.7.3 manifest ([d521675](home-operations/kopiur@d521675)) - **mise:** Update tool aqua:EmbarkStudios/cargo-deny (0.19.9 → 0.20.2) ([#​226](home-operations/kopiur#226)) ([9c109b4](home-operations/kopiur@9c109b4)) - **mise:** Update tool oxfmt (0.58.0 → 0.59.0) ([#​246](home-operations/kopiur#246)) ([41625e9](home-operations/kopiur@41625e9)) - **mise:** Update tool zizmor (1.26.1 → 1.27.0) ([#​247](home-operations/kopiur#247)) ([fe10a6a](home-operations/kopiur@fe10a6a)) </details> --- ### Configuration 📅 **Schedule**: (in timezone Europe/London) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNjAuMiIsInVwZGF0ZWRJblZlciI6IjQ0LjQ2LjUiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbInJlbm92YXRlL2NvbnRhaW5lciIsInR5cGUvbWlub3IiXX0=--> Reviewed-on: https://forgejo.hayden.moe/hayden/phoebe/pulls/273
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two features that close the policy lifecycle gap described in #210: deleting a policy now cleans up its Snapshot CRs (keeping the kopia data), and recreating the policy adopts that history back so retention resumes governing it.
1. SnapshotPolicy deletion cascade
Deleting a
SnapshotPolicynow removes its config-labeled Snapshot CRs via a newpolicy-cleanupfinalizer:spec.deletion.onPolicyDelete: Retain— the CRs are removed, every kopia snapshot survives (rediscoverable on the next catalog scan, adoptable on recreation). The cascade stampspruned-by: policy-cascade, so children release quietly with zero repository contact.Delete— each CR's owndeletionPolicyapplies, issued as external deletions so the mass-deletion breaker (feat: mass-deletion protection (cascade guard, per-repo breaker, batched deletes) #265) gates bulk waves behind theallow-mass-deletionack. Deliberately unstamped: a policy delete withDeleteis exactly the kro-incident shape the breaker exists for.2. Auto-adoption of discovered snapshots (fixes #210)
Discovered snapshots whose exact resolved identity (structured
username/hostname/sourcePathcomparison) matches a liveSnapshotPolicyare adopted automatically: the row is recreated in the policy's namespace asorigin: adopted,phase: Succeeded, config-labeled — indistinguishable from produced snapshots to retention, deletion, batching, catalog dedup, and the breaker. GFS retention then governs and prunes the old history perspec.retention, ending the "pre-DR history occupies storage forever" failure mode.spec.adoption: Ignore) or per repository (spec.catalog.adoption: Ignore).Ignore, and a hardHostClass::ForeignClusterrefusal).catalog-scan-requested-attoken on the repository; scans retire it by token equality (status.catalog.scanRequestHonored), and launch attempts are rate-limited (scanRequestAttemptAt) so a Ready-but-unreachable repo never churns bootstrap Jobs.SnapshotsAdopted/AdoptionScanRequested/SnapshotRetainedOnPolicyDeleteevents,status.adoptionsummary on the policy, andkopiur_snapshots_adopted+ cascade counters.Hardening that rode along
deletionPolicy: Deleteat admission.SnapshotRetainedOnScheduleDeleteevent no longer promises rediscovery "within the catalog refresh interval" (misleading with periodic refresh off).BREAKING CHANGE
Deleting a
SnapshotPolicynow removes its config-labeled Snapshot CRs by default (onPolicyDelete: Retain— kopia snapshots are kept and rediscoverable/adoptable). Previously the CRs were left dangling.Test coverage
PrunedBy × DeletionPolicyprune table, scan-request predicate matrix (equality retirement, launch backoff, no-token regression pins), full adoption matrix (identity near-misses, foreign refusal, 409-heal, knob precedence), webhook origin-flip denials.keepLatest: 1→ scan request → discovered → adopted → retention prunes kopia-side to exactly 1, with a non-matching-identity negative control), ClusterRepository cross-namespace adoption, adoption opt-out, cascade default-Retain (incl. webhook-labeled manual Snapshot), breaker-gated opt-inDelete(hold → ack → drain), and the schedule+policy simultaneous-delete race.cargo test --workspace,clippy -D warnings,gen-check, and the cognitive-complexity ratchet (29/29, unchanged) green. Each milestone was independently review-gated; one Critical (object-store scan-request honoring) was caught in review and fixed with a regression test (734b2f8).Local fresh-kind validation of the new e2e binaries is running; CI's e2e suite is the authoritative gate for this PR.
Closes #210