rustainers/images/
minio.rs

1use std::time::Duration;
2
3use crate::runner::RunnerError;
4use crate::{
5    Container, ExposedPort, HealthCheck, ImageName, Port, PortError, RunnableContainer,
6    RunnableContainerBuilder, ToRunnableContainer,
7};
8
9const DATA: &str = "/data";
10
11const MINIO_IMAGE: &ImageName = &ImageName::new("docker.io/minio/minio");
12
13const PORT: Port = Port(9000);
14
15const CONSOLE_PORT: Port = Port(9001);
16
17/// A `Minio` image
18///
19/// # Example
20///
21/// ```rust, no_run
22/// # async fn run() -> anyhow::Result<()> {
23/// use rustainers::images::Minio;
24///
25/// let default_image = Minio::default();
26///
27/// let custom_image = Minio::default()
28///        .with_tag("RELEASE.2023-10-25T06-33-25Z");
29///
30/// # let runner = rustainers::runner::Runner::auto()?;
31/// // ...
32/// let container = runner.start(default_image).await?;
33/// let endpoint = container.endpoint().await?;
34/// // ...
35/// # Ok(())
36/// # }
37///```
38#[derive(Debug)]
39pub struct Minio {
40    image: ImageName,
41    port: ExposedPort,
42    console_port: ExposedPort,
43}
44
45impl Minio {
46    /// Set the image tag
47    #[must_use]
48    pub fn with_tag(self, tag: impl Into<String>) -> Self {
49        let Self { mut image, .. } = self;
50        image.set_tag(tag);
51        Self { image, ..self }
52    }
53
54    /// Set the image digest
55    #[must_use]
56    pub fn with_digest(self, digest: impl Into<String>) -> Self {
57        let Self { mut image, .. } = self;
58        image.set_digest(digest);
59        Self { image, ..self }
60    }
61
62    /// Set the port mapping
63    #[must_use]
64    pub fn with_port(mut self, port: ExposedPort) -> Self {
65        self.port = port;
66        self
67    }
68}
69
70impl Minio {
71    /// The region
72    #[must_use]
73    pub fn region(&self) -> &'static str {
74        "us-east-1"
75    }
76
77    /// The access key id
78    #[must_use]
79    pub fn access_key_id(&self) -> &'static str {
80        "minioadmin"
81    }
82
83    /// The secret access key
84    #[must_use]
85    pub fn secret_access_key(&self) -> &'static str {
86        "minioadmin"
87    }
88}
89
90impl Container<Minio> {
91    /// Create a bucket
92    ///
93    /// # Errors
94    ///
95    /// Could fail if we cannot create the bucket
96    pub async fn create_s3_bucket(&self, name: &str) -> Result<(), RunnerError> {
97        let bucket = format!("{DATA}/{name}");
98        self.runner.exec(self, ["mc", "mb", &bucket]).await?;
99        self.runner
100            .exec(self, ["mc", "anonymous", "set", "public", &bucket])
101            .await?;
102
103        Ok(())
104    }
105
106    /// Get endpoint URL
107    ///
108    /// # Errors
109    ///
110    /// Could fail if the port is not bind
111    pub async fn endpoint(&self) -> Result<String, PortError> {
112        let port = self.port.host_port().await?;
113
114        let host_ip = self.runner.container_host_ip().await?;
115        let url = format!("http://{host_ip}:{port}");
116
117        Ok(url)
118    }
119
120    /// Get console endpoint URL
121    ///
122    /// # Errors
123    ///
124    /// Could fail if the console port is not bind
125    pub async fn console_endpoint(&self) -> Result<String, PortError> {
126        let port = self.console_port.host_port().await?;
127        let host_ip = self.runner.container_host_ip().await?;
128        let url = format!("http://{host_ip}:{port}");
129
130        Ok(url)
131    }
132}
133
134impl Default for Minio {
135    fn default() -> Self {
136        Minio {
137            image: MINIO_IMAGE.clone(),
138            port: ExposedPort::new(PORT),
139            console_port: ExposedPort::new(CONSOLE_PORT),
140        }
141    }
142}
143
144impl ToRunnableContainer for Minio {
145    fn to_runnable(&self, builder: RunnableContainerBuilder) -> RunnableContainer {
146        builder
147            .with_image(self.image.clone())
148            .with_wait_strategy({
149                HealthCheck::builder()
150                    .with_command("mc ping --exit --json local".to_string())
151                    .with_interval(Duration::from_millis(250))
152                    .build()
153            })
154            .with_command(["server", DATA])
155            .with_port_mappings([self.port.clone(), self.console_port.clone()])
156            .build()
157    }
158}