From 0476915e02b44588cd58ca29bd298ecd8c1847a5 Mon Sep 17 00:00:00 2001 From: Jiatong Wang Date: Wed, 6 Jun 2018 14:54:11 -0700 Subject: [PATCH 1/8] categories cmd full version --- govc/main.go | 2 + govc/tags/categories/categories_create.go | 67 ++++++ govc/tags/categories/categories_delete.go | 57 +++++ govc/tags/categories/categories_get.go | 57 +++++ govc/tags/categories/categories_ls.go | 109 +++++++++ govc/tags/categories/categories_update.go | 84 +++++++ govc/tags/tags_helper/categories.go | 202 ++++++++++++++++ govc/tags/tags_helper/rest_client.go | 272 ++++++++++++++++++++++ 8 files changed, 850 insertions(+) create mode 100755 govc/tags/categories/categories_create.go create mode 100644 govc/tags/categories/categories_delete.go create mode 100755 govc/tags/categories/categories_get.go create mode 100644 govc/tags/categories/categories_ls.go create mode 100644 govc/tags/categories/categories_update.go create mode 100644 govc/tags/tags_helper/categories.go create mode 100644 govc/tags/tags_helper/rest_client.go diff --git a/govc/main.go b/govc/main.go index f389c0fe0..533349835 100644 --- a/govc/main.go +++ b/govc/main.go @@ -20,6 +20,8 @@ import ( "os" "github.com/vmware/govmomi/govc/cli" + _ "github.com/vmware/govmomi/govc/tags/categories" + _ "github.com/vmware/govmomi/govc/tags/tags_helper" _ "github.com/vmware/govmomi/govc/about" _ "github.com/vmware/govmomi/govc/cluster" diff --git a/govc/tags/categories/categories_create.go b/govc/tags/categories/categories_create.go new file mode 100755 index 000000000..78eca7b6d --- /dev/null +++ b/govc/tags/categories/categories_create.go @@ -0,0 +1,67 @@ +package tags + +import ( + "context" + "flag" + "fmt" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/govc/tags/tags_helper" +) + +type create struct { + *flags.ClientFlag +} + +func init() { + cli.Register("tags.categories.create", &create{}) +} + +func (cmd *create) Register(ctx context.Context, f *flag.FlagSet) { + cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) + cmd.ClientFlag.Register(ctx, f) +} + +func (cmd *create) Process(ctx context.Context) error { + if err := cmd.ClientFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *create) Usage() string { + return ` + Examples: + govc tags.categories.create 'name' 'description' 'categoryType' 'multiValue(true/false)'` +} + +func (cmd *create) Run(ctx context.Context, f *flag.FlagSet) error { + if f.NArg() != 4 { + return flag.ErrHelp + } + + name := f.Arg(0) + description := f.Arg(1) + categoryType := f.Arg(2) + multiValue := f.Arg(3) + value := false + if multiValue == "true" { + value = true + } + + return withClient(ctx, cmd.ClientFlag, func(c *tags.RestClient) error { + + id, err := c.CreateCategoryIfNotExist(ctx, name, description, categoryType, value) + if err != nil { + + fmt.Printf(err.Error()) + return err + } + + fmt.Printf("Create category success id %s\n", *id) + + return nil + + }) +} diff --git a/govc/tags/categories/categories_delete.go b/govc/tags/categories/categories_delete.go new file mode 100644 index 000000000..817e62457 --- /dev/null +++ b/govc/tags/categories/categories_delete.go @@ -0,0 +1,57 @@ +package tags + +import ( + "context" + "flag" + "fmt" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/govc/tags/tags_helper" +) + +type delete struct { + *flags.ClientFlag +} + +func init() { + cli.Register("tags.categories.delete", &delete{}) +} + +func (cmd *delete) Register(ctx context.Context, f *flag.FlagSet) { + cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) + cmd.ClientFlag.Register(ctx, f) +} + +func (cmd *delete) Process(ctx context.Context) error { + if err := cmd.ClientFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *delete) Usage() string { + return ` + Examples: + govc tags.categories.delete 'id'` +} + +func (cmd *delete) Run(ctx context.Context, f *flag.FlagSet) error { + if f.NArg() != 1 { + return flag.ErrHelp + } + + categoryID := f.Arg(0) + + return withClient(ctx, cmd.ClientFlag, func(c *tags.RestClient) error { + + err := c.DeleteCategory(ctx, categoryID) + if err != nil { + + fmt.Printf(err.Error()) + return err + } + return nil + + }) +} diff --git a/govc/tags/categories/categories_get.go b/govc/tags/categories/categories_get.go new file mode 100755 index 000000000..b30772f49 --- /dev/null +++ b/govc/tags/categories/categories_get.go @@ -0,0 +1,57 @@ +package tags + +import ( + "context" + "flag" + "fmt" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/govc/tags/tags_helper" +) + +type getbyname struct { + *flags.ClientFlag +} + +func init() { + cli.Register("tags.categories.getbyname", &getbyname{}) +} + +func (cmd *getbyname) Register(ctx context.Context, f *flag.FlagSet) { + cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) + cmd.ClientFlag.Register(ctx, f) +} + +func (cmd *getbyname) Process(ctx context.Context) error { + if err := cmd.ClientFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *getbyname) Usage() string { + return ` + Examples: + govc tags.categories.getbyname 'name'` +} + +func (cmd *getbyname) Run(ctx context.Context, f *flag.FlagSet) error { + if f.NArg() != 1 { + return flag.ErrHelp + } + + name := f.Arg(0) + + return withClient(ctx, cmd.ClientFlag, func(c *tags.RestClient) error { + + categories, err := c.GetCategoriesByName(context.Background(), name) + if err != nil { + fmt.Printf(err.Error()) + return nil + } + fmt.Println(categories) + return nil + + }) +} diff --git a/govc/tags/categories/categories_ls.go b/govc/tags/categories/categories_ls.go new file mode 100644 index 000000000..559c64d2d --- /dev/null +++ b/govc/tags/categories/categories_ls.go @@ -0,0 +1,109 @@ +package tags + +import ( + "context" + "flag" + "fmt" + "net/url" + "os" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/govc/tags/tags_helper" + "github.com/vmware/govmomi/sts" + "github.com/vmware/govmomi/vim25/soap" +) + +type ls struct { + *flags.ClientFlag +} + +func init() { + cli.Register("tags.categories.ls", &ls{}) +} + +func (cmd *ls) Register(ctx context.Context, f *flag.FlagSet) { + cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) + cmd.ClientFlag.Register(ctx, f) +} + +func (cmd *ls) Process(ctx context.Context) error { + if err := cmd.ClientFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *ls) Usage() string { + return ` + Examples: + govc tags.categories.ls` +} + +func withClient(ctx context.Context, cmd *flags.ClientFlag, f func(*tags.RestClient) error) error { + vc, err := cmd.Client() + if err != nil { + return err + } + + govcUrl := "https://" + os.Getenv("GOVC_URL") + + URL, err := url.Parse(govcUrl) + if err != nil { + return err + } + + c := tags.NewClient(URL, true, "") + if err != nil { + return err + } + + // SSO admin server has its own session manager, so the govc persisted session cookies cannot + // be used to authenticate. There is no SSO token persistence in govc yet, so just use an env + // var for now. If no GOVC_LOGIN_TOKEN is set, issue a new token. + token := os.Getenv("GOVC_LOGIN_TOKEN") + header := soap.Header{ + Security: &sts.Signer{ + Certificate: vc.Certificate(), + Token: token, + }, + } + + if token == "" { + tokens, cerr := sts.NewClient(ctx, vc) + if cerr != nil { + return cerr + } + + req := sts.TokenRequest{ + Certificate: vc.Certificate(), + Userinfo: cmd.Userinfo(), + } + + header.Security, cerr = tokens.Issue(ctx, req) + if cerr != nil { + return cerr + } + } + + if err = c.Login(context.TODO()); err != nil { + return err + } + + return f(c) +} + +func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error { + + return withClient(ctx, cmd.ClientFlag, func(c *tags.RestClient) error { + categories, err := c.ListCategories(ctx) + if err != nil { + fmt.Printf(err.Error()) + return err + } + + fmt.Println(categories) + return nil + + }) +} diff --git a/govc/tags/categories/categories_update.go b/govc/tags/categories/categories_update.go new file mode 100644 index 000000000..3bc290581 --- /dev/null +++ b/govc/tags/categories/categories_update.go @@ -0,0 +1,84 @@ +package tags + +import ( + "context" + "flag" + "fmt" + "strings" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/govc/tags/tags_helper" +) + +type update struct { + *flags.ClientFlag +} + +func init() { + cli.Register("tags.categories.update", &update{}) +} + +func (cmd *update) Register(ctx context.Context, f *flag.FlagSet) { + cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) + cmd.ClientFlag.Register(ctx, f) +} + +func (cmd *update) Process(ctx context.Context) error { + if err := cmd.ClientFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *update) Usage() string { + return ` + Examples: + govc tags.categories.create 'name' 'description' 'categoryType' 'multiValue(true/false)'` +} + +func (cmd *update) Run(ctx context.Context, f *flag.FlagSet) error { + if f.NArg() != 5 { + return flag.ErrHelp + } + id := f.Arg(0) + + name := f.Arg(1) + description := f.Arg(2) + categoryType := f.Arg(3) + multiValue := f.Arg(4) + + category := new(tags.CategoryUpdateSpec) + + if categoryType == "" { + category.UpdateSpec = tags.CategoryUpdate{ + // AssociableTypes: categoryType, + Cardinality: multiValue, + Description: description, + Name: name, + } + + } else { + types := strings.Split(categoryType, ",") + category.UpdateSpec = tags.CategoryUpdate{ + AssociableTypes: types, + Cardinality: multiValue, + Description: description, + Name: name, + } + + } + + return withClient(ctx, cmd.ClientFlag, func(c *tags.RestClient) error { + + err := c.UpdateCategory(ctx, id, category) + + if err != nil { + + fmt.Printf(err.Error()) + return err + } + return nil + + }) +} diff --git a/govc/tags/tags_helper/categories.go b/govc/tags/tags_helper/categories.go new file mode 100644 index 000000000..6c42feb12 --- /dev/null +++ b/govc/tags/tags_helper/categories.go @@ -0,0 +1,202 @@ +// Copyright 2017 VMware, 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 tags + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "strings" +) + +const ( + CategoryURL = "/com/vmware/cis/tagging/category" + ErrAlreadyExists = "already_exists" +) + +type CategoryCreateSpec struct { + CreateSpec CategoryCreate `json:"create_spec"` +} + +type CategoryUpdateSpec struct { + UpdateSpec CategoryUpdate `json:"update_spec"` +} + +type CategoryCreate struct { + AssociableTypes []string `json:"associable_types"` + Cardinality string `json:"cardinality"` + Description string `json:"description"` + Name string `json:"name"` +} + +type CategoryUpdate struct { + AssociableTypes []string `json:"associable_types"` + Cardinality string `json:"cardinality"` + Description string `json:"description"` + Name string `json:"name"` +} + +type Category struct { + ID string `json:"id"` + Description string `json:"description"` + Name string `json:"name"` + Cardinality string `json:"cardinality"` + AssociableTypes []string `json:"associable_types"` + UsedBy []string `json:"used_by"` +} + +func (c *RestClient) CreateCategoryIfNotExist(ctx context.Context, name string, description string, categoryType string, multiValue bool) (*string, error) { + categories, err := c.GetCategoriesByName(ctx, name) + if err != nil { + return nil, err + } + + if categories == nil { + var multiValueStr string + if multiValue { + multiValueStr = "MULTIPLE" + } else { + multiValueStr = "SINGLE" + } + categoryCreate := CategoryCreate{[]string{categoryType}, multiValueStr, description, name} + spec := CategoryCreateSpec{categoryCreate} + id, err := c.CreateCategory(ctx, &spec) + if err != nil { + // in case there are two docker daemon try to create inventory category, query the category once again + if strings.Contains(err.Error(), "ErrAlreadyExists") { + if categories, err = c.GetCategoriesByName(ctx, name); err != nil { + fmt.Printf("Failed to get inventory category for %s", err) + return nil, err + } + } else { + fmt.Printf("Failed to create inventory category for %s", err) + return nil, err + } + } else { + return id, nil + } + } + if categories != nil { + return &categories[0].ID, nil + } + // should not happen + fmt.Printf("Failed to create inventory for it's existed, but could not query back. Please check system") + return nil, err +} + +func (c *RestClient) CreateCategory(ctx context.Context, spec *CategoryCreateSpec) (*string, error) { + stream, _, status, err := c.call(ctx, "POST", CategoryURL, spec, nil) + + if status != http.StatusOK || err != nil { + fmt.Printf("Create category failed with status code: %d, error message: %s", status, err) + return nil, err + } + + type RespValue struct { + Value string + } + + var pID RespValue + if err := json.NewDecoder(stream).Decode(&pID); err != nil { + fmt.Printf("Decode response body failed for: %s", err) + return nil, err + } + return &(pID.Value), nil +} + +func (c *RestClient) GetCategory(ctx context.Context, id string) (*Category, error) { + + stream, _, status, err := c.call(ctx, "GET", fmt.Sprintf("%s/id:%s", CategoryURL, id), nil, nil) + + if status != http.StatusOK || err != nil { + fmt.Printf("Get category failed with status code: %s, error message: %s", status, err) + return nil, err + } + + type RespValue struct { + Value Category + } + + var pCategory RespValue + if err := json.NewDecoder(stream).Decode(&pCategory); err != nil { + fmt.Printf("Decode response body failed for: %s", err) + return nil, err + } + return &(pCategory.Value), nil +} + +func (c *RestClient) UpdateCategory(ctx context.Context, id string, spec *CategoryUpdateSpec) error { + _, _, status, err := c.call(ctx, "PATCH", fmt.Sprintf("%s/id:%s", CategoryURL, id), spec, nil) + + if status != http.StatusOK || err != nil { + fmt.Printf("Update category failed with status code: %d, error message: %s", status, err) + return err + } + + return nil +} + +func (c *RestClient) DeleteCategory(ctx context.Context, id string) error { + + _, _, status, err := c.call(ctx, "DELETE", fmt.Sprintf("%s/id:%s", CategoryURL, id), nil, nil) + + if status != http.StatusOK || err != nil { + fmt.Printf("Delete category failed with status code: %s, error message: %s", status, err) + return err + } + return nil +} + +func (c *RestClient) ListCategories(ctx context.Context) ([]string, error) { + + stream, _, status, err := c.call(ctx, "GET", CategoryURL, nil, nil) + + if status != http.StatusOK || err != nil { + fmt.Printf("Get categories failed with status code: %s, error message: %s", status, err) + return nil, err + } + + type Categories struct { + Value []string + } + + var pCategories Categories + if err := json.NewDecoder(stream).Decode(&pCategories); err != nil { + fmt.Printf("Decode response body failed for: %s", err) + return nil, err + } + return pCategories.Value, nil +} + +func (c *RestClient) GetCategoriesByName(ctx context.Context, name string) ([]Category, error) { + categoryIds, err := c.ListCategories(ctx) + if err != nil { + fmt.Printf("Get category failed for: %s", err) + return nil, err + } + + var categories []Category + for _, cID := range categoryIds { + category, err := c.GetCategory(ctx, cID) + if err != nil { + fmt.Printf("Get category %s failed for %s", cID, err) + } + if category.Name == name { + categories = append(categories, *category) + } + } + return categories, nil +} diff --git a/govc/tags/tags_helper/rest_client.go b/govc/tags/tags_helper/rest_client.go new file mode 100644 index 000000000..f8b5e82b5 --- /dev/null +++ b/govc/tags/tags_helper/rest_client.go @@ -0,0 +1,272 @@ +// Copyright 2017 VMware, 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 tags + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strings" + "sync" + + "github.com/pkg/errors" + "github.com/vmware/govmomi/vim25/soap" +) + +const ( + RestPrefix = "/rest" + loginURL = "/com/vmware/cis/session" + sessionIDCookieName = "vmware-api-session-id" +) + +type RestClient struct { + mu sync.Mutex + host string + scheme string + endpoint *url.URL + user *url.Userinfo + HTTP *http.Client + cookies []*http.Cookie +} + +func NewClient(u *url.URL, insecure bool, thumbprint string) *RestClient { + endpoint := &url.URL{} + *endpoint = *u + endpoint.Path = RestPrefix + + sc := soap.NewClient(endpoint, insecure) + if thumbprint != "" { + sc.SetThumbprint(endpoint.Host, thumbprint) + } + + user := endpoint.User + endpoint.User = nil + + return &RestClient{ + endpoint: endpoint, + user: user, + host: endpoint.Host, + scheme: endpoint.Scheme, + HTTP: &sc.Client, + } +} + +// NewClientWithSessionID creates a new REST client with a supplied session ID +// to re-connect to existing sessions. +// +// Note that the session is not checked for validity - to check for a valid +// session after creating the client, use the Valid method. If the session is +// no longer valid and the session needs to be re-saved, Login should be called +// again before calling SessionID to extract the new session ID. Clients +// created with this function function work in the exact same way as clients +// created with NewClient, including supporting re-login on invalid sessions on +// all SDK calls. +func NewClientWithSessionID(u *url.URL, insecure bool, thumbprint string, sessionID string) *RestClient { + c := NewClient(u, insecure, thumbprint) + c.SetSessionID(sessionID) + + return c +} + +func (c *RestClient) encodeData(data interface{}) (*bytes.Buffer, error) { + params := bytes.NewBuffer(nil) + if data != nil { + if err := json.NewEncoder(params).Encode(data); err != nil { + return nil, err + } + } + return params, nil +} + +func (c *RestClient) call(ctx context.Context, method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, http.Header, int, error) { + // Logger.Debugf("%s: %s, headers: %+v", method, path, headers) + params, err := c.encodeData(data) + if err != nil { + return nil, nil, -1, errors.Wrap(err, "call failed") + } + + if data != nil { + if headers == nil { + headers = make(map[string][]string) + } + headers["Content-Type"] = []string{"application/json"} + } + + body, hdr, statusCode, err := c.clientRequest(ctx, method, path, params, headers) + if statusCode == http.StatusUnauthorized && strings.Contains(err.Error(), "This method requires authentication") { + c.Login(ctx) + return c.clientRequest(ctx, method, path, params, headers) + } + + return body, hdr, statusCode, err +} + +func (c *RestClient) clientRequest(ctx context.Context, method, path string, in io.Reader, headers map[string][]string) (io.ReadCloser, http.Header, int, error) { + expectedPayload := (method == "POST" || method == "PUT") + if expectedPayload && in == nil { + in = bytes.NewReader([]byte{}) + } + + req, err := c.newRequest(method, path, in) + if err != nil { + return nil, nil, -1, errors.Wrap(err, "failed to create request") + } + + req = req.WithContext(ctx) + c.mu.Lock() + if c.cookies != nil { + req.AddCookie(c.cookies[0]) + } + c.mu.Unlock() + + if headers != nil { + for k, v := range headers { + req.Header[k] = v + } + } + + if expectedPayload && req.Header.Get("Content-Type") == "" { + req.Header.Set("Content-Type", "application/json") + } + req.Header.Set("Accept", "application/json") + + resp, err := c.HTTP.Do(req) + return c.handleResponse(resp, err) +} + +func (c *RestClient) handleResponse(resp *http.Response, err error) (io.ReadCloser, http.Header, int, error) { + statusCode := -1 + if resp != nil { + statusCode = resp.StatusCode + } + if err != nil { + if strings.Contains(err.Error(), "connection refused") { + return nil, nil, statusCode, errors.Errorf("Cannot connect to endpoint %s. Is vCloud Suite API running on this server?", c.host) + } + return nil, nil, statusCode, errors.Wrap(err, "error occurred trying to connect") + } + + if statusCode < http.StatusOK || statusCode >= http.StatusBadRequest { + body, err := ioutil.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { + return nil, nil, statusCode, errors.Wrap(err, "error reading response") + } + if len(body) == 0 { + return nil, nil, statusCode, errors.Errorf("Error: request returned %s", http.StatusText(statusCode)) + } + fmt.Printf("Error response: %s", bytes.TrimSpace(body)) + return nil, nil, statusCode, err + } + + return resp.Body, resp.Header, statusCode, nil +} + +func (c *RestClient) Login(ctx context.Context) error { + c.mu.Lock() + defer c.mu.Unlock() + + request, err := c.newRequest("POST", loginURL, nil) + if err != nil { + return err + } + if c.user != nil { + password, _ := c.user.Password() + request.SetBasicAuth(c.user.Username(), password) + } + resp, err := c.HTTP.Do(request) + if err != nil { + return err + } + if resp == nil { + return err + } + if resp.StatusCode != http.StatusOK { + // #nosec: Errors unhandled. + // body, _ := ioutil.ReadAll(resp.Body) + resp.Body.Close() + return err + } + + c.cookies = resp.Cookies() + + return nil +} + +func (c *RestClient) newRequest(method, urlStr string, body io.Reader) (*http.Request, error) { + return http.NewRequest(method, c.endpoint.String()+urlStr, body) +} + +// SessionID returns the current session ID of the REST client. An empty string +// means there was no session cookie currently loaded. +func (c *RestClient) SessionID() string { + for _, cookie := range c.cookies { + if cookie.Name == sessionIDCookieName { + return cookie.Value + } + } + return "" +} + +// SetSessionID sets the session cookie with the supplied session ID. +// +// This does not necessarily mean the session is valid. The session should be +// checked with Valid before proceeding, and logged back in if it has expired. +// +// This function will overwrite any existing session. +func (c *RestClient) SetSessionID(sessionID string) { + idx := -1 + for i, cookie := range c.cookies { + if cookie.Name == sessionIDCookieName { + idx = i + } + } + sessionCookie := &http.Cookie{ + Name: sessionIDCookieName, + Value: sessionID, + Path: RestPrefix, + } + if idx > -1 { + c.cookies[idx] = sessionCookie + } else { + c.cookies = append(c.cookies, sessionCookie) + } +} + +// Valid checks to see if the session cookies in a REST client are still valid. +// This should be used when restoring a session to determine if a new login is +// necessary. +func (c *RestClient) Valid(ctx context.Context) bool { + sessionID := c.SessionID() + fmt.Printf("Checking if session ID %q is still valid", sessionID) + + _, _, statusCode, err := c.clientRequest(ctx, "POST", loginURL+"?~action=get", nil, nil) + if err != nil { + fmt.Printf("Error getting current session information for ID %q - session is invalid (%d - %s)", sessionID, statusCode, err) + } + + if statusCode == http.StatusOK { + fmt.Printf("Session ID %q is valid", sessionID) + return true + } + + fmt.Printf("Session is invalid for %v (%d)", sessionID, statusCode) + return false +} From f4f8ae69233ddc923053121347b89b03445fce4a Mon Sep 17 00:00:00 2001 From: Jiatong Wang Date: Wed, 6 Jun 2018 15:07:06 -0700 Subject: [PATCH 2/8] remove errors pkg --- govc/tags/tags_helper/rest_client.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/govc/tags/tags_helper/rest_client.go b/govc/tags/tags_helper/rest_client.go index f8b5e82b5..efd2fdea8 100644 --- a/govc/tags/tags_helper/rest_client.go +++ b/govc/tags/tags_helper/rest_client.go @@ -26,7 +26,6 @@ import ( "strings" "sync" - "github.com/pkg/errors" "github.com/vmware/govmomi/vim25/soap" ) @@ -99,7 +98,7 @@ func (c *RestClient) call(ctx context.Context, method, path string, data interfa // Logger.Debugf("%s: %s, headers: %+v", method, path, headers) params, err := c.encodeData(data) if err != nil { - return nil, nil, -1, errors.Wrap(err, "call failed") + return nil, nil, -1, err } if data != nil { @@ -126,7 +125,7 @@ func (c *RestClient) clientRequest(ctx context.Context, method, path string, in req, err := c.newRequest(method, path, in) if err != nil { - return nil, nil, -1, errors.Wrap(err, "failed to create request") + return nil, nil, -1, err } req = req.WithContext(ctx) @@ -158,19 +157,19 @@ func (c *RestClient) handleResponse(resp *http.Response, err error) (io.ReadClos } if err != nil { if strings.Contains(err.Error(), "connection refused") { - return nil, nil, statusCode, errors.Errorf("Cannot connect to endpoint %s. Is vCloud Suite API running on this server?", c.host) + return nil, nil, statusCode, err } - return nil, nil, statusCode, errors.Wrap(err, "error occurred trying to connect") + return nil, nil, statusCode, err } if statusCode < http.StatusOK || statusCode >= http.StatusBadRequest { body, err := ioutil.ReadAll(resp.Body) resp.Body.Close() if err != nil { - return nil, nil, statusCode, errors.Wrap(err, "error reading response") + return nil, nil, statusCode, err } if len(body) == 0 { - return nil, nil, statusCode, errors.Errorf("Error: request returned %s", http.StatusText(statusCode)) + return nil, nil, statusCode, err } fmt.Printf("Error response: %s", bytes.TrimSpace(body)) return nil, nil, statusCode, err From 6b1ea2bdbb05a8ef8dfc447e797ef28cf9826d84 Mon Sep 17 00:00:00 2001 From: Jiatong Wang Date: Wed, 6 Jun 2018 15:13:21 -0700 Subject: [PATCH 3/8] fixed printf bugs --- govc/tags/tags_helper/categories.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/govc/tags/tags_helper/categories.go b/govc/tags/tags_helper/categories.go index 6c42feb12..650e89905 100644 --- a/govc/tags/tags_helper/categories.go +++ b/govc/tags/tags_helper/categories.go @@ -122,7 +122,7 @@ func (c *RestClient) GetCategory(ctx context.Context, id string) (*Category, err stream, _, status, err := c.call(ctx, "GET", fmt.Sprintf("%s/id:%s", CategoryURL, id), nil, nil) if status != http.StatusOK || err != nil { - fmt.Printf("Get category failed with status code: %s, error message: %s", status, err) + fmt.Printf("Get category failed with status code: %d, error message: %s", status, err) return nil, err } @@ -154,7 +154,7 @@ func (c *RestClient) DeleteCategory(ctx context.Context, id string) error { _, _, status, err := c.call(ctx, "DELETE", fmt.Sprintf("%s/id:%s", CategoryURL, id), nil, nil) if status != http.StatusOK || err != nil { - fmt.Printf("Delete category failed with status code: %s, error message: %s", status, err) + fmt.Printf("Delete category failed with status code: %d, error message: %s", status, err) return err } return nil @@ -165,7 +165,7 @@ func (c *RestClient) ListCategories(ctx context.Context) ([]string, error) { stream, _, status, err := c.call(ctx, "GET", CategoryURL, nil, nil) if status != http.StatusOK || err != nil { - fmt.Printf("Get categories failed with status code: %s, error message: %s", status, err) + fmt.Printf("Get categories failed with status code: %d, error message: %s", status, err) return nil, err } From 4d8b7995e5ba8ffcd3a4cf3c4f8b4668d52cfd10 Mon Sep 17 00:00:00 2001 From: Jiatong Wang Date: Fri, 8 Jun 2018 16:18:28 -0700 Subject: [PATCH 4/8] tags category apis --- govc/main.go | 3 +- govc/tags/.DS_Store | Bin 0 -> 6148 bytes govc/tags/categories/categories_create.go | 67 ------------- govc/tags/categories/categories_delete.go | 57 ----------- govc/tags/categories/categories_get.go | 57 ----------- govc/tags/categories/categories_update.go | 84 ---------------- govc/tags/categories/create.go | 81 +++++++++++++++ govc/tags/categories/get.go | 85 ++++++++++++++++ .../categories/{categories_ls.go => ls.go} | 54 +++++++--- govc/tags/categories/rm.go | 66 +++++++++++++ govc/tags/categories/update.go | 93 ++++++++++++++++++ vapi/.DS_Store | Bin 0 -> 6148 bytes vapi/tags/.DS_Store | Bin 0 -> 6148 bytes .../tags_helper => vapi/tags}/categories.go | 59 ++++++----- .../tags_helper => vapi/tags}/rest_client.go | 12 +-- 15 files changed, 400 insertions(+), 318 deletions(-) create mode 100644 govc/tags/.DS_Store delete mode 100755 govc/tags/categories/categories_create.go delete mode 100644 govc/tags/categories/categories_delete.go delete mode 100755 govc/tags/categories/categories_get.go delete mode 100644 govc/tags/categories/categories_update.go create mode 100755 govc/tags/categories/create.go create mode 100755 govc/tags/categories/get.go rename govc/tags/categories/{categories_ls.go => ls.go} (56%) create mode 100644 govc/tags/categories/rm.go create mode 100644 govc/tags/categories/update.go create mode 100644 vapi/.DS_Store create mode 100644 vapi/tags/.DS_Store rename {govc/tags/tags_helper => vapi/tags}/categories.go (76%) rename {govc/tags/tags_helper => vapi/tags}/rest_client.go (94%) diff --git a/govc/main.go b/govc/main.go index 533349835..b104cab90 100644 --- a/govc/main.go +++ b/govc/main.go @@ -20,8 +20,6 @@ import ( "os" "github.com/vmware/govmomi/govc/cli" - _ "github.com/vmware/govmomi/govc/tags/categories" - _ "github.com/vmware/govmomi/govc/tags/tags_helper" _ "github.com/vmware/govmomi/govc/about" _ "github.com/vmware/govmomi/govc/cluster" @@ -74,6 +72,7 @@ import ( _ "github.com/vmware/govmomi/govc/session" _ "github.com/vmware/govmomi/govc/sso/service" _ "github.com/vmware/govmomi/govc/sso/user" + _ "github.com/vmware/govmomi/govc/tags/categories" _ "github.com/vmware/govmomi/govc/task" _ "github.com/vmware/govmomi/govc/vapp" _ "github.com/vmware/govmomi/govc/version" diff --git a/govc/tags/.DS_Store b/govc/tags/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..73d7d2ddca4b214da891552adf118bbdc5559afb GIT binary patch literal 6148 zcmeHK%}N6?5T4YkyNcL@pvPRi_0U>>55iLG!JDw62bFf$7G0257rEBhMyMm~YB z<4lqkOY2DxDKjwnCi9b!FH5ok08#BW%K!xca8L z$wW3g{v!jlcS~>s0i?KV-}W!;B@ba+g&6+`AKFopmP(%@HV=JdzkEEvpuX z@Q%jR#XkC-xUJ$_j1OyvHH^#vGr$bYAOrTObCzdtZM-CAfEoB519Uz}R6@^UW>6m; z*wF7I#q)$DXwzGQP+IgXW(F~WB1|cwDHZmKAxt^?rOop!W(G|;2t6~7V`mojg(CFq z=$ASjglCXjW`G$OXJF2B3v~Y<|NQOlyx9=r(=J*cz^Eo~qsrAaMXEBOumBfr4k zab|a0ELHF%VrO9X&Cbs3vM*t0modiOVY|wh%@{L55epVHUkHw)E=WOp5IN2fjM9D( zB?0u`7NW`V7a72BXRwU(=WfBQNu_Ve0yW zYwVr-SufD`MUq4_+Yc_o&Zuc`og{JQhjC{hlf!lgLT;|YxSfpLWEi(oIgh6XV`)pi1TXiNRGm_=S#hH0BCbI^%L>m`AV7^$Uf|)xj@hIOC2& zYKZ}2V4i`Z>e^WUPrrZv&nHoj7$64z6$9L8cnue}q-*QK=CIZZ&=x2P#^nlUDPYJ_ g46#^>OQ2G~FVFyVH0BDy140)8Ndq;+z^^j!0if4VxBvhE literal 0 HcmV?d00001 diff --git a/vapi/tags/.DS_Store b/vapi/tags/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 Date: Fri, 8 Jun 2018 21:28:46 -0700 Subject: [PATCH 5/8] delete useless file --- govc/tags/.DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 govc/tags/.DS_Store diff --git a/govc/tags/.DS_Store b/govc/tags/.DS_Store deleted file mode 100644 index 73d7d2ddca4b214da891552adf118bbdc5559afb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%}N6?5T4YkyNcL@pvPRi_0U>>55iLG!JDw62bFf$7G0257rEBhMyMm~YB z<4lqkOY2DxDKjwnCi9b!FH5ok08#BW%K!xca8L z$wW3g{v!jlcS~>s0i?KV-}W!;B@ba+g&6+`AKFopmP(%@HV=JdzkEEvpuX z@Q%jR#XkC-xUJ$_j1OyvHH^#vGr$bYAOrTObCzdtZM-CAfEoB519Uz}R6@^UW>6m; z*wF7I#q)$DXwzGQP+IgXW(F~WB1|cwDHZmKAxt^?rOop!W(G|;2t6~7V`mojg(CFq z=$ASjglCXjW`G$OXJF2B3v~Y<|NQ Date: Tue, 12 Jun 2018 14:21:08 -0700 Subject: [PATCH 6/8] Add Description() method and and rename category package and files Rename categories folder to category, as well as the package name. Add Description() and Usage() methods. Add Logout() in withClient() method to release resource. Format outputs. --- govc/tags/category/create.go | 86 +++++++++++++++++++++++++++++ govc/tags/category/get.go | 90 ++++++++++++++++++++++++++++++ govc/tags/category/ls.go | 104 +++++++++++++++++++++++++++++++++++ govc/tags/category/rm.go | 68 +++++++++++++++++++++++ govc/tags/category/update.go | 97 ++++++++++++++++++++++++++++++++ 5 files changed, 445 insertions(+) create mode 100755 govc/tags/category/create.go create mode 100755 govc/tags/category/get.go create mode 100644 govc/tags/category/ls.go create mode 100644 govc/tags/category/rm.go create mode 100644 govc/tags/category/update.go diff --git a/govc/tags/category/create.go b/govc/tags/category/create.go new file mode 100755 index 000000000..f44c4479f --- /dev/null +++ b/govc/tags/category/create.go @@ -0,0 +1,86 @@ +/* +Copyright (c) 2018 VMware, 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 category + +import ( + "context" + "flag" + "fmt" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/vapi/tags" +) + +type create struct { + *flags.ClientFlag + description string + types string + multi bool +} + +func init() { + cli.Register("tags.category.create", &create{}) +} + +func (cmd *create) Register(ctx context.Context, f *flag.FlagSet) { + cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) + cmd.ClientFlag.Register(ctx, f) + f.StringVar(&cmd.description, "d", "", "Description of category") + f.StringVar(&cmd.types, "t", "", "Associable_types of category") + f.BoolVar(&cmd.multi, "m", false, "Cardinality of category") +} + +func (cmd *create) Process(ctx context.Context) error { + if err := cmd.ClientFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *create) Usage() string { + return "NAME" +} + +func (cmd *create) Description() string { + return ` Create tag category. + +This command will output the ID you just created. + +Examples: + govc tags.category.create -d "description" -t "categoryType" -m(if set, the cardinality will be true) NAME` +} + +func (cmd *create) Run(ctx context.Context, f *flag.FlagSet) error { + if f.NArg() != 1 { + return flag.ErrHelp + } + + name := f.Arg(0) + + return withClient(ctx, cmd.ClientFlag, func(c *tags.RestClient) error { + + id, err := c.CreateCategoryIfNotExist(ctx, name, cmd.description, cmd.types, cmd.multi) + if err != nil { + return err + } + + fmt.Println(*id) + return nil + }) + +} diff --git a/govc/tags/category/get.go b/govc/tags/category/get.go new file mode 100755 index 000000000..5822f8def --- /dev/null +++ b/govc/tags/category/get.go @@ -0,0 +1,90 @@ +/* +Copyright (c) 2018 VMware, 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 category + +import ( + "context" + "flag" + "fmt" + "io" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/vapi/tags" +) + +type get struct { + *flags.ClientFlag + *flags.OutputFlag +} + +type getName []tags.Category + +func init() { + cli.Register("tags.category.get", &get{}) +} + +func (cmd *get) Register(ctx context.Context, f *flag.FlagSet) { + cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) + cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx) + cmd.ClientFlag.Register(ctx, f) + cmd.OutputFlag.Register(ctx, f) +} + +func (cmd *get) Process(ctx context.Context) error { + if err := cmd.ClientFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *get) Usage() string { + return "NAME" +} + +func (cmd *get) Description() string { + return `Get category by name. + +Will return empty if category name doesn't exist. + +Examples: + govc tags.category.get NAME` +} + +func (r getName) Write(w io.Writer) error { + for i := range r { + fmt.Fprintln(w, r[i]) + } + return nil +} + +func (cmd *get) Run(ctx context.Context, f *flag.FlagSet) error { + if f.NArg() != 1 { + return flag.ErrHelp + } + name := f.Arg(0) + + return withClient(ctx, cmd.ClientFlag, func(c *tags.RestClient) error { + categoriesName, err := c.GetCategoriesByName(ctx, name) + if err != nil { + return err + } + result := getName(categoriesName) + cmd.WriteResult(result) + return nil + }) +} diff --git a/govc/tags/category/ls.go b/govc/tags/category/ls.go new file mode 100644 index 000000000..21fc89fb3 --- /dev/null +++ b/govc/tags/category/ls.go @@ -0,0 +1,104 @@ +/* +Copyright (c) 2018 VMware, 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 category + +import ( + "context" + "flag" + "fmt" + "io" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/vapi/tags" +) + +type ls struct { + *flags.ClientFlag + *flags.OutputFlag +} + +func init() { + cli.Register("tags.category.ls", &ls{}) +} + +func (cmd *ls) Register(ctx context.Context, f *flag.FlagSet) { + cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) + cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx) + cmd.ClientFlag.Register(ctx, f) + cmd.OutputFlag.Register(ctx, f) +} + +func (cmd *ls) Process(ctx context.Context) error { + if err := cmd.ClientFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *ls) Description() string { + return `List all categories. + +Examples: + govc tags.category.ls` +} + +func withClient(ctx context.Context, cmd *flags.ClientFlag, f func(*tags.RestClient) error) error { + vc, err := cmd.Client() + if err != nil { + return err + } + tagsURL := vc.URL() + tagsURL.User = cmd.Userinfo() + + c := tags.NewClient(tagsURL, !cmd.IsSecure(), "") + if err != nil { + return err + } + + if err = c.Login(ctx); err != nil { + return err + } + defer c.Logout(ctx) + + return f(c) +} + +type getResult []string + +func (r getResult) Write(w io.Writer) error { + for i := range r { + fmt.Fprintln(w, r[i]) + } + return nil +} + +func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error { + + return withClient(ctx, cmd.ClientFlag, func(c *tags.RestClient) error { + categories, err := c.ListCategories(ctx) + if err != nil { + return err + } + + result := getResult(categories) + cmd.WriteResult(result) + return nil + + }) + +} diff --git a/govc/tags/category/rm.go b/govc/tags/category/rm.go new file mode 100644 index 000000000..889b7f83b --- /dev/null +++ b/govc/tags/category/rm.go @@ -0,0 +1,68 @@ +/* +Copyright (c) 2018 VMware, 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 category + +import ( + "context" + "flag" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/vapi/tags" +) + +type rm struct { + *flags.ClientFlag +} + +func init() { + cli.Register("tags.category.rm", &rm{}) +} + +func (cmd *rm) Register(ctx context.Context, f *flag.FlagSet) { + cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) + cmd.ClientFlag.Register(ctx, f) +} + +func (cmd *rm) Process(ctx context.Context) error { + if err := cmd.ClientFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *rm) Usage() string { + return "ID" +} + +func (cmd *rm) Description() string { + return `Delete category. + +Examples: + govc tags.category.rm ID` +} + +func (cmd *rm) Run(ctx context.Context, f *flag.FlagSet) error { + if f.NArg() != 1 { + return flag.ErrHelp + } + categoryID := f.Arg(0) + + return withClient(ctx, cmd.ClientFlag, func(c *tags.RestClient) error { + return c.DeleteCategory(ctx, categoryID) + }) +} diff --git a/govc/tags/category/update.go b/govc/tags/category/update.go new file mode 100644 index 000000000..97f066fd0 --- /dev/null +++ b/govc/tags/category/update.go @@ -0,0 +1,97 @@ +/* +Copyright (c) 2018 VMware, 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 category + +import ( + "context" + "flag" + "strings" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/vapi/tags" +) + +type update struct { + *flags.ClientFlag + name string + description string + types string + multi string +} + +func init() { + cli.Register("tags.category.update", &update{}) +} + +func (cmd *update) Register(ctx context.Context, f *flag.FlagSet) { + cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) + cmd.ClientFlag.Register(ctx, f) + f.StringVar(&cmd.name, "n", "", "Name of category") + f.StringVar(&cmd.description, "d", "", "Description of category") + f.StringVar(&cmd.types, "t", "", "Associable_types of category") + f.StringVar(&cmd.multi, "m", "", "Cardinality of category") +} + +func (cmd *update) Process(ctx context.Context) error { + if err := cmd.ClientFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *update) Usage() string { + return "ID" +} + +func (cmd *update) Description() string { + return `Update category. + +Cardinality can be either "SINGLE" or "MULTIPLE." + +Examples: + govc tags.category.update -n "name" -d "description" -t "associable_types" -m "cardinality" ID` +} + +func (cmd *update) Run(ctx context.Context, f *flag.FlagSet) error { + if f.NArg() != 1 { + return flag.ErrHelp + } + id := f.Arg(0) + + return withClient(ctx, cmd.ClientFlag, func(c *tags.RestClient) error { + + category := new(tags.CategoryUpdateSpec) + categoryTemp := new(tags.CategoryUpdate) + if cmd.name != "" { + categoryTemp.Name = cmd.name + } + if cmd.description != "" { + categoryTemp.Description = cmd.description + } + if cmd.types != "" { + typesField := strings.Split(cmd.types, ",") + categoryTemp.AssociableTypes = typesField + } + if cmd.multi != "" { + categoryTemp.Cardinality = cmd.multi + } + + category.UpdateSpec = *categoryTemp + return c.UpdateCategory(ctx, id, category) + }) +} From 44ba72ecdf33224363c5bdb2d032a9c9bd99e3ed Mon Sep 17 00:00:00 2001 From: Jiatong Wang Date: Tue, 12 Jun 2018 14:31:15 -0700 Subject: [PATCH 7/8] Add Logout() method to RestCilent type as a receiver function --- govc/main.go | 2 +- govc/tags/categories/create.go | 81 ------------------- govc/tags/categories/get.go | 85 -------------------- govc/tags/categories/ls.go | 139 --------------------------------- govc/tags/categories/rm.go | 66 ---------------- govc/tags/categories/update.go | 93 ---------------------- vapi/tags/rest_client.go | 13 ++- 7 files changed, 10 insertions(+), 469 deletions(-) delete mode 100755 govc/tags/categories/create.go delete mode 100755 govc/tags/categories/get.go delete mode 100644 govc/tags/categories/ls.go delete mode 100644 govc/tags/categories/rm.go delete mode 100644 govc/tags/categories/update.go diff --git a/govc/main.go b/govc/main.go index b104cab90..3d0e5878c 100644 --- a/govc/main.go +++ b/govc/main.go @@ -72,7 +72,7 @@ import ( _ "github.com/vmware/govmomi/govc/session" _ "github.com/vmware/govmomi/govc/sso/service" _ "github.com/vmware/govmomi/govc/sso/user" - _ "github.com/vmware/govmomi/govc/tags/categories" + _ "github.com/vmware/govmomi/govc/tags/category" _ "github.com/vmware/govmomi/govc/task" _ "github.com/vmware/govmomi/govc/vapp" _ "github.com/vmware/govmomi/govc/version" diff --git a/govc/tags/categories/create.go b/govc/tags/categories/create.go deleted file mode 100755 index c90bc69fd..000000000 --- a/govc/tags/categories/create.go +++ /dev/null @@ -1,81 +0,0 @@ -/* -Copyright (c) 2018 VMware, 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 tags - -import ( - "context" - "flag" - "fmt" - - "github.com/vmware/govmomi/govc/cli" - "github.com/vmware/govmomi/govc/flags" - "github.com/vmware/govmomi/vapi/tags" -) - -type create struct { - *flags.ClientFlag - description string - types string - multi bool -} - -func init() { - cli.Register("tags.category.create", &create{}) -} - -func (cmd *create) Register(ctx context.Context, f *flag.FlagSet) { - cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) - cmd.ClientFlag.Register(ctx, f) - f.StringVar(&cmd.description, "d", "", "description of category") - f.StringVar(&cmd.types, "t", "", "associable_types of category") - f.BoolVar(&cmd.multi, "m", false, "cardinality of category") -} - -func (cmd *create) Process(ctx context.Context) error { - if err := cmd.ClientFlag.Process(ctx); err != nil { - return err - } - return nil -} - -func (cmd *create) Usage() string { - return ` Create tag category. This command will output the ID you just created. - -Examples: - govc tags.category.create -d "description" -t "categoryType" -m(optional, if set, the cardinality will be true) NAME` -} - -func (cmd *create) Run(ctx context.Context, f *flag.FlagSet) error { - if f.NArg() != 1 { - return flag.ErrHelp - } - - name := f.Arg(0) - - return withClient(ctx, cmd.ClientFlag, func(c *tags.RestClient) error { - - id, err := c.CreateCategoryIfNotExist(ctx, name, cmd.description, cmd.types, cmd.multi) - if err != nil { - return err - } - - fmt.Println(*id) - - return nil - - }) -} diff --git a/govc/tags/categories/get.go b/govc/tags/categories/get.go deleted file mode 100755 index 482e45bc3..000000000 --- a/govc/tags/categories/get.go +++ /dev/null @@ -1,85 +0,0 @@ -/* -Copyright (c) 2018 VMware, 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 tags - -import ( - "context" - "flag" - "fmt" - "io" - - "github.com/vmware/govmomi/govc/cli" - "github.com/vmware/govmomi/govc/flags" - "github.com/vmware/govmomi/vapi/tags" -) - -type get struct { - *flags.ClientFlag - *flags.OutputFlag -} - -func init() { - cli.Register("tags.category.get", &get{}) -} - -func (cmd *get) Register(ctx context.Context, f *flag.FlagSet) { - cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) - cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx) - cmd.ClientFlag.Register(ctx, f) - cmd.OutputFlag.Register(ctx, f) -} - -func (cmd *get) Process(ctx context.Context) error { - if err := cmd.ClientFlag.Process(ctx); err != nil { - return err - } - return nil -} - -func (cmd *get) Usage() string { - return `Get tags by tags' name. Will return empty if category name not exist. -Examples: - govc tags.category.get NAME` -} - -type getName []tags.Category - -func (r getName) Write(w io.Writer) error { - for i := range r { - fmt.Fprintln(w, r[i]) - } - return nil -} - -func (cmd *get) Run(ctx context.Context, f *flag.FlagSet) error { - if f.NArg() != 1 { - return flag.ErrHelp - } - - name := f.Arg(0) - - return withClient(ctx, cmd.ClientFlag, func(c *tags.RestClient) error { - categoriesName, err := c.GetCategoriesByName(context.Background(), name) - if err != nil { - return err - } - result := getName(categoriesName) - cmd.WriteResult(result) - return nil - - }) -} diff --git a/govc/tags/categories/ls.go b/govc/tags/categories/ls.go deleted file mode 100644 index f6c6270a8..000000000 --- a/govc/tags/categories/ls.go +++ /dev/null @@ -1,139 +0,0 @@ -/* -Copyright (c) 2018 VMware, 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 tags - -import ( - "context" - "flag" - "fmt" - "io" - "net/url" - "os" - - "github.com/vmware/govmomi/govc/cli" - "github.com/vmware/govmomi/govc/flags" - "github.com/vmware/govmomi/sts" - "github.com/vmware/govmomi/vapi/tags" - "github.com/vmware/govmomi/vim25/soap" -) - -type ls struct { - *flags.ClientFlag - *flags.OutputFlag -} - -func init() { - cli.Register("tags.category.ls", &ls{}) -} - -func (cmd *ls) Register(ctx context.Context, f *flag.FlagSet) { - cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) - cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx) - cmd.ClientFlag.Register(ctx, f) - cmd.OutputFlag.Register(ctx, f) -} - -func (cmd *ls) Process(ctx context.Context) error { - if err := cmd.ClientFlag.Process(ctx); err != nil { - return err - } - return nil -} - -func (cmd *ls) Usage() string { - return `List all categories -Examples: - govc tags.category.ls` -} - -func withClient(ctx context.Context, cmd *flags.ClientFlag, f func(*tags.RestClient) error) error { - vc, err := cmd.Client() - if err != nil { - return err - } - usrDecode, err := url.QueryUnescape(cmd.Userinfo().String()) - if err != nil { - return err - } - - govcUrl := "https://" + usrDecode + "@" + vc.URL().Hostname() - - URL, err := url.Parse(govcUrl) - if err != nil { - return err - } - - c := tags.NewClient(URL, true, "") - if err != nil { - return err - } - - token := os.Getenv("GOVC_LOGIN_TOKEN") - header := soap.Header{ - Security: &sts.Signer{ - Certificate: vc.Certificate(), - Token: token, - }, - } - - if token == "" { - tokens, cerr := sts.NewClient(ctx, vc) - if cerr != nil { - return cerr - } - - req := sts.TokenRequest{ - Certificate: vc.Certificate(), - Userinfo: cmd.Userinfo(), - } - - header.Security, cerr = tokens.Issue(ctx, req) - if cerr != nil { - return cerr - } - } - - if err = c.Login(ctx); err != nil { - return err - } - - return f(c) -} - -type getResult []string - -func (r getResult) Write(w io.Writer) error { - for i := range r { - fmt.Fprintln(w, r[i]) - } - return nil -} - -func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error { - - return withClient(ctx, cmd.ClientFlag, func(c *tags.RestClient) error { - categories, err := c.ListCategories(ctx) - if err != nil { - return err - } - - result := getResult(categories) - cmd.WriteResult(result) - return nil - - }) -} diff --git a/govc/tags/categories/rm.go b/govc/tags/categories/rm.go deleted file mode 100644 index 5dba5ab00..000000000 --- a/govc/tags/categories/rm.go +++ /dev/null @@ -1,66 +0,0 @@ -/* -Copyright (c) 2018 VMware, 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 tags - -import ( - "context" - "flag" - - "github.com/vmware/govmomi/govc/cli" - "github.com/vmware/govmomi/govc/flags" - "github.com/vmware/govmomi/vapi/tags" -) - -type rm struct { - *flags.ClientFlag -} - -func init() { - cli.Register("tags.category.rm", &rm{}) -} - -func (cmd *rm) Register(ctx context.Context, f *flag.FlagSet) { - cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) - cmd.ClientFlag.Register(ctx, f) -} - -func (cmd *rm) Process(ctx context.Context) error { - if err := cmd.ClientFlag.Process(ctx); err != nil { - return err - } - return nil -} - -func (cmd *rm) Usage() string { - return `Delete category -Examples: - govc tags.category.rm ID` -} - -func (cmd *rm) Run(ctx context.Context, f *flag.FlagSet) error { - if f.NArg() != 1 { - return flag.ErrHelp - } - - categoryID := f.Arg(0) - - return withClient(ctx, cmd.ClientFlag, func(c *tags.RestClient) error { - - return c.DeleteCategory(ctx, categoryID) - - }) -} diff --git a/govc/tags/categories/update.go b/govc/tags/categories/update.go deleted file mode 100644 index 3cc11b75a..000000000 --- a/govc/tags/categories/update.go +++ /dev/null @@ -1,93 +0,0 @@ -/* -Copyright (c) 2018 VMware, 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 tags - -import ( - "context" - "flag" - "strings" - - "github.com/vmware/govmomi/govc/cli" - "github.com/vmware/govmomi/govc/flags" - "github.com/vmware/govmomi/vapi/tags" -) - -type update struct { - *flags.ClientFlag - name string - description string - types string - multi string -} - -func init() { - cli.Register("tags.category.update", &update{}) -} - -func (cmd *update) Register(ctx context.Context, f *flag.FlagSet) { - cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) - cmd.ClientFlag.Register(ctx, f) - f.StringVar(&cmd.name, "n", "", "name of category") - f.StringVar(&cmd.description, "d", "", "description of category") - f.StringVar(&cmd.types, "t", "", "associable_types of category") - f.StringVar(&cmd.multi, "m", "", "cardinality of category") -} - -func (cmd *update) Process(ctx context.Context) error { - if err := cmd.ClientFlag.Process(ctx); err != nil { - return err - } - return nil -} - -func (cmd *update) Usage() string { - return `Update existing category. cardinality can be either "SINGLE" or "MULTIPLE." -Examples: - govc tags.category.update -n "name" -d "description" -t "associable_types" -m "cardinality" ID` -} - -func (cmd *update) Run(ctx context.Context, f *flag.FlagSet) error { - if f.NArg() != 1 { - return flag.ErrHelp - } - id := f.Arg(0) - - return withClient(ctx, cmd.ClientFlag, func(c *tags.RestClient) error { - - category := new(tags.CategoryUpdateSpec) - categoryTemp := new(tags.CategoryUpdate) - if cmd.name != "" { - categoryTemp.Name = cmd.name - } - - if cmd.description != "" { - categoryTemp.Description = cmd.description - } - - if cmd.types != "" { - typesField := strings.Split(cmd.types, ",") - categoryTemp.AssociableTypes = typesField - } - if cmd.multi != "" { - categoryTemp.Cardinality = cmd.multi - } - - category.UpdateSpec = *categoryTemp - return c.UpdateCategory(ctx, id, category) - - }) -} diff --git a/vapi/tags/rest_client.go b/vapi/tags/rest_client.go index d2c2c4207..985fd4be3 100644 --- a/vapi/tags/rest_client.go +++ b/vapi/tags/rest_client.go @@ -179,8 +179,6 @@ func (c *RestClient) handleResponse(resp *http.Response, err error) (io.ReadClos } func (c *RestClient) Login(ctx context.Context) error { - c.mu.Lock() - defer c.mu.Unlock() request, err := c.newRequest("POST", loginURL, nil) if err != nil { @@ -198,8 +196,6 @@ func (c *RestClient) Login(ctx context.Context) error { return err } if resp.StatusCode != http.StatusOK { - // #nosec: Errors unhandled. - // body, _ := ioutil.ReadAll(resp.Body) resp.Body.Close() return err } @@ -209,6 +205,15 @@ func (c *RestClient) Login(ctx context.Context) error { return nil } +func (c *RestClient) Logout(ctx context.Context) error { + _, _, status, err := c.call(ctx, "DELETE", loginURL, nil, nil) + if status != http.StatusOK || err != nil { + return err + } + c.SetSessionID("") + return nil +} + func (c *RestClient) newRequest(method, urlStr string, body io.Reader) (*http.Request, error) { return http.NewRequest(method, c.endpoint.String()+urlStr, body) } From f033846777804eb2d671121650ff2807d09679e0 Mon Sep 17 00:00:00 2001 From: Jiatong Wang Date: Tue, 12 Jun 2018 15:50:02 -0700 Subject: [PATCH 8/8] Delete .DS_Store files --- vapi/.DS_Store | Bin 6148 -> 0 bytes vapi/tags/.DS_Store | Bin 6148 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 vapi/.DS_Store delete mode 100644 vapi/tags/.DS_Store diff --git a/vapi/.DS_Store b/vapi/.DS_Store deleted file mode 100644 index 89fff4f7ab2697026c45207d5adfb9b5e9e8d212..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK!AiqG5Z!H~CWx4WV2^w8)Olyx9=r(=J*cz^Eo~qsrAaMXEBOumBfr4k zab|a0ELHF%VrO9X&Cbs3vM*t0modiOVY|wh%@{L55epVHUkHw)E=WOp5IN2fjM9D( zB?0u`7NW`V7a72BXRwU(=WfBQNu_Ve0yW zYwVr-SufD`MUq4_+Yc_o&Zuc`og{JQhjC{hlf!lgLT;|YxSfpLWEi(oIgh6XV`)pi1TXiNRGm_=S#hH0BCbI^%L>m`AV7^$Uf|)xj@hIOC2& zYKZ}2V4i`Z>e^WUPrrZv&nHoj7$64z6$9L8cnue}q-*QK=CIZZ&=x2P#^nlUDPYJ_ g46#^>OQ2G~FVFyVH0BDy140)8Ndq;+z^^j!0if4VxBvhE diff --git a/vapi/tags/.DS_Store b/vapi/tags/.DS_Store deleted file mode 100644 index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0