From a971847f0512b826897867c3065aaac860367630 Mon Sep 17 00:00:00 2001 From: Aaron Kuehler Date: Thu, 14 Dec 2023 12:29:21 -0500 Subject: [PATCH] Use more targeted NetworkPolicies (#25) In #4, we added a NetworkPolicy. The intent was to prevent the Redis and/or Sentinel pods from differing RedisFailovers from joining up with one another (See: https://github.com/spotahome/redis-operator/issues/550). These policies have proven to be too coarsely grained. We end up deploying supplemental NetworkPolicies to allow ingress traffic. This change narrows the scope of the NetworkPolicies manged by the operator. One policy allows traffic to the Redis node pods ONLY on the redis port and monitoring port for traffic originating from within the namespace. The other policy allows traffic to the Sentinel pods ONLY on the sentinel port for traffic originating from within the namespace. All other traffic to these pods will be dropped. Connections to the HAProxy pods - IE access to the redis master node - will now be allowed by default. This achieves the goals of #4 and allows us to stop littering additional NetworkPolicies to allow external communication with a Redis instance. --- CHANGELOG.md | 1 + Makefile | 6 +- .../service/RedisFailoverClient.go | 42 +- operator/redisfailover/ensurer.go | 5 +- operator/redisfailover/service/client.go | 19 +- operator/redisfailover/service/constants.go | 29 +- operator/redisfailover/service/generator.go | 66 ++- .../redisfailover/service/generator_test.go | 472 ++++++++++++++++++ operator/redisfailover/service/names.go | 11 +- 9 files changed, 605 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab8998033..e0f7b8a70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Check [releases](https://github.com/spotahome/redis-operator/releases) section f ## Unreleased +- [Use finer grained NetworkPolicies](https://github.com/powerhome/redis-operator/pull/25) - [Fix PodDisruptionBudget deprecation warnings on kube 1.21+](https://github.com/powerhome/redis-operator/pull/19) ## [v1.1.0-rc.3] - 2022-01-19 diff --git a/Makefile b/Makefile index 9b313a939..157538b24 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -VERSION := v1.3.0-rc0 +VERSION := v1.7.1-rc1 # Name of this service/application SERVICE_NAME := redis-operator @@ -31,11 +31,11 @@ BRANCH=$(shell git rev-parse --abbrev-ref HEAD) TAG := $(GITTAG) ifneq ($(COMMIT), $(GITTAG_COMMIT)) - TAG := $(COMMIT) + TAG := $(COMMIT) endif ifneq ($(shell git status --porcelain),) - TAG := $(TAG)-dirty + TAG := $(TAG)-dirty endif diff --git a/mocks/operator/redisfailover/service/RedisFailoverClient.go b/mocks/operator/redisfailover/service/RedisFailoverClient.go index 37e5b9c6a..29e56e160 100644 --- a/mocks/operator/redisfailover/service/RedisFailoverClient.go +++ b/mocks/operator/redisfailover/service/RedisFailoverClient.go @@ -56,20 +56,6 @@ func (_m *RedisFailoverClient) EnsureHAProxyService(rFailover *v1.RedisFailover, return r0 } -// EnsureNetworkPolicy provides a mock function with given fields: rFailover, labels, ownerRefs -func (_m *RedisFailoverClient) EnsureNetworkPolicy(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 -} - // EnsureNotPresentRedisService provides a mock function with given fields: rFailover func (_m *RedisFailoverClient) EnsureNotPresentRedisService(rFailover *v1.RedisFailover) error { ret := _m.Called(rFailover) @@ -126,6 +112,20 @@ func (_m *RedisFailoverClient) EnsureRedisMasterService(rFailover *v1.RedisFailo return r0 } +// EnsureRedisNetworkPolicy provides a mock function with given fields: rFailover, labels, ownerRefs +func (_m *RedisFailoverClient) EnsureRedisNetworkPolicy(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 +} + // EnsureRedisReadinessConfigMap provides a mock function with given fields: rFailover, labels, ownerRefs func (_m *RedisFailoverClient) EnsureRedisReadinessConfigMap(rFailover *v1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { ret := _m.Called(rFailover, labels, ownerRefs) @@ -224,6 +224,20 @@ func (_m *RedisFailoverClient) EnsureSentinelDeployment(rFailover *v1.RedisFailo return r0 } +// EnsureSentinelNetworkPolicy provides a mock function with given fields: rFailover, labels, ownerRefs +func (_m *RedisFailoverClient) EnsureSentinelNetworkPolicy(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 +} + // EnsureSentinelService provides a mock function with given fields: rFailover, labels, ownerRefs func (_m *RedisFailoverClient) EnsureSentinelService(rFailover *v1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { ret := _m.Called(rFailover, labels, ownerRefs) diff --git a/operator/redisfailover/ensurer.go b/operator/redisfailover/ensurer.go index 533427fe5..bf49d45c6 100644 --- a/operator/redisfailover/ensurer.go +++ b/operator/redisfailover/ensurer.go @@ -20,7 +20,10 @@ func (w *RedisFailoverHandler) Ensure(rf *redisfailoverv1.RedisFailover, labels } if !(len(rf.Spec.NetworkPolicyNsList) == 0) { - if err := w.rfService.EnsureNetworkPolicy(rf, labels, or); err != nil { + if err := w.rfService.EnsureRedisNetworkPolicy(rf, labels, or); err != nil { + return err + } + if err := w.rfService.EnsureSentinelNetworkPolicy(rf, labels, or); err != nil { return err } } diff --git a/operator/redisfailover/service/client.go b/operator/redisfailover/service/client.go index 7c8d347fc..3dbaba5f4 100644 --- a/operator/redisfailover/service/client.go +++ b/operator/redisfailover/service/client.go @@ -18,7 +18,8 @@ type RedisFailoverClient interface { EnsureHAProxyConfigmap(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error EnsureHAProxyService(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error EnsureRedisHeadlessService(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error - EnsureNetworkPolicy(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 EnsureSentinelService(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error EnsureSentinelConfigMap(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error EnsureSentinelDeployment(rFailover *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error @@ -80,11 +81,19 @@ func generateComponentLabel(componentType string) map[string]string { } } -// EnsureNetworkPolicy makes sure the network policy exists -func (r *RedisFailoverKubeClient) EnsureNetworkPolicy(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { - svc := generateNetworkPolicy(rf, labels, ownerRefs) +// EnsureRedisNetworkPolicy makes sure the redis network policy exists +func (r *RedisFailoverKubeClient) EnsureRedisNetworkPolicy(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { + svc := generateRedisNetworkPolicy(rf, labels, ownerRefs) err := r.K8SService.CreateOrUpdateNetworkPolicy(rf.Namespace, svc) - r.setEnsureOperationMetrics(svc.Namespace, svc.Name, "NetworkPolicy", rf.Name, err) + r.setEnsureOperationMetrics(svc.Namespace, svc.Name, "EnsureRedisNetworkPolicy", rf.Name, err) + return err +} + +// EnsureSentinelNetworkPolicy makes sure the redis network policy exists +func (r *RedisFailoverKubeClient) EnsureSentinelNetworkPolicy(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { + svc := generateSentinelNetworkPolicy(rf, labels, ownerRefs) + err := r.K8SService.CreateOrUpdateNetworkPolicy(rf.Namespace, svc) + r.setEnsureOperationMetrics(svc.Namespace, svc.Name, "EnsureSentinelNetworkPolicy", rf.Name, err) return err } diff --git a/operator/redisfailover/service/constants.go b/operator/redisfailover/service/constants.go index 9cc2d06aa..ef320fd0f 100644 --- a/operator/redisfailover/service/constants.go +++ b/operator/redisfailover/service/constants.go @@ -14,20 +14,21 @@ const ( ) const ( - baseName = "rf" - sentinelName = "s" - sentinelRoleName = "sentinel" - sentinelConfigFileName = "sentinel.conf" - redisConfigFileName = "redis.conf" - redisName = "r" - redisMasterName = "rm" - redisSlaveName = "rs" - redisShutdownName = "r-s" - redisReadinessName = "r-readiness" - redisRoleName = "redis" - appLabel = "redis-failover" - hostnameTopologyKey = "kubernetes.io/hostname" - networkPolicyName = "network-policy" + 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" ) const ( diff --git a/operator/redisfailover/service/generator.go b/operator/redisfailover/service/generator.go index 3cd0cbf36..d7c1cbe5e 100644 --- a/operator/redisfailover/service/generator.go +++ b/operator/redisfailover/service/generator.go @@ -281,16 +281,15 @@ func generateHAProxyService(rf *redisfailoverv1.RedisFailover, labels map[string } } -func generateNetworkPolicy(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) *np.NetworkPolicy { - name := GetNetworkPolicyName(rf) +func generateRedisNetworkPolicy(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) *np.NetworkPolicy { + name := GetRedisNetworkPolicyName(rf) namespace := rf.Namespace networkPolicyNsList := rf.Spec.NetworkPolicyNsList - selectorLabels := generateSelectorLabels(networkPolicyName, rf.Name) + selectorLabels := generateSelectorLabels(redisRoleName, rf.Name) labels = util.MergeLabels(labels, selectorLabels) - sentinelTargetPort := intstr.FromInt(int(rf.Spec.Sentinel.Port)) metricsTargetPort := intstr.FromInt(9121) redisTargetPort := intstr.FromInt(int(rf.Spec.Redis.Port)) @@ -313,7 +312,59 @@ func generateNetworkPolicy(rf *redisfailoverv1.RedisFailover, labels map[string] Port: &redisTargetPort, }, np.NetworkPolicyPort{ Port: &metricsTargetPort, - }, np.NetworkPolicyPort{ + }) + + return &np.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: labels, + OwnerReferences: ownerRefs, + }, + Spec: np.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: util.MergeLabels( + map[string]string{"redisfailovers.databases.spotahome.com/name": rf.Name}, + generateComponentLabel("redis"), + ), + }, + Ingress: []np.NetworkPolicyIngressRule{ + np.NetworkPolicyIngressRule{ + From: peers, + Ports: ports, + }, + }, + }, + } +} + +func generateSentinelNetworkPolicy(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) *np.NetworkPolicy { + name := GetSentinelNetworkPolicyName(rf) + namespace := rf.Namespace + + networkPolicyNsList := rf.Spec.NetworkPolicyNsList + + selectorLabels := generateSelectorLabels(sentinelRoleName, rf.Name) + labels = util.MergeLabels(labels, selectorLabels) + + sentinelTargetPort := intstr.FromInt(int(rf.Spec.Sentinel.Port)) + + peers := []np.NetworkPolicyPeer{} + + for _, inputPeer := range networkPolicyNsList { + + labelKey := inputPeer.MatchLabelKey + labelValue := inputPeer.MatchLabelValue + + peers = append(peers, np.NetworkPolicyPeer{ + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{labelKey: labelValue}, + }, + }) + } + + ports := make([]np.NetworkPolicyPort, 0) + ports = append(ports, np.NetworkPolicyPort{ Port: &sentinelTargetPort, }) @@ -326,7 +377,10 @@ func generateNetworkPolicy(rf *redisfailoverv1.RedisFailover, labels map[string] }, Spec: np.NetworkPolicySpec{ PodSelector: metav1.LabelSelector{ - MatchLabels: map[string]string{"redisfailovers.databases.spotahome.com/name": rf.Name}, + MatchLabels: util.MergeLabels( + map[string]string{"redisfailovers.databases.spotahome.com/name": rf.Name}, + generateComponentLabel("sentinel"), + ), }, Ingress: []np.NetworkPolicyIngressRule{ np.NetworkPolicyIngressRule{ diff --git a/operator/redisfailover/service/generator_test.go b/operator/redisfailover/service/generator_test.go index 124f16fcd..bb89f5b12 100644 --- a/operator/redisfailover/service/generator_test.go +++ b/operator/redisfailover/service/generator_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/mock" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" @@ -1409,6 +1410,477 @@ func TestHaproxyService(t *testing.T) { } } +func TestRedisNetworkPolicy(t *testing.T) { + tests := []struct { + name string + rfName string + rfNamespace string + rfRedisPort int + rfNetworkPolicyNamespaceEntries []redisfailoverv1.NetworkPolicyNamespaceEntry + rfLabels map[string]string + expected networkingv1.NetworkPolicy + }{ + { + name: "with defaults", + rfNetworkPolicyNamespaceEntries: []redisfailoverv1.NetworkPolicyNamespaceEntry{ + redisfailoverv1.NetworkPolicyNamespaceEntry{ + MatchLabelKey: "app.kubernetes.io/instance", + MatchLabelValue: namespace, + }, + }, + expected: networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "rfr-np-" + name, + Namespace: namespace, + Labels: map[string]string{ + "app.kubernetes.io/component": "redis", + "app.kubernetes.io/name": name, + "app.kubernetes.io/part-of": "redis-failover", + }, + Annotations: nil, + OwnerReferences: []metav1.OwnerReference{ + { + Name: "testing", + }, + }, + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "redisfailovers.databases.spotahome.com/component": "redis", + "redisfailovers.databases.spotahome.com/name": name, + }, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + networkingv1.NetworkPolicyIngressRule{ + From: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/instance": namespace, + }, + }, + }, + }, + Ports: []networkingv1.NetworkPolicyPort{ + networkingv1.NetworkPolicyPort{ + Port: &intstr.IntOrString{ + IntVal: 6379, + Type: intstr.Int, + }, + }, + networkingv1.NetworkPolicyPort{ + Port: &intstr.IntOrString{ + IntVal: 9121, + Type: intstr.Int, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "with custom redis Port", + rfRedisPort: 6698, + rfNetworkPolicyNamespaceEntries: []redisfailoverv1.NetworkPolicyNamespaceEntry{ + redisfailoverv1.NetworkPolicyNamespaceEntry{ + MatchLabelKey: "app.kubernetes.io/instance", + MatchLabelValue: namespace, + }, + }, + expected: networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "rfr-np-" + name, + Namespace: namespace, + Labels: map[string]string{ + "app.kubernetes.io/component": "redis", + "app.kubernetes.io/name": name, + "app.kubernetes.io/part-of": "redis-failover", + }, + Annotations: nil, + OwnerReferences: []metav1.OwnerReference{ + { + Name: "testing", + }, + }, + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "redisfailovers.databases.spotahome.com/component": "redis", + "redisfailovers.databases.spotahome.com/name": name, + }, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + networkingv1.NetworkPolicyIngressRule{ + From: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/instance": namespace, + }, + }, + }, + }, + Ports: []networkingv1.NetworkPolicyPort{ + networkingv1.NetworkPolicyPort{ + Port: &intstr.IntOrString{ + IntVal: 6698, + Type: intstr.Int, + }, + }, + networkingv1.NetworkPolicyPort{ + Port: &intstr.IntOrString{ + IntVal: 9121, + Type: intstr.Int, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "with custom NetorkPolicyNamespaceEntries", + rfNetworkPolicyNamespaceEntries: []redisfailoverv1.NetworkPolicyNamespaceEntry{ + redisfailoverv1.NetworkPolicyNamespaceEntry{ + MatchLabelKey: "app.kubernetes.io/instance", + MatchLabelValue: namespace, + }, + redisfailoverv1.NetworkPolicyNamespaceEntry{ + MatchLabelKey: "app.kubernetes.io/instance", + MatchLabelValue: "extra-namespace", + }, + }, + expected: networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "rfr-np-" + name, + Namespace: namespace, + Labels: map[string]string{ + "app.kubernetes.io/component": "redis", + "app.kubernetes.io/name": name, + "app.kubernetes.io/part-of": "redis-failover", + }, + Annotations: nil, + OwnerReferences: []metav1.OwnerReference{ + { + Name: "testing", + }, + }, + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "redisfailovers.databases.spotahome.com/component": "redis", + "redisfailovers.databases.spotahome.com/name": name, + }, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + networkingv1.NetworkPolicyIngressRule{ + From: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/instance": namespace, + }, + }, + }, + networkingv1.NetworkPolicyPeer{ + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/instance": "extra-namespace", + }, + }, + }, + }, + Ports: []networkingv1.NetworkPolicyPort{ + networkingv1.NetworkPolicyPort{ + Port: &intstr.IntOrString{ + IntVal: 6379, + Type: intstr.Int, + }, + }, + networkingv1.NetworkPolicyPort{ + Port: &intstr.IntOrString{ + IntVal: 9121, + Type: intstr.Int, + }, + }, + }, + }, + }, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + assert := assert.New(t) + + // Generate a default RedisFailover and attaching the required annotations + rf := generateRF() + if test.rfName != "" { + rf.Name = test.rfName + } + if test.rfNamespace != "" { + rf.Namespace = test.rfNamespace + } + if test.rfRedisPort <= 0 { + rf.Spec.Redis.Port = 6379 + + } else { + rf.Spec.Redis.Port = redisfailoverv1.Port(test.rfRedisPort) + } + if test.rfNetworkPolicyNamespaceEntries != nil { + rf.Spec.NetworkPolicyNsList = test.rfNetworkPolicyNamespaceEntries + } + + generated := networkingv1.NetworkPolicy{} + + ms := &mK8SService.Services{} + ms.On("CreateOrUpdateNetworkPolicy", rf.Namespace, mock.Anything).Once().Run(func(args mock.Arguments) { + s := args.Get(1).(*networkingv1.NetworkPolicy) + generated = *s + }).Return(nil) + + client := rfservice.NewRedisFailoverKubeClient(ms, log.Dummy, metrics.Dummy) + err := client.EnsureRedisNetworkPolicy(rf, test.rfLabels, []metav1.OwnerReference{{Name: "testing"}}) + + assert.Equal(test.expected, generated) + assert.NoError(err) + }) + } +} + +func TestSentinelNetworkPolicy(t *testing.T) { + tests := []struct { + name string + rfName string + rfNamespace string + rfSentinelPort int + rfNetworkPolicyNamespaceEntries []redisfailoverv1.NetworkPolicyNamespaceEntry + rfLabels map[string]string + expected networkingv1.NetworkPolicy + }{ + { + name: "with defaults", + rfNetworkPolicyNamespaceEntries: []redisfailoverv1.NetworkPolicyNamespaceEntry{ + redisfailoverv1.NetworkPolicyNamespaceEntry{ + MatchLabelKey: "app.kubernetes.io/instance", + MatchLabelValue: namespace, + }, + }, + expected: networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "rfs-np-" + name, + Namespace: namespace, + Labels: map[string]string{ + "app.kubernetes.io/component": "sentinel", + "app.kubernetes.io/name": name, + "app.kubernetes.io/part-of": "redis-failover", + }, + Annotations: nil, + OwnerReferences: []metav1.OwnerReference{ + { + Name: "testing", + }, + }, + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "redisfailovers.databases.spotahome.com/component": "sentinel", + "redisfailovers.databases.spotahome.com/name": name, + }, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + networkingv1.NetworkPolicyIngressRule{ + From: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/instance": namespace, + }, + }, + }, + }, + Ports: []networkingv1.NetworkPolicyPort{ + networkingv1.NetworkPolicyPort{ + Port: &intstr.IntOrString{ + IntVal: 26379, + Type: intstr.Int, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "with custom sentinel Port", + rfSentinelPort: 17781, + rfNetworkPolicyNamespaceEntries: []redisfailoverv1.NetworkPolicyNamespaceEntry{ + redisfailoverv1.NetworkPolicyNamespaceEntry{ + MatchLabelKey: "app.kubernetes.io/instance", + MatchLabelValue: namespace, + }, + }, + expected: networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "rfs-np-" + name, + Namespace: namespace, + Labels: map[string]string{ + "app.kubernetes.io/component": "sentinel", + "app.kubernetes.io/name": name, + "app.kubernetes.io/part-of": "redis-failover", + }, + Annotations: nil, + OwnerReferences: []metav1.OwnerReference{ + { + Name: "testing", + }, + }, + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "redisfailovers.databases.spotahome.com/component": "sentinel", + "redisfailovers.databases.spotahome.com/name": name, + }, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + networkingv1.NetworkPolicyIngressRule{ + From: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/instance": namespace, + }, + }, + }, + }, + Ports: []networkingv1.NetworkPolicyPort{ + networkingv1.NetworkPolicyPort{ + Port: &intstr.IntOrString{ + IntVal: 17781, + Type: intstr.Int, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "with custom NetorkPolicyNamespaceEntries", + rfNetworkPolicyNamespaceEntries: []redisfailoverv1.NetworkPolicyNamespaceEntry{ + redisfailoverv1.NetworkPolicyNamespaceEntry{ + MatchLabelKey: "app.kubernetes.io/instance", + MatchLabelValue: namespace, + }, + redisfailoverv1.NetworkPolicyNamespaceEntry{ + MatchLabelKey: "app.kubernetes.io/instance", + MatchLabelValue: "extra-namespace", + }, + }, + expected: networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "rfs-np-" + name, + Namespace: namespace, + Labels: map[string]string{ + "app.kubernetes.io/component": "sentinel", + "app.kubernetes.io/name": name, + "app.kubernetes.io/part-of": "redis-failover", + }, + Annotations: nil, + OwnerReferences: []metav1.OwnerReference{ + { + Name: "testing", + }, + }, + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "redisfailovers.databases.spotahome.com/component": "sentinel", + "redisfailovers.databases.spotahome.com/name": name, + }, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + networkingv1.NetworkPolicyIngressRule{ + From: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/instance": namespace, + }, + }, + }, + networkingv1.NetworkPolicyPeer{ + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/instance": "extra-namespace", + }, + }, + }, + }, + Ports: []networkingv1.NetworkPolicyPort{ + networkingv1.NetworkPolicyPort{ + Port: &intstr.IntOrString{ + IntVal: 26379, + Type: intstr.Int, + }, + }, + }, + }, + }, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + assert := assert.New(t) + + // Generate a default RedisFailover and attaching the required annotations + rf := generateRF() + if test.rfName != "" { + rf.Name = test.rfName + } + if test.rfNamespace != "" { + rf.Namespace = test.rfNamespace + } + if test.rfSentinelPort != 0 { + rf.Spec.Sentinel.Port = redisfailoverv1.Port(test.rfSentinelPort) + } + if test.rfNetworkPolicyNamespaceEntries != nil { + rf.Spec.NetworkPolicyNsList = test.rfNetworkPolicyNamespaceEntries + } + + generated := networkingv1.NetworkPolicy{} + + ms := &mK8SService.Services{} + ms.On("CreateOrUpdateNetworkPolicy", rf.Namespace, mock.Anything).Once().Run(func(args mock.Arguments) { + s := args.Get(1).(*networkingv1.NetworkPolicy) + generated = *s + }).Return(nil) + + client := rfservice.NewRedisFailoverKubeClient(ms, log.Dummy, metrics.Dummy) + err := client.EnsureSentinelNetworkPolicy(rf, test.rfLabels, []metav1.OwnerReference{{Name: "testing"}}) + + assert.Equal(test.expected, generated) + assert.NoError(err) + }) + } +} + func TestRedisMasterService(t *testing.T) { tests := []struct { name string diff --git a/operator/redisfailover/service/names.go b/operator/redisfailover/service/names.go index 7d4352ba0..9dc985b67 100644 --- a/operator/redisfailover/service/names.go +++ b/operator/redisfailover/service/names.go @@ -34,9 +34,14 @@ func GetSentinelName(rf *redisfailoverv1.RedisFailover) string { return generateName(sentinelName, rf.Name) } -// GetSentinelName returns the name for sentinel resources -func GetNetworkPolicyName(rf *redisfailoverv1.RedisFailover) string { - return generateName(networkPolicyName, rf.Name) +// GetRedisNetworkPolicyName returns the name for the redis network policy +func GetRedisNetworkPolicyName(rf *redisfailoverv1.RedisFailover) string { + return generateName(redisNetworkPolicyName, rf.Name) +} + +// GetSentinelNetworkPolicyName returns the name for the sentinel network policy +func GetSentinelNetworkPolicyName(rf *redisfailoverv1.RedisFailover) string { + return generateName(sentinelNetworkPolicyName, rf.Name) } func GetRedisMasterName(rf *redisfailoverv1.RedisFailover) string {