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!")
501 names := NewCM().GetNamesFromHelmRepo()
503 t.Errorf("GetNamesFromHelmRepo failed: expected %v, got %v", expectedResult, names)
507 func TestBuildConfigMapSuccess(t *testing.T) {
508 expectedKubeCmd := []string{
509 `get configmap -o jsonpath='{.data.config-file\.json}' -n ricxapp configmap-ricxapp-dummy-xapp-appconfig`,
512 namespace := "ricxapp"
513 m := models.ConfigMetadata{XappName: &name, Namespace: &namespace}
514 s := `{"Metadata": {"XappName": "ueec", "Namespace": "ricxapp"}, ` +
515 `"Config": {"active": true, "interfaceId":{"globalENBId": {"eNBId": 77, "plmnId": "6666"}}}}`
517 defer func() { resetKubeExecMock() }()
518 kubeExec = mockedKubeExec
519 //Fake 'kubectl get configmap' success
520 kubeExecRetOut = `{"logger": {"level": 2}}`
522 cmString, err := NewCM().BuildConfigMap(models.XAppConfig{Metadata: &m, Config: s})
524 t.Errorf("BuildConfigMap failed: %v -> %v", err, cmString)
526 if !reflect.DeepEqual(caughtKubeExecArgs, expectedKubeCmd) {
527 t.Errorf("BuildConfigMap failed: expected: %v, got: %v", expectedKubeCmd, caughtKubeExecArgs)
531 func TestBuildConfigMapReturnErrorIfJsonMarshalFails(t *testing.T) {
533 namespace := "ricxapp"
534 m := models.ConfigMetadata{XappName: &name, Namespace: &namespace}
535 //Give channel as a configuration input, this will fail JSON marshal
536 cmString, err := NewCM().BuildConfigMap(models.XAppConfig{Metadata: &m, Config: make(chan int)})
538 t.Errorf("BuildConfigMap failed: %v -> %v", err, cmString)
542 func TestBuildConfigMapReturnErrorIfKubectlGetConfigmapFails(t *testing.T) {
544 namespace := "ricxapp"
545 m := models.ConfigMetadata{XappName: &name, Namespace: &namespace}
546 s := `{"Metadata": {"XappName": "ueec", "Namespace": "ricxapp"}, ` +
547 `"Config": {"active": true, "interfaceId":{"globalENBId": {"eNBId": 77, "plmnId": "6666"}}}}`
549 defer func() { resetKubeExecMock() }()
550 kubeExec = mockedKubeExec
551 //Fake 'kubectl get configmap' failure
552 kubeExecRetErr = errors.New("some error")
554 cmString, err := NewCM().BuildConfigMap(models.XAppConfig{Metadata: &m, Config: s})
556 t.Errorf("BuildConfigMap failed: %v -> %v", err, cmString)
557 } else if err.Error() != "some error" {
558 t.Errorf("BuildConfigMap failed: expected: 'some error', got: '%s'", err.Error())
562 func TestBuildConfigMapReturnErrorIfJsonParserFails(t *testing.T) {
564 namespace := "ricxapp"
565 m := models.ConfigMetadata{XappName: &name, Namespace: &namespace}
566 s := `{"Metadata": {"XappName": "ueec", "Namespace": "ricxapp"}, ` +
567 `"Config": {"active": true, "interfaceId":{"globalENBId": {"eNBId": 77, "plmnId": "6666"}}}}`
569 defer func() { resetKubeExecMock() }()
570 kubeExec = mockedKubeExec
571 //Return empty json that causes JSON parser to fail
574 cmString, err := NewCM().BuildConfigMap(models.XAppConfig{Metadata: &m, Config: s})
576 t.Errorf("BuildConfigMap failed: %v -> %v", err, cmString)
580 func TestGenerateJSONFileSuccess(t *testing.T) {
581 err := NewCM().GenerateJSONFile("{}")
583 t.Errorf("GenerateJSONFile failed: %v", err)
587 func TestReplaceConfigMapSuccess(t *testing.T) {
589 namespace := "ricxapp"
591 defer func() { resetKubeExecMock() }()
592 kubeExec = mockedKubeExec
593 //Fake 'kubectl create configmap' success
596 err := NewCM().ReplaceConfigMap(name, namespace)
598 t.Errorf("ReplaceConfigMap failed: %v", err)
602 func TestUpdateConfigMapReturnsErrorIfSchemaFileIsMissing(t *testing.T) {
604 namespace := "ricxapp"
605 config := models.XAppConfig{Metadata: &models.ConfigMetadata{XappName: &name, Namespace: &namespace}}
607 defer func() { resetHelmExecMock() }()
608 helmExec = mockedHelmExec
609 helmExecRetOut = `{}`
611 //Will fail at schema reading, because schema file is mission
612 validationErrors, err := NewCM().UpdateConfigMap(config)
614 t.Errorf("UpdateConfigMap failed: %v -> %v", err, validationErrors)
616 if caughtHelmExecArgs != expectedHelmFetchCmd {
617 t.Errorf("UpdateConfigMap failed: expected: %v, got: %v", expectedHelmFetchCmd, caughtHelmExecArgs)
621 func TestUpdateConfigMapReturnsErrorIfHelmFetchChartFails(t *testing.T) {
623 namespace := "ricxapp"
624 config := models.XAppConfig{Metadata: &models.ConfigMetadata{XappName: &name, Namespace: &namespace}}
626 defer func() { resetHelmExecMock() }()
627 helmExec = mockedHelmExec
628 helmExecRetErr = errors.New("some error")
630 validationErrors, err := NewCM().UpdateConfigMap(config)
632 t.Errorf("UpdateConfigMap failed: %v -> %v", err, validationErrors)
636 func TestValidationSuccess(t *testing.T) {
638 var cfg map[string]interface{}
639 err := json.Unmarshal([]byte(`{"active": true, "interfaceId":{"globalENBId": {"eNBId": 77, "plmnId": "6666"}}}`), &cfg)
641 err = NewCM().ReadFile("../../test/schema.json", &d)
643 t.Errorf("ReadFile failed: %v -> %v", err, d)
646 feedback, err := NewCM().doValidate(d, cfg)
648 t.Errorf("doValidate failed: %v -> %v", err, feedback)
652 func TestValidationFails(t *testing.T) {
654 var cfg map[string]interface{}
656 err := json.Unmarshal([]byte(`{"active": "INVALID", "interfaceId":{"globalENBId": {"eNBId": 77, "plmnId": "6666"}}}`), &cfg)
658 err = NewCM().ReadFile("../../test/schema.json", &d)
660 t.Errorf("ConfigMetadata failed: %v -> %v", err, d)
663 feedback, err := NewCM().doValidate(d, cfg)
665 t.Errorf("doValidate should fail but didn't: %v -> %v", err, feedback)
667 appmgr.Logger.Debug("Feedbacks: %v", feedback)
670 func TestReadFileReturnsErrorIfFileReadFails(t *testing.T) {
673 if err := NewCM().ReadFile("not/existing/test/schema.json", &d); err == nil {
674 t.Errorf("ReadFile should fail but it didn't")
678 func TestReadFileReturnsErrorIfJsonUnmarshalFails(t *testing.T) {
681 if err := NewCM().ReadFile("../../test/faulty_schema.json", &d); err == nil {
682 t.Errorf("ReadFile should fail but it didn't")
686 func mockedKubeExec(args string) (out []byte, err error) {
687 caughtKubeExecArgs = append(caughtKubeExecArgs, args)
688 return []byte(kubeExecRetOut), kubeExecRetErr
691 func resetKubeExecMock() {
692 kubeExec = util.KubectlExec
693 caughtKubeExecArgs = nil
698 func mockedHelmExec(args string) (out []byte, err error) {
699 caughtHelmExecArgs = args
700 return []byte(helmExecRetOut), helmExecRetErr
703 func resetHelmExecMock() {
704 helmExec = util.HelmExec
705 caughtHelmExecArgs = ""