blob: fb80276a39cd8e2d09d8e90573a227a90dc5070c [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"
Mohamed Abukar34e43832019-11-13 17:57:15 +020026 "io/ioutil"
27 "os"
28 "path"
29 "regexp"
30 "strings"
Mohamed Abukare71a5a52019-12-05 08:26:30 +020031 "strconv"
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +020032 "github.com/spf13/viper"
33 "github.com/valyala/fastjson"
34 "github.com/xeipuuv/gojsonschema"
Mohamed Abukar34e43832019-11-13 17:57:15 +020035
36 "gerrit.oran-osc.org/r/ric-plt/appmgr/pkg/appmgr"
37 "gerrit.oran-osc.org/r/ric-plt/appmgr/pkg/models"
38 "gerrit.oran-osc.org/r/ric-plt/appmgr/pkg/util"
39)
40
41type CM struct{}
42
43func NewCM() *CM {
44 return &CM{}
45}
46
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +020047func (cm *CM) UploadConfigAll() (configList models.AllXappConfig) {
48 return cm.UploadConfigElement("")
49}
50
51func (cm *CM) UploadConfigElement(Element string) (configList models.AllXappConfig) {
52 namespace := cm.GetNamespace("")
Mohamed Abukar34e43832019-11-13 17:57:15 +020053 for _, name := range cm.GetNamesFromHelmRepo() {
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +020054 var activeConfig interface{}
55 xAppName := name
56 if err := cm.GetConfigmap(xAppName, namespace, &activeConfig); err != nil {
57 appmgr.Logger.Info("No active configMap found for '%s', ignoring ...", xAppName)
Mohamed Abukar34e43832019-11-13 17:57:15 +020058 continue
59 }
60
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +020061 if Element != "" {
62 m := activeConfig.(map[string]interface{})
63 if m[Element] == nil {
64 appmgr.Logger.Info("xApp '%s' doesn't have requested element '%s' in config", name, Element)
65 continue
66 }
67 activeConfig = m[Element]
68 }
69
Mohamed Abukar34e43832019-11-13 17:57:15 +020070 c := models.XAppConfig{
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +020071 Metadata: &models.ConfigMetadata{XappName: &xAppName, Namespace: &namespace},
72 Config: activeConfig,
Mohamed Abukar34e43832019-11-13 17:57:15 +020073 }
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +020074 configList = append(configList, &c)
Mohamed Abukar34e43832019-11-13 17:57:15 +020075 }
76 return
77}
78
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +020079func (cm *CM) GetConfigmap(name, namespace string, c *interface{}) (err error) {
80 cmJson, err := cm.ReadConfigmap(name, namespace)
81 if err != nil {
82 return err
83 }
84
85 return json.Unmarshal([]byte(cmJson), &c)
86}
87
88func (cm *CM) ReadSchema(name string, desc *interface{}) (err error) {
Mohamed Abukar34e43832019-11-13 17:57:15 +020089 if err = cm.FetchChart(name); err != nil {
90 return
91 }
92
93 tarDir := viper.GetString("xapp.tarDir")
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +020094 err = cm.ReadFile(path.Join(tarDir, name, viper.GetString("xapp.schema")), desc)
Mohamed Abukar34e43832019-11-13 17:57:15 +020095 if err != nil {
96 return
97 }
98
99 if err = os.RemoveAll(path.Join(tarDir, name)); err != nil {
100 appmgr.Logger.Info("RemoveAll failed: %v", err)
101 }
102
103 return
104}
105
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200106func (cm *CM) UpdateConfigMap(r models.XAppConfig) (models.ConfigValidationErrors, error) {
107 fmt.Printf("Configmap update: xappName=%s namespace=%s config: %v", *r.Metadata.XappName, *r.Metadata.Namespace, r.Config)
108 if validationErrors, err := cm.Validate(r); err != nil {
109 return validationErrors, err
Mohamed Abukar34e43832019-11-13 17:57:15 +0200110 }
111
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200112 cmContent, err := cm.BuildConfigMap(r)
Mohamed Abukar34e43832019-11-13 17:57:15 +0200113 if err != nil {
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200114 return nil, err
Mohamed Abukar34e43832019-11-13 17:57:15 +0200115 }
116
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200117 if err := cm.GenerateJSONFile(cmContent); err != nil {
118 return nil, err
119 }
120 err = cm.ReplaceConfigMap(*r.Metadata.XappName, *r.Metadata.Namespace)
121
122 return nil, err
Mohamed Abukar34e43832019-11-13 17:57:15 +0200123}
124
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200125func (cm *CM) BuildConfigMap(r models.XAppConfig) (string, error) {
126 configJson, err := json.Marshal(r.Config)
Mohamed Abukar34e43832019-11-13 17:57:15 +0200127 if err != nil {
128 appmgr.Logger.Info("Config marshalling failed: %v", err)
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200129 return "", err
Mohamed Abukar34e43832019-11-13 17:57:15 +0200130 }
131
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200132 cmContent, err := cm.ReadConfigmap(*r.Metadata.XappName, *r.Metadata.Namespace)
Mohamed Abukar34e43832019-11-13 17:57:15 +0200133 if err != nil {
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200134 return "", err
Mohamed Abukar34e43832019-11-13 17:57:15 +0200135 }
136
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200137 v, err := cm.ParseJson(cmContent)
Mohamed Abukar34e43832019-11-13 17:57:15 +0200138 if err == nil {
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200139 v.Set("controls", fastjson.MustParse(string(configJson)))
140 fmt.Println(v.String())
141 return v.String(), nil
Mohamed Abukar34e43832019-11-13 17:57:15 +0200142 }
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200143
144 return "", err
Mohamed Abukar34e43832019-11-13 17:57:15 +0200145}
146
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200147func (cm *CM) ParseJson(dsContent string) (*fastjson.Value, error) {
148 var p fastjson.Parser
149 v, err := p.Parse(dsContent)
Mohamed Abukar34e43832019-11-13 17:57:15 +0200150 if err != nil {
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200151 appmgr.Logger.Info("fastjson.Parser failed: %v", err)
Mohamed Abukar34e43832019-11-13 17:57:15 +0200152 }
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200153 return v, err
Mohamed Abukar34e43832019-11-13 17:57:15 +0200154}
155
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200156
157func (cm *CM) GenerateJSONFile(jsonString string) error {
158 cmJson, err := json.RawMessage(jsonString).MarshalJSON()
Mohamed Abukar34e43832019-11-13 17:57:15 +0200159 if err != nil {
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200160 appmgr.Logger.Error("Config marshalling failed: %v", err)
161 return err
Mohamed Abukar34e43832019-11-13 17:57:15 +0200162 }
Mohamed Abukar34e43832019-11-13 17:57:15 +0200163
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200164 err = ioutil.WriteFile(viper.GetString("xapp.tmpConfig"), cmJson, 0644)
Mohamed Abukar34e43832019-11-13 17:57:15 +0200165 if err != nil {
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200166 appmgr.Logger.Error("WriteFile failed: %v", err)
167 return err
Mohamed Abukar34e43832019-11-13 17:57:15 +0200168 }
169
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200170 return nil
Mohamed Abukar34e43832019-11-13 17:57:15 +0200171}
172
173func (cm *CM) ReadFile(name string, data interface{}) (err error) {
174 f, err := ioutil.ReadFile(name)
175 if err != nil {
176 appmgr.Logger.Info("Reading '%s' file failed: %v", name, err)
177 return
178 }
179
180 err = json.Unmarshal(f, &data)
181 if err != nil {
182 appmgr.Logger.Info("Unmarshalling '%s' file failed: %v", name, err)
183 return
184 }
185
186 return
187}
188
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200189func (cm *CM) ReadConfigmap(name string, ns string) (string, error) {
190 args := fmt.Sprintf("get configmap -o jsonpath='{.data.config-file\\.json}' -n %s %s", ns, cm.GetConfigMapName(name, ns))
191 out, err := util.KubectlExec(args)
192 return string(out), err
193}
194
195func (cm *CM) ReplaceConfigMap(name, ns string) (error) {
196 cmd := " create configmap -n %s %s --from-file=%s -o json --dry-run | kubectl replace -f -"
197 args := fmt.Sprintf(cmd, ns, cm.GetConfigMapName(name, ns), viper.GetString("xapp.tmpConfig"))
198 _, err := util.KubectlExec(args)
199 return err
200}
201
Mohamed Abukar34e43832019-11-13 17:57:15 +0200202func (cm *CM) FetchChart(name string) (err error) {
203 tarDir := viper.GetString("xapp.tarDir")
204 repo := viper.GetString("helm.repo-name")
205 fetchArgs := fmt.Sprintf("--untar --untardir %s %s/%s", tarDir, repo, name)
206
207 _, err = util.HelmExec(strings.Join([]string{"fetch ", fetchArgs}, ""))
208 return
209}
210
Mohamed Abukare71a5a52019-12-05 08:26:30 +0200211func (cm *CM) GetRtmData(name string) (msgs appmgr.RtmData) {
212 appmgr.Logger.Info("Fetching RT data for xApp=%s", name)
Mohamed Abukar34e43832019-11-13 17:57:15 +0200213
214 ns := cm.GetNamespace("")
215 args := fmt.Sprintf("get configmap -o jsonpath='{.data.config-file\\.json}' -n %s %s", ns, cm.GetConfigMapName(name, ns))
216 out, err := util.KubectlExec(args)
217 if err != nil {
218 return
219 }
220
221 var p fastjson.Parser
222 v, err := p.Parse(string(out))
223 if err != nil {
224 appmgr.Logger.Info("fastjson.Parser for '%s' failed: %v", name, err)
225 return
226 }
227
228 for _, m := range v.GetArray("rmr", "txMessages") {
229 msgs.TxMessages = append(msgs.TxMessages, strings.Trim(m.String(), `"`))
230 }
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200231
Mohamed Abukar34e43832019-11-13 17:57:15 +0200232 for _, m := range v.GetArray("rmr", "rxMessages") {
233 msgs.RxMessages = append(msgs.RxMessages, strings.Trim(m.String(), `"`))
234 }
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200235
Mohamed Abukare71a5a52019-12-05 08:26:30 +0200236 for _, m := range v.GetArray("rmr", "policies") {
237 if val, err := strconv.Atoi(strings.Trim(m.String(), `"`)); err == nil {
238 msgs.Policies = append(msgs.Policies, int64(val))
239 }
240 }
Mohamed Abukar34e43832019-11-13 17:57:15 +0200241
242 return
243}
244
245func (cm *CM) GetConfigMapName(xappName, namespace string) string {
246 return " configmap-" + namespace + "-" + xappName + "-appconfig"
247}
248
249func (cm *CM) GetNamespace(ns string) string {
250 if ns != "" {
251 return ns
252 }
253
254 ns = viper.GetString("xapp.namespace")
255 if ns == "" {
256 ns = "ricxapp"
257 }
258 return ns
259}
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200260
261func (cm *CM) GetNamesFromHelmRepo() (names []string) {
262 rname := viper.GetString("helm.repo-name")
263
264 cmdArgs := strings.Join([]string{"search ", rname}, "")
265 out, err := util.HelmExec(cmdArgs)
266 if err != nil {
267 return
268 }
269
270 re := regexp.MustCompile(rname + `/.*`)
271 result := re.FindAllStringSubmatch(string(out), -1)
272 if result != nil {
273 var tmp string
274 for _, v := range result {
275 fmt.Sscanf(v[0], "%s", &tmp)
276 names = append(names, strings.Split(tmp, "/")[1])
277 }
278 }
279 return names
280}
281
282func (cm *CM) Validate(req models.XAppConfig) (errList models.ConfigValidationErrors, err error) {
283 var desc interface{}
284 err = cm.ReadSchema(*req.Metadata.XappName, &desc)
285 if err != nil {
286 appmgr.Logger.Info("No schema file found for '%s', aborting ...", *req.Metadata.XappName)
287 return
288 }
289 return cm.doValidate(desc, req.Config)
290}
291
292func (cm *CM) doValidate(schema, cfg interface{}) (errList models.ConfigValidationErrors, err error) {
293 schemaLoader := gojsonschema.NewGoLoader(schema)
294 documentLoader := gojsonschema.NewGoLoader(cfg)
295
296 result, err := gojsonschema.Validate(schemaLoader, documentLoader)
297 if err != nil {
298 appmgr.Logger.Info("Validation failed: %v", err)
299 return
300 }
301
302 if result.Valid() == false {
303 appmgr.Logger.Info("The document is not valid, Errors: %v", result.Errors())
304 for _, desc := range result.Errors() {
305 field := desc.Field()
306 validationError := desc.Description()
307 errList = append(errList, &models.ConfigValidationError{Field: &field, Error: &validationError})
308 }
309 return errList, errors.New("Validation failed!")
310 }
311 appmgr.Logger.Info("Config validation successful!")
312
313 return
314}