1pub 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
14pub const BUCKET_JOBS: &str = "jobs";
20
21pub const BUCKET_JOBS_YAML: &str = "jobs_yaml";
33pub const BUCKET_SCHEDULES_YAML: &str = "schedules_yaml";
34
35pub const OBJECT_AGENT_RELEASES: &str = "agent_releases";
38
39pub const KEY_AGENT_TARGET_VERSION: &str = "target_version";
43
44pub const KEY_AGENT_CONFIG_GLOBAL: &str = "global";
52pub const PREFIX_AGENT_CONFIG_GROUPS: &str = "groups.";
53pub const PREFIX_AGENT_CONFIG_PCS: &str = "pcs.";
54
55pub fn agent_config_group_key(group: &str) -> String {
56 format!("{PREFIX_AGENT_CONFIG_GROUPS}{group}")
57}
58
59pub fn agent_config_pc_key(pc_id: &str) -> String {
60 format!("{PREFIX_AGENT_CONFIG_PCS}{pc_id}")
61}
62
63pub fn parse_agent_config_group_key(key: &str) -> Option<&str> {
66 key.strip_prefix(PREFIX_AGENT_CONFIG_GROUPS)
67}
68
69pub fn parse_agent_config_pc_key(key: &str) -> Option<&str> {
71 key.strip_prefix(PREFIX_AGENT_CONFIG_PCS)
72}
73
74pub const SCRIPT_STATUS_ACTIVE: &str = "ACTIVE";
75pub const SCRIPT_STATUS_REVOKED: &str = "REVOKED";
76
77pub const STREAM_INVENTORY: &str = "INVENTORY";
78pub const STREAM_RESULTS: &str = "RESULTS";
79pub const STREAM_EXEC: &str = "EXEC";
80pub const STREAM_EVENTS: &str = "EVENTS";
81pub const STREAM_AUDIT: &str = "AUDIT";
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
91 fn bucket_names_are_domain_safe() {
92 for name in [
93 BUCKET_SCRIPT_CURRENT,
94 BUCKET_SCRIPT_STATUS,
95 BUCKET_AGENTS_STATE,
96 BUCKET_AGENT_CONFIG,
97 BUCKET_AGENT_GROUPS,
98 BUCKET_SCHEDULES,
99 BUCKET_JOBS,
100 BUCKET_JOBS_YAML,
101 BUCKET_SCHEDULES_YAML,
102 OBJECT_AGENT_RELEASES,
103 ] {
104 assert!(
105 !name.contains('.'),
106 "bucket name {name:?} contains a dot, which NATS KV rejects"
107 );
108 assert!(
109 name.chars()
110 .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-'),
111 "bucket name {name:?} has non-domain-safe characters"
112 );
113 }
114 }
115
116 #[test]
117 fn stream_names_are_unique() {
118 let names = [
119 STREAM_INVENTORY,
120 STREAM_RESULTS,
121 STREAM_EXEC,
122 STREAM_EVENTS,
123 STREAM_AUDIT,
124 ];
125 let mut deduped = names.to_vec();
126 deduped.sort_unstable();
127 deduped.dedup();
128 assert_eq!(
129 deduped.len(),
130 names.len(),
131 "stream constants collide: {names:?}"
132 );
133 }
134
135 #[test]
136 fn script_status_strings() {
137 assert_eq!(SCRIPT_STATUS_ACTIVE, "ACTIVE");
138 assert_eq!(SCRIPT_STATUS_REVOKED, "REVOKED");
139 assert_ne!(SCRIPT_STATUS_ACTIVE, SCRIPT_STATUS_REVOKED);
140 }
141
142 #[test]
143 fn key_agent_target_version_constant() {
144 assert_eq!(KEY_AGENT_TARGET_VERSION, "target_version");
145 }
146
147 #[test]
148 fn agent_config_group_key_round_trips() {
149 let k = agent_config_group_key("canary");
150 assert_eq!(k, "groups.canary");
151 assert_eq!(parse_agent_config_group_key(&k), Some("canary"));
152 }
153
154 #[test]
155 fn agent_config_pc_key_round_trips() {
156 let k = agent_config_pc_key("MINIPC-01");
157 assert_eq!(k, "pcs.MINIPC-01");
158 assert_eq!(parse_agent_config_pc_key(&k), Some("MINIPC-01"));
159 }
160
161 #[test]
162 fn agent_config_scope_keys_do_not_collide() {
163 assert_ne!(PREFIX_AGENT_CONFIG_GROUPS, PREFIX_AGENT_CONFIG_PCS);
170 assert!(parse_agent_config_group_key("pcs.someone").is_none());
171 assert!(parse_agent_config_pc_key("groups.someone").is_none());
172 assert_eq!(parse_agent_config_group_key("global"), None);
173 assert_eq!(parse_agent_config_pc_key("global"), None);
174 }
175}