aboutsummaryrefslogtreecommitdiffstats
path: root/src/orchestrator/api/app_profilehandler.go
diff options
context:
space:
mode:
authorEric Multanen <eric.w.multanen@intel.com>2020-02-19 16:19:18 -0800
committerEric Multanen <eric.w.multanen@intel.com>2020-03-05 16:29:23 -0800
commit65f9bc3b632a012429cd94aa2e6160b44fe633a7 (patch)
treee9e49dc5b1aa78cd07f8c96c3a90b2d27b566815 /src/orchestrator/api/app_profilehandler.go
parent8e0c4c63e5a014c95805f16d30a64cd53ddc7385 (diff)
Add API support to handle App Profiles
Add support for profiles per App under composite profiles. See: https://wiki.onap.org/display/DW/V2+API+Specification#V2APISpecification-Addingprofileperapplication Issue-ID: MULTICLOUD-997 Signed-off-by: Eric Multanen <eric.w.multanen@intel.com> Change-Id: I8569066d903327d640e315d0de835605ca1779c0
Diffstat (limited to 'src/orchestrator/api/app_profilehandler.go')
-rw-r--r--src/orchestrator/api/app_profilehandler.go266
1 files changed, 266 insertions, 0 deletions
diff --git a/src/orchestrator/api/app_profilehandler.go b/src/orchestrator/api/app_profilehandler.go
new file mode 100644
index 00000000..16423483
--- /dev/null
+++ b/src/orchestrator/api/app_profilehandler.go
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2020 Intel Corporation, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package api
+
+import (
+ "bytes"
+ "encoding/base64"
+ "encoding/json"
+ "io"
+ "io/ioutil"
+ "mime"
+ "mime/multipart"
+ "net/http"
+ "net/textproto"
+
+ moduleLib "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module"
+ "github.com/onap/multicloud-k8s/src/orchestrator/utils"
+
+ "github.com/gorilla/mux"
+ pkgerrors "github.com/pkg/errors"
+)
+
+/* Used to store backend implementation objects
+Also simplifies mocking for unit testing purposes
+*/
+type appProfileHandler struct {
+ client moduleLib.AppProfileManager
+}
+
+// createAppProfileHandler handles the create operation
+func (h appProfileHandler) createAppProfileHandler(w http.ResponseWriter, r *http.Request) {
+
+ vars := mux.Vars(r)
+ project := vars["project-name"]
+ compositeApp := vars["composite-app-name"]
+ compositeAppVersion := vars["composite-app-version"]
+ compositeProfile := vars["composite-profile-name"]
+
+ var ap moduleLib.AppProfile
+ var ac moduleLib.AppProfileContent
+
+ // Implemenation using multipart form
+ // Review and enable/remove at a later date
+ // Set Max size to 16mb here
+ err := r.ParseMultipartForm(16777216)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusUnprocessableEntity)
+ return
+ }
+
+ jsn := bytes.NewBuffer([]byte(r.FormValue("metadata")))
+ err = json.NewDecoder(jsn).Decode(&ap)
+ switch {
+ case err == io.EOF:
+ http.Error(w, "Empty body", http.StatusBadRequest)
+ return
+ case err != nil:
+ http.Error(w, err.Error(), http.StatusUnprocessableEntity)
+ return
+ }
+
+ //Read the file section and ignore the header
+ file, _, err := r.FormFile("file")
+ if err != nil {
+ http.Error(w, "Unable to process file", http.StatusUnprocessableEntity)
+ return
+ }
+
+ defer file.Close()
+
+ //Convert the file content to base64 for storage
+ content, err := ioutil.ReadAll(file)
+ if err != nil {
+ http.Error(w, "Unable to read file", http.StatusUnprocessableEntity)
+ return
+ }
+
+ err = utils.IsTarGz(bytes.NewBuffer(content))
+ if err != nil {
+ http.Error(w, "Error in file format", http.StatusUnprocessableEntity)
+ return
+ }
+
+ ac.Profile = base64.StdEncoding.EncodeToString(content)
+
+ // Name is required.
+ if ap.Metadata.Name == "" {
+ http.Error(w, "Missing name in POST request", http.StatusBadRequest)
+ return
+ }
+
+ ret, err := h.client.CreateAppProfile(project, compositeApp, compositeAppVersion, compositeProfile, ap, ac)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.WriteHeader(http.StatusCreated)
+ err = json.NewEncoder(w).Encode(ret)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+// getHandler handles the GET operations on AppProfile
+func (h appProfileHandler) getAppProfileHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ project := vars["project-name"]
+ compositeApp := vars["composite-app-name"]
+ compositeAppVersion := vars["composite-app-version"]
+ compositeProfile := vars["composite-profile-name"]
+ name := vars["app-profile"]
+ appName := r.URL.Query().Get("app-name")
+
+ if len(name) != 0 && len(appName) != 0 {
+ http.Error(w, pkgerrors.New("Invalid query").Error(), http.StatusInternalServerError)
+ return
+ }
+
+ // handle the get all app profiles case - return a list of only the json parts
+ if len(name) == 0 && len(appName) == 0 {
+ var retList []moduleLib.AppProfile
+
+ ret, err := h.client.GetAppProfiles(project, compositeApp, compositeAppVersion, compositeProfile)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ for _, ap := range ret {
+ retList = append(retList, ap)
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ err = json.NewEncoder(w).Encode(retList)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ return
+ }
+
+ accepted, _, err := mime.ParseMediaType(r.Header.Get("Accept"))
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusNotAcceptable)
+ return
+ }
+
+ var retAppProfile moduleLib.AppProfile
+ var retAppProfileContent moduleLib.AppProfileContent
+
+ if len(appName) != 0 {
+ retAppProfile, err = h.client.GetAppProfileByApp(project, compositeApp, compositeAppVersion, compositeProfile, appName)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ retAppProfileContent, err = h.client.GetAppProfileContentByApp(project, compositeApp, compositeAppVersion, compositeProfile, appName)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ } else {
+ retAppProfile, err = h.client.GetAppProfile(project, compositeApp, compositeAppVersion, compositeProfile, name)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ retAppProfileContent, err = h.client.GetAppProfileContent(project, compositeApp, compositeAppVersion, compositeProfile, name)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ }
+
+ switch accepted {
+ case "multipart/form-data":
+ mpw := multipart.NewWriter(w)
+ w.Header().Set("Content-Type", mpw.FormDataContentType())
+ w.WriteHeader(http.StatusOK)
+ pw, err := mpw.CreatePart(textproto.MIMEHeader{"Content-Type": {"application/json"}, "Content-Disposition": {"form-data; name=metadata"}})
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ if err := json.NewEncoder(pw).Encode(retAppProfile); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ pw, err = mpw.CreatePart(textproto.MIMEHeader{"Content-Type": {"application/octet-stream"}, "Content-Disposition": {"form-data; name=file"}})
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ kc_bytes, err := base64.StdEncoding.DecodeString(retAppProfileContent.Profile)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ _, err = pw.Write(kc_bytes)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ case "application/json":
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ err = json.NewEncoder(w).Encode(retAppProfile)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ case "application/octet-stream":
+ w.Header().Set("Content-Type", "application/octet-stream")
+ w.WriteHeader(http.StatusOK)
+ kc_bytes, err := base64.StdEncoding.DecodeString(retAppProfileContent.Profile)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ _, err = w.Write(kc_bytes)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ default:
+ http.Error(w, "set Accept: multipart/form-data, application/json or application/octet-stream", http.StatusMultipleChoices)
+ return
+ }
+}
+
+// deleteHandler handles the delete operations on AppProfile
+func (h appProfileHandler) deleteAppProfileHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ project := vars["project-name"]
+ compositeApp := vars["composite-app-name"]
+ compositeAppVersion := vars["composite-app-version"]
+ compositeProfile := vars["composite-profile-name"]
+ name := vars["app-profile"]
+
+ err := h.client.DeleteAppProfile(project, compositeApp, compositeAppVersion, compositeProfile, name)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.WriteHeader(http.StatusNoContent)
+}