Skip to main content

alien_core/
kubernetes_naming.rs

1//! Shared Kubernetes naming helpers.
2//!
3//! Terraform workload identity trust, Helm ServiceAccounts, and runtime
4//! controllers must agree on these names exactly.
5
6fn dns_label(input: &str) -> String {
7    let mut out = String::with_capacity(input.len());
8    let mut previous_dash = false;
9
10    for ch in input.chars() {
11        let next = if ch.is_ascii_alphanumeric() {
12            Some(ch.to_ascii_lowercase())
13        } else {
14            Some('-')
15        };
16        let Some(ch) = next else {
17            continue;
18        };
19        if ch == '-' {
20            if !previous_dash && !out.is_empty() {
21                out.push(ch);
22            }
23            previous_dash = true;
24        } else {
25            out.push(ch);
26            previous_dash = false;
27        }
28    }
29
30    let out = out.trim_matches('-').to_string();
31    if out.is_empty() {
32        "alien".to_string()
33    } else {
34        out
35    }
36}
37
38fn truncate_dns_label(mut name: String) -> String {
39    if name.len() > 63 {
40        name.truncate(63);
41        name = name.trim_end_matches('-').to_string();
42    }
43    if name.is_empty() {
44        "alien".to_string()
45    } else {
46        name
47    }
48}
49
50/// Canonical Kubernetes workload name for stack resources.
51pub fn kubernetes_resource_name(_resource_prefix: &str, resource_id: &str) -> String {
52    truncate_dns_label(dns_label(resource_id))
53}
54
55/// Canonical Kubernetes ServiceAccount for a permission profile.
56pub fn kubernetes_service_account_name(resource_prefix: &str, permission_profile: &str) -> String {
57    truncate_dns_label(format!(
58        "{}-{}-sa",
59        dns_label(resource_prefix),
60        dns_label(permission_profile)
61    ))
62}
63
64/// Canonical Kubernetes ServiceAccount for the Alien agent/manager pod.
65pub fn kubernetes_manager_service_account_name(resource_prefix: &str) -> String {
66    truncate_dns_label(format!("{}-manager-sa", dns_label(resource_prefix)))
67}
68
69/// Canonical Kubernetes ServiceAccount for build jobs.
70pub fn kubernetes_build_service_account_name(resource_prefix: &str) -> String {
71    truncate_dns_label(format!("{}-build-sa", dns_label(resource_prefix)))
72}
73
74/// ServiceAccount resource IDs are generated as `{permission_profile}-sa`.
75pub fn permission_profile_from_service_account_id(id: &str) -> String {
76    id.strip_suffix("-sa").unwrap_or(id).to_string()
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn canonical_service_account_names_are_dns_labels() {
85        assert_eq!(
86            kubernetes_service_account_name("e2e-01", "execution"),
87            "e2e-01-execution-sa"
88        );
89        assert_eq!(
90            kubernetes_service_account_name("My_App!", "Writer#Profile"),
91            "my-app-writer-profile-sa"
92        );
93    }
94
95    #[test]
96    fn app_resource_names_are_namespace_local() {
97        assert_eq!(kubernetes_resource_name("e2e-01", "postgres"), "postgres");
98        assert_eq!(
99            kubernetes_resource_name("My_App!", "API#Gateway"),
100            "api-gateway"
101        );
102    }
103
104    #[test]
105    fn strips_generated_service_account_suffix() {
106        assert_eq!(
107            permission_profile_from_service_account_id("execution-sa"),
108            "execution"
109        );
110        assert_eq!(
111            permission_profile_from_service_account_id("custom"),
112            "custom"
113        );
114    }
115}