helm_schema_cli/cli/k8s_args.rs
1use std::path::PathBuf;
2
3use clap::Args;
4
5/// `--k8s-version-fallback` accepts either `auto` or a window size `<n>`.
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub enum K8sVersionFallback {
8 /// Auto-extend the single explicit `--k8s-version` with a default
9 /// sliding window of older minors below it.
10 Auto,
11 /// Explicit window size. `0` is equivalent to no fallback.
12 Window(u32),
13}
14
15impl std::str::FromStr for K8sVersionFallback {
16 type Err = String;
17
18 fn from_str(s: &str) -> Result<Self, Self::Err> {
19 if s.eq_ignore_ascii_case("auto") {
20 Ok(K8sVersionFallback::Auto)
21 } else {
22 s.parse::<u32>()
23 .map(K8sVersionFallback::Window)
24 .map_err(|err| format!("expected `auto` or a non-negative integer: {err}"))
25 }
26 }
27}
28
29/// Kubernetes schema versions, mirrors, cache, and network options.
30#[derive(Args, Debug, Clone)]
31pub struct K8sArgs {
32 /// Kubernetes minor version directory(s) to consult, in
33 /// user-supplied priority order. The first value is the primary;
34 /// any further values are explicit fallbacks.
35 #[arg(long = "k8s-version", default_values_t = vec![String::from("v1.35.0")])]
36 pub k8s_version: Vec<String>,
37
38 /// Auto-extend the (single explicit) `--k8s-version` with older
39 /// minors. `auto` uses the default window; `<n>` selects an
40 /// explicit window size.
41 #[arg(long = "k8s-version-fallback", conflicts_with = "strict_k8s_version")]
42 pub k8s_version_fallback: Option<K8sVersionFallback>,
43
44 /// Additional upstream K8s schema mirror URL. Repeatable. Per-source
45 /// cache namespacing keeps mirror entries from masking the default.
46 #[arg(long = "k8s-schema-mirror")]
47 pub k8s_schema_mirror: Vec<String>,
48
49 /// Managed cache root for K8s schemas. Subject to the cache
50 /// invalidation contract.
51 #[arg(long = "k8s-schema-cache-dir")]
52 pub k8s_schema_cache_dir: Option<PathBuf>,
53
54 /// Bypass K8s schema cache reads and re-check upstream directly.
55 ///
56 /// Successful responses and authoritative 404s still refresh cache
57 /// state, so this can repair stale local entries.
58 #[arg(long = "no-cache")]
59 pub no_cache: bool,
60
61 /// Suppress auto-fallback version semantics. Conflicts only with
62 /// `--k8s-version-fallback`; orthogonal to `--k8s-schema-mirror`.
63 #[arg(long = "strict-k8s-version")]
64 pub strict_k8s_version: bool,
65
66 /// Force offline. Equivalent to setting `HELM_SCHEMA_ALLOW_NET=0`.
67 #[arg(long)]
68 pub offline: bool,
69
70 /// Skip K8s upstream schemas entirely.
71 #[arg(long = "no-k8s-schemas")]
72 pub no_k8s_schemas: bool,
73}
74
75impl K8sArgs {
76 /// Resolved fallback window after applying strict mode + sanity
77 /// checks. Returns `None` when no auto-fallback should apply (either
78 /// because strict is set, the flag is absent, or `Window(0)`).
79 ///
80 /// # Errors
81 ///
82 /// Returns an error when automatic fallback is combined with more than
83 /// one explicitly configured Kubernetes version.
84 pub fn resolved_fallback_window(&self) -> Result<Option<u32>, String> {
85 if self.strict_k8s_version {
86 return Ok(None);
87 }
88 match &self.k8s_version_fallback {
89 None | Some(K8sVersionFallback::Window(0)) => Ok(None),
90 Some(K8sVersionFallback::Window(n)) => Ok(Some(*n)),
91 Some(K8sVersionFallback::Auto) => {
92 if self.k8s_version.len() != 1 {
93 return Err(
94 "--k8s-version-fallback=auto requires exactly one --k8s-version"
95 .to_string(),
96 );
97 }
98 Ok(Some(DEFAULT_AUTO_WINDOW))
99 }
100 }
101 }
102}
103
104/// Default auto-fallback reach when `--k8s-version-fallback=auto` is set.
105///
106/// Sized to cover the realistic K8s deprecation horizon: charts in the
107/// wild still ship `policy/v1beta1` (PSP / PDB) and
108/// `networking.k8s.io/v1beta1` (Ingress) — both removed in v1.25 — so
109/// a primary of the current default (`v1.35.0`) must be able to fall
110/// back at least to `v1.24.0` to find a schema. 15 leaves headroom for
111/// the next few K8s releases without churning this constant.
112pub const DEFAULT_AUTO_WINDOW: u32 = 15;