2 ==================================================================================
3 Copyright (c) 2019 AT&T Intellectual Property.
4 Copyright (c) 2019 Nokia
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
10 http://www.apache.org/licenses/LICENSE-2.0
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 ==================================================================================
30 "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/appmgr"
31 "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/models"
32 "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/util"
36 expectedHelmSearchCmd = "search helm-repo"
37 expectedHelmFetchCmd = `fetch --untar --untardir /tmp helm-repo/dummy-xapp`
40 var caughtKubeExecArgs []string
41 var kubeExecRetOut string
42 var kubeExecRetErr error
43 var caughtHelmExecArgs string
44 var helmExecRetOut string
45 var helmExecRetErr error
47 var expectedKubectlGetCmd []string = []string{
48 `get configmap -o jsonpath='{.data.config-file\.json}' -n ricxapp configmap-ricxapp-anr-appconfig`,
49 `get configmap -o jsonpath='{.data.config-file\.json}' -n ricxapp configmap-ricxapp-appmgr-appconfig`,
50 `get configmap -o jsonpath='{.data.config-file\.json}' -n ricxapp configmap-ricxapp-dualco-appconfig`,
51 `get configmap -o jsonpath='{.data.config-file\.json}' -n ricxapp configmap-ricxapp-reporter-appconfig`,
52 `get configmap -o jsonpath='{.data.config-file\.json}' -n ricxapp configmap-ricxapp-uemgr-appconfig`,
55 var helmSearchOutput = `
56 helm-repo/anr 0.0.1 1.0 Helm Chart for Nokia ANR (Automatic Neighbour Relation) xAPP
57 helm-repo/appmgr 0.0.2 1.0 Helm Chart for xAppManager
58 helm-repo/dualco 0.0.1 1.0 Helm Chart for Nokia dualco xAPP
59 helm-repo/reporter 0.0.1 1.0 Helm Chart for Reporting xAPP
60 helm-repo/uemgr 0.0.1 1.0 Helm Chart for Nokia uemgr xAPP
63 var kubectlConfigmapOutput = `
72 "protPort": "tcp:4560",
75 "txMessages": ["RIC_X2_LOAD_INFORMATION"],
76 "rxMessages": ["RIC_X2_LOAD_INFORMATION"],
77 "policies": [11, 22, 33]
80 "namespace": "ricxapp",
86 var kubectlNewConfigmapOutput = `
96 "registry": "ranco-dev-tools.eastus.cloudapp.azure.com:10001",
114 "path": "ric/v1/health/alive",
117 "initialDelaySeconds": 5,
122 "path": "ric/v1/health/ready",
125 "initialDelaySeconds": 5,
134 "description": "http service"
140 "description": "rmr route port for ueec"
149 "txMessages": ["RIC_X2_LOAD_INFORMATION"],
150 "rxMessages": ["RIC_X2_LOAD_INFORMATION"],
151 "policies": [11, 22, 33],
152 "description": "rmr data port for ueec"
161 "subscriptionActive": true,
166 "host": "service-ricplt-submgr-http.ricplt:8088",
167 "clientEndpoint": "service-ricxapp-ueec-http.ricxapp:8080"
171 "url": "/ric/v1/metrics",
172 "namespace": "ricxapp"
188 type ConfigSample struct {
193 type MockedConfigMapper struct {
196 func (cm *MockedConfigMapper) ReadSchema(name string, c *models.XAppConfig) (err error) {
200 func (cm *MockedConfigMapper) UploadConfig() (cfg []models.XAppConfig) {
204 func (cm *MockedConfigMapper) UpdateConfigMap(r models.XAppConfig) (errList models.ConfigValidationErrors, err error) {
208 func (cm *MockedConfigMapper) ReadConfigMap(name string, ns string, c *interface{}) (err error) {
212 func (cm *MockedConfigMapper) FetchChart(name string) (err error) {
216 func (cm *MockedConfigMapper) GetRtmData(name string) (msgs appmgr.RtmData) {
220 func (cm *MockedConfigMapper) GetNamespace(ns string) (n string) {
224 func (cm *MockedConfigMapper) GetNamesFromHelmRepo() (names []string) {
229 func TestMain(m *testing.M) {
231 appmgr.Logger.SetLevel(0)
237 func TestUploadConfigAllSuccess(t *testing.T) {
239 var expectedResult models.AllXappConfig
241 xapps := []string{"anr", "appmgr", "dualco", "reporter", "uemgr"}
243 if ret := json.Unmarshal([]byte(cfgData), &cfg); ret != nil {
244 t.Errorf("UploadConfigAll Json unmarshal failed: %v", ret)
247 for i, _ := range xapps {
248 expectedResult = append(expectedResult,
251 Metadata: &models.ConfigMetadata{
259 defer func() { resetHelmExecMock() }()
260 helmExec = mockedHelmExec
261 //Fake helm search success
262 helmExecRetOut = helmSearchOutput
264 defer func() { resetKubeExecMock() }()
265 kubeExec = mockedKubeExec
266 //Fake 'kubectl get configmap' success
267 kubeExecRetOut = strings.ReplaceAll(cfgData, "\\", "")
269 result := NewCM().UploadConfigAll()
270 if !reflect.DeepEqual(result, expectedResult) {
271 t.Errorf("UploadConfigAll failed: expected: %v, got: %v", expectedResult, result)
273 if caughtHelmExecArgs != expectedHelmSearchCmd {
274 t.Errorf("UploadConfigAll failed: expected: %v, got: %v", expectedHelmSearchCmd, caughtHelmExecArgs)
276 if !reflect.DeepEqual(caughtKubeExecArgs, expectedKubectlGetCmd) {
277 t.Errorf("UploadConfigAll failed: expected: %v, got: %v", expectedKubectlGetCmd, caughtKubeExecArgs)
281 func TestUploadConfigAllReturnsEmptyMapIfAllConfigMapReadsFail(t *testing.T) {
282 var expectedResult models.AllXappConfig
284 defer func() { resetHelmExecMock() }()
285 helmExec = mockedHelmExec
286 //Fake helm search success
287 helmExecRetOut = helmSearchOutput
289 defer func() { resetKubeExecMock() }()
290 kubeExec = mockedKubeExec
291 //Fake 'kubectl get configmap' failure
292 kubeExecRetErr = errors.New("some error")
294 result := NewCM().UploadConfigAll()
295 if !reflect.DeepEqual(result, expectedResult) {
296 t.Errorf("UploadConfigAll failed: expected: %v, got: %v", expectedResult, result)
300 func TestUploadConfigElementSuccess(t *testing.T) {
302 var expectedResult models.AllXappConfig
304 xapps := []string{"anr", "appmgr", "dualco", "reporter", "uemgr"}
306 if ret := json.Unmarshal([]byte(cfgData), &cfg); ret != nil {
307 t.Errorf("UploadConfigElement Json unmarshal failed: %v", ret)
310 for i, _ := range xapps {
311 expectedResult = append(expectedResult,
313 Config: cfg.(map[string]interface{})["active"],
314 Metadata: &models.ConfigMetadata{
322 defer func() { resetHelmExecMock() }()
323 helmExec = mockedHelmExec
324 //Fake helm search success
325 helmExecRetOut = helmSearchOutput
327 defer func() { resetKubeExecMock() }()
328 kubeExec = mockedKubeExec
329 //Fake 'kubectl get configmap' success
330 kubeExecRetOut = strings.ReplaceAll(cfgData, "\\", "")
332 result := NewCM().UploadConfigElement("active")
333 if !reflect.DeepEqual(result, expectedResult) {
334 t.Errorf("UploadConfigElement failed: expected: %v, got: %v", expectedResult, result)
336 if caughtHelmExecArgs != expectedHelmSearchCmd {
337 t.Errorf("UploadConfigElement failed: expected: %v, got: %v", expectedHelmSearchCmd, caughtHelmExecArgs)
339 if !reflect.DeepEqual(caughtKubeExecArgs, expectedKubectlGetCmd) {
340 t.Errorf("UploadConfigElement failed: expected: %v, got: %v", expectedKubectlGetCmd, caughtKubeExecArgs)
344 func TestUploadConfigElementReturnsEmptyMapIfElementMissingFromConfigMap(t *testing.T) {
345 var expectedResult models.AllXappConfig
347 defer func() { resetHelmExecMock() }()
348 helmExec = mockedHelmExec
349 //Fake helm search success
350 helmExecRetOut = helmSearchOutput
352 defer func() { resetKubeExecMock() }()
353 kubeExec = mockedKubeExec
354 //Fake 'kubectl get configmap' success
355 kubeExecRetOut = strings.ReplaceAll(cfgData, "\\", "")
357 //Try to upload non-existing configuration element
358 result := NewCM().UploadConfigElement("some-not-existing-element")
359 if !reflect.DeepEqual(result, expectedResult) {
360 t.Errorf("UploadConfigElement failed: expected: %v, got: %v", expectedResult, result)
364 func TestGetRtmDataSuccess(t *testing.T) {
365 expectedKubeCmd := []string{
366 `get configmap -o jsonpath='{.data.config-file\.json}' -n ricxapp configmap-ricxapp-dummy-xapp-appconfig`,
368 expectedMsgs := appmgr.RtmData{
369 TxMessages: []string{"RIC_X2_LOAD_INFORMATION"},
370 RxMessages: []string{"RIC_X2_LOAD_INFORMATION"},
371 Policies: []int64{11, 22, 33},
374 defer func() { resetKubeExecMock() }()
375 kubeExec = mockedKubeExec
376 //Fake 'kubectl get configmap' success
377 kubeExecRetOut = kubectlConfigmapOutput
379 result := NewCM().GetRtmData("dummy-xapp")
380 if !reflect.DeepEqual(result, expectedMsgs) {
381 t.Errorf("GetRtmData failed: expected: %v, got: %v", expectedMsgs, result)
383 if !reflect.DeepEqual(caughtKubeExecArgs, expectedKubeCmd) {
384 t.Errorf("GetRtmData failed: expected: '%v', got: '%v'", expectedKubeCmd, caughtKubeExecArgs)
388 func TestGetRtmDataNewSuccess(t *testing.T) {
389 expectedKubeCmd := []string{
390 `get configmap -o jsonpath='{.data.config-file\.json}' -n ricxapp configmap-ricxapp-dummy-xapp-appconfig`,
392 expectedMsgs := appmgr.RtmData{
393 TxMessages: []string{"RIC_X2_LOAD_INFORMATION"},
394 RxMessages: []string{"RIC_X2_LOAD_INFORMATION"},
395 Policies: []int64{11, 22, 33},
398 defer func() { resetKubeExecMock() }()
399 kubeExec = mockedKubeExec
400 //Fake 'kubectl get configmap' success
401 kubeExecRetOut = kubectlNewConfigmapOutput
403 result := NewCM().GetRtmData("dummy-xapp")
404 if !reflect.DeepEqual(result, expectedMsgs) {
405 t.Errorf("GetRtmData failed: expected: %v, got: %v", expectedMsgs, result)
407 if !reflect.DeepEqual(caughtKubeExecArgs, expectedKubeCmd) {
408 t.Errorf("GetRtmData failed: expected: '%v', got: '%v'", expectedKubeCmd, caughtKubeExecArgs)
412 func TestGetRtmDataReturnsNoDataIfConfigmapGetFails(t *testing.T) {
413 var expectedMsgs appmgr.RtmData
415 defer func() { resetKubeExecMock() }()
416 kubeExec = mockedKubeExec
417 //Fake 'kubectl get configmap' failure
418 kubeExecRetErr = errors.New("some error")
420 result := NewCM().GetRtmData("dummy-xapp")
421 if !reflect.DeepEqual(result, expectedMsgs) {
422 t.Errorf("GetRtmData failed: expected: %v, got: %v", expectedMsgs, result)
426 func TestGetRtmDataReturnsNoDataIfJsonParseFails(t *testing.T) {
427 var expectedMsgs appmgr.RtmData
429 defer func() { resetKubeExecMock() }()
430 kubeExec = mockedKubeExec
431 //Fake 'kubectl get configmap' to return nothing what will cause JSON parse failure
433 result := NewCM().GetRtmData("dummy-xapp")
434 if !reflect.DeepEqual(result, expectedMsgs) {
435 t.Errorf("GetRtmData failed: expected: %v, got: %v", expectedMsgs, result)
439 func TestHelmNamespace(t *testing.T) {
440 if NewCM().GetNamespace("pltxapp") != "pltxapp" {
441 t.Errorf("getNamespace failed!")
444 if NewCM().GetNamespace("") != "ricxapp" {
445 t.Errorf("getNamespace failed!")
449 func TestFetchChartFails(t *testing.T) {
450 if NewCM().FetchChart("dummy-xapp") == nil {
451 t.Errorf("TestFetchChart failed!")
455 func TestFetchChartSuccess(t *testing.T) {
456 defer func() { resetHelmExecMock() }()
457 helmExec = mockedHelmExec
459 if NewCM().FetchChart("dummy-xapp") != nil {
460 t.Errorf("TestFetchChart failed!")
464 func TestGetNamespaceSuccess(t *testing.T) {
465 if ns := NewCM().GetNamespace("my-ns"); ns != "my-ns" {
466 t.Errorf("GetNamespace failed: expected: my-ns, got: %s", ns)
470 func TestGetNamespaceReturnsConfiguredNamespaceName(t *testing.T) {
471 if ns := NewCM().GetNamespace(""); ns != "ricxapp" {
472 t.Errorf("GetNamespace failed: expected: ricxapp, got: %s", ns)
476 func TestGetNamesFromHelmRepoSuccess(t *testing.T) {
477 expectedResult := []string{"anr", "appmgr", "dualco", "reporter", "uemgr"}
479 defer func() { resetHelmExecMock() }()
480 helmExec = mockedHelmExec
481 //Fake helm search success
482 helmExecRetOut = helmSearchOutput
484 names := NewCM().GetNamesFromHelmRepo()
485 if !reflect.DeepEqual(names, expectedResult) {
486 t.Errorf("GetNamesFromHelmRepo failed: expected %v, got %v", expectedResult, names)
488 if caughtHelmExecArgs != expectedHelmSearchCmd {
489 t.Errorf("GetNamesFromHelmRepo failed: expected: %v, got: %v", expectedHelmSearchCmd, caughtHelmExecArgs)
493 func TestGetNamesFromHelmRepoFailure(t *testing.T) {
494 expectedResult := []string{}
496 defer func() { resetHelmExecMock() }()
497 helmExec = mockedHelmExec
498 helmExecRetOut = helmSearchOutput
499 helmExecRetErr = errors.New("Command failed!")
502 names := NewCM().GetNamesFromHelmRepo()
504 t.Errorf("GetNamesFromHelmRepo failed: expected %v, got %v", expectedResult, names)
508 func TestBuildConfigMapSuccess(t *testing.T) {
509 expectedKubeCmd := []string{
510 `get configmap -o jsonpath='{.data.config-file\.json}' -n ricxapp configmap-ricxapp-dummy-xapp-appconfig`,
513 namespace := "ricxapp"
514 m := models.ConfigMetadata{XappName: &name, Namespace: &namespace}
515 s := `{"Metadata": {"XappName": "ueec", "Namespace": "ricxapp"}, ` +
516 `"Config": {"active": true, "interfaceId":{"globalENBId": {"eNBId": 77, "plmnId": "6666"}}}}`
518 defer func() { resetKubeExecMock() }()
519 kubeExec = mockedKubeExec
520 //Fake 'kubectl get configmap' success
521 kubeExecRetOut = `{"logger": {"level": 2}}`
523 cmString, err := NewCM().BuildConfigMap(models.XAppConfig{Metadata: &m, Config: s})
525 t.Errorf("BuildConfigMap failed: %v -> %v", err, cmString)
527 if !reflect.DeepEqual(caughtKubeExecArgs, expectedKubeCmd) {
528 t.Errorf("BuildConfigMap failed: expected: %v, got: %v", expectedKubeCmd, caughtKubeExecArgs)
532 func TestBuildConfigMapReturnErrorIfJsonMarshalFails(t *testing.T) {
534 namespace := "ricxapp"
535 m := models.ConfigMetadata{XappName: &name, Namespace: &namespace}
536 //Give channel as a configuration input, this will fail JSON marshal
537 cmString, err := NewCM().BuildConfigMap(models.XAppConfig{Metadata: &m, Config: make(chan int)})
539 t.Errorf("BuildConfigMap failed: %v -> %v", err, cmString)
543 func TestBuildConfigMapReturnErrorIfKubectlGetConfigmapFails(t *testing.T) {
545 namespace := "ricxapp"
546 m := models.ConfigMetadata{XappName: &name, Namespace: &namespace}
547 s := `{"Metadata": {"XappName": "ueec", "Namespace": "ricxapp"}, ` +
548 `"Config": {"active": true, "interfaceId":{"globalENBId": {"eNBId": 77, "plmnId": "6666"}}}}`
550 defer func() { resetKubeExecMock() }()
551 kubeExec = mockedKubeExec
552 //Fake 'kubectl get configmap' failure
553 kubeExecRetErr = errors.New("some error")
555 cmString, err := NewCM().BuildConfigMap(models.XAppConfig{Metadata: &m, Config: s})
557 t.Errorf("BuildConfigMap failed: %v -> %v", err, cmString)
558 } else if err.Error() != "some error" {
559 t.Errorf("BuildConfigMap failed: expected: 'some error', got: '%s'", err.Error())
563 func TestBuildConfigMapReturnErrorIfJsonParserFails(t *testing.T) {
565 namespace := "ricxapp"
566 m := models.ConfigMetadata{XappName: &name, Namespace: &namespace}
567 s := `{"Metadata": {"XappName": "ueec", "Namespace": "ricxapp"}, ` +
568 `"Config": {"active": true, "interfaceId":{"globalENBId": {"eNBId": 77, "plmnId": "6666"}}}}`
570 defer func() { resetKubeExecMock() }()
571 kubeExec = mockedKubeExec
572 //Return empty json that causes JSON parser to fail
575 cmString, err := NewCM().BuildConfigMap(models.XAppConfig{Metadata: &m, Config: s})
577 t.Errorf("BuildConfigMap failed: %v -> %v", err, cmString)
581 func TestGenerateJSONFileSuccess(t *testing.T) {
582 err := NewCM().GenerateJSONFile("{}")
584 t.Errorf("GenerateJSONFile failed: %v", err)
588 func TestReplaceConfigMapSuccess(t *testing.T) {
590 namespace := "ricxapp"
592 defer func() { resetKubeExecMock() }()
593 kubeExec = mockedKubeExec
594 //Fake 'kubectl create configmap' success
597 err := NewCM().ReplaceConfigMap(name, namespace)
599 t.Errorf("ReplaceConfigMap failed: %v", err)
603 func TestUpdateConfigMapReturnsErrorIfSchemaFileIsMissing(t *testing.T) {
605 namespace := "ricxapp"
606 config := models.XAppConfig{Metadata: &models.ConfigMetadata{XappName: &name, Namespace: &namespace}}
608 defer func() { resetHelmExecMock() }()
609 helmExec = mockedHelmExec
610 helmExecRetOut = `{}`
612 //Will fail at schema reading, because schema file is mission
613 validationErrors, err := NewCM().UpdateConfigMap(config)
615 t.Errorf("UpdateConfigMap failed: %v -> %v", err, validationErrors)
617 if caughtHelmExecArgs != expectedHelmFetchCmd {
618 t.Errorf("UpdateConfigMap failed: expected: %v, got: %v", expectedHelmFetchCmd, caughtHelmExecArgs)
622 func TestUpdateConfigMapReturnsErrorIfHelmFetchChartFails(t *testing.T) {
624 namespace := "ricxapp"
625 config := models.XAppConfig{Metadata: &models.ConfigMetadata{XappName: &name, Namespace: &namespace}}
627 defer func() { resetHelmExecMock() }()
628 helmExec = mockedHelmExec
629 helmExecRetErr = errors.New("some error")
631 validationErrors, err := NewCM().UpdateConfigMap(config)
633 t.Errorf("UpdateConfigMap failed: %v -> %v", err, validationErrors)
637 func TestValidationSuccess(t *testing.T) {
639 var cfg map[string]interface{}
640 err := json.Unmarshal([]byte(`{"active": true, "interfaceId":{"globalENBId": {"eNBId": 77, "plmnId": "6666"}}}`), &cfg)
642 err = NewCM().ReadFile("../../test/schema.json", &d)
644 t.Errorf("ReadFile failed: %v -> %v", err, d)
647 feedback, err := NewCM().doValidate(d, cfg)
649 t.Errorf("doValidate failed: %v -> %v", err, feedback)
653 func TestValidationFails(t *testing.T) {
655 var cfg map[string]interface{}
657 err := json.Unmarshal([]byte(`{"active": "INVALID", "interfaceId":{"globalENBId": {"eNBId": 77, "plmnId": "6666"}}}`), &cfg)
659 err = NewCM().ReadFile("../../test/schema.json", &d)
661 t.Errorf("ConfigMetadata failed: %v -> %v", err, d)
664 feedback, err := NewCM().doValidate(d, cfg)
666 t.Errorf("doValidate should fail but didn't: %v -> %v", err, feedback)
668 appmgr.Logger.Debug("Feedbacks: %v", feedback)
671 func TestReadFileReturnsErrorIfFileReadFails(t *testing.T) {
674 if err := NewCM().ReadFile("not/existing/test/schema.json", &d); err == nil {
675 t.Errorf("ReadFile should fail but it didn't")
679 func TestReadFileReturnsErrorIfJsonUnmarshalFails(t *testing.T) {
682 if err := NewCM().ReadFile("../../test/faulty_schema.json", &d); err == nil {
683 t.Errorf("ReadFile should fail but it didn't")
687 func mockedKubeExec(args string) (out []byte, err error) {
688 caughtKubeExecArgs = append(caughtKubeExecArgs, args)
689 return []byte(kubeExecRetOut), kubeExecRetErr
692 func resetKubeExecMock() {
693 kubeExec = util.KubectlExec
694 caughtKubeExecArgs = nil
699 func mockedHelmExec(args string) (out []byte, err error) {
700 caughtHelmExecArgs = args
701 return []byte(helmExecRetOut), helmExecRetErr
704 func resetHelmExecMock() {
705 helmExec = util.HelmExec
706 caughtHelmExecArgs = ""