Build related cleanup. Entrypoint to read config from correct place.
[ric-plt/appmgr.git] / cmd / appmgr / desc.go
1 /*
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
20 package main
21
22 import (
23         "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         "log"
31         "os"
32         "path"
33         "regexp"
34         "strings"
35         "time"
36 )
37
38 type ConfigMetadata struct {
39         Name       string `json:"name"`
40         ConfigName string `json:"configName, omitempty"`
41         Namespace  string `json:"namespace, omitempty"`
42 }
43
44 type XAppConfig struct {
45         Metadata      ConfigMetadata `json:"metadata"`
46         Descriptor    interface{}    `json:"descriptor, omitempty"`
47         Configuration interface{}    `json:"config, omitempty"`
48 }
49
50 type ConfigMap struct {
51         Kind       string      `json:"kind"`
52         ApiVersion string      `json:"apiVersion"`
53         Data       interface{} `json:"data"`
54         Metadata   CMMetadata  `json:"metadata"`
55 }
56
57 type CMMetadata struct {
58         Name      string `json:"name"`
59         Namespace string `json:"namespace"`
60 }
61
62 type CMError struct {
63         Field       string `json:"field"`
64         Description string `json:"description"`
65 }
66
67 func (cm *ConfigMap) UploadConfig() (cfg []XAppConfig) {
68         ns := cm.GetNamespace("")
69         for _, name := range cm.GetNamesFromHelmRepo() {
70                 if name == "appmgr" {
71                         continue
72                 }
73
74                 c := XAppConfig{
75                         Metadata: ConfigMetadata{Name: name, Namespace: ns, ConfigName: cm.GetConfigMapName(name, ns)},
76                 }
77
78                 err := cm.ReadSchema(name, &c)
79                 if err != nil {
80                         continue
81                 }
82
83                 err = cm.ReadConfigMap(c.Metadata.ConfigName, ns, &c.Configuration)
84                 if err != nil {
85                         log.Println("No active configMap found, using default!")
86                 }
87
88                 cfg = append(cfg, c)
89         }
90         return
91 }
92
93 func (cm *ConfigMap) ReadSchema(name string, c *XAppConfig) (err error) {
94         if err = cm.FetchChart(name); err != nil {
95                 return
96         }
97
98         tarDir := viper.GetString("xapp.tarDir")
99         err = cm.ReadFile(path.Join(tarDir, name, viper.GetString("xapp.schema")), &c.Descriptor)
100         if err != nil {
101                 return
102         }
103
104         err = cm.ReadFile(path.Join(tarDir, name, viper.GetString("xapp.config")), &c.Configuration)
105         if err != nil {
106                 return
107         }
108
109         if err = os.RemoveAll(path.Join(tarDir, name)); err != nil {
110                 log.Println("RemoveAll failed", err)
111         }
112
113         return
114 }
115
116 func (cm *ConfigMap) ReadConfigMap(ConfigName string, ns string, c *interface{}) (err error) {
117         args := fmt.Sprintf("get configmap -o jsonpath='{.data.config-file\\.json}' -n %s %s", ns, ConfigName)
118         configMapJson, err := KubectlExec(args)
119         if err != nil {
120                 return
121         }
122
123         err = json.Unmarshal([]byte(configMapJson), &c)
124         if err != nil {
125                 return
126         }
127
128         return
129 }
130
131 func (cm *ConfigMap) ApplyConfigMap(r XAppConfig, action string) (err error) {
132         c := ConfigMap{
133                 Kind:       "ConfigMap",
134                 ApiVersion: "v1",
135                 Metadata:   CMMetadata{Name: r.Metadata.Name, Namespace: r.Metadata.Namespace},
136                 Data:       r.Configuration,
137         }
138
139         cmJson, err := json.Marshal(c.Data)
140         if err != nil {
141                 log.Println("Config marshalling failed: ", err)
142                 return
143         }
144
145         cmFile := viper.GetString("xapp.tmpConfig")
146         err = ioutil.WriteFile(cmFile, cmJson, 0644)
147         if err != nil {
148                 log.Println("WriteFile failed: ", err)
149                 return
150         }
151
152         cmd := " create configmap -n %s %s --from-file=%s -o json --dry-run | kubectl %s -f -"
153         args := fmt.Sprintf(cmd, r.Metadata.Namespace, r.Metadata.ConfigName, cmFile, action)
154         _, err = KubectlExec(args)
155         if err != nil {
156                 return
157         }
158         log.Println("Configmap changes done!")
159
160         return
161 }
162
163 func (cm *ConfigMap) GetConfigMap(m XappDeploy, c *interface{}) (err error) {
164         if m.ConfigName == "" {
165                 m.ConfigName = cm.GetConfigMapName(m.Name, m.Namespace)
166         }
167         return cm.ReadConfigMap(m.ConfigName, m.Namespace, c)
168 }
169
170 func (cm *ConfigMap) CreateConfigMap(r XAppConfig) (errList []CMError, err error) {
171         if errList, err = cm.Validate(r); err != nil {
172                 return
173         }
174         err = cm.ApplyConfigMap(r, "create")
175         return
176 }
177
178 func (cm *ConfigMap) UpdateConfigMap(r XAppConfig) (errList []CMError, err error) {
179         if errList, err = cm.Validate(r); err != nil {
180                 return
181         }
182
183         // Re-create the configmap with the new parameters
184         err = cm.ApplyConfigMap(r, "apply")
185         return
186 }
187
188 func (cm *ConfigMap) DeleteConfigMap(r XAppConfig) (c interface{}, err error) {
189         err = cm.ReadConfigMap(r.Metadata.ConfigName, r.Metadata.Namespace, &c)
190         if err == nil {
191                 args := fmt.Sprintf(" delete configmap --namespace=%s %s", r.Metadata.Namespace, r.Metadata.ConfigName)
192                 _, err = KubectlExec(args)
193         }
194         return
195 }
196
197 func (cm *ConfigMap) PurgeConfigMap(m XappDeploy) (c interface{}, err error) {
198         if m.ConfigName == "" {
199                 m.ConfigName = cm.GetConfigMapName(m.Name, m.Namespace)
200         }
201         md := ConfigMetadata{Name: m.Name, Namespace: m.Namespace, ConfigName: m.ConfigName}
202
203         return cm.DeleteConfigMap(XAppConfig{Metadata: md})
204 }
205
206 func (cm *ConfigMap) RestoreConfigMap(m XappDeploy, c interface{}) (err error) {
207         if m.ConfigName == "" {
208                 m.ConfigName = cm.GetConfigMapName(m.Name, m.Namespace)
209         }
210         md := ConfigMetadata{Name: m.Name, Namespace: m.Namespace, ConfigName: m.ConfigName}
211         time.Sleep(time.Duration(10 * time.Second))
212
213         return cm.ApplyConfigMap(XAppConfig{Metadata: md, Configuration: c}, "create")
214 }
215
216 func (cm *ConfigMap) GetNamesFromHelmRepo() (names []string) {
217         rname := viper.GetString("helm.repo-name")
218
219         cmdArgs := strings.Join([]string{"search ", rname}, "")
220         out, err := HelmExec(cmdArgs)
221         if err != nil {
222                 return
223         }
224
225         re := regexp.MustCompile(rname + `/.*`)
226         result := re.FindAllStringSubmatch(string(out), -1)
227         if result != nil {
228                 var tmp string
229                 for _, v := range result {
230                         fmt.Sscanf(v[0], "%s", &tmp)
231                         names = append(names, strings.Split(tmp, "/")[1])
232                 }
233         }
234         return names
235 }
236
237 func (cm *ConfigMap) Validate(req XAppConfig) (errList []CMError, err error) {
238         c := XAppConfig{}
239         err = cm.ReadSchema(req.Metadata.Name, &c)
240         if err != nil {
241                 log.Printf("No schema file found for '%s', aborting ...", req.Metadata.Name)
242                 return
243         }
244         return cm.doValidate(c.Descriptor, req.Configuration)
245 }
246
247 func (cm *ConfigMap) doValidate(schema, cfg interface{}) (errList []CMError, err error) {
248         schemaLoader := gojsonschema.NewGoLoader(schema)
249         documentLoader := gojsonschema.NewGoLoader(cfg)
250
251         result, err := gojsonschema.Validate(schemaLoader, documentLoader)
252         if err != nil {
253                 log.Println("Validation failed: ", err)
254                 return
255         }
256
257         if result.Valid() == false {
258                 log.Println("The document is not valid, Errors: ", result.Errors())
259                 for _, desc := range result.Errors() {
260                         errList = append(errList, CMError{Field: desc.Field(), Description: desc.Description()})
261                 }
262                 return errList, errors.New("Validation failed!")
263         }
264         return
265 }
266
267 func (cm *ConfigMap) ReadFile(name string, data interface{}) (err error) {
268         f, err := ioutil.ReadFile(name)
269         if err != nil {
270                 log.Printf("Reading '%s' file failed: %v", name, err)
271                 return
272         }
273
274         err = json.Unmarshal(f, &data)
275         if err != nil {
276                 log.Printf("Unmarshalling '%s' file failed: %v", name, err)
277                 return
278         }
279
280         return
281 }
282
283 func (cm *ConfigMap) FetchChart(name string) (err error) {
284         tarDir := viper.GetString("xapp.tarDir")
285         repo := viper.GetString("helm.repo-name")
286         fetchArgs := fmt.Sprintf("--untar --untardir %s %s/%s", tarDir, repo, name)
287
288         _, err = HelmExec(strings.Join([]string{"fetch ", fetchArgs}, ""))
289         return
290 }
291
292 func (cm *ConfigMap) GetMessages(name string) (msgs MessageTypes) {
293         log.Println("Fetching tx/rx messages for: ", name)
294
295         ns := cm.GetNamespace("")
296         args := fmt.Sprintf("get configmap -o jsonpath='{.data.config-file\\.json}' -n %s %s", ns, cm.GetConfigMapName(name, ns))
297         out, err := KubectlExec(args)
298         if err != nil {
299                 return
300         }
301
302         var p fastjson.Parser
303         v, err := p.Parse(string(out))
304         if err != nil {
305                 log.Printf("fastjson.Parser for '%s' failed: %v", name, err)
306                 return
307         }
308
309         for _, m := range v.GetArray("rmr", "txMessages") {
310                 msgs.TxMessages = append(msgs.TxMessages, strings.Trim(m.String(), `"`))
311         }
312         for _, m := range v.GetArray("rmr", "rxMessages") {
313                 msgs.RxMessages = append(msgs.RxMessages, strings.Trim(m.String(), `"`))
314         }
315
316         return
317 }
318
319 func (cm *ConfigMap) GetConfigMapName(xappName, namespace string) string {
320         return " configmap-" + namespace + "-" + xappName + "-appconfig"
321 }
322
323 func (cm *ConfigMap) GetNamespace(ns string) string {
324         if ns != "" {
325                 return ns
326         }
327
328         ns = viper.GetString("xapp.namespace")
329         if ns == "" {
330                 ns = "ricxapp"
331         }
332         return ns
333 }