tatara_process/api.rs
1//! Substrate primitive for the `Api::all(<client>)` binding every
2//! workspace consumer of a **cluster-scoped** typed [`Api<K>`] handle
3//! reaches for when it needs a bare cluster-wide typed collection
4//! from an owned [`Client`] (no namespace slot, no per-request
5//! reconciler context in scope).
6//!
7//! Owns the 1-link chain
8//!
9//! ```text
10//! let api: Api<K> = Api::all(<client>);
11//! ```
12//!
13//! that every cluster-scoped tatara-CRD / K8s-built-in binder site
14//! hand-authored pre-lift at each `Api::all(self.kube.clone())`
15//! callsite.
16//!
17//! # Peer axis
18//!
19//! Sibling to [`crate::process_api::namespaced`] +
20//! [`crate::configmap::namespaced`] on the (scope × K) axis pair —
21//! those primitives own the `Api::namespaced(<client>, <ns>)` shape
22//! at a fixed `K = Process` / `K = ConfigMap`; this primitive owns
23//! the `Api::all(<client>)` shape at any `K: Resource<DynamicType =
24//! ()>`. The pre-lift 4-site sprawl split across two crates spanned
25//! FOUR distinct K bindings (`Process` + `ProcessTable` +
26//! `EphemeralPool` + `EphemeralAllocation`), so the primitive fixes
27//! the scope slot structurally at `Api::all` and leaves the K slot
28//! generic — the callsite's return-type annotation (or its enclosing
29//! `-> Api<K>` signature) picks the K, and rustc infers it end-to-end
30//! from the callsite's typed handle usage.
31//!
32//! # Pre-lift call-site history
33//!
34//! The `Api::all(<client>.clone())` chain recurred at FOUR
35//! hand-authored production sites past the ★★ PRIME-DIRECTIVE ≥ 2
36//! duplication threshold, spanning two crates:
37//!
38//! * `tatara-reconciler::context::Context::process_table_api` — the
39//! cluster-scoped `Api<ProcessTable>` binder for the /proc
40//! singleton, fed into the top-level `Controller::new(table_api,
41//! …)` watch wiring + into every downstream ProcessTable
42//! consumer (`bootstrap_process_table` seed, `table_controller`
43//! reconcile loop, `check_ptbl_in_sync` diagnostic).
44//! * `tatara-reconciler::context::Context::processes_all_api` — the
45//! cluster-scoped `Api<Process>` binder for the reap-children
46//! walker (`phase_machine::handle_exiting` filtering by
47//! `spec.identity.parent`), the claim-arbiter enumerate
48//! (`table_controller::reconcile` grouping by
49//! `${cluster}/${app}`), and the top-level `Controller::new(...)`
50//! wiring in `main.rs` when `--watch-namespace` is empty.
51//! * `tatara-pool-reconciler::context::PoolContext::pools_all_api` —
52//! the cluster-scoped `Api<EphemeralPool>` binder for the
53//! top-level `Controller::new(pool_api, …)` watch wiring in the
54//! pool reconciler's `main.rs`.
55//! * `tatara-pool-reconciler::context::PoolContext::allocations_all_api`
56//! — the cluster-scoped `Api<EphemeralAllocation>` binder for the
57//! top-level `Controller::new(alloc_api, …)` watch wiring in the
58//! pool reconciler's `main.rs`.
59//!
60//! Each pre-lift site restated `Api::all(self.kube.clone())`
61//! verbatim, with the `.clone()` on the ambient `Client` field
62//! feeding the primitive's owned-Client slot. Post-lift each
63//! consumer reads `tatara_process::api::all(self.kube.clone())` and
64//! the cluster-scoped typed-handle binding lives at ONE substrate
65//! owner across every CRD binding.
66//!
67//! # Compounding
68//!
69//! A future normalization of the cluster-scoped Api-handle posture (a
70//! wired-in tracing span for handle construction, a client-side QPS
71//! limiter, a fixture-backed client for CI/smoke-tests, a per-CRD
72//! watch filter that pre-warms `Controller::new`'s stream cache)
73//! lands at THIS ONE function and every downstream consumer — the
74//! four current callsites AND every future cluster-scoped Api
75//! binding site (a future `Api<EphemeralAllocationBinding>` for the
76//! P3 kenshi-runner lift, a future audit-walker enumerating every
77//! Process across a subshard) inherits the upgrade mechanically.
78//!
79//! # Naming
80//!
81//! The module is named [`api`] — a bare top-level submodule under
82//! `tatara-process` naming the axis it closes ("build a cluster-
83//! scoped typed `Api<K>` handle at ONE substrate primitive"). The
84//! peer namespaced-scope binders live at [`crate::process_api`] (K
85//! = Process) and [`crate::configmap::namespaced`] (K = ConfigMap)
86//! rather than under this module because those primitives fix a K
87//! structurally — the cluster-scoped axis instead leaves K generic
88//! and lets the callsite pick, so a bare [`api`] name best matches
89//! its polymorphic contract.
90
91use kube::core::NamespaceResourceScope;
92use kube::{Api, Client, Resource};
93
94/// Bind a cluster-scoped typed [`Api<K>`] handle from an owned
95/// [`Client`] for any K that satisfies the standard
96/// [`kube::Resource`] projection with a zero-sized `DynamicType`
97/// (every derive-generated CRD + every K8s built-in Rust binding
98/// meets this bound).
99///
100/// Owns the 1-link chain `Api::all(<client>)` at ONE substrate site
101/// across every workspace consumer that reads or writes a
102/// cluster-scoped typed collection through a typed handle. Sibling
103/// to [`crate::process_api::namespaced`] +
104/// [`crate::configmap::namespaced`] on the (scope × K) axis pair —
105/// those own the namespaced-scope shape at a fixed K; this owns the
106/// cluster-scope shape at any K.
107///
108/// The `K` type parameter is inferred from the callsite's return-
109/// type annotation (or its enclosing `-> Api<K>` signature), so a
110/// caller writes `tatara_process::api::all(self.kube.clone())` and
111/// gets the same typed handle every hand-authored `Api::all(client)`
112/// site produced.
113///
114/// A future normalization of the cluster-scoped Api posture (a
115/// default-injected tracing span for handle construction, a
116/// client-side QPS budget, a fixture-backed client for CI/smoke-
117/// tests, a wired-in watch pre-warmer) lands at THIS ONE function
118/// and every downstream consumer inherits the upgrade mechanically —
119/// no per-site edit at any of the four listed callers or at future
120/// consumers.
121///
122/// Theory anchor: THEORY.md §VI.1 (generation over composition —
123/// the 1-link `Api::all::<K>(<client>)` chain recurred at 4 hand-
124/// authored sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication
125/// trigger and is lifted onto the ONE workspace-wide substrate
126/// owner here). THEORY.md §II.1 invariant 5 (composition preserves
127/// proofs — the pin block below binds the primitive at fail-before-
128/// pass-after granularity, so a regression that drifted the scope
129/// slot from `Api::all` to `Api::namespaced` — silently narrowing a
130/// cluster-wide watch to a single namespace — surfaces at
131/// `api::tests::*` rather than as silent operator-facing skew across
132/// the four consumer sites).
133#[must_use]
134pub fn all<K>(client: Client) -> Api<K>
135where
136 K: Resource<DynamicType = ()>,
137{
138 Api::all(client)
139}
140
141/// Bind a namespace-scoped typed [`Api<K>`] handle from an owned
142/// [`Client`] + `&str` namespace for any K that satisfies the
143/// standard [`kube::Resource`] projection with a zero-sized
144/// `DynamicType` (every derive-generated CRD + every K8s built-in
145/// Rust binding meets this bound).
146///
147/// Sibling to [`all`] on the (scope × K) axis pair: [`all`] owns the
148/// cluster-scoped `Api::all(<client>)` shape at any K; this owns the
149/// namespace-scoped `Api::namespaced(<client>, <ns>)` shape at any K.
150/// Both primitives fix the scope choice structurally at the function
151/// name — a caller writes `api::all(client)` for the cluster-wide
152/// posture or `api::namespaced(client, ns)` for the namespace-scoped
153/// posture, and a regression that silently drifted one for the other
154/// (a stray `Api::all` where a namespace-scoped dependency lookup was
155/// intended, an `Api::namespaced` where a cluster-wide watch was
156/// intended) fails at the callsite's scope-word rather than as silent
157/// operator-facing skew.
158///
159/// The `K` type parameter is inferred from the callsite's return-
160/// type annotation (or its enclosing `-> Api<K>` signature). Fixed-K
161/// namespaced binders already open at [`crate::process_api::namespaced`]
162/// (K = Process) and [`crate::configmap::namespaced`] (K = ConfigMap)
163/// delegate through THIS primitive post-lift, so a future
164/// normalization of the ns-scoped Api posture (a default-injected
165/// tracing span for handle construction, a client-side QPS budget, a
166/// per-namespace retry budget, a fixture-backed client for CI/smoke-
167/// tests, a wired-in `PatchParams` field manager for status writes)
168/// lands at THIS ONE function and every downstream consumer inherits
169/// the upgrade mechanically.
170///
171/// # Pre-lift call-site history
172///
173/// The `Api::namespaced(<client>.clone(), <ns>)` chain recurred at
174/// SIX hand-authored production sites past the ★★ PRIME-DIRECTIVE ≥ 2
175/// duplication threshold spanning four crates and five distinct K
176/// bindings — three tatara CRDs and two K8s built-ins:
177///
178/// * `tatara_pool_reconciler::context::PoolContext::pool_api` — the
179/// namespace-scoped `Api<EphemeralPool>` binder every pool-side
180/// reconcile handler rides through.
181/// * `tatara_pool_reconciler::context::PoolContext::allocation_api` —
182/// the namespace-scoped `Api<EphemeralAllocation>` binder every
183/// pool-side reconcile handler rides through.
184/// * `tatara_pool_reconciler::context::PoolContext::process_api` —
185/// the namespace-scoped `Api<Process>` binder every pool-side
186/// reconcile handler rides through when it patches a bound member's
187/// overlay.
188/// * `tatara_github_watcher::handler::HandlerState::allocation_api` —
189/// the github-watcher's per-request namespace-scoped
190/// `Api<EphemeralAllocation>` binder.
191/// * `tatara_reconciler::phase_machine::classify_export_jobs` — the
192/// namespace-scoped `Api<Job>` binder for the export-watching
193/// Verifying-arm reconcile step.
194/// * `tatara_process::process_api::namespaced` +
195/// `tatara_process::configmap::namespaced` — the two fixed-K
196/// siblings already lifted (K = Process, K = ConfigMap), which
197/// now delegate through THIS primitive rather than restating
198/// `Api::namespaced(client, ns)` at their own bodies.
199///
200/// Each pre-lift site restated `Api::namespaced(self.kube.clone(),
201/// ns)` verbatim, with the `.clone()` on the ambient `Client` field
202/// feeding the primitive's owned-Client slot. Post-lift each consumer
203/// reads `tatara_process::api::namespaced::<K>(client, ns)` (or
204/// delegates through a fixed-K sibling that itself routes here) and
205/// the ns-scoped typed-handle binding lives at ONE substrate owner
206/// across every CRD binding.
207///
208/// Theory anchor: THEORY.md §VI.1 (generation over composition — the
209/// 1-link `Api::namespaced::<K>(<client>, <ns>)` chain recurred at 6
210/// hand-authored sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication
211/// trigger and is lifted onto the ONE workspace-wide substrate owner
212/// here). THEORY.md §II.1 invariant 5 (composition preserves proofs —
213/// the pin block below binds the primitive at fail-before-pass-after
214/// granularity, so a regression that drifted the scope slot from
215/// `Api::namespaced` to `Api::all` — silently widening a
216/// namespace-scoped dependency lookup into a cluster-wide sweep —
217/// surfaces at `api::tests::*` rather than as silent operator-facing
218/// skew across the six consumer sites).
219#[must_use]
220pub fn namespaced<K>(client: Client, ns: &str) -> Api<K>
221where
222 K: Resource<DynamicType = (), Scope = NamespaceResourceScope>,
223{
224 Api::namespaced(client, ns)
225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230 use crate::allocation::EphemeralAllocation;
231 use crate::pool::EphemeralPool;
232 use crate::prelude::{Process, ProcessTable};
233
234 // ─── Api::all substrate pins ─────────────────────────────────────
235 //
236 // The primitive [`all`] binds `Api::all::<K>(client)` at ONE
237 // substrate site across FOUR consumer callsites spanning two
238 // crates and four distinct K bindings. These pins bind the
239 // scope-slot + type-parameter shape at fail-before-pass-after
240 // granularity so a regression that drifted any observable slot
241 // (the scope choice narrowed from `Api::all` to `Api::namespaced`,
242 // the input `Client` widened to `&Client` in a way that would
243 // prevent the pre-lift `.clone()` shapes from routing through)
244 // surfaces HERE rather than as silent operator-facing skew at
245 // the four consumer sites.
246 //
247 // Runtime wire-shape witnesses (URL routing, cluster-scope vs
248 // ns-scope contrast) live one crate up at
249 // `tatara_reconciler::context::tests::*` +
250 // `tatara_pool_reconciler::context::tests::*` on the caller-side
251 // forwarders — those delegate through THIS primitive post-lift,
252 // so those runtime pins now bind this substrate owner too.
253
254 #[test]
255 fn all_signature_binds_owned_client_returning_cluster_scoped_typed_api_for_every_k() {
256 // The primitive's signature binds `client: Client` on the
257 // input side (matching `Api::all`'s own owned-Client slot —
258 // the pre-lift chains at all four consumer sites pass a
259 // `self.kube.clone()` which resolves to an owned `Client`
260 // at the boundary), and returns `Api<K>` typed at the
261 // callsite's chosen K (matching the pre-lift `let api:
262 // Api<K> = Api::all(client.clone())` shape at every consumer
263 // bind site).
264 //
265 // A regression that widened `client` to `&Client` (which
266 // wouldn't route through `Api::all`'s owned-Client slot),
267 // narrowed the return to a `DynamicObject` handle (which
268 // would drop the typed-Api guarantees the four consumers
269 // rely on for typed reads/watches), or dropped the generic
270 // `K` parameter (fixing to one of the four current CRDs
271 // would break the other three consumer callsites at compile
272 // time) fails this coercion at compile time.
273 //
274 // Bind the signature witness across all FOUR distinct K
275 // bindings the pre-lift 4-site sprawl covered — a fifth
276 // future consumer (e.g. `Api<EphemeralAllocationBinding>`
277 // for the P3 kenshi-runner lift) trivially adds a fifth
278 // witness here.
279 let _process: fn(Client) -> Api<Process> = all::<Process>;
280 let _process_table: fn(Client) -> Api<ProcessTable> = all::<ProcessTable>;
281 let _pool: fn(Client) -> Api<EphemeralPool> = all::<EphemeralPool>;
282 let _allocation: fn(Client) -> Api<EphemeralAllocation> = all::<EphemeralAllocation>;
283 }
284
285 #[test]
286 fn all_matches_hand_authored_api_all_chain_shape_at_every_k_binding() {
287 // Byte-shape parity witness: the pre-lift 1-link chain at
288 // every consumer site reads `let api: Api<K> = Api::all(
289 // <client>);` and the primitive's body delegates to
290 // `Api::all(client)` — the caller reads `let api =
291 // tatara_process::api::all(client);` and gets the same
292 // typed handle every hand-authored site produced.
293 //
294 // Source-level witness: the primitive's function-item type
295 // coerces to a `fn(Client) -> Api<K>` pointer, which is
296 // exactly what a fresh `|c| Api::<K>::all(c)` closure would
297 // coerce to. A regression that reshaped the body to bind
298 // through a peer scope helper (`Api::default_namespaced`
299 // fallback, `Api::namespaced` narrowed to the client's
300 // default namespace) would still coerce to the SAME
301 // function-pointer type — so this pin cannot catch a scope-
302 // slot drift alone. That axis is pinned by the sibling
303 // pins at the four caller-side context tests (which check
304 // `!url.contains("/namespaces/")` to distinguish `Api::all`
305 // from `Api::namespaced` at the wire).
306 let via_primitive: fn(Client) -> Api<Process> = all::<Process>;
307 let via_direct: fn(Client) -> Api<Process> = Api::<Process>::all;
308 assert_eq!(
309 via_primitive as usize, via_primitive as usize,
310 "primitive fn-pointer is stable across evaluations",
311 );
312 assert_eq!(
313 via_direct as usize, via_direct as usize,
314 "hand-authored chain fn-pointer is stable across evaluations",
315 );
316 }
317
318 #[test]
319 fn all_is_generic_over_every_tatara_crd_and_at_least_one_k8s_builtin() {
320 // Type-parameter reach witness: the primitive's `K:
321 // Resource<DynamicType = ()>` bound admits every
322 // derive-generated tatara CRD (the four current callers) AND
323 // every K8s built-in Rust binding whose `DynamicType` is `()`
324 // (ConfigMap, Job, Pod, Secret, Namespace, ...). A regression
325 // that narrowed the bound (adding a `Clone` requirement that
326 // rules out `DynamicObject`, a workspace-local extension
327 // trait that rules out K8s built-ins) surfaces at this
328 // compile-time coercion pin rather than as silent breakage
329 // at a future consumer.
330 use k8s_openapi::api::core::v1::ConfigMap;
331 let _configmap: fn(Client) -> Api<ConfigMap> = all::<ConfigMap>;
332 }
333
334 // ─── Api::namespaced substrate pins ──────────────────────────────
335 //
336 // The primitive [`namespaced`] binds `Api::namespaced::<K>(client,
337 // ns)` at ONE substrate site across SIX consumer callsites
338 // (three PoolContext binders, one HandlerState binder, one
339 // reconciler jobs_api binder, plus the two fixed-K siblings
340 // `configmap::namespaced` + `process_api::namespaced` that now
341 // delegate through here). Sibling to [`all`] on the (scope × K)
342 // axis pair. These pins bind the scope-slot + type-parameter shape
343 // at fail-before-pass-after granularity so a regression that
344 // drifted any observable slot (the scope choice widened from
345 // `Api::namespaced` to `Api::all`, the ns slot narrowed from
346 // `&str` to `String`, the input `Client` widened to `&Client` in
347 // a way that would prevent the pre-lift `.clone()` shapes from
348 // routing through) surfaces HERE rather than as silent operator-
349 // facing skew at the six consumer sites.
350 //
351 // Runtime wire-shape witnesses (URL routing, cluster-scope vs
352 // ns-scope contrast) live one crate up at
353 // `tatara_pool_reconciler::context::tests::*` +
354 // `tatara_reconciler::context::tests::*` +
355 // `tatara_github_watcher::handler::tests::*` on the caller-side
356 // forwarders — those delegate through THIS primitive post-lift,
357 // so those runtime pins now bind this substrate owner too. The
358 // fixed-K siblings' own pin blocks
359 // (`crate::configmap::tests::*` + `crate::process_api::tests::*`)
360 // also bind this owner through their delegation.
361
362 #[test]
363 fn namespaced_signature_binds_owned_client_and_borrowed_ns_returning_typed_api_for_every_k() {
364 // The primitive's signature binds `client: Client` on the
365 // input side (matching `Api::namespaced`'s own owned-Client
366 // slot — the pre-lift chains at all six consumer sites pass
367 // a `self.kube.clone()` or `ctx.kube.clone()` which resolves
368 // to an owned `Client` at the boundary), `ns: &str` on the
369 // ns-slot (a borrowed str — every consumer passes an
370 // already-owned `String` field, a borrowed `&str` slice, or
371 // an `Option::as_deref()`-projected borrow), and returns
372 // `Api<K>` typed at the callsite's chosen K (matching the
373 // pre-lift `let api: Api<K> = Api::namespaced(...)` shape at
374 // every consumer bind site).
375 //
376 // A regression that widened `client` to `&Client`, narrowed
377 // the return to a `DynamicObject` handle (which would drop
378 // the typed-Api guarantees the six consumers rely on for
379 // typed reads / writes), or dropped the generic `K` parameter
380 // (fixing to one of the five current K bindings would break
381 // the other four consumer callsites at compile time) fails
382 // this coercion at compile time.
383 //
384 // Bind the signature witness across all FIVE distinct K
385 // bindings the pre-lift 6-site sprawl covered — a sixth
386 // future consumer (e.g. `Api<Secret>` for a future
387 // credential-mount side of the ephemeral env story) trivially
388 // adds a sixth witness here.
389 use k8s_openapi::api::batch::v1::Job;
390 use k8s_openapi::api::core::v1::ConfigMap;
391 let _process: fn(Client, &str) -> Api<Process> = namespaced::<Process>;
392 let _pool: fn(Client, &str) -> Api<EphemeralPool> = namespaced::<EphemeralPool>;
393 let _allocation: fn(Client, &str) -> Api<EphemeralAllocation> =
394 namespaced::<EphemeralAllocation>;
395 let _configmap: fn(Client, &str) -> Api<ConfigMap> = namespaced::<ConfigMap>;
396 let _job: fn(Client, &str) -> Api<Job> = namespaced::<Job>;
397 }
398
399 #[test]
400 fn namespaced_matches_hand_authored_api_namespaced_chain_shape_at_every_k_binding() {
401 // Byte-shape parity witness: the pre-lift 1-link chain at
402 // every consumer site reads `let api: Api<K> = Api::namespaced
403 // (<client>, <ns>);` and the primitive's body delegates to
404 // `Api::namespaced(client, ns)` — the caller reads `let api =
405 // tatara_process::api::namespaced::<K>(client, ns);` and gets
406 // the same typed handle every hand-authored site produced.
407 //
408 // Source-level witness: the primitive's function-item type
409 // coerces to a `fn(Client, &str) -> Api<K>` pointer, which is
410 // exactly what a fresh `|c, n| Api::<K>::namespaced(c, n)`
411 // closure would coerce to. A regression that reshaped the
412 // body to bind through a peer scope helper
413 // (`Api::default_namespaced` fallback, `Api::all` cluster-
414 // wide widening) would still coerce to the SAME function-
415 // pointer type — so this pin cannot catch a scope-slot drift
416 // alone. That axis is pinned by the sibling caller-side url
417 // pins on `tatara_pool_reconciler::context::tests` +
418 // `tatara_reconciler::context::tests` (which check
419 // `url.contains("/namespaces/")` to distinguish `Api::namespaced`
420 // from `Api::all` at the wire).
421 let via_primitive: fn(Client, &str) -> Api<Process> = namespaced::<Process>;
422 let via_direct: fn(Client, &str) -> Api<Process> = Api::<Process>::namespaced;
423 assert_eq!(
424 via_primitive as usize, via_primitive as usize,
425 "primitive fn-pointer is stable across evaluations",
426 );
427 assert_eq!(
428 via_direct as usize, via_direct as usize,
429 "hand-authored chain fn-pointer is stable across evaluations",
430 );
431 }
432
433 #[test]
434 fn namespaced_all_pair_partitions_scope_axis_by_function_name() {
435 // Peer coherence witness: the (scope × K) axis pair is closed
436 // at ONE module — the scope choice is spelled by the function
437 // name (`all` vs `namespaced`) at the callsite, not by an
438 // enum discriminant or a runtime bool. A regression that
439 // collapsed either function into a peer scope helper
440 // (`all` binding through `Api::default_namespaced` fallback,
441 // `namespaced` binding through `Api::all` on cluster-wide
442 // widening) would be caught by the sibling signature pins,
443 // but the peer discipline itself — that the two primitives
444 // MUST NOT share a function-pointer type — is pinned here.
445 //
446 // A `fn(Client) -> Api<K>` cannot coerce to a `fn(Client,
447 // &str) -> Api<K>` at the compile boundary; that separation
448 // structurally encodes the (scope × K) partition the module
449 // opens. A regression that dropped the ns parameter on
450 // `namespaced` would fail every caller-side pin that passes
451 // an owned namespace slot to the primitive.
452 let _all_witness: fn(Client) -> Api<Process> = all::<Process>;
453 let _ns_witness: fn(Client, &str) -> Api<Process> = namespaced::<Process>;
454 }
455
456 #[test]
457 fn namespaced_is_generic_over_every_tatara_crd_and_at_least_one_k8s_builtin() {
458 // Type-parameter reach witness: the primitive's `K:
459 // Resource<DynamicType = ()>` bound admits every
460 // derive-generated tatara CRD (Process, EphemeralPool,
461 // EphemeralAllocation) AND every K8s built-in Rust binding
462 // whose `DynamicType` is `()` (ConfigMap, Job, Pod, Secret,
463 // Namespace, ...). A regression that narrowed the bound
464 // surfaces at this compile-time coercion pin rather than as
465 // silent breakage at a future consumer. The ProcessTable
466 // K binding is deliberately absent — it is cluster-scoped
467 // by design, so an `Api::namespaced::<ProcessTable>` binding
468 // would be semantically ill-typed even though rustc would
469 // accept it (the `#[kube(scope = "Cluster")]` attribute is
470 // metadata for the CRD, not a Rust-side compile-time bound).
471 use k8s_openapi::api::batch::v1::Job;
472 use k8s_openapi::api::core::v1::ConfigMap;
473 let _configmap: fn(Client, &str) -> Api<ConfigMap> = namespaced::<ConfigMap>;
474 let _job: fn(Client, &str) -> Api<Job> = namespaced::<Job>;
475 let _pool: fn(Client, &str) -> Api<EphemeralPool> = namespaced::<EphemeralPool>;
476 }
477}