This is an automated email from the ASF dual-hosted git repository.
xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-opendal.git
The following commit(s) were added to refs/heads/main by this push:
new a31ad051e feat(services/redis): add redis cluster support (#2858)
a31ad051e is described below
commit a31ad051ec1d37a05cc80881f3c127e250a99a13
Author: G-XD <[email protected]>
AuthorDate: Tue Aug 15 10:05:16 2023 +0800
feat(services/redis): add redis cluster support (#2858)
* feat(services/redis): support redis cluster
* ci(services/redis): redis cluster & redis cluster tls
* ci(services/redis): use docker for redis cluster
* ci(services/redis): make CI happy
* ci(services/redis): use docker-compose for redis cluster & redis cluster
tls
* chore(services/redis): add license
* ci(services/redis): use bitnami images & move ssl files
* ci(services/redis): use docker-compose for redis & redis tls
* ci(services/redis): fix error
* ci(services/redis): specify ip for redis-cluster-tls
* ci(services/redis): update port of redis-tls test
---
.env.example | 1 +
.github/workflows/service_test_redis.yml | 132 +++++++-------
Cargo.lock | 42 ++++-
core/Cargo.toml | 3 +-
core/src/services/redis/backend.rs | 203 ++++++++++++++++-----
core/src/services/redis/docs.md | 1 +
.../fixtures/docker-compose-redis-cluster-tls.yml | 145 +++++++++++++++
.../fixtures/docker-compose-redis-cluster.yml | 75 ++++++++
.../redis/fixtures/docker-compose-redis-tls.yml | 34 ++++
.../redis/fixtures/docker-compose-redis.yml | 27 +++
core/src/services/redis/fixtures/ssl/ca.crt | 21 +++
core/src/services/redis/fixtures/ssl/ca.key | 28 +++
core/src/services/redis/fixtures/ssl/ca.srl | 1 +
core/src/services/redis/fixtures/ssl/redis.crt | 23 +++
core/src/services/redis/fixtures/ssl/redis.key | 28 +++
core/src/services/redis/fixtures/ssl/redis.v3.ext | 13 ++
16 files changed, 655 insertions(+), 122 deletions(-)
diff --git a/.env.example b/.env.example
index 50ae47ae7..62a3f9473 100644
--- a/.env.example
+++ b/.env.example
@@ -61,6 +61,7 @@ OPENDAL_IPFS_ENDPOINT=http://localhost:8080
# redis
OPENDAL_REDIS_TEST=false
OPENDAL_REDIS_ENDPOINT=tcp://127.0.0.1:6379
+#
OPENDAL_REDIS_CLUSTER_ENDPOINTS=rediss://127.0.0.1:6380,rediss://127.0.0.1:6381,rediss://127.0.0.1:6382,rediss://127.0.0.1:6383,rediss://127.0.0.1:6384,rediss://127.0.0.1:6385
OPENDAL_REDIS_ROOT=/
OPENDAL_REDIS_DB=0
# rocksdb
diff --git a/.github/workflows/service_test_redis.yml
b/.github/workflows/service_test_redis.yml
index d7de6a340..68e1d7c02 100644
--- a/.github/workflows/service_test_redis.yml
+++ b/.github/workflows/service_test_redis.yml
@@ -39,13 +39,12 @@ concurrency:
jobs:
redis:
runs-on: ubuntu-latest
- services:
- redis:
- image: redis
- ports:
- - 6379:6379
steps:
- uses: actions/checkout@v3
+ - name: Setup Redis Server
+ shell: bash
+ working-directory: core
+ run: docker-compose -f
`pwd`/src/services/redis/fixtures/docker-compose-redis.yml up -d
- name: Setup Rust toolchain
uses: ./.github/actions/setup
with:
@@ -65,69 +64,17 @@ jobs:
steps:
- uses: actions/checkout@v3
- - name: Configure Redis with TLS
+ - name: Setup Redis with TLS
+ shell: bash
+ working-directory: core
run: |
- mkdir ssl
-
- # Create CA
-
- openssl req \
- -x509 -new -nodes \
- -keyout ssl/ca.key \
- -sha256 \
- -days 365 \
- -out ssl/ca.crt \
- -subj '/CN=Test Root CA/C=US/ST=Test/L=Test/O=Opendal'
-
- # Create redis certificate
-
- openssl req \
- -new -nodes \
- -out ssl/redis.csr \
- -keyout ssl/redis.key \
- -subj '/CN=Redis certificate/C=US/ST=Test/L=Test/O=Opendal'
-
- cat > ssl/redis.v3.ext << EOF
- authorityKeyIdentifier=keyid,issuer
- basicConstraints=CA:FALSE
- keyUsage = digitalSignature, nonRepudiation, keyEncipherment,
dataEncipherment
- subjectAltName = @alt_names
- [alt_names]
- DNS.1 = localhost
- IP.1 = 127.0.0.1
- EOF
-
- openssl x509 \
- -req \
- -in ssl/redis.csr \
- -CA ssl/ca.crt \
- -CAkey ssl/ca.key \
- -CAcreateserial \
- -out ssl/redis.crt \
- -days 300 \
- -sha256 \
- -extfile ssl/redis.v3.ext
-
- chmod 777 ssl/redis.crt ssl/redis.key # allow the redis docker to
read these files
-
- # Launch redis
-
- docker run -d \
- --rm \
- --name redis \
- --network host \
- --mount type=bind,source=$PWD/ssl,target=/etc/redis/ssl \
- redis \
- --tls-port 6380 \
- --tls-cert-file /etc/redis/ssl/redis.crt \
- --tls-key-file /etc/redis/ssl/redis.key \
- --tls-auth-clients no
# Install the CA in the system
-
- sudo cp ssl/ca.crt /usr/local/share/ca-certificates
+ sudo cp `pwd`/src/services/redis/fixtures/ssl/ca.crt
/usr/local/share/ca-certificates
sudo update-ca-certificates
+ docker-compose -f
`pwd`/src/services/redis/fixtures/docker-compose-redis-tls.yml up -d
+
- name: Setup Rust toolchain
uses: ./.github/actions/setup
with:
@@ -138,10 +85,67 @@ jobs:
run: cargo nextest run redis --features services-redis-rustls
env:
OPENDAL_REDIS_TEST: on
- OPENDAL_REDIS_ENDPOINT: rediss://localhost:6380
+ OPENDAL_REDIS_ENDPOINT: rediss://localhost:6379
OPENDAL_REDIS_ROOT: /
OPENDAL_REDIS_DB: 0
+ redis-cluster:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Setup Rust toolchain
+ uses: ./.github/actions/setup
+ with:
+ need-nextest: true
+ - name: Setup Redis Cluster
+ shell: bash
+ working-directory: core
+ run: docker-compose -f
`pwd`/src/services/redis/fixtures/docker-compose-redis-cluster.yml up -d
+ - name: Test
+ shell: bash
+ working-directory: core
+ run: cargo nextest run redis --features services-redis
+ env:
+ OPENDAL_REDIS_TEST: on
+ OPENDAL_REDIS_CLUSTER_ENDPOINTS:
redis://127.0.0.1:6380/,redis://127.0.0.1:6381/,redis://127.0.0.1:6382/,redis://127.0.0.1:6383/,redis://127.0.0.1:6384/,redis://127.0.0.1:6385/
+ OPENDAL_REDIS_ROOT: /test/opendal
+ OPENDAL_REDIS_DB: 0
+
+ redis-cluster-tls:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Setup Redis Cluster with TLS
+ shell: bash
+ working-directory: core
+ run: |
+
+ # Install the CA in the system
+
+ sudo cp `pwd`/src/services/redis/fixtures/ssl/ca.crt
/usr/local/share/ca-certificates
+ sudo update-ca-certificates
+
+ docker-compose -f
`pwd`/src/services/redis/fixtures/docker-compose-redis-cluster-tls.yml up -d
+
+ - name: Setup Rust toolchain
+ uses: ./.github/actions/setup
+ with:
+ need-nextest: true
+
+ - name: Test
+ shell: bash
+ working-directory: core
+ run: cargo nextest run redis --features services-redis-rustls
+ env:
+ OPENDAL_REDIS_TEST: on
+ OPENDAL_REDIS_CLUSTER_ENDPOINTS:
rediss://127.0.0.1:6380/,rediss://127.0.0.1:6381/,rediss://127.0.0.1:6382/,rediss://127.0.0.1:6383/,rediss://127.0.0.1:6384/,rediss://127.0.0.1:6385/
+ OPENDAL_REDIS_PASSWORD: opendal
+ OPENDAL_REDIS_ROOT: /test/opendal
+ OPENDAL_REDIS_DB: 0
+
dragonfly:
runs-on: ubuntu-latest
services:
diff --git a/Cargo.lock b/Cargo.lock
index 6407a0fe5..ea91cdd01 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1085,6 +1085,12 @@ version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484"
+[[package]]
+name = "crc16"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "338089f42c427b86394a5ee60ff321da23a5c89c9d89514c829687b26359fcff"
+
[[package]]
name = "crc32fast"
version = "1.3.2"
@@ -2252,7 +2258,7 @@ checksum =
"0646026eb1b3eea4cd9ba47912ea5ce9cc07713d105b1a14698f4e6433d348b7"
dependencies = [
"http",
"hyper",
- "rustls 0.21.2",
+ "rustls 0.21.6",
"tokio",
"tokio-rustls",
]
@@ -4725,26 +4731,31 @@ dependencies = [
[[package]]
name = "redis"
-version = "0.23.0"
+version = "0.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ea8c51b5dc1d8e5fd3350ec8167f464ec0995e79f2e90a075b63371500d557f"
+checksum = "ffd6543a7bc6428396845f6854ccf3d1ae8823816592e2cbe74f20f50f209d02"
dependencies = [
"arc-swap",
"async-trait",
"bytes",
"combine",
+ "crc16",
"futures",
"futures-util",
"itoa",
+ "log",
"native-tls",
"percent-encoding",
"pin-project-lite",
- "rustls 0.21.2",
+ "rand 0.8.5",
+ "rustls 0.21.6",
"rustls-native-certs",
"ryu",
"sha1_smol",
+ "socket2 0.4.9",
"tokio",
"tokio-native-tls",
+ "tokio-retry",
"tokio-rustls",
"tokio-util",
"url",
@@ -4877,7 +4888,7 @@ dependencies = [
"once_cell",
"percent-encoding",
"pin-project-lite",
- "rustls 0.21.2",
+ "rustls 0.21.6",
"rustls-native-certs",
"rustls-pemfile",
"serde",
@@ -5047,9 +5058,9 @@ dependencies = [
[[package]]
name = "rustls"
-version = "0.21.2"
+version = "0.21.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e32ca28af694bc1bbf399c33a516dbdf1c90090b8ab23c2bc24f834aa2247f5f"
+checksum = "1d1feddffcfcc0b33f5c6ce9a29e341e4cd59c3f78e7ee45f4a40c038b1d6cbb"
dependencies = [
"log",
"ring",
@@ -5080,9 +5091,9 @@ dependencies = [
[[package]]
name = "rustls-webpki"
-version = "0.100.1"
+version = "0.101.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b"
+checksum = "261e9e0888cba427c3316e6322805653c9425240b6fd96cee7cb671ab70ab8d0"
dependencies = [
"ring",
"untrusted",
@@ -5920,13 +5931,24 @@ dependencies = [
"tokio-util",
]
+[[package]]
+name = "tokio-retry"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f"
+dependencies = [
+ "pin-project",
+ "rand 0.8.5",
+ "tokio",
+]
+
[[package]]
name = "tokio-rustls"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
dependencies = [
- "rustls 0.21.2",
+ "rustls 0.21.6",
"tokio",
]
diff --git a/core/Cargo.toml b/core/Cargo.toml
index 57ae252b7..b46cf3494 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -241,7 +241,8 @@ prost = { version = "0.11", optional = true }
quick-xml = { version = "0.29", features = ["serialize", "overlapped-lists"] }
rand = { version = "0.8", optional = true }
redb = { version = "1.0.0", optional = true }
-redis = { version = "0.23", features = [
+redis = { version = "0.23.1", features = [
+ "cluster-async",
"tokio-comp",
"connection-manager",
], optional = true }
diff --git a/core/src/services/redis/backend.rs
b/core/src/services/redis/backend.rs
index 87a1da06b..a777b9345 100644
--- a/core/src/services/redis/backend.rs
+++ b/core/src/services/redis/backend.rs
@@ -24,6 +24,9 @@ use std::time::Duration;
use async_trait::async_trait;
use http::Uri;
use redis::aio::ConnectionManager;
+use redis::cluster::ClusterClient;
+use redis::cluster::ClusterClientBuilder;
+use redis::cluster_async::ClusterConnection;
use redis::AsyncCommands;
use redis::Client;
use redis::ConnectionAddr;
@@ -47,6 +50,10 @@ pub struct RedisBuilder {
///
/// default is "tcp://127.0.0.1:6379"
endpoint: Option<String>,
+ /// network address of the Redis cluster service. Can be
"tcp://127.0.0.1:6379,tcp://127.0.0.1:6380,tcp://127.0.0.1:6381", e.g.
+ ///
+ /// default is None
+ cluster_endpoints: Option<String>,
/// the username to connect redis service.
///
/// default is None
@@ -75,6 +82,9 @@ impl Debug for RedisBuilder {
if let Some(endpoint) = self.endpoint.clone() {
ds.field("endpoint", &endpoint);
}
+ if let Some(cluster_endpoints) = self.cluster_endpoints.clone() {
+ ds.field("cluster_endpoints", &cluster_endpoints);
+ }
if let Some(username) = self.username.clone() {
ds.field("username", &username);
}
@@ -99,6 +109,20 @@ impl RedisBuilder {
self
}
+ /// set the network address of redis cluster service.
+ /// This parameter is mutually exclusive with the endponit parameter.
+ ///
+ /// currently supported schemes:
+ /// - no scheme: will be seen as "tcp"
+ /// - "tcp" or "redis": unsecured redis connections
+ /// - "unix" or "redis+unix": unix socket connection
+ pub fn cluster_endpoints(&mut self, cluster_endpoints: &str) -> &mut Self {
+ if !cluster_endpoints.is_empty() {
+ self.cluster_endpoints = Some(cluster_endpoints.to_owned());
+ }
+ self
+ }
+
/// set the username for redis
///
/// default: no username
@@ -155,6 +179,8 @@ impl Builder for RedisBuilder {
map.get("root").map(|v| builder.root(v));
map.get("endpoint").map(|v| builder.endpoint(v));
+ map.get("cluster_endpoints")
+ .map(|v| builder.cluster_endpoints(v));
map.get("username").map(|v| builder.username(v));
map.get("password").map(|v| builder.password(v));
map.get("db")
@@ -164,11 +190,67 @@ impl Builder for RedisBuilder {
}
fn build(&mut self) -> Result<Self::Accessor> {
- let endpoint = self
- .endpoint
- .clone()
- .unwrap_or_else(|| DEFAULT_REDIS_ENDPOINT.to_string());
+ let root = normalize_root(
+ self.root
+ .clone()
+ .unwrap_or_else(|| "/".to_string())
+ .as_str(),
+ );
+
+ if let Some(endpoints) = self.cluster_endpoints.clone() {
+ let mut cluster_endpoints: Vec<ConnectionInfo> = Vec::default();
+ for endpoint in endpoints.split(',') {
+
cluster_endpoints.push(self.get_connection_info(endpoint.to_string())?);
+ }
+ let mut client_builder =
ClusterClientBuilder::new(cluster_endpoints);
+ if let Some(username) = &self.username {
+ client_builder = client_builder.username(username.clone());
+ }
+ if let Some(password) = &self.password {
+ client_builder = client_builder.password(password.clone());
+ }
+ let client = client_builder.build()?;
+
+ let conn = OnceCell::new();
+
+ Ok(RedisBackend::new(Adapter {
+ addr: endpoints,
+ client: None,
+ cluster_client: Some(client),
+ conn,
+ default_ttl: self.default_ttl,
+ })
+ .with_root(&root))
+ } else {
+ let endpoint = self
+ .endpoint
+ .clone()
+ .unwrap_or_else(|| DEFAULT_REDIS_ENDPOINT.to_string());
+ let client =
+
Client::open(self.get_connection_info(endpoint.clone())?).map_err(|e| {
+ Error::new(ErrorKind::ConfigInvalid, "invalid or
unsupported scheme")
+ .with_context("service", Scheme::Redis)
+ .with_context("endpoint",
self.endpoint.as_ref().unwrap())
+ .with_context("db", self.db.to_string())
+ .set_source(e)
+ })?;
+
+ let conn = OnceCell::new();
+ Ok(RedisBackend::new(Adapter {
+ addr: endpoint,
+ client: Some(client),
+ cluster_client: None,
+ conn,
+ default_ttl: self.default_ttl,
+ })
+ .with_root(&root))
+ }
+ }
+}
+
+impl RedisBuilder {
+ fn get_connection_info(&self, endpoint: String) -> Result<ConnectionInfo> {
let ep_url = endpoint.parse::<Uri>().map_err(|e| {
Error::new(ErrorKind::ConfigInvalid, "endpoint is invalid")
.with_context("service", Scheme::Redis)
@@ -216,43 +298,28 @@ impl Builder for RedisBuilder {
password: self.password.clone(),
};
- let con_info = ConnectionInfo {
+ Ok(ConnectionInfo {
addr: con_addr,
redis: redis_info,
- };
-
- let client = Client::open(con_info).map_err(|e| {
- Error::new(ErrorKind::ConfigInvalid, "invalid or unsupported
scheme")
- .with_context("service", Scheme::Redis)
- .with_context("endpoint", self.endpoint.as_ref().unwrap())
- .with_context("db", self.db.to_string())
- .set_source(e)
- })?;
-
- let root = normalize_root(
- self.root
- .clone()
- .unwrap_or_else(|| "/".to_string())
- .as_str(),
- );
-
- let conn = OnceCell::new();
- Ok(RedisBackend::new(Adapter {
- client,
- conn,
- default_ttl: self.default_ttl,
})
- .with_root(&root))
}
}
/// Backend for redis services.
pub type RedisBackend = kv::Backend<Adapter>;
+#[derive(Clone)]
+enum RedisConnection {
+ Normal(ConnectionManager),
+ Cluster(ClusterConnection),
+}
+
#[derive(Clone)]
pub struct Adapter {
- client: Client,
- conn: OnceCell<ConnectionManager>,
+ addr: String,
+ client: Option<Client>,
+ cluster_client: Option<ClusterClient>,
+ conn: OnceCell<RedisConnection>,
default_ttl: Option<Duration>,
}
@@ -262,19 +329,29 @@ impl Debug for Adapter {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut ds = f.debug_struct("Adapter");
- let info = self.client.get_connection_info();
- ds.field("addr", &info.addr);
- ds.field("db", &info.redis.db);
- ds.field("user", &info.redis.username);
+ ds.field("addr", &self.addr);
ds.finish()
}
}
impl Adapter {
- async fn conn(&self) -> Result<ConnectionManager> {
+ async fn conn(&self) -> Result<RedisConnection> {
Ok(self
.conn
- .get_or_try_init(|| async {
ConnectionManager::new(self.client.clone()).await })
+ .get_or_try_init(|| async {
+ if let Some(client) = self.client.clone() {
+ ConnectionManager::new(client.clone())
+ .await
+ .map(RedisConnection::Normal)
+ } else {
+ self.cluster_client
+ .clone()
+ .unwrap()
+ .get_async_connection()
+ .await
+ .map(RedisConnection::Cluster)
+ }
+ })
.await?
.clone())
}
@@ -285,7 +362,7 @@ impl kv::Adapter for Adapter {
fn metadata(&self) -> kv::Metadata {
kv::Metadata::new(
Scheme::Redis,
- &self.client.get_connection_info().addr.to_string(),
+ self.addr.as_str(),
Capability {
read: true,
write: true,
@@ -297,29 +374,61 @@ impl kv::Adapter for Adapter {
}
async fn get(&self, key: &str) -> Result<Option<Vec<u8>>> {
- let mut conn = self.conn().await?;
- let bs: Option<Vec<u8>> = conn.get(key).await?;
- Ok(bs)
+ let conn = self.conn().await?;
+ match conn {
+ RedisConnection::Normal(mut conn) => {
+ let bs = conn.get(key).await?;
+ Ok(bs)
+ }
+ RedisConnection::Cluster(mut conn) => {
+ let bs = conn.get(key).await?;
+ Ok(bs)
+ }
+ }
}
async fn set(&self, key: &str, value: &[u8]) -> Result<()> {
- let mut conn = self.conn().await?;
+ let conn = self.conn().await?;
match self.default_ttl {
- Some(ttl) => conn.set_ex(key, value, ttl.as_secs() as
usize).await?,
- None => conn.set(key, value).await?,
+ Some(ttl) => match conn {
+ RedisConnection::Normal(mut conn) => {
+ conn.set_ex(key, value, ttl.as_secs() as usize).await?
+ }
+ RedisConnection::Cluster(mut conn) => {
+ conn.set_ex(key, value, ttl.as_secs() as usize).await?
+ }
+ },
+ None => match conn {
+ RedisConnection::Normal(mut conn) => conn.set(key,
value).await?,
+ RedisConnection::Cluster(mut conn) => conn.set(key,
value).await?,
+ },
}
Ok(())
}
async fn delete(&self, key: &str) -> Result<()> {
- let mut conn = self.conn().await?;
- let _: () = conn.del(key).await?;
+ let conn = self.conn().await?;
+ match conn {
+ RedisConnection::Normal(mut conn) => {
+ let _: () = conn.del(key).await?;
+ }
+ RedisConnection::Cluster(mut conn) => {
+ let _: () = conn.del(key).await?;
+ }
+ }
Ok(())
}
async fn append(&self, key: &str, value: &[u8]) -> Result<()> {
- let mut conn = self.conn().await?;
- conn.append(key, value).await?;
+ let conn = self.conn().await?;
+ match conn {
+ RedisConnection::Normal(mut conn) => {
+ conn.append(key, value).await?;
+ }
+ RedisConnection::Cluster(mut conn) => {
+ conn.append(key, value).await?;
+ }
+ }
Ok(())
}
}
diff --git a/core/src/services/redis/docs.md b/core/src/services/redis/docs.md
index 2ee2b86cb..59f25158d 100644
--- a/core/src/services/redis/docs.md
+++ b/core/src/services/redis/docs.md
@@ -18,6 +18,7 @@ This service can be used to:
- `root`: Set the working directory of `OpenDAL`
- `endpoint`: Set the network address of redis server
+- `cluster_endpoints`: Set the network address of redis cluster server. This
parameter is mutually exclusive with the `endponit` parameter.
- `username`: Set the username of Redis
- `password`: Set the password for authentication
- `db`: Set the DB of redis
diff --git
a/core/src/services/redis/fixtures/docker-compose-redis-cluster-tls.yml
b/core/src/services/redis/fixtures/docker-compose-redis-cluster-tls.yml
new file mode 100644
index 000000000..cb6333aba
--- /dev/null
+++ b/core/src/services/redis/fixtures/docker-compose-redis-cluster-tls.yml
@@ -0,0 +1,145 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+version: '3.8'
+
+services:
+ redis-node-0:
+ image: docker.io/bitnami/redis-cluster:7.0
+ networks:
+ redis_cluster:
+ ipv4_address: 172.30.0.2
+ ports:
+ - '6380:6379'
+ environment:
+ - 'REDIS_PASSWORD=opendal'
+ - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3
redis-node-4 redis-node-5'
+ - 'REDIS_TLS_ENABLED=yes'
+ - 'REDIS_TLS_CERT_FILE=/etc/redis/ssl/redis.crt'
+ - 'REDIS_TLS_KEY_FILE=/etc/redis/ssl/redis.key'
+ - 'REDIS_TLS_CA_FILE=/etc/redis/ssl/ca.crt'
+ - 'REDIS_TLS_AUTH_CLIENTS=no'
+ volumes:
+ - ./ssl:/etc/redis/ssl/
+
+ redis-node-1:
+ image: docker.io/bitnami/redis-cluster:7.0
+ networks:
+ redis_cluster:
+ ipv4_address: 172.30.0.3
+ ports:
+ - '6381:6379'
+ environment:
+ - 'REDIS_PASSWORD=opendal'
+ - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3
redis-node-4 redis-node-5'
+ - 'REDIS_TLS_ENABLED=yes'
+ - 'REDIS_TLS_CERT_FILE=/etc/redis/ssl/redis.crt'
+ - 'REDIS_TLS_KEY_FILE=/etc/redis/ssl/redis.key'
+ - 'REDIS_TLS_CA_FILE=/etc/redis/ssl/ca.crt'
+ - 'REDIS_TLS_AUTH_CLIENTS=no'
+ volumes:
+ - ./ssl:/etc/redis/ssl/
+
+ redis-node-2:
+ image: docker.io/bitnami/redis-cluster:7.0
+ networks:
+ redis_cluster:
+ ipv4_address: 172.30.0.4
+ ports:
+ - '6382:6379'
+ environment:
+ - 'REDIS_PASSWORD=opendal'
+ - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3
redis-node-4 redis-node-5'
+ - 'REDIS_TLS_ENABLED=yes'
+ - 'REDIS_TLS_CERT_FILE=/etc/redis/ssl/redis.crt'
+ - 'REDIS_TLS_KEY_FILE=/etc/redis/ssl/redis.key'
+ - 'REDIS_TLS_CA_FILE=/etc/redis/ssl/ca.crt'
+ - 'REDIS_TLS_AUTH_CLIENTS=no'
+ volumes:
+ - ./ssl:/etc/redis/ssl/
+
+ redis-node-3:
+ image: docker.io/bitnami/redis-cluster:7.0
+ networks:
+ redis_cluster:
+ ipv4_address: 172.30.0.5
+ ports:
+ - '6383:6379'
+ environment:
+ - 'REDIS_PASSWORD=opendal'
+ - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3
redis-node-4 redis-node-5'
+ - 'REDIS_TLS_ENABLED=yes'
+ - 'REDIS_TLS_CERT_FILE=/etc/redis/ssl/redis.crt'
+ - 'REDIS_TLS_KEY_FILE=/etc/redis/ssl/redis.key'
+ - 'REDIS_TLS_CA_FILE=/etc/redis/ssl/ca.crt'
+ - 'REDIS_TLS_AUTH_CLIENTS=no'
+ volumes:
+ - ./ssl:/etc/redis/ssl/
+
+ redis-node-4:
+ image: docker.io/bitnami/redis-cluster:7.0
+ networks:
+ redis_cluster:
+ ipv4_address: 172.30.0.6
+ ports:
+ - '6384:6379'
+ environment:
+ - 'REDIS_PASSWORD=opendal'
+ - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3
redis-node-4 redis-node-5'
+ - 'REDIS_TLS_ENABLED=yes'
+ - 'REDIS_TLS_CERT_FILE=/etc/redis/ssl/redis.crt'
+ - 'REDIS_TLS_KEY_FILE=/etc/redis/ssl/redis.key'
+ - 'REDIS_TLS_CA_FILE=/etc/redis/ssl/ca.crt'
+ - 'REDIS_TLS_AUTH_CLIENTS=no'
+ volumes:
+ - ./ssl:/etc/redis/ssl/
+
+ redis-node-5:
+ image: docker.io/bitnami/redis-cluster:7.0
+ networks:
+ redis_cluster:
+ ipv4_address: 172.30.0.7
+ ports:
+ - '6385:6379'
+ depends_on:
+ - redis-node-0
+ - redis-node-1
+ - redis-node-2
+ - redis-node-3
+ - redis-node-4
+ environment:
+ - 'REDIS_CLUSTER_REPLICAS=1'
+ - 'REDIS_CLUSTER_CREATOR=yes'
+ - 'REDISCLI_AUTH=opendal'
+ - 'REDIS_PASSWORD=opendal'
+ - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3
redis-node-4 redis-node-5'
+ - 'REDIS_TLS_ENABLED=yes'
+ - 'REDIS_TLS_CERT_FILE=/etc/redis/ssl/redis.crt'
+ - 'REDIS_TLS_KEY_FILE=/etc/redis/ssl/redis.key'
+ - 'REDIS_TLS_CA_FILE=/etc/redis/ssl/ca.crt'
+ - 'REDIS_TLS_AUTH_CLIENTS=no'
+ volumes:
+ - ./ssl:/etc/redis/ssl/
+
+networks:
+ redis_cluster:
+ driver: bridge
+ ipam:
+ config:
+ -
+ subnet: 172.30.0.0/16
+ gateway: 172.30.0.1
diff --git a/core/src/services/redis/fixtures/docker-compose-redis-cluster.yml
b/core/src/services/redis/fixtures/docker-compose-redis-cluster.yml
new file mode 100644
index 000000000..99d7e5f54
--- /dev/null
+++ b/core/src/services/redis/fixtures/docker-compose-redis-cluster.yml
@@ -0,0 +1,75 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+version: '3.8'
+
+services:
+ redis-node-0:
+ image: docker.io/bitnami/redis-cluster:7.0
+ ports:
+ - '6380:6379'
+ environment:
+ - 'ALLOW_EMPTY_PASSWORD=yes'
+ - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3
redis-node-4 redis-node-5'
+
+ redis-node-1:
+ image: docker.io/bitnami/redis-cluster:7.0
+ ports:
+ - '6381:6379'
+ environment:
+ - 'ALLOW_EMPTY_PASSWORD=yes'
+ - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3
redis-node-4 redis-node-5'
+
+ redis-node-2:
+ image: docker.io/bitnami/redis-cluster:7.0
+ ports:
+ - '6382:6379'
+ environment:
+ - 'ALLOW_EMPTY_PASSWORD=yes'
+ - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3
redis-node-4 redis-node-5'
+
+ redis-node-3:
+ image: docker.io/bitnami/redis-cluster:7.0
+ ports:
+ - '6383:6379'
+ environment:
+ - 'ALLOW_EMPTY_PASSWORD=yes'
+ - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3
redis-node-4 redis-node-5'
+
+ redis-node-4:
+ image: docker.io/bitnami/redis-cluster:7.0
+ ports:
+ - '6384:6379'
+ environment:
+ - 'ALLOW_EMPTY_PASSWORD=yes'
+ - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3
redis-node-4 redis-node-5'
+
+ redis-node-5:
+ image: docker.io/bitnami/redis-cluster:7.0
+ ports:
+ - '6385:6379'
+ depends_on:
+ - redis-node-0
+ - redis-node-1
+ - redis-node-2
+ - redis-node-3
+ - redis-node-4
+ environment:
+ - 'ALLOW_EMPTY_PASSWORD=yes'
+ - 'REDIS_CLUSTER_REPLICAS=1'
+ - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3
redis-node-4 redis-node-5'
+ - 'REDIS_CLUSTER_CREATOR=yes'
diff --git a/core/src/services/redis/fixtures/docker-compose-redis-tls.yml
b/core/src/services/redis/fixtures/docker-compose-redis-tls.yml
new file mode 100644
index 000000000..7fa966e44
--- /dev/null
+++ b/core/src/services/redis/fixtures/docker-compose-redis-tls.yml
@@ -0,0 +1,34 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+version: '3.8'
+
+services:
+ redis:
+ image: docker.io/bitnami/redis:7.0
+ ports:
+ - '6379:6379'
+ environment:
+ - 'ALLOW_EMPTY_PASSWORD=yes'
+ - 'REDIS_TLS_ENABLED=yes'
+ - 'REDIS_TLS_CERT_FILE=/etc/redis/ssl/redis.crt'
+ - 'REDIS_TLS_KEY_FILE=/etc/redis/ssl/redis.key'
+ - 'REDIS_TLS_CA_FILE=/etc/redis/ssl/ca.crt'
+ - 'REDIS_TLS_AUTH_CLIENTS=no'
+ volumes:
+ - ./ssl:/etc/redis/ssl/
+
diff --git a/core/src/services/redis/fixtures/docker-compose-redis.yml
b/core/src/services/redis/fixtures/docker-compose-redis.yml
new file mode 100644
index 000000000..5f90a2de1
--- /dev/null
+++ b/core/src/services/redis/fixtures/docker-compose-redis.yml
@@ -0,0 +1,27 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+version: '3.8'
+
+services:
+ redis:
+ image: docker.io/bitnami/redis:7.0
+ ports:
+ - '6379:6379'
+ environment:
+ - 'ALLOW_EMPTY_PASSWORD=yes'
+
diff --git a/core/src/services/redis/fixtures/ssl/ca.crt
b/core/src/services/redis/fixtures/ssl/ca.crt
new file mode 100644
index 000000000..ea2b48581
--- /dev/null
+++ b/core/src/services/redis/fixtures/ssl/ca.crt
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDiTCCAnGgAwIBAgIUGJWxrMGe9qRZzAfd5w4XIT3lkcEwDQYJKoZIhvcNAQEL
+BQAwVDEVMBMGA1UEAwwMVGVzdCBSb290IENBMQswCQYDVQQGEwJVUzENMAsGA1UE
+CAwEVGVzdDENMAsGA1UEBwwEVGVzdDEQMA4GA1UECgwHT3BlbmRhbDAeFw0yMzA4
+MTQxMTEzMzRaFw0yNDA4MTMxMTEzMzRaMFQxFTATBgNVBAMMDFRlc3QgUm9vdCBD
+QTELMAkGA1UEBhMCVVMxDTALBgNVBAgMBFRlc3QxDTALBgNVBAcMBFRlc3QxEDAO
+BgNVBAoMB09wZW5kYWwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCt
+UF6mcDgSyH5t78XnJusvQxsUfv2XydHtvLcIpwkCkIuIj7nF2WH064Gv12x+y42W
+mb+5z6JTgHRMRqcyQM8q4PQFrKvxPX8R2Limd7VLBJzYjR7Ma7JIrDohLnfywxUP
+19P5SzaGiro+ZK3t3xCnmtHcYoM+An0mQdKyVV7ytzAfg1PqkfDme19I28fH8cOP
+tF+RU8/LEHnte519O1bawx7xNdPsyykMrFij02o1VUeum2K9Wya8xHDixokveYDW
+swg5G4Tsy1QfgqFgxAXahIroPIwQvZOGkWVsmPXRXHtHNFG91ntJivv2HBFniUTq
+A0UbVdj09T+h+JLc19G9AgMBAAGjUzBRMB0GA1UdDgQWBBQ2672x8uh6Lud0EkjO
+wt2aEioeKjAfBgNVHSMEGDAWgBQ2672x8uh6Lud0EkjOwt2aEioeKjAPBgNVHRMB
+Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCQkLp3GzZOXXXOKiMF6Iev1OUW
+w1jr7hVdJHOVGNCD6uZLuwSXJOWnEP8+hp8WvMl7SQAPpVYsTjdqhLATLaAZDucG
+sDq6oUTh/v8QVIBm0qF8+iMU8XZfgoeKuY13RXs23hneMAPQ5rcPwQhQEQkkqUvi
+Fq8qYFVd5mEr6Z62DT0s544WaBrpHr37mHOv0hIkHtX7Dy2Juc23MYw+W4PSD4fm
+sr1kARwHtY1meX+H3iRsX+7juTa33v+7H4IivhcPobIxFp+Hs9R5mx5u80wKMjVv
+t3STmB4nE7pABzucrjkSo43jIUwYN4rwydlSma9VkzvY6ry86HQuemycRb9H
+-----END CERTIFICATE-----
diff --git a/core/src/services/redis/fixtures/ssl/ca.key
b/core/src/services/redis/fixtures/ssl/ca.key
new file mode 100644
index 000000000..0c296d4d6
--- /dev/null
+++ b/core/src/services/redis/fixtures/ssl/ca.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCtUF6mcDgSyH5t
+78XnJusvQxsUfv2XydHtvLcIpwkCkIuIj7nF2WH064Gv12x+y42Wmb+5z6JTgHRM
+RqcyQM8q4PQFrKvxPX8R2Limd7VLBJzYjR7Ma7JIrDohLnfywxUP19P5SzaGiro+
+ZK3t3xCnmtHcYoM+An0mQdKyVV7ytzAfg1PqkfDme19I28fH8cOPtF+RU8/LEHnt
+e519O1bawx7xNdPsyykMrFij02o1VUeum2K9Wya8xHDixokveYDWswg5G4Tsy1Qf
+gqFgxAXahIroPIwQvZOGkWVsmPXRXHtHNFG91ntJivv2HBFniUTqA0UbVdj09T+h
++JLc19G9AgMBAAECggEABLUq/6y1q+DxT41/RoJFSiaG/PK0HbmNhRPRXvVL5oYZ
+nC8sldC2pfSjCa05f7o0vDNXRLX8oUsFyFqfnlr/UEQFrNpzXd1Umciqv1l/aFcV
+kZZHu2LDgtDW0AxB6tYHY5jyY5JPnAOhqncz+EL3t1hvvI5FJVvjnV9AA2MT7gDQ
+u/W5S9DcCneoPj/YnJmlZvXIJZrseg5Jwyk6CSuXW+VxGYnbI+4SxLaVefcU5tBx
+hbNtLkfEQyQPSAsiGUGJnDkn3a5cf6D+BM14KxEKYtweuhUP+ctzIm44T+ts38xm
+ZVkkccY4pcgxFDEn+oAqnMC+Vxz50zLoXpLrry9qmwKBgQDVVJsbta9tVgBrSKGp
+79to0/J9OlDrFVwBb2K8beJU8iDJvz13h0sbXNcDrpnwkvVemnB7waMT/gRJzzfd
+NKTyOfKDm2XMlS63QwVhSbH4zZIEPlInXsz12uuPrtwjodGQNDhRhgUB5KwL8Oue
+U9A2BBOSCo9ch2mTEJQ4OY+FAwKBgQDP+sC7VCPxUFaECWr1fXO1aYubDUzYogmI
+2I2fNhXYcjzm04sdLR8G+4t72b/DCePOBbjtsECEB7vyC3fuDQzaSgA8TgIahAaB
+U+wnTWAe8eMHxeqTpQIM+F2b6Sgz0oIYGRETC3Q6C/MRCmeG5lrfW5EoN3xc0DAC
+7THjgK6yPwKBgFp+AS3H7BpdGOBpduo3LMNS5NHqItkVvml9bkyv8ApIi1AJJ3HE
+mj+JKkwNjI9YR21R5dUZVqvsXLVSiUf0ROPbYNwi1xPpVF+4tleGg1AfI4lZRlAd
+DqRbsQDKE+ephNO+0wUB7K3Y6oJGOCx5MSE4qXSRti5x4n7X0YfoItExAoGBAJaD
+rVmk8gIhFwulWS/io4uln7ANtxCMbAQKXKvdU3/6ZNLUQ7hQwESoZPCzzJDVJnUi
+NQxnYrlqc30fCaNQ9H1B0tvRxLn11FNiLeTfnXGnspscg8BoSYyjbfN4kGy4qwfP
+lEjJIEsl/LnXYscBDMDanrmRNkJhNG3ZxSIVLdi3AoGBAIWbxUr9UgOwgjY2yVmm
+Qhao69fbLPGTJbADFdeO9SQxK/ljn5G+OkdZGGdQaw28KFYedNRNo9cSLho06IYC
+wvxeqgYlVzLnuhzTDdPfGf5/h+0Kg/fKvS2iqRIbNE3GgOurVTzRBzcHioaG7YEk
+sgf2jlGpf0JSWKSrcvx7bGqB
+-----END PRIVATE KEY-----
diff --git a/core/src/services/redis/fixtures/ssl/ca.srl
b/core/src/services/redis/fixtures/ssl/ca.srl
new file mode 100644
index 000000000..4df2505d0
--- /dev/null
+++ b/core/src/services/redis/fixtures/ssl/ca.srl
@@ -0,0 +1 @@
+24A4DFBC0495F919F017BA0B3BCE076490F2BCB5
diff --git a/core/src/services/redis/fixtures/ssl/redis.crt
b/core/src/services/redis/fixtures/ssl/redis.crt
new file mode 100644
index 000000000..6788eb4c7
--- /dev/null
+++ b/core/src/services/redis/fixtures/ssl/redis.crt
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIID1zCCAr+gAwIBAgIUJKTfvASV+RnwF7oLO84HZJDyvLwwDQYJKoZIhvcNAQEL
+BQAwVDEVMBMGA1UEAwwMVGVzdCBSb290IENBMQswCQYDVQQGEwJVUzENMAsGA1UE
+CAwEVGVzdDENMAsGA1UEBwwEVGVzdDEQMA4GA1UECgwHT3BlbmRhbDAeFw0yMzA4
+MTQxNDU5MzZaFw0yNDA2MDkxNDU5MzZaMFkxGjAYBgNVBAMMEVJlZGlzIGNlcnRp
+ZmljYXRlMQswCQYDVQQGEwJVUzENMAsGA1UECAwEVGVzdDENMAsGA1UEBwwEVGVz
+dDEQMA4GA1UECgwHT3BlbmRhbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAIwREKDrRgZ2jlR3tpLHvMiW8JDu4JiLBxyrlJJE5ndhuH7MEgwz8HnXvxbD
+eyuamzkAzQIvqfVFVTRuVEYyEtoGzIegDL76H9ybuMGhKBK1m0TmiH7bOsAVMqZN
+vDtQJiw8qePtSq3G3H7Sw+/oudrJIc/f7kDox/lndKHTBmLbjSrvpkOJk2qnvhPJ
+ih4SuLNiW+tHv4sUdYBXXxn2wLHXNLGrlpeW28jtWGfu2noRCzikOYL/jwg2xzXV
+cBSuFwQ3swLDG/htqpePVA/sLxbXTt03A8fCajYcKiJdW88gqw4dW01ya8rCr5MU
+1C7lPwNCB8qNn8pdkmrh/Oc0zDsCAwEAAaOBmzCBmDAfBgNVHSMEGDAWgBQ2672x
+8uh6Lud0EkjOwt2aEioeKjAJBgNVHRMEAjAAMAsGA1UdDwQEAwIE8DA+BgNVHREE
+NzA1gglsb2NhbGhvc3SHBH8AAAGHBKweAAKHBKweAAOHBKweAASHBKweAAWHBKwe
+AAaHBKweAAcwHQYDVR0OBBYEFGvNF07RBwyi3tbpFIJtvWhXAGblMA0GCSqGSIb3
+DQEBCwUAA4IBAQAd57+0YXfg8eIe2UkqLshIEonoIpKhmsIpRJyXLOUWYaSHri4w
+aDqPjogA39w34UcZsumfSReWBGrCyBroSCqQZOM166tw79+AVdjHHtgNm8pFRhO7
+0vnFdAU30TOQP+mRF3mXz3hcK68U/4cRhXC5jXq8YRLiAG74G3PmXmmk2phtluEL
+SLLCvF5pCz3EaYsEKP+ZQpdY3BLp6Me7XDpGWPuNYVwVTJwwM9CLjQ8pxMlz1O1x
+HVN7xGtLz4dw9nEqnmjYBvH8aum+iAQPiHVuGfQfqIea28XeuyV4c5TL2b+OUsLY
+BRhX+z5OkGHXcMc1QDKo3PZcs8C1w8SC1x9D
+-----END CERTIFICATE-----
diff --git a/core/src/services/redis/fixtures/ssl/redis.key
b/core/src/services/redis/fixtures/ssl/redis.key
new file mode 100644
index 000000000..9fecc1fce
--- /dev/null
+++ b/core/src/services/redis/fixtures/ssl/redis.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCMERCg60YGdo5U
+d7aSx7zIlvCQ7uCYiwccq5SSROZ3Ybh+zBIMM/B5178Ww3srmps5AM0CL6n1RVU0
+blRGMhLaBsyHoAy++h/cm7jBoSgStZtE5oh+2zrAFTKmTbw7UCYsPKnj7Uqtxtx+
+0sPv6LnaySHP3+5A6Mf5Z3Sh0wZi240q76ZDiZNqp74TyYoeErizYlvrR7+LFHWA
+V18Z9sCx1zSxq5aXltvI7Vhn7tp6EQs4pDmC/48INsc11XAUrhcEN7MCwxv4baqX
+j1QP7C8W107dNwPHwmo2HCoiXVvPIKsOHVtNcmvKwq+TFNQu5T8DQgfKjZ/KXZJq
+4fznNMw7AgMBAAECggEAA5l0ABv7s90mbDTwBqvxBdtHJFpXKuRhEui1NosPEctQ
+6+/qQ3uu4YVcqO+YweFFEufFMkSvopjHse4R5q87vR7xRkej0Yvo914zFxrBRmB6
+CdZoyeXFXTv442gvqaXgzUCOgcfOeafDcSjmayBjwk5qkDEqKhXb/w9HDS+N7vVk
+BU+b/lMzQbGWb/oc3pHmEYXqFR+sFkHM2nCWBvQ1hRX4TVaeZpUWH7RBE95z87ug
+F21yqEjQfaTh2cidKXWtnozxIv2XgUncY40njdhRRzyJqWIW4CEdxIAX0IWT0z2+
+4L59DoNyYaimnaaSmNDj5WgDgL7tlTziXBJfBTpqDQKBgQDAe6Tf49eZINZ6bxHC
+RDzFSKikBOvC9rkhGOzD6JBALPbdWH9HnnH4cT5F4b5y96rPEqzO1+RYQdIF4GDx
+NYafMx8Nht0j0WWJLkygUCa8guqaaFczaquaIHQ2YpzzhpLlmAdEz1Jrsk2LfM2Q
+58b7JHb+Aq/+UAIuBhL7FlRf7QKBgQC6SXR6opkjUfkrsWcQiB5CJDk7zuL8G+Ks
+Jle2S1TzFdBL5rNnVttC7yYmZIP7qN6zDtsbDxqW5gEeDmyGDixLeQ0kQ/oPU2/y
+lPr9rHx+BUWEGyDiCfG21Y6R/jdfrA4R5T8vPmJXOnnppXZ5Gqi1X0aHprbLYWhz
+HpkvBaHHxwKBgAGx1PzHo8FMYbcIPU7JjQNrpVh0VqMLywt4jbUX2hVGkBHY0p4N
+zhES5ip1V1jpx041auITUoZYZgH5PMFC6GGEcLSMyGulT1CK4M/UhNLKEEi1vHbO
+bJ5ZxMwpyBn4yFhPI1k+vgoGstoUijbJY54YbxfDbEs/5xUCpq4hPzLtAoGAZVBr
+3AKwnMgJZxz9u7z8D+bZhdCYHJsh5ZSY4ZkI44f6mD0pV0uixj2AlyLVsTn/nIy4
+13eYc3c2Jl2b4jC1IHr+jbm2tz0exmUGOI7lyjgdvaJveOAFqPVuq7IB9bOCl3MB
+sTURkPVJtqv5yhWYqcPefQpLokMg5nM+xpcejKMCgYEAqv5gj3ez0HmLv/9k86Zs
+8/780lNcYnB1dQYNJ7g3T6wu8WVGNtzOdPXGTMX9sbv9Smq0cZLZKNMtXDsc5aJT
+5yzysPkDxqSK4vJmng74aHUI2HW+HvPqWLZnXC0IYGFvN8KyVkdp/FyhQMNMp6ip
+5rgp5RpXJk5MhvvlYdZrz5Q=
+-----END PRIVATE KEY-----
\ No newline at end of file
diff --git a/core/src/services/redis/fixtures/ssl/redis.v3.ext
b/core/src/services/redis/fixtures/ssl/redis.v3.ext
new file mode 100644
index 000000000..92ffe5e52
--- /dev/null
+++ b/core/src/services/redis/fixtures/ssl/redis.v3.ext
@@ -0,0 +1,13 @@
+authorityKeyIdentifier=keyid,issuer
+basicConstraints=CA:FALSE
+keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
+subjectAltName = @alt_names
+[alt_names]
+DNS.1 = localhost
+IP.1 = 127.0.0.1
+IP.2 = 172.30.0.2
+IP.3 = 172.30.0.3
+IP.4 = 172.30.0.4
+IP.5 = 172.30.0.5
+IP.6 = 172.30.0.6
+IP.7 = 172.30.0.7
\ No newline at end of file