blob: 9857e00b80219588dee044318905471332043bb5 [file] [log] [blame]
Mohamed Abukar34e43832019-11-13 17:57:15 +02001/*
2==================================================================================
3 Copyright (c) 2019 AT&T Intellectual Property.
4 Copyright (c) 2019 Nokia
5
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
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
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==================================================================================
18*/
19
20package cm
21
22import (
23 "encoding/json"
24 "errors"
25 "fmt"
Timo Tietavainend3aa5b92020-11-12 03:50:01 +020026 "github.com/spf13/viper"
27 "github.com/valyala/fastjson"
28 "github.com/xeipuuv/gojsonschema"
Mohamed Abukar34e43832019-11-13 17:57:15 +020029 "io/ioutil"
30 "os"
31 "path"
32 "regexp"
Mohamed Abukare71a5a52019-12-05 08:26:30 +020033 "strconv"
Timo Tietavainend3aa5b92020-11-12 03:50:01 +020034 "strings"
Mohamed Abukar34e43832019-11-13 17:57:15 +020035
Abukar Mohamedf8b99662020-03-02 14:44:30 +000036 "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"
Mohamed Abukar34e43832019-11-13 17:57:15 +020039)
40
Timo Tietavainend3aa5b92020-11-12 03:50:01 +020041var kubeExec = util.KubectlExec
42var helmExec = util.HelmExec
43
Mohamed Abukar34e43832019-11-13 17:57:15 +020044type CM struct{}
45
46func NewCM() *CM {
47 return &CM{}
48}
49
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +020050func (cm *CM) UploadConfigAll() (configList models.AllXappConfig) {
51 return cm.UploadConfigElement("")
52}
53
54func (cm *CM) UploadConfigElement(Element string) (configList models.AllXappConfig) {
55 namespace := cm.GetNamespace("")
Mohamed Abukar34e43832019-11-13 17:57:15 +020056 for _, name := range cm.GetNamesFromHelmRepo() {
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +020057 var activeConfig interface{}
58 xAppName := name
59 if err := cm.GetConfigmap(xAppName, namespace, &activeConfig); err != nil {
60 appmgr.Logger.Info("No active configMap found for '%s', ignoring ...", xAppName)
Mohamed Abukar34e43832019-11-13 17:57:15 +020061 continue
62 }
63
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +020064 if Element != "" {
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)
68 continue
69 }
70 activeConfig = m[Element]
71 }
72
Mohamed Abukar34e43832019-11-13 17:57:15 +020073 c := models.XAppConfig{
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +020074 Metadata: &models.ConfigMetadata{XappName: &xAppName, Namespace: &namespace},
Timo Tietavainend3aa5b92020-11-12 03:50:01 +020075 Config: activeConfig,
Mohamed Abukar34e43832019-11-13 17:57:15 +020076 }
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +020077 configList = append(configList, &c)
Mohamed Abukar34e43832019-11-13 17:57:15 +020078 }
79 return
80}
81
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +020082func (cm *CM) GetConfigmap(name, namespace string, c *interface{}) (err error) {
83 cmJson, err := cm.ReadConfigmap(name, namespace)
84 if err != nil {
85 return err
86 }
87
88 return json.Unmarshal([]byte(cmJson), &c)
89}
90
91func (cm *CM) ReadSchema(name string, desc *interface{}) (err error) {
Mohamed Abukar34e43832019-11-13 17:57:15 +020092 if err = cm.FetchChart(name); err != nil {
93 return
94 }
95
96 tarDir := viper.GetString("xapp.tarDir")
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +020097 err = cm.ReadFile(path.Join(tarDir, name, viper.GetString("xapp.schema")), desc)
Mohamed Abukar34e43832019-11-13 17:57:15 +020098 if err != nil {
99 return
100 }
101
102 if err = os.RemoveAll(path.Join(tarDir, name)); err != nil {
103 appmgr.Logger.Info("RemoveAll failed: %v", err)
104 }
105
106 return
107}
108
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200109func (cm *CM) UpdateConfigMap(r models.XAppConfig) (models.ConfigValidationErrors, error) {
Timo Tietavainend3aa5b92020-11-12 03:50:01 +0200110 fmt.Printf("Configmap update: xappName=%s namespace=%s config: %v\n", *r.Metadata.XappName, *r.Metadata.Namespace, r.Config)
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200111 if validationErrors, err := cm.Validate(r); err != nil {
112 return validationErrors, err
Mohamed Abukar34e43832019-11-13 17:57:15 +0200113 }
114
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200115 cmContent, err := cm.BuildConfigMap(r)
Mohamed Abukar34e43832019-11-13 17:57:15 +0200116 if err != nil {
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200117 return nil, err
Mohamed Abukar34e43832019-11-13 17:57:15 +0200118 }
119
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200120 if err := cm.GenerateJSONFile(cmContent); err != nil {
121 return nil, err
122 }
123 err = cm.ReplaceConfigMap(*r.Metadata.XappName, *r.Metadata.Namespace)
124
125 return nil, err
Mohamed Abukar34e43832019-11-13 17:57:15 +0200126}
127
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200128func (cm *CM) BuildConfigMap(r models.XAppConfig) (string, error) {
129 configJson, err := json.Marshal(r.Config)
Mohamed Abukar34e43832019-11-13 17:57:15 +0200130 if err != nil {
131 appmgr.Logger.Info("Config marshalling failed: %v", err)
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200132 return "", err
Mohamed Abukar34e43832019-11-13 17:57:15 +0200133 }
134
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200135 cmContent, err := cm.ReadConfigmap(*r.Metadata.XappName, *r.Metadata.Namespace)
Mohamed Abukar34e43832019-11-13 17:57:15 +0200136 if err != nil {
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200137 return "", err
Mohamed Abukar34e43832019-11-13 17:57:15 +0200138 }
139
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200140 v, err := cm.ParseJson(cmContent)
Mohamed Abukar34e43832019-11-13 17:57:15 +0200141 if err == nil {
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200142 v.Set("controls", fastjson.MustParse(string(configJson)))
143 fmt.Println(v.String())
144 return v.String(), nil
Mohamed Abukar34e43832019-11-13 17:57:15 +0200145 }
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200146
147 return "", err
Mohamed Abukar34e43832019-11-13 17:57:15 +0200148}
149
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200150func (cm *CM) ParseJson(dsContent string) (*fastjson.Value, error) {
151 var p fastjson.Parser
152 v, err := p.Parse(dsContent)
Mohamed Abukar34e43832019-11-13 17:57:15 +0200153 if err != nil {
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200154 appmgr.Logger.Info("fastjson.Parser failed: %v", err)
Mohamed Abukar34e43832019-11-13 17:57:15 +0200155 }
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200156 return v, err
Mohamed Abukar34e43832019-11-13 17:57:15 +0200157}
158
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200159func (cm *CM) GenerateJSONFile(jsonString string) error {
160 cmJson, err := json.RawMessage(jsonString).MarshalJSON()
Mohamed Abukar34e43832019-11-13 17:57:15 +0200161 if err != nil {
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200162 appmgr.Logger.Error("Config marshalling failed: %v", err)
163 return err
Mohamed Abukar34e43832019-11-13 17:57:15 +0200164 }
Mohamed Abukar34e43832019-11-13 17:57:15 +0200165
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200166 err = ioutil.WriteFile(viper.GetString("xapp.tmpConfig"), cmJson, 0644)
Mohamed Abukar34e43832019-11-13 17:57:15 +0200167 if err != nil {
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200168 appmgr.Logger.Error("WriteFile failed: %v", err)
169 return err
Mohamed Abukar34e43832019-11-13 17:57:15 +0200170 }
171
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200172 return nil
Mohamed Abukar34e43832019-11-13 17:57:15 +0200173}
174
175func (cm *CM) ReadFile(name string, data interface{}) (err error) {
176 f, err := ioutil.ReadFile(name)
177 if err != nil {
178 appmgr.Logger.Info("Reading '%s' file failed: %v", name, err)
179 return
180 }
181
182 err = json.Unmarshal(f, &data)
183 if err != nil {
184 appmgr.Logger.Info("Unmarshalling '%s' file failed: %v", name, err)
185 return
186 }
187
188 return
189}
190
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200191func (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))
Timo Tietavainend3aa5b92020-11-12 03:50:01 +0200193 out, err := kubeExec(args)
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200194 return string(out), err
195}
196
Timo Tietavainend3aa5b92020-11-12 03:50:01 +0200197func (cm *CM) ReplaceConfigMap(name, ns string) error {
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200198 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"))
Timo Tietavainend3aa5b92020-11-12 03:50:01 +0200200 _, err := kubeExec(args)
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200201 return err
202}
203
Mohamed Abukar34e43832019-11-13 17:57:15 +0200204func (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)
208
Timo Tietavainend3aa5b92020-11-12 03:50:01 +0200209 _, err = helmExec(strings.Join([]string{"fetch ", fetchArgs}, ""))
Mohamed Abukar34e43832019-11-13 17:57:15 +0200210 return
211}
212
Mohamed Abukare71a5a52019-12-05 08:26:30 +0200213func (cm *CM) GetRtmData(name string) (msgs appmgr.RtmData) {
214 appmgr.Logger.Info("Fetching RT data for xApp=%s", name)
Mohamed Abukar34e43832019-11-13 17:57:15 +0200215
216 ns := cm.GetNamespace("")
217 args := fmt.Sprintf("get configmap -o jsonpath='{.data.config-file\\.json}' -n %s %s", ns, cm.GetConfigMapName(name, ns))
Timo Tietavainend3aa5b92020-11-12 03:50:01 +0200218 out, err := kubeExec(args)
Mohamed Abukar34e43832019-11-13 17:57:15 +0200219 if err != nil {
220 return
221 }
222
223 var p fastjson.Parser
224 v, err := p.Parse(string(out))
225 if err != nil {
226 appmgr.Logger.Info("fastjson.Parser for '%s' failed: %v", name, err)
227 return
228 }
229
230 for _, m := range v.GetArray("rmr", "txMessages") {
231 msgs.TxMessages = append(msgs.TxMessages, strings.Trim(m.String(), `"`))
232 }
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200233
Mohamed Abukar34e43832019-11-13 17:57:15 +0200234 for _, m := range v.GetArray("rmr", "rxMessages") {
235 msgs.RxMessages = append(msgs.RxMessages, strings.Trim(m.String(), `"`))
236 }
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200237
Mohamed Abukare71a5a52019-12-05 08:26:30 +0200238 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))
241 }
242 }
Mohamed Abukar34e43832019-11-13 17:57:15 +0200243
244 return
245}
246
247func (cm *CM) GetConfigMapName(xappName, namespace string) string {
248 return " configmap-" + namespace + "-" + xappName + "-appconfig"
249}
250
251func (cm *CM) GetNamespace(ns string) string {
252 if ns != "" {
253 return ns
254 }
255
256 ns = viper.GetString("xapp.namespace")
257 if ns == "" {
258 ns = "ricxapp"
259 }
260 return ns
261}
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200262
263func (cm *CM) GetNamesFromHelmRepo() (names []string) {
264 rname := viper.GetString("helm.repo-name")
265
266 cmdArgs := strings.Join([]string{"search ", rname}, "")
Timo Tietavainend3aa5b92020-11-12 03:50:01 +0200267 out, err := helmExec(cmdArgs)
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200268 if err != nil {
269 return
270 }
271
272 re := regexp.MustCompile(rname + `/.*`)
273 result := re.FindAllStringSubmatch(string(out), -1)
274 if result != nil {
275 var tmp string
276 for _, v := range result {
277 fmt.Sscanf(v[0], "%s", &tmp)
278 names = append(names, strings.Split(tmp, "/")[1])
279 }
280 }
281 return names
282}
283
284func (cm *CM) Validate(req models.XAppConfig) (errList models.ConfigValidationErrors, err error) {
285 var desc interface{}
286 err = cm.ReadSchema(*req.Metadata.XappName, &desc)
287 if err != nil {
288 appmgr.Logger.Info("No schema file found for '%s', aborting ...", *req.Metadata.XappName)
289 return
290 }
291 return cm.doValidate(desc, req.Config)
292}
293
294func (cm *CM) doValidate(schema, cfg interface{}) (errList models.ConfigValidationErrors, err error) {
295 schemaLoader := gojsonschema.NewGoLoader(schema)
296 documentLoader := gojsonschema.NewGoLoader(cfg)
297
298 result, err := gojsonschema.Validate(schemaLoader, documentLoader)
299 if err != nil {
300 appmgr.Logger.Info("Validation failed: %v", err)
301 return
302 }
303
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})
310 }
311 return errList, errors.New("Validation failed!")
312 }
313 appmgr.Logger.Info("Config validation successful!")
314
315 return
Abukar Mohamedf8b99662020-03-02 14:44:30 +0000316}