Skip to main content

opensearch_testcontainer/
lib.rs

1use std::{borrow::Cow, collections::HashMap};
2
3use testcontainers::{core::*, Image};
4
5const NAME: &str = "opensearchproject/opensearch";
6const TAG: &str = "3.1.0";
7
8#[derive(Debug, Clone)]
9pub struct OpenSearch {
10    image_name: String,
11    env_vars: HashMap<String, String>,
12    tag: String,
13    username: String,
14    password: String,
15}
16
17impl OpenSearch {
18    pub fn with_name(mut self, name: &str) -> Self {
19        self.image_name = name.to_owned();
20        self
21    }
22
23    pub fn with_tag(mut self, tag: &str) -> Self {
24        self.tag = tag.to_owned();
25        self
26    }
27
28    pub fn with_cluster_name(mut self, name: &str) -> Self {
29        self.env_vars
30            .insert("cluster.name".to_owned(), name.to_owned());
31        self
32    }
33
34    pub fn with_env_var(mut self, key: &str, value: &str) -> Self {
35        self.env_vars.insert(key.to_owned(), value.to_owned());
36        self
37    }
38
39    pub fn username(&self) -> String {
40        self.username.to_owned()
41    }
42
43    pub fn password(&self) -> String {
44        self.password.to_owned()
45    }
46}
47
48impl Default for OpenSearch {
49    fn default() -> Self {
50        let mut env_vars = HashMap::new();
51        env_vars.insert("discovery.type".to_owned(), "single-node".to_owned());
52        env_vars.insert(
53            "OPENSEARCH_INITIAL_ADMIN_PASSWORD".to_owned(),
54            "?qbr9:6Y7nk6".to_owned(),
55        );
56        OpenSearch {
57            image_name: NAME.to_owned(),
58            env_vars,
59            tag: TAG.to_owned(),
60            username: "admin".to_owned(),
61            password: "?qbr9:6Y7nk6".to_owned(),
62        }
63    }
64}
65
66impl Image for OpenSearch {
67    fn name(&self) -> &str {
68        &self.image_name.as_str()
69    }
70
71    fn tag(&self) -> &str {
72        self.tag.as_str()
73    }
74
75    fn ready_conditions(&self) -> Vec<WaitFor> {
76        vec![
77            WaitFor::message_on_stdout("[YELLOW] to [GREEN]"),
78            WaitFor::message_on_stdout("ML configuration initialized successfully"),
79        ]
80    }
81
82    fn env_vars(
83        &self,
84    ) -> impl IntoIterator<Item = (impl Into<Cow<'_, str>>, impl Into<Cow<'_, str>>)> {
85        Box::new(self.env_vars.iter())
86    }
87
88    fn expose_ports(&self) -> &[ContainerPort] {
89        &[
90            ContainerPort::Tcp(9200),
91            ContainerPort::Tcp(9300),
92            ContainerPort::Tcp(9600),
93        ]
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use testcontainers::runners::AsyncRunner;
100
101    use crate::OpenSearch as OpenSearchImage;
102
103    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
104    async fn opensearch_default() {
105        let os_image = OpenSearchImage::default();
106        let opensearch = os_image.clone().start().await.unwrap();
107        let host_port = opensearch.get_host_port_ipv4(9200).await.unwrap();
108
109        let client = reqwest::Client::builder()
110            .danger_accept_invalid_certs(true)
111            .build()
112            .unwrap();
113
114        let response = client
115            .get(format!("https://127.0.0.1:{host_port}"))
116            .header("content-type", "application/json")
117            .basic_auth(os_image.username(), Some(os_image.password()))
118            .send()
119            .await
120            .unwrap();
121
122        let response = response.text().await.unwrap();
123        println!("response: {}", response);
124        let response: serde_json::Value = serde_json::from_str(&response).unwrap();
125
126        assert_eq!(response["version"]["number"], "3.1.0");
127    }
128}