From e7f3bf3050608edec03aa9d52cf8de79d56dfbd9 Mon Sep 17 00:00:00 2001 From: vikaskumar Date: Thu, 26 Nov 2020 13:11:45 +0530 Subject: MULTICLOUD-1257 updated gui flow Issue-ID: MULTICLOUD-1257 Change-Id: I5c1432c037952abeed6066cb067192076031f9cd Signed-off-by: vikaskumar --- src/tools/emcoui/middle_end/app/app.go | 1014 ++++++++++++++++++++++++++++++++ 1 file changed, 1014 insertions(+) create mode 100644 src/tools/emcoui/middle_end/app/app.go (limited to 'src/tools/emcoui/middle_end/app/app.go') diff --git a/src/tools/emcoui/middle_end/app/app.go b/src/tools/emcoui/middle_end/app/app.go new file mode 100644 index 00000000..a8511698 --- /dev/null +++ b/src/tools/emcoui/middle_end/app/app.go @@ -0,0 +1,1014 @@ +/* +======================================================================= +Copyright (c) 2017-2020 Aarna Networks, Inc. +All rights reserved. +====================================================================== +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 app + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "log" + "mime/multipart" + "net/http" + + "github.com/gorilla/mux" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" +) + +type deployServiceData struct { + Name string `json:"name"` + Description string `json:"description"` + Spec struct { + ProjectName string `json:"projectName"` + Apps []appsData `json:"appsData"` + } `json:"spec"` +} + +type deployDigData struct { + Name string `json:"name"` + Description string `json:"description"` + CompositeAppName string `json:"compositeApp"` + CompositeProfile string `json:"compositeProfile"` + DigVersion string `json:"version"` + CompositeAppVersion string `json:"compositeAppVersion"` + Spec struct { + ProjectName string `json:"projectName"` + Apps []appsData `json:"appsData"` + } `json:"spec"` +} + +// Exists is for mongo $exists filter +type Exists struct { + Exists string `json:"$exists"` +} + +// This is the json payload that the orchesration API expexts. +type appsData struct { + Metadata struct { + Name string `json:"name"` + Description string `json:"description"` + FileName string `json:"filename"` + } `json:"metadata"` + ProfileMetadata struct { + Name string `json:"name"` + FileName string `json:"filename"` + } `json:"profileMetadata"` + Clusters []struct { + Provider string `json:"provider"` + SelectedClusters []struct { + Name string `json:"name"` + Interfaces []struct { + NetworkName string `json:"networkName"` + IP string `json:"ip"` + Subnet string `json:"subnet"` + } `json:"interfaces"` + } `json:"selectedClusters"` + } `json:"clusters"` +} + +type DigsInProject struct { + Metadata struct { + Name string `json:"name"` + CompositeAppName string `json:"compositeAppName"` + CompositeAppVersion string `json:"compositeAppVersion"` + Description string `json:"description"` + UserData1 string `userData1:"userData1"` + UserData2 string `userData2:"userData2"` + } `json:"metadata"` + Spec struct { + DigIntentsData []DigDeployedIntents `json:"deployedIntents"` + Profile string `json:"profile"` + Version string `json:"version"` + Lcloud string `json:"logicalCloud"` + OverrideValuesObj []OverrideValues `json:"overrideValues"` + GpintArray []*DigsGpint `json:"GenericPlacementIntents,omitempty"` + NwintArray []*DigsNwint `json:"networkCtlIntents,omitempty"` + } `json:"spec"` +} + +type DigsGpint struct { + Metadata apiMetaData `json:"metadata,omitempty"` + Spec struct { + AppIntentArray []PlacementIntentExport `json:"placementIntent,omitempty"` + } `json:"spec,omitempty"` +} + +type DigsNwint struct { + Metadata apiMetaData `json:"metadata,omitempty"` + Spec struct { + WorkloadIntentsArray []*WorkloadIntents `json:"WorkloadIntents,omitempty"` + } `json:"spec,omitempty"` +} +type WorkloadIntents struct { + Metadata apiMetaData `json:"metadata,omitempty"` + Spec struct { + Interfaces []NwInterface `json:"interfaces,omitempty"` + } `json:"spec,omitempty"` +} + +// Project Tree +type ProjectTree struct { + Metadata ProjectMetadata + compositeAppMap map[string]*CompositeAppTree +} + +type treeTraverseFilter struct { + compositeAppName string + compositeAppVersion string + digName string +} + +// Composite app tree +type CompositeAppTree struct { + Metadata CompositeApp + AppsDataArray map[string]*AppsData + ProfileDataArray map[string]*ProfilesData + DigMap map[string]*DigReadData +} + +type DigReadData struct { + DigpData DeploymentIGP + DigIntentsData DigpIntents + GpintMap map[string]*GpintData + NwintMap map[string]*NwintData +} + +type GpintData struct { + Gpint GenericPlacementIntent + AppIntentArray []PlacementIntent +} + +type NwintData struct { + Nwint NetworkCtlIntent + WrkintMap map[string]*WrkintData +} + +type WrkintData struct { + Wrkint NetworkWlIntent + Interfaces []NwInterface +} + +type AppsData struct { + App CompositeApp + CompositeProfile ProfileMeta +} + +type ProfilesData struct { + Profile ProfileMeta + AppProfiles []ProfileMeta +} + +type ClusterMetadata struct { + Metadata apiMetaData `json:"Metadata"` +} + +type apiMetaData struct { + Name string `json:"name"` + Description string `json:"description"` + UserData1 string `userData1:"userData1"` + UserData2 string `userData2:"userData2"` +} + +// The interface +type orchWorkflow interface { + createAnchor() interface{} + createObject() interface{} + getObject() (interface{}, interface{}) + getAnchor() (interface{}, interface{}) + deleteObject() interface{} + deleteAnchor() interface{} +} + +// MiddleendConfig The configmap of the middleent +type MiddleendConfig struct { + OwnPort string `json:"ownport"` + Clm string `json:"clm"` + OrchService string `json:"orchestrator"` + OvnService string `json:"ovnaction"` + Mongo string `json:"mongo"` +} + +// OrchestrationHandler interface, handling the composite app APIs +type OrchestrationHandler struct { + MiddleendConf MiddleendConfig + client http.Client + compositeAppName string + compositeAppDesc string + AppName string + meta []appsData + DigData deployDigData + file map[string]*multipart.FileHeader + dataRead *ProjectTree + treeFilter *treeTraverseFilter + DigpReturnJson []DigsInProject + projectName string + projectDesc string + version string + response struct { + payload map[string][]byte + status map[string]int + } + digpIntents map[string]string + nwCtlIntents map[string]string +} + +// NewAppHandler interface implementing REST callhandler +func NewAppHandler() *OrchestrationHandler { + return &OrchestrationHandler{} +} + +// GetHealth to check connectivity +func (h OrchestrationHandler) GetHealth(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + +} + +func (h OrchestrationHandler) apiGet(url string, statusKey string) (interface{}, []byte, error) { + // prepare and DEL API + request, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, nil, err + } + resp, err := h.client.Do(request) + if err != nil { + return nil, nil, err + } + defer resp.Body.Close() + + // Prepare the response + data, _ := ioutil.ReadAll(resp.Body) + h.response.payload[statusKey] = data + h.response.status[statusKey] = resp.StatusCode + + return resp.StatusCode, data, nil +} + +func (h OrchestrationHandler) apiDel(url string, statusKey string) (interface{}, error) { + // prepare and DEL API + request, err := http.NewRequest("DELETE", url, nil) + if err != nil { + return nil, err + } + resp, err := h.client.Do(request) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + // Prepare the response + data, _ := ioutil.ReadAll(resp.Body) + h.response.payload[statusKey] = data + h.response.status[statusKey] = resp.StatusCode + + return resp.StatusCode, nil +} + +func (h OrchestrationHandler) apiPost(jsonLoad []byte, url string, statusKey string) (interface{}, error) { + // prepare and POST API + request, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonLoad)) + if err != nil { + return nil, err + } + resp, err := h.client.Do(request) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + // Prepare the response + data, _ := ioutil.ReadAll(resp.Body) + h.response.payload[statusKey] = data + h.response.status[statusKey] = resp.StatusCode + + return resp.StatusCode, nil +} + +func (h OrchestrationHandler) apiPostMultipart(jsonLoad []byte, + fh *multipart.FileHeader, url string, statusKey string, fileName string) (interface{}, error) { + // Open the file + file, err := fh.Open() + if err != nil { + return nil, err + } + // Close the file later + defer file.Close() + // Buffer to store our request body as bytes + var requestBody bytes.Buffer + // Create a multipart writer + multiPartWriter := multipart.NewWriter(&requestBody) + // Initialize the file field. Arguments are the field name and file name + // It returns io.Writer + fileWriter, err := multiPartWriter.CreateFormFile("file", fileName) + if err != nil { + return nil, err + } + // Copy the actual file content to the field field's writer + _, err = io.Copy(fileWriter, file) + if err != nil { + return nil, err + } + // Populate other fields + fieldWriter, err := multiPartWriter.CreateFormField("metadata") + if err != nil { + return nil, err + } + + _, err = fieldWriter.Write([]byte(jsonLoad)) + if err != nil { + return nil, err + } + + // We completed adding the file and the fields, let's close the multipart writer + // So it writes the ending boundary + multiPartWriter.Close() + + // By now our original request body should have been populated, + // so let's just use it with our custom request + req, err := http.NewRequest("POST", url, &requestBody) + if err != nil { + return nil, err + } + // We need to set the content type from the writer, it includes necessary boundary as well + req.Header.Set("Content-Type", multiPartWriter.FormDataContentType()) + + // Do the request + resp, err := h.client.Do(req) + if err != nil { + log.Fatalln(err) + return nil, err + } + defer resp.Body.Close() + // Prepare the response + data, _ := ioutil.ReadAll(resp.Body) + h.response.payload[statusKey] = data + h.response.status[statusKey] = resp.StatusCode + + return resp.StatusCode, nil +} +func (h *OrchestrationHandler) prepTreeReq(vars map[string]string) { + // Initialise the project tree with target composite application. + h.treeFilter = &treeTraverseFilter{} + h.treeFilter.compositeAppName = vars["composite-app-name"] + h.treeFilter.compositeAppVersion = vars["version"] + h.treeFilter.digName = vars["deployment-intent-group-name"] +} + +func (h *OrchestrationHandler) DelDig(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + h.projectName = vars["project-name"] + h.treeFilter = nil + + dataPoints := []string{"projectHandler", "compAppHandler", + "digpHandler", + "placementIntentHandler", + "networkIntentHandler"} + h.response.status = make(map[string]int) + h.response.payload = make(map[string][]byte) + + // Initialise the project tree with target composite application. + h.prepTreeReq(vars) + + h.dataRead = &ProjectTree{} + retcode := h.constructTree(dataPoints) + if retcode != nil { + if intval, ok := retcode.(int); ok { + w.WriteHeader(intval) + } else { + w.WriteHeader(500) + } + return + } + + // 1. Call DIG delte workflow + fmt.Printf("Delete wflow start") + deleteDataPoints := []string{"networkIntentHandler", + "placementIntentHandler", + "digpHandler"} + retcode = h.deleteTree(deleteDataPoints) + if retcode != nil { + if intval, ok := retcode.(int); ok { + w.WriteHeader(intval) + } else { + w.WriteHeader(500) + } + return + } + w.WriteHeader(204) +} + +// Delete service workflow +func (h *OrchestrationHandler) DelSvc(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + h.projectName = vars["project-name"] + h.treeFilter = nil + + dataPoints := []string{"projectHandler", "compAppHandler", + "digpHandler", + "ProfileHandler"} + h.response.status = make(map[string]int) + h.response.payload = make(map[string][]byte) + + // Initialise the project tree with target composite application. + h.prepTreeReq(vars) + + h.dataRead = &ProjectTree{} + retcode := h.constructTree(dataPoints) + if retcode != nil { + if intval, ok := retcode.(int); ok { + w.WriteHeader(intval) + } else { + w.WriteHeader(500) + } + return + } + fmt.Printf("tree %+v\n", h.dataRead) + // Check if a dig is present in this composite application + if len(h.dataRead.compositeAppMap[vars["composite-app-name"]].DigMap) != 0 { + w.WriteHeader(409) + w.Write([]byte("Non emtpy DIG in service\n")) + return + } + + // 1. Call delte workflow + fmt.Printf("Delete wflow start") + deleteDataPoints := []string{"ProfileHandler", + "compAppHandler"} + retcode = h.deleteTree(deleteDataPoints) + if retcode != nil { + if intval, ok := retcode.(int); ok { + w.WriteHeader(intval) + } else { + w.WriteHeader(500) + } + return + } + w.WriteHeader(204) +} + +func (h *OrchestrationHandler) getData(I orchWorkflow) (interface{}, interface{}) { + _, retcode := I.getAnchor() + if retcode != 200 { + return nil, retcode + } + dataPointData, retcode := I.getObject() + if retcode != 200 { + return nil, retcode + } + return dataPointData, retcode +} + +func (h *OrchestrationHandler) deleteData(I orchWorkflow) (interface{}, interface{}) { + _ = I.deleteObject() + _ = I.deleteAnchor() + return nil, 204 //FIXME +} + +func (h *OrchestrationHandler) deleteTree(dataPoints []string) interface{} { + //1. Fetch App data + var I orchWorkflow + for _, dataPoint := range dataPoints { + switch dataPoint { + case "projectHandler": + temp := &projectHandler{} + temp.orchInstance = h + I = temp + _, retcode := h.deleteData(I) + if retcode != 204 { + return retcode + } + break + case "compAppHandler": + temp := &compAppHandler{} + temp.orchInstance = h + I = temp + _, retcode := h.deleteData(I) + if retcode != 204 { + return retcode + } + break + case "ProfileHandler": + temp := &ProfileHandler{} + temp.orchInstance = h + I = temp + _, retcode := h.deleteData(I) + if retcode != 204 { + return retcode + } + break + case "digpHandler": + temp := &digpHandler{} + temp.orchInstance = h + I = temp + fmt.Printf("delete digp\n") + _, retcode := h.deleteData(I) + if retcode != 204 { + return retcode + } + break + case "placementIntentHandler": + temp := &placementIntentHandler{} + temp.orchInstance = h + I = temp + _, retcode := h.deleteData(I) + if retcode != 204 { + return retcode + } + break + case "networkIntentHandler": + temp := &networkIntentHandler{} + temp.orchInstance = h + I = temp + _, retcode := h.deleteData(I) + if retcode != 204 { + return retcode + } + break + default: + fmt.Printf("%s\n", dataPoint) + } + } + return nil +} + +func (h *OrchestrationHandler) constructTree(dataPoints []string) interface{} { + //1. Fetch App data + var I orchWorkflow + for _, dataPoint := range dataPoints { + switch dataPoint { + case "projectHandler": + temp := &projectHandler{} + temp.orchInstance = h + I = temp + _, retcode := h.getData(I) + if retcode != 200 { + return retcode + } + break + case "compAppHandler": + temp := &compAppHandler{} + temp.orchInstance = h + I = temp + _, retcode := h.getData(I) + if retcode != 200 { + return retcode + } + break + case "ProfileHandler": + temp := &ProfileHandler{} + temp.orchInstance = h + I = temp + _, retcode := h.getData(I) + if retcode != 200 { + return retcode + } + break + case "digpHandler": + temp := &digpHandler{} + temp.orchInstance = h + I = temp + _, retcode := h.getData(I) + if retcode != 200 { + return retcode + } + break + case "placementIntentHandler": + temp := &placementIntentHandler{} + temp.orchInstance = h + I = temp + _, retcode := h.getData(I) + if retcode != 200 { + return retcode + } + break + case "networkIntentHandler": + temp := &networkIntentHandler{} + temp.orchInstance = h + I = temp + _, retcode := h.getData(I) + if retcode != 200 { + return retcode + } + break + default: + fmt.Printf("%s\n", dataPoint) + } + } + return nil +} + +// This function partest he compositeapp tree read and populates the +// Dig tree +func (h *OrchestrationHandler) copyDigTree() { + dataRead := h.dataRead + h.DigpReturnJson = nil + + for compositeAppName, value := range dataRead.compositeAppMap { + for _, digValue := range dataRead.compositeAppMap[compositeAppName].DigMap { + Dig := DigsInProject{} + SourceDigMetadata := digValue.DigpData.Metadata + + // Copy the metadata + Dig.Metadata.Name = SourceDigMetadata.Name + Dig.Metadata.CompositeAppName = compositeAppName + Dig.Metadata.CompositeAppVersion = value.Metadata.Spec.Version + Dig.Metadata.Description = SourceDigMetadata.Description + Dig.Metadata.UserData1 = SourceDigMetadata.UserData1 + Dig.Metadata.UserData2 = SourceDigMetadata.UserData2 + + // Populate the Spec of dig + SourceDigSpec := digValue.DigpData.Spec + Dig.Spec.DigIntentsData = digValue.DigIntentsData.Intent + Dig.Spec.Profile = SourceDigSpec.Profile + Dig.Spec.Version = SourceDigSpec.Version + Dig.Spec.Lcloud = SourceDigSpec.Lcloud + Dig.Spec.OverrideValuesObj = SourceDigSpec.OverrideValuesObj + + // Pupolate the generic placement intents + SourceGpintMap := digValue.GpintMap + for t, gpintValue := range SourceGpintMap { + fmt.Printf("gpName value %s\n", t) + localGpint := DigsGpint{} + localGpint.Metadata = gpintValue.Gpint.Metadata + //localGpint.Spec.AppIntentArray = gpintValue.AppIntentArray + localGpint.Spec.AppIntentArray = make([]PlacementIntentExport, len(gpintValue.AppIntentArray)) + for k, _ := range gpintValue.AppIntentArray { + localGpint.Spec.AppIntentArray[k].Metadata = gpintValue.AppIntentArray[k].Metadata + localGpint.Spec.AppIntentArray[k].Spec.AppName = + gpintValue.AppIntentArray[k].Spec.AppName + localGpint.Spec.AppIntentArray[k].Spec.Intent.AllofCluster = + make([]AllofExport, len(gpintValue.AppIntentArray[k].Spec.Intent.AllofCluster)) + for i, _ := range gpintValue.AppIntentArray[k].Spec.Intent.AllofCluster { + localGpint.Spec.AppIntentArray[k].Spec.Intent.AllofCluster[i].ProviderName = + gpintValue.AppIntentArray[k].Spec.Intent.AllofCluster[i].ProviderName + localGpint.Spec.AppIntentArray[k].Spec.Intent.AllofCluster[i].ClusterName = + gpintValue.AppIntentArray[k].Spec.Intent.AllofCluster[i].ClusterName + } + } + + Dig.Spec.GpintArray = append(Dig.Spec.GpintArray, &localGpint) + } + // Populate the Nwint intents + SourceNwintMap := digValue.NwintMap + for _, nwintValue := range SourceNwintMap { + localNwint := DigsNwint{} + localNwint.Metadata = nwintValue.Nwint.Metadata + for _, wrkintValue := range nwintValue.WrkintMap { + localWrkint := WorkloadIntents{} + localWrkint.Metadata = wrkintValue.Wrkint.Metadata + localWrkint.Spec.Interfaces = wrkintValue.Interfaces + localNwint.Spec.WorkloadIntentsArray = append(localNwint.Spec.WorkloadIntentsArray, + &localWrkint) + } + Dig.Spec.NwintArray = append(Dig.Spec.NwintArray, &localNwint) + } + h.DigpReturnJson = append(h.DigpReturnJson, Dig) + } + } +} + +// GetSvc get the entrire tree under project// +func (h *OrchestrationHandler) GetAllDigs(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + h.version = vars["version"] + h.projectName = vars["project-name"] + h.response.status = make(map[string]int) + h.response.payload = make(map[string][]byte) + dataPoints := []string{"projectHandler", "compAppHandler", + "digpHandler", + "placementIntentHandler", + "networkIntentHandler"} + + h.dataRead = &ProjectTree{} + h.treeFilter = nil + retcode := h.constructTree(dataPoints) + if retcode != nil { + if intval, ok := retcode.(int); ok { + w.WriteHeader(intval) + } else { + w.WriteHeader(500) + } + return + } + // copy dig tree + h.copyDigTree() + retval, _ := json.Marshal(h.DigpReturnJson) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + w.Write(retval) +} + +// GetSvc get the entrire tree under project// +func (h *OrchestrationHandler) GetSvc(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + h.treeFilter = nil + h.compositeAppName = vars["composite-app-name"] + h.version = vars["version"] + h.projectName = vars["project-name"] + h.response.status = make(map[string]int) + h.response.payload = make(map[string][]byte) + + dataPoints := []string{"compAppHandler", "ProfileHandler", + "digpHandler", + "placementIntentHandler", + "networkIntentHandler"} + h.dataRead = &ProjectTree{} + retcode := h.constructTree(dataPoints) + if retcode != nil { + if intval, ok := retcode.(int); ok { + w.WriteHeader(intval) + } else { + w.WriteHeader(500) + } + return + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) +} + +// CreateApp exported function which creates the composite application +func (h *OrchestrationHandler) CreateDig(w http.ResponseWriter, r *http.Request) { + var jsonData deployDigData + + decoder := json.NewDecoder(r.Body) + err := decoder.Decode(&jsonData) + if err != nil { + log.Printf("Failed to parse json") + log.Fatalln(err) + } + + h.DigData = jsonData + + if len(h.DigData.Spec.Apps) == 0 { + w.WriteHeader(400) + w.Write([]byte("Bad request, no app metadata\n")) + return + } + + h.client = http.Client{} + + // These maps will get populated by the return status and respones of each V2 API + // that is called during the execution of the workflow. + h.response.payload = make(map[string][]byte) + h.response.status = make(map[string]int) + + // 4. Create DIG + h.digpIntents = make(map[string]string) + h.nwCtlIntents = make(map[string]string) + igHandler := &digpHandler{} + igHandler.orchInstance = h + igpStatus := createDInents(igHandler) + if igpStatus != nil { + if intval, ok := igpStatus.(int); ok { + w.WriteHeader(intval) + } else { + w.WriteHeader(500) + } + w.Write(h.response.payload[h.compositeAppName+"_digp"]) + return + } + + // 3. Create intents + intentHandler := &placementIntentHandler{} + intentHandler.orchInstance = h + intentStatus := addPlacementIntent(intentHandler) + if intentStatus != nil { + if intval, ok := intentStatus.(int); ok { + w.WriteHeader(intval) + } else { + w.WriteHeader(500) + } + w.Write(h.response.payload[h.compositeAppName+"_gpint"]) + return + } + + // If the metadata contains network interface request then call the + // network intent related part of the workflow. + if len(h.DigData.Spec.Apps[0].Clusters[0].SelectedClusters[0].Interfaces) != 0 { + nwHandler := &networkIntentHandler{} + nwHandler.orchInstance = h + nwIntentStatus := addNetworkIntent(nwHandler) + if nwIntentStatus != nil { + if intval, ok := nwIntentStatus.(int); ok { + w.WriteHeader(intval) + } else { + w.WriteHeader(500) + } + w.Write(h.response.payload[h.compositeAppName+"_nwctlint"]) + return + } + } + + w.WriteHeader(201) + w.Write(h.response.payload[h.DigData.Name]) +} + +func (h *OrchestrationHandler) CreateApp(w http.ResponseWriter, r *http.Request) { + var jsonData deployServiceData + + err := r.ParseMultipartForm(16777216) + if err != nil { + log.Fatalln(err) + } + + // Populate the multipart.FileHeader MAP. The key will be the + // filename itself. The metadata Map will be keyed on the application + // name. The metadata has a field file name, so later we can parse the metadata + // Map, and fetch the file headers from this file Map with keys as the filename. + h.file = make(map[string]*multipart.FileHeader) + for _, v := range r.MultipartForm.File { + fh := v[0] + h.file[fh.Filename] = fh + } + + jsn := ([]byte(r.FormValue("servicePayload"))) + err = json.Unmarshal(jsn, &jsonData) + if err != nil { + log.Printf("Failed to parse json") + log.Fatalln(err) + } + + h.compositeAppName = jsonData.Name + h.compositeAppDesc = jsonData.Description + h.projectName = jsonData.Spec.ProjectName + h.meta = jsonData.Spec.Apps + + // Sanity check. For each metadata there should be a + // corresponding file in the multipart request. If it + // not found we fail this API call. + for i := range h.meta { + switch { + case h.file[h.meta[i].Metadata.FileName] == nil: + t := fmt.Sprintf("File %s not in request", h.meta[i].Metadata.FileName) + w.WriteHeader(400) + w.Write([]byte(t)) + fmt.Printf("app file not found\n") + return + case h.file[h.meta[i].ProfileMetadata.FileName] == nil: + t := fmt.Sprintf("File %s not in request", h.meta[i].ProfileMetadata.FileName) + w.WriteHeader(400) + w.Write([]byte(t)) + fmt.Printf("profile file not found\n") + return + default: + fmt.Println("Good request") + } + } + + if len(h.meta) == 0 { + w.WriteHeader(400) + w.Write([]byte("Bad request, no app metadata\n")) + return + } + + h.client = http.Client{} + + // These maps will get populated by the return status and respones of each V2 API + // that is called during the execution of the workflow. + h.response.payload = make(map[string][]byte) + h.response.status = make(map[string]int) + + // 1. create the composite application. the compAppHandler implements the + // orchWorkflow interface. + appHandler := &compAppHandler{} + appHandler.orchInstance = h + appStatus := createCompositeapp(appHandler) + if appStatus != nil { + if intval, ok := appStatus.(int); ok { + w.WriteHeader(intval) + } else { + w.WriteHeader(500) + } + w.Write(h.response.payload[h.compositeAppName]) + return + } + + // 2. create the composite application profiles + profileHandler := &ProfileHandler{} + profileHandler.orchInstance = h + profileStatus := createProfile(profileHandler) + if profileStatus != nil { + if intval, ok := profileStatus.(int); ok { + w.WriteHeader(intval) + } else { + w.WriteHeader(500) + } + w.Write(h.response.payload[h.compositeAppName+"_profile"]) + return + } + + w.WriteHeader(201) + w.Write(h.response.payload[h.compositeAppName]) +} + +func (h *OrchestrationHandler) createCluster(filename string, fh *multipart.FileHeader, clusterName string, + jsonData ClusterMetadata) interface{} { + url := "http://" + h.MiddleendConf.Clm + "/v2/cluster-providers/" + clusterName + "/clusters" + + jsonLoad, _ := json.Marshal(jsonData) + + status, err := h.apiPostMultipart(jsonLoad, fh, url, clusterName, filename) + if err != nil { + return err + } + if status != 201 { + return status + } + fmt.Printf("cluster creation %s status %s\n", clusterName, status) + return nil +} + +func (h *OrchestrationHandler) CheckConnection(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + + parse_err := r.ParseMultipartForm(16777216) + if parse_err != nil { + fmt.Printf("multipart error: %s", parse_err.Error()) + w.WriteHeader(500) + return + } + + var fh *multipart.FileHeader + for _, v := range r.MultipartForm.File { + fh = v[0] + } + file, err := fh.Open() + if err != nil { + fmt.Printf("Failed to open the file: %s", err.Error()) + w.WriteHeader(500) + return + } + defer file.Close() + + // Read the kconfig + kubeconfig, _ := ioutil.ReadAll(file) + + jsonData := ClusterMetadata{} + jsn := ([]byte(r.FormValue("metadata"))) + err = json.Unmarshal(jsn, &jsonData) + if err != nil { + fmt.Printf("Failed to parse json") + w.WriteHeader(500) + return + } + fmt.Printf("metadata %+v\n", jsonData) + + // RESTConfigFromKubeConfig is a convenience method to give back + // a restconfig from your kubeconfig bytes. + config, err := clientcmd.RESTConfigFromKubeConfig(kubeconfig) + if err != nil { + fmt.Printf("Error while reading the kubeconfig: %s", err.Error()) + w.WriteHeader(500) + return + } + + // create the clientset + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + fmt.Printf("Failed to create clientset: %s", err.Error()) + w.WriteHeader(500) + return + } + + _, err = clientset.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{}) + if err != nil { + fmt.Printf("Failed to establish the connection: %s", err.Error()) + w.WriteHeader(403) + w.Write([]byte("Cluster connectivity failed\n")) + return + } + + fmt.Printf("Successfully established the connection\n") + h.client = http.Client{} + h.response.status = make(map[string]int) + h.response.payload = make(map[string][]byte) + + status := h.createCluster(fh.Filename, fh, vars["cluster-provider-name"], jsonData) + if status != nil { + w.WriteHeader(500) + return + } + + w.WriteHeader(200) + w.Write(h.response.payload[vars["cluster-provider-name"]]) + return +} -- cgit 1.2.3-korg