From 2e211f71ebc690b26dc34778e27a980db679c671 Mon Sep 17 00:00:00 2001 From: Artur Zheludkov Date: Thu, 18 Jan 2024 14:25:35 -0500 Subject: [PATCH] Generate New HAProxy Service For Redis Replication (#40) This Pull Request presents a different method compared to [Add Redis Slave Endpoint to HAProxy Service configuration](https://github.com/powerhome/redis-operator/pull/35). Here, the redis-operator will establish a completely new HAProxy service, designed to route traffic specifically to Redis instances functioning as slaves. --- CHANGELOG.md | 3 + .../service/RedisFailoverClient.go | 54 ++++- operator/redisfailover/ensurer.go | 19 +- operator/redisfailover/ensurer_test.go | 25 +++ operator/redisfailover/service/check.go | 2 +- operator/redisfailover/service/client.go | 59 +++-- operator/redisfailover/service/constants.go | 32 +-- operator/redisfailover/service/generator.go | 211 ++++++++++++++++-- .../redisfailover/service/generator_test.go | 14 +- operator/redisfailover/service/names.go | 8 + 10 files changed, 365 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e7a2214d..38f4308d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ Also check this project's [releases](https://github.com/powerhome/redis-operator ## Unreleased +### Added +- [Set up a new HAProxy service for routing traffic specifically to Redis nodes set as slaves][https://github.com/powerhome/redis-operator/pull/40] + ## [v1.8.0] - 2024-01-16 ### Fixed diff --git a/mocks/operator/redisfailover/service/RedisFailoverClient.go b/mocks/operator/redisfailover/service/RedisFailoverClient.go index d4f060126..794f43cbb 100644 --- a/mocks/operator/redisfailover/service/RedisFailoverClient.go +++ b/mocks/operator/redisfailover/service/RedisFailoverClient.go @@ -28,8 +28,8 @@ func (_m *RedisFailoverClient) DestroySentinelResources(rFailover *v1.RedisFailo return r0 } -// EnsureHAProxyConfigmap provides a mock function with given fields: rFailover, labels, ownerRefs -func (_m *RedisFailoverClient) EnsureHAProxyConfigmap(rFailover *v1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { +// EnsureHAProxyRedisMasterConfigmap provides a mock function with given fields: rFailover, labels, ownerRefs +func (_m *RedisFailoverClient) EnsureHAProxyRedisMasterConfigmap(rFailover *v1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { ret := _m.Called(rFailover, labels, ownerRefs) var r0 error @@ -42,8 +42,8 @@ func (_m *RedisFailoverClient) EnsureHAProxyConfigmap(rFailover *v1.RedisFailove return r0 } -// EnsureHAProxyDeployment provides a mock function with given fields: rFailover, labels, ownerRefs -func (_m *RedisFailoverClient) EnsureHAProxyDeployment(rFailover *v1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { +// EnsureHAProxyRedisMasterDeployment provides a mock function with given fields: rFailover, labels, ownerRefs +func (_m *RedisFailoverClient) EnsureHAProxyRedisMasterDeployment(rFailover *v1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { ret := _m.Called(rFailover, labels, ownerRefs) var r0 error @@ -56,8 +56,50 @@ func (_m *RedisFailoverClient) EnsureHAProxyDeployment(rFailover *v1.RedisFailov return r0 } -// EnsureHAProxyService provides a mock function with given fields: rFailover, labels, ownerRefs -func (_m *RedisFailoverClient) EnsureHAProxyService(rFailover *v1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { +// EnsureHAProxyRedisMasterService provides a mock function with given fields: rFailover, labels, ownerRefs +func (_m *RedisFailoverClient) EnsureHAProxyRedisMasterService(rFailover *v1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { + ret := _m.Called(rFailover, labels, ownerRefs) + + var r0 error + if rf, ok := ret.Get(0).(func(*v1.RedisFailover, map[string]string, []metav1.OwnerReference) error); ok { + r0 = rf(rFailover, labels, ownerRefs) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// EnsureHAProxyRedisSlaveConfigmap provides a mock function with given fields: rFailover, labels, ownerRefs +func (_m *RedisFailoverClient) EnsureHAProxyRedisSlaveConfigmap(rFailover *v1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { + ret := _m.Called(rFailover, labels, ownerRefs) + + var r0 error + if rf, ok := ret.Get(0).(func(*v1.RedisFailover, map[string]string, []metav1.OwnerReference) error); ok { + r0 = rf(rFailover, labels, ownerRefs) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// EnsureHAProxyRedisSlaveDeployment provides a mock function with given fields: rFailover, labels, ownerRefs +func (_m *RedisFailoverClient) EnsureHAProxyRedisSlaveDeployment(rFailover *v1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { + ret := _m.Called(rFailover, labels, ownerRefs) + + var r0 error + if rf, ok := ret.Get(0).(func(*v1.RedisFailover, map[string]string, []metav1.OwnerReference) error); ok { + r0 = rf(rFailover, labels, ownerRefs) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// EnsureHAProxyRedisSlaveService provides a mock function with given fields: rFailover, labels, ownerRefs +func (_m *RedisFailoverClient) EnsureHAProxyRedisSlaveService(rFailover *v1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { ret := _m.Called(rFailover, labels, ownerRefs) var r0 error diff --git a/operator/redisfailover/ensurer.go b/operator/redisfailover/ensurer.go index a16087e66..392309b76 100644 --- a/operator/redisfailover/ensurer.go +++ b/operator/redisfailover/ensurer.go @@ -29,7 +29,7 @@ func (w *RedisFailoverHandler) Ensure(rf *redisfailoverv1.RedisFailover, labels } if rf.Spec.Haproxy != nil { - if err := w.rfService.EnsureHAProxyService(rf, labels, or); err != nil { + if err := w.rfService.EnsureHAProxyRedisMasterService(rf, labels, or); err != nil { return err } @@ -37,13 +37,26 @@ func (w *RedisFailoverHandler) Ensure(rf *redisfailoverv1.RedisFailover, labels return err } - if err := w.rfService.EnsureHAProxyConfigmap(rf, labels, or); err != nil { + if err := w.rfService.EnsureHAProxyRedisMasterConfigmap(rf, labels, or); err != nil { return err } - if err := w.rfService.EnsureHAProxyDeployment(rf, labels, or); err != nil { + if err := w.rfService.EnsureHAProxyRedisMasterDeployment(rf, labels, or); err != nil { return err } + + if err := w.rfService.EnsureHAProxyRedisSlaveService(rf, labels, or); err != nil { + return err + } + + if err := w.rfService.EnsureHAProxyRedisSlaveConfigmap(rf, labels, or); err != nil { + return err + } + + if err := w.rfService.EnsureHAProxyRedisSlaveDeployment(rf, labels, or); err != nil { + return err + } + } if err := w.rfService.EnsureRedisMasterService(rf, labels, or); err != nil { diff --git a/operator/redisfailover/ensurer_test.go b/operator/redisfailover/ensurer_test.go index 4395f17f0..a62e0c751 100644 --- a/operator/redisfailover/ensurer_test.go +++ b/operator/redisfailover/ensurer_test.go @@ -64,6 +64,7 @@ func TestEnsure(t *testing.T) { exporter bool bootstrapping bool bootstrappingAllowSentinels bool + haproxy bool }{ { name: "Call everything, use exporter", @@ -89,6 +90,13 @@ func TestEnsure(t *testing.T) { bootstrapping: true, bootstrappingAllowSentinels: true, }, + { + name: "with haproxy enabled", + exporter: false, + bootstrapping: false, + bootstrappingAllowSentinels: false, + haproxy: true, + }, } for _, test := range tests { @@ -98,6 +106,12 @@ func TestEnsure(t *testing.T) { rf := generateRF(test.exporter, test.bootstrapping) if test.bootstrapping { rf.Spec.BootstrapNode.AllowSentinels = test.bootstrappingAllowSentinels + } else { + rf.Spec.BootstrapNode = nil + } + + if test.haproxy { + rf.Spec.Haproxy = &redisfailoverv1.HaproxySettings{} } config := generateConfig() @@ -119,6 +133,17 @@ func TestEnsure(t *testing.T) { mrfs.On("DestroySentinelResources", rf, mock.Anything, mock.Anything).Once().Return(nil) } + if test.haproxy { + mrfs.On("EnsureHAProxyRedisMasterService", rf, mock.Anything, mock.Anything).Once().Return(nil) + mrfs.On("EnsureRedisHeadlessService", rf, mock.Anything, mock.Anything).Once().Return(nil) + mrfs.On("EnsureHAProxyRedisMasterConfigmap", rf, mock.Anything, mock.Anything).Once().Return(nil) + mrfs.On("EnsureHAProxyRedisMasterDeployment", rf, mock.Anything, mock.Anything).Once().Return(nil) + + mrfs.On("EnsureHAProxyRedisSlaveService", rf, mock.Anything, mock.Anything).Once().Return(nil) + mrfs.On("EnsureHAProxyRedisSlaveConfigmap", rf, mock.Anything, mock.Anything).Once().Return(nil) + mrfs.On("EnsureHAProxyRedisSlaveDeployment", rf, mock.Anything, mock.Anything).Once().Return(nil) + } + mrfs.On("EnsureRedisMasterService", rf, mock.Anything, mock.Anything).Once().Return(nil) mrfs.On("EnsureRedisSlaveService", rf, mock.Anything, mock.Anything).Once().Return(nil) mrfs.On("EnsureRedisConfigMap", rf, mock.Anything, mock.Anything).Once().Return(nil) diff --git a/operator/redisfailover/service/check.go b/operator/redisfailover/service/check.go index eb511d1b4..0048e5e3b 100644 --- a/operator/redisfailover/service/check.go +++ b/operator/redisfailover/service/check.go @@ -498,7 +498,7 @@ func (r *RedisFailoverChecker) IsHAProxyRunning(rFailover *redisfailoverv1.Redis if rFailover.Spec.Haproxy == nil { return true } - haproxyName := rFailover.GenerateName(redisHAProxyName) + haproxyName := GetHaproxyMasterName(rFailover) dp, err := r.k8sService.GetDeploymentPods(rFailover.Namespace, haproxyName) return err == nil && len(dp.Items) > int(rFailover.Spec.Haproxy.Replicas-1) && AreAllRunning(dp, int(rFailover.Spec.Haproxy.Replicas)) } diff --git a/operator/redisfailover/service/client.go b/operator/redisfailover/service/client.go index 1700d8c16..41ab26ec1 100644 --- a/operator/redisfailover/service/client.go +++ b/operator/redisfailover/service/client.go @@ -15,9 +15,9 @@ import ( // RedisFailoverClient has the minimumm methods that a Redis failover controller needs to satisfy // in order to talk with K8s type RedisFailoverClient interface { - EnsureHAProxyDeployment(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error - EnsureHAProxyConfigmap(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error - EnsureHAProxyService(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error + EnsureHAProxyRedisMasterDeployment(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error + EnsureHAProxyRedisMasterConfigmap(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error + EnsureHAProxyRedisMasterService(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error EnsureRedisHeadlessService(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error EnsureRedisNetworkPolicy(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error EnsureSentinelNetworkPolicy(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error @@ -33,6 +33,10 @@ type RedisFailoverClient interface { EnsureRedisConfigMap(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error EnsureNotPresentRedisService(rFailover *redisfailoverv1.RedisFailover) error + EnsureHAProxyRedisSlaveService(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error + EnsureHAProxyRedisSlaveConfigmap(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error + EnsureHAProxyRedisSlaveDeployment(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error + DestroySentinelResources(rFailover *redisfailoverv1.RedisFailover) error UpdateStatus(rFailover *redisfailoverv1.RedisFailover) (*redisfailoverv1.RedisFailover, error) } @@ -99,11 +103,11 @@ func (r *RedisFailoverKubeClient) EnsureSentinelNetworkPolicy(rf *redisfailoverv return err } -// EnsureHAProxyService makes sure the HAProxy service exists -func (r *RedisFailoverKubeClient) EnsureHAProxyService(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { - svc := generateHAProxyService(rf, labels, ownerRefs) +// EnsureHAProxyRedisMasterService makes sure the HAProxy service exists +func (r *RedisFailoverKubeClient) EnsureHAProxyRedisMasterService(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { + svc := generateHAProxyRedisMasterService(rf, labels, ownerRefs) err := r.K8SService.CreateOrUpdateService(rf.Namespace, svc) - r.setEnsureOperationMetrics(svc.Namespace, svc.Name, "EnsureHAProxyService", rf.Name, err) + r.setEnsureOperationMetrics(svc.Namespace, svc.Name, "EnsureHAProxyRedisMasterService", rf.Name, err) return err } @@ -115,20 +119,45 @@ func (r *RedisFailoverKubeClient) EnsureRedisHeadlessService(rf *redisfailoverv1 return err } -// EnsureHAProxyConfigmap makes sure the HAProxy configmap exists -func (r *RedisFailoverKubeClient) EnsureHAProxyConfigmap(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { - svc := generateHAProxyConfigmap(rf, labels, ownerRefs) +// EnsureHAProxyRedisMasterConfigmap makes sure the HAProxy configmap exists +func (r *RedisFailoverKubeClient) EnsureHAProxyRedisMasterConfigmap(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { + svc := generateHAProxyRedisMasterConfigmap(rf, labels, ownerRefs) + err := r.K8SService.CreateOrUpdateConfigMap(rf.Namespace, svc) + r.setEnsureOperationMetrics(svc.Namespace, svc.Name, "EnsureHAProxyRedisMasterConfigmap", rf.Name, err) + return err +} + +// EnsureHAProxyRedisMasterDeployment makes sure the sentinel deployment exists in the desired state +func (r *RedisFailoverKubeClient) EnsureHAProxyRedisMasterDeployment(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { + d := generateHAProxyRedisMasterDeployment(rf, labels, ownerRefs) + err := r.K8SService.CreateOrUpdateDeployment(rf.Namespace, d) + + r.setEnsureOperationMetrics(d.Namespace, d.Name, "EnsureHAProxyRedisMasterDeployment", rf.Name, err) + return err +} + +// EnsureHAProxyRedisSlaveService makes sure the HAProxy service exists +func (r *RedisFailoverKubeClient) EnsureHAProxyRedisSlaveService(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { + svc := generateHAProxyRedisSlaveService(rf, labels, ownerRefs) + err := r.K8SService.CreateOrUpdateService(rf.Namespace, svc) + r.setEnsureOperationMetrics(svc.Namespace, svc.Name, "EnsureHAProxyRedisMasterService", rf.Name, err) + return err +} + +// EnsureHAProxyRedisSlaveConfigmap makes sure the HAProxy configmap exists +func (r *RedisFailoverKubeClient) EnsureHAProxyRedisSlaveConfigmap(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { + svc := generateHAProxyRedisSlaveConfigmap(rf, labels, ownerRefs) err := r.K8SService.CreateOrUpdateConfigMap(rf.Namespace, svc) - r.setEnsureOperationMetrics(svc.Namespace, svc.Name, "EnsureHAProxyConfigmap", rf.Name, err) + r.setEnsureOperationMetrics(svc.Namespace, svc.Name, "EnsureHAProxyRedisMasterConfigmap", rf.Name, err) return err } -// EnsureHAProxyDeployment makes sure the sentinel deployment exists in the desired state -func (r *RedisFailoverKubeClient) EnsureHAProxyDeployment(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { - d := generateHAProxyDeployment(rf, labels, ownerRefs) +// EnsureHAProxyRedisSlaveDeployment makes sure the deployment exists in the desired state +func (r *RedisFailoverKubeClient) EnsureHAProxyRedisSlaveDeployment(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { + d := generateHAProxyRedisSlaveDeployment(rf, labels, ownerRefs) err := r.K8SService.CreateOrUpdateDeployment(rf.Namespace, d) - r.setEnsureOperationMetrics(d.Namespace, d.Name, "EnsureHAProxyDeployment", rf.Name, err) + r.setEnsureOperationMetrics(d.Namespace, d.Name, "EnsureHAProxyRedisMasterDeployment", rf.Name, err) return err } diff --git a/operator/redisfailover/service/constants.go b/operator/redisfailover/service/constants.go index ef320fd0f..b0c04ae90 100644 --- a/operator/redisfailover/service/constants.go +++ b/operator/redisfailover/service/constants.go @@ -14,21 +14,23 @@ const ( ) const ( - baseName = "rf" - sentinelName = "s" - sentinelRoleName = "sentinel" - sentinelConfigFileName = "sentinel.conf" - sentinelNetworkPolicyName = "s-np" - redisConfigFileName = "redis.conf" - redisName = "r" - redisNetworkPolicyName = "r-np" - redisMasterName = "rm" - redisSlaveName = "rs" - redisShutdownName = "r-s" - redisReadinessName = "r-readiness" - redisRoleName = "redis" - appLabel = "redis-failover" - hostnameTopologyKey = "kubernetes.io/hostname" + baseName = "rf" + sentinelName = "s" + sentinelRoleName = "sentinel" + sentinelConfigFileName = "sentinel.conf" + sentinelNetworkPolicyName = "s-np" + redisConfigFileName = "redis.conf" + redisName = "r" + redisNetworkPolicyName = "r-np" + redisMasterName = "rm" + redisSlaveName = "rs" + redisShutdownName = "r-s" + redisReadinessName = "r-readiness" + redisRoleName = "redis" + appLabel = "redis-failover" + hostnameTopologyKey = "kubernetes.io/hostname" + redisHAProxySlaveRedisName = "rs-haproxy" + redisHAProxyMasterRedisName = "rm-haproxy" ) const ( diff --git a/operator/redisfailover/service/generator.go b/operator/redisfailover/service/generator.go index d4bb94faa..d0162a817 100644 --- a/operator/redisfailover/service/generator.go +++ b/operator/redisfailover/service/generator.go @@ -50,18 +50,13 @@ sentinel parallel-syncs mymaster 2` graceTime = 30 ) -const redisHAProxyName = "redis-haproxy" +func generateHAProxyRedisMasterDeployment(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) *appsv1.Deployment { -func generateHAProxyDeployment(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) *appsv1.Deployment { - - name := rf.GenerateName(redisHAProxyName) + name := GetHaproxyMasterName(rf) namespace := rf.Namespace - labels = util.MergeLabels(labels, map[string]string{ - "app.kubernetes.io/component": "redis", - }) - + labels = util.MergeLabels(labels, generateSelectorLabels("haproxy", rf.Name), generateRedisMasterRoleLabel()) selectorLabels := util.MergeLabels(labels, generateComponentLabel("haproxy")) volumeMounts := []corev1.VolumeMount{ @@ -130,15 +125,13 @@ func generateHAProxyDeployment(rf *redisfailoverv1.RedisFailover, labels map[str return sd } -func generateHAProxyConfigmap(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) *corev1.ConfigMap { - name := rf.GenerateName(redisHAProxyName) +func generateHAProxyRedisMasterConfigmap(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) *corev1.ConfigMap { + name := GetHaproxyMasterName(rf) redisName := rf.GenerateName("redis") namespace := rf.Namespace - labels = util.MergeLabels(labels, map[string]string{ - "app.kubernetes.io/component": "redis", - }) + labels = util.MergeLabels(labels, generateSelectorLabels("haproxy", rf.Name), generateRedisMasterRoleLabel()) port := rf.Spec.Redis.Port haproxyCfg := fmt.Sprintf(`global @@ -237,16 +230,15 @@ func generateRedisHeadlessService(rf *redisfailoverv1.RedisFailover, labels map[ } } -func generateHAProxyService(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) *corev1.Service { +func generateHAProxyRedisMasterService(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) *corev1.Service { name := rf.Spec.Haproxy.RedisHost if name == "" { - name = redisHAProxyName + name = GetHaproxyMasterName(rf) } namespace := rf.Namespace redisTargetPort := intstr.FromInt(int(rf.Spec.Redis.Port)) - selectorLabels := map[string]string{ - "app.kubernetes.io/component": "redis", - } + selectorLabels := util.MergeLabels(labels, generateSelectorLabels("haproxy", rf.Name), generateRedisMasterRoleLabel()) + selectorLabels = util.MergeLabels(selectorLabels, generateComponentLabel("haproxy")) selectorLabels = util.MergeLabels(labels, selectorLabels) @@ -281,6 +273,189 @@ func generateHAProxyService(rf *redisfailoverv1.RedisFailover, labels map[string } } +func generateHAProxyRedisSlaveDeployment(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) *appsv1.Deployment { + + name := GetHaproxySlaveName(rf) + + namespace := rf.Namespace + + labels = util.MergeLabels(labels, generateSelectorLabels("haproxy", rf.Name), generateRedisSlaveRoleLabel()) + + selectorLabels := util.MergeLabels( + labels, + generateComponentLabel("haproxy"), + ) + + volumeMounts := []corev1.VolumeMount{ + { + Name: "config", + MountPath: "/usr/local/etc/haproxy/haproxy.cfg", + SubPath: "haproxy.cfg", + ReadOnly: true, + }, + } + + volumes := []corev1.Volume{ + { + Name: "config", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: name, + }, + }, + }, + }, + } + + sd := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: labels, + OwnerReferences: ownerRefs, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &rf.Spec.Haproxy.Replicas, + Selector: &metav1.LabelSelector{ + MatchLabels: selectorLabels, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: selectorLabels, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "haproxy", + Image: rf.Spec.Haproxy.Image, + Ports: []corev1.ContainerPort{ + { + ContainerPort: rf.Spec.Redis.Port.ToInt32(), + }, + }, + VolumeMounts: volumeMounts, + Resources: rf.Spec.Haproxy.Resources, + }, + }, + Volumes: volumes, + RestartPolicy: "Always", + }, + }, + }, + } + + if rf.Spec.Haproxy.Affinity != nil { + sd.Spec.Template.Spec.Affinity = rf.Spec.Haproxy.Affinity + } + + return sd +} + +func generateHAProxyRedisSlaveConfigmap(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) *corev1.ConfigMap { + name := GetHaproxySlaveName(rf) + redisName := rf.GenerateName("redis") + + namespace := rf.Namespace + + labels = util.MergeLabels(labels, generateSelectorLabels("haproxy", rf.Name), generateRedisSlaveRoleLabel()) + + port := rf.Spec.Redis.Port + haproxyCfg := fmt.Sprintf(`global + daemon + maxconn 5000 + + defaults + mode tcp + timeout connect 5000ms + timeout client 50000ms + timeout server 50000ms + timeout check 5000ms + + frontend http + bind :8080 + default_backend stats + + backend stats + mode http + stats enable + stats uri / + stats refresh 1s + stats show-legends + stats admin if TRUE + + resolvers k8s + parse-resolv-conf + hold other 10s + hold refused 10s + hold nx 10 + hold timeout 10s + hold valid 10s + hold obsolete 10s + + frontend redis-slave + bind *:%d + default_backend redis-slave + + backend redis-slave + mode tcp + balance first + option tcp-check + tcp-check send info\ replication\r\n + tcp-check expect string role:slave + server-template redis %d _redis._tcp.%s.%s.svc.cluster.local:%d check inter 1s resolvers k8s init-addr none +`, port, rf.Spec.Redis.Replicas, redisName, namespace, port) + + return &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: labels, + OwnerReferences: ownerRefs, + }, + Data: map[string]string{ + "haproxy.cfg": haproxyCfg, + }, + } +} + +func generateHAProxyRedisSlaveService(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) *corev1.Service { + name := GetHaproxySlaveName(rf) + + namespace := rf.Namespace + redisTargetPort := intstr.FromInt(int(rf.Spec.Redis.Port)) + + selectorLabels := util.MergeLabels(labels, generateSelectorLabels("haproxy", rf.Name), generateRedisSlaveRoleLabel()) + + selectorLabels = util.MergeLabels( + selectorLabels, + generateComponentLabel("haproxy"), + labels) + + spec := corev1.ServiceSpec{ + Selector: selectorLabels, + Type: "ClusterIP", + Ports: []corev1.ServicePort{ + { + Name: "redis-slave", + Port: rf.Spec.Redis.Port.ToInt32(), + TargetPort: redisTargetPort, + Protocol: "TCP", + }, + }, + } + + return &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: labels, + OwnerReferences: ownerRefs, + }, + Spec: spec, + } +} + func generateRedisNetworkPolicy(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) *np.NetworkPolicy { name := GetRedisNetworkPolicyName(rf) namespace := rf.Namespace diff --git a/operator/redisfailover/service/generator_test.go b/operator/redisfailover/service/generator_test.go index bb89f5b12..888b4b353 100644 --- a/operator/redisfailover/service/generator_test.go +++ b/operator/redisfailover/service/generator_test.go @@ -1295,7 +1295,7 @@ func TestRedisService(t *testing.T) { } func TestHaproxyService(t *testing.T) { - haproxyName := "redis-haproxy" + haproxyName := "rfrm-haproxy-test" portName := "redis-master" defaultRedisPort := redisfailoverv1.Port(6379) customClusterIP := "10.1.1.100" @@ -1325,8 +1325,11 @@ func TestHaproxyService(t *testing.T) { Spec: corev1.ServiceSpec{ Type: corev1.ServiceTypeClusterIP, Selector: map[string]string{ - "app.kubernetes.io/component": "redis", + "app.kubernetes.io/component": "haproxy", + "app.kubernetes.io/name": "test", + "app.kubernetes.io/part-of": "redis-failover", "redisfailovers.databases.spotahome.com/component": "haproxy", + "redisfailovers-role": "master", }, Ports: []corev1.ServicePort{ { @@ -1361,8 +1364,11 @@ func TestHaproxyService(t *testing.T) { Type: corev1.ServiceTypeClusterIP, ClusterIP: customClusterIP, Selector: map[string]string{ - "app.kubernetes.io/component": "redis", + "app.kubernetes.io/component": "haproxy", + "app.kubernetes.io/name": "test", + "app.kubernetes.io/part-of": "redis-failover", "redisfailovers.databases.spotahome.com/component": "haproxy", + "redisfailovers-role": "master", }, Ports: []corev1.ServicePort{ { @@ -1402,7 +1408,7 @@ func TestHaproxyService(t *testing.T) { }).Return(nil) client := rfservice.NewRedisFailoverKubeClient(ms, log.Dummy, metrics.Dummy) - err := client.EnsureHAProxyService(rf, test.rfLabels, []metav1.OwnerReference{{Name: "testing"}}) + err := client.EnsureHAProxyRedisMasterService(rf, test.rfLabels, []metav1.OwnerReference{{Name: "testing"}}) assert.Equal(test.expectedService, generatedService) assert.NoError(err) diff --git a/operator/redisfailover/service/names.go b/operator/redisfailover/service/names.go index 9dc985b67..93a22eb9c 100644 --- a/operator/redisfailover/service/names.go +++ b/operator/redisfailover/service/names.go @@ -52,6 +52,14 @@ func GetRedisSlaveName(rf *redisfailoverv1.RedisFailover) string { return generateName(redisSlaveName, rf.Name) } +func GetHaproxySlaveName(rf *redisfailoverv1.RedisFailover) string { + return generateName(redisHAProxySlaveRedisName, rf.Name) +} + +func GetHaproxyMasterName(rf *redisfailoverv1.RedisFailover) string { + return generateName(redisHAProxyMasterRedisName, rf.Name) +} + func generateName(typeName, metaName string) string { return fmt.Sprintf("%s%s-%s", baseName, typeName, metaName) }