Skip to main content

kanade_shared/
kv.rs

1//! NATS KV bucket name + key helpers (spec §2.3.2).
2//!
3//! NATS KV bucket names must be domain-safe ASCII (a-z, A-Z, 0-9, _, -),
4//! so the spec's dotted names (`script.current`, `script.status`) are
5//! flattened to underscore form here.
6
7pub const BUCKET_SCRIPT_CURRENT: &str = "script_current";
8pub const BUCKET_SCRIPT_STATUS: &str = "script_status";
9pub const BUCKET_AGENTS_STATE: &str = "agents_state";
10pub const BUCKET_AGENT_CONFIG: &str = "agent_config";
11pub const BUCKET_AGENT_GROUPS: &str = "agent_groups";
12pub const BUCKET_SCHEDULES: &str = "schedules";
13
14/// Object Store bucket holding raw agent binaries (one object per
15/// version, e.g. `0.2.0` → file bytes).
16pub const OBJECT_AGENT_RELEASES: &str = "agent_releases";
17
18/// Key inside [`BUCKET_AGENT_CONFIG`] carrying the broadcast target
19/// version. Agents watch this key and self-update when their running
20/// version drifts.
21pub const KEY_AGENT_TARGET_VERSION: &str = "target_version";
22
23pub const SCRIPT_STATUS_ACTIVE: &str = "ACTIVE";
24pub const SCRIPT_STATUS_REVOKED: &str = "REVOKED";
25
26pub const STREAM_INVENTORY: &str = "INVENTORY";
27pub const STREAM_RESULTS: &str = "RESULTS";
28pub const STREAM_DEPLOY: &str = "DEPLOY";
29pub const STREAM_EVENTS: &str = "EVENTS";
30pub const STREAM_AUDIT: &str = "AUDIT";
31
32#[cfg(test)]
33mod tests {
34    use super::*;
35
36    /// NATS KV bucket names must be domain-safe ASCII (a-z, A-Z, 0-9, _, -).
37    /// Lock the constants down so a future edit doesn't introduce a `.` and
38    /// break create_key_value silently on the broker side.
39    #[test]
40    fn bucket_names_are_domain_safe() {
41        for name in [
42            BUCKET_SCRIPT_CURRENT,
43            BUCKET_SCRIPT_STATUS,
44            BUCKET_AGENTS_STATE,
45            BUCKET_AGENT_CONFIG,
46            BUCKET_AGENT_GROUPS,
47            BUCKET_SCHEDULES,
48            OBJECT_AGENT_RELEASES,
49        ] {
50            assert!(
51                !name.contains('.'),
52                "bucket name {name:?} contains a dot, which NATS KV rejects"
53            );
54            assert!(
55                name.chars()
56                    .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-'),
57                "bucket name {name:?} has non-domain-safe characters"
58            );
59        }
60    }
61
62    #[test]
63    fn stream_names_are_unique() {
64        let names = [
65            STREAM_INVENTORY,
66            STREAM_RESULTS,
67            STREAM_DEPLOY,
68            STREAM_EVENTS,
69            STREAM_AUDIT,
70        ];
71        let mut deduped = names.to_vec();
72        deduped.sort_unstable();
73        deduped.dedup();
74        assert_eq!(
75            deduped.len(),
76            names.len(),
77            "stream constants collide: {names:?}"
78        );
79    }
80
81    #[test]
82    fn script_status_strings() {
83        assert_eq!(SCRIPT_STATUS_ACTIVE, "ACTIVE");
84        assert_eq!(SCRIPT_STATUS_REVOKED, "REVOKED");
85        assert_ne!(SCRIPT_STATUS_ACTIVE, SCRIPT_STATUS_REVOKED);
86    }
87
88    #[test]
89    fn key_agent_target_version_constant() {
90        assert_eq!(KEY_AGENT_TARGET_VERSION, "target_version");
91    }
92}