diff --git a/Makefile b/Makefile index 48505c188..bc962cac3 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -VERSION := 0.5.2 +VERSION := 0.5.3 # Name of this service/application SERVICE_NAME := redis-operator diff --git a/README.md b/README.md index a5a6dd4b6..8c0a0d15a 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ This redis-failover will be managed by the operator, resulting in the following * `rfs-`: Sentinel service **NOTE**: `NAME` is the named provided when creating the RedisFailover. +**IMPORTANT**: the name of the redis-failover to be created cannot be longer that 48 characters, due to prepend of redis/sentinel identification and statefulset limitation. ### Persistance The operator has the ability of add persistance to Redis data. By default an `emptyDir` will be used, so the data is not saved. diff --git a/api/redisfailover/v1alpha2/validate.go b/api/redisfailover/v1alpha2/validate.go index 3d073aa2b..09154b064 100644 --- a/api/redisfailover/v1alpha2/validate.go +++ b/api/redisfailover/v1alpha2/validate.go @@ -2,10 +2,19 @@ package v1alpha2 import ( "errors" + "fmt" +) + +const ( + maxNameLength = 48 ) // Validate set the values by default if not defined and checks if the values given are valid func (r *RedisFailover) Validate() error { + if len(r.Name) > maxNameLength { + return fmt.Errorf("name length can't be higher than %d", maxNameLength) + } + if r.Spec.Redis.Replicas == 0 { r.Spec.Redis.Replicas = defaultRedisNumber } else if r.Spec.Redis.Replicas < defaultRedisNumber { diff --git a/metrics/dummy.go b/metrics/dummy.go index b9f42e637..49480e5e9 100644 --- a/metrics/dummy.go +++ b/metrics/dummy.go @@ -8,3 +8,4 @@ type dummy struct{} func (d *dummy) SetClusterOK(namespace string, name string) {} func (d *dummy) SetClusterError(namespace string, name string) {} +func (d *dummy) DeleteCluster(namespace string, name string) {} diff --git a/metrics/metrics.go b/metrics/metrics.go index 67e808cf4..78e5549a5 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -15,6 +15,7 @@ const ( type Instrumenter interface { SetClusterOK(namespace string, name string) SetClusterError(namespace string, name string) + DeleteCluster(namespace string, name string) } // PromMetrics implements the instrumenter so the metrics can be managed by Prometheus. @@ -71,3 +72,8 @@ func (p *PromMetrics) SetClusterOK(namespace string, name string) { func (p *PromMetrics) SetClusterError(namespace string, name string) { p.clusterOK.WithLabelValues(namespace, name).Set(0) } + +// DeleteCluster set the cluster status to Error +func (p *PromMetrics) DeleteCluster(namespace string, name string) { + p.clusterOK.DeleteLabelValues(namespace, name) +} diff --git a/metrics/metrics_test.go b/metrics/metrics_test.go index fb34fa4fa..3c08cc6a6 100644 --- a/metrics/metrics_test.go +++ b/metrics/metrics_test.go @@ -87,6 +87,27 @@ func TestPrometheusMetrics(t *testing.T) { }, expCode: http.StatusOK, }, + { + name: "Deleting a cluster should remove it", + addMetrics: func(pm *metrics.PromMetrics) { + pm.SetClusterOK("testns1", "test") + pm.DeleteCluster("testns1", "test") + }, + expMetrics: []string{}, + expCode: http.StatusOK, + }, + { + name: "Deleting a cluster should remove only the desired one", + addMetrics: func(pm *metrics.PromMetrics) { + pm.SetClusterOK("testns1", "test") + pm.SetClusterOK("testns2", "test") + pm.DeleteCluster("testns1", "test") + }, + expMetrics: []string{ + `my_metrics_controller_cluster_ok{name="test",namespace="testns2"} 1`, + }, + expCode: http.StatusOK, + }, } for _, test := range tests { diff --git a/operator/redisfailover/checker.go b/operator/redisfailover/checker.go index e5f806fba..35d0d349d 100644 --- a/operator/redisfailover/checker.go +++ b/operator/redisfailover/checker.go @@ -43,6 +43,7 @@ func (r *RedisFailoverHandler) CheckAndHeal(rf *redisfailoverv1alpha2.RedisFailo r.logger.Debugf("Time %.f more than expected. Not even one master, fixing...", minTime.Round(time.Second).Seconds()) // We can consider there's an error if err2 := r.rfHealer.SetRandomMaster(rf); err2 != nil { + r.mClient.SetClusterError(rf.Namespace, rf.Name) return err2 } } else { @@ -86,6 +87,7 @@ func (r *RedisFailoverHandler) CheckAndHeal(rf *redisfailoverv1alpha2.RedisFailo if err := r.rfChecker.CheckSentinelMonitor(sip, master); err != nil { r.logger.Debug("Sentinel is not monitoring the correct master") if err := r.rfHealer.NewSentinelMonitor(sip, master, rf); err != nil { + r.mClient.SetClusterError(rf.Namespace, rf.Name) return err } } diff --git a/operator/redisfailover/handler.go b/operator/redisfailover/handler.go index 57bb06367..cf0329b30 100644 --- a/operator/redisfailover/handler.go +++ b/operator/redisfailover/handler.go @@ -3,6 +3,7 @@ package redisfailover import ( "context" "fmt" + "strings" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -63,6 +64,7 @@ func (r *RedisFailoverHandler) Add(_ context.Context, obj runtime.Object) error } if err := rf.Validate(); err != nil { + r.mClient.SetClusterError(rf.Namespace, rf.Name) return err } @@ -82,6 +84,10 @@ func (r *RedisFailoverHandler) Add(_ context.Context, obj runtime.Object) error // Delete handles the deletion of a RF. func (r *RedisFailoverHandler) Delete(_ context.Context, name string) error { + n := strings.Split(name, "/") + if len(n) >= 2 { + r.mClient.DeleteCluster(n[0], n[1]) + } // No need to do anything, it will be handled by the owner reference done // on the creation. r.logger.Debugf("ignoring, kubernetes GCs all using the objects OwnerReference metadata") diff --git a/operator/redisfailover/service/constants.go b/operator/redisfailover/service/constants.go index 1ce7f320a..5cb650c9f 100644 --- a/operator/redisfailover/service/constants.go +++ b/operator/redisfailover/service/constants.go @@ -25,7 +25,7 @@ const ( sentinelConfigFileName = "sentinel.conf" redisConfigFileName = "redis.conf" redisName = "r" - redisShutdownName = "r-shutdown" + redisShutdownName = "r-s" redisRoleName = "redis" redisGroupName = "mymaster" appLabel = "redis-failover"