k8s_openapi_ext/ext/
secret.rs

1use super::*;
2
3// // SecretTypeDockerConfigJSON contains a dockercfg file that follows the same format rules as ~/.docker/config.json
4// //
5// // Required fields:
6// // - Secret.Data[".dockerconfigjson"] - a serialized ~/.docker/config.json file
7// SecretTypeDockerConfigJSON SecretType = "kubernetes.io/dockerconfigjson"
8
9// // DockerConfigJSONKey is the key of the required data for SecretTypeDockerConfigJson secrets
10// DockerConfigJSONKey = ".dockerconfigjson"
11
12const DOCKER_CONFIG_JSON_TYPE: &str = "kubernetes.io/dockerconfigjson";
13const DOCKER_CONFIG_JSON_KEY: &str = ".dockerconfigjson";
14
15const BASIC_AUTH_TYPE: &str = "kubernetes.io/basic-auth";
16const BASIC_AUTH_USERNAME: &str = "username";
17const BASIC_AUTH_PASSWORD: &str = "password";
18
19const SSH_AUTH_TYPE: &str = "kubernetes.io/ssh-auth";
20const SSH_AUTH_PRIVATE_KEY: &str = "ssh-privatekey";
21
22pub trait SecretExt: super::ResourceBuilder + Sized {
23    fn new(name: impl ToString) -> Self;
24
25    fn immutable(self, yes: bool) -> Self;
26
27    fn r#type(self, r#type: impl ToString) -> Self;
28
29    fn data(self, data: impl IntoIterator<Item = (impl ToString, ByteString)>) -> Self;
30
31    fn string_data(self, data: impl IntoIterator<Item = (impl ToString, impl ToString)>) -> Self;
32
33    /// Creates new image pull secret object
34    ///
35    fn image_pull_secret(
36        name: impl ToString,
37        registry: impl ToString,
38        username: impl ToString,
39        password: impl ToString,
40    ) -> Self {
41        let registry = registry.to_string();
42        let username = username.to_string();
43        let password = password.to_string();
44        let auth = format!("{username}:{password}");
45        let auth =
46            base64::display::Base64Display::new(auth.as_bytes(), &base64::prelude::BASE64_STANDARD);
47        let config = format!(
48            r#"{{"auths":{{"{registry}":{{"username":"{username}","password":"{password}","auth":"{auth}"}}}}}}"#
49        );
50        let data = [(DOCKER_CONFIG_JSON_KEY, config)];
51        Self::new(name)
52            .r#type(DOCKER_CONFIG_JSON_TYPE)
53            .string_data(data)
54    }
55
56    /// Creates new basic authentication secret object
57    ///
58    fn basic_auth(name: impl ToString, username: impl ToString, password: impl ToString) -> Self {
59        let data = [
60            (BASIC_AUTH_USERNAME, username.to_string()),
61            (BASIC_AUTH_PASSWORD, password.to_string()),
62        ];
63        Self::new(name).r#type(BASIC_AUTH_TYPE).string_data(data)
64    }
65
66    fn ssh_auth(name: impl ToString, private_key: impl ToString) -> Self {
67        let data = [(SSH_AUTH_PRIVATE_KEY, private_key)];
68        Self::new(name).r#type(SSH_AUTH_TYPE).string_data(data)
69    }
70}
71
72pub trait SecretExt2: SecretExt {
73    /// Creates new image pull secret object when you already have the .docker/config.json
74    /// content extracted from some other source (i.e. secret)
75    ///
76    fn image_pull_secret(name: impl ToString, data: impl ToString) -> Self {
77        let data = [(DOCKER_CONFIG_JSON_KEY, data)];
78        Self::new(name)
79            .r#type(DOCKER_CONFIG_JSON_TYPE)
80            .string_data(data)
81    }
82}
83
84impl SecretExt for corev1::Secret {
85    fn new(name: impl ToString) -> Self {
86        let metadata = metadata(name);
87        Self {
88            metadata,
89            // immutable: todo!(),
90            // data: todo!(),
91            // string_data: todo!(),
92            // type_: todo!(),
93            ..default()
94        }
95    }
96
97    fn immutable(self, yes: bool) -> Self {
98        let immutable = Some(yes);
99        Self { immutable, ..self }
100    }
101
102    fn r#type(self, r#type: impl ToString) -> Self {
103        let type_ = Some(r#type.to_string());
104        Self { type_, ..self }
105    }
106
107    fn data(self, data: impl IntoIterator<Item = (impl ToString, ByteString)>) -> Self {
108        let data = data
109            .into_iter()
110            .map(|(key, value)| (key.to_string(), value))
111            .collect();
112        Self {
113            data: Some(data),
114            ..self
115        }
116    }
117
118    fn string_data(self, data: impl IntoIterator<Item = (impl ToString, impl ToString)>) -> Self {
119        let data = data
120            .into_iter()
121            .map(|(key, value)| (key.to_string(), value.to_string()))
122            .collect();
123        Self {
124            string_data: Some(data),
125            ..self
126        }
127    }
128}
129
130impl SecretExt2 for corev1::Secret {}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135
136    use serde_json as json;
137
138    #[test]
139    fn image_pull_secret() {
140        let secret = <corev1::Secret as SecretExt>::image_pull_secret(
141            "name", "registry", "username", "password",
142        );
143        let string_data = secret.string_data.unwrap_or_default();
144        assert_eq!(string_data.len(), 1);
145        let config: json::Value = json::from_str(&string_data[DOCKER_CONFIG_JSON_KEY]).unwrap();
146        assert!(config.is_object());
147    }
148
149    #[test]
150    fn ssh_auth() {
151        let secret = corev1::Secret::ssh_auth(
152            "name",
153            "KGpwKaqlGas+LaAqdwdfAAAEEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIClTFvhvwp1UH25b",
154        );
155        let string_data = secret.string_data.unwrap_or_default();
156        assert_eq!(string_data.len(), 1);
157        assert_eq!(string_data[SSH_AUTH_PRIVATE_KEY].len(), 70);
158    }
159}