From 1e80370ec4ad2483681777662fbe275b49353593 Mon Sep 17 00:00:00 2001 From: Todd Yan <84100066+rivToadd@users.noreply.github.com> Date: Wed, 28 Feb 2024 10:43:41 -0800 Subject: [PATCH] feat: Add tests and add feature to update daemonset to have selector (#2665) * feat: Allowing daemonset to have selectors * linting --- .../enable-selectors-daemonset-status.yaml | 16 ++ internal/status/collector/collector.go | 5 +- internal/status/collector/collector_test.go | 192 ++++++++++++++++++ 3 files changed, 212 insertions(+), 1 deletion(-) create mode 100755 .chloggen/enable-selectors-daemonset-status.yaml create mode 100644 internal/status/collector/collector_test.go diff --git a/.chloggen/enable-selectors-daemonset-status.yaml b/.chloggen/enable-selectors-daemonset-status.yaml new file mode 100755 index 0000000000..07e2fa3645 --- /dev/null +++ b/.chloggen/enable-selectors-daemonset-status.yaml @@ -0,0 +1,16 @@ +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. operator, target allocator, github action) +component: operator + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Allow for label selectors on the daemonset through update status + +# One or more tracking issues related to the change +issues: [2605] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: diff --git a/internal/status/collector/collector.go b/internal/status/collector/collector.go index cdebae02d0..091b36231c 100644 --- a/internal/status/collector/collector.go +++ b/internal/status/collector/collector.go @@ -35,8 +35,10 @@ func UpdateCollectorStatus(ctx context.Context, cli client.Client, changed *v1al // a version is not set, otherwise let the upgrade mechanism take care of it! changed.Status.Version = version.OpenTelemetryCollector() } + mode := changed.Spec.Mode - if mode != v1alpha1.ModeDeployment && mode != v1alpha1.ModeStatefulSet { + + if mode == v1alpha1.ModeSidecar { changed.Status.Scale.Replicas = 0 changed.Status.Scale.Selector = "" return nil @@ -91,6 +93,7 @@ func UpdateCollectorStatus(ctx context.Context, cli client.Client, changed *v1al } statusImage = obj.Spec.Template.Spec.Containers[0].Image } + changed.Status.Scale.Replicas = replicas changed.Status.Image = statusImage changed.Status.Scale.StatusReplicas = statusReplicas diff --git a/internal/status/collector/collector_test.go b/internal/status/collector/collector_test.go new file mode 100644 index 0000000000..83312187dd --- /dev/null +++ b/internal/status/collector/collector_test.go @@ -0,0 +1,192 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package collector + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" +) + +func TestUpdateCollectorStatusUnsupported(t *testing.T) { + ctx := context.TODO() + cli := client.Client(fake.NewFakeClient()) + + changed := &v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-sidecar", + Namespace: "default", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Mode: v1alpha1.ModeSidecar, + }, + } + + err := UpdateCollectorStatus(ctx, cli, changed) + assert.NoError(t, err) + + assert.Equal(t, int32(0), changed.Status.Scale.Replicas, "expected replicas to be 0") + assert.Equal(t, "", changed.Status.Scale.Selector, "expected selector to be empty") +} + +func createMockKubernetesClientDeployment() client.Client { + deployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-deployment-collector", + Namespace: "default", + }, + Status: appsv1.DeploymentStatus{ + Replicas: 1, + ReadyReplicas: 1, + }, + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + Image: "app:latest", + }, + }, + }, + }, + }, + } + return fake.NewClientBuilder().WithObjects(deployment).Build() +} + +func TestUpdateCollectorStatusDeploymentMode(t *testing.T) { + ctx := context.TODO() + cli := createMockKubernetesClientDeployment() + + changed := &v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-deployment", + Namespace: "default", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Mode: v1alpha1.ModeDeployment, + }, + } + + err := UpdateCollectorStatus(ctx, cli, changed) + assert.NoError(t, err) + + assert.Equal(t, int32(1), changed.Status.Scale.Replicas, "expected replicas to be 1") + assert.Equal(t, "1/1", changed.Status.Scale.StatusReplicas, "expected status replicas to be 1/1") + assert.Equal(t, "app:latest", changed.Status.Image, "expected image to be app:latest") +} + +func createMockKubernetesClientStatefulset() client.Client { + statefulset := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-statefulset-collector", + Namespace: "default", + }, + Status: appsv1.StatefulSetStatus{ + Replicas: 1, + ReadyReplicas: 1, + }, + Spec: appsv1.StatefulSetSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + Image: "app:latest", + }, + }, + }, + }, + }, + } + return fake.NewClientBuilder().WithObjects(statefulset).Build() +} + +func TestUpdateCollectorStatusStatefulset(t *testing.T) { + ctx := context.TODO() + cli := createMockKubernetesClientStatefulset() + + changed := &v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-statefulset", + Namespace: "default", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Mode: v1alpha1.ModeStatefulSet, + }, + } + + err := UpdateCollectorStatus(ctx, cli, changed) + assert.NoError(t, err) + + assert.Equal(t, int32(1), changed.Status.Scale.Replicas, "expected replicas to be 1") + assert.Equal(t, "1/1", changed.Status.Scale.StatusReplicas, "expected status replicas to be 1/1") + assert.Equal(t, "app:latest", changed.Status.Image, "expected image to be app:latest") +} + +func createMockKubernetesClientDaemonset() client.Client { + daemonset := &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-daemonset-collector", + Namespace: "default", + }, + Spec: appsv1.DaemonSetSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + Image: "app:latest", + }, + }, + }, + }, + }, + } + return fake.NewClientBuilder().WithObjects(daemonset).Build() +} + +func TestUpdateCollectorStatusDaemonsetMode(t *testing.T) { + ctx := context.TODO() + cli := createMockKubernetesClientDaemonset() + + changed := &v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-daemonset", + Namespace: "default", + Labels: map[string]string{ + "customLabel": "customValue", + }, + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Mode: v1alpha1.ModeDaemonSet, + }, + } + + err := UpdateCollectorStatus(ctx, cli, changed) + assert.NoError(t, err) + + assert.Contains(t, changed.Status.Scale.Selector, "customLabel=customValue", "expected selector to contain customlabel=customValue") + assert.Equal(t, "app:latest", changed.Status.Image, "expected image to be app:latest") +}