Skip to main content

tatara_process/
finalizers.rs

1//! Workspace-canonical finalizer keys the tatara controllers stamp on
2//! their owned CRDs to gate cascade-delete + orphan-reap on graceful
3//! teardown.
4//!
5//! # Family
6//!
7//! The reconciler binaries in this workspace own three top-level CRDs
8//! that participate in the K8s garbage-collection contract via the
9//! `metadata.finalizers` list:
10//!
11//! - [`PROCESS`] — the [`crate::prelude::Process`] reconciler's
12//!   finalizer key. Stamped by
13//!   `tatara-reconciler::phase_machine::handle_pending` via
14//!   [`crate::patch::ensure_finalizer`]; stripped by `handle_reaped`
15//!   via `remove_finalizer`.
16//! - [`ALLOCATION`] — the [`crate::prelude::EphemeralAllocation`]
17//!   controller's finalizer key. Reserved by
18//!   `tatara-pool-reconciler::controller_allocation::reconcile` for
19//!   the future Bound → Releasing → Released cascade-delete gate.
20//! - [`POOL`] — the [`crate::prelude::EphemeralPool`] controller's
21//!   finalizer key. Reserved by
22//!   `tatara-pool-reconciler::controller_pool::build_member_process`
23//!   for the future member-Process orphan-reap gate.
24//!
25//! [`ALL`] enumerates the three consts in stable declaration order so
26//! downstream sweeps (a fleet-wide finalizer audit binary, an ops
27//! diagnostic dumping every tatara-owned finalizer on a cluster, a
28//! per-key coverage report) iterate through ONE substrate slice
29//! rather than three hand-typed literals.
30//!
31//! # Peer axes
32//!
33//! Peer to [`crate::annotations`] on the "K8s metadata key that pins
34//! a reconciliation contract" axis-family — where [`crate::annotations`]
35//! owns keys the reconciler READS or WRITES on OWNED resources
36//! (FluxCD `HelmRelease`, `Kustomization`, member `Process` on a pool
37//! member), this module owns keys the reconciler stamps on the CRDs
38//! IT OWNS (the [`crate::prelude::Process`],
39//! [`crate::prelude::EphemeralAllocation`],
40//! [`crate::prelude::EphemeralPool`] `metadata.finalizers` list).
41//! Both families carry the same `tatara.pleme.io/<slug>` wire-form
42//! shape; the finalizer family additionally carries the shared
43//! `<owner>-finalizer` suffix — a convention pinned by
44//! [`tests::all_carry_finalizer_suffix`] so a future addition to the
45//! family cannot silently drift the suffix.
46//!
47//! # Compounding
48//!
49//! Pre-lift the three wire-form finalizer literals were spread across
50//! three files (the [`crate::PROCESS_FINALIZER`] top-level const at
51//! [`crate`] + private `POOL_FINALIZER` / `ALLOC_FINALIZER` consts in
52//! `tatara-pool-reconciler`), with no cross-owner coherence pin
53//! binding the shared `tatara.pleme.io/<x>-finalizer` shape. A future
54//! rename that shifted (e.g.) the group segment to `v2/` or the
55//! per-owner suffix to `-guard` had to be applied at three separate
56//! files coherently — a partial edit would silently orphan or
57//! double-finalize whichever owner missed the update. Post-lift the
58//! three consts live at ONE substrate owner + one closed [`ALL`]
59//! slice + four family-invariant pins (per-key wire-form parity,
60//! per-key `-finalizer` suffix, per-key `tatara.pleme.io/` prefix,
61//! cross-key uniqueness).
62//!
63//! Theory grounding: THEORY.md §VI.1 (generation over composition —
64//! the `tatara.pleme.io/<owner>-finalizer` wire-form shape recurred
65//! at THREE hand-authored declaration sites past the ★★
66//! PRIME-DIRECTIVE ≥ 2 duplication threshold; lifted to ONE
67//! per-owner const + ONE `ALL` slice + four family-invariant pins
68//! here). THEORY.md §II.1 invariant 5 (composition preserves proofs
69//! — the invariant pins bind the family shape at fail-before-pass-
70//! after granularity; a future rename that drifted the group prefix
71//! or the suffix at ONE arm surfaces at [`tests::all_share_group_prefix`]
72//! or [`tests::all_carry_finalizer_suffix`] rather than as silent
73//! operator-facing skew at whichever owner's finalizer no longer
74//! matches the K8s-side registered wire form).
75
76/// Finalizer key stamped on the [`crate::prelude::Process`]
77/// `metadata.finalizers` list by
78/// `tatara-reconciler::phase_machine::handle_pending`. The reconciler
79/// blocks K8s garbage collection until it has emitted the terminal
80/// [`crate::prelude::ProcessAttestation`] receipt and stripped the
81/// finalizer at `handle_reaped`.
82///
83/// Re-exported at the crate root as [`crate::PROCESS_FINALIZER`] for
84/// consumers that predate this module.
85pub const PROCESS: &str = "tatara.pleme.io/process-finalizer";
86
87/// Finalizer key reserved for the
88/// [`crate::prelude::EphemeralAllocation`] controller
89/// (`tatara-pool-reconciler::controller_allocation`). Once the Bound
90/// → Releasing → Released cascade is wired the controller will
91/// stamp/strip this key through the same
92/// [`crate::patch::ensure_finalizer`] / `remove_finalizer` primitives
93/// the Process reconciler already routes through for [`PROCESS`].
94pub const ALLOCATION: &str = "tatara.pleme.io/allocation-finalizer";
95
96/// Finalizer key reserved for the [`crate::prelude::EphemeralPool`]
97/// controller (`tatara-pool-reconciler::controller_pool`). Once the
98/// member-Process orphan-reap gate is wired the controller will
99/// stamp/strip this key at pool birth + pool teardown through the
100/// same [`crate::patch::ensure_finalizer`] / `remove_finalizer`
101/// primitives.
102pub const POOL: &str = "tatara.pleme.io/pool-finalizer";
103
104/// Closed family of tatara-owned finalizer keys in declaration
105/// order: [`PROCESS`], [`ALLOCATION`], [`POOL`]. Downstream sweeps
106/// (a per-owner coverage report, a fleet-wide finalizer audit
107/// binary, an ops diagnostic dumping every tatara-owned finalizer
108/// on a cluster) iterate through this ONE slice rather than
109/// re-listing the three consts by hand.
110///
111/// Pinned to length 3 by [`tests::all_matches_declared_family`] so
112/// a future addition to the family cannot slip past the coherence
113/// harness without landing here first.
114pub const ALL: &[&str] = &[PROCESS, ALLOCATION, POOL];
115
116/// Shared group prefix every finalizer key in the [`ALL`] family
117/// carries — the same `tatara.pleme.io/` group segment every
118/// tatara-owned CRD annotation + finalizer key rides through, matching
119/// the `#[kube(group = "tatara.pleme.io", …)]` derive slot on the CRD
120/// structs. Pinned across every arm by
121/// [`tests::all_share_group_prefix`] so a future rename of the group
122/// (a shift to `v2/` under a migration, a per-fleet override) lands
123/// at ONE substrate const here + the paired CRD derive slots rather
124/// than at three independent finalizer wire forms.
125///
126/// Compile-time alias of the crate-wide substrate owner
127/// [`crate::GROUP_PREFIX`] — the ONE `pub const &'static str` that
128/// owns the `<GROUP>/` byte-shape every tatara wire-form key uses as
129/// its reverse-DNS prefix. Pre-lift this const restated the literal
130/// `"tatara.pleme.io/"` inline as a byte-parity peer of the sibling
131/// [`crate::annotations::GROUP_PREFIX`] (pinned equal by
132/// [`crate::annotations_family_tests::group_prefix_matches_finalizers_group_prefix`]);
133/// post-lift both peer consts route through [`crate::GROUP_PREFIX`]
134/// so byte equality holds by construction (the same `&'static str`
135/// address), and a future rename of the reverse-DNS root lands at
136/// the ONE crate-root const with every peer wire-form consumer
137/// inheriting the shift mechanically rather than at three independent
138/// per-module string literals.
139pub const GROUP_PREFIX: &str = crate::GROUP_PREFIX;
140
141/// Shared owner-suffix every finalizer key in the [`ALL`] family
142/// carries — the convention `<owner>-finalizer` distinguishes a
143/// finalizer key from a signal/label/annotation key on the same
144/// group. Pinned across every arm by
145/// [`tests::all_carry_finalizer_suffix`].
146pub const FINALIZER_SUFFIX: &str = "-finalizer";
147
148#[cfg(test)]
149mod tests {
150    use super::*;
151
152    // ─── per-const wire-form pins ─────────────────────────────────
153    //
154    // Each pin binds ONE const to its exact wire-form string at
155    // fail-before-pass-after granularity. A regression that renamed
156    // the const's wire form (a group-segment shift, a suffix swap,
157    // an accidental case drift) surfaces HERE rather than as silent
158    // K8s-API-server-side skew at whichever owner's finalizer no
159    // longer matches the registered key on the live cluster.
160
161    #[test]
162    fn process_wire_form_pin() {
163        assert_eq!(PROCESS, "tatara.pleme.io/process-finalizer");
164    }
165
166    #[test]
167    fn allocation_wire_form_pin() {
168        assert_eq!(ALLOCATION, "tatara.pleme.io/allocation-finalizer");
169    }
170
171    #[test]
172    fn pool_wire_form_pin() {
173        assert_eq!(POOL, "tatara.pleme.io/pool-finalizer");
174    }
175
176    // ─── family-invariant pins ────────────────────────────────────
177    //
178    // The three consts form a closed family. These pins bind the
179    // family-wide invariants (fixed length, no dupes across arms,
180    // shared group prefix, shared `-finalizer` suffix) so a future
181    // addition to the family that violated ANY invariant surfaces at
182    // this module's tests rather than as silent skew at some
183    // downstream site.
184
185    #[test]
186    fn all_matches_declared_family() {
187        // A regression that added a fourth const to the family
188        // without appending it to [`ALL`] (or shrank the family
189        // without pruning the slice) would surface here. The
190        // three-arm listing pins the declaration order the pool
191        // reconciler + downstream sweep binaries iterate through.
192        assert_eq!(ALL, &[PROCESS, ALLOCATION, POOL]);
193        assert_eq!(ALL.len(), 3);
194    }
195
196    #[test]
197    fn all_share_group_prefix() {
198        // Every arm of the family MUST start with the shared
199        // `tatara.pleme.io/` group segment — the SAME prefix every
200        // tatara-owned CRD annotation + finalizer key rides through,
201        // matching the `#[kube(group = "tatara.pleme.io", …)]` slot
202        // on every CRD struct. A future rename that drifted ONE
203        // arm's prefix (a copy-paste that landed on `tatara.io/`)
204        // would silently escape the K8s garbage-collection contract
205        // for that CRD; the pin surfaces the drift at compile-time
206        // rather than at operator-visible orphan cascade skew.
207        assert_eq!(GROUP_PREFIX, "tatara.pleme.io/");
208        for key in ALL {
209            assert!(
210                key.starts_with(GROUP_PREFIX),
211                "{key} must start with {GROUP_PREFIX}"
212            );
213        }
214    }
215
216    #[test]
217    fn all_carry_finalizer_suffix() {
218        // Every arm of the family MUST end with `-finalizer` — the
219        // convention that distinguishes a finalizer key from a
220        // signal/label/annotation key on the same group prefix. A
221        // future addition that omitted the suffix (e.g. a bare
222        // `tatara.pleme.io/pool` accidentally landing in the
223        // family) would collide with an existing annotation key on
224        // the peer [`crate::annotations`] axis and silently
225        // double-book the K8s metadata slot.
226        assert_eq!(FINALIZER_SUFFIX, "-finalizer");
227        for key in ALL {
228            assert!(
229                key.ends_with(FINALIZER_SUFFIX),
230                "{key} must end with {FINALIZER_SUFFIX}"
231            );
232        }
233    }
234
235    #[test]
236    fn all_are_pairwise_unique() {
237        // No two arms of the family may share the same wire form —
238        // a copy-paste regression that duplicated the [`PROCESS`]
239        // wire form at the [`POOL`] arm would silently rejoin the
240        // three cascade-delete gates onto ONE finalizer, breaking
241        // the per-CRD garbage-collection contract at the moment
242        // whichever owner stripped its shared finalizer first.
243        let mut seen: Vec<&str> = Vec::new();
244        for key in ALL {
245            assert!(!seen.contains(key), "duplicate finalizer key in ALL: {key}");
246            seen.push(key);
247        }
248        assert_eq!(seen.len(), ALL.len());
249    }
250
251    // ─── crate-root re-export coherence pin ───────────────────────
252    //
253    // The pre-existing [`crate::PROCESS_FINALIZER`] top-level const
254    // now routes through this module's [`PROCESS`] owner. Pin the
255    // coherence so a regression that decoupled the re-export
256    // surfaces here rather than as silent skew between the reconciler
257    // production callsites (which reach through
258    // `tatara_process::PROCESS_FINALIZER`) and any future consumer
259    // reaching through `tatara_process::finalizers::PROCESS` directly.
260
261    #[test]
262    fn process_finalizer_top_level_reexport_routes_through_finalizers_process() {
263        assert_eq!(crate::PROCESS_FINALIZER, PROCESS);
264    }
265}