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


Reply via email to