c3a27a3b797c7b4a10cde993a5ece91cc21a9965
[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/xeipuuv/gojsonschema"
28         "io/ioutil"
29         "log"
30         "os"
31         "path"
32         "regexp"
33         "strings"
34         "time"
35 )
36
37 type ConfigMetadata struct {
38         Name       string `json:"name"`
39         ConfigName string `json:"configName, omitempty"`
40         Namespace  string `json:"namespace, omitempty"`
41 }
42
43 type XAppConfig struct {
44         Metadata      ConfigMetadata `json:"metadata"`
45         Descriptor    interface{}    `json:"descriptor, omitempty"`
46         Configuration interface{}    `json:"config, omitempty"`
47 }
48
49 type ConfigMap struct {
50         Kind       string      `json:"kind"`
51         ApiVersion string      `json:"apiVersion"`
52         Data       interface{} `json:"data"`
53         Metadata   CMMetadata  `json:"metadata"`
54 }
55
56 type CMMetadata struct {
57         Name      string `json:"name"`
58         Namespace string `json:"namespace"`
59 }
60
61 func UploadConfig() (cfg []XAppConfig) {
62         for _, name := range GetNamesFromHelmRepo() {
63                 if name == "appmgr" {
64                         continue
65                 }
66
67                 c := XAppConfig{
68                         Metadata: ConfigMetadata{Name: name, Namespace: "ricxapp", ConfigName: name + "-appconfig"},
69                 }
70
71                 err := ReadSchema(name, &c)
72                 if err != nil {
73                         continue
74                 }
75
76                 err = ReadConfigMap(name, "ricxapp", &c.Configuration)
77                 if err != nil {
78                         log.Println("No active configMap found, using default!")
79                 }
80
81                 cfg = append(cfg, c)
82         }
83         return
84 }
85
86 func ReadSchema(name string, c *XAppConfig) (err error) {
87         if err = FetchChart(name); err != nil {
88                 return
89         }
90
91         tarDir := viper.GetString("xapp.tarDir")
92         err = ReadFile(path.Join(tarDir, name, viper.GetString("xapp.schema")), &c.Descriptor)
93         if err != nil {
94                 return
95         }
96
97         err = ReadFile(path.Join(tarDir, name, viper.GetString("xapp.config")), &c.Configuration)
98         if err != nil {
99                 return
100         }
101
102         if err = os.RemoveAll(path.Join(tarDir, name)); err != nil {
103                 log.Println("RemoveAll failed", err)
104         }
105
106         return
107 }
108
109 func ReadConfigMap(name string, ns string, c *interface{}) (err error) {
110         args := fmt.Sprintf("get configmap -o jsonpath='{.data.config-file\\.json}' -n %s %s-appconfig", ns, name)
111         configMapJson, err := KubectlExec(args)
112         if err != nil {
113                 return
114         }
115
116         err = json.Unmarshal([]byte(configMapJson), &c)
117         if err != nil {
118                 return
119         }
120
121         return
122 }
123
124 func ApplyConfigMap(r XAppConfig) (err error) {
125         cm := ConfigMap{
126                 Kind:       "ConfigMap",
127                 ApiVersion: "v1",
128                 Metadata:   CMMetadata{Name: r.Metadata.Name, Namespace: r.Metadata.Namespace},
129                 Data:       r.Configuration,
130         }
131
132         cmJson, err := json.Marshal(cm)
133         if err != nil {
134                 log.Println("Config marshalling failed: ", err)
135                 return
136         }
137
138         cmFile := viper.GetString("xapp.tmpConfig")
139         err = ioutil.WriteFile(cmFile, cmJson, 0644)
140         if err != nil {
141                 log.Println("WriteFile failed: ", err)
142                 return
143         }
144
145         cmd := " create configmap -n %s %s --from-file=%s -o json --dry-run | kubectl apply -f -"
146         args := fmt.Sprintf(cmd, r.Metadata.Namespace, r.Metadata.ConfigName, cmFile)
147         _, err = KubectlExec(args)
148         if err != nil {
149                 return
150         }
151         log.Println("Configmap changes created!")
152
153         return
154 }
155
156 func CreateConfigMap(r XAppConfig) (err error) {
157         if err = Validate(r); err != nil {
158                 return
159         }
160         return ApplyConfigMap(r)
161 }
162
163 func DeleteConfigMap(r XAppConfig) (cm interface{}, err error) {
164         err = ReadConfigMap(r.Metadata.Name, r.Metadata.Namespace, &cm)
165         if err == nil {
166                 args := fmt.Sprintf(" delete configmap --namespace=%s %s", r.Metadata.Namespace, r.Metadata.ConfigName)
167                 _, err = KubectlExec(args)
168         }
169         return
170 }
171
172 func PurgeConfigMap(m ConfigMetadata) (cm interface{}, err error) {
173         if m.ConfigName == "" {
174                 m.ConfigName = m.Name + "-appconfig"
175         }
176         return DeleteConfigMap(XAppConfig{Metadata: m})
177 }
178
179 func RestoreConfigMap(m ConfigMetadata, cm interface{}) (err error) {
180         if m.ConfigName == "" {
181                 m.ConfigName = m.Name + "-appconfig"
182         }
183         time.Sleep(time.Duration(10 * time.Second))
184
185         return ApplyConfigMap(XAppConfig{Metadata: m, Configuration: cm})
186 }
187
188 func GetNamesFromHelmRepo() (names []string) {
189         rname := viper.GetString("helm.repo-name")
190
191         cmdArgs := strings.Join([]string{"search ", rname}, "")
192         out, err := HelmExec(cmdArgs)
193         if err != nil {
194                 return
195         }
196
197         re := regexp.MustCompile(rname + `/.*`)
198         result := re.FindAllStringSubmatch(string(out), -1)
199         if result != nil {
200                 var tmp string
201                 for _, v := range result {
202                         fmt.Sscanf(v[0], "%s", &tmp)
203                         names = append(names, strings.Split(tmp, "/")[1])
204                 }
205         }
206         return names
207 }
208
209 func Validate(req XAppConfig) (err error) {
210         c := XAppConfig{}
211         err = ReadSchema(req.Metadata.Name, &c)
212         if err != nil {
213                 log.Printf("No schema file found for '%s', aborting ...", req.Metadata.Name)
214                 return err
215         }
216
217         schemaLoader := gojsonschema.NewGoLoader(c.Descriptor)
218         documentLoader := gojsonschema.NewGoLoader(req.Configuration)
219
220         log.Println("Starting validation ...")
221         result, err := gojsonschema.Validate(schemaLoader, documentLoader)
222         if err != nil {
223                 log.Println("Validation failed: ", err)
224                 return
225         }
226
227         log.Println("validation done ...", err, result.Valid())
228         if result.Valid() == false {
229                 log.Println("The document is not valid, Errors: ", result.Errors())
230                 s := make([]string, 3)
231                 for i, desc := range result.Errors() {
232                         s = append(s, fmt.Sprintf(" (%d): %s.\n", i, desc.String()))
233                 }
234                 return errors.New(strings.Join(s, " "))
235         }
236         return
237 }
238
239 func ReadFile(name string, data interface{}) (err error) {
240         f, err := ioutil.ReadFile(name)
241         if err != nil {
242                 log.Printf("Reading '%s' file failed: %v", name, err)
243                 return
244         }
245
246         err = json.Unmarshal(f, &data)
247         if err != nil {
248                 log.Printf("Unmarshalling '%s' file failed: %v", name, err)
249                 return
250         }
251
252         return
253 }
254
255 func FetchChart(name string) (err error) {
256         tarDir := viper.GetString("xapp.tarDir")
257         repo := viper.GetString("helm.repo-name")
258         fetchArgs := fmt.Sprintf("--untar --untardir %s %s/%s", tarDir, repo, name)
259
260         _, err = HelmExec(strings.Join([]string{"fetch ", fetchArgs}, ""))
261         return
262 }
263
264 func GetMessages(name string) (msgs MessageTypes, err error) {
265         log.Println("Fetching tx/rx messages for: ", name)
266         return
267 }