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}