From d614d3d6cc1d40664215f79fc2c2f38af03a9996 Mon Sep 17 00:00:00 2001 From: Dileep Ranganathan Date: Tue, 8 Oct 2019 17:06:22 -0700 Subject: [PATCH] Collectd Operator Unit Tests Added unit tests for Collectd operator. Issue-ID: ONAPARC-461 Signed-off-by: Dileep Ranganathan Change-Id: Ib50c16eaefc661e077d3477cbe8f2e4e62bbda80 --- vnfs/DAaaS/.gitignore | 1 + .../DAaaS/microservices/collectd-operator/Makefile | 2 +- vnfs/DAaaS/microservices/collectd-operator/go.mod | 1 + vnfs/DAaaS/microservices/collectd-operator/go.sum | 1 + .../pkg/apis/onap/v1alpha1/collectdglobal_types.go | 13 + .../pkg/apis/onap/v1alpha1/collectdplugin_types.go | 13 + .../collectdglobal/collectdglobal_controller.go | 13 + .../collectdglobal_controller_test.go | 536 +++++++++++++++++++++ .../collectdplugin/collectdplugin_controller.go | 15 +- .../collectdplugin_controller_test.go | 370 ++++++++++++++ .../pkg/controller/utils/collectdutils.go | 13 + .../pkg/controller/utils/dsutils.go | 13 + .../pkg/controller/utils/predicate.go | 25 +- 13 files changed, 1001 insertions(+), 15 deletions(-) create mode 100644 vnfs/DAaaS/microservices/collectd-operator/pkg/controller/collectdglobal/collectdglobal_controller_test.go create mode 100644 vnfs/DAaaS/microservices/collectd-operator/pkg/controller/collectdplugin/collectdplugin_controller_test.go diff --git a/vnfs/DAaaS/.gitignore b/vnfs/DAaaS/.gitignore index b13f7a20..ddef8004 100644 --- a/vnfs/DAaaS/.gitignore +++ b/vnfs/DAaaS/.gitignore @@ -1,5 +1,6 @@ *.iml .idea +.vscode target/ dependency-reduced-pom.xml File2hdfsApp.java diff --git a/vnfs/DAaaS/microservices/collectd-operator/Makefile b/vnfs/DAaaS/microservices/collectd-operator/Makefile index c09f4c24..9c023521 100644 --- a/vnfs/DAaaS/microservices/collectd-operator/Makefile +++ b/vnfs/DAaaS/microservices/collectd-operator/Makefile @@ -47,7 +47,7 @@ debug: @go build -o ${COP_LOCAL} collectd-operator/cmd/manager ## deploy: Build Dockerfile and publish to repository -deploy: build publish +deploy: build test publish ## publish: Push docker image to repository publish: diff --git a/vnfs/DAaaS/microservices/collectd-operator/go.mod b/vnfs/DAaaS/microservices/collectd-operator/go.mod index 525d4e3f..fefba3d5 100644 --- a/vnfs/DAaaS/microservices/collectd-operator/go.mod +++ b/vnfs/DAaaS/microservices/collectd-operator/go.mod @@ -6,6 +6,7 @@ require ( github.com/go-openapi/spec v0.19.0 github.com/operator-framework/operator-sdk v0.9.1-0.20190807211748-fe8c81ad98c0 github.com/spf13/pflag v1.0.3 + github.com/stretchr/testify v1.3.0 k8s.io/api v0.0.0-20190612125737-db0771252981 k8s.io/apimachinery v0.0.0-20190612125636-6a5db36e93ad k8s.io/client-go v11.0.0+incompatible diff --git a/vnfs/DAaaS/microservices/collectd-operator/go.sum b/vnfs/DAaaS/microservices/collectd-operator/go.sum index b5b3019c..4a53436e 100644 --- a/vnfs/DAaaS/microservices/collectd-operator/go.sum +++ b/vnfs/DAaaS/microservices/collectd-operator/go.sum @@ -83,6 +83,7 @@ github.com/emicklei/go-restful v2.9.3+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT github.com/emicklei/go-restful-swagger12 v0.0.0-20170926063155-7524189396c6/go.mod h1:qr0VowGBT4CS4Q8vFF8BSeKz34PuqKGxs/L0IAQA9DQ= github.com/evanphx/json-patch v3.0.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.0.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.1.0+incompatible h1:K1MDoo4AZ4wU0GIU/fPmtZg7VpzLjCxu+UwBD1FvwOc= github.com/evanphx/json-patch v4.1.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= diff --git a/vnfs/DAaaS/microservices/collectd-operator/pkg/apis/onap/v1alpha1/collectdglobal_types.go b/vnfs/DAaaS/microservices/collectd-operator/pkg/apis/onap/v1alpha1/collectdglobal_types.go index d5c69fbd..f14294e2 100644 --- a/vnfs/DAaaS/microservices/collectd-operator/pkg/apis/onap/v1alpha1/collectdglobal_types.go +++ b/vnfs/DAaaS/microservices/collectd-operator/pkg/apis/onap/v1alpha1/collectdglobal_types.go @@ -1,3 +1,16 @@ +/* +Copyright 2019 Intel Corporation. +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 v1alpha1 import ( diff --git a/vnfs/DAaaS/microservices/collectd-operator/pkg/apis/onap/v1alpha1/collectdplugin_types.go b/vnfs/DAaaS/microservices/collectd-operator/pkg/apis/onap/v1alpha1/collectdplugin_types.go index eb338639..d814592b 100644 --- a/vnfs/DAaaS/microservices/collectd-operator/pkg/apis/onap/v1alpha1/collectdplugin_types.go +++ b/vnfs/DAaaS/microservices/collectd-operator/pkg/apis/onap/v1alpha1/collectdplugin_types.go @@ -1,3 +1,16 @@ +/* +Copyright 2019 Intel Corporation. +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 v1alpha1 import ( diff --git a/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/collectdglobal/collectdglobal_controller.go b/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/collectdglobal/collectdglobal_controller.go index 8c6023b8..d1bd5c6a 100644 --- a/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/collectdglobal/collectdglobal_controller.go +++ b/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/collectdglobal/collectdglobal_controller.go @@ -1,3 +1,16 @@ +/* +Copyright 2019 Intel Corporation. +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 collectdglobal import ( diff --git a/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/collectdglobal/collectdglobal_controller_test.go b/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/collectdglobal/collectdglobal_controller_test.go new file mode 100644 index 00000000..2410fe6a --- /dev/null +++ b/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/collectdglobal/collectdglobal_controller_test.go @@ -0,0 +1,536 @@ +/* +Copyright 2019 Intel Corporation. +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 collectdglobal + +import ( + "context" + "os" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + onapv1alpha1 "collectd-operator/pkg/apis/onap/v1alpha1" + utils "collectd-operator/pkg/controller/utils" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" +) + +const ( + name = "collectd-operator" + namespace = "test1" + gOpts = "BaseDir \"/opt/collectd/var/lib/collectd\"\nPIDFile \"/opt/collectd/var/run/collectd.pid\"\nPluginDir" + + "\"/opt/collectd/lib/collectd\"\nTypesDB \"/opt/collectd/share/collectd/types.db\"\n" + + "#Hostname \"localhost\"\nInterval 1 \nWriteQueueLimitHigh" + + "10000000\nWriteQueueLimitLow 8000000\nTimeout \"10\"\nReadThreads \"50\"\nWriteThreads" + + "\"50\"\n\n#Enable plugins:\n\nLoadPlugin cpufreq\nLoadPlugin ipmi\nLoadPlugin" + + "turbostat\nLoadPlugin irq\nLoadPlugin memcached\nLoadPlugin memory\nLoadPlugin" + + "processes\nLoadPlugin load\n" + typesCmName = "types-configmap" + watchLabels = "app=collectd" +) + +var ( + one = intstr.FromInt(1) + ls, _ = labels.ConvertSelectorToLabelsMap(watchLabels) + cm = &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cp-config-map", + Namespace: namespace, + Labels: ls, + }, + Data: map[string]string{ + "collectd.conf": "", + }, + } + typesCm = &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: typesCmName, + Namespace: namespace, + Labels: ls, + }, + Data: map[string]string{ + "types.db": "types.db data", + }, + } + ds = &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cp-collectd", + Namespace: namespace, + Labels: ls, + }, + Spec: appsv1.DaemonSetSpec{ + Selector: &metav1.LabelSelector{MatchLabels: ls}, + UpdateStrategy: appsv1.DaemonSetUpdateStrategy{ + Type: appsv1.RollingUpdateDaemonSetStrategyType, + RollingUpdate: &appsv1.RollingUpdateDaemonSet{ + MaxUnavailable: &one, + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: ls, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{Name: "foo", Image: "bar"}}, + }, + }, + }, + } + + cp = &onapv1alpha1.CollectdPlugin{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cpu", + Namespace: namespace, + }, + Spec: onapv1alpha1.CollectdPluginSpec{ + PluginName: "cpu", + PluginConf: "Interval 10", + }, + } + + cpList = &onapv1alpha1.CollectdPluginList{ + Items: []onapv1alpha1.CollectdPlugin{}, + } +) + +// TestCollectdGlobalController runs ReconcileCollectdGlobal.Reconcile() against a +// fake client that tracks a CollectdGlobal object. + +// Reconcile No CR exist. +func TestCollectdGlobalNoCR(t *testing.T) { + // Set the logger to development mode for verbose logs. + logf.SetLogger(logf.ZapLogger(true)) + + os.Setenv("WATCH_LABELS", watchLabels) + + cg := &onapv1alpha1.CollectdGlobal{} + + cgList := &onapv1alpha1.CollectdGlobalList{ + Items: []onapv1alpha1.CollectdGlobal{}, + } + + _ = cp + + // Objects to track in the fake client. + objs := []runtime.Object{ + cm, + ds, + cgList, + } + // Register operator types with the runtime scheme. + s := scheme.Scheme + s.AddKnownTypes(onapv1alpha1.SchemeGroupVersion, cg, cgList, cpList) + + // Create a fake client to mock API calls + fc1 := fake.NewFakeClient(objs...) + rcg := &ReconcileCollectdGlobal{client: fc1, scheme: s} + + req := reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: namespace, + Name: name, + }, + } + res, err := rcg.Reconcile(req) + if err != nil { + t.Fatalf("reconcile: (%v)", err) + } + // Check the result of reconciliation to make sure it has the desired state. + if res.Requeue { + t.Error("Unexpected Reconcile requeue request") + } +} + +// Test CollectdGlobalController - Add CR with non existent ConfigMap. +func TestCollectdGlobalNoCM(t *testing.T) { + // Set the logger to development mode for verbose logs. + logf.SetLogger(logf.ZapLogger(true)) + + os.Setenv("WATCH_LABELS", watchLabels) + + cg := &onapv1alpha1.CollectdGlobal{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: onapv1alpha1.CollectdGlobalSpec{ + GlobalOptions: gOpts, + }, + } + + cgList := &onapv1alpha1.CollectdGlobalList{ + Items: []onapv1alpha1.CollectdGlobal{*cg}, + } + + _ = cp + + // Objects to track in the fake client. + objs := []runtime.Object{ + ds, + cgList, + } + // Register operator types with the runtime scheme. + s := scheme.Scheme + s.AddKnownTypes(onapv1alpha1.SchemeGroupVersion, cg, cgList, cpList) + + // Create a fake client to mock API calls + fc1 := fake.NewFakeClient(objs...) + rcg := &ReconcileCollectdGlobal{client: fc1, scheme: s} + + req := reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: namespace, + Name: name, + }, + } + defer func() { + if r := recover(); r == nil { + t.Errorf("The code did not panic") + } else { + t.Log("Successful Panic") + } + }() + res, err := rcg.Reconcile(req) + if err != nil { + t.Fatalf("reconcile: (%v)", err) + } + // Check the result of reconciliation to make sure it has the desired state. + if !res.Requeue { + t.Error("Reconcile did not requeue request as expected") + } +} + +// Test CollectdGlobalController - HandleDelete +func TestCollectdGlobalHandleDelete(t *testing.T) { + // Set the logger to development mode for verbose logs. + logf.SetLogger(logf.ZapLogger(true)) + + os.Setenv("WATCH_LABELS", watchLabels) + + cg := &onapv1alpha1.CollectdGlobal{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + DeletionTimestamp: &metav1.Time{ + Time: time.Now(), + }, + Finalizers: []string{ + utils.CollectdFinalizer, + }, + }, + Spec: onapv1alpha1.CollectdGlobalSpec{ + GlobalOptions: gOpts, + }, + } + + cgList := &onapv1alpha1.CollectdGlobalList{ + Items: []onapv1alpha1.CollectdGlobal{*cg}, + } + + _ = cp + + // Objects to track in the fake client. + objs := []runtime.Object{ + cm, + ds, + cgList, + } + // Register operator types with the runtime scheme. + s := scheme.Scheme + s.AddKnownTypes(onapv1alpha1.SchemeGroupVersion, cg, cgList, cpList) + + // Create a fake client to mock API calls + fc1 := fake.NewFakeClient(objs...) + rcg := &ReconcileCollectdGlobal{client: fc1, scheme: s} + + req := reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: namespace, + Name: name, + }, + } + res, err := rcg.Reconcile(req) + if err != nil { + t.Fatalf("reconcile: (%v)", err) + } + // Check the result of reconciliation to make sure it has the desired state. + if res.Requeue { + t.Error("Unexpected Reconcile requeue request") + } +} + +// Test CollectdGlobalController +func TestCollectdGlobalController(t *testing.T) { + // Set the logger to development mode for verbose logs. + logf.SetLogger(logf.ZapLogger(true)) + + os.Setenv("WATCH_LABELS", watchLabels) + + cg := &onapv1alpha1.CollectdGlobal{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: onapv1alpha1.CollectdGlobalSpec{ + GlobalOptions: gOpts, + }, + } + + cgList := &onapv1alpha1.CollectdGlobalList{ + Items: []onapv1alpha1.CollectdGlobal{*cg}, + } + + _ = cp + + // Objects to track in the fake client. + objs := []runtime.Object{ + cm, + ds, + cgList, + } + // Register operator types with the runtime scheme. + s := scheme.Scheme + s.AddKnownTypes(onapv1alpha1.SchemeGroupVersion, cg, cgList, cpList) + + // Create a fake client to mock API calls + fc1 := fake.NewFakeClient(objs...) + rcg := &ReconcileCollectdGlobal{client: fc1, scheme: s} + + req := reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: namespace, + Name: name, + }, + } + res, err := rcg.Reconcile(req) + if err != nil { + t.Fatalf("reconcile: (%v)", err) + } + // Check the result of reconciliation to make sure it has the desired state. + if res.Requeue { + t.Error("Unexpected Reconcile requeue request") + } +} + +// Test HandleTypesDB +func TestHandleTypesDB(t *testing.T) { + // Set the logger to development mode for verbose logs. + logf.SetLogger(logf.ZapLogger(true)) + + os.Setenv("WATCH_LABELS", watchLabels) + + cg := &onapv1alpha1.CollectdGlobal{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: onapv1alpha1.CollectdGlobalSpec{ + GlobalOptions: gOpts, + ConfigMap: typesCmName, + }, + } + + cgList := &onapv1alpha1.CollectdGlobalList{ + Items: []onapv1alpha1.CollectdGlobal{*cg}, + } + + _ = cp + + testCases := []struct { + name string + createTypesCm bool + }{ + {name: "Handle TypesDB missing TypesDB ConfigMap", createTypesCm: false}, + {name: "Handle TypesDB", createTypesCm: true}, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + // Objects to track in the fake client. + objs := []runtime.Object{ + cm, + ds, + cgList, + } + if testCase.createTypesCm { + objs = append(objs, typesCm) + } + // Register operator types with the runtime scheme. + s := scheme.Scheme + s.AddKnownTypes(onapv1alpha1.SchemeGroupVersion, cg, cgList, cpList) + + // Create a fake client to mock API calls + fc1 := fake.NewFakeClient(objs...) + rcg := &ReconcileCollectdGlobal{client: fc1, scheme: s} + + req := reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: namespace, + Name: name, + }, + } + res, err := rcg.Reconcile(req) + if err != nil { + t.Fatalf("reconcile: (%v)", err) + } + // Check the result of reconciliation to make sure it has the desired state. + if res.Requeue { + t.Error("Unexpected Reconcile requeue request") + } + }) + } +} + +// Test UpdateStatus +func TestUpdateStatus(t *testing.T) { + // Set the logger to development mode for verbose logs. + logf.SetLogger(logf.ZapLogger(true)) + + os.Setenv("WATCH_LABELS", watchLabels) + + testCases := []struct { + name string + cg *onapv1alpha1.CollectdGlobal + createPods bool + expectedStatus string + expectedError error + }{ + { + name: "Update Status of New CR", + cg: &onapv1alpha1.CollectdGlobal{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: onapv1alpha1.CollectdGlobalSpec{ + GlobalOptions: gOpts, + }, + }, + expectedStatus: onapv1alpha1.Created, + expectedError: nil, + }, + { + name: "Update Initial state to Created", + cg: &onapv1alpha1.CollectdGlobal{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: onapv1alpha1.CollectdGlobalSpec{ + GlobalOptions: gOpts, + }, + Status: onapv1alpha1.CollectdGlobalStatus{ + Status: onapv1alpha1.Initial, + }, + }, + expectedStatus: onapv1alpha1.Created, + expectedError: nil, + }, + { + name: "Update Created state - No Pods", + cg: &onapv1alpha1.CollectdGlobal{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: onapv1alpha1.CollectdGlobalSpec{ + GlobalOptions: gOpts, + }, + Status: onapv1alpha1.CollectdGlobalStatus{ + Status: onapv1alpha1.Created, + }, + }, + expectedStatus: onapv1alpha1.Created, + expectedError: nil, + }, + { + name: "Update Created state to Enabled", + cg: &onapv1alpha1.CollectdGlobal{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: onapv1alpha1.CollectdGlobalSpec{ + GlobalOptions: gOpts, + }, + Status: onapv1alpha1.CollectdGlobalStatus{ + Status: onapv1alpha1.Created, + }, + }, + createPods: true, + expectedStatus: onapv1alpha1.Enabled, + expectedError: nil, + }, + } + req := reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: namespace, + Name: name, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + // Objects to track in the fake client. + objs := []runtime.Object{ + testCase.cg, + } + if testCase.createPods { + pods := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: testCase.cg.Namespace, + Name: "cp-collectd-abcde", + Labels: ls, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + corev1.Container{ + Image: "collectd", + Name: "collectd", + }, + }, + }, + } + objs = append(objs, runtime.Object(pods)) + } + // Register operator types with the runtime scheme. + s := scheme.Scheme + s.AddKnownTypes(onapv1alpha1.SchemeGroupVersion, testCase.cg) + + // Create a fake client to mock API calls + fc1 := fake.NewFakeClient(objs...) + rcg := &ReconcileCollectdGlobal{client: fc1, scheme: s} + + err := rcg.updateStatus(testCase.cg) + assert.Equal(t, testCase.expectedError, err) + // Fetch the CollectdGlobal instance + actual := &onapv1alpha1.CollectdGlobal{} + err = fc1.Get(context.TODO(), req.NamespacedName, actual) + assert.Equal(t, testCase.expectedStatus, actual.Status.Status) + if testCase.createPods { + assert.Equal(t, []string{"cp-collectd-abcde"}, actual.Status.CollectdAgents) + } + }) + } +} diff --git a/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/collectdplugin/collectdplugin_controller.go b/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/collectdplugin/collectdplugin_controller.go index e0e62b87..6ad73782 100644 --- a/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/collectdplugin/collectdplugin_controller.go +++ b/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/collectdplugin/collectdplugin_controller.go @@ -1,3 +1,16 @@ +/* +Copyright 2019 Intel Corporation. +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 collectdplugin import ( @@ -255,7 +268,7 @@ func (r *ReconcileCollectdPlugin) handleDelete(reqLogger logr.Logger, cr *onapv1 } func (r *ReconcileCollectdPlugin) updateStatus(cr *onapv1alpha1.CollectdPlugin) error { - // Fetch the CollectdGlobal instance + // Fetch the CollectdPlugin instance instance := &onapv1alpha1.CollectdPlugin{} key := types.NamespacedName{Namespace: cr.Namespace, Name: cr.Name} err := r.client.Get(context.TODO(), key, instance) diff --git a/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/collectdplugin/collectdplugin_controller_test.go b/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/collectdplugin/collectdplugin_controller_test.go new file mode 100644 index 00000000..f17aa6dc --- /dev/null +++ b/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/collectdplugin/collectdplugin_controller_test.go @@ -0,0 +1,370 @@ +/* +Copyright 2019 Intel Corporation. +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 collectdplugin + +import ( + "context" + "os" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + onapv1alpha1 "collectd-operator/pkg/apis/onap/v1alpha1" + utils "collectd-operator/pkg/controller/utils" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" +) + +const ( + name = "cpu" + namespace = "test1" + gOpts = "BaseDir \"/opt/collectd/var/lib/collectd\"\nPIDFile \"/opt/collectd/var/run/collectd.pid\"\nPluginDir" + + "\"/opt/collectd/lib/collectd\"\nTypesDB \"/opt/collectd/share/collectd/types.db\"\n" + + "#Hostname \"localhost\"\nInterval 1 \nWriteQueueLimitHigh" + + "10000000\nWriteQueueLimitLow 8000000\nTimeout \"10\"\nReadThreads \"50\"\nWriteThreads" + + "\"50\"\n\n#Enable plugins:\n\nLoadPlugin cpufreq\nLoadPlugin ipmi\nLoadPlugin" + + "turbostat\nLoadPlugin irq\nLoadPlugin memcached\nLoadPlugin memory\nLoadPlugin" + + "processes\nLoadPlugin load\n" + typesCmName = "types-configmap" + watchLabels = "app=collectd" +) + +var ( + one = intstr.FromInt(1) + ls, _ = labels.ConvertSelectorToLabelsMap(watchLabels) + cgList = &onapv1alpha1.CollectdGlobalList{ + Items: []onapv1alpha1.CollectdGlobal{}, + } + cm = &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cp-config-map", + Namespace: namespace, + Labels: ls, + }, + Data: map[string]string{ + "collectd.conf": "", + }, + } + readonlyCM = &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cp-config-map-read-only", + Namespace: namespace, + Labels: ls, + }, + Data: map[string]string{ + "collectd.conf": "", + }, + } + typesCm = &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: typesCmName, + Namespace: namespace, + Labels: ls, + }, + Data: map[string]string{ + "types.db": "types.db data", + }, + } + ds = &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cp-collectd", + Namespace: namespace, + Labels: ls, + }, + Spec: appsv1.DaemonSetSpec{ + Selector: &metav1.LabelSelector{MatchLabels: ls}, + UpdateStrategy: appsv1.DaemonSetUpdateStrategy{ + Type: appsv1.RollingUpdateDaemonSetStrategyType, + RollingUpdate: &appsv1.RollingUpdateDaemonSet{ + MaxUnavailable: &one, + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: ls, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{Name: "cp-collectd-1", Image: "collectd"}}, + }, + }, + }, + } + + cp1 = &onapv1alpha1.CollectdPlugin{ + TypeMeta: metav1.TypeMeta{ + Kind: "CollectdPlugin", + APIVersion: "onap.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "cpu", + Namespace: namespace, + }, + Spec: onapv1alpha1.CollectdPluginSpec{ + PluginName: "cpu", + PluginConf: "\n" + + "ReportByCpu true\n" + + "ReportByState true\n" + + "ValuesPercentage true\n" + + "\n", + }, + } + + delCp1 = &onapv1alpha1.CollectdPlugin{ + TypeMeta: metav1.TypeMeta{ + Kind: "CollectdPlugin", + APIVersion: "onap.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "cpu", + Namespace: namespace, + DeletionTimestamp: &metav1.Time{ + Time: time.Now(), + }, + Finalizers: []string{ + utils.CollectdFinalizer, + }, + }, + Spec: onapv1alpha1.CollectdPluginSpec{ + PluginName: "cpu", + PluginConf: "\n" + + "ReportByCpu true\n" + + "ReportByState true\n" + + "ValuesPercentage true\n" + + "\n", + }, + } + + cp2 = &onapv1alpha1.CollectdPlugin{ + ObjectMeta: metav1.ObjectMeta{ + Name: "write_prometheus", + Namespace: namespace, + }, + Spec: onapv1alpha1.CollectdPluginSpec{ + PluginName: "cpu", + PluginConf: "\n" + + "Port 9103\n" + + "\n", + }, + } + + cpList = &onapv1alpha1.CollectdPluginList{ + Items: []onapv1alpha1.CollectdPlugin{}, + } +) + +func TestReconcileCollectdPlugin_Reconcile(t *testing.T) { + logf.SetLogger(logf.ZapLogger(true)) + + os.Setenv("WATCH_LABELS", watchLabels) + + tests := []struct { + name string + objs []runtime.Object + want reconcile.Result + wantErr bool + wantPanic bool + }{ + { + name: "CollectdPlugin Reconcile No CR", + objs: getObjs(nil, []runtime.Object{cm, ds}), + want: reconcile.Result{Requeue: false}, + wantErr: false, + }, + { + name: "CollectdPlugin Add new CR cp1", + objs: getObjs([]onapv1alpha1.CollectdPlugin{*cp1}, []runtime.Object{cm, ds}), + want: reconcile.Result{Requeue: false}, + wantErr: false, + }, + { + name: "CollectdPlugin Delete CR cp1", + objs: getObjs([]onapv1alpha1.CollectdPlugin{*delCp1}, []runtime.Object{cm, ds}), + want: reconcile.Result{Requeue: false}, + wantErr: false, + }, + { + name: "CollectdPlugin Add new CR No configMap", + objs: getObjs([]onapv1alpha1.CollectdPlugin{*cp1}, []runtime.Object{ds}), + want: reconcile.Result{Requeue: false}, + wantErr: false, + wantPanic: true, + }, + { + name: "CollectdPlugin Add new CR no DaemonSet", + objs: getObjs([]onapv1alpha1.CollectdPlugin{*cp1}, []runtime.Object{cm}), + want: reconcile.Result{Requeue: false}, + wantErr: false, + wantPanic: true, + }, + { + name: "CollectdPlugin Add new CR cp2", + objs: getObjs([]onapv1alpha1.CollectdPlugin{*cp2}, []runtime.Object{cm, ds}), + want: reconcile.Result{Requeue: false}, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + if tt.wantPanic { + defer func() { + if r := recover(); r == nil { + t.Errorf("The code did not panic") + } else { + t.Log("Successful Panic") + } + }() + } + // Objects to track in the fake client. + objs := tt.objs + + // Register operator types with the runtime scheme. + s := scheme.Scheme + s.AddKnownTypes(onapv1alpha1.SchemeGroupVersion, cp1, cpList, cgList) + + // Create a fake client to mock API calls + fc1 := fake.NewFakeClient(objs...) + rcp := &ReconcileCollectdPlugin{client: fc1, scheme: s} + req := reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: namespace, + Name: name, + }, + } + got, err := rcp.Reconcile(req) + if err != nil { + t.Fatalf("reconcile: (%v)", err) + } + assert.Equal(t, tt.want, got) + }) + } +} + +// Test UpdateStatus +func TestUpdateStatus(t *testing.T) { + // Set the logger to development mode for verbose logs. + logf.SetLogger(logf.ZapLogger(true)) + + os.Setenv("WATCH_LABELS", watchLabels) + + testCases := []struct { + name string + cp *onapv1alpha1.CollectdPlugin + createPods bool + expectedStatus string + expectedError error + }{ + { + name: "Update Status of New CR", + cp: getNewCPWithStatus(cp1, onapv1alpha1.Initial), + expectedStatus: onapv1alpha1.Created, + expectedError: nil, + }, + { + name: "Update Initial state to Created", + cp: getNewCPWithStatus(cp1, onapv1alpha1.Created), + expectedStatus: onapv1alpha1.Created, + expectedError: nil, + }, + { + name: "Update Created state - No Pods", + cp: getNewCPWithStatus(cp1, onapv1alpha1.Created), + expectedStatus: onapv1alpha1.Created, + expectedError: nil, + }, + { + name: "Update Created state to Enabled", + cp: getNewCPWithStatus(cp1, onapv1alpha1.Created), + createPods: true, + expectedStatus: onapv1alpha1.Enabled, + expectedError: nil, + }, + } + req := reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: namespace, + Name: name, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + // Objects to track in the fake client. + objs := []runtime.Object{ + testCase.cp, + } + if testCase.createPods { + pods := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: testCase.cp.Namespace, + Name: "cp-collectd-abcde", + Labels: ls, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + corev1.Container{ + Image: "collectd", + Name: "collectd", + }, + }, + }, + } + objs = append(objs, runtime.Object(pods)) + } + // Register operator types with the runtime scheme. + s := scheme.Scheme + s.AddKnownTypes(onapv1alpha1.SchemeGroupVersion, testCase.cp) + + // Create a fake client to mock API calls + fc1 := fake.NewFakeClient(objs...) + rcp := &ReconcileCollectdPlugin{client: fc1, scheme: s} + + err := rcp.updateStatus(testCase.cp) + assert.Equal(t, testCase.expectedError, err) + // Fetch the CollectdGlobal instance + actual := &onapv1alpha1.CollectdPlugin{} + err = fc1.Get(context.TODO(), req.NamespacedName, actual) + assert.Equal(t, testCase.expectedStatus, actual.Status.Status) + if testCase.createPods { + assert.Equal(t, []string{"cp-collectd-abcde"}, actual.Status.CollectdAgents) + } + }) + } +} + +func getNewCPWithStatus(cp *onapv1alpha1.CollectdPlugin, status string) *onapv1alpha1.CollectdPlugin { + cpTemp := cp.DeepCopy() + cpTemp.Status.Status = status + return cpTemp +} + +func getObjs(cp []onapv1alpha1.CollectdPlugin, objs []runtime.Object) []runtime.Object { + cpL := cpList.DeepCopy() + cpL.Items = nil + items := append(cpL.Items, cp...) + cpL.Items = items + objs = append(objs, cpL) + return objs +} diff --git a/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/utils/collectdutils.go b/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/utils/collectdutils.go index b379d916..1e73f696 100644 --- a/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/utils/collectdutils.go +++ b/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/utils/collectdutils.go @@ -1,3 +1,16 @@ +/* +Copyright 2019 Intel Corporation. +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 utils import ( diff --git a/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/utils/dsutils.go b/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/utils/dsutils.go index c5a44c44..1892b525 100644 --- a/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/utils/dsutils.go +++ b/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/utils/dsutils.go @@ -1,3 +1,16 @@ +/* +Copyright 2019 Intel Corporation. +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 utils import ( diff --git a/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/utils/predicate.go b/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/utils/predicate.go index a9ec1dc4..a66e70ad 100644 --- a/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/utils/predicate.go +++ b/vnfs/DAaaS/microservices/collectd-operator/pkg/controller/utils/predicate.go @@ -1,16 +1,15 @@ -// Copyright 2018 The Operator-SDK 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. +/* +Copyright 2019 Intel Corporation. +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 utils -- 2.16.6