atlas_local/models/
labels.rs1use bollard::secret::ContainerInspectResponse;
2use semver::Version;
3
4use crate::models::{MongodbType, ParseMongodbTypeError};
5
6pub const LOCAL_DEPLOYMENT_LABEL_KEY: &str = "mongodb-atlas-local";
7pub const LOCAL_DEPLOYMENT_LABEL_VALUE: &str = "container";
8
9pub const MONGODB_TYPE_LABEL_KEY: &str = "mongodb-type";
10pub const MONGODB_VERSION_LABEL_KEY: &str = "version";
11
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct LocalDeploymentLabels {
15 pub mongodb_version: Version,
16 pub mongodb_type: MongodbType,
17}
18
19#[derive(Debug, thiserror::Error, PartialEq, Eq)]
20pub enum GetLocalDeploymentLabelsError {
21 #[error("Missing container labels")]
22 MissingContainerLabels,
23 #[error("Not a local deployment")]
24 NotALocalDeployment,
25 #[error("Missing mongodb version")]
26 MissingMongodbVersion,
27 #[error("Invalid mongodb version: {reason}")]
28 InvalidMongodbVersion { reason: String },
29 #[error("Missing mongodb type")]
30 MissingMongodbType,
31 #[error(transparent)]
32 InvalidMongodbType(#[from] ParseMongodbTypeError),
33}
34
35impl TryFrom<&ContainerInspectResponse> for LocalDeploymentLabels {
38 type Error = GetLocalDeploymentLabelsError;
39
40 fn try_from(value: &ContainerInspectResponse) -> Result<Self, Self::Error> {
41 let container_config = value
44 .config
45 .as_ref()
46 .ok_or(GetLocalDeploymentLabelsError::MissingContainerLabels)?
47 .labels
48 .as_ref()
49 .ok_or(GetLocalDeploymentLabelsError::MissingContainerLabels)?;
50
51 let atlas_local_marker_label = container_config
53 .get(LOCAL_DEPLOYMENT_LABEL_KEY)
54 .ok_or(GetLocalDeploymentLabelsError::NotALocalDeployment)?;
55 if atlas_local_marker_label != LOCAL_DEPLOYMENT_LABEL_VALUE {
56 return Err(GetLocalDeploymentLabelsError::NotALocalDeployment);
57 }
58
59 let mongodb_version_string = container_config
61 .get(MONGODB_VERSION_LABEL_KEY)
62 .ok_or(GetLocalDeploymentLabelsError::MissingMongodbVersion)?;
63 let mongodb_version = mongodb_version_string.parse::<Version>().map_err(|e| {
64 GetLocalDeploymentLabelsError::InvalidMongodbVersion {
65 reason: e.to_string(),
66 }
67 })?;
68
69 let mongodb_type_string = container_config
71 .get(MONGODB_TYPE_LABEL_KEY)
72 .ok_or(GetLocalDeploymentLabelsError::MissingMongodbType)?;
73 let mongodb_type: MongodbType = mongodb_type_string.parse()?;
74
75 Ok(LocalDeploymentLabels {
76 mongodb_version,
77 mongodb_type,
78 })
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use bollard::secret::ContainerConfig;
85
86 use super::*;
87
88 #[test]
89 fn missing_container_config() {
90 let container_inspect_response = ContainerInspectResponse::default();
91 let result = LocalDeploymentLabels::try_from(&container_inspect_response);
92 assert_eq!(
93 result,
94 Err(GetLocalDeploymentLabelsError::MissingContainerLabels)
95 );
96 }
97
98 #[test]
99 fn missing_container_labels() {
100 let container_inspect_response = ContainerInspectResponse {
101 config: Some(ContainerConfig {
102 labels: None,
103 ..Default::default()
104 }),
105 ..Default::default()
106 };
107 let result = LocalDeploymentLabels::try_from(&container_inspect_response);
108 assert_eq!(
109 result,
110 Err(GetLocalDeploymentLabelsError::MissingContainerLabels)
111 );
112 }
113
114 #[test]
115 fn missing_marker_label() {
116 use std::collections::HashMap;
117
118 let mut labels = HashMap::new();
119 labels.insert("some-other-label".to_string(), "value".to_string());
120
121 let container_inspect_response = ContainerInspectResponse {
122 config: Some(ContainerConfig {
123 labels: Some(labels),
124 ..Default::default()
125 }),
126 ..Default::default()
127 };
128 let result = LocalDeploymentLabels::try_from(&container_inspect_response);
129 assert_eq!(
130 result,
131 Err(GetLocalDeploymentLabelsError::NotALocalDeployment)
132 );
133 }
134
135 #[test]
136 fn invalid_marker_label_value() {
137 use std::collections::HashMap;
138
139 let mut labels = HashMap::new();
140 labels.insert(
141 LOCAL_DEPLOYMENT_LABEL_KEY.to_string(),
142 "wrong-value".to_string(),
143 );
144
145 let container_inspect_response = ContainerInspectResponse {
146 config: Some(ContainerConfig {
147 labels: Some(labels),
148 ..Default::default()
149 }),
150 ..Default::default()
151 };
152 let result = LocalDeploymentLabels::try_from(&container_inspect_response);
153 assert_eq!(
154 result,
155 Err(GetLocalDeploymentLabelsError::NotALocalDeployment)
156 );
157 }
158
159 #[test]
160 fn missing_mongodb_version() {
161 use std::collections::HashMap;
162
163 let mut labels = HashMap::new();
164 labels.insert(
165 LOCAL_DEPLOYMENT_LABEL_KEY.to_string(),
166 LOCAL_DEPLOYMENT_LABEL_VALUE.to_string(),
167 );
168
169 let container_inspect_response = ContainerInspectResponse {
170 config: Some(ContainerConfig {
171 labels: Some(labels),
172 ..Default::default()
173 }),
174 ..Default::default()
175 };
176 let result = LocalDeploymentLabels::try_from(&container_inspect_response);
177 assert_eq!(
178 result,
179 Err(GetLocalDeploymentLabelsError::MissingMongodbVersion)
180 );
181 }
182
183 #[test]
184 fn invalid_mongodb_version() {
185 use std::collections::HashMap;
186
187 let mut labels = HashMap::new();
188 labels.insert(
189 LOCAL_DEPLOYMENT_LABEL_KEY.to_string(),
190 LOCAL_DEPLOYMENT_LABEL_VALUE.to_string(),
191 );
192 labels.insert(
193 MONGODB_VERSION_LABEL_KEY.to_string(),
194 "not-a-semver".to_string(),
195 );
196
197 let container_inspect_response = ContainerInspectResponse {
198 config: Some(ContainerConfig {
199 labels: Some(labels),
200 ..Default::default()
201 }),
202 ..Default::default()
203 };
204 let result = LocalDeploymentLabels::try_from(&container_inspect_response);
205 assert!(matches!(
206 result,
207 Err(GetLocalDeploymentLabelsError::InvalidMongodbVersion { .. })
208 ));
209 }
210
211 #[test]
212 fn missing_mongodb_type() {
213 use std::collections::HashMap;
214
215 let mut labels = HashMap::new();
216 labels.insert(
217 LOCAL_DEPLOYMENT_LABEL_KEY.to_string(),
218 LOCAL_DEPLOYMENT_LABEL_VALUE.to_string(),
219 );
220 labels.insert(MONGODB_VERSION_LABEL_KEY.to_string(), "7.0.0".to_string());
221
222 let container_inspect_response = ContainerInspectResponse {
223 config: Some(ContainerConfig {
224 labels: Some(labels),
225 ..Default::default()
226 }),
227 ..Default::default()
228 };
229 let result = LocalDeploymentLabels::try_from(&container_inspect_response);
230 assert_eq!(
231 result,
232 Err(GetLocalDeploymentLabelsError::MissingMongodbType)
233 );
234 }
235
236 #[test]
237 fn invalid_mongodb_type() {
238 use std::collections::HashMap;
239
240 let mut labels = HashMap::new();
241 labels.insert(
242 LOCAL_DEPLOYMENT_LABEL_KEY.to_string(),
243 LOCAL_DEPLOYMENT_LABEL_VALUE.to_string(),
244 );
245 labels.insert(MONGODB_VERSION_LABEL_KEY.to_string(), "7.0.0".to_string());
246 labels.insert(
247 MONGODB_TYPE_LABEL_KEY.to_string(),
248 "invalid-type".to_string(),
249 );
250
251 let container_inspect_response = ContainerInspectResponse {
252 config: Some(ContainerConfig {
253 labels: Some(labels),
254 ..Default::default()
255 }),
256 ..Default::default()
257 };
258 let result = LocalDeploymentLabels::try_from(&container_inspect_response);
259 assert!(matches!(
260 result,
261 Err(GetLocalDeploymentLabelsError::InvalidMongodbType(_))
262 ));
263 }
264
265 #[test]
266 fn successful_parse() {
267 use std::collections::HashMap;
268
269 let mut labels = HashMap::new();
270 labels.insert(
271 LOCAL_DEPLOYMENT_LABEL_KEY.to_string(),
272 LOCAL_DEPLOYMENT_LABEL_VALUE.to_string(),
273 );
274 labels.insert(MONGODB_VERSION_LABEL_KEY.to_string(), "7.0.0".to_string());
275 labels.insert(MONGODB_TYPE_LABEL_KEY.to_string(), "community".to_string());
276
277 let container_inspect_response = ContainerInspectResponse {
278 config: Some(ContainerConfig {
279 labels: Some(labels),
280 ..Default::default()
281 }),
282 ..Default::default()
283 };
284 let result = LocalDeploymentLabels::try_from(&container_inspect_response);
285
286 assert!(result.is_ok());
287 let labels = result.unwrap();
288 assert_eq!(labels.mongodb_version, Version::parse("7.0.0").unwrap());
289 assert_eq!(labels.mongodb_type, MongodbType::Community);
290 }
291}