diff options
author | Ritu Sood <ritu.sood@intel.com> | 2020-08-25 22:01:22 -0700 |
---|---|---|
committer | Ritu Sood <ritu.sood@intel.com> | 2020-08-25 22:14:34 -0700 |
commit | 16ca82713faf6678d4b7055130768541f86ea20c (patch) | |
tree | bc8a457b5a545c79aff23e95e515f09c91dca3b6 /src/tools/emcoctl/cmd/utils.go | |
parent | 706d0990fc2210041f467934f3ec72c9b5a06ff4 (diff) |
CLI code for EMCO
Add cli emcoctl as a client
utility for EMCO
Issue-ID: MULTICLOUD-1065
Signed-off-by: Ritu Sood <ritu.sood@intel.com>
Change-Id: Ie1951910628469b5a7e75550b9daa34ba377d1a4
Diffstat (limited to 'src/tools/emcoctl/cmd/utils.go')
-rw-r--r-- | src/tools/emcoctl/cmd/utils.go | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/src/tools/emcoctl/cmd/utils.go b/src/tools/emcoctl/cmd/utils.go new file mode 100644 index 00000000..34063eee --- /dev/null +++ b/src/tools/emcoctl/cmd/utils.go @@ -0,0 +1,305 @@ +/*Copyright © 2020 Intel Corp + +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 cmd + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + neturl "net/url" + "os" + "strings" + + "github.com/go-resty/resty/v2" + "github.com/mitchellh/mapstructure" + "gopkg.in/yaml.v3" + pkgerrors "github.com/pkg/errors" +) + +var inputFiles []string +var valuesFiles []string + +type ResourceContext struct { + Anchor string `json:"anchor" yaml:"anchor"` +} + +type Metadata struct { + Name string `yaml:"name" json:"name"` + Description string `yaml:"description,omitempty" json:"description,omitempty"` + UserData1 string `yaml:"userData1,omitempty" json:"userData1,omitempty"` + UserData2 string `yaml:"userData2,omitempty" json:"userData2,omitempty"` +} + +type emcoRes struct { + Version string `yaml:"version" json:"version"` + Context ResourceContext `yaml:"resourceContext" json:"resourceContext"` + Meta Metadata `yaml:"metadata" json:"metadata"` + Spec map[string]interface{} `yaml:"spec,omitempty" json:"spec,omitempty"` + File string `yaml:"file,omitempty" json:"file,omitempty"` + Label string `yaml:"label-name,omitempty" json:"label-name,omitempty"` +} + +type emcoBody struct { + Meta Metadata `json:"metadata,omitempty"` + Label string `json:"label-name,omitempty"` + Spec map[string]interface{} `json:"spec,omitempty"` +} + +type emcoCompositeAppSpec struct { + Version string `json: "version"` +} + +type Resources struct { + anchor string + body []byte + file string +} +// RestyClient to use with CLI +type RestyClient struct { + client *resty.Client +} + +var Client RestyClient + +// NewRestClient returns a rest client +func NewRestClient() RestyClient { + // Create a Resty Client + Client.client = resty.New() + // Bearer Auth Token for all request + // Client.client.SetAuthToken() + // Registering global Error object structure for JSON/XML request + //Client.client.SetError(&Error{}) + return Client +} + +// readResources reads all the resources in the file provided +func readResources() []Resources { + // TODO: Remove Assumption only one file + // Open file and Parse to get all resources + var resources []Resources + f, err := os.Open(inputFiles[0]) + defer f.Close() + if err != nil { + fmt.Printf("Error %s reading file %s\n", err, inputFiles[0]) + return []Resources{} + } + if len(valuesFiles) > 0 { + //Apply template + } + + dec := yaml.NewDecoder(f) + // Iterate through all resources in the file + for { + var doc emcoRes + if dec.Decode(&doc) != nil { + break + } + body := &emcoBody{Meta: doc.Meta, Spec: doc.Spec, Label: doc.Label} + jsonBody, err := json.Marshal(body) + if err != nil { + return []Resources{} + } + var res Resources + if doc.File != "" { + res = Resources{anchor: doc.Context.Anchor, body: jsonBody, file: doc.File} + } else { + res = Resources{anchor: doc.Context.Anchor, body: jsonBody} + } + resources = append(resources, res) + } + return resources +} + +//RestClientPost to post to server no multipart +func (r RestyClient) RestClientPost(anchor string, body []byte) error { + + url, err := GetURL(anchor) + if err != nil { + return err + } + + // POST JSON string + resp, err := r.client.R(). + SetHeader("Content-Type", "application/json"). + SetBody(body). + Post(url) + if err != nil { + fmt.Println(err) + return err + } + fmt.Println("URL:", anchor, "Response Code:", resp.StatusCode(), "Response:", resp) + if (resp.StatusCode() >= 201 && resp.StatusCode() <= 299) { + return nil + } + return pkgerrors.Errorf("Server Post Error") +} + +//RestClientMultipartPost to post to server with multipart +func (r RestyClient) RestClientMultipartPost(anchor string, body []byte, file string) error { + url, err := GetURL(anchor) + if err != nil { + return err + } + + // Read file for multipart + f, err := ioutil.ReadFile(file) + if err != nil { + fmt.Printf("Error %s reading file %s\n", err, file) + return err + } + + // Multipart Post + formParams := neturl.Values{} + formParams.Add("metadata", string(body)) + resp, err := r.client.R(). + SetFileReader("file", "filename", bytes.NewReader(f)). + SetFormDataFromValues(formParams). + Post(url) + + if err != nil { + fmt.Println(err) + return err + } + fmt.Println("URL:", anchor, "Response Code:", resp.StatusCode(), "Response:", resp) + if (resp.StatusCode() >= 201 && resp.StatusCode() <= 299) { + return nil + } + return pkgerrors.Errorf("Server Multipart Post Error") +} +// RestClientGetAll returns all resource in the input file +func (r RestyClient) RestClientGetAll(anchor string) error { + url, err := GetURL(anchor) + if err != nil { + return err + } + resp, err := r.client.R(). + Get(url) + if err != nil { + fmt.Println(err) + return err + } + fmt.Println("URL:", anchor, "Response Code:", resp.StatusCode(), "Response:", resp) + return nil +} +// RestClientGet gets resource +func (r RestyClient) RestClientGet(anchor string) error { + s := strings.Split(anchor, "/") + a := s[len(s)-2] + // Determine if multipart + if a == "apps" || a == "profiles" || a == "clusters" { + url, err := GetURL(anchor) + if err != nil { + return err + } + // Supports only getting metadata + resp, err := r.client.R(). + SetHeader("Accept", "application/json"). + Get(url) + if err != nil { + fmt.Println(err) + return err + } + fmt.Println("URL:", anchor, "Response Code:", resp.StatusCode(), "Response:", resp) + } else { + r.RestClientGetAll(anchor) + } + + return nil +} +// RestClientDelete calls rest delete command +func (r RestyClient) RestClientDelete(anchor string, body []byte) error { + var url string + + s := strings.Split(anchor, "/") + a := s[len(s)-1] + if a == "instantiate" { + // Change instantiate to destroy + s[len(s)-1] = "terminate" + anchor = strings.Join(s[:], "/") + fmt.Println("URL:", anchor) + return r.RestClientPost(anchor, []byte{}) + } else if a == "apply" { + // Change apply to terminate + s[len(s)-1] = "terminate" + anchor = strings.Join(s[:], "/") + fmt.Println("URL:", anchor) + return r.RestClientPost(anchor, []byte{}) + } else if a == "approve" || a == "status" { + // Approve and status doesn't have delete + return nil + } + var e emcoBody + err := json.Unmarshal(body, &e) + if err != nil { + fmt.Println(err) + return err + } + if e.Meta.Name != "" { + s := strings.Split(anchor, "/") + a := s[len(s)-1] + name := e.Meta.Name + anchor = anchor + "/" + name + if a == "composite-apps" { + var cav emcoCompositeAppSpec + err := mapstructure.Decode(e.Spec, &cav) + if err != nil { + fmt.Println("mapstruct error") + return err + } + anchor = anchor + "/" + cav.Version + } + } + url, err = GetURL(anchor) + if err != nil { + return err + } + resp, err := r.client.R(). + Delete(url) + if err != nil { + fmt.Println(err) + return err + } + fmt.Println("URL:", anchor, "Response Code:", resp.StatusCode()) + return nil +} +// GetURL reads the configuration file to get URL +func GetURL(anchor string) (string, error) { + var baseUrl string + s := strings.Split(anchor, "/") + if len(s) < 1 { + return "", fmt.Errorf("Invalid Anchor") + } + + switch s[0] { + case "cluster-providers": + if len(s) >= 5 && (s[4] == "networks" || s[4] == "provider-networks" || s[4] == "apply" || s[4] == "terminate") { + baseUrl = GetNcmURL() + } else { + baseUrl = GetClmURL() + } + case "controllers": + baseUrl = GetOrchestratorURL() + case "projects": + if len(s) >= 6 && s[5] == "network-controller-intent" { + baseUrl = GetOvnactionURL() + } else { + baseUrl = GetOrchestratorURL() + } + default: + return "", fmt.Errorf("Invalid Anchor") + } + return (baseUrl + "/" + anchor), nil +} |