From: Mohamed Abukar Date: Tue, 14 Jan 2020 09:10:16 +0000 (+0200) Subject: Enhance config handling X-Git-Tag: v0.4.3~4 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;h=aca8f3cfeaa7e6af5245c2f0370ef517254d62f2;p=ric-plt%2Fappmgr.git Enhance config handling Change-Id: Id63d2cc4461daaab4d0b6a2990a5f108bbe97e73 Signed-off-by: Mohamed Abukar --- diff --git a/api/appmgr_rest_api.yaml b/api/appmgr_rest_api.yaml index 41947b2..d744539 100755 --- a/api/appmgr_rest_api.yaml +++ b/api/appmgr_rest_api.yaml @@ -1,7 +1,7 @@ swagger: '2.0' info: description: This is a draft API for RIC appmgr - version: 0.2.0 + version: 0.3.3 title: RIC appmgr license: name: Apache 2.0 @@ -158,89 +158,7 @@ paths: description: Xapp not found '500': description: Internal error - /xapps/{xAppName}/instances/{xAppInstanceName}/start: - put: - summary: Start given xapp instance - tags: - - xapp - operationId: startXappInstanceByName - produces: - - application/json - parameters: - - name: xAppName - in: path - description: Name of xApp - required: true - type: string - - name: xAppInstanceName - in: path - description: Name of xApp instance to get information - required: true - type: string - responses: - '200': - description: successful operation - '400': - description: Invalid name supplied - '404': - description: Xapp not found - '500': - description: Internal error - /xapps/{xAppName}/instances/{xAppInstanceName}/stop: - put: - summary: Stop given xapp instance - tags: - - xapp - operationId: stopXappInstanceByName - produces: - - application/json - parameters: - - name: xAppName - in: path - description: Name of xApp - required: true - type: string - - name: xAppInstanceName - in: path - description: Name of xApp instance to get information - required: true - type: string - responses: - '200': - description: successful operation - '400': - description: Invalid name supplied - '404': - description: Xapp not found - '500': - description: Internal error /config: - post: - summary: Create xApp config - tags: - - xapp - operationId: createXappConfig - consumes: - - application/json - produces: - - application/json - parameters: - - name: XAppConfig - in: body - description: xApp config - schema: - $ref: '#/definitions/XAppConfig' - responses: - '201': - description: xApp config successfully created - schema: - $ref: '#/definitions/ConfigValidationErrors' - '400': - description: Invalid input - '422': - description: Validation of configuration failed - '500': - description: Internal error put: summary: Modify xApp config tags: @@ -281,43 +199,25 @@ paths: $ref: '#/definitions/AllXappConfig' '500': description: Internal error - delete: - summary: Delete xApp configuration - tags: - - xapp - operationId: deleteXappConfig - parameters: - - name: ConfigMetadata - in: body - description: xApp configuration information - schema: - $ref: '#/definitions/ConfigMetadata' - responses: - '204': - description: Successful deletion of xApp config - '400': - description: Invalid parameters supplied - '500': - description: Internal error - /config/{configName}: + /config/{element}: get: - summary: Returns the configuration of a single xapp + summary: Returns the given element of the configuration tags: - xapp - operationId: getXappConfig + operationId: GetConfigElement produces: - application/json parameters: - - name: configName + - name: element in: path - description: Name of xApp + description: Name of configuration element required: true type: string responses: '200': - description: successful query of xApp config + description: successful query of config elements schema: - $ref: '#/definitions/XAppConfig' + $ref: '#/definitions/AllXappConfig' '500': description: Internal error /subscriptions: @@ -522,14 +422,12 @@ definitions: ConfigMetadata: type: object required: - - name + - xappName + - namespace properties: - name: + xappName: type: string description: Name of the xApp - configName: - type: string - description: Name of the config map namespace: type: string description: Name of the namespace @@ -553,14 +451,10 @@ definitions: type: object required: - metadata - - descriptor - config properties: metadata: $ref: '#/definitions/ConfigMetadata' - descriptor: - type: object - description: Schema of configuration in JSON format config: type: object description: Configuration in JSON format diff --git a/container-tag.yaml b/container-tag.yaml index 61d72fb..fe1699b 100755 --- a/container-tag.yaml +++ b/container-tag.yaml @@ -1,4 +1,4 @@ # The Jenkins job uses this string for the tag in the image name # for example nexus3.o-ran-sc.org:10004/my-image-name:my-tag --- -tag: '0.3.2' +tag: '0.3.3' diff --git a/go.mod b/go.mod index 253e4a2..535dd89 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( gerrit.o-ran-sc.org/r/com/golog v0.0.1 gerrit.oran-osc.org/r/ric-plt/sdlgo v0.0.0 github.com/BurntSushi/toml v0.3.1 // indirect + github.com/RaveNoX/go-jsonmerge v1.0.0 github.com/fsnotify/fsnotify v1.4.7 github.com/ghodss/yaml v1.0.0 github.com/go-openapi/errors v0.19.2 diff --git a/go.sum b/go.sum index 3ffdc4b..73d7342 100644 --- a/go.sum +++ b/go.sum @@ -10,10 +10,14 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= +github.com/RaveNoX/go-jsonmerge v1.0.0 h1:2e0nqnadoGUP8rAvcA0hkQelZreVO5X3BHomT2XMrAk= +github.com/RaveNoX/go-jsonmerge v1.0.0/go.mod h1:qYM/NA77LhO4h51JJM7Z+xBU3ovqrNIACZe+SkSNVFo= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -100,6 +104,7 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= @@ -137,6 +142,7 @@ github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/pkg/appmgr/appmgr.go b/pkg/appmgr/appmgr.go index 03bcdcf..2bd1d59 100755 --- a/pkg/appmgr/appmgr.go +++ b/pkg/appmgr/appmgr.go @@ -68,5 +68,5 @@ func loadConfig() { func Init() { loadConfig() Logger = logger.NewLogger("appmgr") - Logger.SetMdc("xm", "0.3.0") + Logger.SetMdc("xm", "0.3.3") } diff --git a/pkg/cm/cm.go b/pkg/cm/cm.go index a073c0a..fb80276 100755 --- a/pkg/cm/cm.go +++ b/pkg/cm/cm.go @@ -23,16 +23,15 @@ import ( "encoding/json" "errors" "fmt" - "github.com/spf13/viper" - "github.com/valyala/fastjson" - "github.com/xeipuuv/gojsonschema" "io/ioutil" "os" "path" "regexp" "strings" "strconv" - "time" + "github.com/spf13/viper" + "github.com/valyala/fastjson" + "github.com/xeipuuv/gojsonschema" "gerrit.oran-osc.org/r/ric-plt/appmgr/pkg/appmgr" "gerrit.oran-osc.org/r/ric-plt/appmgr/pkg/models" @@ -45,44 +44,54 @@ func NewCM() *CM { return &CM{} } -func (cm *CM) UploadConfig() (cfg models.AllXappConfig) { - ns := cm.GetNamespace("") +func (cm *CM) UploadConfigAll() (configList models.AllXappConfig) { + return cm.UploadConfigElement("") +} + +func (cm *CM) UploadConfigElement(Element string) (configList models.AllXappConfig) { + namespace := cm.GetNamespace("") for _, name := range cm.GetNamesFromHelmRepo() { - if name == "appmgr" { + var activeConfig interface{} + xAppName := name + if err := cm.GetConfigmap(xAppName, namespace, &activeConfig); err != nil { + appmgr.Logger.Info("No active configMap found for '%s', ignoring ...", xAppName) continue } - c := models.XAppConfig{ - Metadata: &models.ConfigMetadata{Name: &name, Namespace: ns, ConfigName: cm.GetConfigMapName(name, ns)}, - } - - err := cm.ReadSchema(name, &c) - if err != nil { - continue + if Element != "" { + m := activeConfig.(map[string]interface{}) + if m[Element] == nil { + appmgr.Logger.Info("xApp '%s' doesn't have requested element '%s' in config", name, Element) + continue + } + activeConfig = m[Element] } - err = cm.ReadConfigMap(c.Metadata.ConfigName, ns, &c.Config) - if err != nil { - appmgr.Logger.Info("No active configMap found, using default!") + c := models.XAppConfig{ + Metadata: &models.ConfigMetadata{XappName: &xAppName, Namespace: &namespace}, + Config: activeConfig, } - - cfg = append(cfg, &c) + configList = append(configList, &c) } return } -func (cm *CM) ReadSchema(name string, c *models.XAppConfig) (err error) { - if err = cm.FetchChart(name); err != nil { - return +func (cm *CM) GetConfigmap(name, namespace string, c *interface{}) (err error) { + cmJson, err := cm.ReadConfigmap(name, namespace) + if err != nil { + return err } - tarDir := viper.GetString("xapp.tarDir") - err = cm.ReadFile(path.Join(tarDir, name, viper.GetString("xapp.schema")), &c.Descriptor) - if err != nil { + return json.Unmarshal([]byte(cmJson), &c) +} + +func (cm *CM) ReadSchema(name string, desc *interface{}) (err error) { + if err = cm.FetchChart(name); err != nil { return } - err = cm.ReadFile(path.Join(tarDir, name, viper.GetString("xapp.config")), &c.Config) + tarDir := viper.GetString("xapp.tarDir") + err = cm.ReadFile(path.Join(tarDir, name, viper.GetString("xapp.schema")), desc) if err != nil { return } @@ -94,148 +103,71 @@ func (cm *CM) ReadSchema(name string, c *models.XAppConfig) (err error) { return } -func (cm *CM) ReadConfigMap(ConfigName string, ns string, c *interface{}) (err error) { - args := fmt.Sprintf("get configmap -o jsonpath='{.data.config-file\\.json}' -n %s %s", ns, ConfigName) - configMapJson, err := util.KubectlExec(args) - if err != nil { - return +func (cm *CM) UpdateConfigMap(r models.XAppConfig) (models.ConfigValidationErrors, error) { + fmt.Printf("Configmap update: xappName=%s namespace=%s config: %v", *r.Metadata.XappName, *r.Metadata.Namespace, r.Config) + if validationErrors, err := cm.Validate(r); err != nil { + return validationErrors, err } - err = json.Unmarshal([]byte(configMapJson), &c) + cmContent, err := cm.BuildConfigMap(r) if err != nil { - return + return nil, err } - return -} - -func (cm *CM) ApplyConfigMap(r models.XAppConfig, action string) (err error) { - c := appmgr.ConfigMap{ - Kind: "ConfigMap", - ApiVersion: "v1", - Metadata: appmgr.CMMetadata{Name: *r.Metadata.Name, Namespace: r.Metadata.Namespace}, - Data: r.Config, + if err := cm.GenerateJSONFile(cmContent); err != nil { + return nil, err } + err = cm.ReplaceConfigMap(*r.Metadata.XappName, *r.Metadata.Namespace) - cmJson, err := json.Marshal(c.Data) - if err != nil { - appmgr.Logger.Info("Config marshalling failed: %v", err) - return - } + return nil, err +} - cmFile := viper.GetString("xapp.tmpConfig") - err = ioutil.WriteFile(cmFile, cmJson, 0644) +func (cm *CM) BuildConfigMap(r models.XAppConfig) (string, error) { + configJson, err := json.Marshal(r.Config) if err != nil { - appmgr.Logger.Info("WriteFile failed: %v", err) - return + appmgr.Logger.Info("Config marshalling failed: %v", err) + return "", err } - cmd := " create configmap -n %s %s --from-file=%s -o json --dry-run | kubectl %s -f -" - args := fmt.Sprintf(cmd, r.Metadata.Namespace, r.Metadata.ConfigName, cmFile, action) - _, err = util.KubectlExec(args) + cmContent, err := cm.ReadConfigmap(*r.Metadata.XappName, *r.Metadata.Namespace) if err != nil { - return - } - appmgr.Logger.Info("Configmap changes done!") - - return -} - -func (cm *CM) GetConfigMap(m models.XappDescriptor, c *interface{}) (err error) { - return cm.ReadConfigMap(cm.GetConfigMapName(*m.XappName, m.Namespace), m.Namespace, c) -} - -func (cm *CM) CreateConfigMap(r models.XAppConfig) (errList models.ConfigValidationErrors, err error) { - if errList, err = cm.Validate(r); err != nil { - return - } - err = cm.ApplyConfigMap(r, "create") - return -} - -func (cm *CM) UpdateConfigMap(r models.XAppConfig) (errList models.ConfigValidationErrors, err error) { - if errList, err = cm.Validate(r); err != nil { - return + return "", err } - // Re-create the configmap with the new parameters - err = cm.ApplyConfigMap(r, "apply") - return -} - -func (cm *CM) DeleteConfigMap(r models.ConfigMetadata) (c interface{}, err error) { - err = cm.ReadConfigMap(r.ConfigName, r.Namespace, &c) + v, err := cm.ParseJson(cmContent) if err == nil { - args := fmt.Sprintf(" delete configmap --namespace=%s %s", r.Namespace, r.ConfigName) - _, err = util.KubectlExec(args) + v.Set("controls", fastjson.MustParse(string(configJson))) + fmt.Println(v.String()) + return v.String(), nil } - return -} - -func (cm *CM) PurgeConfigMap(m models.XappDescriptor) (c interface{}, err error) { - md := models.ConfigMetadata{Name: m.XappName, Namespace: m.Namespace, ConfigName: cm.GetConfigMapName(*m.XappName, m.Namespace)} - return cm.DeleteConfigMap(md) + return "", err } -func (cm *CM) RestoreConfigMap(m models.XappDescriptor, c interface{}) (err error) { - md := &models.ConfigMetadata{Name: m.XappName, Namespace: m.Namespace, ConfigName: cm.GetConfigMapName(*m.XappName, m.Namespace)} - time.Sleep(time.Duration(10 * time.Second)) - - return cm.ApplyConfigMap(models.XAppConfig{Metadata: md, Config: c}, "create") -} - -func (cm *CM) GetNamesFromHelmRepo() (names []string) { - rname := viper.GetString("helm.repo-name") - - cmdArgs := strings.Join([]string{"search ", rname}, "") - out, err := util.HelmExec(cmdArgs) +func (cm *CM) ParseJson(dsContent string) (*fastjson.Value, error) { + var p fastjson.Parser + v, err := p.Parse(dsContent) if err != nil { - return + appmgr.Logger.Info("fastjson.Parser failed: %v", err) } - - re := regexp.MustCompile(rname + `/.*`) - result := re.FindAllStringSubmatch(string(out), -1) - if result != nil { - var tmp string - for _, v := range result { - fmt.Sscanf(v[0], "%s", &tmp) - names = append(names, strings.Split(tmp, "/")[1]) - } - } - return names + return v, err } -func (cm *CM) Validate(req models.XAppConfig) (errList models.ConfigValidationErrors, err error) { - c := models.XAppConfig{} - err = cm.ReadSchema(*req.Metadata.Name, &c) + +func (cm *CM) GenerateJSONFile(jsonString string) error { + cmJson, err := json.RawMessage(jsonString).MarshalJSON() if err != nil { - appmgr.Logger.Info("No schema file found for '%s', aborting ...", *req.Metadata.Name) - return + appmgr.Logger.Error("Config marshalling failed: %v", err) + return err } - return cm.doValidate(c.Descriptor, req.Config) -} - -func (cm *CM) doValidate(schema, cfg interface{}) (errList models.ConfigValidationErrors, err error) { - schemaLoader := gojsonschema.NewGoLoader(schema) - documentLoader := gojsonschema.NewGoLoader(cfg) - result, err := gojsonschema.Validate(schemaLoader, documentLoader) + err = ioutil.WriteFile(viper.GetString("xapp.tmpConfig"), cmJson, 0644) if err != nil { - appmgr.Logger.Info("Validation failed: %v", err) - return + appmgr.Logger.Error("WriteFile failed: %v", err) + return err } - if result.Valid() == false { - appmgr.Logger.Info("The document is not valid, Errors: %v", result.Errors()) - for _, desc := range result.Errors() { - field := desc.Field() - validationError := desc.Description() - errList = append(errList, &models.ConfigValidationError{Field: &field, Error: &validationError}) - } - return errList, errors.New("Validation failed!") - } - return + return nil } func (cm *CM) ReadFile(name string, data interface{}) (err error) { @@ -254,6 +186,19 @@ func (cm *CM) ReadFile(name string, data interface{}) (err error) { return } +func (cm *CM) ReadConfigmap(name string, ns string) (string, error) { + args := fmt.Sprintf("get configmap -o jsonpath='{.data.config-file\\.json}' -n %s %s", ns, cm.GetConfigMapName(name, ns)) + out, err := util.KubectlExec(args) + return string(out), err +} + +func (cm *CM) ReplaceConfigMap(name, ns string) (error) { + cmd := " create configmap -n %s %s --from-file=%s -o json --dry-run | kubectl replace -f -" + args := fmt.Sprintf(cmd, ns, cm.GetConfigMapName(name, ns), viper.GetString("xapp.tmpConfig")) + _, err := util.KubectlExec(args) + return err +} + func (cm *CM) FetchChart(name string) (err error) { tarDir := viper.GetString("xapp.tarDir") repo := viper.GetString("helm.repo-name") @@ -283,9 +228,11 @@ func (cm *CM) GetRtmData(name string) (msgs appmgr.RtmData) { for _, m := range v.GetArray("rmr", "txMessages") { msgs.TxMessages = append(msgs.TxMessages, strings.Trim(m.String(), `"`)) } + for _, m := range v.GetArray("rmr", "rxMessages") { msgs.RxMessages = append(msgs.RxMessages, strings.Trim(m.String(), `"`)) } + for _, m := range v.GetArray("rmr", "policies") { if val, err := strconv.Atoi(strings.Trim(m.String(), `"`)); err == nil { msgs.Policies = append(msgs.Policies, int64(val)) @@ -310,3 +257,58 @@ func (cm *CM) GetNamespace(ns string) string { } return ns } + +func (cm *CM) GetNamesFromHelmRepo() (names []string) { + rname := viper.GetString("helm.repo-name") + + cmdArgs := strings.Join([]string{"search ", rname}, "") + out, err := util.HelmExec(cmdArgs) + if err != nil { + return + } + + re := regexp.MustCompile(rname + `/.*`) + result := re.FindAllStringSubmatch(string(out), -1) + if result != nil { + var tmp string + for _, v := range result { + fmt.Sscanf(v[0], "%s", &tmp) + names = append(names, strings.Split(tmp, "/")[1]) + } + } + return names +} + +func (cm *CM) Validate(req models.XAppConfig) (errList models.ConfigValidationErrors, err error) { + var desc interface{} + err = cm.ReadSchema(*req.Metadata.XappName, &desc) + if err != nil { + appmgr.Logger.Info("No schema file found for '%s', aborting ...", *req.Metadata.XappName) + return + } + return cm.doValidate(desc, req.Config) +} + +func (cm *CM) doValidate(schema, cfg interface{}) (errList models.ConfigValidationErrors, err error) { + schemaLoader := gojsonschema.NewGoLoader(schema) + documentLoader := gojsonschema.NewGoLoader(cfg) + + result, err := gojsonschema.Validate(schemaLoader, documentLoader) + if err != nil { + appmgr.Logger.Info("Validation failed: %v", err) + return + } + + if result.Valid() == false { + appmgr.Logger.Info("The document is not valid, Errors: %v", result.Errors()) + for _, desc := range result.Errors() { + field := desc.Field() + validationError := desc.Description() + errList = append(errList, &models.ConfigValidationError{Field: &field, Error: &validationError}) + } + return errList, errors.New("Validation failed!") + } + appmgr.Logger.Info("Config validation successful!") + + return +} \ No newline at end of file diff --git a/pkg/cm/cm_test.go b/pkg/cm/cm_test.go index 21d1d61..56b2250 100755 --- a/pkg/cm/cm_test.go +++ b/pkg/cm/cm_test.go @@ -79,38 +79,14 @@ func (cm *MockedConfigMapper) UploadConfig() (cfg []models.XAppConfig) { return } -func (cm *MockedConfigMapper) CreateConfigMap(r models.XAppConfig) (errList models.ConfigValidationErrors, err error) { - return -} - -func (cm *MockedConfigMapper) GetConfigMap(m models.XappDescriptor, c *interface{}) (err error) { - return -} - func (cm *MockedConfigMapper) UpdateConfigMap(r models.XAppConfig) (errList models.ConfigValidationErrors, err error) { return } -func (cm *MockedConfigMapper) DeleteConfigMap(r models.XAppConfig) (c interface{}, err error) { - return -} - -func (cm *MockedConfigMapper) PurgeConfigMap(m models.XappDescriptor) (c interface{}, err error) { - return -} - -func (cm *MockedConfigMapper) RestoreConfigMap(m models.XappDescriptor, c interface{}) (err error) { - return -} - func (cm *MockedConfigMapper) ReadConfigMap(name string, ns string, c *interface{}) (err error) { return } -func (cm *MockedConfigMapper) ApplyConfigMap(r models.XAppConfig, action string) (err error) { - return -} - func (cm *MockedConfigMapper) FetchChart(name string) (err error) { return } @@ -203,87 +179,37 @@ func TestGetNamesFromHelmRepoFailure(t *testing.T) { } } -func TestApplyConfigMapSuccess(t *testing.T) { - name := "dummy-xapp" - m := models.ConfigMetadata{Name: &name, Namespace: "ricxapp"} - s := ConfigSample{5, "localhost"} - - util.KubectlExec = func(args string) (out []byte, err error) { - return []byte(`{"logger": {"level": 2}}`), nil - } - - err := NewCM().ApplyConfigMap(models.XAppConfig{Metadata: &m, Config: s}, "create") - if err != nil { - t.Errorf("ApplyConfigMap failed: %v", err) - } -} - -func TestRestoreConfigMapSuccess(t *testing.T) { +func TestBuildConfigMapSuccess(t *testing.T) { name := "dummy-xapp" - m := models.XappDescriptor{XappName: &name, Namespace: "ricxapp"} - s := ConfigSample{5, "localhost"} + namespace := "ricxapp" + m := models.ConfigMetadata{XappName: &name, Namespace: &namespace} + s := `{"Metadata": {"XappName": "ueec", "Namespace": "ricxapp"}, "Config": {"active": true, "interfaceId":{"globalENBId": {"eNBId": 77, "plmnId": "6666"}}}}` util.KubectlExec = func(args string) (out []byte, err error) { return []byte(`{"logger": {"level": 2}}`), nil } - err := NewCM().RestoreConfigMap(m, s) + cmString, err := NewCM().BuildConfigMap(models.XAppConfig{Metadata: &m, Config: s}) if err != nil { - t.Errorf("RestoreConfigMap failed: %v", err) - } -} - -func TestDeleteConfigMapSuccess(t *testing.T) { - util.HelmExec = func(args string) (out []byte, err error) { - return []byte("ok"), nil - } - - util.KubectlExec = func(args string) (out []byte, err error) { - return []byte(`{"logger": {"level": 2}}`), nil - } - - validationErrors, err := NewCM().DeleteConfigMap(models.ConfigMetadata{}) - if err != nil { - t.Errorf("DeleteConfigMap failed: %v -> %v", err, validationErrors) - } -} - -func TestPurgeConfigMapSuccess(t *testing.T) { - util.HelmExec = func(args string) (out []byte, err error) { - return []byte("ok"), nil - } - - util.KubectlExec = func(args string) (out []byte, err error) { - return []byte(`{"logger": {"level": 2}}`), nil - } - - name := "dummy-xapp" - validationErrors, err := NewCM().PurgeConfigMap(models.XappDescriptor{XappName: &name}) - if err != nil { - t.Errorf("PurgeConfigMap failed: %v -> %v", err, validationErrors) - } -} - -func TestCreateConfigMapFails(t *testing.T) { - name := "dummy-xapp" - validationErrors, err := NewCM().CreateConfigMap(models.XAppConfig{Metadata: &models.ConfigMetadata{Name: &name}}) - if err == nil { - t.Errorf("CreateConfigMap failed: %v -> %v", err, validationErrors) + t.Errorf("BuildConfigMap failed: %v -> %v", err, cmString) } } func TestUpdateConfigMapFails(t *testing.T) { name := "dummy-xapp" - validationErrors, err := NewCM().UpdateConfigMap(models.XAppConfig{Metadata: &models.ConfigMetadata{Name: &name}}) + namespace := "ricxapp" + config := models.XAppConfig{Metadata: &models.ConfigMetadata{XappName: &name, Namespace: &namespace}} + + validationErrors, err := NewCM().UpdateConfigMap(config) if err == nil { - t.Errorf("CreateConfigMap failed: %v -> %v", err, validationErrors) + t.Errorf("UpdateConfigMap failed: %v -> %v", err, validationErrors) } } func TestValidationSuccess(t *testing.T) { var d interface{} var cfg map[string]interface{} - err := json.Unmarshal([]byte(`{"local": {"host": ":8080"}, "logger": {"level": 3}}`), &cfg) + err := json.Unmarshal([]byte(`{"active": true, "interfaceId":{"globalENBId": {"eNBId": 77, "plmnId": "6666"}}}`), &cfg) err = NewCM().ReadFile("../../test/schema.json", &d) if err != nil { @@ -299,7 +225,7 @@ func TestValidationSuccess(t *testing.T) { func TestValidationFails(t *testing.T) { var d interface{} var cfg map[string]interface{} - err := json.Unmarshal([]byte(`{"local": {"host": ":8080"}, "logger": {"level": "INVALID"}}`), &cfg) + err := json.Unmarshal([]byte(`{"active": "INVALID", "interfaceId":{"globalENBId": {"eNBId": 77, "plmnId": "6666"}}}`), &cfg) err = NewCM().ReadFile("../../test/schema.json", &d) if err != nil { diff --git a/pkg/helm/helm.go b/pkg/helm/helm.go index 4f847a2..886d015 100755 --- a/pkg/helm/helm.go +++ b/pkg/helm/helm.go @@ -85,28 +85,25 @@ func (h *Helm) Init() (out []byte, err error) { func (h *Helm) AddRepo() (out []byte, err error) { // Get helm repo user name and password from files mounted by secret object - credFile, err := ioutil.ReadFile(viper.GetString("helm.helm-username-file")) + username, err := ioutil.ReadFile(viper.GetString("helm.helm-username-file")) if err != nil { appmgr.Logger.Info("helm_repo_username ReadFile failed: %v", err.Error()) return } - username := " --username " + string(credFile) - credFile, err = ioutil.ReadFile(viper.GetString("helm.helm-password-file")) + password, err := ioutil.ReadFile(viper.GetString("helm.helm-password-file")) if err != nil { appmgr.Logger.Info("helm_repo_password ReadFile failed: %v", err.Error()) return } - pwd := " --password " + string(credFile) - rname := viper.GetString("helm.repo-name") - repo := viper.GetString("helm.repo") + repoArgs := fmt.Sprintf(" %s %s ", viper.GetString("helm.repo-name"), viper.GetString("helm.repo")) + credentials := fmt.Sprintf(" --username %s --password %s", string(username), string(password)) - return util.HelmExec(strings.Join([]string{"repo add ", rname, " ", repo, username, pwd}, "")) + return util.HelmExec(strings.Join([]string{"repo add ", repoArgs, credentials}, "")) } func (h *Helm) Install(m models.XappDescriptor) (xapp models.Xapp, err error) { - var c interface{} m.Namespace = h.cm.GetNamespace(m.Namespace) out, err := h.Run(strings.Join([]string{"repo update "}, "")) @@ -114,29 +111,10 @@ func (h *Helm) Install(m models.XappDescriptor) (xapp models.Xapp, err error) { return } - if err = h.cm.GetConfigMap(m, &c); err != nil { - out, err = h.Run(h.GetInstallArgs(m, false)) - if err != nil { - return - } - return h.ParseStatus(*m.XappName, string(out)) - } - - // ConfigMap exists, try to override - out, err = h.Run(h.GetInstallArgs(m, true)) - if err == nil { - return h.ParseStatus(*m.XappName, string(out)) - } - - c, cmErr := h.cm.PurgeConfigMap(m) out, err = h.Run(h.GetInstallArgs(m, false)) if err != nil { return } - - if cmErr == nil { - cmErr = h.cm.RestoreConfigMap(m, c) - } return h.ParseStatus(*m.XappName, string(out)) } @@ -308,7 +286,8 @@ func (h *Helm) ParseStatus(name string, out string) (xapp models.Xapp, err error func (h *Helm) parseAllStatus(names []string) (xapps models.AllDeployedXapps, err error) { xapps = models.AllDeployedXapps{} for _, name := range names { - err := h.cm.ReadSchema(name, &models.XAppConfig{}) + var desc interface{} + err := h.cm.ReadSchema(name, &desc) if err != nil { continue } @@ -333,19 +312,19 @@ func (h *Helm) AddTillerEnv() (err error) { } func (h *Helm) GetInstallArgs(x models.XappDescriptor, cmOverride bool) (args string) { - args = args + " --namespace=" + x.Namespace + args = fmt.Sprintf("%s --namespace=%s", args, x.Namespace) if x.HelmVersion != "" { - args = args + " --version=" + x.HelmVersion + args = fmt.Sprintf("%s --version=%s", args, x.HelmVersion) } if x.ReleaseName != "" { - args = args + " --name=" + x.ReleaseName + args = fmt.Sprintf("%s --name=%s", args, x.ReleaseName) } else { - args = args + " --name=" + *x.XappName + args = fmt.Sprintf("%s --name=%s", args, *x.XappName) } if cmOverride == true { - args = args + " --set ricapp.appconfig.override=" + *x.XappName + "-appconfig" + args = fmt.Sprintf("%s ---set ricapp.appconfig.override=%s-appconfig", args, *x.XappName) } if x.OverrideFile != nil { diff --git a/pkg/helm/helm_test.go b/pkg/helm/helm_test.go index 9bc1226..2c08afb 100755 --- a/pkg/helm/helm_test.go +++ b/pkg/helm/helm_test.go @@ -85,7 +85,6 @@ func TestMain(m *testing.M) { } func TestHelmStatus(t *testing.T) { - //NewHelm().SetCM(&ConfigMap{}) util.KubectlExec = func(args string) (out []byte, err error) { return []byte("10.102.184.212"), nil } diff --git a/pkg/restful/restful.go b/pkg/restful/restful.go index b2a94e3..e4c149e 100755 --- a/pkg/restful/restful.go +++ b/pkg/restful/restful.go @@ -20,7 +20,6 @@ package restful import ( - //"github.com/spf13/viper" "log" "os" "time" @@ -77,7 +76,8 @@ func (r *Restful) SetupHandler() *operations.AppManagerAPI { func(params health.GetHealthAliveParams) middleware.Responder { return health.NewGetHealthAliveOK() }) - api.HealthGetHealthReadyHandler = health.GetHealthReadyHandlerFunc( + + api.HealthGetHealthReadyHandler = health.GetHealthReadyHandlerFunc( func(params health.GetHealthReadyParams) middleware.Responder { return health.NewGetHealthReadyOK() }) @@ -87,6 +87,7 @@ func (r *Restful) SetupHandler() *operations.AppManagerAPI { func(params operations.GetSubscriptionsParams) middleware.Responder { return operations.NewGetSubscriptionsOK().WithPayload(r.rh.GetAllSubscriptions()) }) + api.GetSubscriptionByIDHandler = operations.GetSubscriptionByIDHandlerFunc( func(params operations.GetSubscriptionByIDParams) middleware.Responder { if result, found := r.rh.GetSubscriptionById(params.SubscriptionID); found { @@ -94,10 +95,12 @@ func (r *Restful) SetupHandler() *operations.AppManagerAPI { } return operations.NewGetSubscriptionByIDNotFound() }) + api.AddSubscriptionHandler = operations.AddSubscriptionHandlerFunc( func(params operations.AddSubscriptionParams) middleware.Responder { return operations.NewAddSubscriptionCreated().WithPayload(r.rh.AddSubscription(*params.SubscriptionRequest)) }) + api.ModifySubscriptionHandler = operations.ModifySubscriptionHandlerFunc( func(params operations.ModifySubscriptionParams) middleware.Responder { if _, ok := r.rh.ModifySubscription(params.SubscriptionID, *params.SubscriptionRequest); ok { @@ -105,6 +108,7 @@ func (r *Restful) SetupHandler() *operations.AppManagerAPI { } return operations.NewModifySubscriptionBadRequest() }) + api.DeleteSubscriptionHandler = operations.DeleteSubscriptionHandlerFunc( func(params operations.DeleteSubscriptionParams) middleware.Responder { if _, ok := r.rh.DeleteSubscription(params.SubscriptionID); ok { @@ -121,6 +125,7 @@ func (r *Restful) SetupHandler() *operations.AppManagerAPI { } return xapp.NewGetAllXappsInternalServerError() }) + api.XappListAllXappsHandler = xapp.ListAllXappsHandlerFunc( func(params xapp.ListAllXappsParams) middleware.Responder { if result := r.helm.SearchAll(); err == nil { @@ -128,6 +133,7 @@ func (r *Restful) SetupHandler() *operations.AppManagerAPI { } return xapp.NewListAllXappsInternalServerError() }) + api.XappGetXappByNameHandler = xapp.GetXappByNameHandlerFunc( func(params xapp.GetXappByNameParams) middleware.Responder { if result, err := r.helm.Status(params.XAppName); err == nil { @@ -135,6 +141,7 @@ func (r *Restful) SetupHandler() *operations.AppManagerAPI { } return xapp.NewGetXappByNameNotFound() }) + api.XappGetXappInstanceByNameHandler = xapp.GetXappInstanceByNameHandlerFunc( func(params xapp.GetXappInstanceByNameParams) middleware.Responder { if result, err := r.helm.Status(params.XAppName); err == nil { @@ -146,6 +153,7 @@ func (r *Restful) SetupHandler() *operations.AppManagerAPI { } return xapp.NewGetXappInstanceByNameNotFound() }) + api.XappDeployXappHandler = xapp.DeployXappHandlerFunc( func(params xapp.DeployXappParams) middleware.Responder { if result, err := r.helm.Install(*params.XappDescriptor); err == nil { @@ -154,6 +162,7 @@ func (r *Restful) SetupHandler() *operations.AppManagerAPI { } return xapp.NewUndeployXappInternalServerError() }) + api.XappUndeployXappHandler = xapp.UndeployXappHandlerFunc( func(params xapp.UndeployXappParams) middleware.Responder { if result, err := r.helm.Delete(params.XAppName); err == nil { @@ -166,25 +175,18 @@ func (r *Restful) SetupHandler() *operations.AppManagerAPI { // URL: /ric/v1/config api.XappGetAllXappConfigHandler = xapp.GetAllXappConfigHandlerFunc( func(params xapp.GetAllXappConfigParams) middleware.Responder { - return xapp.NewGetAllXappConfigOK().WithPayload(r.cm.UploadConfig()) + return xapp.NewGetAllXappConfigOK().WithPayload(r.cm.UploadConfigAll()) }) - api.XappCreateXappConfigHandler = xapp.CreateXappConfigHandlerFunc( - func(params xapp.CreateXappConfigParams) middleware.Responder { - result, err := r.cm.CreateConfigMap(*params.XAppConfig) - if err == nil { - if err.Error() != "Validation failed!" { - return xapp.NewCreateXappConfigInternalServerError() - } else { - return xapp.NewCreateXappConfigUnprocessableEntity() - } - } - r.rh.PublishSubscription(models.Xapp{}, models.EventTypeCreated) - return xapp.NewCreateXappConfigCreated().WithPayload(result) + + api.XappGetConfigElementHandler = xapp.GetConfigElementHandlerFunc( + func(params xapp.GetConfigElementParams) middleware.Responder { + return xapp.NewGetConfigElementOK().WithPayload(r.cm.UploadConfigElement(params.Element)) }) + api.XappModifyXappConfigHandler = xapp.ModifyXappConfigHandlerFunc( func(params xapp.ModifyXappConfigParams) middleware.Responder { result, err := r.cm.UpdateConfigMap(*params.XAppConfig) - if err == nil { + if err != nil { if err.Error() != "Validation failed!" { return xapp.NewModifyXappConfigInternalServerError() } else { @@ -194,25 +196,6 @@ func (r *Restful) SetupHandler() *operations.AppManagerAPI { r.rh.PublishSubscription(models.Xapp{}, models.EventTypeModified) return xapp.NewModifyXappConfigOK().WithPayload(result) }) - api.XappDeleteXappConfigHandler = xapp.DeleteXappConfigHandlerFunc( - func(params xapp.DeleteXappConfigParams) middleware.Responder { - _, err := r.cm.DeleteConfigMap(*params.ConfigMetadata) - if err == nil { - return xapp.NewDeleteXappConfigInternalServerError() - } - r.rh.PublishSubscription(models.Xapp{}, models.EventTypeDeleted) - return xapp.NewDeleteXappConfigNoContent() - }) - - // LCM: /xapps/{xAppName}/instances/{xAppInstanceName}/stop/start - api.XappStartXappInstanceByNameHandler = xapp.StartXappInstanceByNameHandlerFunc( - func(params xapp.StartXappInstanceByNameParams) middleware.Responder { - return xapp.NewStartXappInstanceByNameOK() - }) - api.XappStopXappInstanceByNameHandler = xapp.StopXappInstanceByNameHandlerFunc( - func(params xapp.StopXappInstanceByNameParams) middleware.Responder { - return xapp.NewStopXappInstanceByNameOK() - }) return api } @@ -232,11 +215,11 @@ func (r *Restful) PublishXappCreateEvent(params xapp.DeployXappParams) { } for i := 0; i < 5; i++ { + time.Sleep(time.Duration(5) * time.Second) if result, _ := r.helm.Status(name); result.Instances != nil { r.rh.PublishSubscription(result, models.EventTypeDeployed) break } - time.Sleep(time.Duration(5) * time.Second) } } diff --git a/test/schema.json b/test/schema.json index bc89da8..f7b8ce4 100755 --- a/test/schema.json +++ b/test/schema.json @@ -5,42 +5,58 @@ "type": "object", "title": "The Root Schema", "required": [ - "local", - "logger" + "active", + "interfaceId" ], "properties": { - "local": { - "$id": "#/properties/local", - "type": "object", - "title": "The Local Schema", - "required": [ - "host" - ], - "properties": { - "host": { - "$id": "#/properties/local/properties/host", - "type": "string", - "title": "The Host Schema", - "default": "", - "pattern": "^(.*)$" - } - } + "active": { + "$id": "#/properties/active", + "type": "boolean", + "title": "The Active Schema", + "default": false, + "examples": [ + false + ] }, - "logger": { - "$id": "#/properties/logger", + "interfaceId": { + "$id": "#/properties/interfaceId", "type": "object", - "title": "The Logger Schema", + "title": "The Interfaceid Schema", "required": [ - "level" + "globalENBId" ], "properties": { - "level": { - "$id": "#/properties/logger/properties/level", - "type": "integer", - "title": "The Level Schema", - "default": 0 + "globalENBId": { + "$id": "#/properties/interfaceId/properties/globalENBId", + "type": "object", + "title": "The Globalenbid Schema", + "required": [ + "plmnId", + "eNBId" + ], + "properties": { + "plmnId": { + "$id": "#/properties/interfaceId/properties/globalENBId/properties/plmnId", + "type": "string", + "title": "The Plmnid Schema", + "default": "", + "examples": [ + "310150" + ], + "pattern": "^(.*)$" + }, + "eNBId": { + "$id": "#/properties/interfaceId/properties/globalENBId/properties/eNBId", + "type": "integer", + "title": "The Enbid Schema", + "default": 0, + "examples": [ + 202251 + ] + } + } } } } } -} \ No newline at end of file +}