atlas_local/client/
delete_deployment.rs1use bollard::query_parameters::{RemoveContainerOptions, StopContainerOptions};
2
3use crate::{
4 client::Client,
5 docker::{DockerInspectContainer, DockerRemoveContainer, DockerStopContainer},
6};
7
8use super::GetDeploymentError;
9
10#[derive(Debug, thiserror::Error)]
11pub enum DeleteDeploymentError {
12 #[error("Failed to delete container: {0}")]
13 ContainerStop(bollard::errors::Error),
14 #[error("Failed to get deployment: {0}")]
15 GetDeployment(#[from] GetDeploymentError),
16 #[error("Failed to delete remove: {0}")]
17 ContainerRemove(bollard::errors::Error),
18}
19
20impl<D: DockerStopContainer + DockerRemoveContainer + DockerInspectContainer> Client<D> {
21 pub async fn delete_deployment(&self, name: &str) -> Result<(), DeleteDeploymentError> {
23 let deployment = self.get_deployment(name).await?;
26 let container_id = deployment.container_id.as_str();
27
28 self.docker
30 .stop_container(container_id, None::<StopContainerOptions>)
31 .await
32 .map_err(DeleteDeploymentError::ContainerStop)?;
33
34 self.docker
36 .remove_container(container_id, None::<RemoveContainerOptions>)
37 .await
38 .map_err(DeleteDeploymentError::ContainerRemove)?;
39
40 Ok(())
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 use super::*;
47 use bollard::{
48 errors::Error as BollardError, query_parameters::InspectContainerOptions,
49 secret::ContainerInspectResponse,
50 };
51 use mockall::mock;
52
53 mock! {
54 Docker {}
55
56 impl DockerStopContainer for Docker {
57 async fn stop_container(
58 &self,
59 container_id: &str,
60 options: Option<StopContainerOptions>,
61 ) -> Result<(), BollardError>;
62 }
63
64 impl DockerRemoveContainer for Docker {
65 async fn remove_container(
66 &self,
67 container_id: &str,
68 options: Option<RemoveContainerOptions>,
69 ) -> Result<(), BollardError>;
70 }
71
72 impl DockerInspectContainer for Docker {
73 async fn inspect_container(
74 &self,
75 container_id: &str,
76 options: Option<InspectContainerOptions>,
77 ) -> Result<ContainerInspectResponse, BollardError>;
78 }
79 }
80
81 fn create_test_container_inspect_response() -> ContainerInspectResponse {
82 use bollard::secret::{ContainerConfig, ContainerState, ContainerStateStatusEnum};
83 use std::collections::HashMap;
84
85 let mut labels = HashMap::new();
86 labels.insert("mongodb-atlas-local".to_string(), "container".to_string());
87 labels.insert("version".to_string(), "8.0.0".to_string());
88 labels.insert("mongodb-type".to_string(), "community".to_string());
89
90 let env_vars = vec!["TOOL=ATLASCLI".to_string()];
91
92 ContainerInspectResponse {
93 id: Some("test_container_id".to_string()),
94 name: Some("/test-deployment".to_string()),
95 config: Some(ContainerConfig {
96 labels: Some(labels),
97 env: Some(env_vars),
98 ..Default::default()
99 }),
100 state: Some(ContainerState {
101 status: Some(ContainerStateStatusEnum::RUNNING),
102 ..Default::default()
103 }),
104 ..Default::default()
105 }
106 }
107
108 #[tokio::test]
109 async fn test_delete_deployment() {
110 let mut mock_docker = MockDocker::new();
112
113 mock_docker
115 .expect_inspect_container()
116 .with(
117 mockall::predicate::eq("test-deployment"),
118 mockall::predicate::eq(None::<InspectContainerOptions>),
119 )
120 .times(1)
121 .returning(move |_, _| Ok(create_test_container_inspect_response()));
122
123 mock_docker
124 .expect_stop_container()
125 .with(
126 mockall::predicate::eq("test_container_id"),
127 mockall::predicate::eq(None::<StopContainerOptions>),
128 )
129 .times(1)
130 .returning(|_, _| Ok(()));
131
132 mock_docker
133 .expect_remove_container()
134 .with(
135 mockall::predicate::eq("test_container_id"),
136 mockall::predicate::eq(None::<RemoveContainerOptions>),
137 )
138 .times(1)
139 .returning(|_, _| Ok(()));
140
141 let client = Client::new(mock_docker);
142
143 let result = client.delete_deployment("test-deployment").await;
145
146 assert!(result.is_ok());
148 }
149
150 #[tokio::test]
151 async fn test_delete_deployment_get_deployment_error() {
152 let mut mock_docker = MockDocker::new();
154
155 mock_docker
157 .expect_inspect_container()
158 .times(1)
159 .returning(|_, _| {
160 Err(BollardError::DockerResponseServerError {
161 status_code: 404,
162 message: "No such container".to_string(),
163 })
164 });
165
166 let client = Client::new(mock_docker);
167
168 let result = client.delete_deployment("nonexistent-deployment").await;
170
171 assert!(result.is_err());
173 assert!(matches!(
174 result.unwrap_err(),
175 DeleteDeploymentError::GetDeployment(_)
176 ));
177 }
178
179 #[tokio::test]
180 async fn test_delete_deployment_stop_container_error() {
181 let mut mock_docker = MockDocker::new();
183
184 mock_docker
186 .expect_inspect_container()
187 .times(1)
188 .returning(move |_, _| Ok(create_test_container_inspect_response()));
189
190 mock_docker
191 .expect_stop_container()
192 .times(1)
193 .returning(|_, _| {
194 Err(BollardError::DockerResponseServerError {
195 status_code: 500,
196 message: "Internal Server Error".to_string(),
197 })
198 });
199
200 let client = Client::new(mock_docker);
201
202 let result = client.delete_deployment("test-deployment").await;
204
205 assert!(result.is_err());
207 assert!(matches!(
208 result.unwrap_err(),
209 DeleteDeploymentError::ContainerStop(_)
210 ));
211 }
212
213 #[tokio::test]
214 async fn test_delete_deployment_remove_container_error() {
215 let mut mock_docker = MockDocker::new();
217
218 mock_docker
220 .expect_inspect_container()
221 .times(1)
222 .returning(move |_, _| Ok(create_test_container_inspect_response()));
223
224 mock_docker
225 .expect_stop_container()
226 .times(1)
227 .returning(|_, _| Ok(()));
228
229 mock_docker
230 .expect_remove_container()
231 .times(1)
232 .returning(|_, _| {
233 Err(BollardError::DockerResponseServerError {
234 status_code: 500,
235 message: "Internal Server Error".to_string(),
236 })
237 });
238
239 let client = Client::new(mock_docker);
240
241 let result = client.delete_deployment("test-deployment").await;
243
244 assert!(result.is_err());
246 assert!(matches!(
247 result.unwrap_err(),
248 DeleteDeploymentError::ContainerRemove(_)
249 ));
250 }
251}