Skip to main content

tatara_process/
flux_resource.rs

1//! Closed-set of FluxCD resource kinds emitted or consumed by the
2//! tatara reconciler + typed projections for their `(apiVersion, kind)`
3//! pair — the substrate primitive that owns the workspace-wide (variant
4//! → wire-form identity) mapping every Flux-facing site would otherwise
5//! restate by hand.
6//!
7//! Pre-lift the `(apiVersion, kind)` pairing was hand-authored across
8//! FIVE production sites in `tatara-reconciler` past the ★★
9//! PRIME-DIRECTIVE ≥ 2 duplication threshold, split across two axes:
10//!
11//! * `Kustomization` — two production sites:
12//!     * `tatara-reconciler::boundary::evaluate` — the
13//!       `ConditionKind::KustomizationHealthy` arm's
14//!       `evaluate_flux_ready(..., "kustomize.toolkit.fluxcd.io/v1",
15//!       "Kustomization")` call.
16//!     * `tatara-reconciler::render::render_flux` — the emitted
17//!       `json!({"apiVersion": "kustomize.toolkit.fluxcd.io/v1",
18//!       "kind": "Kustomization", ...})` block.
19//! * `HelmRelease` — two production sites:
20//!     * `tatara-reconciler::boundary::evaluate` — the
21//!       `ConditionKind::HelmReleaseReleased` arm's
22//!       `evaluate_flux_ready(..., "helm.toolkit.fluxcd.io/v2",
23//!       "HelmRelease")` call.
24//!     * `tatara-reconciler::render::render_aplicacao` — the emitted
25//!       `json!({"apiVersion": "helm.toolkit.fluxcd.io/v2",
26//!       "kind": "HelmRelease", ...})` block.
27//! * `OCIRepository` — two production sites (both in
28//!   `tatara-reconciler::render::render_aplicacao`):
29//!     * The emitted `json!({"apiVersion":
30//!       "source.toolkit.fluxcd.io/v1beta2", "kind": "OCIRepository",
31//!       ...})` block for the `oci://` chart-ref branch.
32//!     * The inline `chartRef.kind = "OCIRepository"` slot on the
33//!       sibling HelmRelease spec.
34//!
35//! Every pre-lift site restated the axis-typed `&'static str` slot
36//! byte-identically; a regression that swapped ONE arm's `apiVersion`
37//! from `v1` to `v1beta1` (a Flux API-version bump that reaches only
38//! one of the two sites) or misspelled `Kustomization` at ONE site
39//! would silently mis-route the reconciler's SSA-fetch against the
40//! K8s API server (fetch under one apiVersion + apply under another
41//! is a 404 at wire time). Post-lift every axis binds at ONE closed-
42//! set owner and rustc enforces every consumer reads the pair from
43//! the SAME variant.
44//!
45//! Sibling to the same-shape `IntentKind` closed set on the
46//! `Intent` tagged-union axis (in `crate::intent`): both project a
47//! closed set of variant → wire-form identity slots through named
48//! per-variant methods; `IntentKind::as_str` returns the camelCase
49//! wire key of a serde variant, while `FluxResource::api_version` +
50//! `FluxResource::kind` return the K8s `apiVersion` + `kind` wire
51//! strings of a Flux resource variant. Peer to the sibling
52//! substrate primitives `owner_reference_json` / `api_version()` /
53//! `PROCESS_KIND` in `crate::lib` on the same "K8s wire-form
54//! identity" axis but for the tatara `Process` CRD itself, rather
55//! than for the Flux resources tatara's reconciler emits or
56//! consumes.
57//!
58//! Extension: a fourth Flux resource kind (a `Bucket` for
59//! S3-backed sources, a `Receiver` for webhook-triggered
60//! reconciliation, an `Alert` / `Provider` for notification-
61//! controller integration, an `ImagePolicy` / `ImageRepository`
62//! for image-driven reconciliation, a hypothetical Flux
63//! `GitRepository` variant we don't yet reach for at the wire) lands
64//! as ONE variant + ONE `api_version` arm + ONE `kind` arm + ONE
65//! `ALL` entry, all four exhaustively enforced by rustc's match
66//! coverage. Every downstream Flux-facing consumer inherits the
67//! extension mechanically through the same `FluxResource::X.api_version()`
68//! / `.kind()` dispatch pair.
69//!
70//! Theory grounding: THEORY.md §VI.1 (generation over composition —
71//! the (apiVersion, kind) pairing recurred at five hand-authored sites
72//! past the PRIME-DIRECTIVE ≥ 2 duplication trigger, and is lifted to
73//! ONE closed-set owner per axis here). THEORY.md §II.1 invariant 5
74//! (composition preserves proofs — the two per-axis mappings live at
75//! ONE typed algebra projection apiece; a regression that drifted the
76//! apiVersion at ONE consumer would fail-loudly at this module's byte-
77//! shape pins rather than as silent SSA-fetch-vs-emit skew at the
78//! reconciler wire).
79
80/// Closed set of FluxCD resource kinds this repo's reconciler emits or
81/// consumes at the wire. The three variants partition the current
82/// Flux-facing surface:
83///
84/// * `Kustomization` — `kustomize-controller` reconciles a set of
85///   K8s manifests from a source (`GitRepository` / `OCIRepository`).
86///   Emitted by [`crate::intent::FluxIntent`]; consumed by
87///   `ConditionKind::KustomizationHealthy`.
88/// * `HelmRelease` — `helm-controller` installs / upgrades a Helm
89///   chart from a source. Emitted by [`crate::intent::AplicacaoIntent`];
90///   consumed by `ConditionKind::HelmReleaseReleased`.
91/// * `OCIRepository` — `source-controller` pulls a chart from an OCI
92///   registry. Emitted by [`crate::intent::AplicacaoIntent`] for
93///   `oci://` chart refs, referenced by the sibling HelmRelease's
94///   `chartRef` slot.
95///
96/// Every variant carries a stable `(api_version, kind)` pair through
97/// its typed projections; both projections are `const fn` so the
98/// pair reduces to a `&'static str` slot at every callsite with no
99/// runtime overhead.
100#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
101pub enum FluxResource {
102    Kustomization,
103    HelmRelease,
104    OCIRepository,
105}
106
107impl FluxResource {
108    /// The closed set of Flux resource variants this substrate binds.
109    /// Enumerable so a sweep-test (or a future coherence check) can
110    /// walk every variant without a hand-maintained list on the caller
111    /// side.
112    pub const ALL: [Self; 3] = [Self::Kustomization, Self::HelmRelease, Self::OCIRepository];
113
114    /// K8s wire-form `apiVersion` string for this Flux resource
115    /// variant. Matches the canonical group+version pair the Flux
116    /// controllers expose today:
117    ///
118    /// * `Kustomization` → `kustomize.toolkit.fluxcd.io/v1`
119    /// * `HelmRelease`  → `helm.toolkit.fluxcd.io/v2`
120    /// * `OCIRepository` → `source.toolkit.fluxcd.io/v1beta2`
121    ///
122    /// A future Flux upgrade that bumps any group's version lands at
123    /// ONE arm here; every downstream emit or fetch site inherits the
124    /// upgrade mechanically through the same `.api_version()` call.
125    pub const fn api_version(self) -> &'static str {
126        match self {
127            Self::Kustomization => "kustomize.toolkit.fluxcd.io/v1",
128            Self::HelmRelease => "helm.toolkit.fluxcd.io/v2",
129            Self::OCIRepository => "source.toolkit.fluxcd.io/v1beta2",
130        }
131    }
132
133    /// K8s wire-form `kind` string for this Flux resource variant —
134    /// the PascalCase identifier the K8s API server matches against
135    /// the resource's `kind:` slot.
136    ///
137    /// A regression that misspelled any of the three PascalCase
138    /// identifiers would silently mis-route SSA-fetch against SSA-
139    /// apply (a `HelmRelese` emit against a `HelmRelease` fetch is
140    /// a 404 at wire time); post-lift a rename lands at ONE arm
141    /// here rather than at every emit or fetch site.
142    pub const fn kind(self) -> &'static str {
143        match self {
144            Self::Kustomization => "Kustomization",
145            Self::HelmRelease => "HelmRelease",
146            Self::OCIRepository => "OCIRepository",
147        }
148    }
149
150    /// Project this variant onto the typed
151    /// [`crate::k8s_wire_identity::K8sWireIdentity`] pair — the
152    /// substrate primitive that owns the workspace-wide
153    /// `(apiVersion, kind)` pairing every emit or fetch site
154    /// composes against. Pre-lift the reconciler's three
155    /// `render_flux` / `render_aplicacao` emit sites hand-authored
156    /// the pair as two adjacent `json!` slots (`"apiVersion":
157    /// FluxResource::X.api_version(), "kind": FluxResource::X.kind()`)
158    /// mentioning the same variant twice; post-lift the emit site
159    /// names the variant ONCE via
160    /// `.wire_identity().resource_json(json!({...}))` and the pair
161    /// binds structurally at the closed-set owner.
162    ///
163    /// `const fn` so a caller can bind the pair into a `const` slot
164    /// — a future coherence sweep or a compile-time cache reads the
165    /// same pair with zero runtime overhead.
166    pub const fn wire_identity(self) -> crate::k8s_wire_identity::K8sWireIdentity {
167        crate::k8s_wire_identity::K8sWireIdentity::new(self.api_version(), self.kind())
168    }
169}
170
171#[cfg(test)]
172mod tests {
173    use super::*;
174
175    #[test]
176    fn all_enumerates_every_variant_exactly_once() {
177        // A future 4th variant added without an `ALL` entry (or a
178        // duplicate entry that skewed the sweep) surfaces here.
179        assert_eq!(FluxResource::ALL.len(), 3);
180        // Every variant appears exactly once — no duplicates, no gaps.
181        let mut seen = std::collections::HashSet::new();
182        for v in FluxResource::ALL {
183            assert!(seen.insert(v), "duplicate variant in ALL: {v:?}");
184        }
185    }
186
187    #[test]
188    fn kustomization_api_version_is_kustomize_toolkit_v1() {
189        // Byte-identity pin: the exact wire-form string every pre-lift
190        // callsite hand-authored (both `boundary::evaluate` and
191        // `render::render_flux`). A drift that renamed the group or
192        // bumped the version at ONE site would silently mis-route
193        // the fetch-vs-apply pairing.
194        assert_eq!(
195            FluxResource::Kustomization.api_version(),
196            "kustomize.toolkit.fluxcd.io/v1"
197        );
198    }
199
200    #[test]
201    fn helm_release_api_version_is_helm_toolkit_v2() {
202        assert_eq!(
203            FluxResource::HelmRelease.api_version(),
204            "helm.toolkit.fluxcd.io/v2"
205        );
206    }
207
208    #[test]
209    fn oci_repository_api_version_is_source_toolkit_v1beta2() {
210        assert_eq!(
211            FluxResource::OCIRepository.api_version(),
212            "source.toolkit.fluxcd.io/v1beta2"
213        );
214    }
215
216    #[test]
217    fn kustomization_kind_is_pascalcase_kustomization() {
218        assert_eq!(FluxResource::Kustomization.kind(), "Kustomization");
219    }
220
221    #[test]
222    fn helm_release_kind_is_pascalcase_helm_release() {
223        assert_eq!(FluxResource::HelmRelease.kind(), "HelmRelease");
224    }
225
226    #[test]
227    fn oci_repository_kind_is_pascalcase_oci_repository() {
228        assert_eq!(FluxResource::OCIRepository.kind(), "OCIRepository");
229    }
230
231    #[test]
232    fn every_variants_api_version_and_kind_are_distinct_across_the_closed_set() {
233        // Cross-variant coherence pin: no two variants may share an
234        // `api_version` or a `kind`. A future extension that added a
235        // variant duplicating an existing wire-form pair (e.g. a
236        // `HelmChart` variant that reused the HelmRelease apiVersion
237        // in a copy-paste) would surface here.
238        let mut api_versions = std::collections::HashSet::new();
239        let mut kinds = std::collections::HashSet::new();
240        for v in FluxResource::ALL {
241            assert!(
242                api_versions.insert(v.api_version()),
243                "duplicate api_version at {v:?}: {}",
244                v.api_version()
245            );
246            assert!(
247                kinds.insert(v.kind()),
248                "duplicate kind at {v:?}: {}",
249                v.kind()
250            );
251        }
252    }
253
254    #[test]
255    fn api_version_and_kind_are_const_fn_reachable() {
256        // Compile-time reachability pin: every variant's projections
257        // are `const fn`, so a caller can bind them into a `const`
258        // slot. A regression that dropped the `const` qualifier
259        // would fail-loudly here rather than as a wrong-slot runtime
260        // dispatch at every callsite.
261        const K_AV: &str = FluxResource::Kustomization.api_version();
262        const K_K: &str = FluxResource::Kustomization.kind();
263        const H_AV: &str = FluxResource::HelmRelease.api_version();
264        const H_K: &str = FluxResource::HelmRelease.kind();
265        const O_AV: &str = FluxResource::OCIRepository.api_version();
266        const O_K: &str = FluxResource::OCIRepository.kind();
267        assert_eq!(K_AV, "kustomize.toolkit.fluxcd.io/v1");
268        assert_eq!(K_K, "Kustomization");
269        assert_eq!(H_AV, "helm.toolkit.fluxcd.io/v2");
270        assert_eq!(H_K, "HelmRelease");
271        assert_eq!(O_AV, "source.toolkit.fluxcd.io/v1beta2");
272        assert_eq!(O_K, "OCIRepository");
273    }
274
275    #[test]
276    fn wire_identity_pairs_api_version_and_kind_per_variant() {
277        // Projection pin: every variant's `wire_identity()` binds the
278        // SAME closed-set variant's `api_version` and `kind` into a
279        // typed pair. A regression that projected the pair from two
280        // different variants (a copy-paste that referenced
281        // `Kustomization.api_version()` alongside `HelmRelease.kind()`
282        // at the projection body) would surface here rather than as
283        // a silent wire-form skew at every reconciler emit site.
284        for v in FluxResource::ALL {
285            let id = v.wire_identity();
286            assert_eq!(id.api_version, v.api_version());
287            assert_eq!(id.kind, v.kind());
288        }
289    }
290
291    #[test]
292    fn wire_identity_is_const_reachable() {
293        // Compile-time reachability pin: `wire_identity` is `const
294        // fn` so a caller can bind the pair into a `const` slot at
295        // compile time. A regression that dropped the `const`
296        // qualifier would fail-loudly here rather than as a runtime
297        // dispatch surfacing at every projection site.
298        const K: crate::k8s_wire_identity::K8sWireIdentity =
299            FluxResource::Kustomization.wire_identity();
300        const H: crate::k8s_wire_identity::K8sWireIdentity =
301            FluxResource::HelmRelease.wire_identity();
302        const O: crate::k8s_wire_identity::K8sWireIdentity =
303            FluxResource::OCIRepository.wire_identity();
304        assert_eq!(K.api_version, "kustomize.toolkit.fluxcd.io/v1");
305        assert_eq!(K.kind, "Kustomization");
306        assert_eq!(H.api_version, "helm.toolkit.fluxcd.io/v2");
307        assert_eq!(H.kind, "HelmRelease");
308        assert_eq!(O.api_version, "source.toolkit.fluxcd.io/v1beta2");
309        assert_eq!(O.kind, "OCIRepository");
310    }
311
312    #[test]
313    fn projections_are_pure_functions_of_the_variant() {
314        // Purity pin: calling the projections repeatedly on the same
315        // variant returns byte-identical `&'static str`s. Guards against
316        // an implementation that lazily materialized an interned key
317        // per-call and hashed against runtime state.
318        for v in FluxResource::ALL {
319            assert_eq!(v.api_version(), v.api_version());
320            assert_eq!(v.kind(), v.kind());
321        }
322    }
323}