Skip to main content

xianyun_core/variables/
predefined.rs

1//! Port of `internal/variables/predefined_variable.go`.
2
3use thiserror::Error;
4
5use crate::config::types::ClusterConfig;
6
7/// Carries the parsed config inputs that predefined variables are computed
8/// from, so resolving a variable never triggers a config load.
9#[derive(Debug, Default, Clone)]
10pub struct Options {
11    pub profile_name: String,
12    pub cluster: Option<ClusterConfig>,
13}
14
15#[derive(Debug, Error, PartialEq, Eq)]
16pub enum PredefinedError {
17    #[error("cluster.network.cluster_domain is not set")]
18    NoClusterDomain,
19}
20
21/// `cluster.network.cluster_domain`; undefined when the key is not set.
22pub fn cluster_domain(opts: &Options) -> Result<String, PredefinedError> {
23    let domain = opts
24        .cluster
25        .as_ref()
26        .and_then(|c| c.network.as_ref())
27        .map_or("", |n| n.cluster_domain.as_str());
28
29    if domain.is_empty() {
30        return Err(PredefinedError::NoClusterDomain);
31    }
32
33    Ok(domain.to_string())
34}
35
36/// The cluster's primary hostname, or "" when no network configuration is present.
37pub fn primary_hostname(opts: &Options) -> String {
38    let Some(cluster) = &opts.cluster else {
39        return String::new();
40    };
41
42    cluster
43        .get_all_hostnames(&opts.profile_name)
44        .into_iter()
45        .next()
46        .unwrap_or_default()
47}
48
49/// Reports whether `name` is a built-in predefined variable: either the
50/// primary `XIANYUN_*` name or the `CAPRONI_*` compatibility alias (see
51/// `new_resolver`, which resolves both names to the same values).
52pub fn is_predefined(name: &str) -> bool {
53    matches!(
54        name,
55        XIANYUN_CLUSTER_NAME
56            | XIANYUN_PRIMARY_HOSTNAME
57            | XIANYUN_CLUSTER_DOMAIN
58            | CAPRONI_CLUSTER_NAME
59            | CAPRONI_PRIMARY_HOSTNAME
60            | CAPRONI_CLUSTER_DOMAIN
61    )
62}
63
64pub const XIANYUN_CLUSTER_NAME: &str = "XIANYUN_CLUSTER_NAME";
65pub const XIANYUN_PRIMARY_HOSTNAME: &str = "XIANYUN_PRIMARY_HOSTNAME";
66pub const XIANYUN_CLUSTER_DOMAIN: &str = "XIANYUN_CLUSTER_DOMAIN";
67
68/// Pre-rename names for the same three variables above, kept fully
69/// functional in config `${...}` expansion so existing Go-caproni
70/// projects don't need to update their config to use the new names.
71pub const CAPRONI_CLUSTER_NAME: &str = "CAPRONI_CLUSTER_NAME";
72pub const CAPRONI_PRIMARY_HOSTNAME: &str = "CAPRONI_PRIMARY_HOSTNAME";
73pub const CAPRONI_CLUSTER_DOMAIN: &str = "CAPRONI_CLUSTER_DOMAIN";
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78    use crate::config::types::NetworkConfig;
79    use crate::variables::resolver::new_resolver;
80
81    fn opts_with_hostname(profile: &str, hostname: &str) -> Options {
82        Options {
83            profile_name: profile.to_string(),
84            cluster: Some(ClusterConfig {
85                network: Some(NetworkConfig {
86                    hostname: hostname.to_string(),
87                    ..Default::default()
88                }),
89                ..Default::default()
90            }),
91        }
92    }
93
94    // Port of TestIsPredefined.
95    #[test]
96    fn is_predefined_cases() {
97        assert!(is_predefined(XIANYUN_CLUSTER_NAME));
98        assert!(is_predefined(XIANYUN_PRIMARY_HOSTNAME));
99        assert!(is_predefined(XIANYUN_CLUSTER_DOMAIN));
100        assert!(is_predefined(CAPRONI_CLUSTER_NAME));
101        assert!(is_predefined(CAPRONI_PRIMARY_HOSTNAME));
102        assert!(is_predefined(CAPRONI_CLUSTER_DOMAIN));
103        assert!(!is_predefined("NOT_A_VAR"));
104    }
105
106    // Port of TestPredefinedValues.
107    #[test]
108    fn predefined_values() {
109        let mut opts = opts_with_hostname("my-cluster", "gitlab.example.com");
110
111        assert_eq!(opts.profile_name, "my-cluster");
112        assert_eq!(primary_hostname(&opts), "gitlab.example.com");
113
114        let err = cluster_domain(&opts).unwrap_err();
115        assert_eq!(err, PredefinedError::NoClusterDomain);
116
117        let resolver = new_resolver(opts.clone(), None, None);
118        assert_eq!(resolver.lookup(CAPRONI_CLUSTER_DOMAIN), None);
119
120        opts.cluster
121            .as_mut()
122            .unwrap()
123            .network
124            .as_mut()
125            .unwrap()
126            .cluster_domain = "corp.example".into();
127
128        let domain = cluster_domain(&opts).unwrap();
129        assert_eq!(domain, "corp.example");
130    }
131}