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.
14 package collectdglobal
22 "github.com/stretchr/testify/assert"
24 onapv1alpha1 "collectd-operator/pkg/apis/onap/v1alpha1"
25 utils "collectd-operator/pkg/controller/utils"
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"
41 name = "collectd-operator"
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"
55 one = intstr.FromInt(1)
56 ls, _ = labels.ConvertSelectorToLabelsMap(watchLabels)
57 cm = &corev1.ConfigMap{
58 ObjectMeta: metav1.ObjectMeta{
59 Name: "cp-config-map",
63 Data: map[string]string{
67 typesCm = &corev1.ConfigMap{
68 ObjectMeta: metav1.ObjectMeta{
73 Data: map[string]string{
74 "types.db": "types.db data",
77 ds = &appsv1.DaemonSet{
78 ObjectMeta: metav1.ObjectMeta{
83 Spec: appsv1.DaemonSetSpec{
84 Selector: &metav1.LabelSelector{MatchLabels: ls},
85 UpdateStrategy: appsv1.DaemonSetUpdateStrategy{
86 Type: appsv1.RollingUpdateDaemonSetStrategyType,
87 RollingUpdate: &appsv1.RollingUpdateDaemonSet{
91 Template: corev1.PodTemplateSpec{
92 ObjectMeta: metav1.ObjectMeta{
96 Containers: []corev1.Container{{Name: "foo", Image: "bar"}},
102 cp = &onapv1alpha1.CollectdPlugin{
103 ObjectMeta: metav1.ObjectMeta{
105 Namespace: namespace,
107 Spec: onapv1alpha1.CollectdPluginSpec{
109 PluginConf: "Interval 10",
113 cpList = &onapv1alpha1.CollectdPluginList{
114 Items: []onapv1alpha1.CollectdPlugin{},
118 // TestCollectdGlobalController runs ReconcileCollectdGlobal.Reconcile() against a
119 // fake client that tracks a CollectdGlobal object.
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))
126 os.Setenv("WATCH_LABELS", watchLabels)
128 cg := &onapv1alpha1.CollectdGlobal{}
130 cgList := &onapv1alpha1.CollectdGlobalList{
131 Items: []onapv1alpha1.CollectdGlobal{},
136 // Objects to track in the fake client.
137 objs := []runtime.Object{
142 // Register operator types with the runtime scheme.
144 s.AddKnownTypes(onapv1alpha1.SchemeGroupVersion, cg, cgList, cpList)
146 // Create a fake client to mock API calls
147 fc1 := fake.NewFakeClient(objs...)
148 rcg := &ReconcileCollectdGlobal{client: fc1, scheme: s}
150 req := reconcile.Request{
151 NamespacedName: types.NamespacedName{
152 Namespace: namespace,
156 res, err := rcg.Reconcile(req)
158 t.Fatalf("reconcile: (%v)", err)
160 // Check the result of reconciliation to make sure it has the desired state.
162 t.Error("Unexpected Reconcile requeue request")
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))
171 os.Setenv("WATCH_LABELS", watchLabels)
173 cg := &onapv1alpha1.CollectdGlobal{
174 ObjectMeta: metav1.ObjectMeta{
176 Namespace: namespace,
178 Spec: onapv1alpha1.CollectdGlobalSpec{
179 GlobalOptions: gOpts,
183 cgList := &onapv1alpha1.CollectdGlobalList{
184 Items: []onapv1alpha1.CollectdGlobal{*cg},
189 // Objects to track in the fake client.
190 objs := []runtime.Object{
194 // Register operator types with the runtime scheme.
196 s.AddKnownTypes(onapv1alpha1.SchemeGroupVersion, cg, cgList, cpList)
198 // Create a fake client to mock API calls
199 fc1 := fake.NewFakeClient(objs...)
200 rcg := &ReconcileCollectdGlobal{client: fc1, scheme: s}
202 req := reconcile.Request{
203 NamespacedName: types.NamespacedName{
204 Namespace: namespace,
209 if r := recover(); r == nil {
210 t.Errorf("The code did not panic")
212 t.Log("Successful Panic")
215 res, err := rcg.Reconcile(req)
217 t.Fatalf("reconcile: (%v)", err)
219 // Check the result of reconciliation to make sure it has the desired state.
221 t.Error("Reconcile did not requeue request as expected")
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))
230 os.Setenv("WATCH_LABELS", watchLabels)
232 cg := &onapv1alpha1.CollectdGlobal{
233 ObjectMeta: metav1.ObjectMeta{
235 Namespace: namespace,
236 DeletionTimestamp: &metav1.Time{
239 Finalizers: []string{
240 utils.CollectdFinalizer,
243 Spec: onapv1alpha1.CollectdGlobalSpec{
244 GlobalOptions: gOpts,
248 cgList := &onapv1alpha1.CollectdGlobalList{
249 Items: []onapv1alpha1.CollectdGlobal{*cg},
254 // Objects to track in the fake client.
255 objs := []runtime.Object{
260 // Register operator types with the runtime scheme.
262 s.AddKnownTypes(onapv1alpha1.SchemeGroupVersion, cg, cgList, cpList)
264 // Create a fake client to mock API calls
265 fc1 := fake.NewFakeClient(objs...)
266 rcg := &ReconcileCollectdGlobal{client: fc1, scheme: s}
268 req := reconcile.Request{
269 NamespacedName: types.NamespacedName{
270 Namespace: namespace,
274 res, err := rcg.Reconcile(req)
276 t.Fatalf("reconcile: (%v)", err)
278 // Check the result of reconciliation to make sure it has the desired state.
280 t.Error("Unexpected Reconcile requeue request")
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))
289 os.Setenv("WATCH_LABELS", watchLabels)
291 cg := &onapv1alpha1.CollectdGlobal{
292 ObjectMeta: metav1.ObjectMeta{
294 Namespace: namespace,
296 Spec: onapv1alpha1.CollectdGlobalSpec{
297 GlobalOptions: gOpts,
301 cgList := &onapv1alpha1.CollectdGlobalList{
302 Items: []onapv1alpha1.CollectdGlobal{*cg},
307 // Objects to track in the fake client.
308 objs := []runtime.Object{
313 // Register operator types with the runtime scheme.
315 s.AddKnownTypes(onapv1alpha1.SchemeGroupVersion, cg, cgList, cpList)
317 // Create a fake client to mock API calls
318 fc1 := fake.NewFakeClient(objs...)
319 rcg := &ReconcileCollectdGlobal{client: fc1, scheme: s}
321 req := reconcile.Request{
322 NamespacedName: types.NamespacedName{
323 Namespace: namespace,
327 res, err := rcg.Reconcile(req)
329 t.Fatalf("reconcile: (%v)", err)
331 // Check the result of reconciliation to make sure it has the desired state.
333 t.Error("Unexpected Reconcile requeue request")
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))
342 os.Setenv("WATCH_LABELS", watchLabels)
344 cg := &onapv1alpha1.CollectdGlobal{
345 ObjectMeta: metav1.ObjectMeta{
347 Namespace: namespace,
349 Spec: onapv1alpha1.CollectdGlobalSpec{
350 GlobalOptions: gOpts,
351 ConfigMap: typesCmName,
355 cgList := &onapv1alpha1.CollectdGlobalList{
356 Items: []onapv1alpha1.CollectdGlobal{*cg},
361 testCases := []struct {
365 {name: "Handle TypesDB missing TypesDB ConfigMap", createTypesCm: false},
366 {name: "Handle TypesDB", createTypesCm: true},
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{
376 if testCase.createTypesCm {
377 objs = append(objs, typesCm)
379 // Register operator types with the runtime scheme.
381 s.AddKnownTypes(onapv1alpha1.SchemeGroupVersion, cg, cgList, cpList)
383 // Create a fake client to mock API calls
384 fc1 := fake.NewFakeClient(objs...)
385 rcg := &ReconcileCollectdGlobal{client: fc1, scheme: s}
387 req := reconcile.Request{
388 NamespacedName: types.NamespacedName{
389 Namespace: namespace,
393 res, err := rcg.Reconcile(req)
395 t.Fatalf("reconcile: (%v)", err)
397 // Check the result of reconciliation to make sure it has the desired state.
399 t.Error("Unexpected Reconcile requeue request")
406 func TestUpdateStatus(t *testing.T) {
407 // Set the logger to development mode for verbose logs.
408 logf.SetLogger(logf.ZapLogger(true))
410 os.Setenv("WATCH_LABELS", watchLabels)
412 testCases := []struct {
414 cg *onapv1alpha1.CollectdGlobal
416 expectedStatus string
420 name: "Update Status of New CR",
421 cg: &onapv1alpha1.CollectdGlobal{
422 ObjectMeta: metav1.ObjectMeta{
424 Namespace: namespace,
426 Spec: onapv1alpha1.CollectdGlobalSpec{
427 GlobalOptions: gOpts,
430 expectedStatus: onapv1alpha1.Created,
434 name: "Update Initial state to Created",
435 cg: &onapv1alpha1.CollectdGlobal{
436 ObjectMeta: metav1.ObjectMeta{
438 Namespace: namespace,
440 Spec: onapv1alpha1.CollectdGlobalSpec{
441 GlobalOptions: gOpts,
443 Status: onapv1alpha1.CollectdGlobalStatus{
444 Status: onapv1alpha1.Initial,
447 expectedStatus: onapv1alpha1.Created,
451 name: "Update Created state - No Pods",
452 cg: &onapv1alpha1.CollectdGlobal{
453 ObjectMeta: metav1.ObjectMeta{
455 Namespace: namespace,
457 Spec: onapv1alpha1.CollectdGlobalSpec{
458 GlobalOptions: gOpts,
460 Status: onapv1alpha1.CollectdGlobalStatus{
461 Status: onapv1alpha1.Created,
464 expectedStatus: onapv1alpha1.Created,
468 name: "Update Created state to Enabled",
469 cg: &onapv1alpha1.CollectdGlobal{
470 ObjectMeta: metav1.ObjectMeta{
472 Namespace: namespace,
474 Spec: onapv1alpha1.CollectdGlobalSpec{
475 GlobalOptions: gOpts,
477 Status: onapv1alpha1.CollectdGlobalStatus{
478 Status: onapv1alpha1.Created,
482 expectedStatus: onapv1alpha1.Enabled,
486 req := reconcile.Request{
487 NamespacedName: types.NamespacedName{
488 Namespace: namespace,
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{
499 if testCase.createPods {
501 ObjectMeta: metav1.ObjectMeta{
502 Namespace: testCase.cg.Namespace,
503 Name: "cp-collectd-abcde",
506 Spec: corev1.PodSpec{
507 Containers: []corev1.Container{
515 objs = append(objs, runtime.Object(pods))
517 // Register operator types with the runtime scheme.
519 s.AddKnownTypes(onapv1alpha1.SchemeGroupVersion, testCase.cg)
521 // Create a fake client to mock API calls
522 fc1 := fake.NewFakeClient(objs...)
523 rcg := &ReconcileCollectdGlobal{client: fc1, scheme: s}
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)