Collectd Operator Unit Tests 70/96770/2
authorDileep Ranganathan <dileep.ranganathan@intel.com>
Wed, 9 Oct 2019 00:06:22 +0000 (17:06 -0700)
committerDileep Ranganathan <dileep.ranganathan@intel.com>
Wed, 9 Oct 2019 00:26:38 +0000 (17:26 -0700)
Added unit tests for Collectd operator.

Issue-ID: ONAPARC-461
Signed-off-by: Dileep Ranganathan <dileep.ranganathan@intel.com>
Change-Id: Ib50c16eaefc661e077d3477cbe8f2e4e62bbda80

13 files changed:
vnfs/DAaaS/.gitignore
vnfs/DAaaS/microservices/collectd-operator/Makefile
vnfs/DAaaS/microservices/collectd-operator/go.mod
vnfs/DAaaS/microservices/collectd-operator/go.sum
vnfs/DAaaS/microservices/collectd-operator/pkg/apis/onap/v1alpha1/collectdglobal_types.go
vnfs/DAaaS/microservices/collectd-operator/pkg/apis/onap/v1alpha1/collectdplugin_types.go
vnfs/DAaaS/microservices/collectd-operator/pkg/controller/collectdglobal/collectdglobal_controller.go
vnfs/DAaaS/microservices/collectd-operator/pkg/controller/collectdglobal/collectdglobal_controller_test.go [new file with mode: 0644]
vnfs/DAaaS/microservices/collectd-operator/pkg/controller/collectdplugin/collectdplugin_controller.go
vnfs/DAaaS/microservices/collectd-operator/pkg/controller/collectdplugin/collectdplugin_controller_test.go [new file with mode: 0644]
vnfs/DAaaS/microservices/collectd-operator/pkg/controller/utils/collectdutils.go
vnfs/DAaaS/microservices/collectd-operator/pkg/controller/utils/dsutils.go
vnfs/DAaaS/microservices/collectd-operator/pkg/controller/utils/predicate.go

index b13f7a2..ddef800 100644 (file)
@@ -1,5 +1,6 @@
 *.iml
 .idea
+.vscode
 target/
 dependency-reduced-pom.xml
 File2hdfsApp.java
index c09f4c2..9c02352 100644 (file)
@@ -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:
index 525d4e3..fefba3d 100644 (file)
@@ -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
index b5b3019..4a53436 100644 (file)
@@ -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=
index d5c69fb..f14294e 100644 (file)
@@ -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 (
index eb33863..d814592 100644 (file)
@@ -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 (
index 8c6023b..d1bd5c6 100644 (file)
@@ -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 (file)
index 0000000..2410fe6
--- /dev/null
@@ -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)
+                       }
+               })
+       }
+}
index e0e62b8..6ad7378 100644 (file)
@@ -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 (file)
index 0000000..f17aa6d
--- /dev/null
@@ -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: "<Plugin cpu>\n" +
+                               "ReportByCpu true\n" +
+                               "ReportByState true\n" +
+                               "ValuesPercentage true\n" +
+                               "</Plugin>\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: "<Plugin cpu>\n" +
+                               "ReportByCpu true\n" +
+                               "ReportByState true\n" +
+                               "ValuesPercentage true\n" +
+                               "</Plugin>\n",
+               },
+       }
+
+       cp2 = &onapv1alpha1.CollectdPlugin{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "write_prometheus",
+                       Namespace: namespace,
+               },
+               Spec: onapv1alpha1.CollectdPluginSpec{
+                       PluginName: "cpu",
+                       PluginConf: "<Plugin write_prometheus>\n" +
+                               "Port 9103\n" +
+                               "</Plugin>\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
+}
index b379d91..1e73f69 100644 (file)
@@ -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 (
index c5a44c4..1892b52 100644 (file)
@@ -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 (
index a9ec1dc..a66e70a 100644 (file)
@@ -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