pub struct RedisClusterTemplate { /* private fields */ }Expand description
Redis Cluster template for automatic multi-node cluster setup
Implementations§
Source§impl RedisClusterTemplate
impl RedisClusterTemplate
Sourcepub fn new(name: impl Into<String>) -> Self
pub fn new(name: impl Into<String>) -> Self
Create a new Redis Cluster template with default settings
Sourcepub fn from_env(name: impl Into<String>) -> Self
pub fn from_env(name: impl Into<String>) -> Self
Create a new Redis Cluster template with settings from environment variables.
Falls back to defaults if environment variables are not set.
§Environment Variables
REDIS_CLUSTER_PORT_BASE: Base port for Redis nodes (default: 7000)REDIS_CLUSTER_NUM_MASTERS: Number of master nodes (default: 3)REDIS_CLUSTER_NUM_REPLICAS: Number of replicas per master (default: 0)REDIS_CLUSTER_PASSWORD: Password for cluster authentication (optional)
§Examples
use docker_wrapper::RedisClusterTemplate;
// Uses environment variables if set, otherwise uses defaults
let template = RedisClusterTemplate::from_env("my-cluster");Sourcepub fn get_port_base(&self) -> u16
pub fn get_port_base(&self) -> u16
Get the configured port base
Sourcepub fn get_num_masters(&self) -> usize
pub fn get_num_masters(&self) -> usize
Get the configured number of masters
Sourcepub fn get_num_replicas(&self) -> usize
pub fn get_num_replicas(&self) -> usize
Get the configured number of replicas per master
Sourcepub fn num_masters(self, masters: usize) -> Self
pub fn num_masters(self, masters: usize) -> Self
Set the number of master nodes (minimum 3)
Sourcepub fn num_replicas(self, replicas: usize) -> Self
pub fn num_replicas(self, replicas: usize) -> Self
Set the number of replicas per master
Sourcepub fn cluster_announce_ip(self, ip: impl Into<String>) -> Self
pub fn cluster_announce_ip(self, ip: impl Into<String>) -> Self
Set the IP to announce to other cluster nodes
Sourcepub fn with_persistence(self, volume_prefix: impl Into<String>) -> Self
pub fn with_persistence(self, volume_prefix: impl Into<String>) -> Self
Enable persistence with volume prefix
Sourcepub fn memory_limit(self, limit: impl Into<String>) -> Self
pub fn memory_limit(self, limit: impl Into<String>) -> Self
Set memory limit per node
Sourcepub fn cluster_node_timeout(self, timeout: u32) -> Self
pub fn cluster_node_timeout(self, timeout: u32) -> Self
Set cluster node timeout in milliseconds
Sourcepub fn network_mode(self, mode: impl Into<String>) -> Self
pub fn network_mode(self, mode: impl Into<String>) -> Self
Select the container network mode for cluster nodes.
Only "host" is special-cased: it is equivalent to calling
host_network (see that method for the Linux-only
caveats). Any other value leaves the cluster on its default,
automatically managed bridge network, since a multi-node cluster relies
on a private bridge for inter-node DNS and announce-IP wiring.
Sourcepub fn host_network(self) -> Self
pub fn host_network(self) -> Self
Run every cluster node with --network host.
In host networking mode each node shares the host’s network namespace,
so its Redis port is a real host port and no published port mapping or
--cluster-announce-ip ceremony is needed. To keep the nodes from
colliding on a single shared namespace, each node listens on a distinct
port derived from the port base (port_base + index), which is exactly
the host port reported by node and
RedisClusterConnection::from_template. The private bridge network is
not created in this mode.
§Platform support
Host networking is a Linux-only Docker feature. On Docker Desktop
for macOS and Windows the daemon runs inside a Linux VM, so
--network host binds ports inside that VM rather than on your machine:
the option is effectively a no-op there and the cluster will not be
reachable from the host. This method does not return an error on
non-Linux hosts (the Docker CLI accepts the flag regardless of backend);
only use host mode against a native Linux daemon, such as a Linux CI
runner.
§Example
use docker_wrapper::RedisClusterTemplate;
// Linux only: nodes are reachable on localhost:7000, 7001, 7002 with no
// bridge network and no announce-ip wiring.
let cluster = RedisClusterTemplate::new("host-cluster")
.num_masters(3)
.port_base(7000)
.host_network();Sourcepub fn auto_remove(self) -> Self
pub fn auto_remove(self) -> Self
Enable auto-remove when stopped
Sourcepub fn with_redis_stack(self) -> Self
pub fn with_redis_stack(self) -> Self
Use Redis Stack instead of standard Redis (includes modules like JSON, Search, Graph, TimeSeries, Bloom).
Uses the redis/redis-stack-server image pinned to a known-good default
tag (7.4.0-v3) rather than latest, so that runs are reproducible.
Call Self::stack_version to pin a different tag, or
Self::custom_redis_image for full control.
Sourcepub fn stack_version(self, tag: impl Into<String>) -> Self
pub fn stack_version(self, tag: impl Into<String>) -> Self
Pin the Redis Stack server image tag (e.g. "7.4.0-v3").
Only affects the image used when Self::with_redis_stack is enabled.
The default is a known-good pinned tag rather than latest, so that runs
are reproducible. A Self::custom_redis_image takes precedence over
this setting.
§Example
use docker_wrapper::RedisClusterTemplate;
let template = RedisClusterTemplate::new("my-cluster")
.with_redis_stack()
.stack_version("7.4.0-v3");Sourcepub fn with_redis_insight(self) -> Self
pub fn with_redis_insight(self) -> Self
Enable RedisInsight GUI for cluster visualization and management.
Uses the redislabs/redisinsight image pinned to a known-good default
tag (2.60) rather than latest, so that runs are reproducible. Call
Self::redis_insight_version to pin a different tag.
Sourcepub fn redis_insight_port(self, port: u16) -> Self
pub fn redis_insight_port(self, port: u16) -> Self
Set the port for RedisInsight UI (default: 8001)
Sourcepub fn redis_insight_version(self, tag: impl Into<String>) -> Self
pub fn redis_insight_version(self, tag: impl Into<String>) -> Self
Pin the RedisInsight image tag (e.g. "2.60").
Only affects the image used when Self::with_redis_insight is enabled.
The default is a known-good pinned tag rather than latest, so that runs
are reproducible.
§Example
use docker_wrapper::RedisClusterTemplate;
let template = RedisClusterTemplate::new("my-cluster")
.with_redis_insight()
.redis_insight_version("2.60");Sourcepub fn custom_redis_image(
self,
image: impl Into<String>,
tag: impl Into<String>,
) -> Self
pub fn custom_redis_image( self, image: impl Into<String>, tag: impl Into<String>, ) -> Self
Use a custom Redis image and tag
Sourcepub fn platform(self, platform: impl Into<String>) -> Self
pub fn platform(self, platform: impl Into<String>) -> Self
Set the platform for the containers (e.g., “linux/arm64”, “linux/amd64”)
Sourcepub fn tls(self, certs_dir: impl Into<String>) -> Self
pub fn tls(self, certs_dir: impl Into<String>) -> Self
Enable TLS for every cluster node, bind-mounting the given host certificate directory read-only into each container.
The directory must contain these files (the same layout used by the
single-node RedisTemplate):
redis.crt– the server certificateredis.key– the server private keyca.crt– the CA certificate used to verify peers
Each node is started with --tls-port set to its data port, --port 0
(plaintext disabled), and --tls-cluster yes/--tls-replication yes so
that the cluster bus and replication links are encrypted too. This mirrors
Redis’s documented cluster-over-TLS layout: unlike the single-node
template, a TLS cluster is always TLS-only on the data port – the
gossip protocol cannot mix plaintext and TLS nodes. The
redis-cli --cluster create, readiness, and inspection calls all connect
with --tls and the mounted certificates.
See RedisTemplate::tls for an openssl
recipe to generate throwaway certificates for local testing.
§Example
use docker_wrapper::RedisClusterTemplate;
let cluster = RedisClusterTemplate::new("tls-cluster").tls("/path/to/certs");Sourcepub fn node_names(&self) -> Vec<String>
pub fn node_names(&self) -> Vec<String>
List the container names for every node in the cluster.
Names follow the deterministic {name}-node-{i} contract, ordered by
node index from 0 to total_nodes() - 1. The first
get_num_masters entries are masters and the
remainder are replicas, mirroring how redis-cli --cluster create
assigns roles.
This is the building block for targeted per-node fault injection: pick a name and pause, partition, or kill exactly that container.
§Examples
use docker_wrapper::RedisClusterTemplate;
let cluster = RedisClusterTemplate::new("chaos").num_masters(3);
assert_eq!(
cluster.node_names(),
vec!["chaos-node-0", "chaos-node-1", "chaos-node-2"],
);Sourcepub fn node(&self, index: usize) -> Option<ClusterNode>
pub fn node(&self, index: usize) -> Option<ClusterNode>
Get a handle to a single node by index.
Returns a ClusterNode describing the node’s container name, the host
port mapped to its Redis port, and its expected role, or None if
index is out of range (index >= total_nodes()).
The returned values are derived from configuration only – this is a
plain accessor that performs no Docker calls. The role is the role
redis-cli --cluster create assigns: indices 0..num_masters are
masters and the rest are replicas. To read the live role from a running
node instead (for example after a failover), use
node_role.
§Examples
use docker_wrapper::{NodeRole, RedisClusterTemplate};
let cluster = RedisClusterTemplate::new("chaos")
.num_masters(3)
.num_replicas(1)
.port_base(7000);
let master = cluster.node(0).expect("node 0 exists");
assert_eq!(master.container_name, "chaos-node-0");
assert_eq!(master.host_port, 7000);
assert_eq!(master.role, NodeRole::Master);
// The first three nodes are masters, the last three are replicas.
let replica = cluster.node(3).expect("node 3 exists");
assert_eq!(replica.host_port, 7003);
assert_eq!(replica.role, NodeRole::Replica);
assert!(cluster.node(6).is_none());Sourcepub async fn node_role(&self, index: usize) -> Result<NodeRole, TemplateError>
pub async fn node_role(&self, index: usize) -> Result<NodeRole, TemplateError>
Query the live role of a single node from the running container.
Runs redis-cli role inside the node’s container and reports whether the
node currently identifies as a master or a replica. Unlike
node, which returns the role assigned at creation time,
this reflects the cluster’s current state (for example after a failover).
This performs a Docker exec and is therefore not free; the cluster must
be running.
§Errors
Returns an error if index is out of range, if the docker exec call
fails (for example the container is not running), or if the role output
cannot be parsed.
§Examples
let cluster = RedisClusterTemplate::new("my-cluster");
cluster.start().await?;
let role = cluster.node_role(0).await?;
println!("node 0 is currently a {:?}", role);Sourcepub async fn cluster_info(&self) -> Result<ClusterInfo, TemplateError>
pub async fn cluster_info(&self) -> Result<ClusterInfo, TemplateError>
Check cluster status
Sourcepub async fn is_ready(&self) -> bool
pub async fn is_ready(&self) -> bool
Check if the cluster is ready (all nodes up, slots assigned).
Returns true if the cluster state is “ok”, false otherwise.
§Examples
let template = RedisClusterTemplate::new("my-cluster");
template.start().await?;
if template.is_ready().await {
println!("Cluster is ready!");
}Sourcepub async fn wait_until_ready(
&self,
timeout: Duration,
) -> Result<(), TemplateError>
pub async fn wait_until_ready( &self, timeout: Duration, ) -> Result<(), TemplateError>
Wait for the cluster to become ready, with a timeout.
Polls the cluster state every 500ms until it reports “ok” or the timeout is exceeded.
§Errors
Returns an error if the timeout is exceeded before the cluster becomes ready.
§Examples
let template = RedisClusterTemplate::new("my-cluster");
template.start().await?;
// Wait up to 30 seconds for the cluster to be ready
template.wait_until_ready(Duration::from_secs(30)).await?;
println!("Cluster is ready!");Sourcepub async fn detect_existing(&self) -> Option<RedisClusterConnection>
pub async fn detect_existing(&self) -> Option<RedisClusterConnection>
Check if a Redis cluster is already running at the configured ports.
This is useful in CI environments where an external cluster may be
provided (e.g., via grokzen/redis-cluster Docker image).
Returns connection info if a cluster is detected, None otherwise.
§Examples
let template = RedisClusterTemplate::from_env("my-cluster");
if let Some(conn) = template.detect_existing().await {
println!("Found existing cluster: {}", conn.nodes_string());
} else {
println!("No existing cluster found");
}Sourcepub async fn start_or_detect(
&self,
timeout: Duration,
) -> Result<RedisClusterConnection, TemplateError>
pub async fn start_or_detect( &self, timeout: Duration, ) -> Result<RedisClusterConnection, TemplateError>
Start the cluster, or use an existing one if already running.
This provides a “best of both worlds” approach for hybrid local/CI setups:
- In CI: Uses the externally-provided cluster without starting new containers
- Locally: Starts a new cluster via docker-wrapper
§Examples
// Works in both CI (uses existing) and local (starts new)
let template = RedisClusterTemplate::from_env("test-cluster");
let conn = template.start_or_detect(Duration::from_secs(60)).await?;
println!("Cluster ready at: {}", conn.nodes_string());