aboutsummaryrefslogtreecommitdiffstats
path: root/src/tools/emcoui/middle_end
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/emcoui/middle_end')
-rw-r--r--src/tools/emcoui/middle_end/Dockerfile32
-rw-r--r--src/tools/emcoui/middle_end/Makefile24
-rw-r--r--src/tools/emcoui/middle_end/app/app.go1014
-rw-r--r--src/tools/emcoui/middle_end/app/compositeapp.go247
-rw-r--r--src/tools/emcoui/middle_end/app/digp.go322
-rw-r--r--src/tools/emcoui/middle_end/app/intents.go664
-rw-r--r--src/tools/emcoui/middle_end/app/profile.go261
-rw-r--r--src/tools/emcoui/middle_end/app/projects.go187
-rw-r--r--src/tools/emcoui/middle_end/authproxy/README.md16
-rw-r--r--src/tools/emcoui/middle_end/authproxy/authproxy.go281
-rw-r--r--src/tools/emcoui/middle_end/db/dbconnection.go145
-rw-r--r--src/tools/emcoui/middle_end/go.mod14
-rw-r--r--src/tools/emcoui/middle_end/go.sum428
-rw-r--r--src/tools/emcoui/middle_end/main/main.go112
14 files changed, 3747 insertions, 0 deletions
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/<composite app>/<version>
+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/<composite app>/<version>
+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())
+}