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
46 const HELM_VERSION_3 = "3"
47 const HELM_VERSION_2 = "2"
48 var EnvHelmVersion string = ""
55 func (cm *CM) UploadConfigAll() (configList models.AllXappConfig) {
56 return cm.UploadConfigElement("")
59 func (cm *CM) UploadConfigElement(Element string) (configList models.AllXappConfig) {
60 namespace := cm.GetNamespace("")
61 for _, name := range cm.GetNamesFromHelmRepo() {
62 var activeConfig interface{}
64 if err := cm.GetConfigmap(xAppName, namespace, &activeConfig); err != nil {
65 appmgr.Logger.Info("No active configMap found for '%s', ignoring ...", xAppName)
70 m := activeConfig.(map[string]interface{})
71 if m[Element] == nil {
72 appmgr.Logger.Info("xApp '%s' doesn't have requested element '%s' in config", name, Element)
75 activeConfig = m[Element]
78 c := models.XAppConfig{
79 Metadata: &models.ConfigMetadata{XappName: &xAppName, Namespace: &namespace},
82 configList = append(configList, &c)
87 func (cm *CM) GetConfigmap(name, namespace string, c *interface{}) (err error) {
88 cmJson, err := cm.ReadConfigmap(name, namespace)
93 return json.Unmarshal([]byte(cmJson), &c)
96 func (cm *CM) ReadSchema(name string, desc *interface{}) (err error) {
97 if err = cm.FetchChart(name); err != nil {
101 tarDir := viper.GetString("xapp.tarDir")
102 err = cm.ReadFile(path.Join(tarDir, name, viper.GetString("xapp.schema")), desc)
107 if err = os.RemoveAll(path.Join(tarDir, name)); err != nil {
108 appmgr.Logger.Info("RemoveAll failed: %v", err)
114 func (cm *CM) UpdateConfigMap(r models.XAppConfig) (models.ConfigValidationErrors, error) {
115 fmt.Printf("Configmap update: xappName=%s namespace=%s config: %v\n", *r.Metadata.XappName, *r.Metadata.Namespace, r.Config)
116 if validationErrors, err := cm.Validate(r); err != nil {
117 return validationErrors, err
120 cmContent, err := cm.BuildConfigMap(r)
125 if err := cm.GenerateJSONFile(cmContent); err != nil {
128 err = cm.ReplaceConfigMap(*r.Metadata.XappName, *r.Metadata.Namespace)
133 func (cm *CM) BuildConfigMap(r models.XAppConfig) (string, error) {
134 configJson, err := json.Marshal(r.Config)
136 appmgr.Logger.Info("Config marshalling failed: %v", err)
140 cmContent, err := cm.ReadConfigmap(*r.Metadata.XappName, *r.Metadata.Namespace)
145 v, err := cm.ParseJson(cmContent)
147 v.Set("controls", fastjson.MustParse(string(configJson)))
148 fmt.Println(v.String())
149 return v.String(), nil
155 func (cm *CM) ParseJson(dsContent string) (*fastjson.Value, error) {
156 var p fastjson.Parser
157 v, err := p.Parse(dsContent)
159 appmgr.Logger.Info("fastjson.Parser failed: %v", err)
164 func (cm *CM) GenerateJSONFile(jsonString string) error {
165 cmJson, err := json.RawMessage(jsonString).MarshalJSON()
167 appmgr.Logger.Error("Config marshalling failed: %v", err)
171 err = ioutil.WriteFile(viper.GetString("xapp.tmpConfig"), cmJson, 0644)
173 appmgr.Logger.Error("WriteFile failed: %v", err)
180 func (cm *CM) ReadFile(name string, data interface{}) (err error) {
181 f, err := ioutil.ReadFile(name)
183 appmgr.Logger.Info("Reading '%s' file failed: %v", name, err)
187 err = json.Unmarshal(f, &data)
189 appmgr.Logger.Info("Unmarshalling '%s' file failed: %v", name, err)
196 func (cm *CM) ReadConfigmap(name string, ns string) (string, error) {
197 args := fmt.Sprintf("get configmap -o jsonpath='{.data.config-file\\.json}' -n %s %s", ns, cm.GetConfigMapName(name, ns))
198 out, err := kubeExec(args)
199 return string(out), err
202 func (cm *CM) ReplaceConfigMap(name, ns string) error {
203 cmd := " create configmap -n %s %s --from-file=%s -o json --dry-run | kubectl replace -f -"
204 args := fmt.Sprintf(cmd, ns, cm.GetConfigMapName(name, ns), viper.GetString("xapp.tmpConfig"))
205 _, err := kubeExec(args)
209 func (cm *CM) FetchChart(name string) (err error) {
210 tarDir := viper.GetString("xapp.tarDir")
211 repo := viper.GetString("helm.repo-name")
212 fetchArgs := fmt.Sprintf("--untar --untardir %s %s/%s", tarDir, repo, name)
214 _, err = helmExec(strings.Join([]string{"fetch ", fetchArgs}, ""))
218 func (cm *CM) GetRtmData(name string) (msgs appmgr.RtmData) {
219 appmgr.Logger.Info("Fetching RT data for xApp=%s", name)
221 ns := cm.GetNamespace("")
222 args := fmt.Sprintf("get configmap -o jsonpath='{.data.config-file\\.json}' -n %s %s", ns, cm.GetConfigMapName(name, ns))
223 out, err := kubeExec(args)
228 var p fastjson.Parser
229 v, err := p.Parse(string(out))
231 appmgr.Logger.Info("fastjson.Parser for '%s' failed: %v", name, err)
236 for _, m := range v.GetArray("rmr", "txMessages") {
237 msgs.TxMessages = append(msgs.TxMessages, strings.Trim(m.String(), `"`))
240 for _, m := range v.GetArray("rmr", "rxMessages") {
241 msgs.RxMessages = append(msgs.RxMessages, strings.Trim(m.String(), `"`))
244 for _, m := range v.GetArray("rmr", "policies") {
245 if val, err := strconv.Atoi(strings.Trim(m.String(), `"`)); err == nil {
246 msgs.Policies = append(msgs.Policies, int64(val))
250 for _, p := range v.GetArray("messaging", "ports") {
251 appmgr.Logger.Info("txMessages=%v, rxMessages=%v", p.GetArray("txMessages"), p.GetArray("rxMessages"))
252 for _, m := range p.GetArray("txMessages") {
253 msgs.TxMessages = append(msgs.TxMessages, strings.Trim(m.String(), `"`))
256 for _, m := range p.GetArray("rxMessages") {
257 msgs.RxMessages = append(msgs.RxMessages, strings.Trim(m.String(), `"`))
260 for _, m := range p.GetArray("policies") {
261 if val, err := strconv.Atoi(strings.Trim(m.String(), `"`)); err == nil {
262 msgs.Policies = append(msgs.Policies, int64(val))
270 func (cm *CM) GetConfigMapName(xappName, namespace string) string {
271 return " configmap-" + namespace + "-" + xappName + "-appconfig"
274 func (cm *CM) GetNamespace(ns string) string {
279 ns = viper.GetString("xapp.namespace")
286 func (cm *CM) GetNamesFromHelmRepo() (names []string) {
287 rname := viper.GetString("helm.repo-name")
289 var cmdArgs string = ""
290 if EnvHelmVersion == HELM_VERSION_3 {
291 cmdArgs = strings.Join([]string{"search repo ", rname}, "")
293 cmdArgs = strings.Join([]string{"search ", rname}, "")
296 out, err := helmExec(cmdArgs)
301 re := regexp.MustCompile(rname + `/.*`)
302 result := re.FindAllStringSubmatch(string(out), -1)
305 for _, v := range result {
306 fmt.Sscanf(v[0], "%s", &tmp)
307 names = append(names, strings.Split(tmp, "/")[1])
313 func (cm *CM) Validate(req models.XAppConfig) (errList models.ConfigValidationErrors, err error) {
315 err = cm.ReadSchema(*req.Metadata.XappName, &desc)
317 appmgr.Logger.Info("No schema file found for '%s', aborting ...", *req.Metadata.XappName)
320 return cm.doValidate(desc, req.Config)
323 func (cm *CM) doValidate(schema, cfg interface{}) (errList models.ConfigValidationErrors, err error) {
324 schemaLoader := gojsonschema.NewGoLoader(schema)
325 documentLoader := gojsonschema.NewGoLoader(cfg)
327 result, err := gojsonschema.Validate(schemaLoader, documentLoader)
329 appmgr.Logger.Info("Validation failed: %v", err)
333 if result.Valid() == false {
334 appmgr.Logger.Info("The document is not valid, Errors: %v", result.Errors())
335 for _, desc := range result.Errors() {
336 field := desc.Field()
337 validationError := desc.Description()
338 errList = append(errList, &models.ConfigValidationError{Field: &field, Error: &validationError})
340 return errList, errors.New("Validation failed!")
342 appmgr.Logger.Info("Config validation successful!")