github.com/vmware/govmomi@v0.37.2/vapi/tags/tags.go (about)

     1  /*
     2  Copyright (c) 2018 VMware, Inc. All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package tags
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"net/http"
    23  	"strings"
    24  
    25  	"github.com/vmware/govmomi/vapi/internal"
    26  	"github.com/vmware/govmomi/vapi/rest"
    27  )
    28  
    29  // Manager extends rest.Client, adding tag related methods.
    30  type Manager struct {
    31  	*rest.Client
    32  }
    33  
    34  // NewManager creates a new Manager instance with the given client.
    35  func NewManager(client *rest.Client) *Manager {
    36  	return &Manager{
    37  		Client: client,
    38  	}
    39  }
    40  
    41  // isName returns true if the id is not a urn.
    42  func isName(id string) bool {
    43  	return !strings.HasPrefix(id, "urn:")
    44  }
    45  
    46  // Tag provides methods to create, read, update, delete, and enumerate tags.
    47  type Tag struct {
    48  	ID          string   `json:"id,omitempty"`
    49  	Description string   `json:"description,omitempty"`
    50  	Name        string   `json:"name,omitempty"`
    51  	CategoryID  string   `json:"category_id,omitempty"`
    52  	UsedBy      []string `json:"used_by,omitempty"`
    53  }
    54  
    55  // Patch merges updates from the given src.
    56  func (t *Tag) Patch(src *Tag) {
    57  	if src.Name != "" {
    58  		t.Name = src.Name
    59  	}
    60  	if src.Description != "" {
    61  		t.Description = src.Description
    62  	}
    63  	if src.CategoryID != "" {
    64  		t.CategoryID = src.CategoryID
    65  	}
    66  }
    67  
    68  // CreateTag creates a new tag with the given Name, Description and CategoryID.
    69  func (c *Manager) CreateTag(ctx context.Context, tag *Tag) (string, error) {
    70  	// create avoids the annoyance of CreateTag requiring a "description" key to be included in the request,
    71  	// even though the field value can be empty.
    72  	type create struct {
    73  		Name        string `json:"name"`
    74  		Description string `json:"description"`
    75  		CategoryID  string `json:"category_id"`
    76  	}
    77  	spec := struct {
    78  		Tag create `json:"create_spec"`
    79  	}{
    80  		Tag: create{
    81  			Name:        tag.Name,
    82  			Description: tag.Description,
    83  			CategoryID:  tag.CategoryID,
    84  		},
    85  	}
    86  	if isName(tag.CategoryID) {
    87  		cat, err := c.GetCategory(ctx, tag.CategoryID)
    88  		if err != nil {
    89  			return "", err
    90  		}
    91  		spec.Tag.CategoryID = cat.ID
    92  	}
    93  	url := c.Resource(internal.TagPath)
    94  	var res string
    95  	return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
    96  }
    97  
    98  // UpdateTag can update one or both of the tag Description and Name fields.
    99  func (c *Manager) UpdateTag(ctx context.Context, tag *Tag) error {
   100  	spec := struct {
   101  		Tag Tag `json:"update_spec"`
   102  	}{
   103  		Tag: Tag{
   104  			Name:        tag.Name,
   105  			Description: tag.Description,
   106  		},
   107  	}
   108  	url := c.Resource(internal.TagPath).WithID(tag.ID)
   109  	return c.Do(ctx, url.Request(http.MethodPatch, spec), nil)
   110  }
   111  
   112  // DeleteTag deletes an existing tag.
   113  func (c *Manager) DeleteTag(ctx context.Context, tag *Tag) error {
   114  	url := c.Resource(internal.TagPath).WithID(tag.ID)
   115  	return c.Do(ctx, url.Request(http.MethodDelete), nil)
   116  }
   117  
   118  // GetTag fetches the tag information for the given identifier.
   119  // The id parameter can be a Tag ID or Tag Name.
   120  func (c *Manager) GetTag(ctx context.Context, id string) (*Tag, error) {
   121  	if isName(id) {
   122  		tags, err := c.GetTags(ctx)
   123  		if err != nil {
   124  			return nil, err
   125  		}
   126  
   127  		for i := range tags {
   128  			if tags[i].Name == id {
   129  				return &tags[i], nil
   130  			}
   131  		}
   132  	}
   133  
   134  	url := c.Resource(internal.TagPath).WithID(id)
   135  	var res Tag
   136  	return &res, c.Do(ctx, url.Request(http.MethodGet), &res)
   137  
   138  }
   139  
   140  // GetTagForCategory fetches the tag information for the given identifier in the given category.
   141  func (c *Manager) GetTagForCategory(ctx context.Context, id, category string) (*Tag, error) {
   142  	if category == "" {
   143  		return c.GetTag(ctx, id)
   144  	}
   145  
   146  	ids, err := c.ListTagsForCategory(ctx, category)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  
   151  	for _, tagid := range ids {
   152  		tag, err := c.GetTag(ctx, tagid)
   153  		if err != nil {
   154  			return nil, fmt.Errorf("get tag for category %s %s: %s", category, tagid, err)
   155  		}
   156  		if tag.ID == id || tag.Name == id {
   157  			return tag, nil
   158  		}
   159  	}
   160  
   161  	return nil, fmt.Errorf("tag %q not found in category %q", id, category)
   162  }
   163  
   164  // ListTags returns all tag IDs in the system.
   165  func (c *Manager) ListTags(ctx context.Context) ([]string, error) {
   166  	url := c.Resource(internal.TagPath)
   167  	var res []string
   168  	return res, c.Do(ctx, url.Request(http.MethodGet), &res)
   169  }
   170  
   171  // GetTags fetches an array of tag information in the system.
   172  func (c *Manager) GetTags(ctx context.Context) ([]Tag, error) {
   173  	ids, err := c.ListTags(ctx)
   174  	if err != nil {
   175  		return nil, fmt.Errorf("get tags failed for: %s", err)
   176  	}
   177  
   178  	var tags []Tag
   179  	for _, id := range ids {
   180  		tag, err := c.GetTag(ctx, id)
   181  		if err != nil {
   182  			return nil, fmt.Errorf("get category %s failed for %s", id, err)
   183  		}
   184  
   185  		tags = append(tags, *tag)
   186  
   187  	}
   188  	return tags, nil
   189  }
   190  
   191  // The id parameter can be a Category ID or Category Name.
   192  func (c *Manager) ListTagsForCategory(ctx context.Context, id string) ([]string, error) {
   193  	if isName(id) {
   194  		cat, err := c.GetCategory(ctx, id)
   195  		if err != nil {
   196  			return nil, err
   197  		}
   198  		id = cat.ID
   199  	}
   200  
   201  	body := struct {
   202  		ID string `json:"category_id"`
   203  	}{id}
   204  	url := c.Resource(internal.TagPath).WithID(id).WithAction("list-tags-for-category")
   205  	var res []string
   206  	return res, c.Do(ctx, url.Request(http.MethodPost, body), &res)
   207  }
   208  
   209  // The id parameter can be a Category ID or Category Name.
   210  func (c *Manager) GetTagsForCategory(ctx context.Context, id string) ([]Tag, error) {
   211  	ids, err := c.ListTagsForCategory(ctx, id)
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  
   216  	var tags []Tag
   217  	for _, id := range ids {
   218  		tag, err := c.GetTag(ctx, id)
   219  		if err != nil {
   220  			return nil, fmt.Errorf("get tag %s: %s", id, err)
   221  		}
   222  
   223  		tags = append(tags, *tag)
   224  	}
   225  	return tags, nil
   226  }