omry_integration/
containers.rs1use std::collections::HashMap;
3
4use anyhow::Result;
5use testcontainers::bollard::Docker;
6use testcontainers::bollard::query_parameters::ListNetworksOptions;
7use testcontainers::core::ContainerRequest;
8use testcontainers::core::logs::consumer::{LogConsumer, logging_consumer::LoggingConsumer};
9use testcontainers::{Image, ImageExt};
10
11pub trait ContainerLogConsumer: LogConsumer + Send + Sync + 'static {}
14impl<L> ContainerLogConsumer for L where L: LogConsumer + Send + Sync + 'static {}
15
16#[allow(clippy::missing_docs_in_private_items)]
19#[derive(Debug, Clone)]
20pub struct ImageExtConfig<L>
21where
22 L: ContainerLogConsumer,
23{
24 container_name: Option<String>,
25 network: Option<String>,
26 log_consumer: Option<L>,
27}
28
29impl<L> ImageExtConfig<L>
30where
31 L: ContainerLogConsumer,
32{
33 #[must_use]
35 pub fn with_container_name(mut self, container_name: String) -> Self {
36 self.container_name = Some(container_name);
37 self
38 }
39
40 #[must_use]
42 pub fn with_network(mut self, name: String) -> Self {
43 self.network = Some(name);
44 self
45 }
46
47 #[must_use]
49 pub fn with_log_consumer(mut self, log_consumer: L) -> Self {
50 self.log_consumer = Some(log_consumer);
51 self
52 }
53
54 pub fn configure_container<RI, I>(self, image: RI) -> ContainerRequest<I>
57 where
58 RI: Into<ContainerRequest<I>>,
59 I: Image,
60 {
61 let mut container_request = image.into();
62
63 if let Some(container_name) = self.container_name {
64 container_request = container_request.with_container_name(container_name);
65 }
66
67 if let Some(network) = self.network {
68 container_request = container_request.with_network(network);
69 }
70
71 if let Some(log_consumer) = self.log_consumer {
72 container_request = container_request.with_log_consumer(log_consumer);
73 }
74
75 container_request
76 }
77}
78
79impl Default for ImageExtConfig<LoggingConsumer> {
80 fn default() -> Self {
81 Self {
82 container_name: None,
83 network: None,
84 log_consumer: None,
85 }
86 }
87}
88
89#[derive(Debug, Copy, Clone)]
92pub struct ImageConfig {
93 pub should_expose_ports: bool,
98}
99
100impl Default for ImageConfig {
101 fn default() -> Self {
104 Self {
105 should_expose_ports: true,
106 }
107 }
108}
109
110impl ImageConfig {
111 #[must_use]
113 pub fn with_expose_ports(mut self, should_expose_ports: bool) -> Self {
114 self.should_expose_ports = should_expose_ports;
115 self
116 }
117}
118
119#[derive(Debug)]
121pub struct ContainerConfig<L>
122where
123 L: ContainerLogConsumer,
124{
125 pub image_config: ImageConfig,
127
128 pub image_ext_config: ImageExtConfig<L>,
130}
131
132impl<L> ContainerConfig<L>
133where
134 L: ContainerLogConsumer,
135{
136 pub fn new(image_config: ImageConfig, image_ext_config: ImageExtConfig<L>) -> Self {
138 Self {
139 image_config,
140 image_ext_config,
141 }
142 }
143
144 pub fn configure_container<RI, I>(self, image: RI) -> ContainerRequest<I>
146 where
147 RI: Into<ContainerRequest<I>>,
148 I: Image,
149 {
150 self.image_ext_config.configure_container(image)
151 }
152}
153
154impl Default for ContainerConfig<LoggingConsumer> {
155 fn default() -> Self {
156 Self {
157 image_config: ImageConfig::default(),
158 image_ext_config: ImageExtConfig::default(),
159 }
160 }
161}
162
163pub async fn get_bridge_gateway_ip() -> Result<String> {
176 let docker = Docker::connect_with_socket_defaults()?;
177
178 let mut filters = HashMap::new();
179 let network_name = "bridge".to_string();
180 filters.insert("name".to_string(), vec![network_name.clone()]);
181 let list_options = ListNetworksOptions {
182 filters: Some(filters),
183 };
184
185 let networks = docker.list_networks(Some(list_options)).await?;
186 let bridge = networks
187 .first()
188 .ok_or_else(|| anyhow::anyhow!("can't find network '{network_name}'"))?;
189
190 let gateway_ip = bridge
191 .ipam
192 .as_ref()
193 .and_then(|ipam| ipam.config.as_ref())
194 .and_then(|configs| configs.first())
195 .and_then(|config| config.gateway.as_ref())
196 .ok_or_else(|| {
197 anyhow::anyhow!(
198 "can't get the IPAM gateway IP for network. \
199 full network object:\n{bridge:#?}"
200 )
201 })?;
202
203 Ok(gateway_ip.clone())
204}