From 2b339bd5c75f685704fc8563cd2eeba011e64837 Mon Sep 17 00:00:00 2001 From: "adam.orcholski" Date: Wed, 15 Jan 2025 14:28:26 +0100 Subject: [PATCH] adds TelemetryService service reconciler --- .../dynakube/telemetryservice/props.go | 10 + .../validation/dynakube/telemetryservice.go | 27 +++ .../dynakube/telemetryservice_test.go | 28 +++ pkg/api/validation/dynakube/validation.go | 1 + pkg/consts/otelc.go | 2 + pkg/controllers/dynakube/extension/service.go | 14 +- pkg/controllers/dynakube/otelc/reconciler.go | 10 +- .../dynakube/otelc/service/conditions.go | 5 + .../dynakube/otelc/service/config.go | 7 + .../dynakube/otelc/service/reconciler.go | 181 ++++++++++++++++++ .../dynakube/otelc/service/reconciler_test.go | 134 +++++++++++++ pkg/util/kubeobjects/labels/labels.go | 1 + pkg/util/kubeobjects/service/builder.go | 22 +-- pkg/util/kubeobjects/service/builder_test.go | 5 +- 14 files changed, 418 insertions(+), 29 deletions(-) create mode 100644 pkg/controllers/dynakube/otelc/service/conditions.go create mode 100644 pkg/controllers/dynakube/otelc/service/config.go create mode 100644 pkg/controllers/dynakube/otelc/service/reconciler.go create mode 100644 pkg/controllers/dynakube/otelc/service/reconciler_test.go diff --git a/pkg/api/v1beta3/dynakube/telemetryservice/props.go b/pkg/api/v1beta3/dynakube/telemetryservice/props.go index 1282ce4bfb..9f30097651 100644 --- a/pkg/api/v1beta3/dynakube/telemetryservice/props.go +++ b/pkg/api/v1beta3/dynakube/telemetryservice/props.go @@ -1,5 +1,7 @@ package telemetryservice +import "github.com/Dynatrace/dynatrace-operator/pkg/consts" + type Protocol string const ( @@ -35,6 +37,14 @@ func (spec *Spec) GetProtocols() []Protocol { return protocols } +func (ts *TelemetryService) ServiceName(dynakubeName string) string { + if ts.Spec != nil && ts.Spec.ServiceName != "" { + return ts.Spec.ServiceName + } + + return dynakubeName + consts.TelemetryControllerSuffix +} + func (ts *TelemetryService) IsEnabled() bool { return ts.Spec != nil } diff --git a/pkg/api/validation/dynakube/telemetryservice.go b/pkg/api/validation/dynakube/telemetryservice.go index 36ac8ec51f..b65de8b62d 100644 --- a/pkg/api/validation/dynakube/telemetryservice.go +++ b/pkg/api/validation/dynakube/telemetryservice.go @@ -2,6 +2,8 @@ package validation import ( "context" + "fmt" + "k8s.io/apimachinery/pkg/util/validation" "slices" "strings" @@ -13,6 +15,9 @@ const ( errorTelemetryServiceNotEnoughProtocols = `DynaKube's specification enables the TelemetryService feature, at least one Protocol has to be specified.` errorTelemetryServiceUnknownProtocols = `DynaKube's specification enables the TelemetryService feature, unsupported protocols found on the Protocols list.` errorTelemetryServiceDuplicatedProtocols = `DynaKube's specification enables the TelemetryService feature, duplicated protocols found on the Protocols list.` + errorTelemetryServiceNoDNS1053Label = `DynaKube's specification enables the TelemetryService feature, the telemetry service name violates DNS-1035. + [The length limit for the name is %d. Additionally a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character (e.g. 'my-name', or 'abc-123', regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?')] + ` ) func emptyTelemetryServiceProtocolsList(_ context.Context, _ *Validator, dk *dynakube.DynaKube) string { @@ -82,3 +87,25 @@ func duplicatedTelemetryServiceProtocols(_ context.Context, _ *Validator, dk *dy return "" } + +func invalidTelemetryServiceName(_ context.Context, _ *Validator, dk *dynakube.DynaKube) string { + if !dk.TelemetryService().IsEnabled() { + return "" + } + + var errs []string + + if dk.Spec.TelemetryService.ServiceName != "" { + errs = validation.IsDNS1035Label(dk.Spec.TelemetryService.ServiceName) + } + + if len(errs) == 0 { + return "" + } + + return invalidTelemetryServiceNameErrorMessage() +} + +func invalidTelemetryServiceNameErrorMessage() string { + return fmt.Sprintf(errorTelemetryServiceNoDNS1053Label, validation.DNS1035LabelMaxLength) +} diff --git a/pkg/api/validation/dynakube/telemetryservice_test.go b/pkg/api/validation/dynakube/telemetryservice_test.go index 1afa421a61..894f39ea22 100644 --- a/pkg/api/validation/dynakube/telemetryservice_test.go +++ b/pkg/api/validation/dynakube/telemetryservice_test.go @@ -129,4 +129,32 @@ func TestTelemetryServiceProtocols(t *testing.T) { }, }) }) + + t.Run(`service name too long`, func(t *testing.T) { + assertDenied(t, + []string{invalidTelemetryServiceNameErrorMessage()}, + &dynakube.DynaKube{ + ObjectMeta: defaultDynakubeObjectMeta, + Spec: dynakube.DynaKubeSpec{ + APIURL: testApiUrl, + TelemetryService: &telemetryservice.Spec{ + ServiceName: "a123456789012345678901234567890123456789012345678901234567890123", + }, + }, + }) + }) + + t.Run(`service name violates DNS-1035`, func(t *testing.T) { + assertDenied(t, + []string{invalidTelemetryServiceNameErrorMessage()}, + &dynakube.DynaKube{ + ObjectMeta: defaultDynakubeObjectMeta, + Spec: dynakube.DynaKubeSpec{ + APIURL: testApiUrl, + TelemetryService: &telemetryservice.Spec{ + ServiceName: "0123", + }, + }, + }) + }) } diff --git a/pkg/api/validation/dynakube/validation.go b/pkg/api/validation/dynakube/validation.go index 5029808401..4741c9886e 100644 --- a/pkg/api/validation/dynakube/validation.go +++ b/pkg/api/validation/dynakube/validation.go @@ -57,6 +57,7 @@ var ( emptyTelemetryServiceProtocolsList, unknownTelemetryServiceProtocols, duplicatedTelemetryServiceProtocols, + invalidTelemetryServiceName, extensionsWithoutK8SMonitoring, } validatorWarningFuncs = []validatorFunc{ diff --git a/pkg/consts/otelc.go b/pkg/consts/otelc.go index 4e655cc3d9..ce3d05c9a3 100644 --- a/pkg/consts/otelc.go +++ b/pkg/consts/otelc.go @@ -4,4 +4,6 @@ const ( OtelcTokenSecretKey = "otelc.token" OtelcTokenSecretValuePrefix = "dt0x01" OtelCollectorComPort = 14599 + + TelemetryControllerSuffix = "-telemetry" ) diff --git a/pkg/controllers/dynakube/extension/service.go b/pkg/controllers/dynakube/extension/service.go index 147229842a..1477465559 100644 --- a/pkg/controllers/dynakube/extension/service.go +++ b/pkg/controllers/dynakube/extension/service.go @@ -66,17 +66,19 @@ func (r *reconciler) buildService() (*corev1.Service, error) { // TODO: add proper version later on appLabels := labels.NewAppLabels(labels.ExtensionComponentLabel, r.dk.Name, labels.ExtensionComponentLabel, "") - svcPort := corev1.ServicePort{ - Name: r.dk.ExtensionsPortName(), - Port: consts.OtelCollectorComPort, - Protocol: corev1.ProtocolTCP, - TargetPort: intstr.IntOrString{Type: intstr.String, StrVal: consts.ExtensionsCollectorTargetPortName}, + svcPorts := []corev1.ServicePort{ + { + Name: r.dk.ExtensionsPortName(), + Port: consts.OtelCollectorComPort, + Protocol: corev1.ProtocolTCP, + TargetPort: intstr.IntOrString{Type: intstr.String, StrVal: consts.ExtensionsCollectorTargetPortName}, + }, } return service.Build(r.dk, r.dk.ExtensionsServiceName(), appLabels.BuildMatchLabels(), - svcPort, + svcPorts, service.SetLabels(coreLabels.BuildLabels()), service.SetType(corev1.ServiceTypeClusterIP), ) diff --git a/pkg/controllers/dynakube/otelc/reconciler.go b/pkg/controllers/dynakube/otelc/reconciler.go index 85d770bb10..4ae09df19a 100644 --- a/pkg/controllers/dynakube/otelc/reconciler.go +++ b/pkg/controllers/dynakube/otelc/reconciler.go @@ -5,6 +5,7 @@ import ( "github.com/Dynatrace/dynatrace-operator/pkg/api/v1beta3/dynakube" "github.com/Dynatrace/dynatrace-operator/pkg/controllers" + "github.com/Dynatrace/dynatrace-operator/pkg/controllers/dynakube/otelc/service" "github.com/Dynatrace/dynatrace-operator/pkg/controllers/dynakube/otelc/statefulset" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -14,6 +15,7 @@ type Reconciler struct { apiReader client.Reader dk *dynakube.DynaKube statefulsetReconciler controllers.Reconciler + serviceReconciler *service.Reconciler } type ReconcilerBuilder func(client client.Client, apiReader client.Reader, dk *dynakube.DynaKube) controllers.Reconciler @@ -24,11 +26,17 @@ func NewReconciler(client client.Client, apiReader client.Reader, dk *dynakube.D apiReader: apiReader, dk: dk, statefulsetReconciler: statefulset.NewReconciler(client, apiReader, dk), + serviceReconciler: service.NewReconciler(client, apiReader, dk), } } func (r *Reconciler) Reconcile(ctx context.Context) error { - err := r.statefulsetReconciler.Reconcile(ctx) + err := r.serviceReconciler.Reconcile(ctx) + if err != nil { + return err + } + + err = r.statefulsetReconciler.Reconcile(ctx) if err != nil { log.Info("failed to reconcile Dynatrace OTELc statefulset") diff --git a/pkg/controllers/dynakube/otelc/service/conditions.go b/pkg/controllers/dynakube/otelc/service/conditions.go new file mode 100644 index 0000000000..c6a49c2d7a --- /dev/null +++ b/pkg/controllers/dynakube/otelc/service/conditions.go @@ -0,0 +1,5 @@ +package service + +const ( + serviceConditionType = "OTELCService" +) diff --git a/pkg/controllers/dynakube/otelc/service/config.go b/pkg/controllers/dynakube/otelc/service/config.go new file mode 100644 index 0000000000..16130d363b --- /dev/null +++ b/pkg/controllers/dynakube/otelc/service/config.go @@ -0,0 +1,7 @@ +package service + +import "github.com/Dynatrace/dynatrace-operator/pkg/logd" + +var ( + log = logd.Get().WithName("otelc-service") +) diff --git a/pkg/controllers/dynakube/otelc/service/reconciler.go b/pkg/controllers/dynakube/otelc/service/reconciler.go new file mode 100644 index 0000000000..b4a59da906 --- /dev/null +++ b/pkg/controllers/dynakube/otelc/service/reconciler.go @@ -0,0 +1,181 @@ +package service + +import ( + "github.com/Dynatrace/dynatrace-operator/pkg/api/v1beta3/dynakube" + "github.com/Dynatrace/dynatrace-operator/pkg/api/v1beta3/dynakube/telemetryservice" + "github.com/Dynatrace/dynatrace-operator/pkg/util/conditions" + "github.com/Dynatrace/dynatrace-operator/pkg/util/kubeobjects/labels" + "github.com/Dynatrace/dynatrace-operator/pkg/util/kubeobjects/service" + "golang.org/x/net/context" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/util/intstr" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const ( + portNameZipkin = "zipkin" + portNameOtlpGrpc = "otlp-grpc" + portNameOtlpHttp = "otlp-http" + portNameJaegerGrpc = "jaeger-grpc" + portNameJaegerThriftBinary = "jaeger-thrift-binary" + portNameJaegerThriftCompact = "jaeger-thrift-compact" + portNameJaegerThriftHttp = "jaeger-thrift-http" + portNameStatsd = "statsd" +) + +type Reconciler struct { + client client.Client + apiReader client.Reader + dk *dynakube.DynaKube +} + +type ReconcilerBuilder func(client client.Client, apiReader client.Reader, dk *dynakube.DynaKube) *Reconciler + +func NewReconciler(client client.Client, apiReader client.Reader, dk *dynakube.DynaKube) *Reconciler { + return &Reconciler{ + client: client, + dk: dk, + apiReader: apiReader, + } +} + +func (r *Reconciler) Reconcile(ctx context.Context) error { + if !r.dk.TelemetryService().IsEnabled() { + if meta.FindStatusCondition(*r.dk.Conditions(), serviceConditionType) == nil { + return nil + } + defer meta.RemoveStatusCondition(r.dk.Conditions(), serviceConditionType) + + svc, err := r.buildService() + if err != nil { + log.Error(err, "could not build service during cleanup") + + return err + } + + err = service.Query(r.client, r.apiReader, log).Delete(ctx, svc) + + if err != nil { + log.Error(err, "failed to clean up extension service") + + return nil + } + + return nil + } + + return r.createOrUpdateService(ctx) +} + +func (r *Reconciler) createOrUpdateService(ctx context.Context) error { + newService, err := r.buildService() + if err != nil { + conditions.SetServiceGenFailed(r.dk.Conditions(), serviceConditionType, err) + + return err + } + + _, err = service.Query(r.client, r.apiReader, log).CreateOrUpdate(ctx, newService) + if err != nil { + log.Info("failed to create/update otelc service") + conditions.SetKubeApiError(r.dk.Conditions(), serviceConditionType, err) + + return err + } + + conditions.SetServiceCreated(r.dk.Conditions(), serviceConditionType, r.dk.TelemetryService().ServiceName(r.dk.Name)) + + return nil +} + +func (r *Reconciler) buildService() (*corev1.Service, error) { + coreLabels := labels.NewCoreLabels(r.dk.Name, labels.OtelCComponentLabel) + // TODO: add proper version later on + appLabels := labels.NewAppLabels(labels.OtelCComponentLabel, r.dk.Name, labels.OtelCComponentLabel, "") + + var svcPorts []corev1.ServicePort + if r.dk.TelemetryService().IsEnabled() { + svcPorts = buildServicePortList(r.dk.TelemetryService().GetProtocols()) + } + + return service.Build(r.dk, + r.dk.TelemetryService().ServiceName(r.dk.Name), + appLabels.BuildMatchLabels(), + svcPorts, + service.SetLabels(coreLabels.BuildLabels()), + service.SetType(corev1.ServiceTypeClusterIP), + ) +} + +func buildServicePortList(protocols []telemetryservice.Protocol) []corev1.ServicePort { + if len(protocols) == 0 { + return nil + } + + svcPorts := make([]corev1.ServicePort, 0) + + for _, protocol := range protocols { + switch protocol { + case telemetryservice.ZipkinProtocol: + svcPorts = append(svcPorts, corev1.ServicePort{ + Name: portNameZipkin, + Port: 9411, + Protocol: corev1.ProtocolTCP, + TargetPort: intstr.FromInt32(9411), + }) + case telemetryservice.OtlpProtocol: + svcPorts = append(svcPorts, + corev1.ServicePort{ + Name: portNameOtlpGrpc, + Port: 4317, + Protocol: corev1.ProtocolTCP, + TargetPort: intstr.FromInt32(4317), + }, + corev1.ServicePort{ + Name: portNameOtlpHttp, + Port: 4318, + Protocol: corev1.ProtocolTCP, + TargetPort: intstr.FromInt32(4318), + }) + case telemetryservice.JaegerProtocol: + svcPorts = append(svcPorts, + corev1.ServicePort{ + Name: portNameJaegerGrpc, + Port: 14250, + Protocol: corev1.ProtocolTCP, + TargetPort: intstr.FromInt32(14250), + }, + corev1.ServicePort{ + Name: portNameJaegerThriftBinary, + Port: 6832, + Protocol: corev1.ProtocolTCP, + TargetPort: intstr.FromInt32(6832), + }, + corev1.ServicePort{ + Name: portNameJaegerThriftCompact, + Port: 6831, + Protocol: corev1.ProtocolTCP, + TargetPort: intstr.FromInt32(6831), + }, + corev1.ServicePort{ + Name: portNameJaegerThriftHttp, + Port: 14268, + Protocol: corev1.ProtocolTCP, + TargetPort: intstr.FromInt32(14268), + }) + case telemetryservice.StatsdProtocol: + svcPorts = append(svcPorts, + corev1.ServicePort{ + Name: portNameStatsd, + Port: 8125, + Protocol: corev1.ProtocolTCP, + TargetPort: intstr.FromInt32(8125), + }) + default: + log.Info("unknown telemetry service protocol ignored", "protocol", protocol) + } + } + + return svcPorts +} diff --git a/pkg/controllers/dynakube/otelc/service/reconciler_test.go b/pkg/controllers/dynakube/otelc/service/reconciler_test.go new file mode 100644 index 0000000000..47fc3be155 --- /dev/null +++ b/pkg/controllers/dynakube/otelc/service/reconciler_test.go @@ -0,0 +1,134 @@ +package service + +import ( + "context" + "testing" + + "github.com/Dynatrace/dynatrace-operator/pkg/api/v1beta3/dynakube" + "github.com/Dynatrace/dynatrace-operator/pkg/api/v1beta3/dynakube/telemetryservice" + "github.com/Dynatrace/dynatrace-operator/pkg/util/conditions" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +const ( + testDynakubeName = "dynakube" + testNamespaceName = "dynatrace" + testServiceName = "test-service-name" +) + +func getTestDynakube(telemetryServiceSpec *telemetryservice.Spec) *dynakube.DynaKube { + return &dynakube.DynaKube{ + ObjectMeta: metav1.ObjectMeta{ + Name: testDynakubeName, + Namespace: testNamespaceName, + }, + Spec: dynakube.DynaKubeSpec{ + TelemetryService: telemetryServiceSpec, + }, + Status: dynakube.DynaKubeStatus{}, + } +} + +func TestService(t *testing.T) { + t.Run("create service if it does not exist", func(t *testing.T) { + mockK8sClient := fake.NewFakeClient() + dk := getTestDynakube(&telemetryservice.Spec{}) + err := NewReconciler(mockK8sClient, mockK8sClient, dk).Reconcile(context.Background()) + require.NoError(t, err) + + service := &corev1.Service{} + err = mockK8sClient.Get(context.Background(), client.ObjectKey{Name: dk.TelemetryService().ServiceName(dk.Name), Namespace: dk.Namespace}, service) + require.NoError(t, err) + + require.Len(t, service.Spec.Ports, 8) + assert.Equal(t, portNameOtlpGrpc, service.Spec.Ports[0].Name) + assert.Equal(t, portNameOtlpHttp, service.Spec.Ports[1].Name) + assert.Equal(t, portNameZipkin, service.Spec.Ports[2].Name) + assert.Equal(t, portNameJaegerGrpc, service.Spec.Ports[3].Name) + assert.Equal(t, portNameJaegerThriftBinary, service.Spec.Ports[4].Name) + assert.Equal(t, portNameJaegerThriftCompact, service.Spec.Ports[5].Name) + assert.Equal(t, portNameJaegerThriftHttp, service.Spec.Ports[6].Name) + assert.Equal(t, portNameStatsd, service.Spec.Ports[7].Name) + + require.Len(t, dk.Status.Conditions, 1) + assert.Equal(t, serviceConditionType, dk.Status.Conditions[0].Type) + assert.Equal(t, conditions.ServiceCreatedReason, dk.Status.Conditions[0].Reason) + assert.Equal(t, metav1.ConditionTrue, dk.Status.Conditions[0].Status) + }) + t.Run("create service with custom name if it does not exist", func(t *testing.T) { + mockK8sClient := fake.NewFakeClient() + dk := getTestDynakube(&telemetryservice.Spec{ + ServiceName: testServiceName, + }) + err := NewReconciler(mockK8sClient, mockK8sClient, dk).Reconcile(context.Background()) + require.NoError(t, err) + + service := &corev1.Service{} + err = mockK8sClient.Get(context.Background(), client.ObjectKey{Name: testServiceName, Namespace: dk.Namespace}, service) + require.NoError(t, err) + + assert.Len(t, service.Spec.Ports, 8) + + require.Len(t, dk.Status.Conditions, 1) + assert.Equal(t, serviceConditionType, dk.Status.Conditions[0].Type) + assert.Equal(t, conditions.ServiceCreatedReason, dk.Status.Conditions[0].Reason) + assert.Equal(t, metav1.ConditionTrue, dk.Status.Conditions[0].Status) + }) + t.Run("remove service if it is not needed", func(t *testing.T) { + dk := getTestDynakube(nil) + dk.Status.Conditions = []metav1.Condition{ + { + Type: serviceConditionType, + }, + } + + mockK8sClient := fake.NewFakeClient() + err := mockK8sClient.Create(context.Background(), &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dk.TelemetryService().ServiceName(dk.Name), + Namespace: dk.Namespace, + }, + }) + require.NoError(t, err) + + err = NewReconciler(mockK8sClient, mockK8sClient, dk).Reconcile(context.Background()) + require.NoError(t, err) + + service := &corev1.Service{} + err = mockK8sClient.Get(context.Background(), client.ObjectKey{Name: dk.TelemetryService().ServiceName(dk.Name), Namespace: dk.Namespace}, service) + require.Error(t, err) + assert.True(t, k8serrors.IsNotFound(err)) + + require.Empty(t, dk.Status.Conditions) + }) + t.Run("create service for specified protocols", func(t *testing.T) { + mockK8sClient := fake.NewFakeClient() + dk := getTestDynakube(&telemetryservice.Spec{ + Protocols: []string{ + string(telemetryservice.ZipkinProtocol), + string(telemetryservice.StatsdProtocol), + }, + }) + err := NewReconciler(mockK8sClient, mockK8sClient, dk).Reconcile(context.Background()) + require.NoError(t, err) + + service := &corev1.Service{} + err = mockK8sClient.Get(context.Background(), client.ObjectKey{Name: dk.TelemetryService().ServiceName(dk.Name), Namespace: dk.Namespace}, service) + require.NoError(t, err) + + require.Len(t, service.Spec.Ports, 2) + assert.Equal(t, portNameZipkin, service.Spec.Ports[0].Name) + assert.Equal(t, portNameStatsd, service.Spec.Ports[1].Name) + + require.Len(t, dk.Status.Conditions, 1) + assert.Equal(t, serviceConditionType, dk.Status.Conditions[0].Type) + assert.Equal(t, conditions.ServiceCreatedReason, dk.Status.Conditions[0].Reason) + assert.Equal(t, metav1.ConditionTrue, dk.Status.Conditions[0].Status) + }) +} diff --git a/pkg/util/kubeobjects/labels/labels.go b/pkg/util/kubeobjects/labels/labels.go index 0ac2bee445..29e76e16f7 100644 --- a/pkg/util/kubeobjects/labels/labels.go +++ b/pkg/util/kubeobjects/labels/labels.go @@ -22,6 +22,7 @@ const ( EdgeConnectComponentLabel = "edgeconnect" ExtensionComponentLabel = "dynatrace-extensions-controller" CollectorComponentLabel = "dynatrace-extensions-collector" + OtelCComponentLabel = "dynatrace-otelc-controller" ) type AppMatchLabels struct { diff --git a/pkg/util/kubeobjects/service/builder.go b/pkg/util/kubeobjects/service/builder.go index 947d63beae..45785ab63c 100644 --- a/pkg/util/kubeobjects/service/builder.go +++ b/pkg/util/kubeobjects/service/builder.go @@ -15,7 +15,7 @@ var ( SetLabels = builder.SetLabels[*corev1.Service] ) -func Build(owner metav1.Object, name string, selectorLabels map[string]string, svcPort corev1.ServicePort, options ...builder.Option[*corev1.Service]) (*corev1.Service, error) { +func Build(owner metav1.Object, name string, selectorLabels map[string]string, svcPort []corev1.ServicePort, options ...builder.Option[*corev1.Service]) (*corev1.Service, error) { neededOpts := []builder.Option[*corev1.Service]{ setName(name), setPorts(svcPort), @@ -27,25 +27,9 @@ func Build(owner metav1.Object, name string, selectorLabels map[string]string, s return builder.Build(owner, &corev1.Service{}, neededOpts...) } -func setPorts(svcPort corev1.ServicePort) builder.Option[*corev1.Service] { +func setPorts(svcPorts []corev1.ServicePort) builder.Option[*corev1.Service] { return func(s *corev1.Service) { - targetIndex := 0 - for index := range s.Spec.Ports { - if s.Spec.Ports[targetIndex].Name == svcPort.Name { - targetIndex = index - - break - } - } - - if targetIndex == 0 { - s.Spec.Ports = make([]corev1.ServicePort, 1) - } - - s.Spec.Ports[targetIndex].Name = svcPort.Name - s.Spec.Ports[targetIndex].Port = svcPort.Port - s.Spec.Ports[targetIndex].Protocol = svcPort.Protocol - s.Spec.Ports[targetIndex].TargetPort = svcPort.TargetPort + s.Spec.Ports = svcPorts } } diff --git a/pkg/util/kubeobjects/service/builder_test.go b/pkg/util/kubeobjects/service/builder_test.go index 6d804d4fae..37e192b425 100644 --- a/pkg/util/kubeobjects/service/builder_test.go +++ b/pkg/util/kubeobjects/service/builder_test.go @@ -6,7 +6,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -35,7 +34,7 @@ func TestServiceBuilder(t *testing.T) { service, err := Build(createDeployment(), testServiceName, labels, - corev1.ServicePort{}, + nil, setNamespace(testNamespace)) require.NoError(t, err) require.Len(t, service.OwnerReferences, 1) @@ -47,7 +46,7 @@ func TestServiceBuilder(t *testing.T) { secret, err := Build(createDeployment(), testServiceName, labels, - corev1.ServicePort{}, + nil, SetLabels(labels), setNamespace(testNamespace), )