Compare commits

...

21 Commits

Author SHA1 Message Date
Husni Alhamdani f887e3cc56
Merge aa83379dd3 into 0fa816c684 2025-08-22 10:15:07 +02:00
Daniel Goldman 0fa816c684
fix: do not remove default config volume when providing additional config (#1480)
fix: do not remove default config volume when providing external config

closes #1479

Signed-off-by: Daniel Goldman <danielgoldman4@gmail.com>
2025-08-21 21:15:30 +08:00
Tian 891b85590c
fix: Fix the issue where Sentinel fails to monitor one replica replication (#1481)
* fix: Solve the issue where Sentinel fails to work when starting 1 Master Replication

Signed-off-by: tcxdgit <tiancuixia92@163.com>

* fix: Fix failure to get monitorAddr in reconcileSentinel for single instance

Signed-off-by: tcxdgit <tiancuixia92@163.com>

---------

Signed-off-by: tcxdgit <tiancuixia92@163.com>
2025-08-21 20:37:05 +08:00
Jeffrey Boehm 3d52fdb0b7
feat: Add SecurityContext to init-config when behind GenerateConfigInInitContainer feature flag
feat: Add SecurityContext to Redis init containers in tests and configurations when behind GenerateConfigInInitContainer feature flag

Signed-off-by: Jeffrey Böhm <hello@jeffrey-boehm.de>
2025-08-14 16:35:10 +08:00
yangw 88d688acc5
docs: Rename Size to ClusterSize in RedisClusterSpec and update related comments (#1475)
Signed-off-by: yangw <wuyangmuc@gmail.com>
2025-08-07 19:43:43 +08:00
Husni Alhamdani aa83379dd3 feat: rediscluster observability
Signed-off-by: Husni Alhamdani <dhanielluis@gmail.com>
2025-08-07 18:29:40 +08:00
Husni Alhamdani adbef3c3fd fix: rediscluster observability
Signed-off-by: Husni Alhamdani <dhanielluis@gmail.com>
2025-08-07 18:29:40 +08:00
dcaputo-harmoni e4faaeb5b7
fix: Recover redisreplication from master pod deletion without sentinel(#1449)
* Recover from orphaned master

Signed-off-by: David Caputo <dcaputo@harmoni.io>

* Fix linting error

Signed-off-by: David Caputo <dcaputo@harmoni.io>

* fix lint

Signed-off-by: yangw <wuyangmuc@gmail.com>

---------

Signed-off-by: David Caputo <dcaputo@harmoni.io>
Signed-off-by: yangw <wuyangmuc@gmail.com>
Co-authored-by: yangw <wuyangmuc@gmail.com>
2025-08-07 14:45:28 +08:00
yangw 809d48e57e
feat: Add kubeClientQPS and kubeClientTimeout configuration options (#1473)
* feat: Add kubeClientQPS and kubeClientTimeout configuration options

Signed-off-by: yangw <wuyangmuc@gmail.com>

* fix no flags in viper

Signed-off-by: yangw <wuyangmuc@gmail.com>

---------

Signed-off-by: yangw <wuyangmuc@gmail.com>
2025-08-06 22:25:09 +08:00
dependabot[bot] e1004c0352
chore(deps): bump sigstore/cosign-installer from 3.9.1 to 3.9.2 (#1465)
Bumps [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) from 3.9.1 to 3.9.2.
- [Release notes](https://github.com/sigstore/cosign-installer/releases)
- [Commits](https://github.com/sigstore/cosign-installer/compare/v3.9.1...v3.9.2)

---
updated-dependencies:
- dependency-name: sigstore/cosign-installer
  dependency-version: 3.9.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-06 15:32:48 +08:00
yangw afd663611c
fix: tls connection broken in replication with sentinel (#1472)
* feat: support tls in replication with sentinel

Signed-off-by: yangw <wuyangmuc@gmail.com>

* fix lint

Signed-off-by: yangw <wuyangmuc@gmail.com>

* fix lint

Signed-off-by: yangw <wuyangmuc@gmail.com>

* fix monitor

Signed-off-by: yangw <wuyangmuc@gmail.com>

* tls flag

Signed-off-by: yangw <wuyangmuc@gmail.com>

---------

Signed-off-by: yangw <wuyangmuc@gmail.com>
2025-08-06 11:40:18 +08:00
yangw a2f7243c7f
feat: Add PDB values in redis replication (#1461)
* feat: Add PDB values in redis replication

Signed-off-by: yangw <wuyangmuc@gmail.com>

* add docs

Signed-off-by: yangw <wuyangmuc@gmail.com>

* fix lint

Signed-off-by: yangw <wuyangmuc@gmail.com>

---------

Signed-off-by: yangw <wuyangmuc@gmail.com>
2025-07-28 11:25:41 +08:00
yangw 6d294f5cbe
fix: missing generate common types in api reference (#1460)
Signed-off-by: yangw <wuyangmuc@gmail.com>
2025-07-27 21:15:42 +08:00
yangw ed95c2c660
feat: Add API doc generation tools and update Makefile (#1459)
- Introduced a new Makefile target `generate-api-docs` to automate API documentation generation using the `crd-ref-docs` tool.
- Added a new script `build.sh` for generating API documentation, including configuration for the documentation process.
- Created new documentation files for API reference, detailing the Redis API groups and resource types.
- Removed the obsolete Redis API documentation index file to streamline the documentation structure.

Signed-off-by: yangw <wuyangmuc@gmail.com>
2025-07-27 20:23:42 +08:00
yangw fefd300cb9
feat: Implement RedisCluster clusterSize validation webhook (#1458)
Signed-off-by: yangw <wuyangmuc@gmail.com>
2025-07-27 17:09:56 +08:00
Michel Zehnder cc0c9b0f92
docs: Fix obsolete links (#1457)
* Fix obsolete links

Signed-off-by: Michel Zehnder <MichelZ@users.noreply.github.com>

* Remove obsolete paragraph

Signed-off-by: Michel Zehnder <MichelZ@users.noreply.github.com>

---------

Signed-off-by: Michel Zehnder <MichelZ@users.noreply.github.com>
2025-07-27 15:55:27 +08:00
Michel Zehnder 6d2ec60c8e
docs: Update release history (#1453)
* Update Release History

Signed-off-by: Michel Zehnder <MichelZ@users.noreply.github.com>

* Update Release History v0.15.1

Signed-off-by: Michel Zehnder <MichelZ@users.noreply.github.com>

* Update Release History

Signed-off-by: Michel Zehnder <MichelZ@users.noreply.github.com>

* Update Release History

Signed-off-by: Michel Zehnder <MichelZ@users.noreply.github.com>

---------

Signed-off-by: Michel Zehnder <MichelZ@users.noreply.github.com>
2025-07-25 16:03:14 +08:00
Michel Zehnder aa8d2d9cf1
docs: Update CHANGELOG.md with latest information (#1454)
Update CHANGELOG.md with latest information

Signed-off-by: Michel Zehnder <MichelZ@users.noreply.github.com>
2025-07-25 16:00:11 +08:00
yangw a8ab85d4fa
docs: Sentinel recommendations and update security context fields (#1450)
Signed-off-by: yangw <wuyangmuc@gmail.com>
2025-07-24 19:54:56 +08:00
Dee Kryvenko d17ea8b4bf
feat: Add PersistentVolumeClaimRetentionPolicy support (#1448)
Signed-off-by: Dee Kryvenko <dee@homecloud.zone>
2025-07-24 18:04:47 +08:00
dependabot[bot] 4a41f558dc
chore(deps): bump golang.org/x/oauth2 from 0.24.0 to 0.27.0 (#1447)
Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.24.0 to 0.27.0.
- [Commits](https://github.com/golang/oauth2/compare/v0.24.0...v0.27.0)

---
updated-dependencies:
- dependency-name: golang.org/x/oauth2
  dependency-version: 0.27.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-22 14:50:41 +08:00
82 changed files with 2880 additions and 686 deletions

View File

@ -217,7 +217,7 @@ jobs:
- name: Build Dockerfile
run: docker build . --file Dockerfile --tag redis-operator:e2e
- name: Install Cosign
uses: sigstore/cosign-installer@v3.9.1
uses: sigstore/cosign-installer@v3.9.2
- name: Install chainsaw
uses: kyverno/action-install-chainsaw@v0.2.12
with:
@ -238,11 +238,14 @@ jobs:
# NOTE: This is a workaround for the issue where the default storage class does not support volume expansion.
# Since we don't require PVC resizing (unlike physical disks), we can simply ensure that the requested PVC size is met.
- name: Set allowVolumeExpansion to true
- name: Setup k8s Kind Cluster
run: |
DEFAULT_SC=$(kubectl get storageclass -o=jsonpath='{.items[?(@.metadata.annotations.storageclass\.kubernetes\.io/is-default-class=="true")].metadata.name}')
kubectl patch storageclass $DEFAULT_SC -p '{"allowVolumeExpansion": true}'
# install cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.18.2/cert-manager.yaml
- name: Load Docker image into Kind
run: |
kubectl cluster-info --context kind-kind

View File

@ -2,7 +2,7 @@ extends: default
rules:
line-length:
max: 200
max: 300
level: warning
allow-non-breakable-words: true
allow-non-breakable-inline-mappings: true

View File

@ -1,5 +1,229 @@
### v0.21.0
##### June 2025 Latest
#### :tada: Features
- Round robin where to transfer cluster shards when scaling in a Redis Cluster #1412
- Add auto max memory configuration for Redis instances #1411
- Add bus port configuration for Redis cluster services #1406
- Add automatic Redis pod role label synchronization for rediscluster #1404
- RedisCluster observability #1392
- Add liveness/readiness probes to values.yaml and templates #1378
- Reduce unnecessary requeue when skip reconcile annotation exists #1374
- RedisReplication observability, skip reconcile or not #1369
- Add Redis Sentinel validation webhook for clusterSize #1361
#### :beetle: Bug Fixes
- Resolve StatefulSet selector immutability issues #1382
- Avoid sentinel restart after replication failover #1381
- Define named probe port outside webhook block #1354
#### :tada: Refactors
- Reorganize manager agent cmd package #1383
- Reorganize API structure and update paths #1363
- Remove useless structure and refactor package #1362
- Reorganize command structure for Redis operator #1351
### v0.20.2
##### May 12, 2024
#### :beetle: Bug Fixes
- Handle panic when retrieving StatefulSet in GetRedisNodesByRole #1330
- VCT resize detection logic; add support for scaling out with new VCT size #1342
- Service updated before Statefulset during Reconciliation #1348
#### :tada: Features
- Add data assertion generation and enhance Redis configuration commands #1331
- Add feature gates support for Redis Operator #1333
- Migrate kubebuilder go.kubebuilder.io/v3 to go.kubebuilder.io/v4 #1340
#### :tada: Refactors
- Define container port for http probes in operator chart #1326
- Enhance environment variable management and CI workflow #1315
### v0.20.1
##### April 27, 2024
#### :beetle: Bug Fixes
- Move VCT logic before diff calculation for stateful set #1322
### v0.20.0
##### April 1, 2024
#### :tada: Features
- Sentinel - support hostname resolve and announce #1247
- Add redis agent with bootstrap configuration generation #1254
- Implement comprehensive Redis configuration generation for agent bootstrap #1260
- Added support for hostport to allow direct connection to the pod #1263
- Add preStop hook for Redis Cluster failover #1264
- Sentinel - announce-ip when resolve & announce are set #1271
- Fix PVC resizing issue and refactor PVC resizing logic #1268
- Add redisreplication observability #1274
- Add recreate-stateful-strategy, orphan, background, foreground(default) #1286
- Update Dockerfile and Makefile for unified operator binary #1294
- Add support for anti affinity configuration in helm charts #1296
- Guarantee to avoid bad master ip on Sentinel #1289
- Add feature gates for sentinel configuration generation in init container #1300
- Support redis configuration generation in init container #1303
#### :beetle: Bug Fixes
- Replace hardcoded Redis port 6379 with configurable port from cr.Spec.Port #1261
- Update references from master to main in docs and workflow files #1288
- Svc finalizer removed #1297
- Race condition resulting in permanently broken Redis cluster #1298
### v0.19.1
##### February 19, 2024
#### :warning: Deprecation Notice
The v1beta1 API version will be removed in next release. Users are strongly encouraged to migrate to v1beta2, which offers enhanced features and improved stability.
#### :tada: Features
- Add data-assert tool for Redis data management #1204
- Check data consistent by external tool #1205
- Added actions to publish charts to github container registry #1201
- Add headless service configuration support #1219
- Update redis-operator cert manager configuration #1220
- Add additional service configuration with optional enable flag #1228
- Enhance Redis HA and node scheduling strategy #1237
- Add securityContext config in chart for redis-exporter #1238
- Add dynamic Redis configuration support for Redis Cluster #1241
- Configurable operator maxConcurrentReconciles #1242
#### :beetle: Bug Fixes
- Skip-reconcile annotation still skipping reconcile even the value is false #1202
- Make recreate statefulset only trigger when value true #1240
- Improve pod label patching with error handling and retry mechanism #1231
- Changed certificate serverName to pod+namespace #1221
- Add missing topologySpreadConstraint on RedisCluster follower #1218
### v0.19.0
##### January 12, 2024
#### :tada: Features
- Add PDB and probes, drop unspecified acl in sentinel helm #1123
- Add master/replica service to redis replication #1124
- Add recreateStatefulSetOnUpdateInvalid helm chart value #1127
- Enhance RedisReplication controller and CRD with additional status #1154
- Support PDB in redisreplication #1166
- Enhance RedisSentinel reconciliation logic and update workflow #1176
- Support redis-cluster topologySpreadConstraints #1177
- Add event recording functionality for RedisCluster controller #1182
- Support topologySpreadConstraints in replication & sentinel #1184
- Redis-cluster add podAntiAffinity #1180
- Separate resources section for leader and follower #1188
- Enhance RedisCluster resource management by introducing separate resource handling for leader and follower #1199
#### :beetle: Bug Fixes
- PDB value mapping in redis-sentinel #1136
- Chart render error when enable initcontainer #1146
- InitContainer enabled properties not define in template #1152
- Redis-cluster unexpected downscaling #1173
- Reduce the impact of Redis cluster intermediate states #1178
- Label selector mapping on redisreplication pdb #1191
### v0.18.1
##### November 7, 2024
#### :tada: Features
- Support setting minReadySeconds on the stateful sets #1023
- Add tolerations to operator chart #1051
- Add image pull secret for redis operator #1053
- Add service monitor to redis sentinel chart #1071
- Add readiness/liveness probe to redis operator chart #1072
- Upgrade redis/sentinel image to 7.0.15 #1099
- Reconcile redissentinel only on master changed #1122
#### :beetle: Bug Fixes
- Fix indentation error when enable additional config #1031
- Fix field validate error when enable additional config for sentinel #1033
- Fix unknown field error when upgrade chart #1034
- Fix bad indentation on redis standalone additional configs #1040
- Fix attempt to repair disconnected/failed master nodes before failing over #1105
- Fix set controller probe endpoint handler #1121
### v0.18.0
##### July 11, 2024
#### :tada: Features
- Added redisReplicationPassword values to connect secured replication #1021
- Added redis/redisreplication/redissentinel/rediscluster chart #1007
- Added support for extra volume mounts for redis sentinel #994
- Added automountServiceAccountToken values for deployment and serviceaccount #991
- Added securityContext for exporter, initcontainers and sidecars #987
- Added security context values in operator chart #973
- Added rolling update sequence from leader to follower #966
- Added support for configurable probe handlers #934
- Added redis operator helm chart and release workflow #941
- Added support for other container engines #947
#### :beetle: Bug Fixes
- Added default port to enable `SENTINEL_PORT` environment #999
- ReadyReplicas need to be checked in `IsStatefulSetReady` #993
- watchNamespace value does not take effect in chart #990
- Sentinel should not reconcile until replication cluster ready #964
- Return ASAP after handling finalizer #940
- Check redis replication after handling finalizer #936
### v0.17.0
##### May 14, 2024
#### :tada: Features
- WATCH_NAMESPACE support multi namespace #919
- Add workflow to publish image to ghcr #914
- Probe use built-in, discarded healthcheck.sh #907
- Implement redis cluster ready state #867
- Add redisreplication status masterNode #849
#### :beetle: Bug Fixes
- Runtime panic when delete rediscluster which disable persistent #922
- Runtime panic when storage param is empty #887
- Exporter can not connect to redis when enable tls #902
- Update status if not equal #900
- Should get the really leader count when scale in #885
- ClusterSlaves result should be cut #884
- Redis cluster update as scale out #882
- Add common AddFinalizer for all api #858
### v0.16.0
##### March 27, 2024
#### :tada: Features
- Added support for multiple CRDs and enhanced functionality
- Improved test coverage and CI/CD pipeline
- Enhanced Redis cluster management and scaling capabilities
- Added support for custom Redis configurations
- Implemented advanced security features
#### :beetle: Bug Fixes
- Fixed various StatefulSet and service management issues
- Resolved Redis cluster scaling problems
- Fixed authentication and TLS connectivity issues
- Improved error handling and logging
### v0.15.0
##### July 17, 2023 Latest
##### July 17, 2023
#### :beetle: Bug Fixes

View File

@ -41,6 +41,7 @@ ENVTEST ?= $(LOCALBIN)/setup-envtest-$(ENVTEST_VERSION)
GOLANGCI_LINT = $(LOCALBIN)/golangci-lint-$(GOLANGCI_LINT_VERSION)
KUTTL = $(LOCALBIN)/kuttl-$(KUTTL_VERSION)
KIND = $(LOCALBIN)/kind-$(KIND_VERSION)
CRD_REF_DOCS = $(LOCALBIN)/crd-ref-docs-$(CRD_REF_DOCS_VERSION)
# Tool Versions
KUSTOMIZE_VERSION ?= v5.6.0
@ -49,6 +50,7 @@ ENVTEST_VERSION ?= release-0.17
GOLANGCI_LINT_VERSION ?= v2.2.2
KUTTL_VERSION ?= 0.15.0
KIND_VERSION ?= v0.24.0
CRD_REF_DOCS_VERSION ?= v0.0.12
# Options for 'bundle-build'
ifneq ($(origin CHANNELS), undefined)
@ -134,6 +136,11 @@ vet:
generate: controller-gen
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
# Generate API documentation
.PHONY: generate-api-docs
generate-api-docs:
@hack/api-docs/build.sh
# Create a new builder instance for Docker Buildx with the specified platforms and set it as the current builder
.PHONY: docker-create
docker-create:
@ -169,7 +176,7 @@ bundle-build:
# Rebuild all generated code
.PHONY: codegen
codegen: generate manifests sync-crds generate-dataAssert generate-metricsdocs
codegen: generate manifests sync-crds generate-dataAssert generate-metricsdocs generate-api-docs
# Verify that codegen is up to date.
.PHONY: verify-codegen

View File

@ -3,8 +3,8 @@
</p>
<p align="center">
<a href="https://dev.azure.com/opstreedevops/DevOps/_apis/build/status/redis-operator/redis-operator?repoName=OT-CONTAINER-KIT%2Fredis-operator&branchName=main">
<img src="https://dev.azure.com/opstreedevops/DevOps/_apis/build/status/redis-operator/redis-operator?repoName=OT-CONTAINER-KIT%2Fredis-operator&branchName=main" alt="Azure Pipelines">
<a href="https://github.com/OT-CONTAINER-KIT/redis-operator/actions/workflows/ci.yaml">
<img src="https://github.com/OT-CONTAINER-KIT/redis-operator/actions/workflows/ci.yaml/badge.svg" alt="CI Pipeline">
</a>
<a href="https://goreportcard.com/report/github.com/OT-CONTAINER-KIT/redis-operator">
<img src="https://goreportcard.com/badge/github.com/OT-CONTAINER-KIT/redis-operator" alt="GoReportCard">
@ -23,13 +23,13 @@
</a>
</p>
A Golang based redis operator that will make/oversee Redis standalone and cluster mode setup on top of the Kubernetes. It can create a redis cluster setup with best practices on Cloud as well as the Bare metal environment. Also, it provides an in-built monitoring capability using redis-exporter.
A Golang-based Redis operator that will make/oversee Redis standalone and cluster mode setup on top of Kubernetes. It can create a Redis cluster setup with best practices on Cloud as well as the bare metal environment. Also, it provides an in-built monitoring capability using redis-exporter.
For documentation, please refer to <https://ot-redis-operator.netlify.app/>
For documentation, please refer to <https://redis-operator.opstree.dev/>
Organizations that are using Redis Operator to manage their redis workload can be found [here](./USED_BY_ORGANIZATIONS.md). If your organization is also using Redis Operator, please free to add by creating a [pull request](https://github.com/OT-CONTAINER-KIT/redis-operator/pulls)
Organizations that are using Redis Operator to manage their Redis workload can be found [here](./USED_BY_ORGANIZATIONS.md). If your organization is also using Redis Operator, please feel free to add by creating a [pull request](https://github.com/OT-CONTAINER-KIT/redis-operator/pulls)
This operator only supports versions of redis `=>6`.
This operator only supports versions of Redis `>=6`.
## Architecture
@ -39,29 +39,23 @@ This operator only supports versions of redis `=>6`.
## Purpose
There are multiple problems that people face while setting up redis setup on Kubernetes, specially cluster type setup. The purpose of creating this opperator is to provide an easy and production ready interface for redis setup that include best-practices, security controls, monitoring, and management.
There are multiple problems that people face while setting up Redis setup on Kubernetes, especially cluster type setup. The purpose of creating this operator is to provide an easy and production-ready interface for Redis setup that includes best-practices, security controls, monitoring, and management.
## Supported Features
Here the features which are supported by this operator:-
Here are the features which are supported by this operator:
- Redis cluster and standalone mode setup
- Redis cluster failover and recovery
- Inbuilt monitoring with redis exporter
- Password and password-less setup of redis
- Password and password-less setup of Redis
- TLS support for additional security layer
- Ipv4 and Ipv6 support for redis setup
- Detailed monitoring grafana dashboard
## Getting Started
If you want to deploy redis-operator from scratch to a local Minikube cluster, begin with the [Getting started](https://ot-container-kit.github.io/redis-operator/#/quickstart/quickstart) document. It will guide your through the setup step-by-step.
The configuration of Redis setup should be described in [CRD definitions](config/crd/bases). All the examples related to redis standalone and cluster setup can be found inside [example](./example) folder.
- IPv4 and IPv6 support for Redis setup
- Detailed monitoring Grafana dashboard
## Prerequisites
Redis operator requires a Kubernetes cluster of version `>=1.18.0`. If you have just started with Operators, it's highly recommended using the latest version of Kubernetes.
Redis Operator requires a Kubernetes cluster of version `>=1.18.0`. If you have just started with Operators, it's highly recommended using the latest version of Kubernetes.
## Image Compatibility
@ -80,59 +74,59 @@ The following table shows the compatibility between the Operator Version, Redis
## Quickstart
The setup can be done by using helm. If you want to see more example, please go through the [example](./example) folder.
The setup can be done by using Helm. If you want to see more examples, please go through the [example](./example) folder.
But you can simply use the helm chart for installation.
But you can simply use the Helm chart for installation.
```shell
# Add the helm chart
# Add the Helm chart
$ helm repo add ot-helm https://ot-container-kit.github.io/helm-charts/
```
```shell
# Deploy the redis-operator
# Deploy the Redis operator
$ helm upgrade redis-operator ot-helm/redis-operator \
--install --create-namespace --namespace ot-operators
```
After deployment, verify the installation of operator
After deployment, verify the installation of the operator
```shell
helm test redis-operator --namespace ot-operators
```
Creating redis cluster, standalone, replication and sentinel setup.
Creating Redis cluster, standalone, replication and sentinel setup.
```shell
# Create redis cluster setup
# Create Redis cluster setup
$ helm upgrade redis-cluster ot-helm/redis-cluster \
--set redisCluster.clusterSize=3 --install \
--namespace ot-operators
```
```shell
# Create redis standalone setup
# Create Redis standalone setup
$ helm upgrade redis ot-helm/redis \
--install --namespace ot-operators
```
```shell
# Create redis replication setup
# Create Redis replication setup
$ helm upgrade redis-replication ot-helm/replication \
--install --namespace ot-operators
```
```shell
# Create redis sentinel setup
# Create Redis sentinel setup
$ helm upgrade redis-sentinel ot-helm/sentinel \
--install --namespace ot-operators
```
If you want to customize the value file by yourself while initializing the helm command, the values files for reference are present [here](https://github.com/OT-CONTAINER-KIT/helm-charts/tree/main/charts/redis-setup).
If you want to customize the values file by yourself while initializing the Helm command, the values files for reference are present [here](https://github.com/OT-CONTAINER-KIT/helm-charts/tree/main/charts/redis-setup).
## Monitoring with Prometheus
To monitor redis performance we will be using prometheus. In any case, extra prometheus configuration will not be required because we will be using the Prometheus service discover pattern. For that we already have set these annotations:-
To monitor Redis performance we will be using Prometheus. In any case, extra Prometheus configuration will not be required because we will be using the Prometheus service discovery pattern. For that we already have set these annotations:
```yaml
annotations:

View File

@ -1,4 +1,4 @@
package api
package v1beta2
import (
appsv1 "k8s.io/api/apps/v1"
@ -14,6 +14,7 @@ type KubernetesConfig struct {
ExistingPasswordSecret *ExistingPasswordSecret `json:"redisSecret,omitempty"`
ImagePullSecrets *[]corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"`
UpdateStrategy appsv1.StatefulSetUpdateStrategy `json:"updateStrategy,omitempty"`
PersistentVolumeClaimRetentionPolicy *appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy `json:"persistentVolumeClaimRetentionPolicy,omitempty"`
Service *ServiceConfig `json:"service,omitempty"`
IgnoreAnnotations []string `json:"ignoreAnnotations,omitempty"`
MinReadySeconds *int32 `json:"minReadySeconds,omitempty"`
@ -198,6 +199,7 @@ type Sidecar struct {
// RedisLeader interface will have the redis leader configuration
// +k8s:deepcopy-gen=true
type RedisLeader struct {
// Replicas overrides clusterSize for leader nodes count. If not set, uses clusterSize value
Replicas *int32 `json:"replicas,omitempty"`
RedisConfig *RedisConfig `json:"redisConfig,omitempty"`
Affinity *corev1.Affinity `json:"affinity,omitempty"`
@ -212,6 +214,7 @@ type RedisLeader struct {
// RedisFollower interface will have the redis follower configuration
// +k8s:deepcopy-gen=true
type RedisFollower struct {
// Replicas overrides clusterSize for follower nodes count. If not set, uses clusterSize value
Replicas *int32 `json:"replicas,omitempty"`
RedisConfig *RedisConfig `json:"redisConfig,omitempty"`
Affinity *corev1.Affinity `json:"affinity,omitempty"`

View File

@ -1,4 +1,4 @@
package api
package v1beta2
import (
"testing"

21
api/common/v1beta2/doc.go Normal file
View File

@ -0,0 +1,21 @@
/*
Copyright 2020 Opstree Solutions.
Licensed 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.
*/
// Package v1beta2 contains common types used by Redis Operator APIs.
// These types are shared across different Redis resource types.
//
// +groupName=redis.redis.opstreelabs.in
package v1beta2

View File

@ -18,9 +18,10 @@ limitations under the License.
// Code generated by controller-gen. DO NOT EDIT.
package api
package v1beta2
import (
appsv1 "k8s.io/api/apps/v1"
"k8s.io/api/core/v1"
)
@ -172,6 +173,11 @@ func (in *KubernetesConfig) DeepCopyInto(out *KubernetesConfig) {
}
}
in.UpdateStrategy.DeepCopyInto(&out.UpdateStrategy)
if in.PersistentVolumeClaimRetentionPolicy != nil {
in, out := &in.PersistentVolumeClaimRetentionPolicy, &out.PersistentVolumeClaimRetentionPolicy
*out = new(appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy)
**out = **in
}
if in.Service != nil {
in, out := &in.Service, &out.Service
*out = new(ServiceConfig)

View File

@ -24,7 +24,8 @@ import (
// RedisClusterSpec defines the desired state of RedisCluster
type RedisClusterSpec struct {
Size *int32 `json:"clusterSize"`
// ClusterSize defines the default number of replicas for both leader and follower when not explicitly set
ClusterSize *int32 `json:"clusterSize"`
KubernetesConfig common.KubernetesConfig `json:"kubernetesConfig"`
HostNetwork bool `json:"hostNetwork,omitempty"`
// +kubebuilder:default:=6379
@ -58,7 +59,7 @@ type ClusterStorage struct {
}
func (cr *RedisClusterSpec) GetReplicaCounts(t string) int32 {
replica := cr.Size
replica := cr.ClusterSize
if t == "leader" && cr.RedisLeader.Replicas != nil {
replica = cr.RedisLeader.Replicas
} else if t == "follower" && cr.RedisFollower.Replicas != nil {

View File

@ -17,15 +17,84 @@ limitations under the License.
package v1beta2
import (
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
ctrl "sigs.k8s.io/controller-runtime"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)
//+kubebuilder:webhook:path=/mutate-core-v1-pod,mutating=true,failurePolicy=fail,sideEffects=None,groups=core,resources=pods,verbs=create,versions=v1,name=ot-mutate-pod.opstree.com,admissionReviewVersions=v1
const (
webhookPath = "/validate-redis-redis-opstreelabs-in-v1beta2-rediscluster"
)
// log is for logging in this package.
var redisclusterlog = logf.Log.WithName("rediscluster-v1beta2-validation")
// +kubebuilder:webhook:path=/validate-redis-redis-opstreelabs-in-v1beta2-rediscluster,mutating=false,failurePolicy=fail,sideEffects=None,groups=redis.redis.opstreelabs.in,resources=redisclusters,verbs=create;update,versions=v1beta2,name=validate-rediscluster.redis.opstreelabs.in,admissionReviewVersions=v1
// SetupWebhookWithManager will setup the manager
func (r *RedisCluster) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(r).
Complete()
}
// TODO(user): EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
var _ webhook.Validator = &RedisCluster{}
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *RedisCluster) ValidateCreate() (admission.Warnings, error) {
redisclusterlog.Info("validate create", "name", r.Name)
return r.validate(nil)
}
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *RedisCluster) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
redisclusterlog.Info("validate update", "name", r.Name)
return r.validate(old.(*RedisCluster))
}
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *RedisCluster) ValidateDelete() (admission.Warnings, error) {
redisclusterlog.Info("validate delete", "name", r.Name)
return nil, nil
}
// validate validates the Redis Cluster CR
func (r *RedisCluster) validate(_ *RedisCluster) (admission.Warnings, error) {
var errors field.ErrorList
var warnings admission.Warnings
if r.Spec.ClusterSize == nil {
return warnings, nil
}
// Check if the Size is at least 3 for proper cluster operation
if *r.Spec.ClusterSize < 3 {
errors = append(errors, field.Invalid(
field.NewPath("spec").Child("clusterSize"),
*r.Spec.ClusterSize,
"Redis cluster must have at least 3 shards",
))
}
if len(errors) == 0 {
return nil, nil
}
return nil, apierrors.NewInvalid(
schema.GroupKind{Group: "redis.redis.opstreelabs.in", Kind: "RedisCluster"},
r.Name,
errors,
)
}
func (r *RedisCluster) WebhookPath() string {
return webhookPath
}

View File

@ -0,0 +1,68 @@
package v1beta2_test
import (
"encoding/json"
"fmt"
"testing"
v1beta2 "github.com/OT-CONTAINER-KIT/redis-operator/api/rediscluster/v1beta2"
"github.com/OT-CONTAINER-KIT/redis-operator/internal/testutil/webhook"
"github.com/stretchr/testify/require"
admissionv1beta1 "k8s.io/api/admission/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/utils/ptr"
)
func TestRedisClusterWebhook(t *testing.T) {
cases := []webhook.ValidationWebhookTestCase{
{
Name: "success-create-v1beta2-rediscluster-validate-clusterSize-3",
Operation: admissionv1beta1.Create,
Object: func(t *testing.T, uid string) []byte {
t.Helper()
cluster := mkRedisCluster(uid)
cluster.Spec.ClusterSize = ptr.To(int32(3))
return marshal(t, cluster)
},
Check: webhook.ValidationWebhookSucceeded,
},
{
Name: "failed-create-v1beta2-rediscluster-validate-clusterSize-2",
Operation: admissionv1beta1.Create,
Object: func(t *testing.T, uid string) []byte {
t.Helper()
cluster := mkRedisCluster(uid)
cluster.Spec.ClusterSize = ptr.To(int32(2))
return marshal(t, cluster)
},
Check: webhook.ValidationWebhookFailed("Redis cluster must have at least 3 shards"),
},
}
gvk := metav1.GroupVersionKind{
Group: "redis.redis.opstreelabs.in",
Version: "v1beta2",
Kind: "RedisCluster",
}
cluster := &v1beta2.RedisCluster{}
webhook.RunValidationWebhookTests(t, gvk, cluster, cases...)
}
func mkRedisCluster(uid string) *v1beta2.RedisCluster {
return &v1beta2.RedisCluster{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("test-%s", uid),
UID: types.UID(fmt.Sprintf("test-%s", uid)),
},
Spec: v1beta2.RedisClusterSpec{},
}
}
func marshal(t *testing.T, obj interface{}) []byte {
t.Helper()
bytes, err := json.Marshal(obj)
require.NoError(t, err)
return bytes
}

View File

@ -23,7 +23,7 @@ package v1beta2
import (
commonv1beta2 "github.com/OT-CONTAINER-KIT/redis-operator/api/common/v1beta2"
"k8s.io/api/core/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
@ -105,8 +105,8 @@ func (in *RedisClusterList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RedisClusterSpec) DeepCopyInto(out *RedisClusterSpec) {
*out = *in
if in.Size != nil {
in, out := &in.Size, &out.Size
if in.ClusterSize != nil {
in, out := &in.ClusterSize, &out.ClusterSize
*out = new(int32)
**out = **in
}

View File

@ -68,7 +68,7 @@ helm delete <my-release> --namespace <namespace>
| podSecurityContext.fsGroup | int | `1000` | |
| podSecurityContext.runAsUser | int | `1000` | |
| priorityClassName | string | `""` | |
| redisCluster.clusterSize | int | `3` | |
| redisCluster.clusterSize | int | `3` | Default number of replicas for both leader and follower when not explicitly set |
| redisCluster.clusterVersion | string | `"v7"` | |
| redisCluster.enableMasterSlaveAntiAffinity | bool | `false` | Enable pod anti-affinity between leader and follower pods by adding the appropriate label. Notice that this requires the operator to have its mutating webhook enabled, otherwise it will only add an annotation to the RedisCluster CR. Default is false. |
| redisCluster.follower.affinity | string | `nil` | |
@ -78,7 +78,7 @@ helm delete <my-release> --namespace <namespace>
| redisCluster.follower.pdb.maxUnavailable | int | `1` | |
| redisCluster.follower.pdb.minAvailable | int | `1` | |
| redisCluster.follower.readinessProbe | object | `{}` | |
| redisCluster.follower.replicas | int | `3` | |
| redisCluster.follower.replicas | int | `3` | Number of Redis follower (slave) nodes. If not set, uses clusterSize value |
| redisCluster.follower.securityContext | object | `{}` | |
| redisCluster.follower.serviceType | string | `"ClusterIP"` | |
| redisCluster.follower.tolerations | list | `[]` | |
@ -92,7 +92,7 @@ helm delete <my-release> --namespace <namespace>
| redisCluster.leader.pdb.maxUnavailable | int | `1` | |
| redisCluster.leader.pdb.minAvailable | int | `1` | |
| redisCluster.leader.readinessProbe | object | `{}` | |
| redisCluster.leader.replicas | int | `3` | |
| redisCluster.leader.replicas | int | `3` | Number of Redis leader (master) nodes. If not set, uses clusterSize value |
| redisCluster.leader.securityContext | object | `{}` | |
| redisCluster.leader.serviceType | string | `"ClusterIP"` | |
| redisCluster.leader.tolerations | list | `[]` | |

View File

@ -1,6 +1,7 @@
---
redisCluster:
name: ""
# -- Default number of replicas for both leader and follower when not explicitly set
clusterSize: 3
clusterVersion: v7
persistenceEnabled: true
@ -28,6 +29,7 @@ redisCluster:
# otherwise it will only add an annotation to the RedisCluster CR. Default is false.
enableMasterSlaveAntiAffinity: false
leader:
# -- Number of Redis leader (master) nodes. If not set, uses clusterSize value
replicas: 3
serviceType: ClusterIP
affinity: {}
@ -65,6 +67,7 @@ redisCluster:
# initialDelaySeconds: 15
follower:
# -- Number of Redis follower (slave) nodes. If not set, uses clusterSize value
replicas: 3
serviceType: ClusterIP
affinity: null

View File

@ -102,6 +102,8 @@ kubectl create secret tls <webhook-server-cert> --key tls.key --cert tls.crt -n
| issuer.solver.enabled | bool | `true` | |
| issuer.solver.ingressClass | string | `"nginx"` | |
| issuer.type | string | `"selfSigned"` | |
| manager.config.kubeClientQPS | int | `0` | If value > 0, it will override the default value in the operator |
| manager.config.kubeClientTimeout | string | `"60s"` | |
| nodeSelector | object | `{}` | |
| podSecurityContext | object | `{}` | |
| priorityClassName | string | `""` | |

View File

@ -1564,6 +1564,27 @@ spec:
minReadySeconds:
format: int32
type: integer
persistentVolumeClaimRetentionPolicy:
description: |-
StatefulSetPersistentVolumeClaimRetentionPolicy describes the policy used for PVCs
created from the StatefulSet VolumeClaimTemplates.
properties:
whenDeleted:
description: |-
WhenDeleted specifies what happens to PVCs created from StatefulSet
VolumeClaimTemplates when the StatefulSet is deleted. The default policy
of `Retain` causes PVCs to not be affected by StatefulSet deletion. The
`Delete` policy causes those PVCs to be deleted.
type: string
whenScaled:
description: |-
WhenScaled specifies what happens to PVCs created from StatefulSet
VolumeClaimTemplates when the StatefulSet is scaled down. The default
policy of `Retain` causes PVCs to not be affected by a scaledown. The
`Delete` policy causes the associated PVCs for any excess pods above
the replica count to be deleted.
type: string
type: object
redisSecret:
description: ExistingPasswordSecret is the struct to access the
existing secret
@ -5580,6 +5601,8 @@ spec:
type: object
type: object
clusterSize:
description: ClusterSize defines the default number of replicas for
both leader and follower when not explicitly set
format: int32
type: integer
clusterVersion:
@ -6085,6 +6108,27 @@ spec:
minReadySeconds:
format: int32
type: integer
persistentVolumeClaimRetentionPolicy:
description: |-
StatefulSetPersistentVolumeClaimRetentionPolicy describes the policy used for PVCs
created from the StatefulSet VolumeClaimTemplates.
properties:
whenDeleted:
description: |-
WhenDeleted specifies what happens to PVCs created from StatefulSet
VolumeClaimTemplates when the StatefulSet is deleted. The default policy
of `Retain` causes PVCs to not be affected by StatefulSet deletion. The
`Delete` policy causes those PVCs to be deleted.
type: string
whenScaled:
description: |-
WhenScaled specifies what happens to PVCs created from StatefulSet
VolumeClaimTemplates when the StatefulSet is scaled down. The default
policy of `Retain` causes PVCs to not be affected by a scaledown. The
`Delete` policy causes the associated PVCs for any excess pods above
the replica count to be deleted.
type: string
type: object
redisSecret:
description: ExistingPasswordSecret is the struct to access the
existing secret
@ -8042,6 +8086,8 @@ spec:
type: integer
type: object
replicas:
description: Replicas overrides clusterSize for follower nodes
count. If not set, uses clusterSize value
format: int32
type: integer
resources:
@ -9719,6 +9765,8 @@ spec:
type: integer
type: object
replicas:
description: Replicas overrides clusterSize for leader nodes count.
If not set, uses clusterSize value
format: int32
type: integer
resources:
@ -14779,6 +14827,27 @@ spec:
minReadySeconds:
format: int32
type: integer
persistentVolumeClaimRetentionPolicy:
description: |-
StatefulSetPersistentVolumeClaimRetentionPolicy describes the policy used for PVCs
created from the StatefulSet VolumeClaimTemplates.
properties:
whenDeleted:
description: |-
WhenDeleted specifies what happens to PVCs created from StatefulSet
VolumeClaimTemplates when the StatefulSet is deleted. The default policy
of `Retain` causes PVCs to not be affected by StatefulSet deletion. The
`Delete` policy causes those PVCs to be deleted.
type: string
whenScaled:
description: |-
WhenScaled specifies what happens to PVCs created from StatefulSet
VolumeClaimTemplates when the StatefulSet is scaled down. The default
policy of `Retain` causes PVCs to not be affected by a scaledown. The
`Delete` policy causes the associated PVCs for any excess pods above
the replica count to be deleted.
type: string
type: object
redisSecret:
description: ExistingPasswordSecret is the struct to access the
existing secret
@ -20277,6 +20346,27 @@ spec:
minReadySeconds:
format: int32
type: integer
persistentVolumeClaimRetentionPolicy:
description: |-
StatefulSetPersistentVolumeClaimRetentionPolicy describes the policy used for PVCs
created from the StatefulSet VolumeClaimTemplates.
properties:
whenDeleted:
description: |-
WhenDeleted specifies what happens to PVCs created from StatefulSet
VolumeClaimTemplates when the StatefulSet is deleted. The default policy
of `Retain` causes PVCs to not be affected by StatefulSet deletion. The
`Delete` policy causes those PVCs to be deleted.
type: string
whenScaled:
description: |-
WhenScaled specifies what happens to PVCs created from StatefulSet
VolumeClaimTemplates when the StatefulSet is scaled down. The default
policy of `Retain` causes PVCs to not be affected by a scaledown. The
`Delete` policy causes the associated PVCs for any excess pods above
the replica count to be deleted.
type: string
type: object
redisSecret:
description: ExistingPasswordSecret is the struct to access the
existing secret

View File

@ -53,6 +53,12 @@ spec:
{{- if .Values.redisOperator.metrics.enabled }}
- --metrics-bind-address={{ .Values.redisOperator.metrics.bindAddress }}
{{- end }}
{{- if .Values.manager.config.kubeClientTimeout }}
- --kube-client-timeout={{ .Values.manager.config.kubeClientTimeout }}
{{- end }}
{{- if and .Values.manager.config.kubeClientQPS (gt (.Values.manager.config.kubeClientQPS | float64) 0) }}
- --kube-client-qps={{ .Values.manager.config.kubeClientQPS }}
{{- end }}
{{- range $arg := .Values.redisOperator.extraArgs }}
- {{ $arg }}
{{- end }}

View File

@ -108,3 +108,10 @@ securityContext: {}
featureGates:
# Enable generating Redis configuration using an init container instead of a regular container
GenerateConfigInInitContainer: false
manager:
# config values for the operator manager
config:
kubeClientTimeout: 60s
# -- If value > 0, it will override the default value in the operator
kubeClientQPS: 0

View File

@ -1,8 +1,8 @@
apiVersion: v2
name: redis-replication
description: Provides easy redis setup definitions for Kubernetes services, and deployment.
version: 0.16.7
appVersion: "0.16.7"
version: 0.16.8
appVersion: "0.16.8"
type: application
engine: gotpl
maintainers:

View File

@ -65,6 +65,9 @@ helm delete <my-release> --namespace <namespace>
| initContainer.resources | object | `{}` | |
| labels | object | `{}` | |
| nodeSelector | object | `{}` | |
| pdb.enabled | bool | `false` | |
| pdb.maxUnavailable | string | `nil` | |
| pdb.minAvailable | int | `1` | |
| podSecurityContext.fsGroup | int | `1000` | |
| podSecurityContext.runAsUser | int | `1000` | |
| priorityClassName | string | `""` | |

View File

@ -95,4 +95,10 @@ spec:
{{- if .Values.env }}
env: {{ toYaml .Values.env | nindent 4 }}
{{- end }}
{{- if .Values.pdb.enabled }}
pdb:
enabled: {{ .Values.pdb.enabled }}
minAvailable: {{ .Values.pdb.minAvailable }}
maxUnavailable: {{ .Values.pdb.maxUnavailable }}
{{- end }}

View File

@ -156,3 +156,8 @@ acl:
env: []
# - name: VAR_NAME
# value: "value1"
pdb:
enabled: false
minAvailable: 1
maxUnavailable: null

View File

@ -1565,6 +1565,27 @@ spec:
minReadySeconds:
format: int32
type: integer
persistentVolumeClaimRetentionPolicy:
description: |-
StatefulSetPersistentVolumeClaimRetentionPolicy describes the policy used for PVCs
created from the StatefulSet VolumeClaimTemplates.
properties:
whenDeleted:
description: |-
WhenDeleted specifies what happens to PVCs created from StatefulSet
VolumeClaimTemplates when the StatefulSet is deleted. The default policy
of `Retain` causes PVCs to not be affected by StatefulSet deletion. The
`Delete` policy causes those PVCs to be deleted.
type: string
whenScaled:
description: |-
WhenScaled specifies what happens to PVCs created from StatefulSet
VolumeClaimTemplates when the StatefulSet is scaled down. The default
policy of `Retain` causes PVCs to not be affected by a scaledown. The
`Delete` policy causes the associated PVCs for any excess pods above
the replica count to be deleted.
type: string
type: object
redisSecret:
description: ExistingPasswordSecret is the struct to access the
existing secret

View File

@ -210,6 +210,8 @@ spec:
type: object
type: object
clusterSize:
description: ClusterSize defines the default number of replicas for
both leader and follower when not explicitly set
format: int32
type: integer
clusterVersion:
@ -715,6 +717,27 @@ spec:
minReadySeconds:
format: int32
type: integer
persistentVolumeClaimRetentionPolicy:
description: |-
StatefulSetPersistentVolumeClaimRetentionPolicy describes the policy used for PVCs
created from the StatefulSet VolumeClaimTemplates.
properties:
whenDeleted:
description: |-
WhenDeleted specifies what happens to PVCs created from StatefulSet
VolumeClaimTemplates when the StatefulSet is deleted. The default policy
of `Retain` causes PVCs to not be affected by StatefulSet deletion. The
`Delete` policy causes those PVCs to be deleted.
type: string
whenScaled:
description: |-
WhenScaled specifies what happens to PVCs created from StatefulSet
VolumeClaimTemplates when the StatefulSet is scaled down. The default
policy of `Retain` causes PVCs to not be affected by a scaledown. The
`Delete` policy causes the associated PVCs for any excess pods above
the replica count to be deleted.
type: string
type: object
redisSecret:
description: ExistingPasswordSecret is the struct to access the
existing secret
@ -2672,6 +2695,8 @@ spec:
type: integer
type: object
replicas:
description: Replicas overrides clusterSize for follower nodes
count. If not set, uses clusterSize value
format: int32
type: integer
resources:
@ -4349,6 +4374,8 @@ spec:
type: integer
type: object
replicas:
description: Replicas overrides clusterSize for leader nodes count.
If not set, uses clusterSize value
format: int32
type: integer
resources:

View File

@ -1574,6 +1574,27 @@ spec:
minReadySeconds:
format: int32
type: integer
persistentVolumeClaimRetentionPolicy:
description: |-
StatefulSetPersistentVolumeClaimRetentionPolicy describes the policy used for PVCs
created from the StatefulSet VolumeClaimTemplates.
properties:
whenDeleted:
description: |-
WhenDeleted specifies what happens to PVCs created from StatefulSet
VolumeClaimTemplates when the StatefulSet is deleted. The default policy
of `Retain` causes PVCs to not be affected by StatefulSet deletion. The
`Delete` policy causes those PVCs to be deleted.
type: string
whenScaled:
description: |-
WhenScaled specifies what happens to PVCs created from StatefulSet
VolumeClaimTemplates when the StatefulSet is scaled down. The default
policy of `Retain` causes PVCs to not be affected by a scaledown. The
`Delete` policy causes the associated PVCs for any excess pods above
the replica count to be deleted.
type: string
type: object
redisSecret:
description: ExistingPasswordSecret is the struct to access the
existing secret

View File

@ -1500,6 +1500,27 @@ spec:
minReadySeconds:
format: int32
type: integer
persistentVolumeClaimRetentionPolicy:
description: |-
StatefulSetPersistentVolumeClaimRetentionPolicy describes the policy used for PVCs
created from the StatefulSet VolumeClaimTemplates.
properties:
whenDeleted:
description: |-
WhenDeleted specifies what happens to PVCs created from StatefulSet
VolumeClaimTemplates when the StatefulSet is deleted. The default policy
of `Retain` causes PVCs to not be affected by StatefulSet deletion. The
`Delete` policy causes those PVCs to be deleted.
type: string
whenScaled:
description: |-
WhenScaled specifies what happens to PVCs created from StatefulSet
VolumeClaimTemplates when the StatefulSet is scaled down. The default
policy of `Retain` causes PVCs to not be affected by a scaledown. The
`Delete` policy causes the associated PVCs for any excess pods above
the replica count to be deleted.
type: string
type: object
redisSecret:
description: ExistingPasswordSecret is the struct to access the
existing secret

View File

@ -1,8 +1,8 @@
---
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
kind: ValidatingWebhookConfiguration
metadata:
name: mutating-webhook-configuration
name: validating-webhook-configuration
webhooks:
- admissionReviewVersions:
- v1
@ -10,25 +10,20 @@ webhooks:
service:
name: webhook-service
namespace: system
path: /mutate-core-v1-pod
path: /validate-redis-redis-opstreelabs-in-v1beta2-rediscluster
failurePolicy: Fail
name: ot-mutate-pod.opstree.com
name: validate-rediscluster.redis.opstreelabs.in
rules:
- apiGroups:
- ""
- redis.redis.opstreelabs.in
apiVersions:
- v1
- v1beta2
operations:
- CREATE
- UPDATE
resources:
- pods
- redisclusters
sideEffects: None
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: validating-webhook-configuration
webhooks:
- admissionReviewVersions:
- v1
clientConfig:

View File

@ -0,0 +1,637 @@
---
title: "API Reference Documentation"
linkTitle: "API Docs"
weight: 10
date: 2025-01-27
description: >
Complete API reference documentation for Redis Operator CRDs
---
# API Reference
## Packages
- [redis.redis.opstreelabs.in/v1beta2](#redisredisopstreelabsinv1beta2)
## redis.redis.opstreelabs.in/v1beta2
Package v1beta2 contains common types used by Redis Operator APIs.
These types are shared across different Redis resource types.
### Resource Types
- [Redis](#redis)
- [RedisCluster](#rediscluster)
- [RedisReplication](#redisreplication)
- [RedisSentinel](#redissentinel)
#### ACLConfig
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
- [RedisReplicationSpec](#redisreplicationspec)
- [RedisSpec](#redisspec)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `secret` _[SecretVolumeSource](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#secretvolumesource-v1-core)_ | | | |
#### AdditionalVolume
Additional Volume is provided by user that is mounted on the pods
_Appears in:_
- [ClusterStorage](#clusterstorage)
- [RedisSentinelSpec](#redissentinelspec)
- [Storage](#storage)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `volume` _[Volume](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#volume-v1-core) array_ | | | |
| `mountPath` _[VolumeMount](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#volumemount-v1-core) array_ | | | |
#### ClusterStorage
Node-conf needs to be added only in redis cluster
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `nodeConfVolume` _boolean_ | | false | |
| `nodeConfVolumeClaimTemplate` _[PersistentVolumeClaim](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#persistentvolumeclaim-v1-core)_ | | | |
| `keepAfterDelete` _boolean_ | | | |
| `volumeClaimTemplate` _[PersistentVolumeClaim](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#persistentvolumeclaim-v1-core)_ | | | |
| `volumeMount` _[AdditionalVolume](#additionalvolume)_ | | | |
#### ExistingPasswordSecret
ExistingPasswordSecret is the struct to access the existing secret
_Appears in:_
- [KubernetesConfig](#kubernetesconfig)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `name` _string_ | | | |
| `key` _string_ | | | |
#### InitContainer
InitContainer for each Redis pods
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
- [RedisReplicationSpec](#redisreplicationspec)
- [RedisSentinelSpec](#redissentinelspec)
- [RedisSpec](#redisspec)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `enabled` _boolean_ | | | |
| `image` _string_ | | | |
| `imagePullPolicy` _[PullPolicy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#pullpolicy-v1-core)_ | | | |
| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#resourcerequirements-v1-core)_ | | | |
| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#envvar-v1-core)_ | | | |
| `command` _string array_ | | | |
| `args` _string array_ | | | |
| `securityContext` _[SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#securitycontext-v1-core)_ | | | |
#### KubernetesConfig
KubernetesConfig will be the JSON struct for Basic Redis Config
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
- [RedisReplicationSpec](#redisreplicationspec)
- [RedisSentinelSpec](#redissentinelspec)
- [RedisSpec](#redisspec)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `image` _string_ | | | |
| `imagePullPolicy` _[PullPolicy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#pullpolicy-v1-core)_ | | | |
| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#resourcerequirements-v1-core)_ | | | |
| `redisSecret` _[ExistingPasswordSecret](#existingpasswordsecret)_ | | | |
| `imagePullSecrets` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#localobjectreference-v1-core)_ | | | |
| `updateStrategy` _[StatefulSetUpdateStrategy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#statefulsetupdatestrategy-v1-apps)_ | | | |
| `persistentVolumeClaimRetentionPolicy` _[StatefulSetPersistentVolumeClaimRetentionPolicy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#statefulsetpersistentvolumeclaimretentionpolicy-v1-apps)_ | | | |
| `service` _[ServiceConfig](#serviceconfig)_ | | | |
| `ignoreAnnotations` _string array_ | | | |
| `minReadySeconds` _integer_ | | | |
#### Redis
Redis is the Schema for the redis API
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `apiVersion` _string_ | `redis.redis.opstreelabs.in/v1beta2` | | |
| `kind` _string_ | `Redis` | | |
| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | |
| `spec` _[RedisSpec](#redisspec)_ | | | |
#### RedisCluster
RedisCluster is the Schema for the redisclusters API
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `apiVersion` _string_ | `redis.redis.opstreelabs.in/v1beta2` | | |
| `kind` _string_ | `RedisCluster` | | |
| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | |
| `spec` _[RedisClusterSpec](#redisclusterspec)_ | | | |
#### RedisClusterSpec
RedisClusterSpec defines the desired state of RedisCluster
_Appears in:_
- [RedisCluster](#rediscluster)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `clusterSize` _integer_ | ClusterSize defines the default number of replicas for both leader and follower when not explicitly set | | |
| `kubernetesConfig` _[KubernetesConfig](#kubernetesconfig)_ | | | |
| `hostNetwork` _boolean_ | | | |
| `port` _integer_ | | 6379 | |
| `clusterVersion` _string_ | | v7 | |
| `redisConfig` _[RedisConfig](#redisconfig)_ | | | |
| `redisLeader` _[RedisLeader](#redisleader)_ | | | |
| `redisFollower` _[RedisFollower](#redisfollower)_ | | | |
| `redisExporter` _[RedisExporter](#redisexporter)_ | | | |
| `storage` _[ClusterStorage](#clusterstorage)_ | | | |
| `podSecurityContext` _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#podsecuritycontext-v1-core)_ | | | |
| `priorityClassName` _string_ | | | |
| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#resourcerequirements-v1-core)_ | | | |
| `TLS` _[TLSConfig](#tlsconfig)_ | | | |
| `acl` _[ACLConfig](#aclconfig)_ | | | |
| `initContainer` _[InitContainer](#initcontainer)_ | | | |
| `sidecars` _[Sidecar](#sidecar)_ | | | |
| `serviceAccountName` _string_ | | | |
| `persistenceEnabled` _boolean_ | | | |
| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#envvar-v1-core)_ | | | |
| `hostPort` _integer_ | | | |
#### RedisConfig
RedisConfig defines the external configuration of Redis
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
- [RedisFollower](#redisfollower)
- [RedisFollower](#redisfollower)
- [RedisLeader](#redisleader)
- [RedisLeader](#redisleader)
- [RedisReplicationSpec](#redisreplicationspec)
- [RedisSpec](#redisspec)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `maxMemoryPercentOfLimit` _integer_ | MaxMemoryPercentOfLimit is the percentage of redis container memory limit to be used as maxmemory. | | Maximum: 100 <br />Minimum: 1 <br /> |
| `dynamicConfig` _string array_ | | | |
| `additionalRedisConfig` _string_ | | | |
#### RedisExporter
RedisExporter interface will have the information for redis exporter related stuff
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
- [RedisReplicationSpec](#redisreplicationspec)
- [RedisSentinelSpec](#redissentinelspec)
- [RedisSpec](#redisspec)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `enabled` _boolean_ | | | |
| `port` _integer_ | | 9121 | |
| `image` _string_ | | | |
| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#resourcerequirements-v1-core)_ | | | |
| `imagePullPolicy` _[PullPolicy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#pullpolicy-v1-core)_ | | | |
| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#envvar-v1-core)_ | | | |
| `securityContext` _[SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#securitycontext-v1-core)_ | | | |
#### RedisFollower
RedisFollower interface will have the redis follower configuration
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `replicas` _integer_ | Replicas overrides clusterSize for follower nodes count. If not set, uses clusterSize value | | |
| `redisConfig` _[RedisConfig](#redisconfig)_ | | | |
| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#affinity-v1-core)_ | | | |
| `pdb` _[RedisPodDisruptionBudget](#redispoddisruptionbudget)_ | | | |
| `readinessProbe` _[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#probe-v1-core)_ | | | |
| `livenessProbe` _[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#probe-v1-core)_ | | | |
| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#toleration-v1-core)_ | | | |
| `nodeSelector` _object (keys:string, values:string)_ | | | |
| `topologySpreadConstraints` _[TopologySpreadConstraint](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#topologyspreadconstraint-v1-core) array_ | | | |
| `securityContext` _[SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#securitycontext-v1-core)_ | | | |
| `terminationGracePeriodSeconds` _integer_ | | | |
| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#resourcerequirements-v1-core)_ | | | |
#### RedisLeader
RedisLeader interface will have the redis leader configuration
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `replicas` _integer_ | Replicas overrides clusterSize for leader nodes count. If not set, uses clusterSize value | | |
| `redisConfig` _[RedisConfig](#redisconfig)_ | | | |
| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#affinity-v1-core)_ | | | |
| `pdb` _[RedisPodDisruptionBudget](#redispoddisruptionbudget)_ | | | |
| `readinessProbe` _[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#probe-v1-core)_ | | | |
| `livenessProbe` _[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#probe-v1-core)_ | | | |
| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#toleration-v1-core)_ | | | |
| `nodeSelector` _object (keys:string, values:string)_ | | | |
| `topologySpreadConstraints` _[TopologySpreadConstraint](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#topologyspreadconstraint-v1-core) array_ | | | |
| `securityContext` _[SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#securitycontext-v1-core)_ | | | |
| `terminationGracePeriodSeconds` _integer_ | | | |
| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#resourcerequirements-v1-core)_ | | | |
#### RedisPodDisruptionBudget
RedisPodDisruptionBudget configure a PodDisruptionBudget on the resource (leader/follower)
_Appears in:_
- [RedisFollower](#redisfollower)
- [RedisFollower](#redisfollower)
- [RedisLeader](#redisleader)
- [RedisLeader](#redisleader)
- [RedisReplicationSpec](#redisreplicationspec)
- [RedisSentinelSpec](#redissentinelspec)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `enabled` _boolean_ | | | |
| `minAvailable` _integer_ | | | |
| `maxUnavailable` _integer_ | | | |
#### RedisReplication
Redis is the Schema for the redis API
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `apiVersion` _string_ | `redis.redis.opstreelabs.in/v1beta2` | | |
| `kind` _string_ | `RedisReplication` | | |
| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | |
| `spec` _[RedisReplicationSpec](#redisreplicationspec)_ | | | |
#### RedisReplicationSpec
_Appears in:_
- [RedisReplication](#redisreplication)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `clusterSize` _integer_ | | | |
| `kubernetesConfig` _[KubernetesConfig](#kubernetesconfig)_ | | | |
| `redisExporter` _[RedisExporter](#redisexporter)_ | | | |
| `redisConfig` _[RedisConfig](#redisconfig)_ | | | |
| `storage` _[Storage](#storage)_ | | | |
| `nodeSelector` _object (keys:string, values:string)_ | | | |
| `podSecurityContext` _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#podsecuritycontext-v1-core)_ | | | |
| `securityContext` _[SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#securitycontext-v1-core)_ | | | |
| `priorityClassName` _string_ | | | |
| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#affinity-v1-core)_ | | | |
| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#toleration-v1-core)_ | | | |
| `TLS` _[TLSConfig](#tlsconfig)_ | | | |
| `pdb` _[RedisPodDisruptionBudget](#redispoddisruptionbudget)_ | | | |
| `acl` _[ACLConfig](#aclconfig)_ | | | |
| `readinessProbe` _[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#probe-v1-core)_ | | | |
| `livenessProbe` _[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#probe-v1-core)_ | | | |
| `initContainer` _[InitContainer](#initcontainer)_ | | | |
| `sidecars` _[Sidecar](#sidecar)_ | | | |
| `serviceAccountName` _string_ | | | |
| `terminationGracePeriodSeconds` _integer_ | | | |
| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#envvar-v1-core)_ | | | |
| `topologySpreadConstraints` _[TopologySpreadConstraint](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#topologyspreadconstraint-v1-core) array_ | | | |
| `hostPort` _integer_ | | | |
#### RedisSentinel
Redis is the Schema for the redis API
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `apiVersion` _string_ | `redis.redis.opstreelabs.in/v1beta2` | | |
| `kind` _string_ | `RedisSentinel` | | |
| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | |
| `spec` _[RedisSentinelSpec](#redissentinelspec)_ | | | |
#### RedisSentinelConfig
_Appears in:_
- [RedisSentinelSpec](#redissentinelspec)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `additionalSentinelConfig` _string_ | | | |
| `redisReplicationName` _string_ | | | |
| `redisReplicationPassword` _[EnvVarSource](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#envvarsource-v1-core)_ | | | |
| `masterGroupName` _string_ | | myMaster | |
| `redisPort` _string_ | | 6379 | |
| `quorum` _string_ | | 2 | |
| `parallelSyncs` _string_ | | 1 | |
| `failoverTimeout` _string_ | | 180000 | |
| `downAfterMilliseconds` _string_ | | 30000 | |
| `resolveHostnames` _string_ | | no | |
| `announceHostnames` _string_ | | no | |
#### RedisSentinelSpec
_Appears in:_
- [RedisSentinel](#redissentinel)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `clusterSize` _integer_ | | 3 | Minimum: 1 <br /> |
| `kubernetesConfig` _[KubernetesConfig](#kubernetesconfig)_ | | | |
| `redisExporter` _[RedisExporter](#redisexporter)_ | | | |
| `redisSentinelConfig` _[RedisSentinelConfig](#redissentinelconfig)_ | | | |
| `nodeSelector` _object (keys:string, values:string)_ | | | |
| `podSecurityContext` _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#podsecuritycontext-v1-core)_ | | | |
| `securityContext` _[SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#securitycontext-v1-core)_ | | | |
| `priorityClassName` _string_ | | | |
| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#affinity-v1-core)_ | | | |
| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#toleration-v1-core)_ | | | |
| `TLS` _[TLSConfig](#tlsconfig)_ | | | |
| `pdb` _[RedisPodDisruptionBudget](#redispoddisruptionbudget)_ | | | |
| `readinessProbe` _[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#probe-v1-core)_ | | | |
| `livenessProbe` _[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#probe-v1-core)_ | | | |
| `initContainer` _[InitContainer](#initcontainer)_ | | | |
| `sidecars` _[Sidecar](#sidecar)_ | | | |
| `serviceAccountName` _string_ | | | |
| `terminationGracePeriodSeconds` _integer_ | | | |
| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#envvar-v1-core)_ | | | |
| `volumeMount` _[AdditionalVolume](#additionalvolume)_ | | | |
| `topologySpreadConstraints` _[TopologySpreadConstraint](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#topologyspreadconstraint-v1-core) array_ | | | |
| `hostPort` _integer_ | | | |
#### RedisSpec
RedisSpec defines the desired state of Redis
_Appears in:_
- [Redis](#redis)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `kubernetesConfig` _[KubernetesConfig](#kubernetesconfig)_ | | | |
| `redisExporter` _[RedisExporter](#redisexporter)_ | | | |
| `redisConfig` _[RedisConfig](#redisconfig)_ | | | |
| `storage` _[Storage](#storage)_ | | | |
| `nodeSelector` _object (keys:string, values:string)_ | | | |
| `podSecurityContext` _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#podsecuritycontext-v1-core)_ | | | |
| `securityContext` _[SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#securitycontext-v1-core)_ | | | |
| `priorityClassName` _string_ | | | |
| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#affinity-v1-core)_ | | | |
| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#toleration-v1-core)_ | | | |
| `TLS` _[TLSConfig](#tlsconfig)_ | | | |
| `acl` _[ACLConfig](#aclconfig)_ | | | |
| `readinessProbe` _[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#probe-v1-core)_ | | | |
| `livenessProbe` _[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#probe-v1-core)_ | | | |
| `initContainer` _[InitContainer](#initcontainer)_ | | | |
| `sidecars` _[Sidecar](#sidecar)_ | | | |
| `serviceAccountName` _string_ | | | |
| `terminationGracePeriodSeconds` _integer_ | | | |
| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#envvar-v1-core)_ | | | |
| `hostPort` _integer_ | | | |
#### Service
Service is the struct to define the service type and its annotations
_Appears in:_
- [ServiceConfig](#serviceconfig)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `type` _string_ | | ClusterIP | Enum: [LoadBalancer NodePort ClusterIP] <br /> |
| `additionalAnnotations` _object (keys:string, values:string)_ | | | |
| `includeBusPort` _boolean_ | IncludeBusPort when set to true, it will add bus port to the service, such as 16379.<br />This field is only used for Redis cluster mode. | | |
| `enabled` _boolean_ | | true | |
#### ServiceConfig
ServiceConfig define the type of service to be created and its annotations
_Appears in:_
- [KubernetesConfig](#kubernetesconfig)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `serviceType` _string_ | | | Enum: [LoadBalancer NodePort ClusterIP] <br /> |
| `annotations` _object (keys:string, values:string)_ | | | |
| `includeBusPort` _boolean_ | IncludeBusPort when set to true, it will add bus port to the service, such as 16379.<br />This field is only used for Redis cluster mode. | | |
| `headless` _[Service](#service)_ | Headless config for which suffix is -headless service | | |
| `additional` _[Service](#service)_ | Additional config for which suffix is -additional service | | |
#### Sidecar
Sidecar for each Redis pods
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
- [RedisReplicationSpec](#redisreplicationspec)
- [RedisSentinelSpec](#redissentinelspec)
- [RedisSpec](#redisspec)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `name` _string_ | | | |
| `image` _string_ | | | |
| `imagePullPolicy` _[PullPolicy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#pullpolicy-v1-core)_ | | | |
| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#resourcerequirements-v1-core)_ | | | |
| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#envvar-v1-core)_ | | | |
| `mountPath` _[VolumeMount](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#volumemount-v1-core)_ | | | |
| `command` _string array_ | | | |
| `ports` _[ContainerPort](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#containerport-v1-core)_ | | | |
| `securityContext` _[SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#securitycontext-v1-core)_ | | | |
#### Storage
Storage is the inteface to add pvc and pv support in redis
_Appears in:_
- [ClusterStorage](#clusterstorage)
- [RedisReplicationSpec](#redisreplicationspec)
- [RedisSpec](#redisspec)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `keepAfterDelete` _boolean_ | | | |
| `volumeClaimTemplate` _[PersistentVolumeClaim](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#persistentvolumeclaim-v1-core)_ | | | |
| `volumeMount` _[AdditionalVolume](#additionalvolume)_ | | | |
#### TLSConfig
TLS Configuration for redis instances
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
- [RedisReplicationSpec](#redisreplicationspec)
- [RedisSentinelSpec](#redissentinelspec)
- [RedisSpec](#redisspec)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `ca` _string_ | | | |
| `cert` _string_ | | | |
| `key` _string_ | | | |
| `secret` _[SecretVolumeSource](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#secretvolumesource-v1-core)_ | Reference to secret which contains the certificates | | |

View File

@ -1,399 +0,0 @@
---
title: "Custom Resource Object API"
linkTitle: "Custom Resource Object API"
weight: 10
date: 2022-11-02T00:19:19Z
description: >
CRD Schema details for Redis and Redis Cluster Reference API
---
# Redis API Reference
This page documents the Redis API Schema definitions for the redis API group.
## Packages
- [redis.redis.opstreelabs.in/v1beta2](#redisredisopstreelabsinv1beta2)
## redis.redis.opstreelabs.in/v1beta2
Package v1beta2 contains API Schema definitions for the redis v1beta2 API group
### Annotations
Redis Operator supports the following annotations that can be added to Redis, RedisCluster, RedisReplication, and RedisSentinel resources:
| Annotation | Description | Default | Values |
| --- | --- | --- | --- |
| `redis.opstreelabs.in/recreate-statefulset` | Controls whether the StatefulSet should be recreated when changed | `false` | `"true"`, `"false"` |
| `redis.opstreelabs.in/recreate-statefulset-strategy` | Controls how dependent resources are handled when the StatefulSet is recreated | `foreground` | `"foreground"`, `"background"`, `"orphan"` |
#### Deletion Propagation Strategies
When `redis.opstreelabs.in/recreate-statefulset` is set to `"true"`, you can control the deletion behavior using the `redis.opstreelabs.in/recreate-statefulset-strategy` annotation:
- **foreground**: The StatefulSet and its dependent objects (like Pods) are deleted synchronously
- **background**: The StatefulSet is deleted immediately, and dependent objects are deleted asynchronously
- **orphan**: The StatefulSet is deleted but its dependent objects (Pods) are kept running
### Resource Types
- [Redis](#redis)
- [RedisCluster](#rediscluster)
- [RedisReplication](#redisreplication)
- [RedisSentinel](#redissentinel)
#### ExistingPasswordSecret
ExistingPasswordSecret is the struct to access the existing secret
_Appears in:_
- [KubernetesConfig](#kubernetesconfig)
| Field | Description |
| --- | --- |
| `name` _string_ | |
| `key` _string_ | |
#### KubernetesConfig
KubernetesConfig will be the JSON struct for Basic Redis Config
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
- [RedisSpec](#redisspec)
- [RedisReplicationSpec](#redisreplicationspec)
- [RedisSentinel](#redissentinelspec)
| Field | Description |
| --- | --- |
| `image` _string_ | |
| `imagePullPolicy` _[ImagePullPolicy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy)_ | |
| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#resourcerequirements-v1-core)_ | |
| `redisSecret` _[ExistingPasswordSecret](#existingpasswordsecret)_ | |
| `imagePullSecrets` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#localobjectreference-v1-core)_ | |
| `updateStrategy` _[StatefulSetUpdateStrategy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#statefulsetupdatestrategy-v1-apps)_ | |
#### VolumeMount
Mount External Volumes
_Appears in:_
- [RedisSentinel](#redissentinelspec)
| Field | Description |
| --- | --- |
| `volume` _[Volume Array](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#volume-v1-core)_ | |
| `mountPath` _[VolumeMount Array](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#volumemount-v1-core)_ | |
#### Redis
Redis is the Schema for the redis API
| Field | Description | Scheme | Required |
| ----- | ----------- | ------ | -------- |
| `apiVersion` _string_ | `redis.redis.opstreelabs.in/v1beta2` | | |
| `kind` _string_ | `Redis`
| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. |
| `spec` _[RedisSpec](#redisspec)_ | |
#### RedisCluster
RedisCluster is the Schema for the redisclusters API
| Field | Description |
| --- | --- |
| `apiVersion` _string_ | `redis.redis.opstreelabs.in/v1beta2`
| `kind` _string_ | `RedisCluster`
| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. |
| `spec` _[RedisClusterSpec](#redisclusterspec)_ | |
#### RedisReplication
RedisReplication is the Schema for the redisreplication API
| Field | Description |
| --- | --- |
| `apiVersion` _string_ | `redis.redis.opstreelabs.in/v1beta2`
| `kind` _string_ | `RedisReplication`
| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. |
| `spec` _[RedisReplicationSpec](#redisreplicationspec)_ | |
#### RedisSentinel
RedisSentinel is the Schema for the redissentinel API
| Field | Description |
| --- | --- |
| `apiVersion` _string_ | `redis.redis.opstreelabs.in/v1beta2`
| `kind` _string_ | `RedisSentinel`
| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. |
| `spec` _[RedisSentinelSpec](#redissentinelspec)_ | |
#### RedisClusterSpec
RedisClusterSpec defines the desired state of RedisCluster
_Appears in:_
- [RedisCluster](#rediscluster)
| Field | Description |
| --- | --- |
| `clusterSize` _integer_ | |
| `kubernetesConfig` _[KubernetesConfig](#kubernetesconfig)_ | |
| `clusterVersion` _string_ | |
| `redisLeader` _[RedisLeader](#redisleader)_ | |
| `redisFollower` _[RedisFollower](#redisfollower)_ | |
| `redisExporter` _[RedisExporter](#redisexporter)_ | |
| `storage` _[Storage](#storage)_ | |
| `nodeSelector` _object (keys:string, values:string)_ | |
| `podSecurityContext` _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#podsecuritycontext-v1-core)_ | |
| `priorityClassName` _string_ | |
| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#toleration-v1-core)_ | |
| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#resourcerequirements-v1-core)_ | |
| `TLS` _[TLSConfig](#tlsconfig)_ | |
| `sidecars` _[Sidecar](#sidecar)_ | |
| `serviceAccountName` _string_ | |
| `persistenceEnabled` _boolean_ | |
#### RedisSpec
RedisSpec defines the desired state of Redis
_Appears in:_
- [Redis](#redis)
| Field | Description |
| --- | --- |
| `kubernetesConfig` _[KubernetesConfig](#kubernetesconfig)_ | |
| `redisExporter` _[RedisExporter](#redisexporter)_ | |
| `redisConfig` _[RedisConfig](#redisconfig)_ | |
| `storage` _[Storage](#storage)_ | |
| `nodeSelector` _object (keys:string, values:string)_ | |
| `securityContext` _[SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#securitycontext-v1-core)_ | |
| `podSecurityContext` _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#podsecuritycontext-v1-core)_ | |
| `priorityClassName` _string_ | |
| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#affinity-v1-core)_ | |
| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#toleration-v1-core)_ | |
| `TLS` _[TLSConfig](#tlsconfig)_ | |
| `readinessProbe` _[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#probe-v1-core)_ | |
| `livenessProbe` _[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#probe-v1-core)_ | |
| `sidecars` _[Sidecar](#sidecar)_ | |
| `serviceAccountName` _string_ | |
#### RedisReplicationSpec
RedisReplicationSpec defines the desired state of RedisReplication
_Appears in:_
- [RedisReplication](#redisreplication)
| Field | Description |
| --- | --- |
| `clusterSize` _integer_ | |
| `kubernetesConfig` _[KubernetesConfig](#kubernetesconfig)_ | |
| `redisExporter` _[RedisExporter](#redisexporter)_ | |
| `redisConfig` _[RedisConfig](#redisconfig)_ | |
| `storage` _[Storage](#storage)_ | |
| `nodeSelector` _object (keys:string, values:string)_ | |
| `securityContext` _[SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#securitycontext-v1-core)_ | |
| `podSecurityContext` _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#podsecuritycontext-v1-core)_ | |
| `priorityClassName` _string_ | |
| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#affinity-v1-core)_ | |
| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#toleration-v1-core)_ | |
| `TLS` _[TLSConfig](#tlsconfig)_ | |
| `readinessProbe` _[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#probe-v1-core)_ | |
| `livenessProbe` _[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#probe-v1-core)_ | |
| `sidecars` _[Sidecar](#sidecar)_ | |
| `serviceAccountName` _string_ | |
#### RedisSentinelSpec
RedisSentinelSpec defines the desired state of RedisSentinel
_Appears in:_
- [RedisSentinel](#redissentinel)
| Field | Description |
| --- | --- |
| `clusterSize` _integer_ | |
| `kubernetesConfig` _[KubernetesConfig](#kubernetesconfig)_ | |
| `redisSentinelConfig` _[RedisSentinelConfig](#redissentinelconfig)_ | |
| `nodeSelector` _object (keys:string, values:string)_ | |
| `securityContext` _[SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#securitycontext-v1-core)_ | |
| `podSecurityContext` _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#podsecuritycontext-v1-core)_ | |
| `priorityClassName` _string_ | |
| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#affinity-v1-core)_ | |
| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#toleration-v1-core)_ | |
| `TLS` _[TLSConfig](#tlsconfig)_ | |
| `readinessProbe` _[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#probe-v1-core)_ | |
| `livenessProbe` _[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#probe-v1-core)_ | |
| `sidecars` _[Sidecar](#sidecar)_ | |
| `serviceAccountName` _string_ | |
| `volumeMount` _[VolumeMount](#volumemount)_ | |
#### RedisConfig
RedisConfig defines the external configuration of Redis
_Appears in:_
- [RedisFollower](#redisfollower)
- [RedisLeader](#redisleader)
- [RedisSpec](#redisspec)
- [RedisReplicationSpec](#redisreplicationspec)
| Field | Description |
| --- | --- |
| `additionalRedisConfig` _string_ | |
#### RedisSentinelConfig
RedisSentinelConfig defines the external configuration of RedisSentinel
_Appears in:_
- [RedisSentinelSpec](#redissentinelspec)
| Field | Description |
| --- | --- |
| `additionalRedisConfig` _string_ | |
| `masterGroupName` _string_ | |
| `redisPort` _string_ | |
| `quorum` _string_ | |
| `parallelSyncs` _string_ | |
| `failoverTimeout` _string_ | |
| `downAfterMilliseconds` _string_ | |
| `resolveHostnames` _string_ | |
| `announceHostnames` _string_ | |
#### RedisExporter
RedisExporter interface will have the information for redis exporter related stuff
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
- [RedisSpec](#redisspec)
- [RedisReplicationSpec](#redisreplicationspec)
| Field | Description |
| --- | --- |
| `enabled` _boolean_ | |
| `image` _string_ | |
| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#resourcerequirements-v1-core)_ | |
| `imagePullPolicy` _[ImagePullPolicy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy)_ | |
| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#envvar-v1-core)_ | |
| `securityContext` _[SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#securitycontext-v1-core)_ | |
#### RedisFollower
RedisFollower interface will have the redis follower configuration
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
| Field | Description |
| --- | --- |
| `replicas` _integer_ | |
| `redisConfig` _[RedisConfig](#redisconfig)_ | |
| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#affinity-v1-core)_ | |
| `securityContext` _[SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#securitycontext-v1-core)_ | |
| `pdb` _[RedisPodDisruptionBudget](#redispoddisruptionbudget)_ | |
| `readinessProbe` _[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#probe-v1-core)_ | |
| `livenessProbe` _[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#probe-v1-core)_ | |
#### RedisLeader
RedisLeader interface will have the redis leader configuration
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
| Field | Description |
| --- | --- |
| `replicas` _integer_ | |
| `redisConfig` _[RedisConfig](#redisconfig)_ | |
| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#affinity-v1-core)_ | |
| `securityContext` _[SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#securitycontext-v1-core)_ | |
| `pdb` _[RedisPodDisruptionBudget](#redispoddisruptionbudget)_ | |
| `readinessProbe` _[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#probe-v1-core)_ | |
| `livenessProbe` _[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#probe-v1-core)_ | |
#### RedisPodDisruptionBudget
RedisPodDisruptionBudget configure a PodDisruptionBudget on the resource (leader/follower)
_Appears in:_
- [RedisFollower](#redisfollower)
- [RedisLeader](#redisleader)
- [RedisReplication](#redisreplicationspec)
- [RedisSentinel](#redissentinelspec)
| Field | Description |
| --- | --- |
| `enabled` _boolean_ | |
| `minAvailable` _integer_ | |
| `maxUnavailable` _integer_ | |
#### Sidecar
Sidecar for each Redis pods
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
- [RedisSpec](#redisspec)
- [RedisReplicationSpec](#redisreplicationspec)
- [RedisSentinel](#redissentinelspec)
| Field | Description |
| --- | --- |
| `name` _string_ | |
| `image` _string_ | |
| `imagePullPolicy` _[ImagePullPolicy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy)_ | |
| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#resourcerequirements-v1-core)_ | |
| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#envvar-v1-core)_ | |
| `securityContext` _[SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#securitycontext-v1-core)_ | |
#### Storage
Storage is the inteface to add pvc and pv support in redis
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
- [RedisSpec](#redisspec)
- [RedisReplicationSpec](#redisreplicationspec)
| Field | Description |
| --- | --- |
| `volumeClaimTemplate` _[PersistentVolumeClaim](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#persistentvolumeclaim-v1-core)_ | |
#### TLSConfig
TLS Configuration for redis instances
_Appears in:_
- [RedisClusterSpec](#redisclusterspec)
- [RedisSpec](#redisspec)
- [RedisReplicationSpec](#redisreplicationspec)
- [RedisSentinel](#redissentinelspec)
| Field | Description |
| --- | --- |
| `ca` _string_ | |
| `cert` _string_ | |
| `key` _string_ | |
| `secret` _[SecretVolumeSource](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#secretvolumesource-v1-core)_ | Reference to secret which contains the certificates |

View File

@ -7,6 +7,8 @@ description: >
Instructions for setting up Redis Replication
---
> **Note:** It is recommended to use [Sentinel](../Sentinel) to monitor the Replication cluster for enhanced high availability and reliability. By implementing Redis Sentinel, you can ensure that your Redis Replication remains operational even if one or more nodes fail, thereby improving the resilience and reliability of your application.
## Architecture
Redis is an in-memory key-value store that can be used as a database, cache, and message broker. Redis replication is the process of synchronizing data from a Redis leader node to one or more Redis follower nodes.
@ -21,8 +23,6 @@ Redis replication is a powerful feature that enhances the durability and scalabi
<img src="../../../images/replication-redis.png">
</div>
> **Note:** By using Redis Sentinel, you can ensure that your Redis Replication remains available even if one or more nodes go down, improving the resilience and reliability of your application.
## Helm Installation
For redis replication setup we can use `helm` command with the reference of replication helm chart and additional properties:
@ -78,7 +78,7 @@ metadata:
name: redis-replication
spec:
clusterSize: 3
securityContext:
podSecurityContext:
runAsUser: 1000
fsGroup: 1000
kubernetesConfig:

View File

@ -78,7 +78,7 @@ spec:
resources:
requests:
storage: 1Gi
securityContext:
podSecurityContext:
runAsUser: 1000
fsGroup: 1000
```

View File

@ -41,9 +41,21 @@ Total number of rediscluster rebalance operations. Type: Counter.
### rediscluster_remove_follower_attempt
Number of times to remove follower attempts. Type: Counter.
### rediscluster_repair_disconnected_attempt
Number of times to repair a Redis cluster disconnected from the cluster. Type: Counter.
### rediscluster_repair_failed
Number of times to repair a Redis cluster failed. Type: Counter.
### rediscluster_replicas_size_desired
Total desired number of rediscluster replicas. Type: Gauge.
### rediscluster_reset_attempt
Number of times to reset a Redis cluster. Type: Counter.
### rediscluster_reset_failed
Number of times to reset a Redis cluster failed. Type: Counter.
### rediscluster_reshard_total
Total number of rediscluster reshard operations. Type: Counter.

View File

@ -2,14 +2,14 @@
title: "Overview"
linkTitle: "Overview"
weight: 1
date: 2022-11-02T00:19:19Z
date: 2025-07-25T00:00:00Z
description: >
Redis Operator is a software to set up and manage Redis on [Kubernetes](https://kubernetes.io).
---
A Golang based redis operator that will make/oversee Redis standalone/cluster/replication/sentinel mode setup on top of the Kubernetes. It can create a redis cluster setup with best practices on Cloud as well as the Bare-metal environment. Also, it provides an in-built monitoring capability using redis-exporter.
Documentation is available here:- https://ot-container-kit.github.io/redis-operator/
Documentation is available here:- https://redis-operator.opstree.dev/docs/
The type of Redis setup which is currently supported:-

View File

@ -2,10 +2,325 @@
title: "Release History"
linkTitle: "Release History"
weight: 10
date: 2022-11-02T00:19:19Z
date: 2025-07-25T00:00:00Z
description: >
Release versions and their description about Redis Operator
---
The most up to date release notes can be found on [GitHub](https://github.com/OT-CONTAINER-KIT/redis-operator/releases)
### v0.21.0
#### June 2025
**🎉 Features**
- Round robin where to transfer cluster shards when scaling in a Redis Cluster [#1412](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1412)
- Add auto max memory configuration for Redis instances [#1411](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1411)
- Add bus port configuration for Redis cluster services [#1406](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1406)
- Add automatic Redis pod role label synchronization for rediscluster [#1404](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1404)
- RedisCluster observability [#1392](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1392)
- Add liveness/readiness probes to values.yaml and templates [#1378](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1378)
- Reduce unnecessary requeue when skip reconcile annotation exists [#1374](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1374)
- RedisReplication observability, skip reconcile or not [#1369](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1369)
- Add Redis Sentinel validation webhook for clusterSize [#1361](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1361)
**🪲 Bug Fixes**
- Resolve StatefulSet selector immutability issues [#1382](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1382)
- Avoid sentinel restart after replication failover [#1381](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1381)
- Define named probe port outside webhook block [#1354](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1354)
**🎉 Refactors**
- Reorganize manager agent cmd package [#1383](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1383)
- Reorganize API structure and update paths [#1363](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1363)
- Remove useless structure and refactor package [#1362](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1362)
- Reorganize command structure for Redis operator [#1351](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1351)
### v0.20.2
#### May 12, 2024
**🪲 Bug Fixes**
- Handle panic when retrieving StatefulSet in GetRedisNodesByRole [#1330](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1330)
- VCT resize detection logic; add support for scaling out with new VCT size [#1342](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1342)
- Service updated before Statefulset during Reconciliation [#1348](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1348)
**🎉 Features**
- Add data assertion generation and enhance Redis configuration commands [#1331](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1331)
- Add feature gates support for Redis Operator [#1333](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1333)
- Migrate kubebuilder go.kubebuilder.io/v3 to go.kubebuilder.io/v4 [#1340](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1340)
**🎉 Refactors**
- Define container port for http probes in operator chart [#1326](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1326)
- Enhance environment variable management and CI workflow [#1315](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1315)
### v0.20.1
#### April 27, 2024
**🪲 Bug Fixes**
- Move VCT logic before diff calculation for stateful set [#1322](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1322)
### v0.20.0
#### April 1, 2024
**🎉 Features**
- Sentinel - support hostname resolve and announce [#1247](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1247)
- Add redis agent with bootstrap configuration generation [#1254](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1254)
- Implement comprehensive Redis configuration generation for agent bootstrap [#1260](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1260)
- Added support for hostport to allow direct connection to the pod [#1263](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1263)
- Add preStop hook for Redis Cluster failover [#1264](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1264)
- Sentinel - announce-ip when resolve & announce are set [#1271](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1271)
- Fix PVC resizing issue and refactor PVC resizing logic [#1268](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1268)
- Add redisreplication observability [#1274](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1274)
- Add recreate-stateful-strategy, orphan, background, foreground(default) [#1286](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1286)
- Update Dockerfile and Makefile for unified operator binary [#1294](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1294)
- Add support for anti affinity configuration in helm charts [#1296](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1296)
- Guarantee to avoid bad master ip on Sentinel [#1289](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1289)
- Add feature gates for sentinel configuration generation in init container [#1300](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1300)
- Support redis configuration generation in init container [#1303](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1303)
**🪲 Bug Fixes**
- Replace hardcoded Redis port 6379 with configurable port from cr.Spec.Port [#1261](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1261)
- Update references from master to main in docs and workflow files [#1288](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1288)
- Svc finalizer removed [#1297](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1297)
- Race condition resulting in permanently broken Redis cluster [#1298](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1298)
### v0.19.1
#### February 19, 2024
**⚠️ Deprecation Notice**
The v1beta1 API version will be removed in next release. Users are strongly encouraged to migrate to v1beta2, which offers enhanced features and improved stability.
**🎉 Features**
- Add data-assert tool for Redis data management [#1204](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1204)
- Check data consistent by external tool [#1205](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1205)
- Added actions to publish charts to github container registry [#1201](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1201)
- Add headless service configuration support [#1219](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1219)
- Update redis-operator cert manager configuration [#1220](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1220)
- Add additional service configuration with optional enable flag [#1228](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1228)
- Enhance Redis HA and node scheduling strategy [#1237](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1237)
- Add securityContext config in chart for redis-exporter [#1238](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1238)
- Add dynamic Redis configuration support for Redis Cluster [#1241](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1241)
- Configurable operator maxConcurrentReconciles [#1242](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1242)
**🪲 Bug Fixes**
- Skip-reconcile annotation still skipping reconcile even the value is false [#1202](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1202)
- Make recreate statefulset only trigger when value true [#1240](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1240)
- Improve pod label patching with error handling and retry mechanism [#1231](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1231)
- Changed certificate serverName to pod+namespace [#1221](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1221)
- Add missing topologySpreadConstraint on RedisCluster follower [#1218](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1218)
### v0.19.0
#### January 12, 2024
**🎉 Features**
- Add PDB and probes, drop unspecified acl in sentinel helm [#1123](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1123)
- Add master/replica service to redis replication [#1124](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1124)
- Add recreateStatefulSetOnUpdateInvalid helm chart value [#1127](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1127)
- Enhance RedisReplication controller and CRD with additional status [#1154](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1154)
- Support PDB in redisreplication [#1166](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1166)
- Enhance RedisSentinel reconciliation logic and update workflow [#1176](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1176)
- Support redis-cluster topologySpreadConstraints [#1177](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1177)
- Add event recording functionality for RedisCluster controller [#1182](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1182)
- Support topologySpreadConstraints in replication & sentinel [#1184](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1184)
- Redis-cluster add podAntiAffinity [#1180](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1180)
- Separate resources section for leader and follower [#1188](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1188)
- Enhance RedisCluster resource management by introducing separate resource handling for leader and follower [#1199](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1199)
**🪲 Bug Fixes**
- PDB value mapping in redis-sentinel [#1136](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1136)
- Chart render error when enable initcontainer [#1146](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1146)
- InitContainer enabled properties not define in template [#1152](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1152)
- Redis-cluster unexpected downscaling [#1173](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1173)
- Reduce the impact of Redis cluster intermediate states [#1178](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1178)
- Label selector mapping on redisreplication pdb [#1191](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1191)
### v0.18.1
#### November 7, 2024
**🎉 Features**
- Support setting minReadySeconds on the stateful sets [#1023](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1023)
- Add tolerations to operator chart [#1051](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1051)
- Add image pull secret for redis operator [#1053](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1053)
- Add service monitor to redis sentinel chart [#1071](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1071)
- Add readiness/liveness probe to redis operator chart [#1072](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1072)
- Upgrade redis/sentinel image to 7.0.15 [#1099](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1099)
- Reconcile redissentinel only on master changed [#1122](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1122)
**🪲 Bug Fixes**
- Fix indentation error when enable additional config [#1031](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1031)
- Fix field validate error when enable additional config for sentinel [#1033](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1033)
- Fix unknown field error when upgrade chart [#1034](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1034)
- Fix bad indentation on redis standalone additional configs [#1040](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1040)
- Fix attempt to repair disconnected/failed master nodes before failing over [#1105](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1105)
- Fix set controller probe endpoint handler [#1121](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1121)
### v0.18.0
#### July 11, 2024
**🎉 Features**
- Added redisReplicationPassword values to connect secured replication [#1021](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1021)
- Added redis/redisreplication/redissentinel/rediscluster chart [#1007](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/1007)
- Added support for extra volume mounts for redis sentinel [#994](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/994)
- Added automountServiceAccountToken values for deployment and serviceaccount [#991](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/991)
- Added securityContext for exporter, initcontainers and sidecars [#987](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/987)
- Added security context values in operator chart [#973](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/973)
- Added rolling update sequence from leader to follower [#966](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/966)
- Added support for configurable probe handlers [#934](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/934)
- Added redis operator helm chart and release workflow [#941](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/941)
- Added support for other container engines [#947](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/947)
**🪲 Bug Fixes**
- Added default port to enable `SENTINEL_PORT` environment [#999](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/999)
- ReadyReplicas need to be checked in `IsStatefulSetReady` [#993](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/993)
- watchNamespace value does not take effect in chart [#990](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/990)
- Sentinel should not reconcile until replication cluster ready [#964](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/964)
- Return ASAP after handling finalizer [#940](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/940)
- Check redis replication after handling finalizer [#936](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/936)
### v0.17.0
#### May 14, 2024
**🎉 Features**
- WATCH_NAMESPACE support multi namespace [#919](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/919)
- Add workflow to publish image to ghcr [#914](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/914)
- Probe use built-in, discarded healthcheck.sh [#907](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/907)
- Implement redis cluster ready state [#867](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/867)
- Add redisreplication status masterNode [#849](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/849)
**🪲 Bug Fixes**
- Runtime panic when delete rediscluster which disable persistent [#922](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/922)
- Runtime panic when storage param is empty [#887](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/887)
- Exporter can not connect to redis when enable tls [#902](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/902)
- Update status if not equal [#900](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/900)
- Should get the really leader count when scale in [#885](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/885)
- ClusterSlaves result should be cut [#884](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/884)
- Redis cluster update as scale out [#882](https://github.com/OT-CONTAINER-KIT/redis-operator/issues/882)
- Add common AddFinalizer for all api [#858](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/858)
### v0.16.0
#### March 27, 2024
**🎉 Features**
- Added support for multiple CRDs and enhanced functionality
- Improved test coverage and CI/CD pipeline
- Enhanced Redis cluster management and scaling capabilities
- Added support for custom Redis configurations
- Implemented advanced security features
**🪲 Bug Fixes**
- Fixed various StatefulSet and service management issues
- Resolved Redis cluster scaling problems
- Fixed authentication and TLS connectivity issues
- Improved error handling and logging
### v0.15.1
#### September 24, 2023
**🪲 Bug Fixes**
- Added Custom EnvVars support [#631](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/631)
- Fix the backup and restore script and manifest [#624](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/624)
- e2e test for Redis Cluster [#623](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/623)
- Nil pointer de-reference in conversion webhook [#615](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/615)
- Fixes Restore script [#609](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/609)
- Close redis client to avoid resource leak [#572](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/572)
- fix:resize clusters pvc with wrong label [#562](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/562)
- Hardcoded 1Mi size of node-conf PVC in RedisCluster [#552](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/552)
- Cluster leader failover loop if there is only a single leader [#542](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/542)
**🎉 Features**
- e2e test for Redis Cluster [#623](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/623)
- Support ENABLE_WEBHOOKS env which cloud disable webhook server locally [#617](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/617)
- Status Field to Redis Cluster [#612](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/612)
- Support recreate statefulset of redissentinel [#607](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/607)
- Add support for multiple versions [#592](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/592)
**🎉 Refactors**
- Track examples/docs refactoring v1beta2 [#633](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/633)
- Refactor the backup and restore script and manifest [#624](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/624)
- Optional Volume split [#603](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/603) ( Breaking Change )
- kustomize install not working [#602](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/602)
- Support apply crds by kubectl apply --server-side [#601](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/601)
- Fix image path [#591](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/591)
- Add RBAC for redisreplications and redissentinels. [#590](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/590)
- Write the docs for the restore and backup [#588](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/588)
- Support redis sentinel pdb [#589](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/589)
- Migrate the Pipeline from Azure to Github actions [#571](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/571)
- Fix log pollution [#585](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/585)
### v0.15.0
##### July 17, 2023
**🐞 Bug Fixes**
- Fix Linter Issue #479
- Fix exporter ports enabled even when exporters disabled [#484](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/484)
- Corrected scenario "go-get-tool" in makefile [#499](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/499)
- Operator Crash when persistence is false/Disabled [#519](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/519) -- Breaking Change
- call of func checkAttachedSlave for 1 Master Replication [#523](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/523)
- Only created /node-conf VolumeMount for clusters [#532](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/532)
- Redis Sentinel Exporter ports in Env Vars [#533](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/533)
- Init Container tried to mount invalid volume name [#538](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/538)
- Cluster leader failover loop if there is only a single leader [#542](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/542)
**🎉 Features**
- Add RedisExporter for sentinel [#440](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/440)
- Add InitContainer Field [#458](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/458)
- ACL redis via secret [#486](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/486)
- Adding Custom TerminationGracePeriodSeconds and additional fields for Sidecar [#487](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/487)
- Enable Support for Backup and Restore via script [#489](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/489)
- Support Scaling for Redis Cluster [#531](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/531) -- Breaking Change
**🎉 Refactors**
- Fixed StatefulSet(sentinel) Label for Service(Selector) [#442](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/442)
- Declare Module Correctly On sentinel [#478](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/478)
- Manage (Pod and Container) security Context explicitly [#518](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/518)
- Add watchnamespace function as per operator hub [#520](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/520)
- Remove sentinel default validation not effect [#535](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/535)
- Remove sentinel cluster size validation no effect [#536](https://github.com/OT-CONTAINER-KIT/redis-operator/pull/536)
### v0.14.0
##### Feburary 13, 2023
**🐞 Bug Fixes**
- Added check for persistent volume nil condition
- Fix crash with go panic
- Fix memory address bug and nil pointer
- CR annotations fixes w.r.t. to stateful set
- Fix issues with ARM64 support
**🎉 Features**
- Added serviceType functionality for redis standalone and cluster
- Added feature for additional volume mounts
- Added nodeSelectory and tolerations for redis cluster
- Added recreation logic for redis stateful sets
- Added replication mode support for the redis cluster
- Added sentinel support for replication failover
### v0.13.0
##### November 10, 2022

View File

@ -0,0 +1,35 @@
---
apiVersion: redis.redis.opstreelabs.in/v1beta2
kind: RedisCluster
metadata:
name: redis-cluster
spec:
clusterSize: 3
clusterVersion: v7
podSecurityContext:
runAsUser: 1000
fsGroup: 1000
persistenceEnabled: true
kubernetesConfig:
image: quay.io/opstree/redis:v7.0.12
imagePullPolicy: IfNotPresent
persistentVolumeClaimRetentionPolicy:
whenScaled: Delete
whenDeleted: Delete
redisExporter:
enabled: false
image: quay.io/opstree/redis-exporter:v1.44.0
storage:
volumeClaimTemplate:
spec:
# storageClassName: standard
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
nodeConfVolumeClaimTemplate:
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi

View File

@ -0,0 +1,27 @@
---
apiVersion: redis.redis.opstreelabs.in/v1beta2
kind: RedisReplication
metadata:
name: redis-replication
spec:
clusterSize: 3
kubernetesConfig:
image: quay.io/opstree/redis:v7.0.12
imagePullPolicy: IfNotPresent
persistentVolumeClaimRetentionPolicy:
whenScaled: Delete
whenDeleted: Delete
podSecurityContext:
runAsUser: 1000
fsGroup: 1000
storage:
volumeClaimTemplate:
spec:
# storageClassName: standard
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
redisExporter:
enabled: false
image: quay.io/opstree/redis-exporter:v1.44.0

View File

@ -0,0 +1,26 @@
---
apiVersion: redis.redis.opstreelabs.in/v1beta2
kind: Redis
metadata:
name: redis-standalone
spec:
kubernetesConfig:
image: quay.io/opstree/redis:v7.0.12
imagePullPolicy: IfNotPresent
persistentVolumeClaimRetentionPolicy:
whenScaled: Delete
whenDeleted: Delete
podSecurityContext:
runAsUser: 1000
fsGroup: 1000
storage:
volumeClaimTemplate:
spec:
# storageClassName: standard
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
redisExporter:
enabled: false
image: quay.io/opstree/redis-exporter:v1.44.0

18
go.mod
View File

@ -13,6 +13,7 @@ require (
github.com/prometheus/client_golang v1.22.0
github.com/redis/go-redis/v9 v9.11.0
github.com/spf13/cobra v1.7.0
github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.10.0
gomodules.xyz/jsonpatch/v2 v2.4.0
k8s.io/api v0.29.4
@ -28,7 +29,7 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/emicklei/go-restful/v3 v3.12.0 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
@ -48,27 +49,37 @@ require (
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/spdystream v0.2.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/automaxprocs v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/oauth2 v0.24.0 // indirect
golang.org/x/oauth2 v0.27.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/text v0.23.0 // indirect
@ -76,6 +87,7 @@ require (
golang.org/x/tools v0.31.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.29.3 // indirect

44
go.sum
View File

@ -25,8 +25,9 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
@ -42,6 +43,8 @@ github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCv
github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
@ -110,6 +113,8 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
@ -135,9 +140,13 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -165,10 +174,13 @@ github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGV
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
@ -185,17 +197,38 @@ github.com/redis/go-redis/v9 v9.11.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
@ -233,8 +266,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -302,6 +335,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@ -309,6 +344,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

44
hack/api-docs/build.sh Executable file
View File

@ -0,0 +1,44 @@
#!/bin/bash
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
CRD_REF_DOCS_VERSION="v0.0.12"
echo "Starting API documentation generation..."
# Install crd-ref-docs if not present
echo "Installing crd-ref-docs ${CRD_REF_DOCS_VERSION}..."
go install github.com/elastic/crd-ref-docs@${CRD_REF_DOCS_VERSION}
# Use full path to crd-ref-docs
GOPATH=$(go env GOPATH)
if [ -z "$GOPATH" ]; then
GOPATH="$HOME/go"
fi
CRD_REF_DOCS="${GOPATH}/bin/crd-ref-docs"
if [ ! -f "${CRD_REF_DOCS}" ]; then
echo "Error: crd-ref-docs not found at ${CRD_REF_DOCS}"
exit 1
fi
# Create output directory
OUTPUT_DIR="${REPO_ROOT}/docs/content/en/docs/CRD Reference/API Reference"
mkdir -p "${OUTPUT_DIR}"
echo "Generating API documentation..."
echo "Source path: ${REPO_ROOT}/api"
echo "Config file: ${SCRIPT_DIR}/config.yaml"
echo "Templates: ${SCRIPT_DIR}/templates"
echo "Output: ${OUTPUT_DIR}/_index.md"
# Generate the documentation using custom templates
"${CRD_REF_DOCS}" \
--source-path="${REPO_ROOT}/api" \
--config="${SCRIPT_DIR}/config.yaml" \
--templates-dir="${SCRIPT_DIR}/templates/markdown" \
--renderer=markdown \
--output-path="${OUTPUT_DIR}/_index.md"
echo "API documentation generated successfully at: ${OUTPUT_DIR}/_index.md"

10
hack/api-docs/config.yaml Normal file
View File

@ -0,0 +1,10 @@
processor:
ignoreTypes:
- ".*List$"
- ".*Status$"
ignoreFields:
- "TypeMeta"
- "ObjectMeta"
render:
kubernetesVersion: "1.31"
renderer: "markdown"

View File

@ -0,0 +1,19 @@
{{- define "gvDetails" -}}
{{- $gv := . -}}
## {{ $gv.GroupVersionString }}
{{ $gv.Doc }}
{{- if $gv.Kinds }}
### Resource Types
{{- range $gv.SortedKinds }}
- {{ $gv.TypeForKind . | markdownRenderTypeLink }}
{{- end }}
{{ end }}
{{ range $gv.SortedTypes }}
{{ template "type" . }}
{{ end }}
{{- end -}}

View File

@ -0,0 +1,23 @@
{{- define "gvList" -}}
{{- $groupVersions := . -}}
---
title: "API Reference Documentation"
linkTitle: "API Docs"
weight: 10
date: 2025-01-27
description: >
Complete API reference documentation for Redis Operator CRDs
---
# API Reference
## Packages
{{- range $groupVersions }}
- {{ markdownRenderGVLink . }}
{{- end }}
{{ range $groupVersions }}
{{ template "gvDetails" . }}
{{ end }}
{{- end -}}

View File

@ -0,0 +1,43 @@
{{- define "type" -}}
{{- $type := . -}}
{{- if markdownShouldRenderType $type -}}
#### {{ $type.Name }}
{{ if $type.IsAlias }}_Underlying type:_ _{{ markdownRenderTypeLink $type.UnderlyingType }}_{{ end }}
{{ $type.Doc }}
{{ if $type.Validation -}}
_Validation:_
{{- range $type.Validation }}
- {{ . }}
{{- end }}
{{- end }}
{{ if $type.References -}}
_Appears in:_
{{- range $type.SortedReferences }}
- {{ markdownRenderTypeLink . }}
{{- end }}
{{- end }}
{{ if $type.Members -}}
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
{{ if $type.GVK -}}
| `apiVersion` _string_ | `{{ $type.GVK.Group }}/{{ $type.GVK.Version }}` | | |
| `kind` _string_ | `{{ $type.GVK.Kind }}` | | |
{{ end -}}
{{ range $type.Members -}}
| `{{ .Name }}` _{{ markdownRenderType .Type }}_ | {{ template "type_members" . }} | {{ markdownRenderDefault .Default }} | {{ range .Validation -}} {{ markdownRenderFieldDoc . }} <br />{{ end }} |
{{ end -}}
{{ end -}}
{{/* EnumValues feature removed for compatibility */}}
{{- end -}}
{{- end -}}

View File

@ -0,0 +1,8 @@
{{- define "type_members" -}}
{{- $field := . -}}
{{- if eq $field.Name "metadata" -}}
Refer to Kubernetes API documentation for fields of `metadata`.
{{- else -}}
{{ markdownRenderFieldDoc $field.Doc }}
{{- end -}}
{{- end -}}

View File

@ -108,6 +108,8 @@ func GenerateConfig() error {
cfg.Append("tls-key-file", redisTLSCertKey)
cfg.Append("tls-ca-cert-file", redisTLSCAKey)
cfg.Append("tls-auth-clients", "optional")
// Sentinel should use tls for replication connection.
cfg.Append("tls-replication", "yes")
} else {
fmt.Println("Running sentinel without TLS mode")
}

View File

@ -18,11 +18,13 @@ package manager
import (
"flag"
"time"
rvb2 "github.com/OT-CONTAINER-KIT/redis-operator/api/redis/v1beta2"
rcvb2 "github.com/OT-CONTAINER-KIT/redis-operator/api/rediscluster/v1beta2"
rrvb2 "github.com/OT-CONTAINER-KIT/redis-operator/api/redisreplication/v1beta2"
rsvb2 "github.com/OT-CONTAINER-KIT/redis-operator/api/redissentinel/v1beta2"
"github.com/OT-CONTAINER-KIT/redis-operator/internal/controller/common/operator"
"github.com/OT-CONTAINER-KIT/redis-operator/internal/controller/common/redis"
"github.com/OT-CONTAINER-KIT/redis-operator/internal/controller/common/scheme"
rediscontroller "github.com/OT-CONTAINER-KIT/redis-operator/internal/controller/redis"
@ -36,6 +38,7 @@ import (
"github.com/OT-CONTAINER-KIT/redis-operator/internal/monitoring"
coreWebhook "github.com/OT-CONTAINER-KIT/redis-operator/internal/webhook"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"k8s.io/client-go/kubernetes"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/cache"
@ -72,6 +75,10 @@ func CMD() *cobra.Command {
cmd := &cobra.Command{
Use: "manager",
Short: "Start the Redis operator manager",
PreRunE: func(cmd *cobra.Command, args []string) error {
// Bind all flags to viper
return viper.BindPFlags(cmd.Flags())
},
RunE: func(cmd *cobra.Command, args []string) error {
return runManager(opts)
},
@ -92,7 +99,16 @@ func addFlags(cmd *cobra.Command, opts *managerOptions) {
cmd.Flags().IntVar(&opts.maxConcurrentReconciles, "max-concurrent-reconciles", 1, "Max concurrent reconciles")
cmd.Flags().StringVar(&opts.featureGatesString, "feature-gates", internalenv.GetFeatureGates(), "A set of key=value pairs that describe feature gates for alpha/experimental features. "+
"Options are:\n GenerateConfigInInitContainer=true|false: enables using init container for config generation")
cmd.Flags().Duration(
operator.KubeClientTimeoutMGRFlag,
60*time.Second,
"Timeout for requests made by the Kubernetes API client.",
)
cmd.Flags().Float32(
operator.KubeClientQPSMGRFlag,
0,
"Maximum number of queries per second to the Kubernetes API.",
)
zapFlagSet := flag.NewFlagSet("zap", flag.ExitOnError)
opts.zapOptions.BindFlags(zapFlagSet)
zapFlagSet.VisitAll(func(f *flag.Flag) {
@ -114,8 +130,18 @@ func runManager(opts *managerOptions) error {
if err := setupFeatureGates(opts.featureGatesString); err != nil {
return err
}
// Config to talk to k8s api server
cfg := ctrl.GetConfigOrDie()
if qps := float32(viper.GetFloat64(operator.KubeClientQPSMGRFlag)); qps > 0 {
cfg.QPS = qps
cfg.Burst = int(qps * 2)
}
cfg.Timeout = viper.GetDuration(operator.KubeClientTimeoutMGRFlag)
ctrlOptions := createControllerOptions(opts)
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrlOptions)
mgr, err := ctrl.NewManager(cfg, ctrlOptions)
if err != nil {
setupLog.Error(err, "unable to start manager")
return err

View File

@ -0,0 +1,6 @@
package operator
const (
KubeClientTimeoutMGRFlag = "kube-client-timeout"
KubeClientQPSMGRFlag = "kube-client-qps"
)

View File

@ -71,11 +71,7 @@ func (c *checker) GetMasterFromReplication(ctx context.Context, rr *rr.RedisRepl
var masterPods []corev1.Pod
for _, pod := range pods.Items {
connInfo := &redis.ConnectionInfo{
IP: pod.Status.PodIP,
Port: "6379",
Password: password,
}
connInfo := createConnectionInfo(ctx, pod, password, rr.Spec.TLS, c.k8s, rr.Namespace, "6379")
isMaster, err := c.redis.Connect(connInfo).IsMaster(ctx)
if err != nil {
return corev1.Pod{}, err
@ -87,11 +83,7 @@ func (c *checker) GetMasterFromReplication(ctx context.Context, rr *rr.RedisRepl
var realMasterPod corev1.Pod
for _, pod := range masterPods {
connInfo := &redis.ConnectionInfo{
IP: pod.Status.PodIP,
Port: "6379",
Password: password,
}
connInfo := createConnectionInfo(ctx, pod, password, rr.Spec.TLS, c.k8s, rr.Namespace, "6379")
count, err := c.redis.Connect(connInfo).GetAttachedReplicaCount(ctx)
if err != nil {
continue
@ -99,6 +91,12 @@ func (c *checker) GetMasterFromReplication(ctx context.Context, rr *rr.RedisRepl
if count != 0 {
realMasterPod = pod
break
} else {
replicaNum := *sts.Spec.Replicas
if replicaNum == 1 {
realMasterPod = pod
break
}
}
}
return realMasterPod, nil

View File

@ -2,6 +2,8 @@ package redis
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"strings"
@ -9,6 +11,7 @@ import (
rsvb2 "github.com/OT-CONTAINER-KIT/redis-operator/api/redissentinel/v1beta2"
"github.com/OT-CONTAINER-KIT/redis-operator/internal/controller/common"
"github.com/OT-CONTAINER-KIT/redis-operator/internal/service/redis"
"github.com/OT-CONTAINER-KIT/redis-operator/internal/util/cryptutil"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
@ -22,7 +25,7 @@ type Healer interface {
SentinelReset(ctx context.Context, rs *rsvb2.RedisSentinel) error
// UpdatePodRoleLabel connect to all redis pods and update pod role label `redis-role` to `master` or `slave` according to their role.
UpdateRedisRoleLabel(ctx context.Context, ns string, labels map[string]string, secret *commonapi.ExistingPasswordSecret) error
UpdateRedisRoleLabel(ctx context.Context, ns string, labels map[string]string, secret *commonapi.ExistingPasswordSecret, tlsConfig *commonapi.TLSConfig) error
}
type healer struct {
@ -37,7 +40,7 @@ func NewHealer(clientset kubernetes.Interface) Healer {
}
}
func (h *healer) UpdateRedisRoleLabel(ctx context.Context, ns string, labels map[string]string, secret *commonapi.ExistingPasswordSecret) error {
func (h *healer) UpdateRedisRoleLabel(ctx context.Context, ns string, labels map[string]string, secret *commonapi.ExistingPasswordSecret, tlsConfig *commonapi.TLSConfig) error {
selector := make([]string, 0, len(labels))
for key, value := range labels {
selector = append(selector, fmt.Sprintf("%s=%s", key, value))
@ -53,11 +56,7 @@ func (h *healer) UpdateRedisRoleLabel(ctx context.Context, ns string, labels map
return err
}
for _, pod := range pods.Items {
connInfo := &redis.ConnectionInfo{
IP: pod.Status.PodIP,
Port: "6379",
Password: password,
}
connInfo := createConnectionInfo(ctx, pod, password, tlsConfig, h.k8s, ns, "6379")
isMaster, err := h.redis.Connect(connInfo).IsMaster(ctx)
if err != nil {
return err
@ -98,11 +97,8 @@ func (h *healer) SentinelReset(ctx context.Context, rs *rsvb2.RedisSentinel) err
}
for _, pod := range pods.Items {
connInfo := &redis.ConnectionInfo{
IP: pod.Status.PodIP,
Port: "26379",
Password: sentinelPass,
}
connInfo := createConnectionInfo(ctx, pod, sentinelPass, rs.Spec.TLS, h.k8s, rs.Namespace, "26379")
err = h.redis.Connect(connInfo).SentinelReset(ctx, rs.Spec.RedisSentinelConfig.MasterGroupName)
if err != nil {
return err
@ -135,13 +131,10 @@ func (h *healer) SentinelMonitor(ctx context.Context, rs *rsvb2.RedisSentinel, m
}
for _, pod := range pods.Items {
connInfo := &redis.ConnectionInfo{
IP: pod.Status.PodIP,
Port: "26379",
Password: sentinelPass,
}
connInfo := createConnectionInfo(ctx, pod, sentinelPass, rs.Spec.TLS, h.k8s, rs.Namespace, "26379")
masterConnInfo := &redis.ConnectionInfo{
IP: master,
Host: master,
Port: "6379",
Password: masterPass,
}
@ -177,3 +170,63 @@ func (h *healer) getSentinelPods(ctx context.Context, rs *rsvb2.RedisSentinel) (
}
return pods, nil
}
// getRedisTLSConfig creates a TLS configuration for Redis connections
func getRedisTLSConfig(ctx context.Context, client kubernetes.Interface, namespace, tlsSecretName string) *tls.Config {
// This is a wrapper to access the k8sutils internal function
// We'll implement a simplified version here for now
secret, err := client.CoreV1().Secrets(namespace).Get(ctx, tlsSecretName, metav1.GetOptions{})
if err != nil {
log.FromContext(ctx).Error(err, "Failed in getting TLS secret", "secretName", tlsSecretName, "namespace", namespace)
return nil
}
tlsClientCert, certExists := secret.Data["tls.crt"]
tlsClientKey, keyExists := secret.Data["tls.key"]
tlsCACert, caExists := secret.Data["ca.crt"]
if !certExists || !keyExists || !caExists {
log.FromContext(ctx).Error(fmt.Errorf("TLS secret missing required keys"), "TLS secret is missing required keys", "secretName", tlsSecretName)
return nil
}
cert, err := tls.X509KeyPair(tlsClientCert, tlsClientKey)
if err != nil {
log.FromContext(ctx).Error(err, "Failed to load TLS key pair", "secretName", tlsSecretName)
return nil
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(tlsCACert)
return &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caCertPool,
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: true,
VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
_, _, err := cryptutil.VerifyCertificateExceptServerName(rawCerts, &tls.Config{RootCAs: caCertPool})
return err
},
}
}
// createConnectionInfo creates a Redis connection info with TLS support
func createConnectionInfo(ctx context.Context, pod v1.Pod, password string, tlsConfig *commonapi.TLSConfig, k8sClient kubernetes.Interface, namespace, port string) *redis.ConnectionInfo {
connInfo := &redis.ConnectionInfo{
Host: pod.Status.PodIP,
Port: port,
Password: password,
}
// Configure TLS if enabled
if tlsConfig != nil && tlsConfig.Secret.SecretName != "" {
serviceName := common.GetHeadlessServiceNameFromPodName(pod.Name)
connInfo.Host = fmt.Sprintf("%s.%s.%s.svc.cluster.local", pod.Name, serviceName, namespace)
// Get TLS configuration
tlsCfg := getRedisTLSConfig(ctx, k8sClient, namespace, tlsConfig.Secret.SecretName)
connInfo.TLSConfig = tlsCfg
}
return connInfo
}

View File

@ -0,0 +1,30 @@
package common
import (
"strconv"
"strings"
)
// GetHeadlessServiceNameFromPodName trims the trailing ordinal (e.g. "-0", "-1") from a pod name to
// derive the headless service name. If the pod name does not contain a trailing
// ordinal segment the original name is returned unchanged.
//
// Examples for different Redis modes:
// - RedisReplication: "redis-replication-0" -> "redis-replication-headless"
// - RedisCluster Leader: "redis-cluster-leader-0" -> "redis-cluster-leader-headless"
// - RedisCluster Follower: "redis-cluster-follower-1" -> "redis-cluster-follower-headless"
// - RedisSentinel: "redis-sentinel-sentinel-0" -> "redis-sentinel-sentinel-headless"
func GetHeadlessServiceNameFromPodName(podName string) string {
// Find the last dash in the pod name. If there is none, return the whole name.
idx := strings.LastIndex(podName, "-")
if idx == -1 {
return podName
}
// Check whether the suffix after the last dash is a number (the StatefulSet
// ordinal). If it is, trim it to get the service name; otherwise return the
// original pod name.
if _, err := strconv.Atoi(podName[idx+1:]); err == nil {
return podName[:idx] + "-headless"
}
return podName
}

View File

@ -66,7 +66,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
}
return intctrlutil.Reconciled()
}
monitoring.RedisReplicationSkipReconcile.WithLabelValues(instance.Namespace, instance.Name).Set(0)
monitoring.RedisClusterSkipReconcile.WithLabelValues(instance.Namespace, instance.Name).Set(0)
if common.IsSkipReconcile(ctx, instance) {
monitoring.RedisClusterSkipReconcile.WithLabelValues(instance.Namespace, instance.Name).Set(1)
return intctrlutil.Reconciled()
@ -230,7 +230,9 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
}
logger.Info("healthy leader count does not match desired; attempting to repair disconnected masters")
monitoring.RedisClusterRepairDisconnectedAttempt.WithLabelValues(instance.Namespace, instance.Name).Inc()
if err = k8sutils.RepairDisconnectedMasters(ctx, r.K8sClient, instance); err != nil {
monitoring.RedisClusterRepairDisconnectedFailed.WithLabelValues(instance.Namespace, instance.Name).Inc()
logger.Error(err, "failed to repair disconnected masters")
}
@ -256,7 +258,9 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
}
if int(totalReplicas) > 1 && unhealthyNodeCount >= int(totalReplicas)-1 {
logger.Info("unhealthy nodes exist after attempting to repair disconnected masters; starting failover")
monitoring.RedisClusterResetAttempt.WithLabelValues(instance.Namespace, instance.Name).Inc()
if err = k8sutils.ExecuteFailoverOperation(ctx, r.K8sClient, instance); err != nil {
monitoring.RedisClusterResetFailed.WithLabelValues(instance.Namespace, instance.Name).Inc()
return intctrlutil.RequeueE(ctx, err, "")
}
}
@ -287,7 +291,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
for _, fakeRole := range []string{"leader", "follower"} {
labels := common.GetRedisLabels(instance.GetName()+"-"+fakeRole, common.SetupTypeCluster, fakeRole, instance.GetLabels())
if err = r.Healer.UpdateRedisRoleLabel(ctx, instance.GetNamespace(), labels, instance.Spec.KubernetesConfig.ExistingPasswordSecret); err != nil {
if err = r.Healer.UpdateRedisRoleLabel(ctx, instance.GetNamespace(), labels, instance.Spec.KubernetesConfig.ExistingPasswordSecret, instance.Spec.TLS); err != nil {
return intctrlutil.RequeueE(ctx, err, "")
}
}

View File

@ -135,7 +135,7 @@ var _ = Describe("Redis Cluster Controller", func() {
Object: &rcvb2.RedisCluster{
ObjectMeta: testutil.CreateTestObject("redis-cluster-skip-test", ns, nil),
Spec: rcvb2.RedisClusterSpec{
Size: ptr.To(int32(3)),
ClusterSize: ptr.To(int32(3)),
KubernetesConfig: common.KubernetesConfig{
Image: testutil.DefaultRedisImage,
},

View File

@ -137,12 +137,15 @@ func (r *Reconciler) reconcileService(ctx context.Context, instance *rrvb2.Redis
}
func (r *Reconciler) reconcileRedis(ctx context.Context, instance *rrvb2.RedisReplication) (ctrl.Result, error) {
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
var realMaster string
masterNodes := k8sutils.GetRedisNodesByRole(ctx, r.K8sClient, instance, "master")
slaveNodes := k8sutils.GetRedisNodesByRole(ctx, r.K8sClient, instance, "slave")
masterNodes, err := k8sutils.GetRedisNodesByRole(ctx, r.K8sClient, instance, "master")
if err != nil {
return intctrlutil.RequeueE(ctx, err, "")
}
slaveNodes, err := k8sutils.GetRedisNodesByRole(ctx, r.K8sClient, instance, "slave")
if err != nil {
return intctrlutil.RequeueE(ctx, err, "")
}
if len(masterNodes) > 1 {
log.FromContext(ctx).Info("Creating redis replication by executing replication creation commands")
@ -153,6 +156,22 @@ func (r *Reconciler) reconcileRedis(ctx context.Context, instance *rrvb2.RedisRe
if err := k8sutils.CreateMasterSlaveReplication(ctx, r.K8sClient, instance, masterNodes, realMaster); err != nil {
return intctrlutil.RequeueAfter(ctx, time.Second*60, "")
}
} else if len(masterNodes) == 1 && len(slaveNodes) > 0 {
realMaster = masterNodes[0]
currentRealMaster := k8sutils.GetRedisReplicationRealMaster(ctx, r.K8sClient, instance, masterNodes)
if currentRealMaster == "" {
log.FromContext(ctx).Info("Detected disconnected slaves, reconfiguring replication",
"master", realMaster, "slaves", slaveNodes)
allPods := append(masterNodes, slaveNodes...)
if err := k8sutils.CreateMasterSlaveReplication(ctx, r.K8sClient, instance, allPods, realMaster); err != nil {
log.FromContext(ctx).Error(err, "Failed to reconfigure master-slave replication",
"master", realMaster, "slaves", slaveNodes)
return intctrlutil.RequeueAfter(ctx, time.Second*60, "")
}
log.FromContext(ctx).Info("Successfully reconfigured slave replication")
}
}
monitoring.RedisReplicationReplicasSizeMismatch.WithLabelValues(instance.Namespace, instance.Name).Set(0)
@ -171,17 +190,23 @@ func (r *Reconciler) reconcileStatus(ctx context.Context, instance *rrvb2.RedisR
var err error
var realMaster string
masterNodes := k8sutils.GetRedisNodesByRole(ctx, r.K8sClient, instance, "master")
masterNodes, err := k8sutils.GetRedisNodesByRole(ctx, r.K8sClient, instance, "master")
if err != nil {
return intctrlutil.RequeueE(ctx, err, "")
}
realMaster = k8sutils.GetRedisReplicationRealMaster(ctx, r.K8sClient, instance, masterNodes)
if err = r.UpdateRedisReplicationMaster(ctx, instance, realMaster); err != nil {
return intctrlutil.RequeueE(ctx, err, "")
}
labels := common.GetRedisLabels(instance.GetName(), common.SetupTypeReplication, "replication", instance.GetLabels())
if err = r.Healer.UpdateRedisRoleLabel(ctx, instance.GetNamespace(), labels, instance.Spec.KubernetesConfig.ExistingPasswordSecret); err != nil {
if err = r.Healer.UpdateRedisRoleLabel(ctx, instance.GetNamespace(), labels, instance.Spec.KubernetesConfig.ExistingPasswordSecret, instance.Spec.TLS); err != nil {
return intctrlutil.RequeueE(ctx, err, "")
}
slaveNodes := k8sutils.GetRedisNodesByRole(ctx, r.K8sClient, instance, "slave")
slaveNodes, err := k8sutils.GetRedisNodesByRole(ctx, r.K8sClient, instance, "slave")
if err != nil {
return intctrlutil.RequeueE(ctx, err, "")
}
if realMaster != "" {
monitoring.RedisReplicationConnectedSlavesTotal.WithLabelValues(instance.Namespace, instance.Name).Set(float64(len(slaveNodes)))
} else {

View File

@ -54,9 +54,9 @@ func (r *RedisSentinelReconciler) Reconcile(ctx context.Context, req ctrl.Reques
reconcilers := []reconciler{
{typ: "finalizer", rec: r.reconcileFinalizer},
{typ: "replication", rec: r.reconcileReplication},
{typ: "sentinel", rec: r.reconcileSentinel},
{typ: "pdb", rec: r.reconcilePDB},
{typ: "service", rec: r.reconcileService},
{typ: "sentinel", rec: r.reconcileSentinel},
}
for _, reconciler := range reconcilers {
@ -133,7 +133,7 @@ func (r *RedisSentinelReconciler) reconcileSentinel(ctx context.Context, instanc
return intctrlutil.RequeueE(ctx, err, "")
} else {
if instance.Spec.RedisSentinelConfig.ResolveHostnames == "yes" {
monitorAddr = fmt.Sprintf("%s.%s-headless.%s.svc", master.Name, rr.Name, rr.Namespace)
monitorAddr = fmt.Sprintf("%s.%s.%s.svc.cluster.local", master.Name, common.GetHeadlessServiceNameFromPodName(master.Name), rr.Namespace)
} else {
monitorAddr = master.Status.PodIP
}

View File

@ -105,7 +105,7 @@ func generatePodDisruptionBudgetDef(ctx context.Context, cr *rcvb2.RedisCluster,
}
// If we don't have a value for either, assume quorum: (N/2)+1
if pdbTemplate.Spec.MaxUnavailable == nil && pdbTemplate.Spec.MinAvailable == nil {
pdbTemplate.Spec.MinAvailable = &intstr.IntOrString{Type: intstr.Int, IntVal: (*cr.Spec.Size / 2) + 1}
pdbTemplate.Spec.MinAvailable = &intstr.IntOrString{Type: intstr.Int, IntVal: (*cr.Spec.ClusterSize / 2) + 1}
}
AddOwnerRefToObject(pdbTemplate, redisClusterAsOwner(cr))
return pdbTemplate

View File

@ -54,6 +54,7 @@ func generateRedisClusterParams(ctx context.Context, cr *rcvb2.RedisCluster, rep
Tolerations: params.Tolerations,
ServiceAccountName: cr.Spec.ServiceAccountName,
UpdateStrategy: cr.Spec.KubernetesConfig.UpdateStrategy,
PersistentVolumeClaimRetentionPolicy: cr.Spec.KubernetesConfig.PersistentVolumeClaimRetentionPolicy,
IgnoreAnnotations: cr.Spec.KubernetesConfig.IgnoreAnnotations,
HostNetwork: cr.Spec.HostNetwork,
MinReadySeconds: minreadyseconds,

View File

@ -165,7 +165,7 @@ func Test_generateRedisClusterParams(t *testing.T) {
t.Fatalf("Failed to unmarshal file %s: %v", path, err)
}
actualLeaderSTS := generateRedisClusterParams(context.TODO(), input, *input.Spec.Size, input.Spec.RedisLeader.RedisConfig.AdditionalRedisConfig, RedisClusterSTS{
actualLeaderSTS := generateRedisClusterParams(context.TODO(), input, *input.Spec.ClusterSize, input.Spec.RedisLeader.RedisConfig.AdditionalRedisConfig, RedisClusterSTS{
RedisStateFulType: "leader",
ExternalConfig: input.Spec.RedisLeader.RedisConfig.AdditionalRedisConfig,
SecurityContext: input.Spec.RedisLeader.SecurityContext,
@ -179,7 +179,7 @@ func Test_generateRedisClusterParams(t *testing.T) {
})
assert.EqualValues(t, expectedLeaderSTS, actualLeaderSTS, "Expected %+v, got %+v", expectedLeaderSTS, actualLeaderSTS)
actualFollowerSTS := generateRedisClusterParams(context.TODO(), input, *input.Spec.Size, input.Spec.RedisFollower.RedisConfig.AdditionalRedisConfig, RedisClusterSTS{
actualFollowerSTS := generateRedisClusterParams(context.TODO(), input, *input.Spec.ClusterSize, input.Spec.RedisFollower.RedisConfig.AdditionalRedisConfig, RedisClusterSTS{
RedisStateFulType: "follower",
ExternalConfig: input.Spec.RedisFollower.RedisConfig.AdditionalRedisConfig,
SecurityContext: input.Spec.RedisFollower.SecurityContext,
@ -498,6 +498,16 @@ func Test_generateRedisClusterInitContainerParams(t *testing.T) {
Name: "example-config",
},
},
SecurityContext: &corev1.SecurityContext{
RunAsUser: ptr.To(int64(1000)),
RunAsGroup: ptr.To(int64(1000)),
AllowPrivilegeEscalation: ptr.To(false),
ReadOnlyRootFilesystem: ptr.To(true),
Capabilities: &corev1.Capabilities{
Drop: []corev1.Capability{"ALL"},
Add: []corev1.Capability{"NET_BIND_SERVICE"},
},
},
}
data, err := os.ReadFile(path)

View File

@ -105,6 +105,7 @@ func generateRedisReplicationParams(cr *rrvb2.RedisReplication) statefulSetParam
TopologySpreadConstraints: cr.Spec.TopologySpreadConstrains,
TerminationGracePeriodSeconds: cr.Spec.TerminationGracePeriodSeconds,
UpdateStrategy: cr.Spec.KubernetesConfig.UpdateStrategy,
PersistentVolumeClaimRetentionPolicy: cr.Spec.KubernetesConfig.PersistentVolumeClaimRetentionPolicy,
IgnoreAnnotations: cr.Spec.KubernetesConfig.IgnoreAnnotations,
MinReadySeconds: minreadyseconds,
}

View File

@ -104,6 +104,7 @@ func generateRedisSentinelParams(ctx context.Context, cr *rsvb2.RedisSentinel, r
TopologySpreadConstraints: cr.Spec.TopologySpreadConstrains,
ServiceAccountName: cr.Spec.ServiceAccountName,
UpdateStrategy: cr.Spec.KubernetesConfig.UpdateStrategy,
PersistentVolumeClaimRetentionPolicy: cr.Spec.KubernetesConfig.PersistentVolumeClaimRetentionPolicy,
IgnoreAnnotations: cr.Spec.KubernetesConfig.IgnoreAnnotations,
MinReadySeconds: minreadyseconds,
}
@ -330,11 +331,21 @@ func getRedisReplicationMasterPod(ctx context.Context, client kubernetes.Interfa
log.FromContext(ctx).V(1).Info("Successfully got RedisReplication", "replication name", replicationName, "namespace", replicationNamespace)
}
masterPods := GetRedisNodesByRole(ctx, client, &replicationInstance, "master")
masterPods, err := GetRedisNodesByRole(ctx, client, &replicationInstance, "master")
if err != nil {
log.FromContext(ctx).Error(err, "Failed to get RedisReplication master pods", "replication name", replicationName, "namespace", replicationNamespace)
return emptyRedisInfo
}
if len(masterPods) == 0 {
log.FromContext(ctx).Error(errors.New("no master pods found"), "")
return emptyRedisInfo
}
replicasNum := *cr.Spec.Size
if replicasNum == 1 {
realMasterPod = masterPods[0]
} else {
for _, podName := range masterPods {
redisClient := configureRedisReplicationClient(ctx, client, &replicationInstance, podName)
defer redisClient.Close()
@ -344,6 +355,8 @@ func getRedisReplicationMasterPod(ctx context.Context, client kubernetes.Interfa
break
}
}
}
if realMasterPod == "" {
log.FromContext(ctx).Error(errors.New("no real master pod found"), "")
return emptyRedisInfo

View File

@ -281,6 +281,16 @@ func Test_generateRedisSentinelInitContainerParams(t *testing.T) {
SubPathExpr: "",
},
},
SecurityContext: &corev1.SecurityContext{
RunAsUser: ptr.To(int64(1000)),
RunAsGroup: ptr.To(int64(1000)),
AllowPrivilegeEscalation: ptr.To(false),
ReadOnlyRootFilesystem: ptr.To(true),
Capabilities: &corev1.Capabilities{
Drop: []corev1.Capability{"ALL"},
Add: []corev1.Capability{"NET_BIND_SERVICE"},
},
},
}
data, err := os.ReadFile(path)

View File

@ -112,6 +112,7 @@ func generateRedisStandaloneParams(cr *rvb2.Redis) statefulSetParameters {
TerminationGracePeriodSeconds: cr.Spec.TerminationGracePeriodSeconds,
Tolerations: cr.Spec.Tolerations,
UpdateStrategy: cr.Spec.KubernetesConfig.UpdateStrategy,
PersistentVolumeClaimRetentionPolicy: cr.Spec.KubernetesConfig.PersistentVolumeClaimRetentionPolicy,
IgnoreAnnotations: cr.Spec.KubernetesConfig.IgnoreAnnotations,
MinReadySeconds: minreadyseconds,
}

View File

@ -291,6 +291,16 @@ func Test_generateRedisStandaloneInitContainerParams(t *testing.T) {
Name: "example-config",
},
},
SecurityContext: &corev1.SecurityContext{
RunAsUser: ptr.To(int64(1000)),
RunAsGroup: ptr.To(int64(1000)),
AllowPrivilegeEscalation: ptr.To(false),
ReadOnlyRootFilesystem: ptr.To(true),
Capabilities: &corev1.Capabilities{
Drop: []corev1.Capability{"ALL"},
Add: []corev1.Capability{"NET_BIND_SERVICE"},
},
},
}
data, err := os.ReadFile(path)

View File

@ -568,8 +568,16 @@ func configureRedisReplicationClient(ctx context.Context, client kubernetes.Inte
log.FromContext(ctx).Error(err, "Error in getting redis password")
}
}
var addr string
if cr.Spec.TLS != nil {
// Use DNS name for TLS connections
addr = fmt.Sprintf("%s:%d", getRedisReplicationHostname(redisInfo, cr), 6379)
} else {
// Use IP address for non-TLS connections
addr = getRedisServerAddress(ctx, client, redisInfo, 6379)
}
opts := &redis.Options{
Addr: getRedisServerAddress(ctx, client, redisInfo, 6379),
Addr: addr,
Password: pass,
DB: 0,
}
@ -579,12 +587,16 @@ func configureRedisReplicationClient(ctx context.Context, client kubernetes.Inte
return redis.NewClient(opts)
}
func getRedisReplicationHostname(redisInfo RedisDetails, cr *rrvb2.RedisReplication) string {
return fmt.Sprintf("%s.%s-headless.%s.svc.cluster.local", redisInfo.PodName, cr.Name, cr.Namespace)
}
// Get Redis nodes by it's role i.e. master, slave and sentinel
func GetRedisNodesByRole(ctx context.Context, cl kubernetes.Interface, cr *rrvb2.RedisReplication, redisRole string) []string {
func GetRedisNodesByRole(ctx context.Context, cl kubernetes.Interface, cr *rrvb2.RedisReplication, redisRole string) ([]string, error) {
statefulset, err := GetStatefulSet(ctx, cl, cr.GetNamespace(), cr.GetName())
if err != nil {
log.FromContext(ctx).Error(err, "Failed to Get the Statefulset of the", "custom resource", cr.Name, "in namespace", cr.Namespace)
return nil
return nil, err
}
var pods []string
@ -594,32 +606,35 @@ func GetRedisNodesByRole(ctx context.Context, cl kubernetes.Interface, cr *rrvb2
podName := statefulset.Name + "-" + strconv.Itoa(i)
redisClient := configureRedisReplicationClient(ctx, cl, cr, podName)
defer redisClient.Close()
podRole := checkRedisServerRole(ctx, redisClient, podName)
podRole, err := checkRedisServerRole(ctx, redisClient, podName)
if err != nil {
return nil, err
}
if podRole == redisRole {
pods = append(pods, podName)
}
}
return pods
return pods, nil
}
// Check the Redis Server Role i.e. master, slave and sentinel
func checkRedisServerRole(ctx context.Context, redisClient *redis.Client, podName string) string {
func checkRedisServerRole(ctx context.Context, redisClient *redis.Client, podName string) (string, error) {
info, err := redisClient.Info(ctx, "Replication").Result()
if err != nil {
log.FromContext(ctx).Error(err, "Failed to Get the role Info of the", "redis pod", podName)
return ""
return "", err
}
lines := strings.Split(info, "\r\n")
for _, line := range lines {
if strings.HasPrefix(line, "role:") {
role := strings.TrimPrefix(line, "role:")
log.FromContext(ctx).V(1).Info("Role of the Redis Pod", "pod", podName, "role", role)
return role
return role, nil
}
}
log.FromContext(ctx).Error(err, "Failed to find role from Info # Replication in", "redis pod", podName)
return ""
return "", err
}
// checkAttachedSlave would return redis pod name which has slave
@ -655,20 +670,29 @@ func CreateMasterSlaveReplication(ctx context.Context, client kubernetes.Interfa
Namespace: cr.Namespace,
}
var realMasterAddr string
if cr.Spec.TLS != nil {
// Use DNS name for TLS connections to match certificate validation
realMasterAddr = getRedisReplicationHostname(realMasterInfo, cr)
log.FromContext(ctx).V(1).Info("Using DNS address for TLS master replication", "masterAddr", realMasterAddr)
} else {
// Use IP address for non-TLS connections
realMasterPodIP := getRedisServerIP(ctx, client, realMasterInfo)
if realMasterPodIP == "" {
return errors.New("CreateMasterSlaveReplication got empty master IP, refusing")
}
realMasterAddr = realMasterPodIP
log.FromContext(ctx).V(1).Info("Using IP address for non-TLS master replication", "masterAddr", realMasterAddr)
}
for i := 0; i < len(masterPods); i++ {
if masterPods[i] != realMasterPod {
redisClient := configureRedisReplicationClient(ctx, client, cr, masterPods[i])
defer redisClient.Close()
log.FromContext(ctx).V(1).Info("Setting the", "pod", masterPods[i], "to slave of", realMasterPod)
err := redisClient.SlaveOf(ctx, realMasterPodIP, "6379").Err()
log.FromContext(ctx).V(1).Info("Setting the", "pod", masterPods[i], "to slave of", realMasterPod, "masterAddr", realMasterAddr)
err := redisClient.SlaveOf(ctx, realMasterAddr, "6379").Err()
if err != nil {
log.FromContext(ctx).Error(err, "Failed to set", "pod", masterPods[i], "to slave of", realMasterPod)
log.FromContext(ctx).Error(err, "Failed to set", "pod", masterPods[i], "to slave of", realMasterPod, "masterAddr", realMasterAddr)
return err
}
}

View File

@ -365,7 +365,7 @@ func TestCreateMultipleLeaderRedisCommand(t *testing.T) {
Namespace: "default",
},
Spec: rcvb2.RedisClusterSpec{
Size: ptr.To(int32(3)),
ClusterSize: ptr.To(int32(3)),
ClusterVersion: ptr.To("v7"),
Port: ptr.To(6379),
},
@ -386,7 +386,7 @@ func TestCreateMultipleLeaderRedisCommand(t *testing.T) {
Namespace: "default",
},
Spec: rcvb2.RedisClusterSpec{
Size: ptr.To(int32(3)),
ClusterSize: ptr.To(int32(3)),
Port: ptr.To(6379),
},
},
@ -466,7 +466,7 @@ func TestCreateRedisReplicationCommand(t *testing.T) {
Namespace: "default",
},
Spec: rcvb2.RedisClusterSpec{
Size: ptr.To(int32(3)),
ClusterSize: ptr.To(int32(3)),
KubernetesConfig: common.KubernetesConfig{
ExistingPasswordSecret: &common.ExistingPasswordSecret{
Name: ptr.To("redis-password-secret"),
@ -506,7 +506,7 @@ func TestCreateRedisReplicationCommand(t *testing.T) {
Namespace: "default",
},
Spec: rcvb2.RedisClusterSpec{
Size: ptr.To(int32(3)),
ClusterSize: ptr.To(int32(3)),
KubernetesConfig: common.KubernetesConfig{
ExistingPasswordSecret: &common.ExistingPasswordSecret{
Name: ptr.To("redis-password-secret"),
@ -540,7 +540,7 @@ func TestCreateRedisReplicationCommand(t *testing.T) {
Namespace: "default",
},
Spec: rcvb2.RedisClusterSpec{
Size: ptr.To(int32(3)),
ClusterSize: ptr.To(int32(3)),
Port: ptr.To(6379),
},
},
@ -837,7 +837,10 @@ func Test_checkRedisServerRole(t *testing.T) {
mock.ExpectInfo("Replication").SetVal(tt.infoReturn)
}
role := checkRedisServerRole(ctx, client, tt.podName)
role, err := checkRedisServerRole(ctx, client, tt.podName)
if err != nil {
assert.Error(t, err)
}
if tt.shouldFail {
assert.Empty(t, role, "Test case: "+tt.name)
} else {

View File

@ -7,6 +7,7 @@ import (
"errors"
"strings"
"github.com/OT-CONTAINER-KIT/redis-operator/internal/util/cryptutil"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
logf "sigs.k8s.io/controller-runtime/pkg/log"
@ -60,9 +61,13 @@ func getRedisTLSConfig(ctx context.Context, client kubernetes.Interface, namespa
return &tls.Config{
Certificates: []tls.Certificate{cert},
ServerName: podName + "." + namespace,
RootCAs: tlsCaCertificates,
MinVersion: tls.VersionTLS12,
ClientAuth: tls.NoClientCert,
InsecureSkipVerify: true,
VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
_, _, err := cryptutil.VerifyCertificateExceptServerName(rawCerts, &tls.Config{RootCAs: tlsCaCertificates})
return err
},
}
}

View File

@ -114,6 +114,7 @@ type statefulSetParameters struct {
ExternalConfig *string
ServiceAccountName *string
UpdateStrategy appsv1.StatefulSetUpdateStrategy
PersistentVolumeClaimRetentionPolicy *appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy
RecreateStatefulSet bool
RecreateStatefulsetStrategy *metav1.DeletionPropagation
TerminationGracePeriodSeconds *int64
@ -281,6 +282,7 @@ func generateStatefulSetsDef(stsMeta metav1.ObjectMeta, params statefulSetParame
ServiceName: fmt.Sprintf("%s-headless", stsMeta.Name),
Replicas: params.Replicas,
UpdateStrategy: params.UpdateStrategy,
PersistentVolumeClaimRetentionPolicy: params.PersistentVolumeClaimRetentionPolicy,
MinReadySeconds: params.MinReadySeconds,
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
@ -328,7 +330,7 @@ func generateStatefulSetsDef(stsMeta metav1.ObjectMeta, params statefulSetParame
statefulset.Spec.VolumeClaimTemplates = append(statefulset.Spec.VolumeClaimTemplates, createPVCTemplate(pvcTplName, stsMeta, params.PersistentVolumeClaim))
}
if params.ExternalConfig != nil {
statefulset.Spec.Template.Spec.Volumes = getExternalConfig(*params.ExternalConfig)
statefulset.Spec.Template.Spec.Volumes = append(statefulset.Spec.Template.Spec.Volumes, getExternalConfig(*params.ExternalConfig)...)
}
if containerParams.AdditionalVolume != nil {
statefulset.Spec.Template.Spec.Volumes = append(statefulset.Spec.Template.Spec.Volumes, containerParams.AdditionalVolume...)
@ -594,6 +596,7 @@ func generateInitContainerDef(role, name string, initcontainerParams initContain
Image: image,
ImagePullPolicy: corev1.PullIfNotPresent,
Command: []string{"/operator", "agent"},
SecurityContext: initcontainerParams.SecurityContext,
Env: getEnvironmentVariables(
containerParams.Role,
containerParams.EnabledPassword,

View File

@ -7,6 +7,7 @@ import (
"testing"
common "github.com/OT-CONTAINER-KIT/redis-operator/api/common/v1beta2"
"github.com/OT-CONTAINER-KIT/redis-operator/internal/features"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
appsv1 "k8s.io/api/apps/v1"
@ -1144,6 +1145,277 @@ func TestGenerateInitContainerDef(t *testing.T) {
}
}
func TestGenerateInitContainerDefWithSecurityContext(t *testing.T) {
// Save original feature gate state
originalEnabled := features.Enabled(features.GenerateConfigInInitContainer)
// Test cases for SecurityContext functionality
tests := []struct {
name string
initContainerName string
initContainerDef initContainerParameters
featureEnabled bool
expectedInitContainerDef []corev1.Container
mountPaths []corev1.VolumeMount
}{
{
name: "Test with SecurityContext when feature is enabled",
initContainerName: "redis",
initContainerDef: initContainerParameters{
Enabled: ptr.To(true),
Image: "redis-init-container:latest",
ImagePullPolicy: corev1.PullAlways,
Command: []string{"/bin/bash", "-c", "/app/restore.bash"},
PersistenceEnabled: ptr.To(false),
SecurityContext: &corev1.SecurityContext{
RunAsUser: ptr.To(int64(1000)),
RunAsGroup: ptr.To(int64(1000)),
},
Resources: &corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("100m"),
corev1.ResourceMemory: resource.MustParse("128Mi"),
},
},
},
featureEnabled: true,
expectedInitContainerDef: []corev1.Container{
{
Name: "init-config",
Image: "redis-operator:latest", // This will be the operator image
ImagePullPolicy: corev1.PullIfNotPresent,
Command: []string{"/operator", "agent"},
Args: []string{"bootstrap"},
SecurityContext: &corev1.SecurityContext{
RunAsUser: ptr.To(int64(1000)),
RunAsGroup: ptr.To(int64(1000)),
},
VolumeMounts: []corev1.VolumeMount{
{
Name: "config",
MountPath: "/etc/redis",
},
},
},
{
Name: "initredis",
Image: "redis-init-container:latest",
Command: []string{"/bin/bash", "-c", "/app/restore.bash"},
ImagePullPolicy: corev1.PullAlways,
VolumeMounts: getVolumeMount("redis", ptr.To(false), false, false, nil, []corev1.VolumeMount{}, nil, nil),
SecurityContext: &corev1.SecurityContext{
RunAsUser: ptr.To(int64(1000)),
RunAsGroup: ptr.To(int64(1000)),
},
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("100m"),
corev1.ResourceMemory: resource.MustParse("128Mi"),
},
},
Env: []corev1.EnvVar{},
},
},
mountPaths: []corev1.VolumeMount{},
},
{
name: "Test with SecurityContext when feature is disabled",
initContainerName: "redis",
initContainerDef: initContainerParameters{
Enabled: ptr.To(true),
Image: "redis-init-container:latest",
ImagePullPolicy: corev1.PullAlways,
Command: []string{"/bin/bash", "-c", "/app/restore.bash"},
PersistenceEnabled: ptr.To(false),
SecurityContext: &corev1.SecurityContext{
RunAsUser: ptr.To(int64(1000)),
RunAsGroup: ptr.To(int64(1000)),
},
Resources: &corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("100m"),
corev1.ResourceMemory: resource.MustParse("128Mi"),
},
},
},
featureEnabled: false,
expectedInitContainerDef: []corev1.Container{
{
Name: "initredis",
Image: "redis-init-container:latest",
Command: []string{"/bin/bash", "-c", "/app/restore.bash"},
ImagePullPolicy: corev1.PullAlways,
VolumeMounts: getVolumeMount("redis", ptr.To(false), false, false, nil, []corev1.VolumeMount{}, nil, nil),
SecurityContext: &corev1.SecurityContext{
RunAsUser: ptr.To(int64(1000)),
RunAsGroup: ptr.To(int64(1000)),
},
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("100m"),
corev1.ResourceMemory: resource.MustParse("128Mi"),
},
},
Env: []corev1.EnvVar{},
},
},
mountPaths: []corev1.VolumeMount{},
},
{
name: "Test without SecurityContext when feature is enabled",
initContainerName: "redis",
initContainerDef: initContainerParameters{
Enabled: ptr.To(true),
Image: "redis-init-container:latest",
ImagePullPolicy: corev1.PullAlways,
Command: []string{"/bin/bash", "-c", "/app/restore.bash"},
PersistenceEnabled: ptr.To(false),
Resources: &corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("100m"),
corev1.ResourceMemory: resource.MustParse("128Mi"),
},
},
},
featureEnabled: true,
expectedInitContainerDef: []corev1.Container{
{
Name: "init-config",
Image: "redis-operator:latest", // This will be the operator image
ImagePullPolicy: corev1.PullIfNotPresent,
Command: []string{"/operator", "agent"},
Args: []string{"bootstrap"},
VolumeMounts: []corev1.VolumeMount{
{
Name: "config",
MountPath: "/etc/redis",
},
},
},
{
Name: "initredis",
Image: "redis-init-container:latest",
Command: []string{"/bin/bash", "-c", "/app/restore.bash"},
ImagePullPolicy: corev1.PullAlways,
VolumeMounts: getVolumeMount("redis", ptr.To(false), false, false, nil, []corev1.VolumeMount{}, nil, nil),
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("100m"),
corev1.ResourceMemory: resource.MustParse("128Mi"),
},
},
Env: []corev1.EnvVar{},
},
},
mountPaths: []corev1.VolumeMount{},
},
{
name: "Test sentinel role with SecurityContext when feature is enabled",
initContainerName: "sentinel",
initContainerDef: initContainerParameters{
Enabled: ptr.To(true),
Image: "redis-sentinel-init:latest",
ImagePullPolicy: corev1.PullAlways,
Command: []string{"/bin/bash", "-c", "/app/sentinel-init.bash"},
PersistenceEnabled: ptr.To(false),
SecurityContext: &corev1.SecurityContext{
RunAsUser: ptr.To(int64(999)),
RunAsGroup: ptr.To(int64(999)),
},
},
featureEnabled: true,
expectedInitContainerDef: []corev1.Container{
{
Name: "init-config",
Image: "redis-operator:latest", // This will be the operator image
ImagePullPolicy: corev1.PullIfNotPresent,
Command: []string{"/operator", "agent"},
Args: []string{"bootstrap", "--sentinel"},
SecurityContext: &corev1.SecurityContext{
RunAsUser: ptr.To(int64(999)),
RunAsGroup: ptr.To(int64(999)),
},
VolumeMounts: []corev1.VolumeMount{
{
Name: "config",
MountPath: "/etc/redis",
},
},
},
{
Name: "initsentinel",
Image: "redis-sentinel-init:latest",
Command: []string{"/bin/bash", "-c", "/app/sentinel-init.bash"},
ImagePullPolicy: corev1.PullAlways,
VolumeMounts: getVolumeMount("sentinel", ptr.To(false), false, false, nil, []corev1.VolumeMount{}, nil, nil),
SecurityContext: &corev1.SecurityContext{
RunAsUser: ptr.To(int64(999)),
RunAsGroup: ptr.To(int64(999)),
},
Resources: corev1.ResourceRequirements{},
Env: []corev1.EnvVar{},
},
},
mountPaths: []corev1.VolumeMount{},
},
}
for i := range tests {
test := tests[i]
t.Run(test.name, func(t *testing.T) {
// Set feature gate state for this test
if test.featureEnabled {
if err := features.MutableFeatureGate.Set("GenerateConfigInInitContainer=true"); err != nil {
t.Fatalf("failed to set feature gate: %v", err)
}
} else {
if err := features.MutableFeatureGate.Set("GenerateConfigInInitContainer=false"); err != nil {
t.Fatalf("failed to set feature gate: %v", err)
}
}
// Note: The operator image will be determined by the actual implementation
// We'll test the SecurityContext functionality without mocking the image
initContainer := generateInitContainerDef("", test.initContainerName, test.initContainerDef, test.mountPaths, containerParameters{}, ptr.To("v6"))
// Verify the SecurityContext is correctly applied
if test.featureEnabled {
// When feature is enabled, we should have both init-config and user init container
assert.Len(t, initContainer, 2, "Should have 2 init containers when feature is enabled")
// Check init-config container
initConfigContainer := initContainer[0]
assert.Equal(t, "init-config", initConfigContainer.Name)
assert.Equal(t, test.initContainerDef.SecurityContext, initConfigContainer.SecurityContext, "init-config container should have SecurityContext")
// Check user init container
userInitContainer := initContainer[1]
assert.Equal(t, "init"+test.initContainerName, userInitContainer.Name)
assert.Equal(t, test.initContainerDef.SecurityContext, userInitContainer.SecurityContext, "user init container should have SecurityContext")
} else {
// When feature is disabled, we should only have user init container
assert.Len(t, initContainer, 1, "Should have 1 init container when feature is disabled")
userInitContainer := initContainer[0]
assert.Equal(t, "init"+test.initContainerName, userInitContainer.Name)
assert.Equal(t, test.initContainerDef.SecurityContext, userInitContainer.SecurityContext, "user init container should have SecurityContext")
}
})
}
// Restore original feature gate state
if originalEnabled {
if err := features.MutableFeatureGate.Set("GenerateConfigInInitContainer=true"); err != nil {
t.Fatalf("failed to restore feature gate: %v", err)
}
} else {
if err := features.MutableFeatureGate.Set("GenerateConfigInInitContainer=false"); err != nil {
t.Fatalf("failed to restore feature gate: %v", err)
}
}
}
func TestGenerateTLSEnvironmentVariables(t *testing.T) {
tlsConfig := &common.TLSConfig{
CaKeyFile: "test_ca.crt",
@ -1527,6 +1799,12 @@ func TestGenerateStatefulSetsDef(t *testing.T) {
},
},
Volumes: []corev1.Volume{
{
Name: "config",
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
},
{
Name: "external-config",
VolumeSource: corev1.VolumeSource{

View File

@ -32,5 +32,9 @@ func RegisterRedisClusterMetrics() {
RedisClusterRebalanceTotal,
RedisClusterRemoveFollowerAttempt,
RedisClusterReshardTotal,
RedisClusterRepairDisconnectedAttempt,
RedisClusterRepairDisconnectedFailed,
RedisClusterResetAttempt,
RedisClusterResetFailed,
)
}

View File

@ -48,6 +48,30 @@ var RedisClusterDescription = map[string]MetricDescription{
Type: "Counter",
labels: []string{"namespace", "instance"},
},
"RedisClusterRepairDisconnectedAttempt": {
Name: "rediscluster_repair_disconnected_attempt",
Help: "Number of times to repair a Redis cluster disconnected from the cluster.",
Type: "Counter",
labels: []string{"namespace", "instance"},
},
"RedisClusterRepairFailed": {
Name: "rediscluster_repair_failed",
Help: "Number of times to repair a Redis cluster failed.",
Type: "Counter",
labels: []string{"namespace", "instance"},
},
"RedisClusterResetAttempt": {
Name: "rediscluster_reset_attempt",
Help: "Number of times to reset a Redis cluster.",
Type: "Counter",
labels: []string{"namespace", "instance"},
},
"RedisClusterResetFailed": {
Name: "rediscluster_reset_failed",
Help: "Number of times to reset a Redis cluster failed.",
Type: "Counter",
labels: []string{"namespace", "instance"},
},
}
var (
@ -114,6 +138,38 @@ var (
},
RedisClusterDescription["RedisClusterAddingNodeAttempt"].labels,
)
RedisClusterRepairDisconnectedAttempt = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: RedisClusterDescription["RedisClusterRepairDisconnectedAttempt"].Name,
Help: RedisClusterDescription["RedisClusterRepairDisconnectedAttempt"].Help,
},
RedisClusterDescription["RedisClusterRepairDisconnectedAttempt"].labels,
)
RedisClusterRepairDisconnectedFailed = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: RedisClusterDescription["RedisClusterRepairDisconnectedFailed"].Name,
Help: RedisClusterDescription["RedisClusterRepairDisconnectedFailed"].Help,
},
RedisClusterDescription["RedisClusterRepairDisconnectedFailed"].labels,
)
RedisClusterResetAttempt = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: RedisClusterDescription["RedisClusterResetAttempt"].Name,
Help: RedisClusterDescription["RedisClusterResetAttempt"].Help,
},
RedisClusterDescription["RedisClusterResetAttempt"].labels,
)
RedisClusterResetFailed = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: RedisClusterDescription["RedisClusterResetFailed"].Name,
Help: RedisClusterDescription["RedisClusterResetFailed"].Help,
},
RedisClusterDescription["RedisClusterResetFailed"].labels,
)
)
// ListMetrics will create a slice with the metrics available in metricDescription

View File

@ -2,6 +2,7 @@ package redis
import (
"context"
"crypto/tls"
"net"
"strconv"
"strings"
@ -14,9 +15,18 @@ const (
)
type ConnectionInfo struct {
IP string
// Host is the IP address or hostname for connection
Host string
// Port is the port for redis or sentinel
Port string
Password string
// TLSConfig configuration, nil means TLS is disabled
TLSConfig *tls.Config
}
// GetAddress returns the connection address
func (c *ConnectionInfo) GetAddress() string {
return net.JoinHostPort(c.Host, c.Port)
}
type Client interface {
@ -55,10 +65,13 @@ func (s *service) createClient() *rediscli.Client {
return nil
}
opts := &rediscli.Options{
Addr: net.JoinHostPort(s.connectionInfo.IP, s.connectionInfo.Port),
Addr: s.connectionInfo.GetAddress(),
Password: s.connectionInfo.Password,
DB: 0,
}
if s.connectionInfo.TLSConfig != nil {
opts.TLSConfig = s.connectionInfo.TLSConfig
}
return rediscli.NewClient(opts)
}
@ -101,7 +114,7 @@ func (c *service) SentinelMonitor(ctx context.Context, master *ConnectionInfo, m
return err
}
cmd = rediscli.NewBoolCmd(ctx, "SENTINEL", "MONITOR", masterGroupName, master.IP, master.Port, quorum)
cmd = rediscli.NewBoolCmd(ctx, "SENTINEL", "MONITOR", masterGroupName, master.Host, master.Port, quorum)
err = client.Process(ctx, cmd)
if err != nil {
return err

View File

@ -37,7 +37,7 @@ func New(name string, options ...customFieldOption) *v1beta2.RedisCluster {
Namespace: "default",
},
Spec: v1beta2.RedisClusterSpec{
Size: &size,
ClusterSize: &size,
Storage: &v1beta2.ClusterStorage{},
},
}

View File

@ -0,0 +1,71 @@
package cryptutil
import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"time"
)
// VerifyCertificateExceptServerName verifies a certificate chain without hostname verification.
// This function performs the same validation as the standard TLS verification process,
// but deliberately omits DNS name checking to support scenarios where certificates
// may not match the internal Kubernetes service names.
func VerifyCertificateExceptServerName(rawCerts [][]byte, config *tls.Config) ([]*x509.Certificate, [][]*x509.Certificate, error) {
if len(rawCerts) == 0 {
return nil, nil, errors.New("tls: no certificates provided by peer")
}
if config == nil {
return nil, nil, errors.New("tls: config cannot be nil")
}
if config.RootCAs == nil {
return nil, nil, errors.New("tls: no root CAs configured for verification")
}
// Parse all certificates in the chain
certs := make([]*x509.Certificate, len(rawCerts))
for i, asn1Data := range rawCerts {
cert, err := x509.ParseCertificate(asn1Data)
if err != nil {
return nil, nil, fmt.Errorf("tls: failed to parse certificate at index %d: %w", i, err)
}
certs[i] = cert
}
// Get the verification time
var verifyTime time.Time
if config.Time != nil {
verifyTime = config.Time()
} else {
verifyTime = time.Now()
}
// Build the intermediate certificate pool from the certificate chain
intermediates := x509.NewCertPool()
for i := 1; i < len(certs); i++ {
intermediates.AddCert(certs[i])
}
// Set up verification options without DNS name validation
// This is the key difference from standard TLS verification
opts := x509.VerifyOptions{
Roots: config.RootCAs,
Intermediates: intermediates,
CurrentTime: verifyTime,
// Deliberately omit DNSName to skip hostname verification
// This allows certificates that don't match the server hostname
// but are still valid and signed by a trusted CA
}
// Verify the leaf certificate (first in the chain)
leafCert := certs[0]
chains, err := leafCert.Verify(opts)
if err != nil {
return nil, nil, fmt.Errorf("tls: certificate verification failed: %w", err)
}
return certs, chains, nil
}

View File

@ -2,6 +2,7 @@ package main
import (
"context"
"crypto/tls"
"fmt"
"os"
"strings"
@ -27,6 +28,7 @@ var (
host string
pass string
mode string
tlsFlag bool
sentinelPass string
)
@ -52,6 +54,7 @@ func main() {
rootCmd.PersistentFlags().StringVarP(&host, hostFlag, "H", "", "redis host")
rootCmd.PersistentFlags().StringVarP(&pass, passFlag, "P", "", "redis password")
rootCmd.PersistentFlags().StringVarP(&mode, modeFlag, "M", "", "redis mode")
rootCmd.PersistentFlags().BoolVarP(&tlsFlag, "tls", "T", false, "enable tls")
rootCmd.PersistentFlags().StringVarP(&sentinelPass, sentinelPassFlag, "S", "", "redis sentinel password")
rootCmd.Execute()
}
@ -74,7 +77,7 @@ func genRedisDataCmd(cmd *cobra.Command, args []string) {
hosts[i] = strings.TrimSpace(hosts[i])
}
rdb, err := createRedisClient(mode, hosts, pass, sentinelPass)
rdb, err := createRedisClient(mode, hosts, pass, sentinelPass, tlsFlag)
if err != nil {
fmt.Printf("failed to create redis client: %v\n", err)
return
@ -125,7 +128,7 @@ func checkRedisData() error {
hosts[i] = strings.TrimSpace(hosts[i])
}
rdb, err := createRedisClient(mode, hosts, pass, sentinelPass)
rdb, err := createRedisClient(mode, hosts, pass, sentinelPass, tlsFlag)
if err != nil {
return fmt.Errorf("failed to create redis client: %w", err)
}
@ -153,20 +156,28 @@ func checkRedisData() error {
return nil
}
func createRedisClient(mode string, hosts []string, pass string, sentinelPass string) (redis.UniversalClient, error) {
func createRedisClient(mode string, hosts []string, pass string, sentinelPass string, tlsFlag bool) (redis.UniversalClient, error) {
switch mode {
case "cluster":
return redis.NewClusterClient(&redis.ClusterOptions{
opts := &redis.ClusterOptions{
Addrs: hosts,
Password: pass,
}), nil
}
if tlsFlag {
opts.TLSConfig = &tls.Config{InsecureSkipVerify: true}
}
return redis.NewClusterClient(opts), nil
case "sentinel":
return redis.NewFailoverClient(&redis.FailoverOptions{
opts := &redis.FailoverOptions{
MasterName: "myMaster",
SentinelAddrs: hosts,
Password: pass,
SentinelPassword: sentinelPass,
}), nil
}
if tlsFlag {
opts.TLSConfig = &tls.Config{InsecureSkipVerify: true}
}
return redis.NewFailoverClient(opts), nil
default:
return nil, fmt.Errorf("unsupported redis mode: %s", mode)
}

View File

@ -9,6 +9,7 @@ data:
import (
"context"
"crypto/tls"
"fmt"
"os"
"strings"
@ -34,6 +35,7 @@ data:
host string
pass string
mode string
tlsFlag bool
sentinelPass string
)
@ -59,6 +61,7 @@ data:
rootCmd.PersistentFlags().StringVarP(&host, hostFlag, "H", "", "redis host")
rootCmd.PersistentFlags().StringVarP(&pass, passFlag, "P", "", "redis password")
rootCmd.PersistentFlags().StringVarP(&mode, modeFlag, "M", "", "redis mode")
rootCmd.PersistentFlags().BoolVarP(&tlsFlag, "tls", "T", false, "enable tls")
rootCmd.PersistentFlags().StringVarP(&sentinelPass, sentinelPassFlag, "S", "", "redis sentinel password")
rootCmd.Execute()
}
@ -81,7 +84,7 @@ data:
hosts[i] = strings.TrimSpace(hosts[i])
}
rdb, err := createRedisClient(mode, hosts, pass, sentinelPass)
rdb, err := createRedisClient(mode, hosts, pass, sentinelPass, tlsFlag)
if err != nil {
fmt.Printf("failed to create redis client: %v\n", err)
return
@ -132,7 +135,7 @@ data:
hosts[i] = strings.TrimSpace(hosts[i])
}
rdb, err := createRedisClient(mode, hosts, pass, sentinelPass)
rdb, err := createRedisClient(mode, hosts, pass, sentinelPass, tlsFlag)
if err != nil {
return fmt.Errorf("failed to create redis client: %w", err)
}
@ -160,20 +163,28 @@ data:
return nil
}
func createRedisClient(mode string, hosts []string, pass string, sentinelPass string) (redis.UniversalClient, error) {
func createRedisClient(mode string, hosts []string, pass string, sentinelPass string, tlsFlag bool) (redis.UniversalClient, error) {
switch mode {
case "cluster":
return redis.NewClusterClient(&redis.ClusterOptions{
opts := &redis.ClusterOptions{
Addrs: hosts,
Password: pass,
}), nil
}
if tlsFlag {
opts.TLSConfig = &tls.Config{InsecureSkipVerify: true}
}
return redis.NewClusterClient(opts), nil
case "sentinel":
return redis.NewFailoverClient(&redis.FailoverOptions{
opts := &redis.FailoverOptions{
MasterName: "myMaster",
SentinelAddrs: hosts,
Password: pass,
SentinelPassword: sentinelPass,
}), nil
}
if tlsFlag {
opts.TLSConfig = &tls.Config{InsecureSkipVerify: true}
}
return redis.NewFailoverClient(opts), nil
default:
return nil, fmt.Errorf("unsupported redis mode: %s", mode)
}
@ -263,7 +274,7 @@ metadata:
spec:
containers:
- name: data-assert
image: docker.io/library/golang:1.23.4
image: docker.m.daocloud.io/library/golang:1.23.4
command: ["/bin/sh", "-c"]
args:
- |

View File

@ -18,7 +18,7 @@ metadata:
spec:
containers:
- name: data-assert
image: docker.io/library/golang:1.23.4
image: docker.m.daocloud.io/library/golang:1.23.4
command: ["/bin/sh", "-c"]
args:
- |

View File

@ -17,6 +17,35 @@ spec:
file: ha.yaml
- apply:
file: ../../../../data-assert/resources.yaml
# create certificate secret use script, because the namespace name is unknown in the yaml file
- script:
timeout: 10s
content: |
cat <<EOF | kubectl apply -f -
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: selfsigned-issuer
namespace: ${NAMESPACE}
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: redis-tls
namespace: ${NAMESPACE}
spec:
commonName: redis-replication
dnsNames:
- "*.redis-replication-headless.${NAMESPACE}.svc.cluster.local"
- "*.redis-sentinel-sentinel-headless.${NAMESPACE}.svc.cluster.local"
issuerRef:
name: selfsigned-issuer
kind: Issuer
secretName: redis-tls-cert
EOF
- name: Test Master IP consistency
try:
@ -26,10 +55,9 @@ spec:
timeout: 10s
content: |
export MASTER_POD_FROM_STATUS=$(kubectl -n ${NAMESPACE} get redisreplication redis-replication -o jsonpath='{.status.masterNode}');
export MASTER_IP_FROM_STATUS=$(kubectl -n ${NAMESPACE} get pod ${MASTER_POD_FROM_STATUS} -o jsonpath='{.status.podIP}');
export FROM_SENTINEL=$(kubectl -n ${NAMESPACE} exec redis-sentinel-sentinel-0 -- redis-cli -a Opstree@1234sentinel -p 26379 sentinel get-master-addr-by-name myMaster | head -n 1);
export MASTER_IP_FROM_LABEL=$(kubectl -n ${NAMESPACE} get pod -l app=redis-replication,redis-role=master,redis_setup_type=replication -o jsonpath='{.items[0].status.podIP}');
if [ "$FROM_SENTINEL" = "$MASTER_IP_FROM_LABEL" ] && [ "$FROM_SENTINEL" = "$MASTER_IP_FROM_STATUS" ]; then echo "OK"; else echo "FAIL"; fi
export FROM_SENTINEL=$(kubectl -n ${NAMESPACE} exec redis-sentinel-sentinel-0 -c redis-sentinel-sentinel -- redis-cli --no-auth-warning --tls --cacert /tls/ca.crt -a Opstree@1234sentinel -p 26379 sentinel get-master-addr-by-name myMaster | head -n 1 | cut -d'.' -f1);
export MASTER_POD_FROM_LABEL=$(kubectl -n ${NAMESPACE} get pod -l app=redis-replication,redis-role=master,redis_setup_type=replication -o jsonpath='{.items[0].metadata.name}');
if [ "$FROM_SENTINEL" = "$MASTER_POD_FROM_LABEL" ] && [ "$FROM_SENTINEL" = "$MASTER_POD_FROM_STATUS" ]; then echo "OK"; else echo "FAIL"; fi
check:
(contains($stdout, 'OK')): true
@ -42,6 +70,7 @@ spec:
bash -c "cd /go/src/data-assert && go run main.go gen-redis-data \
--host redis-sentinel-sentinel.${NAMESPACE}.svc.cluster.local:26379 \
--mode sentinel \
--tls \
--password-sentinel Opstree@1234sentinel \
--password Opstree@1234replication"
check:
@ -62,10 +91,9 @@ spec:
timeout: 10s
content: |
export MASTER_POD_FROM_STATUS=$(kubectl -n ${NAMESPACE} get redisreplication redis-replication -o jsonpath='{.status.masterNode}');
export MASTER_IP_FROM_STATUS=$(kubectl -n ${NAMESPACE} get pod ${MASTER_POD_FROM_STATUS} -o jsonpath='{.status.podIP}');
export FROM_SENTINEL=$(kubectl -n ${NAMESPACE} exec redis-sentinel-sentinel-0 -- redis-cli -a Opstree@1234sentinel -p 26379 sentinel get-master-addr-by-name myMaster | head -n 1);
export MASTER_IP_FROM_LABEL=$(kubectl -n ${NAMESPACE} get pod -l app=redis-replication,redis-role=master,redis_setup_type=replication -o jsonpath='{.items[0].status.podIP}');
if [ "$FROM_SENTINEL" = "$MASTER_IP_FROM_LABEL" ] && [ "$FROM_SENTINEL" = "$MASTER_IP_FROM_STATUS" ]; then echo "OK"; else echo "FAIL"; fi
export FROM_SENTINEL=$(kubectl -n ${NAMESPACE} exec redis-sentinel-sentinel-0 -c redis-sentinel-sentinel -- redis-cli --no-auth-warning --tls --cacert /tls/ca.crt -a Opstree@1234sentinel -p 26379 sentinel get-master-addr-by-name myMaster | head -n 1 | cut -d'.' -f1);
export MASTER_POD_FROM_LABEL=$(kubectl -n ${NAMESPACE} get pod -l app=redis-replication,redis-role=master,redis_setup_type=replication -o jsonpath='{.items[0].metadata.name}');
if [ "$FROM_SENTINEL" = "$MASTER_POD_FROM_LABEL" ] && [ "$FROM_SENTINEL" = "$MASTER_POD_FROM_STATUS" ]; then echo "OK"; else echo "FAIL"; fi
check:
(contains($stdout, 'OK')): true
@ -78,6 +106,7 @@ spec:
bash -c "cd /go/src/data-assert && go run main.go chk-redis-data \
--host redis-sentinel-sentinel.${NAMESPACE}.svc.cluster.local:26379 \
--mode sentinel \
--tls \
--password-sentinel Opstree@1234sentinel \
--password Opstree@1234replication"
check:

View File

@ -14,6 +14,12 @@ kind: RedisReplication
metadata:
name: redis-replication
spec:
TLS:
ca: ca.crt
cert: tls.crt
key: tls.key
secret:
secretName: redis-tls-cert
clusterSize: 3
podSecurityContext:
runAsUser: 1000
@ -53,13 +59,22 @@ kind: RedisSentinel
metadata:
name: redis-sentinel
spec:
TLS:
ca: ca.crt
cert: tls.crt
key: tls.key
secret:
secretName: redis-tls-cert
clusterSize: 1
podSecurityContext:
runAsUser: 1000
fsGroup: 1000
redisSentinelConfig:
resolveHostnames: "yes"
announceHostnames: "yes"
redisReplicationName: redis-replication
quorum: '1'
failoverTimeout: '10000'
redisReplicationPassword:
secretKeyRef:
name: redis-replication-password

View File

@ -155,6 +155,16 @@ spec:
imagePullPolicy: Always
command: [/bin/bash, -c, /app/restore.bash]
args: [--restore-from, redis-cluster-restore]
securityContext:
runAsUser: 1000
runAsGroup: 1000
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
resources:
requests:
cpu: 100m

View File

@ -107,6 +107,16 @@ spec:
imagePullPolicy: Always
command: [/bin/bash, -c, /app/restore.bash]
args: [--restore-from, redis-sentinel-restore]
securityContext:
runAsUser: 1000
runAsGroup: 1000
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
resources:
requests:
cpu: 100m

View File

@ -119,6 +119,16 @@ spec:
imagePullPolicy: Always
command: [/bin/bash, -c, /app/restore.bash]
args: [--restore-from, redis-standalone-restore]
securityContext:
runAsUser: 1000
runAsGroup: 1000
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
resources:
requests:
cpu: 100m