Skip to main content

atlas_local/client/
start_deployment.rs

1use bollard::query_parameters::StartContainerOptions;
2
3use crate::{
4    client::Client,
5    docker::{DockerInspectContainer, DockerStartContainer},
6};
7
8use super::GetDeploymentError;
9
10#[derive(Debug, thiserror::Error)]
11pub enum StartDeploymentError {
12    #[error("Failed to start container: {0}")]
13    ContainerStart(String),
14    #[error("Failed to get deployment: {0}")]
15    GetDeployment(#[from] GetDeploymentError),
16}
17
18impl<D: DockerStartContainer + DockerInspectContainer> Client<D> {
19    /// Starts a local Atlas deployment.
20    pub async fn start_deployment(&self, name: &str) -> Result<(), StartDeploymentError> {
21        // Check that a deployment with that name exists and get the container ID.
22        // This ensures we only try to start valid Atlas local deployments.
23        let deployment = self.get_deployment(name).await?;
24        let container_id = deployment.container_id.as_str();
25
26        // Start the container.
27        self.docker
28            .start_container(container_id, None::<StartContainerOptions>)
29            .await
30            .map_err(|e| StartDeploymentError::ContainerStart(e.to_string()))?;
31
32        Ok(())
33    }
34}
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39    use bollard::{
40        errors::Error as BollardError, query_parameters::InspectContainerOptions,
41        secret::ContainerInspectResponse,
42    };
43    use mockall::mock;
44
45    mock! {
46        Docker {}
47
48        impl DockerStartContainer for Docker {
49            async fn start_container(
50                &self,
51                container_id: &str,
52                options: Option<StartContainerOptions>,
53            ) -> Result<(), BollardError>;
54        }
55
56        impl DockerInspectContainer for Docker {
57            async fn inspect_container(
58                &self,
59                container_id: &str,
60                options: Option<InspectContainerOptions>,
61            ) -> Result<ContainerInspectResponse, BollardError>;
62        }
63    }
64
65    fn create_test_container_inspect_response() -> ContainerInspectResponse {
66        use bollard::secret::{ContainerConfig, ContainerState, ContainerStateStatusEnum};
67        use std::collections::HashMap;
68
69        let mut labels = HashMap::new();
70        labels.insert("mongodb-atlas-local".to_string(), "container".to_string());
71        labels.insert("version".to_string(), "8.0.0".to_string());
72        labels.insert("mongodb-type".to_string(), "community".to_string());
73
74        let env_vars = vec!["TOOL=ATLASCLI".to_string()];
75
76        ContainerInspectResponse {
77            id: Some("test_container_id".to_string()),
78            name: Some("/test-deployment".to_string()),
79            config: Some(ContainerConfig {
80                labels: Some(labels),
81                env: Some(env_vars),
82                ..Default::default()
83            }),
84            state: Some(ContainerState {
85                status: Some(ContainerStateStatusEnum::RUNNING),
86                ..Default::default()
87            }),
88            ..Default::default()
89        }
90    }
91
92    #[tokio::test]
93    async fn test_start_deployment() {
94        // Arrange
95        let mut mock_docker = MockDocker::new();
96
97        // Set up expectations
98        mock_docker
99            .expect_inspect_container()
100            .with(
101                mockall::predicate::eq("test-deployment"),
102                mockall::predicate::eq(None::<InspectContainerOptions>),
103            )
104            .times(1)
105            .returning(move |_, _| Ok(create_test_container_inspect_response()));
106
107        mock_docker
108            .expect_start_container()
109            .with(
110                mockall::predicate::eq("test_container_id"),
111                mockall::predicate::eq(None::<StartContainerOptions>),
112            )
113            .times(1)
114            .returning(|_, _| Ok(()));
115
116        let client = Client::new(mock_docker);
117
118        // Act
119        let result = client.start_deployment("test-deployment").await;
120
121        // Assert
122        assert!(result.is_ok());
123    }
124
125    #[tokio::test]
126    async fn test_start_deployment_get_deployment_error() {
127        // Arrange
128        let mut mock_docker = MockDocker::new();
129
130        // Set up expectations
131        mock_docker
132            .expect_inspect_container()
133            .times(1)
134            .returning(|_, _| {
135                Err(BollardError::DockerResponseServerError {
136                    status_code: 404,
137                    message: "No such container".to_string(),
138                })
139            });
140
141        let client = Client::new(mock_docker);
142
143        // Act
144        let result = client.start_deployment("nonexistent-deployment").await;
145
146        // Assert
147        assert!(result.is_err());
148        assert!(matches!(
149            result.unwrap_err(),
150            StartDeploymentError::GetDeployment(_)
151        ));
152    }
153
154    #[tokio::test]
155    async fn test_start_deployment_start_container_error() {
156        // Arrange
157        let mut mock_docker = MockDocker::new();
158
159        // Set up expectations
160        mock_docker
161            .expect_inspect_container()
162            .times(1)
163            .returning(move |_, _| Ok(create_test_container_inspect_response()));
164
165        mock_docker
166            .expect_start_container()
167            .times(1)
168            .returning(|_, _| {
169                Err(BollardError::DockerResponseServerError {
170                    status_code: 500,
171                    message: "Internal Server Error".to_string(),
172                })
173            });
174
175        let client = Client::new(mock_docker);
176
177        // Act
178        let result = client.start_deployment("test-deployment").await;
179
180        // Assert
181        assert!(result.is_err());
182        assert!(matches!(
183            result.unwrap_err(),
184            StartDeploymentError::ContainerStart(_)
185        ));
186    }
187}