Repository: Azure/ARO-RP
Pull request #1903 (2022-01-18 03:38:07)

Allow fluentbit/mdsd image setting via feature flags
### Which issue this PR addresses:

for upcoming release

### What this PR does / why we need it:

Allows the fluentbit/mdsd image deployed to be overridden in the ARO Operator's config.

### Test plan for issue:

Contains tests

### Is there any documentation that needs to be updated for this PR?
N/A



Patch:
diff --git a/pkg/operator/apis/aro.openshift.io/v1alpha1/cluster_types.go b/pkg/operator/apis/aro.openshift.io/v1alpha1/cluster_types.go
index fad6bdc02..f41c78b16 100644
--- a/pkg/operator/apis/aro.openshift.io/v1alpha1/cluster_types.go
+++ b/pkg/operator/apis/aro.openshift.io/v1alpha1/cluster_types.go
@@ -52,6 +52,14 @@ type InternetCheckerSpec struct {
 
 type OperatorFlags map[string]string
 
+func (f OperatorFlags) GetWithDefault(key string, sentinel string) string {
+	val, ext := f[key]
+	if !ext {
+		return sentinel
+	}
+	return val
+}
+
 func (f OperatorFlags) GetSimpleBoolean(key string) bool {
 	v, ext := f[key]
 	if ext {
diff --git a/pkg/operator/controllers/genevalogging/genevalogging.go b/pkg/operator/controllers/genevalogging/genevalogging.go
index 409c2e4c2..9932870a0 100644
--- a/pkg/operator/controllers/genevalogging/genevalogging.go
+++ b/pkg/operator/controllers/genevalogging/genevalogging.go
@@ -41,6 +41,9 @@ func (r *Reconciler) daemonset(cluster *arov1alpha1.Cluster) (*appsv1.DaemonSet,
 		return nil, err
 	}
 
+	fluentbitPullspec := cluster.Spec.OperatorFlags.GetWithDefault(FLUENTBIT_PULLSPEC, version.FluentbitImage(cluster.Spec.ACRDomain))
+	mdsdPullspec := cluster.Spec.OperatorFlags.GetWithDefault(MDSD_PULLSPEC, version.MdsdImage(cluster.Spec.ACRDomain))
+
 	return &appsv1.DaemonSet{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      "mdsd",
@@ -115,7 +118,7 @@ func (r *Reconciler) daemonset(cluster *arov1alpha1.Cluster) (*appsv1.DaemonSet,
 					Containers: []corev1.Container{
 						{
 							Name:  "fluentbit",
-							Image: version.FluentbitImage(cluster.Spec.ACRDomain),
+							Image: fluentbitPullspec,
 							Command: []string{
 								"/opt/td-agent-bit/bin/td-agent-bit",
 							},
@@ -152,7 +155,7 @@ func (r *Reconciler) daemonset(cluster *arov1alpha1.Cluster) (*appsv1.DaemonSet,
 						},
 						{
 							Name:  "mdsd",
-							Image: version.MdsdImage(cluster.Spec.ACRDomain),
+							Image: mdsdPullspec,
 							Command: []string{
 								"/usr/sbin/mdsd",
 							},
diff --git a/pkg/operator/controllers/genevalogging/genevalogging_controller.go b/pkg/operator/controllers/genevalogging/genevalogging_controller.go
index a0a31cfe7..6d99ecfb3 100644
--- a/pkg/operator/controllers/genevalogging/genevalogging_controller.go
+++ b/pkg/operator/controllers/genevalogging/genevalogging_controller.go
@@ -30,6 +30,10 @@ import (
 const (
 	CONFIG_NAMESPACE string = "aro.genevalogging"
 	ENABLED          string = CONFIG_NAMESPACE + ".enabled"
+	// full pullspec of fluentbit image
+	FLUENTBIT_PULLSPEC string = CONFIG_NAMESPACE + ".fluentbit.pullSpec"
+	// full pullspec of mdsd image
+	MDSD_PULLSPEC string = CONFIG_NAMESPACE + ".mdsd.pullSpec"
 )
 
 // Reconciler reconciles a Cluster object


Test patch:
diff --git a/pkg/operator/controllers/genevalogging/genevalogging_test.go b/pkg/operator/controllers/genevalogging/genevalogging_test.go
new file mode 100644
index 000000000..9282cac56
--- /dev/null
+++ b/pkg/operator/controllers/genevalogging/genevalogging_test.go
@@ -0,0 +1,174 @@
+package genevalogging
+
+// Copyright (c) Microsoft Corporation.
+// Licensed under the Apache License 2.0.
+
+import (
+	"errors"
+	"fmt"
+	"testing"
+
+	"github.com/go-test/deep"
+	operatorv1 "github.com/openshift/api/operator/v1"
+	"github.com/sirupsen/logrus"
+	appsv1 "k8s.io/api/apps/v1"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	ctrl "sigs.k8s.io/controller-runtime"
+
+	arov1alpha1 "github.com/Azure/ARO-RP/pkg/operator/apis/aro.openshift.io/v1alpha1"
+	arofake "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned/fake"
+	"github.com/Azure/ARO-RP/pkg/util/version"
+	testdatabase "github.com/Azure/ARO-RP/test/database"
+)
+
+func getContainer(d *appsv1.DaemonSet, containerName string) (corev1.Container, error) {
+	for _, container := range d.Spec.Template.Spec.Containers {
+		if container.Name == containerName {
+			return container, nil
+		}
+	}
+	return corev1.Container{}, errors.New("not found")
+}
+
+func TestGenevaLoggingDaemonset(t *testing.T) {
+	tests := []struct {
+		name              string
+		request           ctrl.Request
+		arocli            *arofake.Clientset
+		operatorFlags     arov1alpha1.OperatorFlags
+		validateDaemonset func(*appsv1.DaemonSet) []error
+	}{
+		{
+			name: "no flags given",
+			operatorFlags: arov1alpha1.OperatorFlags{
+				ENABLED: "true",
+			},
+			validateDaemonset: func(d *appsv1.DaemonSet) (errs []error) {
+				if len(d.Spec.Template.Spec.Containers) != 2 {
+					errs = append(errs, fmt.Errorf("expected 2 containers, got %d", len(d.Spec.Template.Spec.Containers)))
+				}
+
+				// we want the default fluentbit image
+				fluentbit, err := getContainer(d, "fluentbit")
+				if err != nil {
+					errs = append(errs, err)
+					return
+				}
+				for _, err := range deep.Equal(fluentbit.Image, version.FluentbitImage("acrDomain")) {
+					errs = append(errs, errors.New(err))
+				}
+
+				// we want the default mdsd image
+				mdsd, err := getContainer(d, "mdsd")
+				if err != nil {
+					errs = append(errs, err)
+					return
+				}
+				for _, err := range deep.Equal(mdsd.Image, version.MdsdImage("acrDomain")) {
+					errs = append(errs, errors.New(err))
+				}
+
+				return
+			},
+		},
+		{
+			name: "fluentbit changed",
+			operatorFlags: arov1alpha1.OperatorFlags{
+				ENABLED:            "true",
+				FLUENTBIT_PULLSPEC: "otherurl/fluentbit",
+			},
+			validateDaemonset: func(d *appsv1.DaemonSet) (errs []error) {
+				if len(d.Spec.Template.Spec.Containers) != 2 {
+					errs = append(errs, fmt.Errorf("expected 2 containers, got %d", len(d.Spec.Template.Spec.Containers)))
+				}
+
+				// we want our fluentbit image
+				fluentbit, err := getContainer(d, "fluentbit")
+				if err != nil {
+					errs = append(errs, err)
+					return
+				}
+				for _, err := range deep.Equal(fluentbit.Image, "otherurl/fluentbit") {
+					errs = append(errs, errors.New(err))
+				}
+
+				// we want the default mdsd image
+				mdsd, err := getContainer(d, "mdsd")
+				if err != nil {
+					errs = append(errs, err)
+					return
+				}
+				for _, err := range deep.Equal(mdsd.Image, version.MdsdImage("acrDomain")) {
+					errs = append(errs, errors.New(err))
+				}
+
+				return
+			},
+		},
+		{
+			name: "mdsd changed",
+			operatorFlags: arov1alpha1.OperatorFlags{
+				ENABLED:       "true",
+				MDSD_PULLSPEC: "otherurl/mdsd",
+			},
+			validateDaemonset: func(d *appsv1.DaemonSet) (errs []error) {
+				if len(d.Spec.Template.Spec.Containers) != 2 {
+					errs = append(errs, fmt.Errorf("expected 2 containers, got %d", len(d.Spec.Template.Spec.Containers)))
+				}
+
+				// we want the default fluentbit image
+				fluentbit, err := getContainer(d, "fluentbit")
+				if err != nil {
+					errs = append(errs, err)
+					return
+				}
+				for _, err := range deep.Equal(fluentbit.Image, version.FluentbitImage("acrDomain")) {
+					errs = append(errs, errors.New(err))
+				}
+
+				// we want the default mdsd image
+				mdsd, err := getContainer(d, "mdsd")
+				if err != nil {
+					errs = append(errs, err)
+					return
+				}
+				for _, err := range deep.Equal(mdsd.Image, "otherurl/mdsd") {
+					errs = append(errs, errors.New(err))
+				}
+
+				return
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+
+			cluster := &arov1alpha1.Cluster{
+				ObjectMeta: metav1.ObjectMeta{Name: "cluster"},
+				Status:     arov1alpha1.ClusterStatus{Conditions: []operatorv1.OperatorCondition{}},
+				Spec: arov1alpha1.ClusterSpec{
+					ResourceID:    testdatabase.GetResourcePath("00000000-0000-0000-0000-000000000000", "testcluster"),
+					OperatorFlags: tt.operatorFlags,
+					ACRDomain:     "acrDomain",
+				},
+			}
+
+			r := &Reconciler{
+				log:    logrus.NewEntry(logrus.StandardLogger()),
+				arocli: arofake.NewSimpleClientset(cluster),
+			}
+
+			daemonset, err := r.daemonset(cluster)
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			errs := tt.validateDaemonset(daemonset)
+			for _, err := range errs {
+				t.Error(err)
+			}
+
+		})
+	}
+}