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/Dockerfile | 32 + src/tools/emcoui/middle_end/Makefile | 24 + src/tools/emcoui/middle_end/app/app.go | 1014 ++++++++++++++++++++ src/tools/emcoui/middle_end/app/compositeapp.go | 247 +++++ src/tools/emcoui/middle_end/app/digp.go | 322 +++++++ src/tools/emcoui/middle_end/app/intents.go | 664 +++++++++++++ src/tools/emcoui/middle_end/app/profile.go | 261 +++++ src/tools/emcoui/middle_end/app/projects.go | 187 ++++ src/tools/emcoui/middle_end/authproxy/README.md | 16 + src/tools/emcoui/middle_end/authproxy/authproxy.go | 281 ++++++ src/tools/emcoui/middle_end/db/dbconnection.go | 145 +++ src/tools/emcoui/middle_end/go.mod | 14 + src/tools/emcoui/middle_end/go.sum | 428 +++++++++ src/tools/emcoui/middle_end/main/main.go | 112 +++ 14 files changed, 3747 insertions(+) create mode 100644 src/tools/emcoui/middle_end/Dockerfile create mode 100644 src/tools/emcoui/middle_end/Makefile create mode 100644 src/tools/emcoui/middle_end/app/app.go create mode 100644 src/tools/emcoui/middle_end/app/compositeapp.go create mode 100644 src/tools/emcoui/middle_end/app/digp.go create mode 100644 src/tools/emcoui/middle_end/app/intents.go create mode 100644 src/tools/emcoui/middle_end/app/profile.go create mode 100644 src/tools/emcoui/middle_end/app/projects.go create mode 100644 src/tools/emcoui/middle_end/authproxy/README.md create mode 100644 src/tools/emcoui/middle_end/authproxy/authproxy.go create mode 100644 src/tools/emcoui/middle_end/db/dbconnection.go create mode 100644 src/tools/emcoui/middle_end/go.mod create mode 100644 src/tools/emcoui/middle_end/go.sum create mode 100644 src/tools/emcoui/middle_end/main/main.go (limited to 'src/tools/emcoui/middle_end') diff --git a/src/tools/emcoui/middle_end/Dockerfile b/src/tools/emcoui/middle_end/Dockerfile new file mode 100644 index 00000000..4e35322c --- /dev/null +++ b/src/tools/emcoui/middle_end/Dockerfile @@ -0,0 +1,32 @@ +#======================================================================= +# 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. +# ======================================================================== + +FROM golang:1.14.1 + +# Set the Current Working Directory inside the container +WORKDIR /src +COPY ./ ./ +RUN make all + +# Build the Go app +FROM ubuntu:16.04 +WORKDIR /opt/emco +RUN groupadd -r emco && useradd -r -g emco emco +RUN chown emco:emco /opt/emco -R +RUN mkdir ./config +COPY --chown=emco --from=0 /src/middleend ./ + +# Command to run the executable +CMD ["./middleend"] diff --git a/src/tools/emcoui/middle_end/Makefile b/src/tools/emcoui/middle_end/Makefile new file mode 100644 index 00000000..a44fc785 --- /dev/null +++ b/src/tools/emcoui/middle_end/Makefile @@ -0,0 +1,24 @@ +#======================================================================= +# 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. +# ======================================================================== + +export GO111MODULE=on + +all: clean + CGO_ENABLED=1 GOOS=linux GOARCH=amd64 + @go build -tags netgo -o ./middleend ./main/main.go + +clean: + @find . -name "*so" -delete + @rm -f middleend 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 +} diff --git a/src/tools/emcoui/middle_end/app/compositeapp.go b/src/tools/emcoui/middle_end/app/compositeapp.go new file mode 100644 index 00000000..daae5b00 --- /dev/null +++ b/src/tools/emcoui/middle_end/app/compositeapp.go @@ -0,0 +1,247 @@ +/* +======================================================================= +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 ( + "encoding/json" + "fmt" +) + +// CompositeApp application structure +type CompositeApp struct { + Metadata apiMetaData `json:"metadata"` + Spec compositeAppSpec `json:"spec"` +} + +type compositeAppSpec struct { + Version string `json:"version"` +} + +// compAppHandler , This implements the orchworkflow interface +type compAppHandler struct { + orchURL string + orchInstance *OrchestrationHandler +} + +// CompositeAppKey is the mongo key to fetch apps in a composite app +type CompositeAppKey struct { + Cname string `json:"compositeapp"` + Project string `json:"project"` + Cversion string `json:"compositeappversion"` + App interface{} `json:"app"` +} + +func (h *compAppHandler) getObject() (interface{}, interface{}) { + orch := h.orchInstance + respcode := 200 + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + "/apps" + + respcode, respdata, err := orch.apiGet(h.orchURL, orch.compositeAppName+"_getapps") + if err != nil { + return nil, 500 + } + if respcode != 200 { + return nil, respcode + } + fmt.Printf("Get app status %s\n", respcode) + compositeAppValue.AppsDataArray = make(map[string]*AppsData, len(respdata)) + var appList []CompositeApp + json.Unmarshal(respdata, &appList) + for _, value := range appList { + var appsDataInstance AppsData + appName := value.Metadata.Name + appsDataInstance.App = value + compositeAppValue.AppsDataArray[appName] = &appsDataInstance + } + } + return nil, respcode +} + +func (h *compAppHandler) getAnchor() (interface{}, interface{}) { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + respcode := 200 + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + respcode, _, err := orch.apiGet(h.orchURL, orch.compositeAppName+"_getcompositeapp") + if err != nil { + return nil, 500 + } + if respcode != 200 { + return nil, respcode + } + fmt.Printf("Get composite App %s\n", respcode) + //json.Unmarshal(respdata, &dataRead.CompositeApp) + } + return nil, respcode +} + +func (h *compAppHandler) deleteObject() interface{} { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + appList := compositeAppValue.AppsDataArray + for _, value := range appList { + url := h.orchURL + "/apps/" + value.App.Metadata.Name + fmt.Printf("Delete app %s\n", url) + resp, err := orch.apiDel(url, compositeAppMetadata.Name+"_delapp") + if err != nil { + return err + } + if resp != 204 { + return resp + } + fmt.Printf("Delete app status %s\n", resp) + } + } + return nil +} + +func (h *compAppHandler) deleteAnchor() interface{} { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + fmt.Printf("Delete composite app %s\n", h.orchURL) + resp, err := orch.apiDel(h.orchURL, compositeAppMetadata.Name+"_delcompapp") + if err != nil { + return err + } + if resp != 204 { + return resp + } + fmt.Printf("Delete compapp status %s\n", resp) + } + return nil +} + +// CreateAnchor creates the anchor point for composite applications, +// profiles, intents etc. For example Anchor for the composite application +// will create the composite application resource in the the DB, and all apps +// will get created and uploaded under this anchor point. +func (h *compAppHandler) createAnchor() interface{} { + orch := h.orchInstance + + compAppCreate := CompositeApp{ + Metadata: apiMetaData{ + Name: orch.compositeAppName, + Description: orch.compositeAppDesc, + UserData1: "data 1", + UserData2: "data 2"}, + Spec: compositeAppSpec{ + Version: "v1"}, + } + + jsonLoad, _ := json.Marshal(compAppCreate) + tem := CompositeApp{} + json.Unmarshal(jsonLoad, &tem) + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps" + resp, err := orch.apiPost(jsonLoad, h.orchURL, orch.compositeAppName) + if err != nil { + return err + } + if resp != 201 { + return resp + } + orch.version = "v1" + fmt.Printf("compAppHandler resp %s\n", resp) + + return nil +} + +func (h *compAppHandler) createObject() interface{} { + orch := h.orchInstance + for i := range orch.meta { + fileName := orch.meta[i].Metadata.FileName + appName := orch.meta[i].Metadata.Name + appDesc := orch.meta[i].Metadata.Description + + // Upload the application helm chart + fh := orch.file[fileName] + compAppAdd := CompositeApp{ + Metadata: apiMetaData{ + Name: appName, + Description: appDesc, + UserData1: "data 1", + UserData2: "data2"}, + } + url := h.orchURL + "/" + orch.compositeAppName + "/" + orch.version + "/apps" + + jsonLoad, _ := json.Marshal(compAppAdd) + + status, err := orch.apiPostMultipart(jsonLoad, fh, url, appName, fileName) + if err != nil { + return err + } + if status != 201 { + return status + } + fmt.Printf("Composite app %s createObject status %s\n", appName, status) + } + + return nil +} + +func createCompositeapp(I orchWorkflow) interface{} { + // 1. Create the Anchor point + err := I.createAnchor() + if err != nil { + return err + } + // 2. Create the Objects + err = I.createObject() + if err != nil { + return err + } + return nil +} + +func delCompositeapp(I orchWorkflow) interface{} { + // 1. Delete the object + err := I.deleteObject() + if err != nil { + return err + } + // 2. Delete the Anchor + err = I.deleteAnchor() + if err != nil { + return err + } + return nil +} diff --git a/src/tools/emcoui/middle_end/app/digp.go b/src/tools/emcoui/middle_end/app/digp.go new file mode 100644 index 00000000..b4c83e15 --- /dev/null +++ b/src/tools/emcoui/middle_end/app/digp.go @@ -0,0 +1,322 @@ +/* +======================================================================= +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 ( + "encoding/json" + "fmt" + "log" +) + +type DeploymentIGP struct { + Metadata apiMetaData `json:"metadata"` + Spec DigpSpec `json:"spec"` +} + +type DigpSpec struct { + Profile string `json:"profile"` + Version string `json:"version"` + Lcloud string `json:"logical-cloud"` + OverrideValuesObj []OverrideValues `json:"override-values"` +} + +// OverrideValues ... +type OverrideValues struct { + AppName string `json:"app-name"` + ValuesObj map[string]string `json:"values"` +} + +type IgpIntents struct { + Metadata apiMetaData `json:"metadata"` + Spec AppIntents `json:"spec"` +} + +type AppIntents struct { + Intent map[string]string `json:"intent"` +} + +type DigpIntents struct { + Intent []DigDeployedIntents `json:"intent"` +} +type DigDeployedIntents struct { + GenericPlacementIntent string `json:"genericPlacementIntent"` + Ovnaction string `json:"ovnaction"` +} + +// digpHandler implements the orchworkflow interface +type digpHandler struct { + orchURL string + orchInstance *OrchestrationHandler +} + +func (h *digpHandler) getAnchor() (interface{}, interface{}) { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + retcode := 200 + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + var digpList []DeploymentIGP + // This for the cases where the dig name is in the URL + if orch.treeFilter != nil && orch.treeFilter.digName != ""{ + temp:=DeploymentIGP{} + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + orch.treeFilter.digName + retcode, retval, err := orch.apiGet(h.orchURL, orch.compositeAppName+"_digp") + fmt.Printf("Get Digp in composite app %s status %d\n", compositeAppMetadata.Name, retcode) + if err != nil { + fmt.Printf("Failed to read digp") + return nil, 500 + } + if retcode != 200 { + fmt.Printf("Failed to read digp") + return nil, retcode + } + json.Unmarshal(retval, &temp) + digpList = append(digpList, temp) + } else { + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups" + retcode, retval, err := orch.apiGet(h.orchURL, orch.compositeAppName+"_digp") + fmt.Printf("Get Digp in composite app %s status %d\n", compositeAppMetadata.Name, retcode) + if err != nil { + fmt.Printf("Failed to read digp") + return nil, 500 + } + if retcode != 200 { + fmt.Printf("Failed to read digp") + return nil, retcode + } + json.Unmarshal(retval, &digpList) + } + + compositeAppValue.DigMap = make(map[string]*DigReadData, len(digpList)) + for _, digpValue := range digpList { + var Dig DigReadData + Dig.DigpData = digpValue + compositeAppValue.DigMap[digpValue.Metadata.Name] = &Dig + } + } + return nil, retcode +} + +func (h *digpHandler) getObject() (interface{}, interface{}) { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + digpList := compositeAppValue.DigMap + for digName, digValue := range digpList { + url := h.orchURL + digName + "/intents" + retcode, retval, err := orch.apiGet(url, compositeAppMetadata.Name+"_digpIntents") + fmt.Printf("Get Dig int composite app %s Dig %s status %d \n", orch.compositeAppName, + digName, retcode) + if err != nil { + fmt.Printf("Failed to read digp intents") + return nil, 500 + } + if retcode != 200 { + fmt.Printf("Failed to read digp intents") + return nil, retcode + + } + err = json.Unmarshal(retval, &digValue.DigIntentsData) + if err != nil { + fmt.Printf("Failed to read intents %s\n", err) + } + } + } + return nil, 200 +} + +func (h *digpHandler) deleteObject() interface{} { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + digpList := compositeAppValue.DigMap + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + + for digName, _ := range digpList { + url := h.orchURL + digName + "/intents/PlacementIntent" + fmt.Printf("dlete intents %s\n", url) + resp, err := orch.apiDel(url, orch.compositeAppName+"_deldigintents") + if err != nil { + return err + } + if resp != 204 { + return resp + } + fmt.Printf("Delete dig intents resp %s\n", resp) + } + } + return nil +} + +func (h *digpHandler) deleteAnchor() interface{} { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + digpList := compositeAppValue.DigMap + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + + // loop through all the intents in the dig + for digName, _ := range digpList { + url := h.orchURL + digName + turl := h.orchURL + digName + "/terminate" + fmt.Printf("delete intents %s\n", url) + jsonLoad, _ := json.Marshal("{}") + resp, err := orch.apiPost(jsonLoad, turl, orch.compositeAppName+"_terminatedig") + //Not checking the status of terminate FIXME + resp, err = orch.apiDel(url, orch.compositeAppName+"_deldig") + if err != nil { + return err + } + if resp != 204 { + return resp + } + fmt.Printf("Delete dig resp %s\n", resp) + } + } + return nil +} + +func (h *digpHandler) createAnchor() interface{} { + digData := h.orchInstance.DigData + orch := h.orchInstance + + digp := DeploymentIGP{ + Metadata: apiMetaData{ + Name: digData.Name, + Description: digData.Description, + UserData1: "data 1", + UserData2: "data2"}, + Spec: DigpSpec{ + Profile: digData.CompositeProfile, + Version: digData.DigVersion, + Lcloud: "unused_logical_cloud", + OverrideValuesObj: make([]OverrideValues, len(digData.Spec.Apps)), + }, + } + overrideVals := digp.Spec.OverrideValuesObj + for i, value := range digData.Spec.Apps { + overrideVals[i].ValuesObj = make(map[string]string) + overrideVals[i].AppName = value.Metadata.Name + overrideVals[i].ValuesObj["Values.global.dcaeCollectorIp"] = "1.8.0" + } + + jsonLoad, _ := json.Marshal(digp) + + // POST the generic placement intent + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + digData.Spec.ProjectName + + "/composite-apps/" + digData.CompositeAppName + "/" + digData.CompositeAppVersion + + "/deployment-intent-groups" + resp, err := orch.apiPost(jsonLoad, h.orchURL, digData.Name) + if err != nil { + return err + } + if resp != 201 { + return resp + } + orch.digpIntents["generic-placement-intent"] = digData.CompositeAppName + "_gpint" + orch.nwCtlIntents["network-controller-intent"] = digData.CompositeAppName + "_nwctlint" + fmt.Printf("Deloyment intent group resp %s\n", resp) + + return nil +} + +func (h *digpHandler) createObject() interface{} { + digData := h.orchInstance.DigData + orch := h.orchInstance + intentName := "PlacementIntent" + igp := IgpIntents{ + Metadata: apiMetaData{ + Name: intentName, + Description: "NA", + UserData1: "data 1", + UserData2: "data2"}, + } + if len(digData.Spec.Apps[0].Clusters[0].SelectedClusters[0].Interfaces) != 0 { + igp.Spec.Intent = make(map[string]string) + igp.Spec.Intent["genericPlacementIntent"] = orch.digpIntents["generic-placement-intent"] + igp.Spec.Intent["ovnaction"] = orch.nwCtlIntents["network-controller-intent"] + } else { + igp.Spec.Intent = make(map[string]string) + igp.Spec.Intent["genericPlacementIntent"] = orch.digpIntents["generic-placement-intent"] + } + + url := h.orchURL + "/" + digData.Name + "/intents" + jsonLoad, _ := json.Marshal(igp) + status, err := orch.apiPost(jsonLoad, url, intentName) + fmt.Printf("DIG name req %s", string(jsonLoad)) + if err != nil { + log.Fatalln(err) + } + if status != 201 { + return status + } + fmt.Printf("Placement intent %s status %s %s\n", intentName, status, url) + + return nil +} + +func createDInents(I orchWorkflow) interface{} { + // 1. Create the Anchor point + err := I.createAnchor() + if err != nil { + return err + } + // 2. Create the Objects + err = I.createObject() + if err != nil { + return err + } + return nil +} + +func delDigp(I orchWorkflow) interface{} { + // 1. Delete the object + err := I.deleteObject() + if err != nil { + return err + } + // 2. Delete the Anchor + err = I.deleteAnchor() + if err != nil { + return err + } + return nil +} diff --git a/src/tools/emcoui/middle_end/app/intents.go b/src/tools/emcoui/middle_end/app/intents.go new file mode 100644 index 00000000..992f7b66 --- /dev/null +++ b/src/tools/emcoui/middle_end/app/intents.go @@ -0,0 +1,664 @@ +/* +======================================================================= +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 ( + "encoding/json" + "fmt" + "log" + "strconv" +) + +type GenericPlacementIntent struct { + Metadata apiMetaData `json:"metadata"` +} + +type PlacementIntent struct { + Metadata apiMetaData `json:"metadata"` + Spec AppPlacementIntentSpec `json:"spec"` +} +type PlacementIntentExport struct { + Metadata apiMetaData `json:"metadata"` + Spec AppPlacementIntentSpecExport `json:"spec"` +} + +// appPlacementIntentSpec is the spec for per app intent +type AppPlacementIntentSpec struct { + AppName string `json:"app-name"` + Intent arrayIntent `json:"intent"` +} +type arrayIntent struct { + AllofCluster []Allof `json:"allof"` +} +type Allof struct { + ProviderName string `json:"provider-name"` + ClusterName string `json:"cluster-name"` +} +type AppPlacementIntentSpecExport struct { + AppName string `json:"appName"` + Intent arrayIntentExport `json:"intent"` +} +type arrayIntentExport struct { + AllofCluster []AllofExport `json:"allof"` +} +type AllofExport struct { + ProviderName string `json:"providerName"` + ClusterName string `json:"clusterName"` +} + +// plamcentIntentHandler implements the orchworkflow interface +type placementIntentHandler struct { + orchURL string + orchInstance *OrchestrationHandler +} + +type NetworkCtlIntent struct { + Metadata apiMetaData `json:"metadata"` +} + +type NetworkWlIntent struct { + Metadata apiMetaData `json:"metadata"` + Spec WorkloadIntentSpec `json:"spec"` +} + +type WorkloadIntentSpec struct { + AppName string `json:"application-name"` + Resource string `json:"workload-resource"` + Type string `json:"type"` +} +type WorkloadIntentSpecExport struct { + AppName string `json:"applicationName"` + Resource string `json:"workloadResource"` + Type string `json:"type"` +} + +type NwInterface struct { + Metadata apiMetaData `json:"metadata"` + Spec InterfaceSpec `json:"spec"` +} + +type InterfaceSpec struct { + Interface string `json:"interface"` + Name string `json:"name"` + DefaultGateway string `json:"defaultGateway"` + IPAddress string `json:"ipAddress"` + MacAddress string `json:"macAddress"` +} + +// networkIntentHandler implements the orchworkflow interface +type networkIntentHandler struct { + ovnURL string + orchInstance *OrchestrationHandler +} + +func (h *placementIntentHandler) getObject() (interface{}, interface{}) { + orch := h.orchInstance + retcode := 200 + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + Dig := compositeAppValue.DigMap + Apps := compositeAppValue.AppsDataArray + for digName, digValue := range Dig { + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + digName + "/generic-placement-intents" + for gpintName, gpintValue := range digValue.GpintMap { + for appName, _ := range Apps { + var appPint PlacementIntent + url := h.orchURL + "/" + gpintName + "/app-intents/" + appName + "_pint" + retcode, retval, err := orch.apiGet(url, compositeAppMetadata.Name+"_getappPint") + fmt.Printf("Get Gpint App intent in Composite app %s dig %s Gpint %s status %s\n", + orch.compositeAppName, digName, gpintName, retcode) + if err != nil { + fmt.Printf("Failed to read app pint\n") + return nil, 500 + } + if retcode != 200 { + fmt.Printf("Failed to read app pint\n") + return nil, 200 + } + err = json.Unmarshal(retval, &appPint) + if err != nil { + fmt.Printf("Failed to unmarshal json %s\n", err) + return nil, 500 + } + gpintValue.AppIntentArray = append(gpintValue.AppIntentArray, appPint) + } + } + } + } + return nil, retcode +} + +func (h *placementIntentHandler) getAnchor() (interface{}, interface{}) { + orch := h.orchInstance + retcode := 200 + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + Dig := compositeAppValue.DigMap + for digName, digValue := range Dig { + var gpintList []GenericPlacementIntent + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + digName + "/generic-placement-intents" + retcode, retval, err := orch.apiGet(h.orchURL, compositeAppMetadata.Name+"_getgpint") + fmt.Printf("Get Gpint in Composite app %s dig %s status %s\n", orch.compositeAppName, + digName, retcode) + if err != nil { + fmt.Printf("Failed to read gpint\n") + return nil, 500 + } + if retcode != 200 { + fmt.Printf("Failed to read gpint\n") + return nil, retcode + } + json.Unmarshal(retval, &gpintList) + digValue.GpintMap = make(map[string]*GpintData, len(gpintList)) + for _, value := range gpintList { + var GpintDataInstance GpintData + GpintDataInstance.Gpint = value + digValue.GpintMap[value.Metadata.Name] = &GpintDataInstance + } + } + } + return nil, retcode +} + +func (h *placementIntentHandler) deleteObject() interface{} { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + Dig := compositeAppValue.DigMap + Apps := compositeAppValue.AppsDataArray + + // loop through all app intens in the gpint + for digName, digValue := range Dig { + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + digName + "/generic-placement-intents/" + for gpintName, _ := range digValue.GpintMap { + for appName, _ := range Apps { + url := h.orchURL + gpintName + + "/app-intents/" + appName + "_pint" // FIXME when query API works, change this API call to + // query based on app name. + fmt.Printf("Delete gping app intents %s\n", url) + resp, err := orch.apiDel(url, orch.compositeAppName+"_delgpintintents") + if err != nil { + return err + } + if resp != 204 { + return resp + } + fmt.Printf("Delete gpint intents resp %s\n", resp) + } + } + } + } + return nil +} + +func (h placementIntentHandler) deleteAnchor() interface{} { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + Dig := compositeAppValue.DigMap + + // loop through all app intens in the gpint + for digName, digValue := range Dig { + for gpintName, _ := range digValue.GpintMap { + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + digName + "/generic-placement-intents/" + + gpintName + fmt.Printf("Delete gpint %s\n", h.orchURL) + resp, err := orch.apiDel(h.orchURL, compositeAppMetadata.Name+"_delgpints") + if err != nil { + return err + } + if resp != 204 { + return resp + } + fmt.Printf("Delete gpint resp %s\n", resp) + } + } + } + return nil +} + +func (h *placementIntentHandler) createAnchor() interface{} { + orch := h.orchInstance + intentData := h.orchInstance.DigData + + gpi := GenericPlacementIntent{ + Metadata: apiMetaData{ + Name: intentData.CompositeAppName + "_gpint", + Description: "Generic placement intent created from middleend", + UserData1: "data 1", + UserData2: "data2"}, + } + + jsonLoad, _ := json.Marshal(gpi) + // POST the generic placement intent + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + intentData.Spec.ProjectName + + "/composite-apps/" + intentData.CompositeAppName + "/" + intentData.CompositeAppVersion + + "/deployment-intent-groups/" + intentData.Name + url := h.orchURL + "/generic-placement-intents" + resp, err := orch.apiPost(jsonLoad, url, orch.digpIntents["generic-placement-intent"]) + if err != nil { + return err + } + if resp != 201 { + return resp + } + fmt.Printf("Generic placement intent resp %s\n", resp) + + return nil +} + +func (h *placementIntentHandler) createObject() interface{} { + orch := h.orchInstance + intentData := h.orchInstance.DigData + + for _, value := range intentData.Spec.Apps { + appName := value.Metadata.Name + intentName := appName + "_pint" + genericAppIntentName := intentData.CompositeAppName + "_gpint" + providerName := value.Clusters[0].Provider + clusterName := value.Clusters[0].SelectedClusters[0].Name + + pint := PlacementIntent{ + Metadata: apiMetaData{ + Name: intentName, + Description: "NA", + UserData1: "data 1", + UserData2: "data2"}, + Spec: AppPlacementIntentSpec{ + AppName: appName, + Intent: arrayIntent{ + AllofCluster: []Allof{ // FIXME: the logic requires to handle allof/anyof and multi cluster. + Allof{ + ProviderName: providerName, + ClusterName: clusterName}, + }, + }, + }, + } + + url := h.orchURL + "/generic-placement-intents/" + genericAppIntentName + "/app-intents" + jsonLoad, _ := json.Marshal(pint) + status, err := orch.apiPost(jsonLoad, url, intentName) + if err != nil { + log.Fatalln(err) + } + if status != 201 { + return status + } + fmt.Printf("Placement intent %s status %s %s\n", intentName, status, url) + } + + return nil +} + +func addPlacementIntent(I orchWorkflow) interface{} { + // 1. Create the Anchor point + err := I.createAnchor() + if err != nil { + return err + } + // 2. Create the Objects + err = I.createObject() + if err != nil { + return err + } + return nil +} + +func delGpint(I orchWorkflow) interface{} { + // 1. Create the Anchor point + err := I.deleteObject() + if err != nil { + return err + } + // 2. Create the Objects + err = I.deleteAnchor() + if err != nil { + return err + } + return nil +} + +func (h *networkIntentHandler) createAnchor() interface{} { + orch := h.orchInstance + intentData := h.orchInstance.DigData + + nwIntent := NetworkCtlIntent{ + Metadata: apiMetaData{ + Name: intentData.CompositeAppName + "_nwctlint", + Description: "Network Controller created from middleend", + UserData1: "data 1", + UserData2: "data2"}, + } + jsonLoad, _ := json.Marshal(nwIntent) + // POST the network controller intent + h.ovnURL = "http://" + orch.MiddleendConf.OvnService + "/v2/projects/" + intentData.Spec.ProjectName + + "/composite-apps/" + intentData.CompositeAppName + "/" + intentData.CompositeAppVersion + + "/deployment-intent-groups/" + intentData.Name + url := h.ovnURL + "/network-controller-intent" + resp, err := orch.apiPost(jsonLoad, url, orch.nwCtlIntents["network-controller-intent"]) + if err != nil { + return err + } + if resp != 201 { + return resp + } + fmt.Printf("Network contoller intent resp %s\n", resp) + + return nil +} + +func (h *networkIntentHandler) createObject() interface{} { + orch := h.orchInstance + intentData := h.orchInstance.DigData + + for _, value := range intentData.Spec.Apps { + + appName := value.Metadata.Name + intentName := value.Metadata.Name + "_wnwlint" + genericAppIntentName := intentData.CompositeAppName + "_nwctlint" + + wlIntent := NetworkWlIntent{ + Metadata: apiMetaData{ + Name: intentName, + Description: "NA", + UserData1: "data 1", + UserData2: "data2"}, + Spec: WorkloadIntentSpec{ + AppName: appName, + Resource: appName, + Type: "deployment", + }, + } + + url := h.ovnURL + "/network-controller-intent/" + genericAppIntentName + "/workload-intents" + jsonLoad, _ := json.Marshal(wlIntent) + status, err := orch.apiPost(jsonLoad, url, intentName) + if err != nil { + log.Fatalln(err) + } + if status != 201 { + return status + } + fmt.Printf("Workload intent %s status %s %s\n", intentName, status, url) + } + + // Add interfaces for to each application + for _, value := range intentData.Spec.Apps { + interfaces := value.Clusters[0].SelectedClusters[0].Interfaces + for j := range interfaces { + interfaceNum := strconv.Itoa(j) + interfaceName := value.Metadata.Name + "_interface" + interfaceNum + genericAppIntentName := intentData.CompositeAppName + "_nwctlint" + workloadIntent := value.Metadata.Name + "_wnwlint" + + iface := NwInterface{ + Metadata: apiMetaData{ + Name: interfaceName, + Description: "NA", + UserData1: "data 1", + UserData2: "data2"}, + Spec: InterfaceSpec{ + Interface: "eth" + interfaceNum, + Name: interfaces[j].NetworkName, + DefaultGateway: "false", + IPAddress: interfaces[j].IP, + }, + } + + url := h.ovnURL + "/network-controller-intent" + "/" + genericAppIntentName + + "/workload-intents/" + workloadIntent + "/interfaces" + jsonLoad, _ := json.Marshal(iface) + status, err := orch.apiPost(jsonLoad, url, interfaceName) + if err != nil { + log.Fatalln(err) + } + if status != 201 { + return status + } + fmt.Printf("interface %s status %s %s\n", interfaceName, status, url) + } + } + + return nil +} + +func (h *networkIntentHandler) getObject() (interface{}, interface{}) { + orch := h.orchInstance + retcode := 200 + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + Dig := compositeAppValue.DigMap + for digName, digValue := range Dig { + h.ovnURL = "http://" + orch.MiddleendConf.OvnService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + digName + for nwintName, nwintValue := range digValue.NwintMap { + var wrlintList []NetworkWlIntent + wlurl := h.ovnURL + "/network-controller-intent/" + nwintName + "/workload-intents" + retcode, retval, err := orch.apiGet(wlurl, orch.compositeAppName+"_getnwwlint") + fmt.Printf("Get Wrkld intents in Composite app %s dig %s nw intent %s status %d\n", + orch.compositeAppName, digName, nwintName, retcode) + if err != nil { + fmt.Printf("Failed to read nw workload int") + return nil, 500 + } + if retcode != 200 { + fmt.Printf("Failed to read nw workload int") + return nil, retcode + } + json.Unmarshal(retval, &wrlintList) + nwintValue.WrkintMap = make(map[string]*WrkintData, len(wrlintList)) + for _, wrlIntValue := range wrlintList { + var WrkintDataInstance WrkintData + WrkintDataInstance.Wrkint = wrlIntValue + + var ifaceList []NwInterface + ifaceurl := h.ovnURL + "/network-controller-intent/" + nwintName + + "/workload-intents/" + wrlIntValue.Metadata.Name + "/interfaces" + retcode, retval, err := orch.apiGet(ifaceurl, orch.compositeAppName+"_getnwiface") + fmt.Printf("Get interface in Composite app %s dig %s nw intent %s wrkld intent %s status %d\n", + orch.compositeAppName, digName, nwintName, wrlIntValue.Metadata.Name, retcode) + if err != nil { + fmt.Printf("Failed to read nw interface") + return nil, 500 + } + if retcode != 200 { + fmt.Printf("Failed to read nw interface") + return nil, retcode + } + json.Unmarshal(retval, &ifaceList) + WrkintDataInstance.Interfaces = ifaceList + nwintValue.WrkintMap[wrlIntValue.Metadata.Name] = &WrkintDataInstance + } + } + } + } + return nil, retcode +} + +func (h *networkIntentHandler) getAnchor() (interface{}, interface{}) { + orch := h.orchInstance + retcode := 200 + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + Dig := compositeAppValue.DigMap + for digName, digValue := range Dig { + h.ovnURL = "http://" + orch.MiddleendConf.OvnService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + digName + var nwintList []NetworkCtlIntent + + url := h.ovnURL + "/network-controller-intent" + retcode, retval, err := orch.apiGet(url, orch.compositeAppName+"_getnwint") + fmt.Printf("Get Network Ctl intent in Composite app %s dig %s status %d\n", + orch.compositeAppName, digName, retcode) + if err != nil { + fmt.Printf("Failed to read nw int %s\n", err) + return nil, 500 + } + if retcode != 200 { + fmt.Printf("Failed to read nw int") + return nil, retcode + } + json.Unmarshal(retval, &nwintList) + digValue.NwintMap = make(map[string]*NwintData, len(nwintList)) + for _, nwIntValue := range nwintList { + var NwintDataInstance NwintData + NwintDataInstance.Nwint = nwIntValue + digValue.NwintMap[nwIntValue.Metadata.Name] = &NwintDataInstance + } + } + } + return nil, retcode +} + +func (h *networkIntentHandler) deleteObject() interface{} { + orch := h.orchInstance + retcode := 200 + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + Dig := compositeAppValue.DigMap + for digName, digValue := range Dig { + h.ovnURL = "http://" + orch.MiddleendConf.OvnService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + digName + + for nwintName, nwintValue := range digValue.NwintMap { + for wrkintName, wrkintValue := range nwintValue.WrkintMap { + // Delete the interfaces per workload intent. + for _, value := range wrkintValue.Interfaces { + url := h.ovnURL + "network-controller-intent/" + nwintName + "/workload-intents/" + + wrkintName + "/interfaces/" + value.Spec.Name + fmt.Printf("Delete app nw interface %s\n", url) + retcode, err := orch.apiDel(url, orch.compositeAppName+"_delnwinterface") + if err != nil { + return err + } + if retcode != 204 { + return retcode + } + fmt.Printf("Delete nw interface resp %s\n", retcode) + } + // Delete the workload intents. + url := h.ovnURL + "network-controller-intent/" + nwintName + "/workload-intents/" + wrkintName + fmt.Printf("Delete app nw wl intent %s\n", url) + retcode, err := orch.apiDel(url, orch.compositeAppName+"_delnwwrkintent") + if err != nil { + return err + } + if retcode != 204 { + return retcode + } + fmt.Printf("Delete nw wl intent resp %s\n", retcode) + } // For workload intents in network controller intent. + } // For network controller intents in Dig. + } // For Dig. + } // For composite app. + return retcode +} + +func (h networkIntentHandler) deleteAnchor() interface{} { + orch := h.orchInstance + retcode := 200 + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + Dig := compositeAppValue.DigMap + for digName, digValue := range Dig { + h.ovnURL = "http://" + orch.MiddleendConf.OvnService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + digName + for nwintName, _ := range digValue.NwintMap { + // loop through all app intens in the gpint + url := h.ovnURL + "/network-controller-intent/" + nwintName + fmt.Printf("Delete app nw controller intent %s\n", url) + retcode, err := orch.apiDel(url, compositeAppMetadata.Name+"_delnwctlintent") + if err != nil { + return err + } + if retcode != 204 { + return retcode + } + fmt.Printf("Delete nw controller intent %s\n", retcode) + } + } + } + return retcode +} + +func addNetworkIntent(I orchWorkflow) interface{} { + //1. Add network controller Intent + err := I.createAnchor() + if err != nil { + return err + } + + //2. Add network workload intent + err = I.createObject() + if err != nil { + return err + } + + return nil +} + +func delNwintData(I orchWorkflow) interface{} { + // 1. Create the Anchor point + err := I.deleteObject() + if err != nil { + return err + } + // 2. Create the Objects + err = I.deleteAnchor() + if err != nil { + return err + } + return nil +} diff --git a/src/tools/emcoui/middle_end/app/profile.go b/src/tools/emcoui/middle_end/app/profile.go new file mode 100644 index 00000000..fff43cdc --- /dev/null +++ b/src/tools/emcoui/middle_end/app/profile.go @@ -0,0 +1,261 @@ +/* +======================================================================= +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 ( + "encoding/json" + "fmt" + "log" +) + +// ProfileData captures per app profile +type ProfileData struct { + Name string `json:"profileName"` + AppProfiles map[string]string `json:"appProfile"` +} + +// ProfileMeta is metadta for the profile APIs +type ProfileMeta struct { + Metadata apiMetaData `json:"metadata"` + Spec ProfileSpec `json:"spec"` +} + +// ProfileSpec is the spec for the profile APIs +type ProfileSpec struct { + AppName string `json:"app-name"` +} + +// ProfileHandler This implements the orchworkflow interface +type ProfileHandler struct { + orchURL string + orchInstance *OrchestrationHandler + response struct { + payload map[string][]byte + status map[string]string + } +} + +func (h *ProfileHandler) getObject() (interface{}, interface{}) { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + retcode := 200 + for _, compositeAppValue := range dataRead.compositeAppMap { + var profileList []ProfileMeta + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + "/composite-profiles" + for profileName, profileValue := range compositeAppValue.ProfileDataArray { + url := h.orchURL + "/" + profileName + "/profiles" + retcode, respval, err := orch.apiGet(url, compositeAppMetadata.Name+"_getprofiles") + fmt.Printf("Get app profiles status %d\n", retcode) + if err != nil { + fmt.Printf("Failed to read profile %s\n", profileName) + return nil, 500 + } + if retcode != 200 { + fmt.Printf("Failed to read profile %s\n", profileName) + return nil, retcode + } + json.Unmarshal(respval, &profileList) + profileValue.AppProfiles = make([]ProfileMeta, len(profileList)) + for appProfileIndex, appProfile := range profileList { + profileValue.AppProfiles[appProfileIndex] = appProfile + } + } + } + return nil, retcode +} + +func (h *ProfileHandler) getAnchor() (interface{}, interface{}) { + orch := h.orchInstance + respcode := 200 + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + var profilemetaList []ProfileMeta + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + "/composite-profiles" + + respcode, respdata, err := orch.apiGet(h.orchURL, compositeAppMetadata.Name+"_getcprofile") + if err != nil { + fmt.Printf("Failed to get composite profiles\n") + return nil, 500 + } + if respcode != 200 { + fmt.Printf("composite profile GET status %d\n", respcode) + return nil, respcode + } + json.Unmarshal(respdata, &profilemetaList) + compositeAppValue.ProfileDataArray = make(map[string]*ProfilesData, len(profilemetaList)) + for _, value := range profilemetaList { + ProfilesDataInstance := ProfilesData{} + ProfilesDataInstance.Profile = value + compositeAppValue.ProfileDataArray[value.Metadata.Name] = &ProfilesDataInstance + } + } + return nil, respcode +} + +func (h *ProfileHandler) deleteObject() interface{} { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + "/composite-profiles/" + for profileName, profileValue := range compositeAppValue.ProfileDataArray { + for _, appProfileValue := range profileValue.AppProfiles { + url := h.orchURL + profileName + "/profiles/" + appProfileValue.Metadata.Name + + fmt.Printf("Delete app profiles %s\n", url) + resp, err := orch.apiDel(url, compositeAppMetadata.Name+"_delappProfiles") + if err != nil { + return err + } + if resp != 204 { + return resp + } + fmt.Printf("Delete profiles status %s\n", resp) + } + } + } + return nil +} + +func (h *ProfileHandler) deleteAnchor() interface{} { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + "/composite-profiles/" + + for profileName, _ := range compositeAppValue.ProfileDataArray { + url := h.orchURL + profileName + fmt.Printf("Delete profile %s\n", url) + resp, err := orch.apiDel(url, compositeAppMetadata.Name+"_delProfile") + if err != nil { + return err + } + if resp != 204 { + return resp + } + fmt.Printf("Delete profile status %s\n", resp) + } + } + return nil +} + +func (h *ProfileHandler) createAnchor() interface{} { + orch := h.orchInstance + + profileCreate := ProfileMeta{ + Metadata: apiMetaData{ + Name: orch.compositeAppName + "_profile", + Description: "Profile created from middleend", + UserData1: "data 1", + UserData2: "data2"}, + } + jsonLoad, _ := json.Marshal(profileCreate) + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps" + url := h.orchURL + "/" + orch.compositeAppName + "/" + "v1" + "/composite-profiles" + resp, err := orch.apiPost(jsonLoad, url, orch.compositeAppName+"_profile") + if err != nil { + return err + } + if resp != 201 { + return resp + } + fmt.Printf("ProfileHandler resp %s\n", resp) + + return nil +} + +func (h *ProfileHandler) createObject() interface{} { + orch := h.orchInstance + + for i := range orch.meta { + fileName := orch.meta[i].ProfileMetadata.FileName + appName := orch.meta[i].Metadata.Name + profileName := orch.meta[i].Metadata.Name + "_profile" + + // Upload the application helm chart + fh := orch.file[fileName] + profileAdd := ProfileMeta{ + Metadata: apiMetaData{ + Name: profileName, + Description: "NA", + UserData1: "data 1", + UserData2: "data2"}, + Spec: ProfileSpec{ + AppName: appName}, + } + compositeProfilename := orch.compositeAppName + "_profile" + + url := h.orchURL + "/" + orch.compositeAppName + "/" + "v1" + "/" + + "composite-profiles" + "/" + compositeProfilename + "/profiles" + jsonLoad, _ := json.Marshal(profileAdd) + status, err := orch.apiPostMultipart(jsonLoad, fh, url, profileName, fileName) + if err != nil { + log.Fatalln(err) + } + if status != 201 { + return status + } + fmt.Printf("CompositeProfile Profile %s status %s %s\n", profileName, status, url) + } + + return nil +} + +func createProfile(I orchWorkflow) interface{} { + // 1. Create the Anchor point + err := I.createAnchor() + if err != nil { + return err + } + // 2. Create the Objects + err = I.createObject() + if err != nil { + return err + } + return nil +} + +func delProfileData(I orchWorkflow) interface{} { + // 1. Delete the object + err := I.deleteObject() + if err != nil { + return err + } + // 2. Delete the Anchor + err = I.deleteAnchor() + if err != nil { + return err + } + return nil +} diff --git a/src/tools/emcoui/middle_end/app/projects.go b/src/tools/emcoui/middle_end/app/projects.go new file mode 100644 index 00000000..39fe7573 --- /dev/null +++ b/src/tools/emcoui/middle_end/app/projects.go @@ -0,0 +1,187 @@ +/* +======================================================================= +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 ( + "encoding/json" + "fmt" +) + +// CompositeApp application structure +type ProjectMetadata struct { + Metadata apiMetaData `json:"metadata"` +} + +// CompAppHandler , This implements the orchworkflow interface +type projectHandler struct { + orchURL string + orchInstance *OrchestrationHandler +} + +func (h *projectHandler) getObject() (interface{}, interface{}) { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + var cappList []CompositeApp + if orch.treeFilter != nil { + temp:=CompositeApp{} + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + orch.treeFilter.compositeAppName+"/"+ + orch.treeFilter.compositeAppVersion + respcode, respdata, err := orch.apiGet(h.orchURL, orch.projectName+"_getcapps") + fmt.Printf("Get capp status %s\n", respcode) + if err != nil { + return nil, 500 + } + if respcode != 200 { + return nil, respcode + } + fmt.Printf("Get capp status %s\n", respcode) + json.Unmarshal(respdata, &temp) + cappList = append(cappList, temp) + } else { + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps" + respcode, respdata, err := orch.apiGet(h.orchURL, orch.projectName+"_getcapps") + fmt.Printf("Get capp status %s\n", respcode) + if err != nil { + return nil, 500 + } + if respcode != 200 { + return nil, respcode + } + fmt.Printf("Get capp status %s\n", respcode) + json.Unmarshal(respdata, &cappList) + } + + dataRead.compositeAppMap = make(map[string]*CompositeAppTree, len(cappList)) + for k, value := range cappList { + fmt.Printf("%+v", cappList[k]) + var cappsDataInstance CompositeAppTree + cappName := value.Metadata.Name + cappsDataInstance.Metadata = value + dataRead.compositeAppMap[cappName] = &cappsDataInstance + } + return nil, 200 +} + +func (h *projectHandler) getAnchor() (interface{}, interface{}) { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + + respcode, respdata, err := orch.apiGet(h.orchURL, orch.projectName+"_getProject") + if err != nil { + return nil, 500 + } + if respcode != 200 { + return nil, respcode + } + fmt.Printf("Get project %s\n", respcode) + json.Unmarshal(respdata, &dataRead.Metadata) + return nil, respcode +} + +func (h *projectHandler) deleteObject() interface{} { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + cappList := dataRead.compositeAppMap + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps" + for compositeAppName, compositeAppValue := range cappList { + url := h.orchURL + "/" + compositeAppName + "/" + compositeAppValue.Metadata.Spec.Version + fmt.Printf("Delete composite app %s\n", url) + resp, err := orch.apiDel(url, compositeAppName+"_delcapp") + if err != nil { + return err + } + if resp != 204 { + return resp + } + fmt.Printf("Delete composite app status %s\n", resp) + } + return nil +} + +func (h *projectHandler) deleteAnchor() interface{} { + orch := h.orchInstance + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + orch.projectName + fmt.Printf("Delete Project %s \n", h.orchURL) + resp, err := orch.apiDel(h.orchURL, orch.projectName+"_delProject") + if err != nil { + return err + } + if resp != 204 { + return resp + } + fmt.Printf("Delete Project status %s\n", resp) + return nil +} + +func (h *projectHandler) createAnchor() interface{} { + orch := h.orchInstance + + projectCreate := ProjectMetadata{ + Metadata: apiMetaData{ + Name: orch.projectName, + Description: orch.projectDesc, + UserData1: "data 1", + UserData2: "data 2"}, + } + + jsonLoad, _ := json.Marshal(projectCreate) + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + orch.projectName + resp, err := orch.apiPost(jsonLoad, h.orchURL, orch.projectName) + if err != nil { + return err + } + if resp != 201 { + return resp + } + orch.version = "v1" + fmt.Printf("projectHandler resp %s\n", resp) + + return nil +} + +func (h *projectHandler) createObject() interface{} { + return nil +} + +func createProject(I orchWorkflow) interface{} { + // 1. Create the Anchor point + err := I.createAnchor() + if err != nil { + return err + } + return nil +} + +func delProject(I orchWorkflow) interface{} { + // 1. Delete the object + err := I.deleteObject() + if err != nil { + return err + } + // 2. Delete the Anchor + err = I.deleteAnchor() + if err != nil { + return err + } + return nil +} diff --git a/src/tools/emcoui/middle_end/authproxy/README.md b/src/tools/emcoui/middle_end/authproxy/README.md new file mode 100644 index 00000000..1d68a431 --- /dev/null +++ b/src/tools/emcoui/middle_end/authproxy/README.md @@ -0,0 +1,16 @@ + +Authproxy is part of middleend and it exposes following 3 apis +1. **/v1/login** + - Redirects user to keycloak login page. + - Sets a cookie with original URL +2. **/v1/callback** + - After successful login gets auth code and exchange it for token. + - Set id_token and access_token in cookie and redirects to original URL +3. **/v1/auth** + - Retrieve idtoken from cookie and verifies the JWT. + - If id_token is valid then access to resources else redirects to login page. + +Required inputs of authproxy comes from authproxy section of helm config +- Issuer +- Redirect URI +- Client id diff --git a/src/tools/emcoui/middle_end/authproxy/authproxy.go b/src/tools/emcoui/middle_end/authproxy/authproxy.go new file mode 100644 index 00000000..78819ef6 --- /dev/null +++ b/src/tools/emcoui/middle_end/authproxy/authproxy.go @@ -0,0 +1,281 @@ +/* +======================================================================= +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 authproxy + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "net/url" + "strings" + + "github.com/dgrijalva/jwt-go" +) + +type AuthProxy struct { + AuthProxyConf AuthProxyConfig +} + +// AuthProxyConfig holds inputs of authproxy +type AuthProxyConfig struct { + Issuer string `json:"issuer"` + RedirectURI string `json:"redirect_uri"` + ClientID string `json:"client_id"` +} + +// NewAppHandler interface implementing REST callhandler +func NewAppHandler() *AuthProxy { + return &AuthProxy{} +} + +// OpenIDConfiguration struct to map response from OIDC +type OpenIDConfiguration struct { + Issuer string `json:"issuer"` + AuthzEndpoint string `json:"authorization_endpoint"` + TokenEndPoint string `json:"token_endpoint"` + IntrospectionEndpoint string `json:"introspection_endpoint"` + JWKSURI string `json:"jwks_uri"` +} + +// TokenConfig struct holds tokens +type TokenConfig struct { + ACCESSTOKEN string `json:"access_token"` + IDTOKEN string `json:"id_token"` +} + +// RealmConfig struct holds public_key of issuer +type RealmConfig struct { + PublicKey string `json:"public_key"` +} + +var openIDConfig *OpenIDConfiguration +var realmConfig *RealmConfig + +// Loads the openIDconfig from the OIDC only once +func getOpenIDConfig(issuer string) OpenIDConfiguration { + if openIDConfig != nil { + log.Println("openidconfig is not null and returning the cached value") + return *openIDConfig + } + log.Println("openidconfig is null and loading the values") + url := issuer + ".well-known/openid-configuration" + response, err := http.Get(url) + if err != nil { + log.Printf("The openidconfig HTTP request failed with error %s", err) + return *openIDConfig + } + + defer response.Body.Close() + bodyBytes, _ := ioutil.ReadAll(response.Body) + json.Unmarshal(bodyBytes, &openIDConfig) + return *openIDConfig +} + +// LoginHandler redirects to client login page and sets cookie with the original path +func (h AuthProxy) LoginHandler(w http.ResponseWriter, r *http.Request) { + log.Println("LoginHandler start") + + rd := r.FormValue("rd") + scope := r.FormValue("scope") + + log.Printf("[LoginHandler] url Param 'rd' is: %s, 'scope' is: %s\n", string(rd), string(scope)) + redirect := r.Header.Get("X-Auth-Request-Redirect") + log.Println("redirect url from HEADER is: " + redirect) + if len(redirect) == 0 { + redirect = rd + } + + cookie := http.Cookie{ + Name: "org", + Value: redirect, + Path: "/", + Domain: "", + Secure: false, + HttpOnly: false, + } + // Set cookie with original URL + http.SetCookie(w, &cookie) + state := "1234" // Optional parameter included in all login redirects + if len(scope) == 0 { + // generate token with offline_access scope so that it can be stored in cookie and reused + scope = "openid offline_access" + } + + // get authorization endpoint from function openidconfig + authzEndpoint := getOpenIDConfig(h.AuthProxyConf.Issuer).AuthzEndpoint + + // Construct redirect URL with params + u, _ := url.Parse(authzEndpoint) + q := u.Query() + q.Add("client_id", h.AuthProxyConf.ClientID) + // h.AuthProxyConf.RedirectURI is the callback endpoint of middleend. + // after successful authentication, url will be redirected to this one + q.Add("redirect_uri", h.AuthProxyConf.RedirectURI) + q.Add("response_type", "code") + q.Add("scope", scope) + q.Add("state", state) + u.RawQuery = q.Encode() + + log.Println("[LoginHandler] Redireced URL -> " + u.String()) + http.Redirect(w, r, u.String(), http.StatusFound) +} + +/* + * CallbackHandler reads the OIDC config + * Gets token with API and sets id and access tokens in cookies + * Redirects to original URL + */ +func (h AuthProxy) CallbackHandler(w http.ResponseWriter, r *http.Request) { + state := r.FormValue("state") + code := r.FormValue("code") + tokenEndpoint := getOpenIDConfig(h.AuthProxyConf.Issuer).TokenEndPoint + log.Printf("[CallbackHandler] state: %s , code: %s , tokenEndpoint: %s \n", state, code, tokenEndpoint) + + client := http.Client{} + form := url.Values{} + form.Add("client_id", h.AuthProxyConf.ClientID) + form.Add("client_secret", "") + form.Add("grant_type", "authorization_code") + form.Add("code", code) + form.Add("redirect_uri", h.AuthProxyConf.RedirectURI) + request, err := http.NewRequest("POST", tokenEndpoint, strings.NewReader(form.Encode())) + request.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + resp, err := client.Do(request) + if err != nil { + log.Printf("[CallbackHandler] HTTP request to %s failed\n", tokenEndpoint) + log.Println(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Println("[CallbackHandler] Error while reading response from tokenEndpoint") + log.Println(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + var tokenConfig TokenConfig + json.Unmarshal(body, &tokenConfig) + log.Printf("[CallbackHandler] access_token: %s \n id_token: %s\n", tokenConfig.ACCESSTOKEN, tokenConfig.IDTOKEN) + + // Construct the original URL with cookie org + var orginalURL string + cookie, err := r.Cookie("org") + if err == nil { + orginalURL = cookie.Value + } + fmt.Println("[CallbackHandler] orginalURL from cookie: " + orginalURL) + + // Create cookies with id_token, access_token + idTokenCookie := http.Cookie{ + Name: "idtoken", + Value: tokenConfig.IDTOKEN, + Path: "/", + Domain: "", + Secure: false, + HttpOnly: false, + } + accessTokencookie := http.Cookie{ + Name: "accesstoken", + Value: tokenConfig.ACCESSTOKEN, + Path: "/", + Domain: "", + Secure: false, + HttpOnly: false, + } + + http.SetCookie(w, &idTokenCookie) + http.SetCookie(w, &accessTokencookie) + + // Finally return the original URL with the cookies + http.Redirect(w, r, orginalURL, http.StatusFound) +} + +// AuthHandler verifies the token and returns response +func (h AuthProxy) AuthHandler(w http.ResponseWriter, r *http.Request) { + log.Println("[AuthHandler] Authenticating the token") + + var idToken string + cookie, err := r.Cookie("idtoken") + if err == nil { + cookieVal := cookie.Value + idToken = cookieVal + } + + if idToken == "" { + log.Println("[AuthHandler] id token is nil ") + w.WriteHeader(http.StatusUnauthorized) + return + } + error := validateToken(h.AuthProxyConf.Issuer, idToken) + if error != nil { + log.Println("[AuthHandler] Issue with token and returning failed response") + w.WriteHeader(http.StatusUnauthorized) + } +} + +/* +* Validates JWT token +* verifies signature, token expiry and invalid check... etc + */ +func validateToken(issuer string, reqToken string) error { + log.Printf("[AuthHandler] Validating JWT token: \n%s\n", reqToken) + + //load realm public key only once + if realmConfig == nil { + log.Println("[AuthHandler] realmconfig is null and loading the value") + response, err := http.Get(issuer) + if err != nil { + log.Printf("[AuthHandler] Error while retreiving issuer details : %s\n", err) + return err + } + defer response.Body.Close() + bodyBytes, _ := ioutil.ReadAll(response.Body) + json.Unmarshal(bodyBytes, &realmConfig) + } + SecretKey := "-----BEGIN CERTIFICATE-----\n" + realmConfig.PublicKey + "\n-----END CERTIFICATE-----" + key, er := jwt.ParseRSAPublicKeyFromPEM([]byte(SecretKey)) + if er != nil { + log.Println("[AuthHandler] Error occured while parsing public key") + log.Println(er) + return er + } + + token, err := jwt.Parse(reqToken, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok { + return nil, fmt.Errorf("[AuthHandler] Unexpected signing method: %v", token.Header["alg"]) + } + return key, nil + }) + + if err != nil { + log.Println("[AuthHandler] Error while parsing token") + log.Println(err) + return err + } + if _, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { + log.Println("[AuthHandler] Token is valid") + } + return nil +} diff --git a/src/tools/emcoui/middle_end/db/dbconnection.go b/src/tools/emcoui/middle_end/db/dbconnection.go new file mode 100644 index 00000000..5496c39c --- /dev/null +++ b/src/tools/emcoui/middle_end/db/dbconnection.go @@ -0,0 +1,145 @@ +/* +======================================================================= +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 db + +import ( + "encoding/json" + "fmt" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "golang.org/x/net/context" +) + +// MongoStore is the interface which implements the db.Store interface +type MongoStore struct { + db *mongo.Database +} + +// Key interface +type Key interface { +} + +// DBconn variable of type Store +var DBconn Store + +// Store Interface which implements the data store functions +type Store interface { + HealthCheck() error + Find(coll string, key []byte, tag string) ([][]byte, error) + Unmarshal(inp []byte, out interface{}) error +} + +// NewMongoStore Return mongo client +func NewMongoStore(name string, store *mongo.Database, svcEp string) (Store, error) { + if store == nil { + ip := "mongodb://" + svcEp + clientOptions := options.Client() + clientOptions.ApplyURI(ip) + mongoClient, err := mongo.NewClient(clientOptions) + if err != nil { + return nil, err + } + + err = mongoClient.Connect(context.Background()) + if err != nil { + return nil, err + } + store = mongoClient.Database(name) + } + return &MongoStore{ + db: store, + }, nil +} + +// CreateDBClient creates the DB client. currently only mongo +func CreateDBClient(dbType string, dbName string, svcEp string) error { + var err error + switch dbType { + case "mongo": + DBconn, err = NewMongoStore(dbName, nil, svcEp) + default: + fmt.Println(dbType + "DB not supported") + } + return err +} + +// HealthCheck verifies the database connection +func (m *MongoStore) HealthCheck() error { + _, err := (*mongo.SingleResult).DecodeBytes(m.db.RunCommand(context.Background(), bson.D{{"serverStatus", 1}})) + if err != nil { + fmt.Println("Error getting DB server status: err %s", err) + } + return nil +} + +func (m *MongoStore) Unmarshal(inp []byte, out interface{}) error { + err := bson.Unmarshal(inp, out) + if err != nil { + fmt.Printf("Failed to unmarshall bson") + return err + } + return nil +} + +// Find a document +func (m *MongoStore) Find(coll string, key []byte, tag string) ([][]byte, error) { + var bsonMap bson.M + err := json.Unmarshal([]byte(key), &bsonMap) + if err != nil { + fmt.Println("Failed to unmarshall %s\n", key) + return nil, err + } + + filter := bson.M{ + "$and": []bson.M{bsonMap}, + } + + fmt.Printf("%+v %s\n", filter, tag) + projection := bson.D{ + {tag, 1}, + {"_id", 0}, + } + + c := m.db.Collection(coll) + + cursor, err := c.Find(context.Background(), filter, options.Find().SetProjection(projection)) + if err != nil { + fmt.Println("Failed to find the document %s\n", err) + return nil, err + } + + defer cursor.Close(context.Background()) + var data []byte + var result [][]byte + for cursor.Next(context.Background()) { + d := cursor.Current + switch d.Lookup(tag).Type { + case bson.TypeString: + data = []byte(d.Lookup(tag).StringValue()) + default: + r, err := d.LookupErr(tag) + if err != nil { + fmt.Println("Unable to read data %s %s\n", string(r.Value), err) + } + data = r.Value + } + result = append(result, data) + } + return result, nil +} diff --git a/src/tools/emcoui/middle_end/go.mod b/src/tools/emcoui/middle_end/go.mod new file mode 100644 index 00000000..3d195979 --- /dev/null +++ b/src/tools/emcoui/middle_end/go.mod @@ -0,0 +1,14 @@ +module example.com/middleend + +go 1.14 + +require ( + github.com/dgrijalva/jwt-go v3.2.0+incompatible + github.com/gorilla/handlers v1.5.0 + github.com/gorilla/mux v1.8.0 + github.com/lestrrat-go/jwx v1.0.5 + go.mongodb.org/mongo-driver v1.4.1 + golang.org/x/net v0.0.0-20200707034311-ab3426394381 + k8s.io/apimachinery v0.19.3 + k8s.io/client-go v0.19.3 +) diff --git a/src/tools/emcoui/middle_end/go.sum b/src/tools/emcoui/middle_end/go.sum new file mode 100644 index 00000000..d8e5a43f --- /dev/null +++ b/src/tools/emcoui/middle_end/go.sum @@ -0,0 +1,428 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/aws/aws-sdk-go v1.29.15 h1:0ms/213murpsujhsnxnNKNeVouW60aJqSd992Ks3mxs= +github.com/aws/aws-sdk-go v1.29.15/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v1.0.2 h1:KPldsxuKGsS2FPWsNeg9ZO18aCrGKujPoWXn2yo+KQM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= +github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY= +github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I= +github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/gorilla/handlers v1.5.0 h1:4wjo3sf9azi99c8hTmyaxp9y5S+pFszsy3pP0rAw/lw= +github.com/gorilla/handlers v1.5.0/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M= +github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lestrrat-go/iter v0.0.0-20200422075355-fc1769541911 h1:FvnrqecqX4zT0wOIbYK1gNgTm0677INEWiFY8UEYggY= +github.com/lestrrat-go/iter v0.0.0-20200422075355-fc1769541911/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc= +github.com/lestrrat-go/jwx v1.0.5 h1:8bVUGXXkR3+YQNwuFof3lLxSJMLtrscHJfGI6ZIBRD0= +github.com/lestrrat-go/jwx v1.0.5/go.mod h1:TPF17WiSFegZo+c20fdpw49QD+/7n4/IsGvEmCSWwT0= +github.com/lestrrat-go/pdebug v0.0.0-20200204225717-4d6bd78da58d/go.mod h1:B06CSso/AWxiPejj+fheUINGeBKeeEZNt8w+EoU7+L8= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc h1:n+nNi93yXLkJvKwXNP9d55HC7lGK4H/SRcwB5IaUZLo= +github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.mongodb.org/mongo-driver v1.4.1 h1:38NSAyDPagwnFpUA/D5SFgbugUYR3NzYRNa4Qk9UxKs= +go.mongodb.org/mongo-driver v1.4.1/go.mod h1:llVBH2pkj9HywK0Dtdt6lDikOjFLbceHVu/Rc0iMKLs= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 h1:8dUaAV7K4uHsF56JQWkprecIQKdPHtR9jCHF5nB8uzc= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 h1:pE8b58s1HRDMi8RDc79m0HISf9D4TzseP40cEA6IGfs= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 h1:5/PjkGUjvEU5Gl6BxmvKRPpqo2uNMv4rcHBMwzk/st8= +golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200417140056-c07e33ef3290/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +k8s.io/api v0.19.3 h1:GN6ntFnv44Vptj/b+OnMW7FmzkpDoIDLZRvKX3XH9aU= +k8s.io/api v0.19.3/go.mod h1:VF+5FT1B74Pw3KxMdKyinLo+zynBaMBiAfGMuldcNDs= +k8s.io/apimachinery v0.19.3 h1:bpIQXlKjB4cB/oNpnNnV+BybGPR7iP5oYpsOTEJ4hgc= +k8s.io/apimachinery v0.19.3/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= +k8s.io/client-go v0.19.3 h1:ctqR1nQ52NUs6LpI0w+a5U+xjYwflFwA13OJKcicMxg= +k8s.io/client-go v0.19.3/go.mod h1:+eEMktZM+MG0KO+PTkci8xnbCZHvj9TqR6Q1XDUIJOM= +k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.2.0 h1:XRvcwJozkgZ1UQJmfMGpvRthQHOvihEhYtDfAaxMz/A= +k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= +k8s.io/utils v0.0.0-20200729134348-d5654de09c73 h1:uJmqzgNWG7XyClnU/mLPBWwfKKF1K8Hf8whTseBgJcg= +k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +sigs.k8s.io/structured-merge-diff/v4 v4.0.1 h1:YXTMot5Qz/X1iBRJhAt+vI+HVttY0WkSqqhKxQ0xVbA= +sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/src/tools/emcoui/middle_end/main/main.go b/src/tools/emcoui/middle_end/main/main.go new file mode 100644 index 00000000..97a31017 --- /dev/null +++ b/src/tools/emcoui/middle_end/main/main.go @@ -0,0 +1,112 @@ +/* +======================================================================= +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 main + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + "os/signal" + "time" + + "example.com/middleend/app" + "example.com/middleend/authproxy" + "example.com/middleend/db" + "github.com/gorilla/handlers" + "github.com/gorilla/mux" +) + +/* This is the main package of the middleend. This package + * implements the http server which exposes service ar 9891. + * It also intialises an API router which handles the APIs with + * subpath /v1. + */ +func main() { + depHandler := app.NewAppHandler() + authProxyHandler := authproxy.NewAppHandler() + configFile, err := os.Open("/opt/emco/config/middleend.conf") + if err != nil { + fmt.Printf("Failed to read middleend configuration") + return + } + defer configFile.Close() + + // Read the configuration json + byteValue, _ := ioutil.ReadAll(configFile) + json.Unmarshal(byteValue, &depHandler.MiddleendConf) + json.Unmarshal(byteValue, &authProxyHandler.AuthProxyConf) + + // Connect to the DB + err = db.CreateDBClient("mongo", "mco", depHandler.MiddleendConf.Mongo) + if err != nil { + fmt.Println("Failed to connect to DB") + return + } + // Get an instance of the OrchestrationHandler, this type implements + // the APIs i.e CreateApp, ShowApp, DeleteApp. + httpRouter := mux.NewRouter().PathPrefix("/middleend").Subrouter() + loggedRouter := handlers.LoggingHandler(os.Stdout, httpRouter) + log.Println("Starting middle end service") + + httpServer := &http.Server{ + Handler: loggedRouter, + Addr: ":" + depHandler.MiddleendConf.OwnPort, + WriteTimeout: 15 * time.Second, + ReadTimeout: 15 * time.Second, + } + httpRouter.HandleFunc("/healthcheck", depHandler.GetHealth).Methods("GET") + + // POST, GET, DELETE composite apps + httpRouter.HandleFunc("/projects/{project-name}/composite-apps", depHandler.CreateApp).Methods("POST") + //httpRouter.HandleFunc("/projects/{project-name}/composite-apps", depHandler.GetAllCaps).Methods("GET") + httpRouter.HandleFunc("/projects/{project-name}/composite-apps/{composite-app-name}/{version}", + depHandler.GetSvc).Methods("GET") + httpRouter.HandleFunc("/projects/{project-name}/composite-apps/{composite-app-name}/{version}", + depHandler.DelSvc).Methods("DELETE") + // POST, GET, DELETE deployment intent groups + httpRouter.HandleFunc("/projects/{project-name}/composite-apps/{composite-app-name}/{version}/deployment-intent-groups", + depHandler.CreateDig).Methods("POST") + httpRouter.HandleFunc("/projects/{project-name}/deployment-intent-groups", depHandler.GetAllDigs).Methods("GET") + httpRouter.HandleFunc("/projects/{project-name}/composite-apps/{composite-app-name}/{version}/deployment-intent-groups/{deployment-intent-group-name}", + depHandler.DelDig).Methods("DELETE") + + // Authproxy relates APIs + httpRouter.HandleFunc("/login", authProxyHandler.LoginHandler).Methods("GET") + httpRouter.HandleFunc("/callback", authProxyHandler.CallbackHandler).Methods("GET") + httpRouter.HandleFunc("/auth", authProxyHandler.AuthHandler).Methods("GET") + // Cluster createion API + httpRouter.HandleFunc("/clusterproviders/{cluster-provider-name}/clusters", depHandler.CheckConnection).Methods("POST") + + // Start server in a go routine. + go func() { + log.Fatal(httpServer.ListenAndServe()) + }() + + // Gracefull shutdown of the server, + // create a channel and wait for SIGINT + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + log.Println("wait for signal") + <-c + log.Println("Bye Bye") + httpServer.Shutdown(context.Background()) +} -- cgit 1.2.3-korg