diff --git a/.env.example b/.env.example index 50ae47ae7cff..62a3f9473043 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 d7de6a340933..68e1d7c02dcf 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 6407a0fe51c0..ea91cdd01953 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 57ae252b7885..b46cf3494cd4 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 87a1da06b8d5..a777b9345c5d 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, + /// 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, /// 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 { - 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 = 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 { let ep_url = endpoint.parse::().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; +#[derive(Clone)] +enum RedisConnection { + Normal(ConnectionManager), + Cluster(ClusterConnection), +} + #[derive(Clone)] pub struct Adapter { - client: Client, - conn: OnceCell, + addr: String, + client: Option, + cluster_client: Option, + conn: OnceCell, default_ttl: Option, } @@ -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 { + async fn conn(&self) -> Result { 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>> { - let mut conn = self.conn().await?; - let bs: Option> = 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 2ee2b86cb66a..59f25158de62 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 000000000000..cb6333aba7b5 --- /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 000000000000..99d7e5f54256 --- /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 000000000000..7fa966e442b4 --- /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 000000000000..5f90a2de18fd --- /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 000000000000..ea2b48581fa1 --- /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 000000000000..0c296d4d6463 --- /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 000000000000..4df2505d0ae6 --- /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 000000000000..6788eb4c7fd6 --- /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 000000000000..9fecc1fcea98 --- /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 000000000000..92ffe5e5222f --- /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