Improve appmgr UT coverage
[ric-plt/appmgr.git] / pkg / helm / helm_test.go
1 /*
2 ==================================================================================
3   Copyright (c) 2019 AT&T Intellectual Property.
4   Copyright (c) 2019 Nokia
5
6    Licensed under the Apache License, Version 2.0 (the "License");
7    you may not use this file except in compliance with the License.
8    You may obtain a copy of the License at
9
10        http://www.apache.org/licenses/LICENSE-2.0
11
12    Unless required by applicable law or agreed to in writing, software
13    distributed under the License is distributed on an "AS IS" BASIS,
14    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15    See the License for the specific language governing permissions and
16    limitations under the License.
17 ==================================================================================
18 */
19
20 package helm
21
22 import (
23         "errors"
24         "github.com/spf13/viper"
25         "os"
26         "reflect"
27         "strconv"
28         "strings"
29         "testing"
30
31         "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/appmgr"
32         "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/models"
33         "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/util"
34 )
35
36 var caughtKubeExecArgs string
37 var kubeExecRetOut string
38 var kubeExecRetErr error
39 var caughtHelmExecArgs string
40 var helmExecRetOut string
41 var helmExecRetErr error
42 var helmStatusOutput = `
43 LAST DEPLOYED: Sat Mar  9 06:50:45 2019
44 NAMESPACE: default
45 STATUS: DEPLOYED
46
47 RESOURCES:
48 ==> v1/Pod(related)
49 NAME                        READY  STATUS   RESTARTS  AGE
50 dummy-xapp-8984fc9fd-bkcbp  1/1    Running  0         55m
51 dummy-xapp-8984fc9fd-l6xch  1/1    Running  0         55m
52 dummy-xapp-8984fc9fd-pp4hg  1/1    Running  0         55m
53
54 ==> v1/Service
55 NAME                         TYPE       CLUSTER-IP      EXTERNAL-IP  PORT(S)  AGE
56 dummy-xapp-dummy-xapp-chart  ClusterIP  10.102.184.212  <none>       80/TCP   55m
57
58 ==> v1beta1/Deployment
59 NAME        READY  UP-TO-DATE  AVAILABLE  AGE
60 dummy-xapp  3/3    3           3          55m
61 `
62
63 var helListAllOutput = `Next: ""
64 Releases:
65 - AppVersion: "1.0"
66   Chart: dummy-xapp-chart-0.1.0
67   Name: dummy-xapp
68   Namespace: default
69   Revision: 1
70   Status: DEPLOYED
71   Updated: Mon Mar 11 06:55:05 2019
72 - AppVersion: "2.0"
73   Chart: dummy-xapp-chart-0.1.0
74   Name: dummy-xapp2
75   Namespace: default
76   Revision: 1
77   Status: DEPLOYED
78   Updated: Mon Mar 11 06:55:05 2019
79 - AppVersion: "1.0"
80   Chart: appmgr-0.0.1
81   Name: appmgr
82   Namespace: default
83   Revision: 1
84   Status: DEPLOYED
85   Updated: Sun Mar 24 07:17:00 2019
86   `
87
88 var helListOutput = `Next: ""
89 Releases:
90 - AppVersion: "1.0"
91   Chart: dummy-xapp-chart-0.1.0
92   Name: dummy-xapp
93   Namespace: default
94   Revision: 1
95   Status: DEPLOYED
96   Updated: Mon Mar 11 06:55:05 2019
97   `
98
99 var kubeServiceOutput = `{
100     "apiVersion": "v1",
101     "kind": "Service",
102     "metadata": {
103         "creationTimestamp": "2020-03-31T12:27:12Z",
104         "labels": {
105             "app": "ricxapp-dummy-xapp",
106             "chart": "dummy-xapp-0.0.4",
107             "heritage": "Tiller",
108             "release": "dummy-xapp"
109         },
110         "name": "service-ricxapp-dummy-xapp-rmr",
111         "namespace": "ricxapp",
112         "resourceVersion": "4423380",
113         "selfLink": "/api/v1/namespaces/ricxapp/services/service-ricxapp-dummy-xapp-rmr",
114         "uid": "2254b77d-7dd6-43e0-beff-3e2a7b24c89a"
115     },
116     "spec": {
117         "clusterIP": "10.98.239.107",
118         "ports": [
119             {
120                 "name": "rmrdata",
121                 "port": 4560,
122                 "protocol": "TCP",
123                 "targetPort": "rmrdata"
124             },
125             {
126                 "name": "rmrroute",
127                 "port": 4561,
128                 "protocol": "TCP",
129                 "targetPort": "rmrroute"
130             }
131         ],
132         "selector": {
133             "app": "ricxapp-dummy-xapp",
134             "release": "dummy-xapp"
135         },
136         "sessionAffinity": "None",
137         "type": "ClusterIP"
138     },
139     "status": {
140         "loadBalancer": {}
141     }
142 }`
143
144 // Test cases
145 func TestMain(m *testing.M) {
146         appmgr.Init()
147         appmgr.Logger.SetLevel(0)
148
149         code := m.Run()
150         os.Exit(code)
151 }
152
153 func TestInit(t *testing.T) {
154         defer func() { resetHelmExecMock() }()
155         helmExec = mockedHelmExec
156
157         NewHelm().Init()
158
159         expectedHelmCommand := "init -c --skip-refresh"
160         if caughtHelmExecArgs != expectedHelmCommand {
161                 t.Errorf("Init failed: expected %v, got %v", expectedHelmCommand, caughtHelmExecArgs)
162         }
163 }
164
165 func TestAddRepoSuccess(t *testing.T) {
166         defer func() {
167                 resetHelmExecMock()
168                 removeTestUsernameFile()
169                 removeTestPasswordFile()
170         }()
171         helmExec = mockedHelmExec
172
173         if err := writeTestUsernameFile(); err != nil {
174                 t.Errorf("AddRepo username file create failed: %s", err)
175                 return
176         }
177         if err := writeTestPasswordFile(); err != nil {
178                 t.Errorf("AddRepo password file create failed: %s", err)
179                 return
180         }
181
182         if _, err := NewHelm().AddRepo(); err != nil {
183                 t.Errorf("AddRepo failed: %v", err)
184         }
185
186         if !strings.Contains(caughtHelmExecArgs, "repo add") {
187                 t.Errorf("AddRepo failed: expected %v, got %v", "repo add", caughtHelmExecArgs)
188         }
189 }
190
191 func TestAddRepoReturnsErrorIfNoUsernameFile(t *testing.T) {
192         if _, err := NewHelm().AddRepo(); err == nil {
193                 t.Errorf("AddRepo expected to fail but it didn't")
194         }
195 }
196
197 func TestAddRepoReturnsErrorIfNoPasswordFile(t *testing.T) {
198         defer func() { resetHelmExecMock(); removeTestUsernameFile() }()
199         helmExec = mockedHelmExec
200
201         if err := writeTestUsernameFile(); err != nil {
202                 t.Errorf("AddRepo username file create failed: %s", err)
203                 return
204         }
205         if _, err := NewHelm().AddRepo(); err == nil {
206                 t.Errorf("AddRepo expected to fail but it didn't")
207         }
208 }
209
210 func TestInstallSuccess(t *testing.T) {
211         name := "dummy-xapp"
212         xappDesc := models.XappDescriptor{XappName: &name, Namespace: "ricxapp"}
213
214         defer func() { resetHelmExecMock() }()
215         helmExec = mockedHelmExec
216         helmExecRetOut = helmStatusOutput
217
218         defer func() { resetKubeExecMock() }()
219         kubeExec = mockedKubeExec
220         kubeExecRetOut = kubeServiceOutput
221
222         xapp, err := NewHelm().Install(xappDesc)
223         if err != nil {
224                 t.Errorf("Install failed: %v", err)
225         }
226         validateXappModel(t, xapp)
227 }
228
229 func TestInstallReturnsErrorIfHelmRepoUpdateFails(t *testing.T) {
230         name := "dummy-xapp"
231         xappDesc := models.XappDescriptor{XappName: &name, Namespace: "ricxapp"}
232
233         defer func() { resetHelmExecMock() }()
234         helmExec = mockedHelmExec
235         helmExecRetErr = errors.New("some helm command error")
236
237         if _, err := NewHelm().Install(xappDesc); err == nil {
238                 t.Errorf("Install expected to fail but it didn't")
239         }
240 }
241
242 func TestStatusSuccess(t *testing.T) {
243         name := "dummy-xapp"
244
245         defer func() { resetHelmExecMock() }()
246         helmExec = mockedHelmExec
247         helmExecRetOut = helmStatusOutput
248
249         xapp, err := NewHelm().Status(name)
250         if err != nil {
251                 t.Errorf("Status failed: %v", err)
252         }
253         validateXappModel(t, xapp)
254 }
255
256 func TestStatusReturnsErrorIfHelmStatusFails(t *testing.T) {
257         name := "dummy-xapp"
258
259         defer func() { resetHelmExecMock() }()
260         helmExec = mockedHelmExec
261         helmExecRetErr = errors.New("some helm command error")
262
263         if _, err := NewHelm().Status(name); err == nil {
264                 t.Errorf("Status expected to fail but it didn't")
265         }
266 }
267
268 func TestParseStatusSuccess(t *testing.T) {
269         defer func() { resetHelmExecMock() }()
270         helmExec = mockedHelmExec
271         helmExecRetOut = helListOutput
272
273         defer func() { resetKubeExecMock() }()
274         kubeExec = mockedKubeExec
275         kubeExecRetOut = kubeServiceOutput
276
277         xapp, err := NewHelm().ParseStatus("dummy-xapp", helmStatusOutput)
278         if err != nil {
279                 t.Errorf("ParseStatus failed: %v", err)
280         }
281
282         validateXappModel(t, xapp)
283
284         expectedHelmCommand := "list --deployed --output yaml --namespace=ricxapp dummy-xapp"
285         if caughtHelmExecArgs != expectedHelmCommand {
286                 t.Errorf("ParseStatus failed: expected %v, got %v", expectedHelmCommand, caughtHelmExecArgs)
287         }
288 }
289
290 func TestListSuccess(t *testing.T) {
291         defer func() { resetHelmExecMock() }()
292         helmExec = mockedHelmExec
293         helmExecRetOut = helListAllOutput
294
295         names, err := NewHelm().List()
296         if err != nil {
297                 t.Errorf("List failed: %v", err)
298         }
299
300         if !reflect.DeepEqual(names, []string{"dummy-xapp", "dummy-xapp2"}) {
301                 t.Errorf("List failed: %v", err)
302         }
303         expectedHelmCommand := "list --all --deployed --output yaml --namespace=ricxapp"
304         if caughtHelmExecArgs != expectedHelmCommand {
305                 t.Errorf("List: expected %v, got %v", expectedHelmCommand, caughtHelmExecArgs)
306         }
307 }
308
309 func TestListReturnsErrorIfHelmListFails(t *testing.T) {
310         defer func() { resetHelmExecMock() }()
311         helmExec = mockedHelmExec
312         helmExecRetErr = errors.New("some helm command error")
313
314         if _, err := NewHelm().List(); err == nil {
315                 t.Errorf("List expected to fail but it didn't")
316         }
317 }
318
319 func TestDeleteSuccess(t *testing.T) {
320         name := "dummy-xapp"
321
322         defer func() { resetHelmExecMock() }()
323         helmExec = mockedHelmExec
324         helmExecRetOut = helmStatusOutput
325
326         defer func() { resetKubeExecMock() }()
327         kubeExec = mockedKubeExec
328         kubeExecRetOut = kubeServiceOutput
329
330         xapp, err := NewHelm().Delete(name)
331         if err != nil {
332                 t.Errorf("Delete failed: %v", err)
333         }
334
335         validateXappModel(t, xapp)
336
337         expectedHelmCommand := "del --purge dummy-xapp"
338         if caughtHelmExecArgs != expectedHelmCommand {
339                 t.Errorf("Delete failed: expected %v, got %v", expectedHelmCommand, caughtHelmExecArgs)
340         }
341 }
342
343 func TestDeleteReturnsErrorIfHelmStatusFails(t *testing.T) {
344         name := "dummy-xapp"
345
346         defer func() { resetHelmExecMock() }()
347         helmExec = mockedHelmExec
348         helmExecRetErr = errors.New("some helm command error")
349
350         if _, err := NewHelm().Delete(name); err == nil {
351                 t.Errorf("Delete expected to fail but it didn't")
352         }
353 }
354
355 func TestFetchSuccessIfCmdArgHasTestSuffix(t *testing.T) {
356         if err := NewHelm().Fetch("kissa", "koira"); err != nil {
357                 t.Errorf("Fetch failed: %v", err)
358         }
359 }
360
361 func TestGetVersionSuccess(t *testing.T) {
362         defer func() { resetHelmExecMock() }()
363         helmExec = mockedHelmExec
364         helmExecRetOut = helListOutput
365
366         if version := NewHelm().GetVersion("dummy-xapp"); version != "1.0" {
367                 t.Errorf("GetVersion failed: expected 1.0, got %v", version)
368         }
369
370         expectedHelmCommand := "list --deployed --output yaml --namespace=ricxapp dummy-xapp"
371         if caughtHelmExecArgs != expectedHelmCommand {
372                 t.Errorf("GetVersion failed: expected %v, got %v", expectedHelmCommand, caughtHelmExecArgs)
373         }
374 }
375
376 func TestGetVersionReturnsEmptyStringIfHelmListFails(t *testing.T) {
377         defer func() { resetHelmExecMock() }()
378         helmExec = mockedHelmExec
379         helmExecRetErr = errors.New("some helm command error")
380
381         if version := NewHelm().GetVersion("dummy-xapp"); version != "" {
382                 t.Errorf("GetVersion expected to return empty string, got %v", version)
383         }
384 }
385
386 func TestGetAddressSuccess(t *testing.T) {
387         ip, port := NewHelm().GetAddress(helmStatusOutput)
388         if ip != "10.102.184.212" {
389                 t.Errorf("GetAddress failed: expected 10.102.184.212, got %v", ip)
390         }
391         if port != "80/TCP" {
392                 t.Errorf("GetAddress failed: expected 80/TCP, got %v", port)
393         }
394 }
395
396 func TestGetEndpointInfoSuccess(t *testing.T) {
397         defer func() { resetKubeExecMock() }()
398         kubeExec = mockedKubeExec
399         kubeExecRetOut = kubeServiceOutput
400
401         svc, port := NewHelm().GetEndpointInfo("dummy-xapp")
402         expectedSvc := "service-ricxapp-dummy-xapp-rmr.ricxapp"
403         if svc != expectedSvc {
404                 t.Errorf("GetEndpointInfo failed: expected %v, got %v", expectedSvc, svc)
405         }
406         if port != 4560 {
407                 t.Errorf("GetEndpointInfo failed: expected port 4560, got %v", port)
408         }
409         expectedKubeCommand := " get service -n ricxapp service-ricxapp-dummy-xapp-rmr -o json"
410         if caughtKubeExecArgs != expectedKubeCommand {
411                 t.Errorf("GetEndpointInfo failed: expected %v, got %v", expectedKubeCommand, caughtKubeExecArgs)
412         }
413 }
414
415 func TestGetEndpointInfoReturnsDefaultPortIfJsonParseFails(t *testing.T) {
416         defer func() { resetKubeExecMock() }()
417         kubeExec = mockedKubeExec
418         kubeExecRetOut = "not-json-syntax"
419
420         svc, port := NewHelm().GetEndpointInfo("dummy-xapp")
421         expectedSvc := "service-ricxapp-dummy-xapp-rmr.ricxapp"
422         if svc != expectedSvc {
423                 t.Errorf("GetEndpointInfo failed: expected %v, got %v", expectedSvc, svc)
424         }
425         if port != 4560 {
426                 t.Errorf("GetEndpointInfo failed: expected port 4560, got %v", port)
427         }
428 }
429
430 func TestGetEndpointInfoReturnsDefaultPortIfKubeGetServiceFails(t *testing.T) {
431         defer func() { resetKubeExecMock() }()
432         kubeExec = mockedKubeExec
433         kubeExecRetErr = errors.New("some helm command error")
434
435         svc, port := NewHelm().GetEndpointInfo("dummy-xapp")
436         expectedSvc := "service-ricxapp-dummy-xapp-rmr.ricxapp"
437         if svc != expectedSvc {
438                 t.Errorf("GetEndpointInfo failed: expected %v, got %v", expectedSvc, svc)
439         }
440         if port != 4560 {
441                 t.Errorf("GetEndpointInfo failed: expected port 4560, got %v", port)
442         }
443 }
444
445 func TestHelmStatusAllSuccess(t *testing.T) {
446         defer func() { resetHelmExecMock() }()
447         helmExec = mockedHelmExec
448         helmExecRetOut = helListAllOutput
449
450         if _, err := NewHelm().StatusAll(); err != nil {
451                 t.Errorf("StatusAll failed: %v", err)
452         }
453         // Todo: check StatusAll response content
454 }
455
456 func TestStatusAllReturnsErrorIfHelmListFails(t *testing.T) {
457         defer func() { resetHelmExecMock() }()
458         helmExec = mockedHelmExec
459         helmExecRetErr = errors.New("some helm command error")
460
461         if _, err := NewHelm().StatusAll(); err == nil {
462                 t.Errorf("StatusAll expected to fail but it didn't")
463         }
464 }
465
466 func TestGetNamesSuccess(t *testing.T) {
467         names, err := NewHelm().GetNames(helListAllOutput)
468         if err != nil {
469                 t.Errorf("GetNames failed: %v", err)
470         }
471         if !reflect.DeepEqual(names, []string{"dummy-xapp", "dummy-xapp2"}) {
472                 t.Errorf("GetNames failed: %v", err)
473         }
474 }
475
476 func TestAddTillerEnv(t *testing.T) {
477         if NewHelm().AddTillerEnv() != nil {
478                 t.Errorf("AddTillerEnv failed!")
479         }
480 }
481
482 func TestGetInstallArgs(t *testing.T) {
483         name := "dummy-xapp"
484         x := models.XappDescriptor{XappName: &name, Namespace: "ricxapp"}
485
486         expectedArgs := "install helm-repo/dummy-xapp  --namespace=ricxapp --name=dummy-xapp"
487         if args := NewHelm().GetInstallArgs(x, false); args != expectedArgs {
488                 t.Errorf("GetInstallArgs failed: expected %v, got %v", expectedArgs, args)
489         }
490
491         expectedArgs += " --set ricapp.appconfig.override=dummy-xapp-appconfig"
492         if args := NewHelm().GetInstallArgs(x, true); args != expectedArgs {
493                 t.Errorf("GetInstallArgs failed: expected %v, got %v", expectedArgs, args)
494         }
495
496         x.HelmVersion = "1.2.3"
497         expectedArgs = "install helm-repo/dummy-xapp  --namespace=ricxapp --version=1.2.3 --name=dummy-xapp"
498         if args := NewHelm().GetInstallArgs(x, false); args != expectedArgs {
499                 t.Errorf("GetInstallArgs failed: expected %v, got %v", expectedArgs, args)
500         }
501
502         x.ReleaseName = "ueec-xapp"
503         expectedArgs = "install helm-repo/dummy-xapp  --namespace=ricxapp --version=1.2.3 --name=ueec-xapp"
504         if args := NewHelm().GetInstallArgs(x, false); args != expectedArgs {
505                 t.Errorf("GetInstallArgs failed: expected %v, got %v", expectedArgs, args)
506         }
507
508         x.OverrideFile = "../../test/dummy-xapp_values.json"
509         expectedArgs += " -f=/tmp/appmgr_override.yaml"
510         if args := NewHelm().GetInstallArgs(x, false); args != expectedArgs {
511                 t.Errorf("GetInstallArgs failed: expected %v, got %v", expectedArgs, args)
512         }
513 }
514
515 func writeTestUsernameFile() error {
516         f, err := os.Create(viper.GetString("helm.helm-username-file"))
517         if err != nil {
518                 return err
519         }
520         _, err = f.WriteString("some-username")
521         f.Close()
522         return err
523 }
524
525 func removeTestUsernameFile() error {
526         return os.Remove(viper.GetString("helm.helm-username-file"))
527 }
528
529 func writeTestPasswordFile() (err error) {
530         f, err := os.Create(viper.GetString("helm.helm-password-file"))
531         if err != nil {
532                 return err
533         }
534
535         _, err = f.WriteString("some-password")
536         f.Close()
537         return err
538 }
539
540 func removeTestPasswordFile() error {
541         return os.Remove(viper.GetString("helm.helm-password-file"))
542 }
543
544 func getXappData() (x models.Xapp) {
545         //name1 := "dummy-xapp-8984fc9fd-l6xch"
546         //name2 := "dummy-xapp-8984fc9fd-pp4hg"
547         x = generateXapp("dummy-xapp", "deployed", "1.0", "dummy-xapp-8984fc9fd-bkcbp", "running", "service-ricxapp-dummy-xapp-rmr.ricxapp", "4560")
548         //x.Instances = append(x.Instances, x.Instances[0])
549         //x.Instances = append(x.Instances, x.Instances[0])
550         //x.Instances[1].Name = &name1
551         //x.Instances[2].Name = &name2
552
553         return x
554 }
555
556 func generateXapp(name, status, ver, iname, istatus, ip, port string) (x models.Xapp) {
557         x.Name = &name
558         x.Status = status
559         x.Version = ver
560         p, _ := strconv.Atoi(port)
561         var msgs appmgr.RtmData
562
563         instance := &models.XappInstance{
564                 Name:       &iname,
565                 Status:     istatus,
566                 IP:         ip,
567                 Port:       int64(p),
568                 TxMessages: msgs.TxMessages,
569                 RxMessages: msgs.RxMessages,
570         }
571         x.Instances = append(x.Instances, instance)
572
573         return
574 }
575
576 func mockedKubeExec(args string) (out []byte, err error) {
577         caughtKubeExecArgs = args
578         return []byte(kubeExecRetOut), kubeExecRetErr
579 }
580
581 func resetKubeExecMock() {
582         kubeExec = util.KubectlExec
583         caughtKubeExecArgs = ""
584         kubeExecRetOut = ""
585         kubeExecRetErr = nil
586 }
587
588 func mockedHelmExec(args string) (out []byte, err error) {
589         caughtHelmExecArgs = args
590         return []byte(helmExecRetOut), helmExecRetErr
591 }
592
593 func resetHelmExecMock() {
594         helmExec = util.HelmExec
595         caughtHelmExecArgs = ""
596         helmExecRetOut = ""
597         helmExecRetErr = nil
598 }
599
600 func validateXappModel(t *testing.T, xapp models.Xapp) {
601         expXapp := getXappData()
602         xapp.Version = "1.0"
603
604         if *expXapp.Name != *xapp.Name || expXapp.Status != xapp.Status || expXapp.Version != xapp.Version {
605                 t.Errorf("\n%v \n%v", *xapp.Name, *expXapp.Name)
606         }
607
608         if *expXapp.Instances[0].Name != *xapp.Instances[0].Name || expXapp.Instances[0].Status != xapp.Instances[0].Status {
609                 t.Errorf("\n1:%v 2:%v", *expXapp.Instances[0].Name, *xapp.Instances[0].Name)
610         }
611
612         if expXapp.Instances[0].IP != xapp.Instances[0].IP || expXapp.Instances[0].Port != xapp.Instances[0].Port {
613                 t.Errorf("\n%v - %v, %v - %v", expXapp.Instances[0].IP, xapp.Instances[0].IP, expXapp.Instances[0].Port, xapp.Instances[0].Port)
614         }
615 }