testcontainers_modules/hashicorp_vault/
mod.rs

1use std::{borrow::Cow, collections::BTreeMap};
2
3use testcontainers::{core::WaitFor, Image};
4
5const DEFAULT_IMAGE_NAME: &str = "hashicorp/vault";
6const DEFAULT_IMAGE_TAG: &str = "1.17";
7
8/// Module to work with [`Hashicorp Vault`] inside of tests.
9///
10/// This module is based on the official [`Hashicorp Vault docker image`].
11///
12/// # Example
13/// ```
14/// use testcontainers_modules::{hashicorp_vault, testcontainers::runners::SyncRunner};
15///
16/// let vault = hashicorp_vault::HashicorpVault::default().start().unwrap();
17/// let http_port = vault.get_host_port_ipv4(8200).unwrap();
18///
19/// // do something with the running vault instance..
20/// ```
21///
22/// [`Hashicorp Vault`]: https://github.com/hashicorp/vault
23/// [`Hashicorp Vault docker image`]: https://hub.docker.com/r/hashicorp/vault
24/// [`Hashicorp Vault commands`]: https://developer.hashicorp.com/vault/docs/commands
25#[derive(Debug, Clone)]
26pub struct HashicorpVault {
27    name: String,
28    tag: String,
29    env_vars: BTreeMap<String, String>,
30}
31
32impl Default for HashicorpVault {
33    /**
34     * Starts an in-memory instance in dev mode, with horrible token values.
35     * Obviously not to be emulated in production.
36     */
37    fn default() -> Self {
38        let mut env_vars = BTreeMap::new();
39        env_vars.insert("VAULT_DEV_ROOT_TOKEN_ID".to_string(), "myroot".to_string());
40        HashicorpVault::new(
41            DEFAULT_IMAGE_NAME.to_string(),
42            DEFAULT_IMAGE_TAG.to_string(),
43            env_vars,
44        )
45    }
46}
47
48impl HashicorpVault {
49    fn new(name: String, tag: String, env_vars: BTreeMap<String, String>) -> Self {
50        HashicorpVault {
51            name,
52            tag,
53            env_vars,
54        }
55    }
56}
57
58impl Image for HashicorpVault {
59    fn name(&self) -> &str {
60        &self.name
61    }
62
63    fn tag(&self) -> &str {
64        &self.tag
65    }
66
67    fn ready_conditions(&self) -> Vec<WaitFor> {
68        vec![WaitFor::message_on_stdout("Vault server started!")]
69    }
70
71    fn env_vars(
72        &self,
73    ) -> impl IntoIterator<Item = (impl Into<Cow<'_, str>>, impl Into<Cow<'_, str>>)> {
74        &self.env_vars
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use serde::{Deserialize, Serialize};
81    use vaultrs::{
82        client::{VaultClient, VaultClientSettingsBuilder},
83        kv2,
84    };
85
86    use super::*;
87    use crate::testcontainers::runners::AsyncRunner;
88
89    // Create and read secrets
90    #[derive(Debug, Deserialize, Serialize)]
91    struct MySecret {
92        key: String,
93        password: String,
94    }
95
96    #[tokio::test]
97    async fn hashicorp_vault_secret_set_and_read(
98    ) -> Result<(), Box<dyn std::error::Error + 'static>> {
99        let vault = HashicorpVault::default().start().await.unwrap();
100        let endpoint = format!("http://0.0.0.0:{}", vault.get_host_port_ipv4(8200).await?);
101
102        // Create a client
103        let client = VaultClient::new(
104            VaultClientSettingsBuilder::default()
105                .address(endpoint)
106                .token("myroot")
107                .build()
108                .unwrap(),
109        )
110        .unwrap();
111
112        let secret = MySecret {
113            key: "super".to_string(),
114            password: "secret".to_string(),
115        };
116        kv2::set(&client, "secret", "mysecret", &secret).await?;
117
118        let secret: MySecret = kv2::read(&client, "secret", "mysecret").await.unwrap();
119        assert_eq!(secret.key, "super");
120        assert_eq!(secret.password, "secret");
121        Ok(())
122    }
123}