diff --git a/Dockerfile b/Dockerfile index f953b9f25..1a76f506e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,6 +22,7 @@ COPY main.go main.go COPY api/ api/ COPY controllers/ controllers/ COPY k8sutils/ k8sutils/ +COPY pkg/ pkg/ COPY mocks/ mocks/ # Build diff --git a/api/common_types.go b/api/common_types.go index 8d78ad389..40cbd9695 100644 --- a/api/common_types.go +++ b/api/common_types.go @@ -36,7 +36,9 @@ type ExistingPasswordSecret struct { // RedisExporter interface will have the information for redis exporter related stuff // +k8s:deepcopy-gen=true type RedisExporter struct { - Enabled bool `json:"enabled,omitempty"` + Enabled bool `json:"enabled,omitempty"` + // +kubebuilder:default:=9121 + Port *int `json:"port,omitempty"` Image string `json:"image"` Resources *corev1.ResourceRequirements `json:"resources,omitempty"` ImagePullPolicy corev1.PullPolicy `json:"imagePullPolicy,omitempty"` diff --git a/api/v1beta2/rediscluster_default.go b/api/v1beta2/rediscluster_default.go index 7dbc9ea8f..9b6c16ad1 100644 --- a/api/v1beta2/rediscluster_default.go +++ b/api/v1beta2/rediscluster_default.go @@ -7,4 +7,7 @@ func (r *RedisCluster) SetDefault() { if r.Spec.Port == nil { r.Spec.Port = pointer.Int(6379) } + if r.Spec.RedisExporter != nil && r.Spec.RedisExporter.Port == nil { + r.Spec.RedisExporter.Port = pointer.Int(9121) + } } diff --git a/api/zz_generated.deepcopy.go b/api/zz_generated.deepcopy.go index efeda74c2..499bb939b 100644 --- a/api/zz_generated.deepcopy.go +++ b/api/zz_generated.deepcopy.go @@ -162,6 +162,11 @@ func (in *RedisConfig) DeepCopy() *RedisConfig { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RedisExporter) DeepCopyInto(out *RedisExporter) { *out = *in + if in.Port != nil { + in, out := &in.Port, &out.Port + *out = new(int) + **out = **in + } if in.Resources != nil { in, out := &in.Resources, &out.Resources *out = new(v1.ResourceRequirements) diff --git a/config/crd/bases/redis.redis.opstreelabs.in_redis.yaml b/config/crd/bases/redis.redis.opstreelabs.in_redis.yaml index 2222d4ed0..fec228a5e 100644 --- a/config/crd/bases/redis.redis.opstreelabs.in_redis.yaml +++ b/config/crd/bases/redis.redis.opstreelabs.in_redis.yaml @@ -1274,6 +1274,9 @@ spec: description: PullPolicy describes a policy for if/when to pull a container image type: string + port: + default: 9121 + type: integer resources: description: ResourceRequirements describes the compute resource requirements. @@ -5615,6 +5618,9 @@ spec: description: PullPolicy describes a policy for if/when to pull a container image type: string + port: + default: 9121 + type: integer resources: description: ResourceRequirements describes the compute resource requirements. diff --git a/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml b/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml index cdf11fc59..e525f917e 100644 --- a/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml +++ b/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml @@ -402,6 +402,9 @@ spec: description: PullPolicy describes a policy for if/when to pull a container image type: string + port: + default: 9121 + type: integer resources: description: ResourceRequirements describes the compute resource requirements. @@ -5920,6 +5923,9 @@ spec: description: PullPolicy describes a policy for if/when to pull a container image type: string + port: + default: 9121 + type: integer resources: description: ResourceRequirements describes the compute resource requirements. diff --git a/config/crd/bases/redis.redis.opstreelabs.in_redisreplications.yaml b/config/crd/bases/redis.redis.opstreelabs.in_redisreplications.yaml index cf9dbd5bf..76856bcfc 100644 --- a/config/crd/bases/redis.redis.opstreelabs.in_redisreplications.yaml +++ b/config/crd/bases/redis.redis.opstreelabs.in_redisreplications.yaml @@ -1276,6 +1276,9 @@ spec: description: PullPolicy describes a policy for if/when to pull a container image type: string + port: + default: 9121 + type: integer resources: description: ResourceRequirements describes the compute resource requirements. @@ -5620,6 +5623,9 @@ spec: description: PullPolicy describes a policy for if/when to pull a container image type: string + port: + default: 9121 + type: integer resources: description: ResourceRequirements describes the compute resource requirements. diff --git a/config/crd/bases/redis.redis.opstreelabs.in_redissentinels.yaml b/config/crd/bases/redis.redis.opstreelabs.in_redissentinels.yaml index 91c5b1e5f..e89f42317 100644 --- a/config/crd/bases/redis.redis.opstreelabs.in_redissentinels.yaml +++ b/config/crd/bases/redis.redis.opstreelabs.in_redissentinels.yaml @@ -3316,6 +3316,9 @@ spec: description: PullPolicy describes a policy for if/when to pull a container image type: string + port: + default: 9121 + type: integer resources: description: ResourceRequirements describes the compute resource requirements. diff --git a/k8sutils/labels.go b/k8sutils/labels.go index b8e780dc9..4d0d4518d 100644 --- a/k8sutils/labels.go +++ b/k8sutils/labels.go @@ -1,6 +1,8 @@ package k8sutils import ( + "strconv" + redisv1beta2 "github.com/OT-CONTAINER-KIT/redis-operator/api/v1beta2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -111,13 +113,16 @@ func filterAnnotations(anots map[string]string, ignoreAnnots ...string) map[stri } // generateServiceAnots generates and returns service annotations -func generateServiceAnots(stsMeta metav1.ObjectMeta, additionalSvcAnnotations map[string]string) map[string]string { +func generateServiceAnots(stsMeta metav1.ObjectMeta, additionalSvcAnnotations map[string]string, epp exporterPortProvider) map[string]string { anots := map[string]string{ "redis.opstreelabs.in": "true", "redis.opstreelabs.instance": stsMeta.GetName(), "prometheus.io/scrape": "true", "prometheus.io/port": "9121", } + if exporterPort, ok := epp(); ok { + anots["prometheus.io/port"] = strconv.Itoa(exporterPort) + } for k, v := range stsMeta.GetAnnotations() { anots[k] = v } diff --git a/k8sutils/labels_test.go b/k8sutils/labels_test.go index 10622b513..274f9c1a0 100644 --- a/k8sutils/labels_test.go +++ b/k8sutils/labels_test.go @@ -215,7 +215,7 @@ func TestGenerateServiceAnots(t *testing.T) { "additional-annotation": "additional-value", } - resultAnnotations := generateServiceAnots(stsMeta, additionalSvcAnnotations) + resultAnnotations := generateServiceAnots(stsMeta, additionalSvcAnnotations, defaultExporterPortProvider) if !reflect.DeepEqual(resultAnnotations, expectedAnnotations) { t.Errorf("Expected annotations to be %v but got %v", expectedAnnotations, resultAnnotations) diff --git a/k8sutils/redis-cluster.go b/k8sutils/redis-cluster.go index b6ae773b3..0962d90ef 100644 --- a/k8sutils/redis-cluster.go +++ b/k8sutils/redis-cluster.go @@ -3,7 +3,9 @@ package k8sutils import ( commonapi "github.com/OT-CONTAINER-KIT/redis-operator/api" redisv1beta2 "github.com/OT-CONTAINER-KIT/redis-operator/api/v1beta2" + "github.com/OT-CONTAINER-KIT/redis-operator/pkg/util" corev1 "k8s.io/api/core/v1" + "k8s.io/utils/pointer" ) // RedisClusterSTS is a interface to call Redis Statefulset function @@ -124,11 +126,12 @@ func generateRedisClusterContainerParams(cr *redisv1beta2.RedisCluster, security if cr.Spec.RedisExporter.Resources != nil { containerProp.RedisExporterResources = cr.Spec.RedisExporter.Resources } - if cr.Spec.RedisExporter.EnvVars != nil { containerProp.RedisExporterEnv = cr.Spec.RedisExporter.EnvVars } - + if cr.Spec.RedisExporter.Port != nil { + containerProp.RedisExporterPort = cr.Spec.RedisExporter.Port + } } if readinessProbeDef != nil { containerProp.ReadinessProbe = readinessProbeDef @@ -235,25 +238,29 @@ func (service RedisClusterService) CreateRedisClusterService(cr *redisv1beta2.Re serviceName := cr.ObjectMeta.Name + "-" + service.RedisServiceRole logger := serviceLogger(cr.Namespace, serviceName) labels := getRedisLabels(serviceName, cluster, service.RedisServiceRole, cr.ObjectMeta.Labels) - annotations := generateServiceAnots(cr.ObjectMeta, nil) - if cr.Spec.RedisExporter != nil && cr.Spec.RedisExporter.Enabled { - enableMetrics = true + var epp exporterPortProvider + if cr.Spec.RedisExporter != nil { + epp = func() (port int, enable bool) { + defaultP := pointer.Int(redisExporterPort) + return *util.Coalesce(cr.Spec.RedisExporter.Port, defaultP), cr.Spec.RedisExporter.Enabled + } } else { - enableMetrics = false + epp = disableMetrics } + annotations := generateServiceAnots(cr.ObjectMeta, nil, epp) additionalServiceAnnotations := map[string]string{} if cr.Spec.KubernetesConfig.Service != nil { additionalServiceAnnotations = cr.Spec.KubernetesConfig.Service.ServiceAnnotations } objectMetaInfo := generateObjectMetaInformation(serviceName, cr.Namespace, labels, annotations) headlessObjectMetaInfo := generateObjectMetaInformation(serviceName+"-headless", cr.Namespace, labels, annotations) - additionalObjectMetaInfo := generateObjectMetaInformation(serviceName+"-additional", cr.Namespace, labels, generateServiceAnots(cr.ObjectMeta, additionalServiceAnnotations)) - err := CreateOrUpdateService(cr.Namespace, headlessObjectMetaInfo, redisClusterAsOwner(cr), false, true, "ClusterIP", *cr.Spec.Port) + additionalObjectMetaInfo := generateObjectMetaInformation(serviceName+"-additional", cr.Namespace, labels, generateServiceAnots(cr.ObjectMeta, additionalServiceAnnotations, epp)) + err := CreateOrUpdateService(cr.Namespace, headlessObjectMetaInfo, redisClusterAsOwner(cr), disableMetrics, true, "ClusterIP", *cr.Spec.Port) if err != nil { logger.Error(err, "Cannot create headless service for Redis", "Setup.Type", service.RedisServiceRole) return err } - err = CreateOrUpdateService(cr.Namespace, objectMetaInfo, redisClusterAsOwner(cr), enableMetrics, false, "ClusterIP", *cr.Spec.Port) + err = CreateOrUpdateService(cr.Namespace, objectMetaInfo, redisClusterAsOwner(cr), epp, false, "ClusterIP", *cr.Spec.Port) if err != nil { logger.Error(err, "Cannot create service for Redis", "Setup.Type", service.RedisServiceRole) return err @@ -262,7 +269,7 @@ func (service RedisClusterService) CreateRedisClusterService(cr *redisv1beta2.Re if cr.Spec.KubernetesConfig.Service != nil { additionalServiceType = cr.Spec.KubernetesConfig.Service.ServiceType } - err = CreateOrUpdateService(cr.Namespace, additionalObjectMetaInfo, redisClusterAsOwner(cr), false, false, additionalServiceType, *cr.Spec.Port) + err = CreateOrUpdateService(cr.Namespace, additionalObjectMetaInfo, redisClusterAsOwner(cr), disableMetrics, false, additionalServiceType, *cr.Spec.Port) if err != nil { logger.Error(err, "Cannot create additional service for Redis", "Setup.Type", service.RedisServiceRole) return err diff --git a/k8sutils/redis-replication.go b/k8sutils/redis-replication.go index 5e5223a3c..b1452d174 100644 --- a/k8sutils/redis-replication.go +++ b/k8sutils/redis-replication.go @@ -1,30 +1,38 @@ package k8sutils -import redisv1beta2 "github.com/OT-CONTAINER-KIT/redis-operator/api/v1beta2" +import ( + redisv1beta2 "github.com/OT-CONTAINER-KIT/redis-operator/api/v1beta2" + "github.com/OT-CONTAINER-KIT/redis-operator/pkg/util" + "k8s.io/utils/pointer" +) // CreateReplicationService method will create replication service for Redis func CreateReplicationService(cr *redisv1beta2.RedisReplication) error { logger := serviceLogger(cr.Namespace, cr.ObjectMeta.Name) labels := getRedisLabels(cr.ObjectMeta.Name, replication, "replication", cr.ObjectMeta.Labels) - annotations := generateServiceAnots(cr.ObjectMeta, nil) - if cr.Spec.RedisExporter != nil && cr.Spec.RedisExporter.Enabled { - enableMetrics = true + var epp exporterPortProvider + if cr.Spec.RedisExporter != nil { + epp = func() (port int, enable bool) { + defaultP := pointer.Int(redisExporterPort) + return *util.Coalesce(cr.Spec.RedisExporter.Port, defaultP), cr.Spec.RedisExporter.Enabled + } } else { - enableMetrics = false + epp = disableMetrics } + annotations := generateServiceAnots(cr.ObjectMeta, nil, epp) additionalServiceAnnotations := map[string]string{} if cr.Spec.KubernetesConfig.Service != nil { additionalServiceAnnotations = cr.Spec.KubernetesConfig.Service.ServiceAnnotations } objectMetaInfo := generateObjectMetaInformation(cr.ObjectMeta.Name, cr.Namespace, labels, annotations) headlessObjectMetaInfo := generateObjectMetaInformation(cr.ObjectMeta.Name+"-headless", cr.Namespace, labels, annotations) - additionalObjectMetaInfo := generateObjectMetaInformation(cr.ObjectMeta.Name+"-additional", cr.Namespace, labels, generateServiceAnots(cr.ObjectMeta, additionalServiceAnnotations)) - err := CreateOrUpdateService(cr.Namespace, headlessObjectMetaInfo, redisReplicationAsOwner(cr), false, true, "ClusterIP", redisPort) + additionalObjectMetaInfo := generateObjectMetaInformation(cr.ObjectMeta.Name+"-additional", cr.Namespace, labels, generateServiceAnots(cr.ObjectMeta, additionalServiceAnnotations, epp)) + err := CreateOrUpdateService(cr.Namespace, headlessObjectMetaInfo, redisReplicationAsOwner(cr), disableMetrics, true, "ClusterIP", redisPort) if err != nil { logger.Error(err, "Cannot create replication headless service for Redis") return err } - err = CreateOrUpdateService(cr.Namespace, objectMetaInfo, redisReplicationAsOwner(cr), enableMetrics, false, "ClusterIP", redisPort) + err = CreateOrUpdateService(cr.Namespace, objectMetaInfo, redisReplicationAsOwner(cr), epp, false, "ClusterIP", redisPort) if err != nil { logger.Error(err, "Cannot create replication service for Redis") return err @@ -33,7 +41,7 @@ func CreateReplicationService(cr *redisv1beta2.RedisReplication) error { if cr.Spec.KubernetesConfig.Service != nil { additionalServiceType = cr.Spec.KubernetesConfig.Service.ServiceType } - err = CreateOrUpdateService(cr.Namespace, additionalObjectMetaInfo, redisReplicationAsOwner(cr), false, false, additionalServiceType, redisPort) + err = CreateOrUpdateService(cr.Namespace, additionalObjectMetaInfo, redisReplicationAsOwner(cr), disableMetrics, false, additionalServiceType, redisPort) if err != nil { logger.Error(err, "Cannot create additional service for Redis Replication") return err diff --git a/k8sutils/redis-sentinel.go b/k8sutils/redis-sentinel.go index 6eef41716..014284772 100644 --- a/k8sutils/redis-sentinel.go +++ b/k8sutils/redis-sentinel.go @@ -4,6 +4,8 @@ import ( "context" "encoding/json" "errors" + "github.com/OT-CONTAINER-KIT/redis-operator/pkg/util" + "k8s.io/utils/pointer" commonapi "github.com/OT-CONTAINER-KIT/redis-operator/api" redisv1beta2 "github.com/OT-CONTAINER-KIT/redis-operator/api/v1beta2" @@ -198,14 +200,16 @@ func (service RedisSentinelService) CreateRedisSentinelService(cr *redisv1beta2. serviceName := cr.ObjectMeta.Name + "-" + service.RedisServiceRole logger := serviceLogger(cr.Namespace, serviceName) labels := getRedisLabels(serviceName, sentinel, service.RedisServiceRole, cr.ObjectMeta.Labels) - annotations := generateServiceAnots(cr.ObjectMeta, nil) - - if cr.Spec.RedisExporter != nil && cr.Spec.RedisExporter.Enabled { - enableMetrics = true + var epp exporterPortProvider + if cr.Spec.RedisExporter != nil { + epp = func() (port int, enable bool) { + defaultP := pointer.Int(redisExporterPort) + return *util.Coalesce(cr.Spec.RedisExporter.Port, defaultP), cr.Spec.RedisExporter.Enabled + } } else { - enableMetrics = false + epp = disableMetrics } - + annotations := generateServiceAnots(cr.ObjectMeta, nil, epp) additionalServiceAnnotations := map[string]string{} if cr.Spec.KubernetesConfig.Service != nil { additionalServiceAnnotations = cr.Spec.KubernetesConfig.Service.ServiceAnnotations @@ -213,14 +217,14 @@ func (service RedisSentinelService) CreateRedisSentinelService(cr *redisv1beta2. objectMetaInfo := generateObjectMetaInformation(serviceName, cr.Namespace, labels, annotations) headlessObjectMetaInfo := generateObjectMetaInformation(serviceName+"-headless", cr.Namespace, labels, annotations) - additionalObjectMetaInfo := generateObjectMetaInformation(serviceName+"-additional", cr.Namespace, labels, generateServiceAnots(cr.ObjectMeta, additionalServiceAnnotations)) + additionalObjectMetaInfo := generateObjectMetaInformation(serviceName+"-additional", cr.Namespace, labels, generateServiceAnots(cr.ObjectMeta, additionalServiceAnnotations, epp)) - err := CreateOrUpdateService(cr.Namespace, headlessObjectMetaInfo, redisSentinelAsOwner(cr), false, true, "ClusterIP", sentinelPort) + err := CreateOrUpdateService(cr.Namespace, headlessObjectMetaInfo, redisSentinelAsOwner(cr), disableMetrics, true, "ClusterIP", sentinelPort) if err != nil { logger.Error(err, "Cannot create headless service for Redis", "Setup.Type", service.RedisServiceRole) return err } - err = CreateOrUpdateService(cr.Namespace, objectMetaInfo, redisSentinelAsOwner(cr), enableMetrics, false, "ClusterIP", sentinelPort) + err = CreateOrUpdateService(cr.Namespace, objectMetaInfo, redisSentinelAsOwner(cr), epp, false, "ClusterIP", sentinelPort) if err != nil { logger.Error(err, "Cannot create service for Redis", "Setup.Type", service.RedisServiceRole) return err @@ -230,7 +234,7 @@ func (service RedisSentinelService) CreateRedisSentinelService(cr *redisv1beta2. if cr.Spec.KubernetesConfig.Service != nil { additionalServiceType = cr.Spec.KubernetesConfig.Service.ServiceType } - err = CreateOrUpdateService(cr.Namespace, additionalObjectMetaInfo, redisSentinelAsOwner(cr), false, false, additionalServiceType, sentinelPort) + err = CreateOrUpdateService(cr.Namespace, additionalObjectMetaInfo, redisSentinelAsOwner(cr), disableMetrics, false, additionalServiceType, sentinelPort) if err != nil { logger.Error(err, "Cannot create additional service for Redis", "Setup.Type", service.RedisServiceRole) return err diff --git a/k8sutils/redis-standalone.go b/k8sutils/redis-standalone.go index 33d2e4c03..dd116b2ba 100644 --- a/k8sutils/redis-standalone.go +++ b/k8sutils/redis-standalone.go @@ -1,34 +1,42 @@ package k8sutils -import redisv1beta2 "github.com/OT-CONTAINER-KIT/redis-operator/api/v1beta2" - -var ( - enableMetrics bool +import ( + redisv1beta2 "github.com/OT-CONTAINER-KIT/redis-operator/api/v1beta2" + "github.com/OT-CONTAINER-KIT/redis-operator/pkg/util" + "k8s.io/utils/pointer" ) +//var ( +// enableMetrics bool +//) + // CreateStandaloneService method will create standalone service for Redis func CreateStandaloneService(cr *redisv1beta2.Redis) error { logger := serviceLogger(cr.Namespace, cr.ObjectMeta.Name) labels := getRedisLabels(cr.ObjectMeta.Name, standalone, "standalone", cr.ObjectMeta.Labels) - annotations := generateServiceAnots(cr.ObjectMeta, nil) - if cr.Spec.RedisExporter != nil && cr.Spec.RedisExporter.Enabled { - enableMetrics = true + var epp exporterPortProvider + if cr.Spec.RedisExporter != nil { + epp = func() (port int, enable bool) { + defaultP := pointer.Int(redisExporterPort) + return *util.Coalesce(cr.Spec.RedisExporter.Port, defaultP), cr.Spec.RedisExporter.Enabled + } } else { - enableMetrics = false + epp = disableMetrics } + annotations := generateServiceAnots(cr.ObjectMeta, nil, epp) additionalServiceAnnotations := map[string]string{} if cr.Spec.KubernetesConfig.Service != nil { additionalServiceAnnotations = cr.Spec.KubernetesConfig.Service.ServiceAnnotations } objectMetaInfo := generateObjectMetaInformation(cr.ObjectMeta.Name, cr.Namespace, labels, annotations) headlessObjectMetaInfo := generateObjectMetaInformation(cr.ObjectMeta.Name+"-headless", cr.Namespace, labels, annotations) - additionalObjectMetaInfo := generateObjectMetaInformation(cr.ObjectMeta.Name+"-additional", cr.Namespace, labels, generateServiceAnots(cr.ObjectMeta, additionalServiceAnnotations)) - err := CreateOrUpdateService(cr.Namespace, headlessObjectMetaInfo, redisAsOwner(cr), false, true, "ClusterIP", redisPort) + additionalObjectMetaInfo := generateObjectMetaInformation(cr.ObjectMeta.Name+"-additional", cr.Namespace, labels, generateServiceAnots(cr.ObjectMeta, additionalServiceAnnotations, epp)) + err := CreateOrUpdateService(cr.Namespace, headlessObjectMetaInfo, redisAsOwner(cr), disableMetrics, true, "ClusterIP", redisPort) if err != nil { logger.Error(err, "Cannot create standalone headless service for Redis") return err } - err = CreateOrUpdateService(cr.Namespace, objectMetaInfo, redisAsOwner(cr), enableMetrics, false, "ClusterIP", redisPort) + err = CreateOrUpdateService(cr.Namespace, objectMetaInfo, redisAsOwner(cr), epp, false, "ClusterIP", redisPort) if err != nil { logger.Error(err, "Cannot create standalone service for Redis") return err @@ -37,7 +45,7 @@ func CreateStandaloneService(cr *redisv1beta2.Redis) error { if cr.Spec.KubernetesConfig.Service != nil { additionalServiceType = cr.Spec.KubernetesConfig.Service.ServiceType } - err = CreateOrUpdateService(cr.Namespace, additionalObjectMetaInfo, redisAsOwner(cr), false, false, additionalServiceType, redisPort) + err = CreateOrUpdateService(cr.Namespace, additionalObjectMetaInfo, redisAsOwner(cr), disableMetrics, false, additionalServiceType, redisPort) if err != nil { logger.Error(err, "Cannot create additional service for Redis") return err diff --git a/k8sutils/services.go b/k8sutils/services.go index c28e6ba29..30a50027b 100644 --- a/k8sutils/services.go +++ b/k8sutils/services.go @@ -22,8 +22,15 @@ var ( serviceType corev1.ServiceType ) +// exporterPortProvider return the exporter port if bool is true +type exporterPortProvider func() (port int, enable bool) + +var disableMetrics exporterPortProvider = func() (int, bool) { + return 0, false +} + // generateServiceDef generates service definition for Redis -func generateServiceDef(serviceMeta metav1.ObjectMeta, enableMetrics bool, ownerDef metav1.OwnerReference, headless bool, serviceType string, port int) *corev1.Service { +func generateServiceDef(serviceMeta metav1.ObjectMeta, epp exporterPortProvider, ownerDef metav1.OwnerReference, headless bool, serviceType string, port int) *corev1.Service { var PortName string if serviceMeta.Labels["role"] == "sentinel" { PortName = "sentinel-client" @@ -50,8 +57,8 @@ func generateServiceDef(serviceMeta metav1.ObjectMeta, enableMetrics bool, owner if headless { service.Spec.ClusterIP = "None" } - if enableMetrics { - redisExporterService := enableMetricsPort() + if exporterPort, ok := epp(); ok { + redisExporterService := enableMetricsPort(exporterPort) service.Spec.Ports = append(service.Spec.Ports, *redisExporterService) } AddOwnerRefToObject(service, ownerDef) @@ -59,11 +66,11 @@ func generateServiceDef(serviceMeta metav1.ObjectMeta, enableMetrics bool, owner } // enableMetricsPort will enable the metrics for Redis service -func enableMetricsPort() *corev1.ServicePort { +func enableMetricsPort(port int) *corev1.ServicePort { return &corev1.ServicePort{ Name: redisExporterPortName, - Port: redisExporterPort, - TargetPort: intstr.FromInt(int(redisExporterPort)), + Port: int32(port), + TargetPort: intstr.FromInt(port), Protocol: corev1.ProtocolTCP, } } @@ -143,9 +150,9 @@ func serviceLogger(namespace string, name string) logr.Logger { } // CreateOrUpdateService method will create or update Redis service -func CreateOrUpdateService(namespace string, serviceMeta metav1.ObjectMeta, ownerDef metav1.OwnerReference, enableMetrics, headless bool, serviceType string, port int) error { +func CreateOrUpdateService(namespace string, serviceMeta metav1.ObjectMeta, ownerDef metav1.OwnerReference, epp exporterPortProvider, headless bool, serviceType string, port int) error { logger := serviceLogger(namespace, serviceMeta.Name) - serviceDef := generateServiceDef(serviceMeta, enableMetrics, ownerDef, headless, serviceType, port) + serviceDef := generateServiceDef(serviceMeta, epp, ownerDef, headless, serviceType, port) storedService, err := getService(namespace, serviceMeta.Name) if err != nil { if errors.IsNotFound(err) { diff --git a/k8sutils/services_test.go b/k8sutils/services_test.go index cbd012b2f..786783944 100644 --- a/k8sutils/services_test.go +++ b/k8sutils/services_test.go @@ -9,11 +9,15 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" ) +var defaultExporterPortProvider exporterPortProvider = func() (int, bool) { + return redisExporterPort, true +} + func TestGenerateServiceDef(t *testing.T) { tests := []struct { name string serviceMeta metav1.ObjectMeta - enableMetrics bool + enableMetrics exporterPortProvider headless bool serviceType string port int @@ -27,7 +31,7 @@ func TestGenerateServiceDef(t *testing.T) { "role": "sentinel", }, }, - enableMetrics: false, + enableMetrics: disableMetrics, headless: false, serviceType: "ClusterIP", port: sentinelPort, @@ -68,7 +72,7 @@ func TestGenerateServiceDef(t *testing.T) { "role": "sentinel", }, }, - enableMetrics: false, + enableMetrics: disableMetrics, headless: true, serviceType: "ClusterIP", port: sentinelPort, @@ -109,7 +113,7 @@ func TestGenerateServiceDef(t *testing.T) { "role": "redis", }, }, - enableMetrics: false, + enableMetrics: disableMetrics, headless: false, serviceType: "ClusterIP", port: redisPort, @@ -150,7 +154,7 @@ func TestGenerateServiceDef(t *testing.T) { "role": "redis", }, }, - enableMetrics: false, + enableMetrics: disableMetrics, headless: true, serviceType: "ClusterIP", port: redisPort, @@ -191,7 +195,7 @@ func TestGenerateServiceDef(t *testing.T) { "role": "redis", }, }, - enableMetrics: true, + enableMetrics: defaultExporterPortProvider, headless: false, serviceType: "ClusterIP", port: redisPort, @@ -217,7 +221,7 @@ func TestGenerateServiceDef(t *testing.T) { TargetPort: intstr.FromInt(int(redisPort)), Protocol: corev1.ProtocolTCP, }, - *enableMetricsPort(), + *enableMetricsPort(redisExporterPort), }, Selector: map[string]string{"role": "redis"}, ClusterIP: "", @@ -234,3 +238,41 @@ func TestGenerateServiceDef(t *testing.T) { }) } } + +func TestGenerateServiceType(t *testing.T) { + tests := []struct { + name string + serviceType string + expectedType corev1.ServiceType + }{ + { + name: "LoadBalancer service type", + serviceType: "LoadBalancer", + expectedType: corev1.ServiceTypeLoadBalancer, + }, + { + name: "NodePort service type", + serviceType: "NodePort", + expectedType: corev1.ServiceTypeNodePort, + }, + { + name: "ClusterIP service type", + serviceType: "ClusterIP", + expectedType: corev1.ServiceTypeClusterIP, + }, + { + name: "Default service type", + serviceType: "InvalidServiceType", + expectedType: corev1.ServiceTypeClusterIP, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actualType := generateServiceType(tt.serviceType) + if actualType != tt.expectedType { + t.Errorf("Expected service type %v, but got %v", tt.expectedType, actualType) + } + }) + } +} diff --git a/k8sutils/statefulset.go b/k8sutils/statefulset.go index 72cbba5f6..750498464 100644 --- a/k8sutils/statefulset.go +++ b/k8sutils/statefulset.go @@ -8,6 +8,9 @@ import ( "strconv" "strings" + "github.com/OT-CONTAINER-KIT/redis-operator/pkg/util" + "k8s.io/utils/pointer" + commonapi "github.com/OT-CONTAINER-KIT/redis-operator/api" redisv1beta2 "github.com/OT-CONTAINER-KIT/redis-operator/api/v1beta2" "github.com/banzaicloud/k8s-objectmatcher/patch" @@ -58,6 +61,7 @@ type containerParameters struct { RedisExporterImagePullPolicy corev1.PullPolicy RedisExporterResources *corev1.ResourceRequirements RedisExporterEnv *[]corev1.EnvVar + RedisExporterPort *int Role string EnabledPassword *bool SecretName *string @@ -468,12 +472,12 @@ func enableRedisMonitoring(params containerParameters) corev1.Container { Name: redisExporterContainer, Image: params.RedisExporterImage, ImagePullPolicy: params.RedisExporterImagePullPolicy, - Env: getExporterEnvironmentVariables(params.TLSConfig, params.RedisExporterEnv), + Env: getExporterEnvironmentVariables(params), VolumeMounts: getVolumeMount("", nil, false, false, nil, params.AdditionalMountPath, params.TLSConfig, params.ACLConfig), // We need/want the tls-certs but we DON'T need the PVC (if one is available) Ports: []corev1.ContainerPort{ { Name: redisExporterPortName, - ContainerPort: redisExporterPort, + ContainerPort: int32(*util.Coalesce(params.RedisExporterPort, pointer.Int(redisExporterPort))), Protocol: corev1.ProtocolTCP, }, }, @@ -484,9 +488,9 @@ func enableRedisMonitoring(params containerParameters) corev1.Container { return exporterDefinition } -func getExporterEnvironmentVariables(tlsConfig *redisv1beta2.TLSConfig, env *[]corev1.EnvVar) []corev1.EnvVar { +func getExporterEnvironmentVariables(params containerParameters) []corev1.EnvVar { var envVars []corev1.EnvVar - if tlsConfig != nil { + if params.TLSConfig != nil { envVars = append(envVars, corev1.EnvVar{ Name: "REDIS_EXPORTER_TLS_CLIENT_KEY_FILE", Value: "/tls/tls.key", @@ -504,8 +508,14 @@ func getExporterEnvironmentVariables(tlsConfig *redisv1beta2.TLSConfig, env *[]c Value: "true", }) } - if env != nil { - envVars = append(envVars, *env...) + if params.Port != nil { + envVars = append(envVars, corev1.EnvVar{ + Name: "REDIS_EXPORTER_WEB_LISTEN_ADDRESS", + Value: fmt.Sprintf(":%d", *params.RedisExporterPort), + }) + } + if params.RedisExporterEnv != nil { + envVars = append(envVars, *params.RedisExporterEnv...) } return envVars } diff --git a/k8sutils/statefulset_test.go b/k8sutils/statefulset_test.go index 66672b475..925c3f1a3 100644 --- a/k8sutils/statefulset_test.go +++ b/k8sutils/statefulset_test.go @@ -373,24 +373,27 @@ func TestGetEnvironmentVariables(t *testing.T) { func Test_getExporterEnvironmentVariables(t *testing.T) { tests := []struct { name string + params containerParameters tlsConfig *redisv1beta2.TLSConfig envVar *[]corev1.EnvVar expectedEnvironment []corev1.EnvVar }{ { name: "Test with tls enabled and env var", - tlsConfig: &redisv1beta2.TLSConfig{ - TLSConfig: common.TLSConfig{ - CaKeyFile: "test_ca.crt", - CertKeyFile: "test_tls.crt", - KeyFile: "test_tls.key", - Secret: corev1.SecretVolumeSource{ - SecretName: "tls-secret", + params: containerParameters{ + TLSConfig: &redisv1beta2.TLSConfig{ + TLSConfig: common.TLSConfig{ + CaKeyFile: "test_ca.crt", + CertKeyFile: "test_tls.crt", + KeyFile: "test_tls.key", + Secret: corev1.SecretVolumeSource{ + SecretName: "tls-secret", + }, }, }, - }, - envVar: &[]corev1.EnvVar{ - {Name: "TEST_ENV", Value: "test-value"}, + RedisExporterEnv: &[]corev1.EnvVar{ + {Name: "TEST_ENV", Value: "test-value"}, + }, }, expectedEnvironment: []corev1.EnvVar{ {Name: "REDIS_EXPORTER_TLS_CLIENT_KEY_FILE", Value: "/tls/tls.key"}, @@ -403,7 +406,7 @@ func Test_getExporterEnvironmentVariables(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - actualEnvironment := getExporterEnvironmentVariables(tt.tlsConfig, tt.envVar) + actualEnvironment := getExporterEnvironmentVariables(tt.params) assert.ElementsMatch(t, tt.expectedEnvironment, actualEnvironment) }) diff --git a/pkg/util/coalesce_test.go b/pkg/util/coalesce_test.go new file mode 100644 index 000000000..1b3dfffd5 --- /dev/null +++ b/pkg/util/coalesce_test.go @@ -0,0 +1,46 @@ +package util + +import "testing" + +func TestCoalesce(t *testing.T) { + tests := []struct { + name string + val int + defaultVal int + expectedVal int + }{ + { + name: "Value is zero, default value is provided", + val: 0, + defaultVal: 10, + expectedVal: 10, + }, + { + name: "Value is non-zero, default value is provided", + val: 5, + defaultVal: 10, + expectedVal: 5, + }, + { + name: "Value is zero, default value is zero", + val: 0, + defaultVal: 0, + expectedVal: 0, + }, + { + name: "Value is non-zero, default value is zero", + val: 5, + defaultVal: 0, + expectedVal: 5, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actualVal := Coalesce(tt.val, tt.defaultVal) + if actualVal != tt.expectedVal { + t.Errorf("Expected value %v, but got %v", tt.expectedVal, actualVal) + } + }) + } +} diff --git a/tests/e2e-chainsaw/v1beta2/hostnetwork/redis-cluster/chainsaw-test.yaml b/tests/e2e-chainsaw/v1beta2/hostnetwork/redis-cluster/chainsaw-test.yaml index c8086a5ff..c17efa8f1 100644 --- a/tests/e2e-chainsaw/v1beta2/hostnetwork/redis-cluster/chainsaw-test.yaml +++ b/tests/e2e-chainsaw/v1beta2/hostnetwork/redis-cluster/chainsaw-test.yaml @@ -72,6 +72,45 @@ spec: check: ($stdout=='PONG'): true + - name: Curl Exporter from every node + try: + - script: + timeout: 30s + content: | + kubectl get node -o wide | grep 'worker' | awk '{print $6}' | head -n 1 | tail -n 1 | xargs -I {} curl -s -o /dev/null -w '%{http_code}' http://{}:9122 + check: + ($stdout=='200'): true + - script: + timeout: 30s + content: | + kubectl get node -o wide | grep 'worker' | awk '{print $6}' | head -n 2 | tail -n 1 | xargs -I {} curl -s -o /dev/null -w '%{http_code}' http://{}:9122 + check: + ($stdout=='200'): true + - script: + timeout: 30s + content: | + kubectl get node -o wide | grep 'worker' | awk '{print $6}' | head -n 3 | tail -n 1 | xargs -I {} curl -s -o /dev/null -w '%{http_code}' http://{}:9122 + check: + ($stdout=='200'): true + - script: + timeout: 30s + content: | + kubectl get node -o wide | grep 'worker' | awk '{print $6}' | head -n 4 | tail -n 1 | xargs -I {} curl -s -o /dev/null -w '%{http_code}' http://{}:9122 + check: + ($stdout=='200'): true + - script: + timeout: 30s + content: | + kubectl get node -o wide | grep 'worker' | awk '{print $6}' | head -n 5 | tail -n 1 | xargs -I {} curl -s -o /dev/null -w '%{http_code}' http://{}:9122 + check: + ($stdout=='200'): true + - script: + timeout: 30s + content: | + kubectl get node -o wide | grep 'worker' | awk '{print $6}' | head -n 6 | tail -n 1 | xargs -I {} curl -s -o /dev/null -w '%{http_code}' http://{}:9122 + check: + ($stdout=='200'): true + - name: Try saving a key from every node try: - script: diff --git a/tests/e2e-chainsaw/v1beta2/hostnetwork/redis-cluster/cluster.yaml b/tests/e2e-chainsaw/v1beta2/hostnetwork/redis-cluster/cluster.yaml index afe493178..687044052 100644 --- a/tests/e2e-chainsaw/v1beta2/hostnetwork/redis-cluster/cluster.yaml +++ b/tests/e2e-chainsaw/v1beta2/hostnetwork/redis-cluster/cluster.yaml @@ -23,6 +23,7 @@ spec: memory: 128Mi redisExporter: enabled: true + port: 9122 image: quay.io/opstree/redis-exporter:v1.44.0 imagePullPolicy: Always resources: diff --git a/tests/e2e-chainsaw/v1beta2/hostnetwork/redis-cluster/ready-svc.yaml b/tests/e2e-chainsaw/v1beta2/hostnetwork/redis-cluster/ready-svc.yaml index 7dd11eb37..1d248a5d5 100644 --- a/tests/e2e-chainsaw/v1beta2/hostnetwork/redis-cluster/ready-svc.yaml +++ b/tests/e2e-chainsaw/v1beta2/hostnetwork/redis-cluster/ready-svc.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: annotations: - prometheus.io/port: "9121" + prometheus.io/port: "9122" prometheus.io/scrape: "true" redis.opstreelabs.in: "true" redis.opstreelabs.instance: redis-cluster-v1beta2 @@ -34,7 +34,7 @@ apiVersion: v1 kind: Service metadata: annotations: - prometheus.io/port: "9121" + prometheus.io/port: "9122" prometheus.io/scrape: "true" redis.opstreelabs.in: "true" redis.opstreelabs.instance: redis-cluster-v1beta2 @@ -55,9 +55,9 @@ spec: protocol: TCP targetPort: 6380 - name: redis-exporter - port: 9121 + port: 9122 protocol: TCP - targetPort: 9121 + targetPort: 9122 selector: app: redis-cluster-v1beta2-leader redis_setup_type: cluster @@ -70,7 +70,7 @@ apiVersion: v1 kind: Service metadata: annotations: - prometheus.io/port: "9121" + prometheus.io/port: "9122" prometheus.io/scrape: "true" redis.opstreelabs.in: "true" redis.opstreelabs.instance: redis-cluster-v1beta2 @@ -103,7 +103,7 @@ apiVersion: v1 kind: Service metadata: annotations: - prometheus.io/port: "9121" + prometheus.io/port: "9122" prometheus.io/scrape: "true" redis.opstreelabs.in: "true" redis.opstreelabs.instance: redis-cluster-v1beta2 @@ -124,9 +124,9 @@ spec: protocol: TCP targetPort: 6380 - name: redis-exporter - port: 9121 + port: 9122 protocol: TCP - targetPort: 9121 + targetPort: 9122 selector: app: redis-cluster-v1beta2-follower redis_setup_type: cluster @@ -139,7 +139,7 @@ apiVersion: v1 kind: Service metadata: annotations: - prometheus.io/port: "9121" + prometheus.io/port: "9122" prometheus.io/scrape: "true" redis.opstreelabs.in: "true" redis.opstreelabs.instance: redis-cluster-v1beta2 @@ -171,7 +171,7 @@ apiVersion: v1 kind: Service metadata: annotations: - prometheus.io/port: "9121" + prometheus.io/port: "9122" prometheus.io/scrape: "true" redis.opstreelabs.in: "true" redis.opstreelabs.instance: redis-cluster-v1beta2