testcontainers_rs/
container.rs

1use super::client::DockerClient;
2use super::image::DockerImage;
3use bollard::Docker;
4use futures::executor::block_on;
5use std::{fmt, net};
6
7// pub struct Container<'d, I: Image> {
8pub struct Container<C>
9// pub struct Container
10where
11    C: DockerClient,
12{
13    id: String,
14    // client: Box<&dyn DockerClient>,
15    // client: Box<dyn DockerClient<Client = _, Error = _>>,
16    client: C,
17    image: DockerImage,
18    // image: RunnableImage<I>,
19    // command: Command,
20    // /// Tracks the lifetime of the client to make sure the container is dropped before the client.
21    // client_lifetime: PhantomData<&'d ()>,
22}
23
24// impl<'d, I> Container<'d, I>
25impl<C> Container<C>
26// impl Container
27where
28    C: DockerClient,
29    // I: Image,
30{
31    /// Returns the id of this container.
32    pub async fn new(
33        id: String,
34        // client: impl DockerClient + 'static,
35        client: C, // impl DockerClient + 'static,
36        image: DockerImage,
37        // command: env::Command,
38    ) -> Self {
39        let container = Self {
40            id,
41            client,
42            image,
43            // command,
44            // client_lifetime: PhantomData,
45        };
46        // container.block_until_ready().await;
47        container
48    }
49
50    async fn block_until_ready(&self) {
51        log::debug!("Waiting for container {} to be ready", self.id);
52
53        // for condition in self.image.ready_conditions() {
54        //     match condition {
55        //         WaitFor::StdOutMessage { message } => self
56        //             .docker_client
57        //             .stdout_logs(&self.id)
58        //             .wait_for_message(&message)
59        //             .await
60        //             .unwrap(),
61        //         WaitFor::StdErrMessage { message } => self
62        //             .docker_client
63        //             .stderr_logs(&self.id)
64        //             .wait_for_message(&message)
65        //             .await
66        //             .unwrap(),
67        //         WaitFor::Duration { length } => {
68        //             tokio::time::sleep(length).await;
69        //         }
70        //         WaitFor::Healthcheck => loop {
71        //             use HealthStatusEnum::*;
72
73        //             let health_status = self
74        //                 .docker_client
75        //                 .inspect(&self.id)
76        //                 .await
77        //                 .state
78        //                 .unwrap_or_else(|| panic!("Container state not available"))
79        //                 .health
80        //                 .unwrap_or_else(|| panic!("Health state not available"))
81        //                 .status;
82
83        //             match health_status {
84        //                 Some(HEALTHY) => break,
85        //                 None | Some(EMPTY) | Some(NONE) => {
86        //                     panic!("Healthcheck not configured for container")
87        //                 }
88        //                 Some(UNHEALTHY) => panic!("Healthcheck reports unhealthy"),
89        //                 Some(STARTING) => sleep(Duration::from_millis(100)).await,
90        //             }
91        //             panic!("Healthcheck for the container is not configured");
92        //         },
93        //         WaitFor::Nothing => {}
94        //     }
95        // }
96
97        log::debug!("container {} is ready!", self.id);
98    }
99
100    /// Returns the id of this container.
101    pub fn id(&self) -> &str {
102        &self.id
103    }
104
105    /// Starts the container.
106    pub async fn start(&self) -> Result<(), C::Error> {
107        log::debug!("starting docker container {}", self.id);
108        self.client.start(&self.id).await
109    }
110
111    /// Stops the container
112    pub async fn stop(&self) -> Result<(), C::Error> {
113        log::debug!("stopping docker container {}", self.id);
114        self.client.stop(&self.id).await
115    }
116
117    /// Removes the container
118    pub async fn rm(self) -> Result<(), C::Error> {
119        log::debug!("removing docker container {}", self.id);
120        self.client.rm(&self.id).await
121    }
122
123    /// Gets the host IP address of the container
124    pub async fn host(&self) -> Result<net::IpAddr, C::Error> {
125        self.client.host(&self.id).await
126    }
127
128    /// Get the mapped host IPv4 port for the given internal port
129    pub async fn mapped_port_ipv4(&self, internal_port: u16) -> Result<Option<u16>, C::Error> {
130        let ports = self.client.ports(&self.id).await?;
131        Ok(ports.mapped_port_ipv4(internal_port))
132    }
133
134    /// Get the mapped host IPv6 port for the given internal port
135    pub async fn mapped_port_ipv6(&self, internal_port: u16) -> Result<Option<u16>, C::Error> {
136        let ports = self.client.ports(&self.id).await?;
137        Ok(ports.mapped_port_ipv6(internal_port))
138    }
139
140    /// Drops and removes the container
141    async fn drop_async(&self) {
142        if let Err(err) = self.client.rm(&self.id).await {
143            log::error!("failed to remove docker container {}", self.id);
144        }
145        // match self.command {
146        //     env::Command::Remove => self.docker_client.rm(&self.id).await,
147        //     env::Command::Keep => {}
148        // }
149    }
150}
151
152// impl<'d, I> fmt::Debug for Container<'d, I>
153// where
154//     I: fmt::Debug + Image,
155impl<C> fmt::Debug for Container<C>
156// impl fmt::Debug for Container
157where
158    C: DockerClient,
159    // I: fmt::Debug + Image,
160{
161    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162        f.debug_struct("Container")
163            .field("id", &self.id)
164            .field("image", &self.image.descriptor())
165            .finish()
166    }
167}
168
169// impl<'d, I> Drop for Container<'d, I>
170impl<C> Drop for Container<C>
171// impl Drop for Container
172where
173    C: DockerClient,
174    // I: Image,
175{
176    fn drop(&mut self) {
177        block_on(self.drop_async())
178    }
179}