github.com/spotahome/redis-operator@v1.2.4/operator/redisfailover/service/client.go (about)

     1  package service
     2  
     3  import (
     4  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
     5  	"k8s.io/apimachinery/pkg/util/intstr"
     6  
     7  	redisfailoverv1 "github.com/spotahome/redis-operator/api/redisfailover/v1"
     8  	"github.com/spotahome/redis-operator/log"
     9  	"github.com/spotahome/redis-operator/metrics"
    10  	"github.com/spotahome/redis-operator/operator/redisfailover/util"
    11  	"github.com/spotahome/redis-operator/service/k8s"
    12  )
    13  
    14  // RedisFailoverClient has the minimumm methods that a Redis failover controller needs to satisfy
    15  // in order to talk with K8s
    16  type RedisFailoverClient interface {
    17  	EnsureSentinelService(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error
    18  	EnsureSentinelConfigMap(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error
    19  	EnsureSentinelDeployment(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error
    20  	EnsureRedisStatefulset(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error
    21  	EnsureRedisService(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error
    22  	EnsureRedisShutdownConfigMap(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error
    23  	EnsureRedisReadinessConfigMap(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error
    24  	EnsureRedisConfigMap(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error
    25  	EnsureNotPresentRedisService(rFailover *redisfailoverv1.RedisFailover) error
    26  }
    27  
    28  // RedisFailoverKubeClient implements the required methods to talk with kubernetes
    29  type RedisFailoverKubeClient struct {
    30  	K8SService    k8s.Services
    31  	logger        log.Logger
    32  	metricsClient metrics.Recorder
    33  }
    34  
    35  // NewRedisFailoverKubeClient creates a new RedisFailoverKubeClient
    36  func NewRedisFailoverKubeClient(k8sService k8s.Services, logger log.Logger, metricsClient metrics.Recorder) *RedisFailoverKubeClient {
    37  	return &RedisFailoverKubeClient{
    38  		K8SService:    k8sService,
    39  		logger:        logger,
    40  		metricsClient: metricsClient,
    41  	}
    42  }
    43  
    44  func generateSelectorLabels(component, name string) map[string]string {
    45  	return map[string]string{
    46  		"app.kubernetes.io/name":      name,
    47  		"app.kubernetes.io/component": component,
    48  		"app.kubernetes.io/part-of":   appLabel,
    49  	}
    50  }
    51  
    52  func generateRedisDefaultRoleLabel() map[string]string {
    53  	return generateRedisSlaveRoleLabel()
    54  }
    55  
    56  func generateRedisMasterRoleLabel() map[string]string {
    57  	return map[string]string{
    58  		redisRoleLabelKey: redisRoleLabelMaster,
    59  	}
    60  }
    61  
    62  func generateRedisSlaveRoleLabel() map[string]string {
    63  	return map[string]string{
    64  		redisRoleLabelKey: redisRoleLabelSlave,
    65  	}
    66  }
    67  
    68  // EnsureSentinelService makes sure the sentinel service exists
    69  func (r *RedisFailoverKubeClient) EnsureSentinelService(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error {
    70  	svc := generateSentinelService(rf, labels, ownerRefs)
    71  	err := r.K8SService.CreateOrUpdateService(rf.Namespace, svc)
    72  	r.setEnsureOperationMetrics(svc.Namespace, svc.Name, "Service", rf.Name, err)
    73  	return err
    74  }
    75  
    76  // EnsureSentinelConfigMap makes sure the sentinel configmap exists
    77  func (r *RedisFailoverKubeClient) EnsureSentinelConfigMap(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error {
    78  	cm := generateSentinelConfigMap(rf, labels, ownerRefs)
    79  	err := r.K8SService.CreateOrUpdateConfigMap(rf.Namespace, cm)
    80  	r.setEnsureOperationMetrics(cm.Namespace, cm.Name, "ConfigMap", rf.Name, err)
    81  	return err
    82  }
    83  
    84  // EnsureSentinelDeployment makes sure the sentinel deployment exists in the desired state
    85  func (r *RedisFailoverKubeClient) EnsureSentinelDeployment(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error {
    86  	if err := r.ensurePodDisruptionBudget(rf, sentinelName, sentinelRoleName, labels, ownerRefs); err != nil {
    87  		return err
    88  	}
    89  	d := generateSentinelDeployment(rf, labels, ownerRefs)
    90  	err := r.K8SService.CreateOrUpdateDeployment(rf.Namespace, d)
    91  
    92  	r.setEnsureOperationMetrics(d.Namespace, d.Name, "Deployment", rf.Name, err)
    93  	return err
    94  }
    95  
    96  // EnsureRedisStatefulset makes sure the redis statefulset exists in the desired state
    97  func (r *RedisFailoverKubeClient) EnsureRedisStatefulset(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error {
    98  	if err := r.ensurePodDisruptionBudget(rf, redisName, redisRoleName, labels, ownerRefs); err != nil {
    99  		return err
   100  	}
   101  	ss := generateRedisStatefulSet(rf, labels, ownerRefs)
   102  	err := r.K8SService.CreateOrUpdateStatefulSet(rf.Namespace, ss)
   103  
   104  	r.setEnsureOperationMetrics(ss.Namespace, ss.Name, "StatefulSet", rf.Name, err)
   105  	return err
   106  }
   107  
   108  // EnsureRedisConfigMap makes sure the Redis ConfigMap exists
   109  func (r *RedisFailoverKubeClient) EnsureRedisConfigMap(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error {
   110  
   111  	password, err := k8s.GetRedisPassword(r.K8SService, rf)
   112  	if err != nil {
   113  		return err
   114  	}
   115  
   116  	cm := generateRedisConfigMap(rf, labels, ownerRefs, password)
   117  	err = r.K8SService.CreateOrUpdateConfigMap(rf.Namespace, cm)
   118  
   119  	r.setEnsureOperationMetrics(cm.Namespace, cm.Name, "ConfigMap", rf.Name, err)
   120  	return err
   121  }
   122  
   123  // EnsureRedisShutdownConfigMap makes sure the redis configmap with shutdown script exists
   124  func (r *RedisFailoverKubeClient) EnsureRedisShutdownConfigMap(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error {
   125  	if rf.Spec.Redis.ShutdownConfigMap != "" {
   126  		if _, err := r.K8SService.GetConfigMap(rf.Namespace, rf.Spec.Redis.ShutdownConfigMap); err != nil {
   127  			return err
   128  		}
   129  	} else {
   130  		cm := generateRedisShutdownConfigMap(rf, labels, ownerRefs)
   131  		err := r.K8SService.CreateOrUpdateConfigMap(rf.Namespace, cm)
   132  		r.setEnsureOperationMetrics(cm.Namespace, cm.Name, "ConfigMap", rf.Name, err)
   133  		return err
   134  	}
   135  	return nil
   136  }
   137  
   138  // EnsureRedisReadinessConfigMap makes sure the redis configmap with shutdown script exists
   139  func (r *RedisFailoverKubeClient) EnsureRedisReadinessConfigMap(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error {
   140  	cm := generateRedisReadinessConfigMap(rf, labels, ownerRefs)
   141  	err := r.K8SService.CreateOrUpdateConfigMap(rf.Namespace, cm)
   142  	r.setEnsureOperationMetrics(cm.Namespace, cm.Name, "ConfigMap", rf.Name, err)
   143  	return err
   144  }
   145  
   146  // EnsureRedisService makes sure the redis statefulset exists
   147  func (r *RedisFailoverKubeClient) EnsureRedisService(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error {
   148  	svc := generateRedisService(rf, labels, ownerRefs)
   149  	err := r.K8SService.CreateOrUpdateService(rf.Namespace, svc)
   150  
   151  	r.setEnsureOperationMetrics(svc.Namespace, svc.Name, "Service", rf.Name, err)
   152  	return err
   153  }
   154  
   155  // EnsureNotPresentRedisService makes sure the redis service is not present
   156  func (r *RedisFailoverKubeClient) EnsureNotPresentRedisService(rf *redisfailoverv1.RedisFailover) error {
   157  	name := GetRedisName(rf)
   158  	namespace := rf.Namespace
   159  	// If the service exists (no get error), delete it
   160  	if _, err := r.K8SService.GetService(namespace, name); err == nil {
   161  		return r.K8SService.DeleteService(namespace, name)
   162  	}
   163  	return nil
   164  }
   165  
   166  // EnsureRedisStatefulset makes sure the pdb exists in the desired state
   167  func (r *RedisFailoverKubeClient) ensurePodDisruptionBudget(rf *redisfailoverv1.RedisFailover, name string, component string, labels map[string]string, ownerRefs []metav1.OwnerReference) error {
   168  	name = generateName(name, rf.Name)
   169  	namespace := rf.Namespace
   170  
   171  	minAvailable := intstr.FromInt(2)
   172  	if rf.Spec.Redis.Replicas <= 2 {
   173  		minAvailable = intstr.FromInt(1)
   174  	}
   175  
   176  	labels = util.MergeLabels(labels, generateSelectorLabels(component, rf.Name))
   177  
   178  	pdb := generatePodDisruptionBudget(name, namespace, labels, ownerRefs, minAvailable)
   179  	err := r.K8SService.CreateOrUpdatePodDisruptionBudget(namespace, pdb)
   180  	r.setEnsureOperationMetrics(pdb.Namespace, pdb.Name, "PodDisruptionBudget" /* pdb.TypeMeta.Kind isnt working;  pdb.Kind isnt working either */, rf.Name, err)
   181  	return err
   182  }
   183  
   184  func (r *RedisFailoverKubeClient) setEnsureOperationMetrics(objectNamespace string, objectName string, objectKind string, ownerName string, err error) {
   185  	if nil != err {
   186  		r.metricsClient.RecordEnsureOperation(objectNamespace, objectName, objectKind, ownerName, metrics.FAIL)
   187  	}
   188  	r.metricsClient.RecordEnsureOperation(objectNamespace, objectName, objectKind, ownerName, metrics.SUCCESS)
   189  }