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 ==================================================================================
26 "github.com/spf13/viper"
27 "github.com/valyala/fastjson"
28 "github.com/xeipuuv/gojsonschema"
36 "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/appmgr"
37 "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/models"
38 "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/util"
41 var kubeExec = util.KubectlExec
42 var helmExec = util.HelmExec
50 func (cm *CM) UploadConfigAll() (configList models.AllXappConfig) {
51 return cm.UploadConfigElement("")
54 func (cm *CM) UploadConfigElement(Element string) (configList models.AllXappConfig) {
55 namespace := cm.GetNamespace("")
56 for _, name := range cm.GetNamesFromHelmRepo() {
57 var activeConfig interface{}
59 if err := cm.GetConfigmap(xAppName, namespace, &activeConfig); err != nil {
60 appmgr.Logger.Info("No active configMap found for '%s', ignoring ...", xAppName)
65 m := activeConfig.(map[string]interface{})
66 if m[Element] == nil {
67 appmgr.Logger.Info("xApp '%s' doesn't have requested element '%s' in config", name, Element)
70 activeConfig = m[Element]
73 c := models.XAppConfig{
74 Metadata: &models.ConfigMetadata{XappName: &xAppName, Namespace: &namespace},
77 configList = append(configList, &c)
82 func (cm *CM) GetConfigmap(name, namespace string, c *interface{}) (err error) {
83 cmJson, err := cm.ReadConfigmap(name, namespace)
88 return json.Unmarshal([]byte(cmJson), &c)
91 func (cm *CM) ReadSchema(name string, desc *interface{}) (err error) {
92 if err = cm.FetchChart(name); err != nil {
96 tarDir := viper.GetString("xapp.tarDir")
97 err = cm.ReadFile(path.Join(tarDir, name, viper.GetString("xapp.schema")), desc)
102 if err = os.RemoveAll(path.Join(tarDir, name)); err != nil {
103 appmgr.Logger.Info("RemoveAll failed: %v", err)
109 func (cm *CM) UpdateConfigMap(r models.XAppConfig) (models.ConfigValidationErrors, error) {
110 fmt.Printf("Configmap update: xappName=%s namespace=%s config: %v\n", *r.Metadata.XappName, *r.Metadata.Namespace, r.Config)
111 if validationErrors, err := cm.Validate(r); err != nil {
112 return validationErrors, err
115 cmContent, err := cm.BuildConfigMap(r)
120 if err := cm.GenerateJSONFile(cmContent); err != nil {
123 err = cm.ReplaceConfigMap(*r.Metadata.XappName, *r.Metadata.Namespace)
128 func (cm *CM) BuildConfigMap(r models.XAppConfig) (string, error) {
129 configJson, err := json.Marshal(r.Config)
131 appmgr.Logger.Info("Config marshalling failed: %v", err)
135 cmContent, err := cm.ReadConfigmap(*r.Metadata.XappName, *r.Metadata.Namespace)
140 v, err := cm.ParseJson(cmContent)
142 v.Set("controls", fastjson.MustParse(string(configJson)))
143 fmt.Println(v.String())
144 return v.String(), nil
150 func (cm *CM) ParseJson(dsContent string) (*fastjson.Value, error) {
151 var p fastjson.Parser
152 v, err := p.Parse(dsContent)
154 appmgr.Logger.Info("fastjson.Parser failed: %v", err)
159 func (cm *CM) GenerateJSONFile(jsonString string) error {
160 cmJson, err := json.RawMessage(jsonString).MarshalJSON()
162 appmgr.Logger.Error("Config marshalling failed: %v", err)
166 err = ioutil.WriteFile(viper.GetString("xapp.tmpConfig"), cmJson, 0644)
168 appmgr.Logger.Error("WriteFile failed: %v", err)
175 func (cm *CM) ReadFile(name string, data interface{}) (err error) {
176 f, err := ioutil.ReadFile(name)
178 appmgr.Logger.Info("Reading '%s' file failed: %v", name, err)
182 err = json.Unmarshal(f, &data)
184 appmgr.Logger.Info("Unmarshalling '%s' file failed: %v", name, err)
191 func (cm *CM) ReadConfigmap(name string, ns string) (string, error) {
192 args := fmt.Sprintf("get configmap -o jsonpath='{.data.config-file\\.json}' -n %s %s", ns, cm.GetConfigMapName(name, ns))
193 out, err := kubeExec(args)
194 return string(out), err
197 func (cm *CM) ReplaceConfigMap(name, ns string) error {
198 cmd := " create configmap -n %s %s --from-file=%s -o json --dry-run | kubectl replace -f -"
199 args := fmt.Sprintf(cmd, ns, cm.GetConfigMapName(name, ns), viper.GetString("xapp.tmpConfig"))
200 _, err := kubeExec(args)
204 func (cm *CM) FetchChart(name string) (err error) {
205 tarDir := viper.GetString("xapp.tarDir")
206 repo := viper.GetString("helm.repo-name")
207 fetchArgs := fmt.Sprintf("--untar --untardir %s %s/%s", tarDir, repo, name)
209 _, err = helmExec(strings.Join([]string{"fetch ", fetchArgs}, ""))
213 func (cm *CM) GetRtmData(name string) (msgs appmgr.RtmData) {
214 appmgr.Logger.Info("Fetching RT data for xApp=%s", name)
216 ns := cm.GetNamespace("")
217 args := fmt.Sprintf("get configmap -o jsonpath='{.data.config-file\\.json}' -n %s %s", ns, cm.GetConfigMapName(name, ns))
218 out, err := kubeExec(args)
223 var p fastjson.Parser
224 v, err := p.Parse(string(out))
226 appmgr.Logger.Info("fastjson.Parser for '%s' failed: %v", name, err)
230 for _, m := range v.GetArray("rmr", "txMessages") {
231 msgs.TxMessages = append(msgs.TxMessages, strings.Trim(m.String(), `"`))
234 for _, m := range v.GetArray("rmr", "rxMessages") {
235 msgs.RxMessages = append(msgs.RxMessages, strings.Trim(m.String(), `"`))
238 for _, m := range v.GetArray("rmr", "policies") {
239 if val, err := strconv.Atoi(strings.Trim(m.String(), `"`)); err == nil {
240 msgs.Policies = append(msgs.Policies, int64(val))
247 func (cm *CM) GetConfigMapName(xappName, namespace string) string {
248 return " configmap-" + namespace + "-" + xappName + "-appconfig"
251 func (cm *CM) GetNamespace(ns string) string {
256 ns = viper.GetString("xapp.namespace")
263 func (cm *CM) GetNamesFromHelmRepo() (names []string) {
264 rname := viper.GetString("helm.repo-name")
266 cmdArgs := strings.Join([]string{"search ", rname}, "")
267 out, err := helmExec(cmdArgs)
272 re := regexp.MustCompile(rname + `/.*`)
273 result := re.FindAllStringSubmatch(string(out), -1)
276 for _, v := range result {
277 fmt.Sscanf(v[0], "%s", &tmp)
278 names = append(names, strings.Split(tmp, "/")[1])
284 func (cm *CM) Validate(req models.XAppConfig) (errList models.ConfigValidationErrors, err error) {
286 err = cm.ReadSchema(*req.Metadata.XappName, &desc)
288 appmgr.Logger.Info("No schema file found for '%s', aborting ...", *req.Metadata.XappName)
291 return cm.doValidate(desc, req.Config)
294 func (cm *CM) doValidate(schema, cfg interface{}) (errList models.ConfigValidationErrors, err error) {
295 schemaLoader := gojsonschema.NewGoLoader(schema)
296 documentLoader := gojsonschema.NewGoLoader(cfg)
298 result, err := gojsonschema.Validate(schemaLoader, documentLoader)
300 appmgr.Logger.Info("Validation failed: %v", err)
304 if result.Valid() == false {
305 appmgr.Logger.Info("The document is not valid, Errors: %v", result.Errors())
306 for _, desc := range result.Errors() {
307 field := desc.Field()
308 validationError := desc.Description()
309 errList = append(errList, &models.ConfigValidationError{Field: &field, Error: &validationError})
311 return errList, errors.New("Validation failed!")
313 appmgr.Logger.Info("Config validation successful!")