1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use std::collections::HashMap;

use testcontainers::{core::WaitFor, Image};

const NAME: &str = "opensearchproject/opensearch";
const TAG: &str = "2.11.1";

#[derive(Debug, Clone)]
pub struct OpenSearch {
  image_name: String,
  env_vars: HashMap<String, String>,
  tag: String,
  username: String,
  password: String,
}

impl OpenSearch {
  pub fn with_name(mut self, name: &str) -> Self {
    self.image_name = name.to_owned();
    self
  }

  pub fn with_tag(mut self, tag: &str) -> Self {
    self.tag = tag.to_owned();
    self
  }

  pub fn with_cluster_name(mut self, name: &str) -> Self {
    self.env_vars.insert("cluster.name".to_owned(), name.to_owned());
    self
  }

  pub fn with_env_var(mut self, key: &str, value: &str) -> Self {
    self.env_vars.insert(key.to_owned(), value.to_owned());
    self
  }

  pub fn username(&self) -> String {
    self.username.to_owned()
  }

  pub fn password(&self) -> String {
    self.password.to_owned()
  }
}

impl Default for OpenSearch {
  fn default() -> Self {
    let mut env_vars = HashMap::new();
    env_vars.insert("discovery.type".to_owned(), "single-node".to_owned());
    OpenSearch {
      image_name: NAME.to_owned(),
      env_vars,
      tag: TAG.to_owned(),
      username: "admin".to_owned(),
      password: "admin".to_owned(),
    }
  }
}

impl Image for OpenSearch {
  type Args = ();

  fn name(&self) -> String {
    self.image_name.to_owned()
  }

  fn tag(&self) -> String {
    self.tag.to_owned()
  }

  fn ready_conditions(&self) -> Vec<WaitFor> {
    vec![
      WaitFor::message_on_stdout("[YELLOW] to [GREEN]"),
      WaitFor::message_on_stdout("ML configuration initialized successfully"),
    ]
  }

  fn env_vars(&self) -> Box<dyn Iterator<Item = (&String, &String)> + '_> {
    Box::new(self.env_vars.iter())
  }

  fn expose_ports(&self) -> Vec<u16> {
    vec![9200, 9300, 9600]
  }
}

#[cfg(test)]
mod tests {
  use testcontainers::clients;

  use crate::OpenSearch as OpenSearchImage;

  #[test]
  fn opensearch_default() {
    let docker = clients::Cli::default();
    let os_image = OpenSearchImage::default();
    let node: testcontainers::Container<'_, OpenSearchImage> = docker.run(os_image.clone());
    let host_port = node.get_host_port_ipv4(9200);

    let client = reqwest::blocking::Client::builder()
      .danger_accept_invalid_certs(true)
      .build()
      .unwrap();

    let response = client
      .get(format!("https://127.0.0.1:{host_port}"))
      .header("content-type", "application/json")
      .basic_auth(os_image.username(), Some(os_image.password()))
      .send()
      .unwrap();

    let response = response.text().unwrap();
    println!("response: {}", response);
    let response: serde_json::Value = serde_json::from_str(&response).unwrap();

    assert_eq!(response["version"]["number"], "2.11.1");
  }
}