blob: 0356b197bb21a41580010c30efc585f42b2c5752 [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 (
Alok Bhatt6ff31e22020-11-11 06:52:26 +000023 "encoding/json"
24 "errors"
25 "fmt"
26 "github.com/spf13/viper"
27 "github.com/valyala/fastjson"
28 "github.com/xeipuuv/gojsonschema"
29 "io/ioutil"
30 "os"
31 "path"
32 "regexp"
33 "strconv"
34 "strings"
Mohamed Abukar34e43832019-11-13 17:57:15 +020035
Alok Bhatt6ff31e22020-11-11 06:52:26 +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
Alok Bhatt6ff31e22020-11-11 06:52:26 +000046const HELM_VERSION_3 = "3"
47const HELM_VERSION_2 = "2"
48var EnvHelmVersion string = ""
49
50
Mohamed Abukar34e43832019-11-13 17:57:15 +020051func NewCM() *CM {
Alok Bhatt6ff31e22020-11-11 06:52:26 +000052 return &CM{}
Mohamed Abukar34e43832019-11-13 17:57:15 +020053}
54
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +020055func (cm *CM) UploadConfigAll() (configList models.AllXappConfig) {
Alok Bhatt6ff31e22020-11-11 06:52:26 +000056 return cm.UploadConfigElement("")
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +020057}
58
59func (cm *CM) UploadConfigElement(Element string) (configList models.AllXappConfig) {
Alok Bhatt6ff31e22020-11-11 06:52:26 +000060 namespace := cm.GetNamespace("")
61 for _, name := range cm.GetNamesFromHelmRepo() {
62 var activeConfig interface{}
63 xAppName := name
64 if err := cm.GetConfigmap(xAppName, namespace, &activeConfig); err != nil {
65 appmgr.Logger.Info("No active configMap found for '%s', ignoring ...", xAppName)
66 continue
67 }
Mohamed Abukar34e43832019-11-13 17:57:15 +020068
Alok Bhatt6ff31e22020-11-11 06:52:26 +000069 if Element != "" {
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)
73 continue
74 }
75 activeConfig = m[Element]
76 }
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +020077
Alok Bhatt6ff31e22020-11-11 06:52:26 +000078 c := models.XAppConfig{
79 Metadata: &models.ConfigMetadata{XappName: &xAppName, Namespace: &namespace},
80 Config: activeConfig,
81 }
82 configList = append(configList, &c)
83 }
84 return
Mohamed Abukar34e43832019-11-13 17:57:15 +020085}
86
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +020087func (cm *CM) GetConfigmap(name, namespace string, c *interface{}) (err error) {
Alok Bhatt6ff31e22020-11-11 06:52:26 +000088 cmJson, err := cm.ReadConfigmap(name, namespace)
89 if err != nil {
90 return err
91 }
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +020092
Alok Bhatt6ff31e22020-11-11 06:52:26 +000093 return json.Unmarshal([]byte(cmJson), &c)
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +020094}
95
96func (cm *CM) ReadSchema(name string, desc *interface{}) (err error) {
Alok Bhatt6ff31e22020-11-11 06:52:26 +000097 if err = cm.FetchChart(name); err != nil {
98 return
99 }
Mohamed Abukar34e43832019-11-13 17:57:15 +0200100
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000101 tarDir := viper.GetString("xapp.tarDir")
102 err = cm.ReadFile(path.Join(tarDir, name, viper.GetString("xapp.schema")), desc)
103 if err != nil {
104 return
105 }
Mohamed Abukar34e43832019-11-13 17:57:15 +0200106
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000107 if err = os.RemoveAll(path.Join(tarDir, name)); err != nil {
108 appmgr.Logger.Info("RemoveAll failed: %v", err)
109 }
Mohamed Abukar34e43832019-11-13 17:57:15 +0200110
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000111 return
Mohamed Abukar34e43832019-11-13 17:57:15 +0200112}
113
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200114func (cm *CM) UpdateConfigMap(r models.XAppConfig) (models.ConfigValidationErrors, error) {
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000115 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
118 }
Mohamed Abukar34e43832019-11-13 17:57:15 +0200119
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000120 cmContent, err := cm.BuildConfigMap(r)
121 if err != nil {
122 return nil, err
123 }
Mohamed Abukar34e43832019-11-13 17:57:15 +0200124
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000125 if err := cm.GenerateJSONFile(cmContent); err != nil {
126 return nil, err
127 }
128 err = cm.ReplaceConfigMap(*r.Metadata.XappName, *r.Metadata.Namespace)
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200129
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000130 return nil, err
Mohamed Abukar34e43832019-11-13 17:57:15 +0200131}
132
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200133func (cm *CM) BuildConfigMap(r models.XAppConfig) (string, error) {
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000134 configJson, err := json.Marshal(r.Config)
135 if err != nil {
136 appmgr.Logger.Info("Config marshalling failed: %v", err)
137 return "", err
138 }
Mohamed Abukar34e43832019-11-13 17:57:15 +0200139
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000140 cmContent, err := cm.ReadConfigmap(*r.Metadata.XappName, *r.Metadata.Namespace)
141 if err != nil {
142 return "", err
143 }
Mohamed Abukar34e43832019-11-13 17:57:15 +0200144
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000145 v, err := cm.ParseJson(cmContent)
146 if err == nil {
147 v.Set("controls", fastjson.MustParse(string(configJson)))
148 fmt.Println(v.String())
149 return v.String(), nil
150 }
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200151
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000152 return "", err
Mohamed Abukar34e43832019-11-13 17:57:15 +0200153}
154
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200155func (cm *CM) ParseJson(dsContent string) (*fastjson.Value, error) {
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000156 var p fastjson.Parser
157 v, err := p.Parse(dsContent)
158 if err != nil {
159 appmgr.Logger.Info("fastjson.Parser failed: %v", err)
160 }
161 return v, err
Mohamed Abukar34e43832019-11-13 17:57:15 +0200162}
163
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200164func (cm *CM) GenerateJSONFile(jsonString string) error {
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000165 cmJson, err := json.RawMessage(jsonString).MarshalJSON()
166 if err != nil {
167 appmgr.Logger.Error("Config marshalling failed: %v", err)
168 return err
169 }
Mohamed Abukar34e43832019-11-13 17:57:15 +0200170
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000171 err = ioutil.WriteFile(viper.GetString("xapp.tmpConfig"), cmJson, 0644)
172 if err != nil {
173 appmgr.Logger.Error("WriteFile failed: %v", err)
174 return err
175 }
Mohamed Abukar34e43832019-11-13 17:57:15 +0200176
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000177 return nil
Mohamed Abukar34e43832019-11-13 17:57:15 +0200178}
179
180func (cm *CM) ReadFile(name string, data interface{}) (err error) {
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000181 f, err := ioutil.ReadFile(name)
182 if err != nil {
183 appmgr.Logger.Info("Reading '%s' file failed: %v", name, err)
184 return
185 }
Mohamed Abukar34e43832019-11-13 17:57:15 +0200186
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000187 err = json.Unmarshal(f, &data)
188 if err != nil {
189 appmgr.Logger.Info("Unmarshalling '%s' file failed: %v", name, err)
190 return
191 }
Mohamed Abukar34e43832019-11-13 17:57:15 +0200192
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000193 return
Mohamed Abukar34e43832019-11-13 17:57:15 +0200194}
195
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200196func (cm *CM) ReadConfigmap(name string, ns string) (string, error) {
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000197 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
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200200}
201
Timo Tietavainend3aa5b92020-11-12 03:50:01 +0200202func (cm *CM) ReplaceConfigMap(name, ns string) error {
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000203 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)
206 return err
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200207}
208
Mohamed Abukar34e43832019-11-13 17:57:15 +0200209func (cm *CM) FetchChart(name string) (err error) {
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000210 tarDir := viper.GetString("xapp.tarDir")
211 repo := viper.GetString("helm.repo-name")
212 fetchArgs := fmt.Sprintf("--untar --untardir %s %s/%s", tarDir, repo, name)
Mohamed Abukar34e43832019-11-13 17:57:15 +0200213
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000214 _, err = helmExec(strings.Join([]string{"fetch ", fetchArgs}, ""))
215 return
Mohamed Abukar34e43832019-11-13 17:57:15 +0200216}
217
Mohamed Abukare71a5a52019-12-05 08:26:30 +0200218func (cm *CM) GetRtmData(name string) (msgs appmgr.RtmData) {
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000219 appmgr.Logger.Info("Fetching RT data for xApp=%s", name)
Mohamed Abukar34e43832019-11-13 17:57:15 +0200220
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000221 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)
224 if err != nil {
225 return
226 }
Mohamed Abukar34e43832019-11-13 17:57:15 +0200227
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000228 var p fastjson.Parser
229 v, err := p.Parse(string(out))
230 if err != nil {
231 appmgr.Logger.Info("fastjson.Parser for '%s' failed: %v", name, err)
232 return
233 }
Mohamed Abukar34e43832019-11-13 17:57:15 +0200234
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000235 if v.Exists("rmr") {
236 for _, m := range v.GetArray("rmr", "txMessages") {
237 msgs.TxMessages = append(msgs.TxMessages, strings.Trim(m.String(), `"`))
238 }
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200239
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000240 for _, m := range v.GetArray("rmr", "rxMessages") {
241 msgs.RxMessages = append(msgs.RxMessages, strings.Trim(m.String(), `"`))
242 }
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200243
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000244 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))
247 }
248 }
249 } else {
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(), `"`))
254 }
Mohamed Abukar9261ff62020-11-20 10:23:15 +0200255
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000256 for _, m := range p.GetArray("rxMessages") {
257 msgs.RxMessages = append(msgs.RxMessages, strings.Trim(m.String(), `"`))
258 }
Mohamed Abukar9261ff62020-11-20 10:23:15 +0200259
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000260 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))
263 }
264 }
265 }
266 }
267 return
Mohamed Abukar34e43832019-11-13 17:57:15 +0200268}
269
270func (cm *CM) GetConfigMapName(xappName, namespace string) string {
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000271 return " configmap-" + namespace + "-" + xappName + "-appconfig"
Mohamed Abukar34e43832019-11-13 17:57:15 +0200272}
273
274func (cm *CM) GetNamespace(ns string) string {
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000275 if ns != "" {
276 return ns
277 }
Mohamed Abukar34e43832019-11-13 17:57:15 +0200278
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000279 ns = viper.GetString("xapp.namespace")
280 if ns == "" {
281 ns = "ricxapp"
282 }
283 return ns
Mohamed Abukar34e43832019-11-13 17:57:15 +0200284}
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200285
286func (cm *CM) GetNamesFromHelmRepo() (names []string) {
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000287 rname := viper.GetString("helm.repo-name")
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200288
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000289 var cmdArgs string = ""
290 if EnvHelmVersion == HELM_VERSION_3 {
291 cmdArgs = strings.Join([]string{"search repo ", rname}, "")
292 }else {
293 cmdArgs = strings.Join([]string{"search ", rname}, "")
294 }
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200295
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000296 out, err := helmExec(cmdArgs)
297 if err != nil {
298 return
299 }
300
301 re := regexp.MustCompile(rname + `/.*`)
302 result := re.FindAllStringSubmatch(string(out), -1)
303 if result != nil {
304 var tmp string
305 for _, v := range result {
306 fmt.Sscanf(v[0], "%s", &tmp)
307 names = append(names, strings.Split(tmp, "/")[1])
308 }
309 }
310 return names
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200311}
312
313func (cm *CM) Validate(req models.XAppConfig) (errList models.ConfigValidationErrors, err error) {
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000314 var desc interface{}
315 err = cm.ReadSchema(*req.Metadata.XappName, &desc)
316 if err != nil {
317 appmgr.Logger.Info("No schema file found for '%s', aborting ...", *req.Metadata.XappName)
318 return
319 }
320 return cm.doValidate(desc, req.Config)
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200321}
322
323func (cm *CM) doValidate(schema, cfg interface{}) (errList models.ConfigValidationErrors, err error) {
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000324 schemaLoader := gojsonschema.NewGoLoader(schema)
325 documentLoader := gojsonschema.NewGoLoader(cfg)
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200326
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000327 result, err := gojsonschema.Validate(schemaLoader, documentLoader)
328 if err != nil {
329 appmgr.Logger.Info("Validation failed: %v", err)
330 return
331 }
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200332
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000333 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})
339 }
340 return errList, errors.New("Validation failed!")
341 }
342 appmgr.Logger.Info("Config validation successful!")
Mohamed Abukaraca8f3c2020-01-14 11:10:16 +0200343
Alok Bhatt6ff31e22020-11-11 06:52:26 +0000344 return
Abukar Mohamedf8b99662020-03-02 14:44:30 +0000345}