Collectd Operator Unit Tests
[demo.git] / vnfs / DAaaS / microservices / collectd-operator / pkg / controller / collectdplugin / collectdplugin_controller_test.go
1 /*
2 Copyright 2019 Intel Corporation.
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6     http://www.apache.org/licenses/LICENSE-2.0
7 Unless required by applicable law or agreed to in writing, software
8 distributed under the License is distributed on an "AS IS" BASIS,
9 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 See the License for the specific language governing permissions and
11 limitations under the License.
12 */
13
14 package collectdplugin
15
16 import (
17         "context"
18         "os"
19         "testing"
20         "time"
21
22         "github.com/stretchr/testify/assert"
23
24         onapv1alpha1 "collectd-operator/pkg/apis/onap/v1alpha1"
25         utils "collectd-operator/pkg/controller/utils"
26
27         appsv1 "k8s.io/api/apps/v1"
28         corev1 "k8s.io/api/core/v1"
29         metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30         "k8s.io/apimachinery/pkg/labels"
31         "k8s.io/apimachinery/pkg/runtime"
32         "k8s.io/apimachinery/pkg/types"
33         "k8s.io/apimachinery/pkg/util/intstr"
34         "k8s.io/client-go/kubernetes/scheme"
35         "sigs.k8s.io/controller-runtime/pkg/client/fake"
36         "sigs.k8s.io/controller-runtime/pkg/reconcile"
37         logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
38 )
39
40 const (
41         name      = "cpu"
42         namespace = "test1"
43         gOpts     = "BaseDir     \"/opt/collectd/var/lib/collectd\"\nPIDFile     \"/opt/collectd/var/run/collectd.pid\"\nPluginDir" +
44                 "\"/opt/collectd/lib/collectd\"\nTypesDB     \"/opt/collectd/share/collectd/types.db\"\n" +
45                 "#Hostname \"localhost\"\nInterval 1 \nWriteQueueLimitHigh" +
46                 "10000000\nWriteQueueLimitLow   8000000\nTimeout \"10\"\nReadThreads \"50\"\nWriteThreads" +
47                 "\"50\"\n\n#Enable plugins:\n\nLoadPlugin cpufreq\nLoadPlugin ipmi\nLoadPlugin" +
48                 "turbostat\nLoadPlugin irq\nLoadPlugin memcached\nLoadPlugin memory\nLoadPlugin" +
49                 "processes\nLoadPlugin load\n"
50         typesCmName = "types-configmap"
51         watchLabels = "app=collectd"
52 )
53
54 var (
55         one    = intstr.FromInt(1)
56         ls, _  = labels.ConvertSelectorToLabelsMap(watchLabels)
57         cgList = &onapv1alpha1.CollectdGlobalList{
58                 Items: []onapv1alpha1.CollectdGlobal{},
59         }
60         cm = &corev1.ConfigMap{
61                 ObjectMeta: metav1.ObjectMeta{
62                         Name:      "cp-config-map",
63                         Namespace: namespace,
64                         Labels:    ls,
65                 },
66                 Data: map[string]string{
67                         "collectd.conf": "",
68                 },
69         }
70         readonlyCM = &corev1.ConfigMap{
71                 ObjectMeta: metav1.ObjectMeta{
72                         Name:      "cp-config-map-read-only",
73                         Namespace: namespace,
74                         Labels:    ls,
75                 },
76                 Data: map[string]string{
77                         "collectd.conf": "",
78                 },
79         }
80         typesCm = &corev1.ConfigMap{
81                 ObjectMeta: metav1.ObjectMeta{
82                         Name:      typesCmName,
83                         Namespace: namespace,
84                         Labels:    ls,
85                 },
86                 Data: map[string]string{
87                         "types.db": "types.db data",
88                 },
89         }
90         ds = &appsv1.DaemonSet{
91                 ObjectMeta: metav1.ObjectMeta{
92                         Name:      "cp-collectd",
93                         Namespace: namespace,
94                         Labels:    ls,
95                 },
96                 Spec: appsv1.DaemonSetSpec{
97                         Selector: &metav1.LabelSelector{MatchLabels: ls},
98                         UpdateStrategy: appsv1.DaemonSetUpdateStrategy{
99                                 Type: appsv1.RollingUpdateDaemonSetStrategyType,
100                                 RollingUpdate: &appsv1.RollingUpdateDaemonSet{
101                                         MaxUnavailable: &one,
102                                 },
103                         },
104                         Template: corev1.PodTemplateSpec{
105                                 ObjectMeta: metav1.ObjectMeta{
106                                         Labels: ls,
107                                 },
108                                 Spec: corev1.PodSpec{
109                                         Containers: []corev1.Container{{Name: "cp-collectd-1", Image: "collectd"}},
110                                 },
111                         },
112                 },
113         }
114
115         cp1 = &onapv1alpha1.CollectdPlugin{
116                 TypeMeta: metav1.TypeMeta{
117                         Kind:       "CollectdPlugin",
118                         APIVersion: "onap.org/v1alpha1",
119                 },
120                 ObjectMeta: metav1.ObjectMeta{
121                         Name:      "cpu",
122                         Namespace: namespace,
123                 },
124                 Spec: onapv1alpha1.CollectdPluginSpec{
125                         PluginName: "cpu",
126                         PluginConf: "<Plugin cpu>\n" +
127                                 "ReportByCpu true\n" +
128                                 "ReportByState true\n" +
129                                 "ValuesPercentage true\n" +
130                                 "</Plugin>\n",
131                 },
132         }
133
134         delCp1 = &onapv1alpha1.CollectdPlugin{
135                 TypeMeta: metav1.TypeMeta{
136                         Kind:       "CollectdPlugin",
137                         APIVersion: "onap.org/v1alpha1",
138                 },
139                 ObjectMeta: metav1.ObjectMeta{
140                         Name:      "cpu",
141                         Namespace: namespace,
142                         DeletionTimestamp: &metav1.Time{
143                                 Time: time.Now(),
144                         },
145                         Finalizers: []string{
146                                 utils.CollectdFinalizer,
147                         },
148                 },
149                 Spec: onapv1alpha1.CollectdPluginSpec{
150                         PluginName: "cpu",
151                         PluginConf: "<Plugin cpu>\n" +
152                                 "ReportByCpu true\n" +
153                                 "ReportByState true\n" +
154                                 "ValuesPercentage true\n" +
155                                 "</Plugin>\n",
156                 },
157         }
158
159         cp2 = &onapv1alpha1.CollectdPlugin{
160                 ObjectMeta: metav1.ObjectMeta{
161                         Name:      "write_prometheus",
162                         Namespace: namespace,
163                 },
164                 Spec: onapv1alpha1.CollectdPluginSpec{
165                         PluginName: "cpu",
166                         PluginConf: "<Plugin write_prometheus>\n" +
167                                 "Port 9103\n" +
168                                 "</Plugin>\n",
169                 },
170         }
171
172         cpList = &onapv1alpha1.CollectdPluginList{
173                 Items: []onapv1alpha1.CollectdPlugin{},
174         }
175 )
176
177 func TestReconcileCollectdPlugin_Reconcile(t *testing.T) {
178         logf.SetLogger(logf.ZapLogger(true))
179
180         os.Setenv("WATCH_LABELS", watchLabels)
181
182         tests := []struct {
183                 name      string
184                 objs      []runtime.Object
185                 want      reconcile.Result
186                 wantErr   bool
187                 wantPanic bool
188         }{
189                 {
190                         name:    "CollectdPlugin Reconcile No CR",
191                         objs:    getObjs(nil, []runtime.Object{cm, ds}),
192                         want:    reconcile.Result{Requeue: false},
193                         wantErr: false,
194                 },
195                 {
196                         name:    "CollectdPlugin Add new CR cp1",
197                         objs:    getObjs([]onapv1alpha1.CollectdPlugin{*cp1}, []runtime.Object{cm, ds}),
198                         want:    reconcile.Result{Requeue: false},
199                         wantErr: false,
200                 },
201                 {
202                         name:    "CollectdPlugin Delete CR cp1",
203                         objs:    getObjs([]onapv1alpha1.CollectdPlugin{*delCp1}, []runtime.Object{cm, ds}),
204                         want:    reconcile.Result{Requeue: false},
205                         wantErr: false,
206                 },
207                 {
208                         name:      "CollectdPlugin Add new CR No configMap",
209                         objs:      getObjs([]onapv1alpha1.CollectdPlugin{*cp1}, []runtime.Object{ds}),
210                         want:      reconcile.Result{Requeue: false},
211                         wantErr:   false,
212                         wantPanic: true,
213                 },
214                 {
215                         name:      "CollectdPlugin Add new CR no DaemonSet",
216                         objs:      getObjs([]onapv1alpha1.CollectdPlugin{*cp1}, []runtime.Object{cm}),
217                         want:      reconcile.Result{Requeue: false},
218                         wantErr:   false,
219                         wantPanic: true,
220                 },
221                 {
222                         name:    "CollectdPlugin Add new CR cp2",
223                         objs:    getObjs([]onapv1alpha1.CollectdPlugin{*cp2}, []runtime.Object{cm, ds}),
224                         want:    reconcile.Result{Requeue: false},
225                         wantErr: false,
226                 },
227         }
228         for _, tt := range tests {
229                 t.Run(tt.name, func(t *testing.T) {
230
231                         if tt.wantPanic {
232                                 defer func() {
233                                         if r := recover(); r == nil {
234                                                 t.Errorf("The code did not panic")
235                                         } else {
236                                                 t.Log("Successful Panic")
237                                         }
238                                 }()
239                         }
240                         // Objects to track in the fake client.
241                         objs := tt.objs
242
243                         // Register operator types with the runtime scheme.
244                         s := scheme.Scheme
245                         s.AddKnownTypes(onapv1alpha1.SchemeGroupVersion, cp1, cpList, cgList)
246
247                         // Create a fake client to mock API calls
248                         fc1 := fake.NewFakeClient(objs...)
249                         rcp := &ReconcileCollectdPlugin{client: fc1, scheme: s}
250                         req := reconcile.Request{
251                                 NamespacedName: types.NamespacedName{
252                                         Namespace: namespace,
253                                         Name:      name,
254                                 },
255                         }
256                         got, err := rcp.Reconcile(req)
257                         if err != nil {
258                                 t.Fatalf("reconcile: (%v)", err)
259                         }
260                         assert.Equal(t, tt.want, got)
261                 })
262         }
263 }
264
265 // Test UpdateStatus
266 func TestUpdateStatus(t *testing.T) {
267         // Set the logger to development mode for verbose logs.
268         logf.SetLogger(logf.ZapLogger(true))
269
270         os.Setenv("WATCH_LABELS", watchLabels)
271
272         testCases := []struct {
273                 name           string
274                 cp             *onapv1alpha1.CollectdPlugin
275                 createPods     bool
276                 expectedStatus string
277                 expectedError  error
278         }{
279                 {
280                         name:           "Update Status of New CR",
281                         cp:             getNewCPWithStatus(cp1, onapv1alpha1.Initial),
282                         expectedStatus: onapv1alpha1.Created,
283                         expectedError:  nil,
284                 },
285                 {
286                         name:           "Update Initial state to Created",
287                         cp:             getNewCPWithStatus(cp1, onapv1alpha1.Created),
288                         expectedStatus: onapv1alpha1.Created,
289                         expectedError:  nil,
290                 },
291                 {
292                         name:           "Update Created state - No Pods",
293                         cp:             getNewCPWithStatus(cp1, onapv1alpha1.Created),
294                         expectedStatus: onapv1alpha1.Created,
295                         expectedError:  nil,
296                 },
297                 {
298                         name:           "Update Created state to Enabled",
299                         cp:             getNewCPWithStatus(cp1, onapv1alpha1.Created),
300                         createPods:     true,
301                         expectedStatus: onapv1alpha1.Enabled,
302                         expectedError:  nil,
303                 },
304         }
305         req := reconcile.Request{
306                 NamespacedName: types.NamespacedName{
307                         Namespace: namespace,
308                         Name:      name,
309                 },
310         }
311
312         for _, testCase := range testCases {
313                 t.Run(testCase.name, func(t *testing.T) {
314                         // Objects to track in the fake client.
315                         objs := []runtime.Object{
316                                 testCase.cp,
317                         }
318                         if testCase.createPods {
319                                 pods := &corev1.Pod{
320                                         ObjectMeta: metav1.ObjectMeta{
321                                                 Namespace: testCase.cp.Namespace,
322                                                 Name:      "cp-collectd-abcde",
323                                                 Labels:    ls,
324                                         },
325                                         Spec: corev1.PodSpec{
326                                                 Containers: []corev1.Container{
327                                                         corev1.Container{
328                                                                 Image: "collectd",
329                                                                 Name:  "collectd",
330                                                         },
331                                                 },
332                                         },
333                                 }
334                                 objs = append(objs, runtime.Object(pods))
335                         }
336                         // Register operator types with the runtime scheme.
337                         s := scheme.Scheme
338                         s.AddKnownTypes(onapv1alpha1.SchemeGroupVersion, testCase.cp)
339
340                         // Create a fake client to mock API calls
341                         fc1 := fake.NewFakeClient(objs...)
342                         rcp := &ReconcileCollectdPlugin{client: fc1, scheme: s}
343
344                         err := rcp.updateStatus(testCase.cp)
345                         assert.Equal(t, testCase.expectedError, err)
346                         // Fetch the CollectdGlobal instance
347                         actual := &onapv1alpha1.CollectdPlugin{}
348                         err = fc1.Get(context.TODO(), req.NamespacedName, actual)
349                         assert.Equal(t, testCase.expectedStatus, actual.Status.Status)
350                         if testCase.createPods {
351                                 assert.Equal(t, []string{"cp-collectd-abcde"}, actual.Status.CollectdAgents)
352                         }
353                 })
354         }
355 }
356
357 func getNewCPWithStatus(cp *onapv1alpha1.CollectdPlugin, status string) *onapv1alpha1.CollectdPlugin {
358         cpTemp := cp.DeepCopy()
359         cpTemp.Status.Status = status
360         return cpTemp
361 }
362
363 func getObjs(cp []onapv1alpha1.CollectdPlugin, objs []runtime.Object) []runtime.Object {
364         cpL := cpList.DeepCopy()
365         cpL.Items = nil
366         items := append(cpL.Items, cp...)
367         cpL.Items = items
368         objs = append(objs, cpL)
369         return objs
370 }