Collectd Operator Unit Tests
[demo.git] / vnfs / DAaaS / microservices / collectd-operator / pkg / controller / collectdglobal / collectdglobal_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 collectdglobal
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      = "collectd-operator"
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         cm    = &corev1.ConfigMap{
58                 ObjectMeta: metav1.ObjectMeta{
59                         Name:      "cp-config-map",
60                         Namespace: namespace,
61                         Labels:    ls,
62                 },
63                 Data: map[string]string{
64                         "collectd.conf": "",
65                 },
66         }
67         typesCm = &corev1.ConfigMap{
68                 ObjectMeta: metav1.ObjectMeta{
69                         Name:      typesCmName,
70                         Namespace: namespace,
71                         Labels:    ls,
72                 },
73                 Data: map[string]string{
74                         "types.db": "types.db data",
75                 },
76         }
77         ds = &appsv1.DaemonSet{
78                 ObjectMeta: metav1.ObjectMeta{
79                         Name:      "cp-collectd",
80                         Namespace: namespace,
81                         Labels:    ls,
82                 },
83                 Spec: appsv1.DaemonSetSpec{
84                         Selector: &metav1.LabelSelector{MatchLabels: ls},
85                         UpdateStrategy: appsv1.DaemonSetUpdateStrategy{
86                                 Type: appsv1.RollingUpdateDaemonSetStrategyType,
87                                 RollingUpdate: &appsv1.RollingUpdateDaemonSet{
88                                         MaxUnavailable: &one,
89                                 },
90                         },
91                         Template: corev1.PodTemplateSpec{
92                                 ObjectMeta: metav1.ObjectMeta{
93                                         Labels: ls,
94                                 },
95                                 Spec: corev1.PodSpec{
96                                         Containers: []corev1.Container{{Name: "foo", Image: "bar"}},
97                                 },
98                         },
99                 },
100         }
101
102         cp = &onapv1alpha1.CollectdPlugin{
103                 ObjectMeta: metav1.ObjectMeta{
104                         Name:      "cpu",
105                         Namespace: namespace,
106                 },
107                 Spec: onapv1alpha1.CollectdPluginSpec{
108                         PluginName: "cpu",
109                         PluginConf: "Interval 10",
110                 },
111         }
112
113         cpList = &onapv1alpha1.CollectdPluginList{
114                 Items: []onapv1alpha1.CollectdPlugin{},
115         }
116 )
117
118 // TestCollectdGlobalController runs ReconcileCollectdGlobal.Reconcile() against a
119 // fake client that tracks a CollectdGlobal object.
120
121 // Reconcile No CR exist.
122 func TestCollectdGlobalNoCR(t *testing.T) {
123         // Set the logger to development mode for verbose logs.
124         logf.SetLogger(logf.ZapLogger(true))
125
126         os.Setenv("WATCH_LABELS", watchLabels)
127
128         cg := &onapv1alpha1.CollectdGlobal{}
129
130         cgList := &onapv1alpha1.CollectdGlobalList{
131                 Items: []onapv1alpha1.CollectdGlobal{},
132         }
133
134         _ = cp
135
136         // Objects to track in the fake client.
137         objs := []runtime.Object{
138                 cm,
139                 ds,
140                 cgList,
141         }
142         // Register operator types with the runtime scheme.
143         s := scheme.Scheme
144         s.AddKnownTypes(onapv1alpha1.SchemeGroupVersion, cg, cgList, cpList)
145
146         // Create a fake client to mock API calls
147         fc1 := fake.NewFakeClient(objs...)
148         rcg := &ReconcileCollectdGlobal{client: fc1, scheme: s}
149
150         req := reconcile.Request{
151                 NamespacedName: types.NamespacedName{
152                         Namespace: namespace,
153                         Name:      name,
154                 },
155         }
156         res, err := rcg.Reconcile(req)
157         if err != nil {
158                 t.Fatalf("reconcile: (%v)", err)
159         }
160         // Check the result of reconciliation to make sure it has the desired state.
161         if res.Requeue {
162                 t.Error("Unexpected Reconcile requeue request")
163         }
164 }
165
166 // Test CollectdGlobalController - Add CR with non existent ConfigMap.
167 func TestCollectdGlobalNoCM(t *testing.T) {
168         // Set the logger to development mode for verbose logs.
169         logf.SetLogger(logf.ZapLogger(true))
170
171         os.Setenv("WATCH_LABELS", watchLabels)
172
173         cg := &onapv1alpha1.CollectdGlobal{
174                 ObjectMeta: metav1.ObjectMeta{
175                         Name:      name,
176                         Namespace: namespace,
177                 },
178                 Spec: onapv1alpha1.CollectdGlobalSpec{
179                         GlobalOptions: gOpts,
180                 },
181         }
182
183         cgList := &onapv1alpha1.CollectdGlobalList{
184                 Items: []onapv1alpha1.CollectdGlobal{*cg},
185         }
186
187         _ = cp
188
189         // Objects to track in the fake client.
190         objs := []runtime.Object{
191                 ds,
192                 cgList,
193         }
194         // Register operator types with the runtime scheme.
195         s := scheme.Scheme
196         s.AddKnownTypes(onapv1alpha1.SchemeGroupVersion, cg, cgList, cpList)
197
198         // Create a fake client to mock API calls
199         fc1 := fake.NewFakeClient(objs...)
200         rcg := &ReconcileCollectdGlobal{client: fc1, scheme: s}
201
202         req := reconcile.Request{
203                 NamespacedName: types.NamespacedName{
204                         Namespace: namespace,
205                         Name:      name,
206                 },
207         }
208         defer func() {
209                 if r := recover(); r == nil {
210                         t.Errorf("The code did not panic")
211                 } else {
212                         t.Log("Successful Panic")
213                 }
214         }()
215         res, err := rcg.Reconcile(req)
216         if err != nil {
217                 t.Fatalf("reconcile: (%v)", err)
218         }
219         // Check the result of reconciliation to make sure it has the desired state.
220         if !res.Requeue {
221                 t.Error("Reconcile did not requeue request as expected")
222         }
223 }
224
225 // Test CollectdGlobalController - HandleDelete
226 func TestCollectdGlobalHandleDelete(t *testing.T) {
227         // Set the logger to development mode for verbose logs.
228         logf.SetLogger(logf.ZapLogger(true))
229
230         os.Setenv("WATCH_LABELS", watchLabels)
231
232         cg := &onapv1alpha1.CollectdGlobal{
233                 ObjectMeta: metav1.ObjectMeta{
234                         Name:      name,
235                         Namespace: namespace,
236                         DeletionTimestamp: &metav1.Time{
237                                 Time: time.Now(),
238                         },
239                         Finalizers: []string{
240                                 utils.CollectdFinalizer,
241                         },
242                 },
243                 Spec: onapv1alpha1.CollectdGlobalSpec{
244                         GlobalOptions: gOpts,
245                 },
246         }
247
248         cgList := &onapv1alpha1.CollectdGlobalList{
249                 Items: []onapv1alpha1.CollectdGlobal{*cg},
250         }
251
252         _ = cp
253
254         // Objects to track in the fake client.
255         objs := []runtime.Object{
256                 cm,
257                 ds,
258                 cgList,
259         }
260         // Register operator types with the runtime scheme.
261         s := scheme.Scheme
262         s.AddKnownTypes(onapv1alpha1.SchemeGroupVersion, cg, cgList, cpList)
263
264         // Create a fake client to mock API calls
265         fc1 := fake.NewFakeClient(objs...)
266         rcg := &ReconcileCollectdGlobal{client: fc1, scheme: s}
267
268         req := reconcile.Request{
269                 NamespacedName: types.NamespacedName{
270                         Namespace: namespace,
271                         Name:      name,
272                 },
273         }
274         res, err := rcg.Reconcile(req)
275         if err != nil {
276                 t.Fatalf("reconcile: (%v)", err)
277         }
278         // Check the result of reconciliation to make sure it has the desired state.
279         if res.Requeue {
280                 t.Error("Unexpected Reconcile requeue request")
281         }
282 }
283
284 // Test CollectdGlobalController
285 func TestCollectdGlobalController(t *testing.T) {
286         // Set the logger to development mode for verbose logs.
287         logf.SetLogger(logf.ZapLogger(true))
288
289         os.Setenv("WATCH_LABELS", watchLabels)
290
291         cg := &onapv1alpha1.CollectdGlobal{
292                 ObjectMeta: metav1.ObjectMeta{
293                         Name:      name,
294                         Namespace: namespace,
295                 },
296                 Spec: onapv1alpha1.CollectdGlobalSpec{
297                         GlobalOptions: gOpts,
298                 },
299         }
300
301         cgList := &onapv1alpha1.CollectdGlobalList{
302                 Items: []onapv1alpha1.CollectdGlobal{*cg},
303         }
304
305         _ = cp
306
307         // Objects to track in the fake client.
308         objs := []runtime.Object{
309                 cm,
310                 ds,
311                 cgList,
312         }
313         // Register operator types with the runtime scheme.
314         s := scheme.Scheme
315         s.AddKnownTypes(onapv1alpha1.SchemeGroupVersion, cg, cgList, cpList)
316
317         // Create a fake client to mock API calls
318         fc1 := fake.NewFakeClient(objs...)
319         rcg := &ReconcileCollectdGlobal{client: fc1, scheme: s}
320
321         req := reconcile.Request{
322                 NamespacedName: types.NamespacedName{
323                         Namespace: namespace,
324                         Name:      name,
325                 },
326         }
327         res, err := rcg.Reconcile(req)
328         if err != nil {
329                 t.Fatalf("reconcile: (%v)", err)
330         }
331         // Check the result of reconciliation to make sure it has the desired state.
332         if res.Requeue {
333                 t.Error("Unexpected Reconcile requeue request")
334         }
335 }
336
337 // Test HandleTypesDB
338 func TestHandleTypesDB(t *testing.T) {
339         // Set the logger to development mode for verbose logs.
340         logf.SetLogger(logf.ZapLogger(true))
341
342         os.Setenv("WATCH_LABELS", watchLabels)
343
344         cg := &onapv1alpha1.CollectdGlobal{
345                 ObjectMeta: metav1.ObjectMeta{
346                         Name:      name,
347                         Namespace: namespace,
348                 },
349                 Spec: onapv1alpha1.CollectdGlobalSpec{
350                         GlobalOptions: gOpts,
351                         ConfigMap:     typesCmName,
352                 },
353         }
354
355         cgList := &onapv1alpha1.CollectdGlobalList{
356                 Items: []onapv1alpha1.CollectdGlobal{*cg},
357         }
358
359         _ = cp
360
361         testCases := []struct {
362                 name          string
363                 createTypesCm bool
364         }{
365                 {name: "Handle TypesDB missing TypesDB ConfigMap", createTypesCm: false},
366                 {name: "Handle TypesDB", createTypesCm: true},
367         }
368         for _, testCase := range testCases {
369                 t.Run(testCase.name, func(t *testing.T) {
370                         // Objects to track in the fake client.
371                         objs := []runtime.Object{
372                                 cm,
373                                 ds,
374                                 cgList,
375                         }
376                         if testCase.createTypesCm {
377                                 objs = append(objs, typesCm)
378                         }
379                         // Register operator types with the runtime scheme.
380                         s := scheme.Scheme
381                         s.AddKnownTypes(onapv1alpha1.SchemeGroupVersion, cg, cgList, cpList)
382
383                         // Create a fake client to mock API calls
384                         fc1 := fake.NewFakeClient(objs...)
385                         rcg := &ReconcileCollectdGlobal{client: fc1, scheme: s}
386
387                         req := reconcile.Request{
388                                 NamespacedName: types.NamespacedName{
389                                         Namespace: namespace,
390                                         Name:      name,
391                                 },
392                         }
393                         res, err := rcg.Reconcile(req)
394                         if err != nil {
395                                 t.Fatalf("reconcile: (%v)", err)
396                         }
397                         // Check the result of reconciliation to make sure it has the desired state.
398                         if res.Requeue {
399                                 t.Error("Unexpected Reconcile requeue request")
400                         }
401                 })
402         }
403 }
404
405 // Test UpdateStatus
406 func TestUpdateStatus(t *testing.T) {
407         // Set the logger to development mode for verbose logs.
408         logf.SetLogger(logf.ZapLogger(true))
409
410         os.Setenv("WATCH_LABELS", watchLabels)
411
412         testCases := []struct {
413                 name           string
414                 cg             *onapv1alpha1.CollectdGlobal
415                 createPods     bool
416                 expectedStatus string
417                 expectedError  error
418         }{
419                 {
420                         name: "Update Status of New CR",
421                         cg: &onapv1alpha1.CollectdGlobal{
422                                 ObjectMeta: metav1.ObjectMeta{
423                                         Name:      name,
424                                         Namespace: namespace,
425                                 },
426                                 Spec: onapv1alpha1.CollectdGlobalSpec{
427                                         GlobalOptions: gOpts,
428                                 },
429                         },
430                         expectedStatus: onapv1alpha1.Created,
431                         expectedError:  nil,
432                 },
433                 {
434                         name: "Update Initial state to Created",
435                         cg: &onapv1alpha1.CollectdGlobal{
436                                 ObjectMeta: metav1.ObjectMeta{
437                                         Name:      name,
438                                         Namespace: namespace,
439                                 },
440                                 Spec: onapv1alpha1.CollectdGlobalSpec{
441                                         GlobalOptions: gOpts,
442                                 },
443                                 Status: onapv1alpha1.CollectdGlobalStatus{
444                                         Status: onapv1alpha1.Initial,
445                                 },
446                         },
447                         expectedStatus: onapv1alpha1.Created,
448                         expectedError:  nil,
449                 },
450                 {
451                         name: "Update Created state - No Pods",
452                         cg: &onapv1alpha1.CollectdGlobal{
453                                 ObjectMeta: metav1.ObjectMeta{
454                                         Name:      name,
455                                         Namespace: namespace,
456                                 },
457                                 Spec: onapv1alpha1.CollectdGlobalSpec{
458                                         GlobalOptions: gOpts,
459                                 },
460                                 Status: onapv1alpha1.CollectdGlobalStatus{
461                                         Status: onapv1alpha1.Created,
462                                 },
463                         },
464                         expectedStatus: onapv1alpha1.Created,
465                         expectedError:  nil,
466                 },
467                 {
468                         name: "Update Created state to Enabled",
469                         cg: &onapv1alpha1.CollectdGlobal{
470                                 ObjectMeta: metav1.ObjectMeta{
471                                         Name:      name,
472                                         Namespace: namespace,
473                                 },
474                                 Spec: onapv1alpha1.CollectdGlobalSpec{
475                                         GlobalOptions: gOpts,
476                                 },
477                                 Status: onapv1alpha1.CollectdGlobalStatus{
478                                         Status: onapv1alpha1.Created,
479                                 },
480                         },
481                         createPods:     true,
482                         expectedStatus: onapv1alpha1.Enabled,
483                         expectedError:  nil,
484                 },
485         }
486         req := reconcile.Request{
487                 NamespacedName: types.NamespacedName{
488                         Namespace: namespace,
489                         Name:      name,
490                 },
491         }
492
493         for _, testCase := range testCases {
494                 t.Run(testCase.name, func(t *testing.T) {
495                         // Objects to track in the fake client.
496                         objs := []runtime.Object{
497                                 testCase.cg,
498                         }
499                         if testCase.createPods {
500                                 pods := &corev1.Pod{
501                                         ObjectMeta: metav1.ObjectMeta{
502                                                 Namespace: testCase.cg.Namespace,
503                                                 Name:      "cp-collectd-abcde",
504                                                 Labels:    ls,
505                                         },
506                                         Spec: corev1.PodSpec{
507                                                 Containers: []corev1.Container{
508                                                         corev1.Container{
509                                                                 Image: "collectd",
510                                                                 Name:  "collectd",
511                                                         },
512                                                 },
513                                         },
514                                 }
515                                 objs = append(objs, runtime.Object(pods))
516                         }
517                         // Register operator types with the runtime scheme.
518                         s := scheme.Scheme
519                         s.AddKnownTypes(onapv1alpha1.SchemeGroupVersion, testCase.cg)
520
521                         // Create a fake client to mock API calls
522                         fc1 := fake.NewFakeClient(objs...)
523                         rcg := &ReconcileCollectdGlobal{client: fc1, scheme: s}
524
525                         err := rcg.updateStatus(testCase.cg)
526                         assert.Equal(t, testCase.expectedError, err)
527                         // Fetch the CollectdGlobal instance
528                         actual := &onapv1alpha1.CollectdGlobal{}
529                         err = fc1.Get(context.TODO(), req.NamespacedName, actual)
530                         assert.Equal(t, testCase.expectedStatus, actual.Status.Status)
531                         if testCase.createPods {
532                                 assert.Equal(t, []string{"cp-collectd-abcde"}, actual.Status.CollectdAgents)
533                         }
534                 })
535         }
536 }