github.com/spotahome/redis-operator@v1.2.4/operator/redisfailover/handler.go (about) 1 package redisfailover 2 3 import ( 4 "context" 5 "fmt" 6 "regexp" 7 8 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 "k8s.io/apimachinery/pkg/runtime" 10 11 redisfailoverv1 "github.com/spotahome/redis-operator/api/redisfailover/v1" 12 "github.com/spotahome/redis-operator/log" 13 "github.com/spotahome/redis-operator/metrics" 14 rfservice "github.com/spotahome/redis-operator/operator/redisfailover/service" 15 "github.com/spotahome/redis-operator/operator/redisfailover/util" 16 "github.com/spotahome/redis-operator/service/k8s" 17 ) 18 19 const ( 20 rfLabelManagedByKey = "app.kubernetes.io/managed-by" 21 rfLabelNameKey = "redisfailovers.databases.spotahome.com/name" 22 ) 23 24 var ( 25 defaultLabels = map[string]string{ 26 rfLabelManagedByKey: operatorName, 27 } 28 ) 29 30 // RedisFailoverHandler is the Redis Failover handler. This handler will create the required 31 // resources that a RF needs. 32 type RedisFailoverHandler struct { 33 config Config 34 k8sservice k8s.Service 35 rfService rfservice.RedisFailoverClient 36 rfChecker rfservice.RedisFailoverCheck 37 rfHealer rfservice.RedisFailoverHeal 38 mClient metrics.Recorder 39 logger log.Logger 40 } 41 42 // NewRedisFailoverHandler returns a new RF handler 43 func NewRedisFailoverHandler(config Config, rfService rfservice.RedisFailoverClient, rfChecker rfservice.RedisFailoverCheck, rfHealer rfservice.RedisFailoverHeal, k8sservice k8s.Service, mClient metrics.Recorder, logger log.Logger) *RedisFailoverHandler { 44 return &RedisFailoverHandler{ 45 config: config, 46 rfService: rfService, 47 rfChecker: rfChecker, 48 rfHealer: rfHealer, 49 mClient: mClient, 50 k8sservice: k8sservice, 51 logger: logger, 52 } 53 } 54 55 // Handle will ensure the redis failover is in the expected state. 56 func (r *RedisFailoverHandler) Handle(_ context.Context, obj runtime.Object) error { 57 rf, ok := obj.(*redisfailoverv1.RedisFailover) 58 if !ok { 59 return fmt.Errorf("can't handle the received object: not a redisfailover") 60 } 61 62 if err := rf.Validate(); err != nil { 63 r.mClient.SetClusterError(rf.Namespace, rf.Name) 64 return err 65 } 66 67 // Create owner refs so the objects manager by this handler have ownership to the 68 // received RF. 69 oRefs := r.createOwnerReferences(rf) 70 71 // Create the labels every object derived from this need to have. 72 labels := r.getLabels(rf) 73 74 if err := r.Ensure(rf, labels, oRefs, r.mClient); err != nil { 75 r.mClient.SetClusterError(rf.Namespace, rf.Name) 76 return err 77 } 78 79 if err := r.CheckAndHeal(rf); err != nil { 80 r.mClient.SetClusterError(rf.Namespace, rf.Name) 81 return err 82 } 83 84 r.mClient.SetClusterOK(rf.Namespace, rf.Name) 85 return nil 86 } 87 88 // getLabels merges the labels (dynamic and operator static ones). 89 func (r *RedisFailoverHandler) getLabels(rf *redisfailoverv1.RedisFailover) map[string]string { 90 dynLabels := map[string]string{ 91 rfLabelNameKey: rf.Name, 92 } 93 94 // Filter the labels based on the whitelist 95 filteredCustomLabels := make(map[string]string) 96 if rf.Spec.LabelWhitelist != nil && len(rf.Spec.LabelWhitelist) != 0 { 97 for _, regex := range rf.Spec.LabelWhitelist { 98 compiledRegexp, err := regexp.Compile(regex) 99 if err != nil { 100 r.logger.Errorf("Unable to compile label whitelist regex '%s', ignoring it.", regex) 101 continue 102 } 103 for labelKey, labelValue := range rf.Labels { 104 if match := compiledRegexp.MatchString(labelKey); match { 105 filteredCustomLabels[labelKey] = labelValue 106 } 107 } 108 } 109 } else { 110 // If no whitelist is specified then don't filter the labels. 111 filteredCustomLabels = rf.Labels 112 } 113 return util.MergeLabels(defaultLabels, dynLabels, filteredCustomLabels) 114 } 115 116 func (w *RedisFailoverHandler) createOwnerReferences(rf *redisfailoverv1.RedisFailover) []metav1.OwnerReference { 117 rfvk := redisfailoverv1.VersionKind(redisfailoverv1.RFKind) 118 return []metav1.OwnerReference{ 119 *metav1.NewControllerRef(rf, rfvk), 120 } 121 }