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")
+}