Skip to main content

greentic_deployer/
defaults.rs

1//! Default capability-slot bindings for the bootstrap `local` Environment (A4).
2//!
3//! Pack-descriptor strings are exposed as `&'static str` constants so callers
4//! that need only the names (CLI output, telemetry tags) can avoid parsing.
5//! [`local_pack_bindings`] returns the five [`EnvPackBinding`]s ready to drop
6//! into [`greentic_deploy_spec::Environment::packs`]; parsing failures propagate
7//! as [`PackDescriptorParseError`] but are unreachable for the compile-time
8//! constants below.
9
10use greentic_deploy_spec::{
11    CapabilitySlot, EnvPackBinding, PackDescriptor, PackDescriptorParseError, PackId,
12};
13
14/// `EnvId` string used by the bootstrap environment.
15pub const LOCAL_ENV_ID: &str = "local";
16
17/// Pack descriptor for the deployer slot on the `local` env.
18pub const LOCAL_DEPLOYER_PACK: &str = "greentic.deployer.local-process@0.1.0";
19/// Pack descriptor for the secrets slot on the `local` env.
20pub const LOCAL_SECRETS_PACK: &str = "greentic.secrets.dev-store@0.1.0";
21/// Version-independent descriptor path of the dev-store secrets backend — the
22/// default binding, delivered into a cluster as the `gtc-dev-secrets` Secret.
23pub const DEV_STORE_SECRETS_PATH: &str = "greentic.secrets.dev-store";
24/// Version-independent descriptor path of the HashiCorp Vault secrets backend
25/// (Phase E). Bound to the `Secrets` slot to make the worker resolve `secret://`
26/// refs from Vault under its pod ServiceAccount identity.
27pub const VAULT_SECRETS_PATH: &str = "greentic.secrets.vault";
28/// Pack descriptor for the Vault secrets backend binding.
29pub const VAULT_SECRETS_PACK: &str = "greentic.secrets.vault@0.1.0";
30/// Pack descriptor for the telemetry slot on the `local` env.
31pub const LOCAL_TELEMETRY_PACK: &str = "greentic.telemetry.stdout@0.1.0";
32/// Pack descriptor for the sessions slot on the `local` env.
33pub const LOCAL_SESSIONS_PACK: &str = "greentic.sessions.in-memory@0.1.0";
34/// Pack descriptor for the state slot on the `local` env.
35pub const LOCAL_STATE_PACK: &str = "greentic.state.in-memory@0.1.0";
36
37/// `(slot, descriptor)` pairs for the five default bindings.
38pub const LOCAL_DEFAULT_BINDINGS: &[(CapabilitySlot, &str)] = &[
39    (CapabilitySlot::Deployer, LOCAL_DEPLOYER_PACK),
40    (CapabilitySlot::Secrets, LOCAL_SECRETS_PACK),
41    (CapabilitySlot::Telemetry, LOCAL_TELEMETRY_PACK),
42    (CapabilitySlot::Sessions, LOCAL_SESSIONS_PACK),
43    (CapabilitySlot::State, LOCAL_STATE_PACK),
44];
45
46/// Builds the default [`EnvPackBinding`] set for the `local` environment.
47///
48/// Each binding starts at `generation = 0` with `pack_ref` mirroring the
49/// descriptor string; the env-pack registry (A9) is responsible for resolving
50/// the descriptor to a concrete handler at runtime.
51pub fn local_pack_bindings() -> Result<Vec<EnvPackBinding>, PackDescriptorParseError> {
52    LOCAL_DEFAULT_BINDINGS
53        .iter()
54        .map(|(slot, descriptor)| {
55            let kind = PackDescriptor::try_new(*descriptor)?;
56            Ok(EnvPackBinding {
57                slot: *slot,
58                kind,
59                pack_ref: PackId::new(*descriptor),
60                answers_ref: None,
61                generation: 0,
62                previous_binding_ref: None,
63            })
64        })
65        .collect()
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn local_pack_bindings_returns_five_bindings_one_per_slot() {
74        let bindings = local_pack_bindings().expect("default descriptors parse");
75        assert_eq!(bindings.len(), 5);
76        let slots: Vec<CapabilitySlot> = bindings.iter().map(|b| b.slot).collect();
77        assert_eq!(
78            slots,
79            vec![
80                CapabilitySlot::Deployer,
81                CapabilitySlot::Secrets,
82                CapabilitySlot::Telemetry,
83                CapabilitySlot::Sessions,
84                CapabilitySlot::State,
85            ]
86        );
87    }
88
89    #[test]
90    fn local_pack_bindings_descriptors_match_constants() {
91        let bindings = local_pack_bindings().expect("default descriptors parse");
92        let kinds: Vec<&str> = bindings.iter().map(|b| b.kind.as_str()).collect();
93        assert_eq!(
94            kinds,
95            vec![
96                LOCAL_DEPLOYER_PACK,
97                LOCAL_SECRETS_PACK,
98                LOCAL_TELEMETRY_PACK,
99                LOCAL_SESSIONS_PACK,
100                LOCAL_STATE_PACK,
101            ]
102        );
103    }
104
105    #[test]
106    fn local_pack_bindings_start_at_generation_zero_with_no_rollback() {
107        let bindings = local_pack_bindings().expect("default descriptors parse");
108        for binding in &bindings {
109            assert_eq!(binding.generation, 0);
110            assert!(binding.answers_ref.is_none());
111            assert!(binding.previous_binding_ref.is_none());
112            assert_eq!(binding.pack_ref.as_str(), binding.kind.as_str());
113        }
114    }
115
116    #[test]
117    fn local_default_bindings_table_covers_every_non_revocation_slot() {
118        let table_slots: std::collections::BTreeSet<CapabilitySlot> =
119            LOCAL_DEFAULT_BINDINGS.iter().map(|(s, _)| *s).collect();
120        let expected: std::collections::BTreeSet<CapabilitySlot> = [
121            CapabilitySlot::Deployer,
122            CapabilitySlot::Secrets,
123            CapabilitySlot::Telemetry,
124            CapabilitySlot::Sessions,
125            CapabilitySlot::State,
126        ]
127        .into_iter()
128        .collect();
129        assert_eq!(table_slots, expected);
130        assert!(!table_slots.contains(&CapabilitySlot::Revocation));
131    }
132}