Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add redis-role label to redis replication #925

Merged
merged 10 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions controllers/redisreplication_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
redisv1beta2 "github.com/OT-CONTAINER-KIT/redis-operator/api/v1beta2"
"github.com/OT-CONTAINER-KIT/redis-operator/k8sutils"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/dynamic"
Expand All @@ -19,6 +20,7 @@
// RedisReplicationReconciler reconciles a RedisReplication object
type RedisReplicationReconciler struct {
client.Client
k8sutils.Pod
K8sClient kubernetes.Interface
Dk8sClient dynamic.Interface
Log logr.Logger
Expand Down Expand Up @@ -86,13 +88,15 @@
if len(slaveNodes) == 0 {
realMaster = masterNodes[0]
}
err := k8sutils.CreateMasterSlaveReplication(ctx, r.K8sClient, r.Log, instance, masterNodes, realMaster)
if err != nil {
if err = k8sutils.CreateMasterSlaveReplication(ctx, r.K8sClient, r.Log, instance, masterNodes, realMaster); err != nil {

Check warning on line 91 in controllers/redisreplication_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/redisreplication_controller.go#L91

Added line #L91 was not covered by tests
return ctrl.Result{RequeueAfter: time.Second * 60}, err
}
}
realMaster = k8sutils.GetRedisReplicationRealMaster(ctx, r.K8sClient, r.Log, instance, masterNodes)
if err := r.UpdateRedisReplicationMaster(ctx, instance, realMaster); err != nil {
if err = r.UpdateRedisReplicationMaster(ctx, instance, realMaster); err != nil {
return ctrl.Result{}, err

Check warning on line 97 in controllers/redisreplication_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/redisreplication_controller.go#L96-L97

Added lines #L96 - L97 were not covered by tests
}
if err = r.UpdateRedisPodRoleLabel(ctx, instance, realMaster); err != nil {

Check warning on line 99 in controllers/redisreplication_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/redisreplication_controller.go#L99

Added line #L99 was not covered by tests
return ctrl.Result{}, err
}
reqLogger.Info("Will reconcile redis operator in again 10 seconds")
Expand All @@ -110,6 +114,31 @@
return nil
}

func (r *RedisReplicationReconciler) UpdateRedisPodRoleLabel(ctx context.Context, cr *redisv1beta2.RedisReplication, masterNode string) error {
labels := k8sutils.GetRedisReplicationLabels(cr)
pods, err := r.ListPods(ctx, cr.GetNamespace(), labels)
if err != nil {
return err

Check warning on line 121 in controllers/redisreplication_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/redisreplication_controller.go#L117-L121

Added lines #L117 - L121 were not covered by tests
}
updateRoleLabelFunc := func(ctx context.Context, namespace string, pod corev1.Pod, role string) error {
if pod.Labels[k8sutils.RedisRoleLabelKey] != role {
return r.PatchPodLabels(ctx, namespace, pod.GetName(), map[string]string{k8sutils.RedisRoleLabelKey: role})

Check warning on line 125 in controllers/redisreplication_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/redisreplication_controller.go#L123-L125

Added lines #L123 - L125 were not covered by tests
}
return nil

Check warning on line 127 in controllers/redisreplication_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/redisreplication_controller.go#L127

Added line #L127 was not covered by tests
}
for _, pod := range pods.Items {
if masterNode == pod.GetName() {
err = updateRoleLabelFunc(ctx, cr.GetNamespace(), pod, k8sutils.RedisRoleLabelMaster)
} else {
err = updateRoleLabelFunc(ctx, cr.GetNamespace(), pod, k8sutils.RedisRoleLabelSlave)

Check warning on line 133 in controllers/redisreplication_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/redisreplication_controller.go#L129-L133

Added lines #L129 - L133 were not covered by tests
}
if err != nil {
return err

Check warning on line 136 in controllers/redisreplication_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/redisreplication_controller.go#L135-L136

Added lines #L135 - L136 were not covered by tests
}
}
return nil

Check warning on line 139 in controllers/redisreplication_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/redisreplication_controller.go#L139

Added line #L139 was not covered by tests
}

// SetupWithManager sets up the controller with the Manager.
func (r *RedisReplicationReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
Expand Down
6 changes: 6 additions & 0 deletions k8sutils/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,9 @@ const (
const (
EnvOperatorSTSPVCTemplateName = "OPERATOR_STS_PVC_TEMPLATE_NAME"
)

const (
RedisRoleLabelKey = "redis-role"
RedisRoleLabelMaster = "master"
RedisRoleLabelSlave = "slave"
)
4 changes: 4 additions & 0 deletions k8sutils/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,7 @@
}
return lbls
}

func GetRedisReplicationLabels(cr *redisv1beta2.RedisReplication) map[string]string {
return getRedisLabels(cr.GetName(), replication, "replication", cr.GetLabels())

Check warning on line 154 in k8sutils/labels.go

View check run for this annotation

Codecov / codecov/patch

k8sutils/labels.go#L153-L154

Added lines #L153 - L154 were not covered by tests
}
69 changes: 69 additions & 0 deletions k8sutils/pod.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package k8sutils

import (
"context"
"encoding/json"
"fmt"
"strings"

"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes"
)

type Pod interface {
ListPods(ctx context.Context, namespace string, labels map[string]string) (*corev1.PodList, error)
PatchPodLabels(ctx context.Context, namespace, name string, labels map[string]string) error
}

type PodService struct {
kubeClient kubernetes.Interface
log logr.Logger
}

func NewPodService(kubeClient kubernetes.Interface, log logr.Logger) *PodService {
log = log.WithValues("service", "k8s.pod")
return &PodService{
kubeClient: kubeClient,
log: log,

Check warning on line 30 in k8sutils/pod.go

View check run for this annotation

Codecov / codecov/patch

k8sutils/pod.go#L26-L30

Added lines #L26 - L30 were not covered by tests
}
}

func (s *PodService) ListPods(ctx context.Context, namespace string, labels map[string]string) (*corev1.PodList, error) {
selector := make([]string, 0, len(labels))
for key, value := range labels {
selector = append(selector, fmt.Sprintf("%s=%s", key, value))

Check warning on line 37 in k8sutils/pod.go

View check run for this annotation

Codecov / codecov/patch

k8sutils/pod.go#L34-L37

Added lines #L34 - L37 were not covered by tests
}
return s.kubeClient.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{
LabelSelector: strings.Join(selector, ","),
})

Check warning on line 41 in k8sutils/pod.go

View check run for this annotation

Codecov / codecov/patch

k8sutils/pod.go#L39-L41

Added lines #L39 - L41 were not covered by tests
}

type patchStringValue struct {
Op string `json:"op"`
Path string `json:"path"`
Value interface{} `json:"value"`
}

func (s *PodService) PatchPodLabels(ctx context.Context, namespace, podName string, labels map[string]string) error {
s.log.Info("Patch pod labels", "namespace", namespace, "podName", podName, "labels", labels)

Check warning on line 51 in k8sutils/pod.go

View check run for this annotation

Codecov / codecov/patch

k8sutils/pod.go#L50-L51

Added lines #L50 - L51 were not covered by tests

var payloads []interface{}
for labelKey, labelValue := range labels {
payload := patchStringValue{
Op: "replace",
Path: "/metadata/labels/" + labelKey,
Value: labelValue,

Check warning on line 58 in k8sutils/pod.go

View check run for this annotation

Codecov / codecov/patch

k8sutils/pod.go#L53-L58

Added lines #L53 - L58 were not covered by tests
}
payloads = append(payloads, payload)

Check warning on line 60 in k8sutils/pod.go

View check run for this annotation

Codecov / codecov/patch

k8sutils/pod.go#L60

Added line #L60 was not covered by tests
}
payloadBytes, _ := json.Marshal(payloads)

Check warning on line 62 in k8sutils/pod.go

View check run for this annotation

Codecov / codecov/patch

k8sutils/pod.go#L62

Added line #L62 was not covered by tests

_, err := s.kubeClient.CoreV1().Pods(namespace).Patch(ctx, podName, types.JSONPatchType, payloadBytes, metav1.PatchOptions{})
if err != nil {
s.log.Error(err, "Patch pod labels failed", "namespace", namespace, "podName", podName)

Check warning on line 66 in k8sutils/pod.go

View check run for this annotation

Codecov / codecov/patch

k8sutils/pod.go#L64-L66

Added lines #L64 - L66 were not covered by tests
}
return err

Check warning on line 68 in k8sutils/pod.go

View check run for this annotation

Codecov / codecov/patch

k8sutils/pod.go#L68

Added line #L68 was not covered by tests
}
4 changes: 3 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,14 @@ func main() {
setupLog.Error(err, "unable to create controller", "controller", "RedisCluster")
os.Exit(1)
}
rrLog := ctrl.Log.WithName("controllers").WithName("RedisReplication")
if err = (&controllers.RedisReplicationReconciler{
Client: mgr.GetClient(),
K8sClient: k8sclient,
Dk8sClient: dk8sClient,
Log: ctrl.Log.WithName("controllers").WithName("RedisReplication"),
Log: rrLog,
Scheme: mgr.GetScheme(),
Pod: k8sutils.NewPodService(k8sclient, rrLog),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "RedisReplication")
os.Exit(1)
Expand Down
16 changes: 12 additions & 4 deletions tests/e2e-chainsaw/v1beta2/ha-failover/chainsaw-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,22 @@ spec:
selector: control-plane=redis-operator
container: manager
tail: -1 # tail all logs

- name: Sleep for 3 minutes
try:
- sleep:
duration: 3m

- name: Test sentinel monitoring
try:
- script:
timeout: 10s
content: |
kubectl exec --namespace ${NAMESPACE} redis -- redis-cli -h redis-sentinel-sentinel.${NAMESPACE}.svc -p 26379 sentinel master myMaster | grep -A 1 'flags' | tail -n 1
export MASTER_IP_FROM_SENTINEL=$(kubectl exec --namespace ${NAMESPACE} redis-sentinel-sentinel-0 -- redis-cli -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 [ "$MASTER_IP_FROM_SENTINEL" = "$MASTER_IP_FROM_LABEL" ]; then echo "OK"; else echo "FAIL"; fi
check:
($stdout=='master'): true
($stdout=='OK'): true
catch:
- description: Redis Operator Logs
podLogs:
Expand All @@ -53,18 +57,22 @@ spec:
selector: control-plane=redis-operator
container: manager
tail: -1 # tail all logs

- name: Sleep for 3 minutes
try:
- sleep:
duration: 3m

- name: Test sentinel monitoring
try:
- script:
timeout: 10s
content: |
kubectl exec --namespace ${NAMESPACE} redis -- redis-cli -h redis-sentinel-sentinel.${NAMESPACE}.svc -p 26379 sentinel master myMaster | grep -A 1 'flags' | tail -n 1
export MASTER_IP_FROM_SENTINEL=$(kubectl exec --namespace ${NAMESPACE} redis-sentinel-sentinel-0 -- redis-cli -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 [ $MASTER_IP_FROM_SENTINEL = $MASTER_IP_FROM_LABEL ]; then echo "OK"; else echo "FAIL"; fi
check:
($stdout=='master'): true
($stdout=='OK'): true
catch:
- description: Redis Operator Logs
podLogs:
Expand Down
Loading