ec9e296d93cccdeffa5f463e19ae82a548e29841
[ealt-edge.git] / mecm / mepm / applcm / broker / pkg / handlers / handlersImpl.go
1 /*
2  * Copyright 2020 Huawei Technologies Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package handlers
17
18 import (
19         "archive/zip"
20         "broker/pkg/handlers/adapter/dbAdapter"
21         "broker/pkg/handlers/adapter/pluginAdapter"
22         "broker/pkg/handlers/common"
23         "broker/pkg/handlers/model"
24         "bytes"
25         "encoding/json"
26         "fmt"
27         "github.com/buger/jsonparser"
28         "github.com/ghodss/yaml"
29         "github.com/google/uuid"
30         "github.com/gorilla/mux"
31         "github.com/jinzhu/gorm"
32         "io"
33         "io/ioutil"
34         "log"
35         "net/http"
36         "os"
37         "path/filepath"
38         "strings"
39 )
40
41 func UploadFileHldr(db *gorm.DB, w http.ResponseWriter, r *http.Request) {
42
43         file, header, err := r.FormFile("file")
44         defer file.Close()
45         if err != nil {
46                 common.RespondError(w, http.StatusBadRequest, err.Error())
47                 return
48         }
49
50         buf := bytes.NewBuffer(nil)
51         if _, err := io.Copy(buf, file); err != nil {
52                 common.RespondError(w, http.StatusBadRequest, err.Error())
53                 return
54         }
55
56         var packageName = ""
57         f := strings.Split(header.Filename, ".")
58         if len(f) > 0 {
59                 packageName = f[0]
60         }
61         fmt.Println(packageName)
62
63         pkgPath := PackageFolderPath + header.Filename
64         newFile, err := os.Create(pkgPath)
65         if err != nil {
66                 common.RespondError(w, http.StatusInternalServerError, err.Error())
67                 return
68         }
69
70         defer newFile.Close()
71         if _, err := newFile.Write(buf.Bytes()); err != nil {
72                 common.RespondError(w, http.StatusInternalServerError, err.Error())
73                 return
74         }
75
76         /* Unzip package to decode appDescriptor */
77         openPackage(w, pkgPath)
78
79         var yamlFile = PackageFolderPath + packageName + "/Definitions/" + "MainServiceTemplate.yaml"
80         appPkgInfo := decodeApplicationDescriptor(w, yamlFile)
81         appPkgInfo.AppPackage = header.Filename
82         appPkgInfo.OnboardingState = "ONBOARDED"
83
84         log.Println("Application package info from package")
85         defer r.Body.Close()
86
87         dbAdapter.InsertAppPackageInfo(db, appPkgInfo)
88
89         /*http.StatusOK*/
90         common.RespondJSON(w, http.StatusCreated, appPkgInfo)
91 }
92
93 func openPackage(w http.ResponseWriter, packagePath string) {
94         zipReader, _ := zip.OpenReader(packagePath)
95         for _, file := range zipReader.Reader.File {
96
97                 zippedFile, err := file.Open()
98                 if err != nil {
99                         common.RespondError(w, http.StatusBadRequest, err.Error())
100                 }
101                 defer zippedFile.Close()
102
103                 targetDir := PackageFolderPath + "/"
104                 extractedFilePath := filepath.Join(
105                         targetDir,
106                         file.Name,
107                 )
108
109                 if file.FileInfo().IsDir() {
110                         os.MkdirAll(extractedFilePath, file.Mode())
111                 } else {
112                         outputFile, err := os.OpenFile(
113                                 extractedFilePath,
114                                 os.O_WRONLY|os.O_CREATE|os.O_TRUNC,
115                                 file.Mode(),
116                         )
117                         if err != nil {
118                                 common.RespondError(w, http.StatusBadRequest, err.Error())
119                         }
120                         defer outputFile.Close()
121
122                         _, err = io.Copy(outputFile, zippedFile)
123                         if err != nil {
124                                 common.RespondError(w, http.StatusBadRequest, err.Error())
125                         }
126                 }
127         }
128 }
129
130 func decodeApplicationDescriptor(w http.ResponseWriter, serviceTemplate string) model.AppPackageInfo {
131
132         yamlFile, err := ioutil.ReadFile(serviceTemplate)
133         if err != nil {
134                 common.RespondError(w, http.StatusBadRequest, err.Error())
135         }
136
137         jsondata, err := yaml.YAMLToJSON(yamlFile)
138         if err != nil {
139                 common.RespondError(w, http.StatusBadRequest, err.Error())
140         }
141
142         appDId, _, _, _ := jsonparser.Get(jsondata, "topology_template", "node_templates", "face_recognition", "properties", "appDId")
143         appProvider, _, _, _ := jsonparser.Get(jsondata, "topology_template", "node_templates", "face_recognition", "properties", "appProvider")
144         appInfoName, _, _, _ := jsonparser.Get(jsondata, "topology_template", "node_templates", "face_recognition", "properties", "appInfoName")
145         appSoftVersion, _, _, _ := jsonparser.Get(jsondata, "topology_template", "node_templates", "face_recognition", "properties", "appSoftVersion")
146         appDVersion, _, _, _ := jsonparser.Get(jsondata, "topology_template", "node_templates", "face_recognition", "properties", "appDVersion")
147         deployType, _, _, _ := jsonparser.Get(jsondata, "topology_template", "node_templates", "face_recognition", "properties", "type")
148
149         appPkgInfo := model.AppPackageInfo{
150                 ID:                 string(appDId),
151                 AppDID:             string(appDId),
152                 AppProvider:        string(appProvider),
153                 AppName:            string(appInfoName),
154                 AppSoftwareVersion: string(appSoftVersion),
155                 AppDVersion:        string(appDVersion),
156                 DeployType:         string(deployType),
157         }
158
159         //return appPackageInfo
160         return appPkgInfo
161 }
162
163 func QueryAppPackageInfo(db *gorm.DB, w http.ResponseWriter, r *http.Request) {
164         params := mux.Vars(r)
165         appPkgId := params["appPkgId"]
166         appPkgInfo := dbAdapter.GetAppPackageInfo(db, appPkgId)
167         if appPkgInfo.ID == "" {
168                 common.RespondJSON(w, http.StatusNotFound, "ID not exist")
169                 return
170         }
171         common.RespondJSON(w, http.StatusAccepted, json.NewEncoder(w).Encode(appPkgInfo))
172 }
173
174 func DeleteAppPackage(db *gorm.DB, w http.ResponseWriter, r *http.Request) {
175         params := mux.Vars(r)
176         appPkgId := params["appPkgId"]
177         appPackageInfo := dbAdapter.GetAppPackageInfo(db, appPkgId)
178         if appPackageInfo.ID == "" {
179                 common.RespondJSON(w, http.StatusNotFound, "ID not exist")
180                 return
181         }
182         dbAdapter.DeleteAppPackageInfo(db, appPkgId)
183
184         deletePackage := PackageFolderPath + appPackageInfo.AppPackage
185
186         /* Delete ZIP*/
187         os.Remove(deletePackage)
188         f := strings.Split(appPackageInfo.AppPackage, ".")
189         if len(f) > 0 {
190                 packageName := f[0]
191                 /*Delete unzipped*/
192                 os.Remove(packageName)
193         }
194         common.RespondJSON(w, http.StatusAccepted, json.NewEncoder(w).Encode(""))
195 }
196
197 func CreateAppInstanceHldr(db *gorm.DB, w http.ResponseWriter, r *http.Request) {
198         var req model.CreateApplicationReq
199         err := json.NewDecoder(r.Body).Decode(&req)
200         if err != nil {
201                 common.RespondError(w, http.StatusInternalServerError, err.Error())
202                 return
203         }
204
205         appPkgInfo := dbAdapter.GetAppPackageInfo(db, req.AppDID)
206         if appPkgInfo.ID == "" {
207                 common.RespondJSON(w, http.StatusNotFound, "ID not exist")
208                 return
209         }
210         fmt.Println("Query appPkg Info:", appPkgInfo)
211
212         appInstanceId, err := uuid.NewUUID()
213         if err != nil {
214                 common.RespondError(w, http.StatusInternalServerError, err.Error())
215         }
216
217         appInstanceInfo := model.AppInstanceInfo{
218
219                 ID:                     appInstanceId.String(),
220                 AppInstanceName:        req.AppInstancename,
221                 AppInstanceDescription: req.AppInstanceDescriptor,
222                 AppDID:                 req.AppDID,
223                 AppProvider:            appPkgInfo.AppProvider,
224                 AppName:                appPkgInfo.AppName,
225                 AppSoftVersion:         appPkgInfo.AppSoftwareVersion,
226                 AppDVersion:            appPkgInfo.AppDVersion,
227                 AppPkgID:               appPkgInfo.AppDID,
228                 InstantiationState:     "NOT_INSTANTIATED",
229         }
230         dbAdapter.InsertAppInstanceInfo(db, appInstanceInfo)
231         fmt.Println("CreateAppInstanceHldr:", req)
232         /*http.StatusOK*/
233         common.RespondJSON(w, http.StatusCreated, json.NewEncoder(w).Encode(appInstanceInfo))
234 }
235
236 func InstantiateAppInstanceHldr(db *gorm.DB, w http.ResponseWriter, r *http.Request) {
237         var req model.InstantiateApplicationReq
238         err := json.NewDecoder(r.Body).Decode(&req)
239         if err != nil {
240                 common.RespondError(w, http.StatusInternalServerError, err.Error())
241                 return
242         }
243
244         params := mux.Vars(r)
245         appInstanceId := params["appInstanceId"]
246
247         appInstanceInfo := dbAdapter.GetAppInstanceInfo(db, appInstanceId)
248         appPackageInfo := dbAdapter.GetAppPackageInfo(db, appInstanceInfo.AppDID)
249         if appInstanceInfo.ID == "" || appPackageInfo.ID == "" {
250                 common.RespondJSON(w, http.StatusNotFound, "ID not exist")
251                 return
252         }
253
254         if appInstanceInfo.InstantiationState == "INSTANTIATED" {
255                 common.RespondError(w, http.StatusInternalServerError, "Application already instantiated")
256                 return
257         }
258
259         //remove extension
260         var packageName = ""
261         f := strings.Split(appPackageInfo.AppPackage, ".")
262         if len(f) > 0 {
263                 packageName = f[0]
264         }
265         fmt.Println(packageName)
266
267         var artifact string
268         var pluginInfo string
269
270         switch appPackageInfo.DeployType {
271         case "helm":
272                 pkgPath := PackageFolderPath + packageName + PackageArtifactPath + "Charts"
273                 artifact = getDeploymentArtifact(pkgPath, ".tar")
274                 if artifact == "" {
275                         common.RespondError(w, http.StatusInternalServerError, "artifact not available in application package")
276                         return
277                 }
278                 pluginInfo = "helm.plugin" + ":" + os.Getenv("HELM_PLUGIN_PORT")
279         case "kubernetes":
280                 pkgPath := PackageFolderPath + packageName + PackageArtifactPath + "Kubernetes"
281                 artifact = getDeploymentArtifact(pkgPath, "*.yaml")
282                 if artifact == "" {
283                         common.RespondError(w, http.StatusInternalServerError, "artifact not available in application package")
284                         return
285                 }
286                 pluginInfo = "kubernetes.plugin" + ":" + os.Getenv("KUBERNETES_PLUGIN_PORT")
287         default:
288                 common.RespondError(w, http.StatusInternalServerError, "Deployment type not supported")
289                 return
290         }
291         fmt.Println("Artifact to deploy:", artifact)
292
293         workloadId, err := pluginAdapter.Instantiate(pluginInfo, req.SelectedMECHostInfo.HostID, artifact)
294         if err != nil {
295                 common.RespondError(w, http.StatusInternalServerError, err.Error())
296                 return
297         }
298         dbAdapter.UpdateAppInstanceInfoInstStatusHostAndWorkloadId(db, appInstanceId, "INSTANTIATED", req.SelectedMECHostInfo.HostID, workloadId)
299
300         common.RespondJSON(w, http.StatusAccepted, json.NewEncoder(w).Encode(""))
301 }
302
303 func getDeploymentArtifact(dir string, ext string) string {
304         d, err := os.Open(dir)
305         if err != nil {
306                 fmt.Println(err)
307                 return ""
308         }
309         defer d.Close()
310
311         files, err := d.Readdir(-1)
312         if err != nil {
313                 fmt.Println(err)
314                 return ""
315         }
316
317         fmt.Println("Directory to read " + dir)
318
319         for _, file := range files {
320                 if file.Mode().IsRegular() {
321                         if filepath.Ext(file.Name()) == ext || filepath.Ext(file.Name()) == ".gz" {
322                                 fmt.Println(file.Name())
323                                 fmt.Println(dir + "/" + file.Name())
324                                 return dir + "/" + file.Name()
325                         }
326                 }
327         }
328         return ""
329 }
330
331 func QueryAppInstanceInfoHldr(db *gorm.DB, w http.ResponseWriter, r *http.Request) {
332
333         params := mux.Vars(r)
334         appInstanceId := params["appInstanceId"]
335
336         appInstanceInfo := dbAdapter.GetAppInstanceInfo(db, appInstanceId)
337         appPackageInfo := dbAdapter.GetAppPackageInfo(db, appInstanceInfo.AppDID)
338         if appInstanceInfo.ID == "" || appPackageInfo.ID == "" {
339                 common.RespondJSON(w, http.StatusNotFound, "ID not exist")
340                 return
341         }
342         var instantiatedAppState string
343         if appInstanceInfo.InstantiationState == "INSTANTIATED" {
344
345                 var pluginInfo string
346
347                 switch appPackageInfo.DeployType {
348                 case "helm":
349                         pluginInfo = "helm.plugin" + ":" + os.Getenv("HELM_PLUGIN_PORT")
350                 case "kubernetes":
351                         pluginInfo = "kubernetes.plugin" + ":" + os.Getenv("KUBERNETES_PLUGIN_PORT")
352                 default:
353                         common.RespondError(w, http.StatusInternalServerError, "Deployment type not supported")
354                         return
355                 }
356
357                 state, err := pluginAdapter.Query(pluginInfo, appInstanceInfo.Host, appInstanceInfo.WorkloadID)
358                 if err != nil {
359                         common.RespondError(w, http.StatusInternalServerError, err.Error())
360                         return
361                 }
362                 instantiatedAppState = state
363         }
364         appInstanceInfo.InstantiatedAppState = instantiatedAppState
365
366         common.RespondJSON(w, http.StatusCreated, json.NewEncoder(w).Encode(appInstanceInfo))
367 }
368
369 func QueryAppLcmOperationStatusHldr(db *gorm.DB, w http.ResponseWriter, r *http.Request) {
370         var req model.QueryApplicationLCMOperStatusReq
371         err := json.NewDecoder(r.Body).Decode(&req)
372         if err != nil {
373                 common.RespondError(w, http.StatusInternalServerError, err.Error())
374                 return
375         }
376
377         fmt.Fprintf(w, "QueryApplicationLCMOperStatus: %+v", req)
378 }
379
380 func TerminateAppInsHldr(db *gorm.DB, w http.ResponseWriter, r *http.Request) {
381         log.Println("TerminateAppInsHldr...")
382         params := mux.Vars(r)
383         appInstanceId := params["appInstanceId"]
384
385         appInstanceInfo := dbAdapter.GetAppInstanceInfo(db, appInstanceId)
386         appPackageInfo := dbAdapter.GetAppPackageInfo(db, appInstanceInfo.AppDID)
387         if appInstanceInfo.ID == "" || appPackageInfo.ID == "" {
388                 common.RespondJSON(w, http.StatusNotFound, "ID not exist")
389                 return
390         }
391
392         if appInstanceInfo.InstantiationState == "NOT_INSTANTIATED" {
393                 common.RespondError(w, http.StatusNotAcceptable, "instantiationState: NOT_INSTANTIATED")
394                 return
395         }
396
397         var pluginInfo string
398         switch appPackageInfo.DeployType {
399         case "helm":
400                 pluginInfo = "helm.plugin" + ":" + os.Getenv("HELM_PLUGIN_PORT")
401         case "kubernetes":
402                 pluginInfo = "kubernetes.plugin" + ":" + os.Getenv("KUBERNETES_PLUGIN_PORT")
403         default:
404                 common.RespondError(w, http.StatusInternalServerError, "Deployment type not supported")
405                 return
406         }
407
408         _, err := pluginAdapter.Terminate(pluginInfo, appInstanceInfo.Host, appInstanceInfo.WorkloadID)
409         if err != nil {
410                 common.RespondError(w, http.StatusInternalServerError, err.Error())
411                 return
412         }
413         dbAdapter.UpdateAppInstanceInfoInstStatusAndWorkload(db, appInstanceId, "NOT_INSTANTIATED", "")
414
415         common.RespondJSON(w, http.StatusAccepted, json.NewEncoder(w).Encode(""))
416 }
417 func DeleteAppInstanceIdentifierHldr(db *gorm.DB, w http.ResponseWriter, r *http.Request) {
418         fmt.Println("DeleteAppInstanceIdentifierHldr:")
419         params := mux.Vars(r)
420         appInstanceId := params["appInstanceId"]
421
422         dbAdapter.DeleteAppInstanceInfo(db, appInstanceId)
423         common.RespondJSON(w, http.StatusOK, json.NewEncoder(w).Encode(""))
424 }