Skip to main content

atlas_local/client/
get_deployment.rs

1use bollard::query_parameters::InspectContainerOptions;
2
3use crate::{
4    client::Client,
5    docker::DockerInspectContainer,
6    models::{Deployment, IntoDeploymentError},
7};
8
9#[derive(Debug, thiserror::Error)]
10pub enum GetDeploymentError {
11    #[error("Failed to inspect container: {0}")]
12    ContainerInspect(#[from] bollard::errors::Error),
13    #[error("The container is not a local Atlas deployment: {0}")]
14    IntoDeployment(#[from] IntoDeploymentError),
15}
16
17impl<D: DockerInspectContainer> Client<D> {
18    /// Inspects a container.
19    ///
20    /// # Arguments
21    ///
22    /// * `container_id_or_name` - The ID or name of the container to inspect.
23    pub async fn get_deployment(
24        &self,
25        container_id_or_name: &str,
26    ) -> Result<Deployment, GetDeploymentError> {
27        // Inspect the container to get the deployment details
28        let container_inspect_response = self
29            .docker
30            .inspect_container(container_id_or_name, None::<InspectContainerOptions>)
31            .await?;
32
33        // Convert the container inspect response to a deployment
34        Ok(container_inspect_response.try_into()?)
35    }
36}
37
38#[cfg(test)]
39mod tests {
40    use super::*;
41    use crate::models::{CreationSource, MongodbType, State};
42    use bollard::{
43        errors::Error as BollardError,
44        secret::{
45            ContainerConfig, ContainerInspectResponse, ContainerState, ContainerStateStatusEnum,
46        },
47    };
48    use maplit::hashmap;
49    use mockall::mock;
50    use pretty_assertions::assert_eq;
51    use semver::Version;
52
53    mock! {
54        Docker {}
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    #[tokio::test]
66    async fn test_get_deployment() {
67        // Arrange
68        let mut mock_docker = MockDocker::new();
69        let container_inspect_response = ContainerInspectResponse {
70            id: Some("test_container_id".to_string()),
71            name: Some("/test-deployment".to_string()),
72            config: Some(ContainerConfig {
73                labels: Some(hashmap! {
74                    "mongodb-atlas-local".to_string() => "container".to_string(),
75                    "version".to_string() => "8.0.0".to_string(),
76                    "mongodb-type".to_string() => "community".to_string(),
77                }),
78                env: Some(vec!["TOOL=ATLASCLI".to_string()]),
79                ..Default::default()
80            }),
81            state: Some(ContainerState {
82                status: Some(ContainerStateStatusEnum::RUNNING),
83                ..Default::default()
84            }),
85            ..Default::default()
86        };
87
88        // Set up expectations
89        mock_docker
90            .expect_inspect_container()
91            .with(
92                mockall::predicate::eq("test-deployment"),
93                mockall::predicate::eq(None::<InspectContainerOptions>),
94            )
95            .times(1)
96            .returning(move |_, _| Ok(container_inspect_response.clone()));
97
98        let client = Client::new(mock_docker);
99
100        // Act
101        let result = client.get_deployment("test-deployment").await;
102
103        // Assert
104        assert!(result.is_ok());
105
106        assert_eq!(
107            result.unwrap(),
108            Deployment {
109                container_id: "test_container_id".to_string(),
110                name: Some("test-deployment".to_string()),
111                state: State::Running,
112                mongodb_type: MongodbType::Community,
113                mongodb_version: Version::new(8, 0, 0),
114                port_bindings: None,
115                creation_source: Some(CreationSource::AtlasCLI),
116                local_seed_location: None,
117                mongodb_initdb_database: None,
118                mongodb_initdb_root_password_file: None,
119                mongodb_initdb_root_password: None,
120                mongodb_initdb_root_username_file: None,
121                mongodb_initdb_root_username: None,
122                mongodb_load_sample_data: None,
123                voyage_api_key: None,
124                mongot_log_file: None,
125                runner_log_file: None,
126                do_not_track: false,
127                telemetry_base_url: None,
128            }
129        );
130    }
131
132    #[tokio::test]
133    async fn test_get_deployment_container_inspect_error() {
134        // Arrange
135        let mut mock_docker = MockDocker::new();
136
137        // Set up expectations
138        mock_docker
139            .expect_inspect_container()
140            .with(
141                mockall::predicate::eq("nonexistent-deployment"),
142                mockall::predicate::eq(None::<InspectContainerOptions>),
143            )
144            .times(1)
145            .returning(|_, _| {
146                Err(BollardError::DockerResponseServerError {
147                    status_code: 404,
148                    message: "No such container".to_string(),
149                })
150            });
151
152        let client = Client::new(mock_docker);
153
154        // Act
155        let result = client.get_deployment("nonexistent-deployment").await;
156
157        // Assert
158        assert!(result.is_err());
159        assert!(matches!(
160            result.unwrap_err(),
161            GetDeploymentError::ContainerInspect(_)
162        ));
163    }
164
165    #[tokio::test]
166    async fn test_get_deployment_into_deployment_error() {
167        // Arrange
168        let mut mock_docker = MockDocker::new();
169        let container_inspect_response = ContainerInspectResponse {
170            id: Some("test_container_id".to_string()),
171            name: Some("/test-deployment".to_string()),
172            config: Some(ContainerConfig {
173                labels: None, // Missing labels will cause IntoDeploymentError
174                ..Default::default()
175            }),
176            ..Default::default()
177        };
178
179        // Set up expectations
180        mock_docker
181            .expect_inspect_container()
182            .with(
183                mockall::predicate::eq("invalid-deployment"),
184                mockall::predicate::eq(None::<InspectContainerOptions>),
185            )
186            .times(1)
187            .returning(move |_, _| Ok(container_inspect_response.clone()));
188
189        let client = Client::new(mock_docker);
190
191        // Act
192        let result = client.get_deployment("invalid-deployment").await;
193
194        // Assert
195        assert!(result.is_err());
196        assert!(matches!(
197            result.unwrap_err(),
198            GetDeploymentError::IntoDeployment(_)
199        ));
200    }
201}