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#[derive(Debug, Clone)]
19pub struct ImageExtConfig<L>
20where
21 L: ContainerLogConsumer,
22{
23 container_name: Option<String>,
24 network: Option<String>,
25 log_consumer: Option<L>,
26}
27
28impl<L> ImageExtConfig<L>
29where
30 L: ContainerLogConsumer,
31{
32 #[must_use]
34 pub fn with_container_name(mut self, container_name: String) -> Self {
35 self.container_name = Some(container_name);
36 self
37 }
38
39 #[must_use]
41 pub fn with_network(mut self, name: String) -> Self {
42 self.network = Some(name);
43 self
44 }
45
46 #[must_use]
48 pub fn with_log_consumer(mut self, log_consumer: L) -> Self {
49 self.log_consumer = Some(log_consumer);
50 self
51 }
52
53 pub fn configure_container<RI, I>(self, image: RI) -> ContainerRequest<I>
56 where
57 RI: Into<ContainerRequest<I>>,
58 I: Image,
59 {
60 let mut container_request = image.into();
61
62 if let Some(container_name) = self.container_name {
63 container_request = container_request.with_container_name(container_name);
64 }
65
66 if let Some(network) = self.network {
67 container_request = container_request.with_network(network);
68 }
69
70 if let Some(log_consumer) = self.log_consumer {
71 container_request = container_request.with_log_consumer(log_consumer);
72 }
73
74 container_request
75 }
76}
77
78impl Default for ImageExtConfig<LoggingConsumer> {
79 fn default() -> Self {
80 Self {
81 container_name: None,
82 network: None,
83 log_consumer: None,
84 }
85 }
86}
87
88#[derive(Debug, Copy, Clone)]
91pub struct ImageConfig {
92 pub should_expose_ports: bool,
97}
98
99impl Default for ImageConfig {
100 fn default() -> Self {
103 Self {
104 should_expose_ports: true,
105 }
106 }
107}
108
109impl ImageConfig {
110 #[must_use]
112 pub fn with_expose_ports(mut self, should_expose_ports: bool) -> Self {
113 self.should_expose_ports = should_expose_ports;
114 self
115 }
116}
117
118#[derive(Debug)]
120pub struct ContainerConfig<L>
121where
122 L: ContainerLogConsumer,
123{
124 pub image_config: ImageConfig,
126
127 pub image_ext_config: ImageExtConfig<L>,
129}
130
131impl<L> ContainerConfig<L>
132where
133 L: ContainerLogConsumer,
134{
135 pub fn new(image_config: ImageConfig, image_ext_config: ImageExtConfig<L>) -> Self {
137 Self {
138 image_config,
139 image_ext_config,
140 }
141 }
142
143 pub fn configure_container<RI, I>(self, image: RI) -> ContainerRequest<I>
145 where
146 RI: Into<ContainerRequest<I>>,
147 I: Image,
148 {
149 self.image_ext_config.configure_container(image)
150 }
151}
152
153impl Default for ContainerConfig<LoggingConsumer> {
154 fn default() -> Self {
155 Self {
156 image_config: ImageConfig::default(),
157 image_ext_config: ImageExtConfig::default(),
158 }
159 }
160}
161
162pub async fn get_bridge_gateway_ip() -> Result<String> {
175 let docker = Docker::connect_with_socket_defaults()?;
176
177 let mut filters = HashMap::new();
178 let network_name = "bridge".to_string();
179 filters.insert("name".to_string(), vec![network_name.clone()]);
180 let list_options = ListNetworksOptions {
181 filters: Some(filters),
182 };
183
184 let networks = docker.list_networks(Some(list_options)).await?;
185 let bridge = networks
186 .first()
187 .ok_or_else(|| anyhow::anyhow!("can't find network '{network_name}'"))?;
188
189 let gateway_ip = bridge
190 .ipam
191 .as_ref()
192 .and_then(|ipam| ipam.config.as_ref())
193 .and_then(|configs| configs.first())
194 .and_then(|config| config.gateway.as_ref())
195 .ok_or_else(|| {
196 anyhow::anyhow!(
197 "can't get the IPAM gateway IP for network. \
198 full network object:\n{bridge:#?}"
199 )
200 })?;
201
202 Ok(gateway_ip.to_string())
203}