vault_mgmt_lib/
version.rs

1use std::str::FromStr;
2
3use k8s_openapi::api::{apps::v1::StatefulSet, core::v1::Pod};
4
5#[derive(Clone, Debug, Hash, PartialEq, Eq)]
6pub struct VaultVersion {
7    pub version: String,
8}
9
10impl FromStr for VaultVersion {
11    type Err = anyhow::Error;
12
13    fn from_str(s: &str) -> Result<Self, Self::Err> {
14        Ok(Self {
15            version: s.to_string(),
16        })
17    }
18}
19
20/// Construct VaultVersion from statefulset
21impl TryFrom<&StatefulSet> for VaultVersion {
22    type Error = anyhow::Error;
23
24    fn try_from(statefulset: &StatefulSet) -> Result<Self, Self::Error> {
25        let container = statefulset
26            .spec
27            .clone()
28            .ok_or(anyhow::anyhow!("statefulset does not have a spec"))?
29            .template
30            .spec
31            .ok_or(anyhow::anyhow!("statefulset does not have a pod spec"))?
32            .containers;
33        let container = container
34            .first()
35            .ok_or(anyhow::anyhow!("statefulset does not have a container"))?;
36
37        let image = container
38            .image
39            .clone()
40            .ok_or(anyhow::anyhow!("container does not have an image"))?;
41
42        let version = image
43            .split(':')
44            .nth(1)
45            .ok_or(anyhow::anyhow!("image does not have a tag"))?
46            .to_string();
47
48        Ok(Self { version })
49    }
50}
51
52/// Construct VaultVersion from pod spec
53impl TryFrom<&Pod> for VaultVersion {
54    type Error = anyhow::Error;
55
56    fn try_from(pod: &Pod) -> Result<Self, Self::Error> {
57        let container = pod
58            .spec
59            .clone()
60            .ok_or(anyhow::anyhow!("pod does not have a spec"))?
61            .containers;
62        let container = container
63            .first()
64            .ok_or(anyhow::anyhow!("pod does not have a container"))?;
65
66        let image = container
67            .image
68            .clone()
69            .ok_or(anyhow::anyhow!("container does not have an image"))?;
70
71        let version = image
72            .split(':')
73            .nth(1)
74            .ok_or(anyhow::anyhow!("image does not have a tag"))?
75            .to_string();
76
77        Ok(Self { version })
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use k8s_openapi::api::{apps::v1::StatefulSet, core::v1::Pod};
84
85    use crate::VaultVersion;
86
87    #[tokio::test]
88    async fn constructing_vault_version_from_statefulset_works() {
89        let file = tokio::fs::read_to_string(format!(
90            "tests/resources/installed/{}.yaml",
91            "apis/apps/v1/namespaces/vault-mgmt-e2e/statefulsets/vault-mgmt-e2e-2274"
92        ))
93        .await
94        .unwrap();
95
96        let statefulset: &StatefulSet = &serde_yaml::from_str(&file).unwrap();
97
98        let version: VaultVersion = statefulset.try_into().unwrap();
99
100        assert_eq!(version.version, "1.13.0");
101    }
102
103    #[tokio::test]
104    async fn constructing_vault_version_from_pod_works() {
105        let file = tokio::fs::read_to_string(format!(
106            "tests/resources/installed/{}.yaml",
107            "api/v1/namespaces/vault-mgmt-e2e/pods/vault-mgmt-e2e-2274-0"
108        ))
109        .await
110        .unwrap();
111
112        let pod: &k8s_openapi::api::core::v1::Pod = &serde_yaml::from_str(&file).unwrap();
113
114        let version: VaultVersion = pod.try_into().unwrap();
115
116        assert_eq!(version.version, "1.13.0");
117    }
118
119    #[tokio::test]
120    async fn constructing_vault_version_from_pod_with_invalid_image_fails() {
121        let file = tokio::fs::read_to_string(format!(
122            "tests/resources/installed/{}.yaml",
123            "api/v1/namespaces/vault-mgmt-e2e/pods/vault-mgmt-e2e-2274-0"
124        ))
125        .await
126        .unwrap();
127
128        let mut pod: Pod = serde_yaml::from_str(&file).unwrap();
129
130        pod.spec
131            .as_mut()
132            .unwrap()
133            .containers
134            .first_mut()
135            .unwrap()
136            .image = Some("vault".to_string());
137
138        let version: Result<VaultVersion, _> = (&pod).try_into();
139
140        assert!(version.is_err());
141    }
142
143    #[test]
144    fn comparing_vault_versions_works() {
145        let current = VaultVersion {
146            version: "1.13.0".to_string(),
147        };
148
149        let outdated = VaultVersion {
150            version: "1.12.0".to_string(),
151        };
152
153        let newer = VaultVersion {
154            version: "1.14.0".to_string(),
155        };
156
157        assert!(current == current);
158        assert!(current != outdated);
159        assert!(current != newer);
160        assert!(outdated != newer);
161    }
162}