Skip to content

Commit 8837e07

Browse files
committed
Add support for custom configuration and latest image tag (closes testcontainers#314)
1 parent 0b83d15 commit 8837e07

File tree

1 file changed

+157
-13
lines changed

1 file changed

+157
-13
lines changed

src/valkey/mod.rs

+157-13
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
use testcontainers::{
2-
core::{ContainerPort, WaitFor},
3-
Image,
4-
};
1+
use testcontainers::{core::{ContainerPort, WaitFor}, Image, CopyDataSource, CopyToContainer,};
2+
use std::collections::BTreeMap;
3+
use std::borrow::Cow;
54

65
const NAME: &str = "valkey/valkey";
7-
const TAG: &str = "8.0.1-alpine";
6+
const TAG: &str = "8.0.2-alpine";
87

98
/// Default port (6379) on which Valkey is exposed
109
pub const VALKEY_PORT: ContainerPort = ContainerPort::Tcp(6379);
@@ -42,10 +41,63 @@ pub const VALKEY_PORT: ContainerPort = ContainerPort::Tcp(6379);
4241
/// [`VALKEY_PORT`]: super::VALKEY_PORT
4342
#[derive(Debug, Default, Clone)]
4443
pub struct Valkey {
45-
/// (remove if there is another variable)
46-
/// Field is included to prevent this struct to be a unit struct.
47-
/// This allows extending functionality (and thus further variables) without breaking changes
48-
_priv: (),
44+
env_vars: BTreeMap<String, String>,
45+
tag: Option<String>,
46+
copy_to_container: Vec<CopyToContainer>,
47+
}
48+
49+
impl Valkey {
50+
/// Create a new Valkey instance with the latest image.
51+
///
52+
/// # Example
53+
/// ```
54+
/// use testcontainers_modules::{
55+
/// testcontainers::runners::SyncRunner,
56+
/// valkey::{Valkey, VALKEY_PORT},
57+
/// };
58+
///
59+
/// let valkey_instance = Valkey::latest().start().unwrap();
60+
/// ```
61+
pub fn latest() -> Self {
62+
Self {
63+
tag: Some("latest".to_string()),
64+
..Default::default()
65+
}
66+
}
67+
68+
/// Add extra flags by passing additional start arguments.
69+
///
70+
/// # Example
71+
/// ```
72+
/// use testcontainers_modules::{
73+
/// testcontainers::runners::SyncRunner,
74+
/// valkey::{Valkey, VALKEY_PORT},
75+
/// };
76+
///
77+
/// let valkey_instance = Valkey::default().with_valkey_extra_flags("--maxmemory 2mb").start().unwrap();
78+
/// ```
79+
pub fn with_valkey_extra_flags(self, valkey_extra_flags: &str) -> Self {
80+
let mut env_vars = self.env_vars;
81+
env_vars.insert("VALKEY_EXTRA_FLAGS".to_string(), valkey_extra_flags.to_string());
82+
Self { env_vars, tag: self.tag, copy_to_container: self.copy_to_container }
83+
}
84+
85+
/// Add custom valkey configuration.
86+
///
87+
/// # Example
88+
/// ```
89+
/// use testcontainers_modules::{
90+
/// testcontainers::runners::SyncRunner,
91+
/// valkey::{Valkey, VALKEY_PORT},
92+
/// };
93+
///
94+
/// let valkey_instance = Valkey::default().with_valkey_conf("maxmemory 2mb".to_string().into_bytes(),).start().unwrap();
95+
/// ```
96+
pub fn with_valkey_conf(self, valky_conf: impl Into<CopyDataSource>) -> Self {
97+
let mut copy_to_container = self.copy_to_container;
98+
copy_to_container.push(CopyToContainer::new(valky_conf.into(), "/usr/local/etc/valkey/valkey.conf"));
99+
Self { env_vars: self.env_vars, tag: self.tag, copy_to_container }
100+
}
49101
}
50102

51103
impl Image for Valkey {
@@ -54,28 +106,76 @@ impl Image for Valkey {
54106
}
55107

56108
fn tag(&self) -> &str {
57-
TAG
109+
self.tag.as_deref().unwrap_or(TAG)
58110
}
59111

60112
fn ready_conditions(&self) -> Vec<WaitFor> {
61113
vec![WaitFor::message_on_stdout("Ready to accept connections")]
62114
}
115+
116+
fn env_vars(&self,) -> impl IntoIterator<Item = (impl Into<Cow<'_, str>>, impl Into<Cow<'_, str>>)> {
117+
&self.env_vars
118+
}
119+
120+
fn copy_to_sources(&self) -> impl IntoIterator<Item = &CopyToContainer> {
121+
&self.copy_to_container
122+
}
123+
124+
fn cmd(&self) -> impl IntoIterator<Item = impl Into<Cow<'_, str>>> {
125+
let command;
126+
if self.copy_to_container.len() > 0 {
127+
command = vec!["valkey-server", "/usr/local/etc/valkey/valkey.conf"];
128+
} else {
129+
command = Vec::new();
130+
}
131+
command
132+
}
63133
}
64134

65135
#[cfg(test)]
66136
mod tests {
67137
use redis::Commands;
68-
69-
use crate::{testcontainers::runners::SyncRunner, valkey::Valkey};
138+
use std::collections::HashMap;
139+
use testcontainers::Image;
140+
use crate::{testcontainers::runners::SyncRunner, valkey::Valkey, valkey::VALKEY_PORT, valkey::TAG};
70141

71142
#[test]
72143
fn valkey_fetch_an_integer() -> Result<(), Box<dyn std::error::Error + 'static>> {
73144
let _ = pretty_env_logger::try_init();
74145
let node = Valkey::default().start()?;
146+
147+
let tag = node.image().tag.clone();
148+
assert_eq!(None, tag);
149+
let tag_from_method = node.image().tag();
150+
assert_eq!(TAG, tag_from_method);
151+
assert_eq!(0, node.image().copy_to_container.len());
152+
75153
let host_ip = node.get_host()?;
76-
let host_port = node.get_host_port_ipv4(6379)?;
154+
let host_port = node.get_host_port_ipv4(VALKEY_PORT)?;
77155
let url = format!("redis://{host_ip}:{host_port}");
156+
let client = redis::Client::open(url.as_ref()).unwrap();
157+
let mut con = client.get_connection().unwrap();
158+
159+
con.set::<_, _, ()>("my_key", 42).unwrap();
160+
let result: i64 = con.get("my_key").unwrap();
161+
assert_eq!(42, result);
162+
Ok(())
163+
}
164+
165+
#[test]
166+
fn valkey_latest() -> Result<(), Box<dyn std::error::Error + 'static>> {
167+
let _ = pretty_env_logger::try_init();
168+
let node = Valkey::latest().start()?;
78169

170+
let tag = node.image().tag.clone();
171+
assert_eq!(Some("latest".to_string()), tag);
172+
let tag_from_method = node.image().tag();
173+
assert_eq!("latest", tag_from_method);
174+
assert_eq!(0, node.image().copy_to_container.len());
175+
176+
let host_ip = node.get_host()?;
177+
let host_port = node.get_host_port_ipv4(VALKEY_PORT)?;
178+
let url = format!("redis://{host_ip}:{host_port}");
79179
let client = redis::Client::open(url.as_ref()).unwrap();
80180
let mut con = client.get_connection().unwrap();
81181

@@ -84,4 +184,48 @@ mod tests {
84184
assert_eq!(42, result);
85185
Ok(())
86186
}
187+
188+
#[test]
189+
fn valkey_extra_flags() -> Result<(), Box<dyn std::error::Error + 'static>> {
190+
let _ = pretty_env_logger::try_init();
191+
let node = Valkey::default().with_valkey_extra_flags("--maxmemory 2mb").start()?;
192+
let tag = node.image().tag.clone();
193+
assert_eq!(None, tag);
194+
let tag_from_method = node.image().tag();
195+
assert_eq!(TAG, tag_from_method);
196+
assert_eq!(0, node.image().copy_to_container.len());
197+
198+
let host_ip = node.get_host()?;
199+
let host_port = node.get_host_port_ipv4(VALKEY_PORT)?;
200+
let url = format!("redis://{host_ip}:{host_port}");
201+
202+
let client = redis::Client::open(url.as_ref()).unwrap();
203+
let mut con = client.get_connection().unwrap();
204+
let max_memory: HashMap<String, isize> = redis::cmd("CONFIG").arg("GET").arg("maxmemory").query(&mut con).unwrap();
205+
let max = *max_memory.get("maxmemory").unwrap();
206+
assert_eq!(2097152, max);
207+
Ok(())
208+
}
209+
210+
#[test]
211+
fn valkey_conf() -> Result<(), Box<dyn std::error::Error + 'static>> {
212+
let _ = pretty_env_logger::try_init();
213+
let node = Valkey::default().with_valkey_conf("maxmemory 2mb".to_string().into_bytes(),).start()?;
214+
let tag = node.image().tag.clone();
215+
assert_eq!(None, tag);
216+
let tag_from_method = node.image().tag();
217+
assert_eq!(TAG, tag_from_method);
218+
assert_eq!(1, node.image().copy_to_container.len());
219+
220+
let host_ip = node.get_host()?;
221+
let host_port = node.get_host_port_ipv4(VALKEY_PORT)?;
222+
let url = format!("redis://{host_ip}:{host_port}");
223+
224+
let client = redis::Client::open(url.as_ref()).unwrap();
225+
let mut con = client.get_connection().unwrap();
226+
let max_memory: HashMap<String, isize> = redis::cmd("CONFIG").arg("GET").arg("maxmemory").query(&mut con).unwrap();
227+
let max = *max_memory.get("maxmemory").unwrap();
228+
assert_eq!(2097152, max);
229+
Ok(())
230+
}
87231
}

0 commit comments

Comments
 (0)