godeps: update vmware/govmomi to v0.20 release

This commit is contained in:
Doug MacEachern
2019-03-20 10:31:41 -07:00
parent 055061637a
commit 243da8c365
76 changed files with 54784 additions and 303 deletions

View File

@@ -51,15 +51,13 @@ func (o AssociatedObject) Reference() types.ManagedObjectReference {
// Association for tag-association requests.
type Association struct {
TagID string `json:"tag_id,omitempty"`
ObjectID *AssociatedObject `json:"object_id,omitempty"`
}
// NewAssociation returns an Association, converting ref to an AssociatedObject.
func NewAssociation(tagID string, ref mo.Reference) Association {
func NewAssociation(ref mo.Reference) Association {
obj := AssociatedObject(ref.Reference())
return Association{
TagID: tagID,
ObjectID: &obj,
}
}

View File

@@ -43,6 +43,16 @@ func NewClient(c *vim25.Client) *Client {
return &Client{sc}
}
type Signer interface {
SignRequest(*http.Request) error
}
type signerContext struct{}
func (c *Client) WithSigner(ctx context.Context, s Signer) context.Context {
return context.WithValue(ctx, signerContext{}, s)
}
// Do sends the http.Request, decoding resBody if provided.
func (c *Client) Do(ctx context.Context, req *http.Request, resBody interface{}) error {
switch req.Method {
@@ -52,6 +62,12 @@ func (c *Client) Do(ctx context.Context, req *http.Request, resBody interface{})
req.Header.Set("Accept", "application/json")
if s, ok := ctx.Value(signerContext{}).(Signer); ok {
if err := s.SignRequest(req); err != nil {
return err
}
}
return c.Client.Do(ctx, req, func(res *http.Response) error {
switch res.StatusCode {
case http.StatusOK:
@@ -98,6 +114,10 @@ func (c *Client) Login(ctx context.Context, user *url.Userinfo) error {
return c.Do(ctx, req, nil)
}
func (c *Client) LoginByToken(ctx context.Context) error {
return c.Login(ctx, nil)
}
// Logout deletes the current session.
func (c *Client) Logout(ctx context.Context) error {
req := internal.URL(c, internal.SessionPath).Request(http.MethodDelete)

View File

@@ -26,6 +26,7 @@ import (
"reflect"
"strings"
"sync"
"time"
"github.com/google/uuid"
"github.com/vmware/govmomi/vapi/internal"
@@ -33,12 +34,19 @@ import (
vim "github.com/vmware/govmomi/vim25/types"
)
type session struct {
User string `json:"user"`
Created time.Time `json:"created_time"`
LastAccessed time.Time `json:"last_accessed_time"`
}
type handler struct {
*http.ServeMux
sync.Mutex
Category map[string]*tags.Category
Tag map[string]*tags.Tag
Association map[string]map[internal.AssociatedObject]bool
Session map[string]*session
}
// New creates a vAPI simulator.
@@ -48,6 +56,7 @@ func New(u *url.URL, settings []vim.BaseOptionValue) (string, http.Handler) {
Category: make(map[string]*tags.Category),
Tag: make(map[string]*tags.Tag),
Association: make(map[string]map[internal.AssociatedObject]bool),
Session: make(map[string]*session),
}
handlers := []struct {
@@ -60,6 +69,7 @@ func New(u *url.URL, settings []vim.BaseOptionValue) (string, http.Handler) {
{internal.TagPath, s.tag},
{internal.TagPath + "/", s.tagID},
{internal.AssociationPath, s.association},
{internal.AssociationPath + "/", s.associationID},
}
for i := range handlers {
@@ -68,6 +78,11 @@ func New(u *url.URL, settings []vim.BaseOptionValue) (string, http.Handler) {
s.Lock()
defer s.Unlock()
if !s.isAuthorized(r) {
w.WriteHeader(http.StatusUnauthorized)
return
}
h.m(w, r)
})
}
@@ -75,6 +90,99 @@ func New(u *url.URL, settings []vim.BaseOptionValue) (string, http.Handler) {
return internal.Path + "/", s
}
func (s *handler) isAuthorized(r *http.Request) bool {
if r.Method == http.MethodPost && strings.HasSuffix(r.URL.Path, internal.SessionPath) {
return true
}
id := r.Header.Get(internal.SessionCookieName)
if id == "" {
if cookie, err := r.Cookie(internal.SessionCookieName); err == nil {
id = cookie.Value
r.Header.Set(internal.SessionCookieName, id)
}
}
info, ok := s.Session[id]
if ok {
info.LastAccessed = time.Now()
}
return ok
}
func (s *handler) hasAuthorization(r *http.Request) (string, bool) {
u, p, ok := r.BasicAuth()
if ok { // user+pass auth
if u == "" || p == "" {
return u, false
}
return u, true
}
auth := r.Header.Get("Authorization")
return "TODO", strings.HasPrefix(auth, "SIGN ") // token auth
}
func (s *handler) findTag(e vim.VslmTagEntry) *tags.Tag {
for _, c := range s.Category {
if c.Name == e.ParentCategoryName {
for _, t := range s.Tag {
if t.Name == e.TagName && t.CategoryID == c.ID {
return t
}
}
}
}
return nil
}
// AttachedObjects is meant for internal use via simulator.Registry.tagManager
func (s *handler) AttachedObjects(tag vim.VslmTagEntry) ([]vim.ManagedObjectReference, vim.BaseMethodFault) {
t := s.findTag(tag)
if t == nil {
return nil, new(vim.NotFound)
}
var ids []vim.ManagedObjectReference
for id := range s.Association[t.ID] {
ids = append(ids, vim.ManagedObjectReference(id))
}
return ids, nil
}
// AttachedTags is meant for internal use via simulator.Registry.tagManager
func (s *handler) AttachedTags(ref vim.ManagedObjectReference) ([]vim.VslmTagEntry, vim.BaseMethodFault) {
oid := internal.AssociatedObject(ref)
var tags []vim.VslmTagEntry
for id, objs := range s.Association {
if objs[oid] {
tag := s.Tag[id]
cat := s.Category[tag.CategoryID]
tags = append(tags, vim.VslmTagEntry{
TagName: tag.Name,
ParentCategoryName: cat.Name,
})
}
}
return tags, nil
}
// AttachTag is meant for internal use via simulator.Registry.tagManager
func (s *handler) AttachTag(ref vim.ManagedObjectReference, tag vim.VslmTagEntry) vim.BaseMethodFault {
t := s.findTag(tag)
if t == nil {
return new(vim.NotFound)
}
s.Association[t.ID][internal.AssociatedObject(ref)] = true
return nil
}
// DetachTag is meant for internal use via simulator.Registry.tagManager
func (s *handler) DetachTag(id vim.ManagedObjectReference, tag vim.VslmTagEntry) vim.BaseMethodFault {
t := s.findTag(tag)
if t == nil {
return new(vim.NotFound)
}
delete(s.Association[t.ID], internal.AssociatedObject(id))
return nil
}
// ok responds with http.StatusOK and json encodes val if given.
func (s *handler) ok(w http.ResponseWriter, val ...interface{}) {
w.WriteHeader(http.StatusOK)
@@ -136,23 +244,28 @@ func (s *handler) decode(r *http.Request, w http.ResponseWriter, val interface{}
}
func (s *handler) session(w http.ResponseWriter, r *http.Request) {
var id string
id := r.Header.Get(internal.SessionCookieName)
switch r.Method {
case http.MethodPost:
user, ok := s.hasAuthorization(r)
if !ok {
w.WriteHeader(http.StatusUnauthorized)
return
}
id = uuid.New().String()
// TODO: save session
now := time.Now()
s.Session[id] = &session{user, now, now}
http.SetCookie(w, &http.Cookie{
Name: internal.SessionCookieName,
Value: id,
})
s.ok(w)
s.ok(w, id)
case http.MethodDelete:
// TODO: delete session
delete(s.Session, id)
s.ok(w)
case http.MethodGet:
// TODO: test is session is valid
s.ok(w, id)
s.ok(w, s.Session[id])
}
}
@@ -161,8 +274,12 @@ func (s *handler) action(r *http.Request) string {
}
func (s *handler) id(r *http.Request) string {
id := path.Base(r.URL.Path)
return strings.TrimPrefix(id, "id:")
base := path.Base(r.URL.Path)
id := strings.TrimPrefix(base, "id:")
if id == base {
return "" // trigger 404 Not Found w/o id: prefix
}
return id
}
func newID(kind string) string {
@@ -321,21 +438,7 @@ func (s *handler) association(w http.ResponseWriter, r *http.Request) {
return
}
if spec.TagID != "" {
if _, exists := s.Association[spec.TagID]; !exists {
log.Printf("association tag not found: %s", spec.TagID)
http.NotFound(w, r)
return
}
}
switch s.action(r) {
case "attach":
s.Association[spec.TagID][*spec.ObjectID] = true
s.ok(w)
case "detach":
delete(s.Association[spec.TagID], *spec.ObjectID)
s.ok(w)
case "list-attached-tags":
var ids []string
for id, objs := range s.Association {
@@ -344,9 +447,37 @@ func (s *handler) association(w http.ResponseWriter, r *http.Request) {
}
}
s.ok(w, ids)
}
}
func (s *handler) associationID(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
id := s.id(r)
if _, exists := s.Association[id]; !exists {
log.Printf("association tag not found: %s", id)
http.NotFound(w, r)
return
}
var spec internal.Association
if !s.decode(r, w, &spec) {
return
}
switch s.action(r) {
case "attach":
s.Association[id][*spec.ObjectID] = true
s.ok(w)
case "detach":
delete(s.Association[id], *spec.ObjectID)
s.ok(w)
case "list-attached-objects":
var ids []internal.AssociatedObject
for id := range s.Association[spec.TagID] {
for id := range s.Association[id] {
ids = append(ids, id)
}
s.ok(w, ids)

View File

@@ -42,8 +42,8 @@ func (c *Manager) AttachTag(ctx context.Context, tagID string, ref mo.Reference)
if err != nil {
return err
}
spec := internal.NewAssociation(id, ref)
url := internal.URL(c, internal.AssociationPath).WithAction("attach")
spec := internal.NewAssociation(ref)
url := internal.URL(c, internal.AssociationPath).WithID(id).WithAction("attach")
return c.Do(ctx, url.Request(http.MethodPost, spec), nil)
}
@@ -54,14 +54,14 @@ func (c *Manager) DetachTag(ctx context.Context, tagID string, ref mo.Reference)
if err != nil {
return err
}
spec := internal.NewAssociation(id, ref)
url := internal.URL(c, internal.AssociationPath).WithAction("detach")
spec := internal.NewAssociation(ref)
url := internal.URL(c, internal.AssociationPath).WithID(id).WithAction("detach")
return c.Do(ctx, url.Request(http.MethodPost, spec), nil)
}
// ListAttachedTags fetches the array of tag IDs attached to the given object.
func (c *Manager) ListAttachedTags(ctx context.Context, ref mo.Reference) ([]string, error) {
spec := internal.NewAssociation("", ref)
spec := internal.NewAssociation(ref)
url := internal.URL(c, internal.AssociationPath).WithAction("list-attached-tags")
var res []string
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
@@ -91,12 +91,9 @@ func (c *Manager) ListAttachedObjects(ctx context.Context, tagID string) ([]mo.R
if err != nil {
return nil, err
}
spec := internal.Association{
TagID: id,
}
url := internal.URL(c, internal.AssociationPath).WithAction("list-attached-objects")
url := internal.URL(c, internal.AssociationPath).WithID(id).WithAction("list-attached-objects")
var res []internal.AssociatedObject
if err := c.Do(ctx, url.Request(http.MethodPost, spec), &res); err != nil {
if err := c.Do(ctx, url.Request(http.MethodPost, nil), &res); err != nil {
return nil, err
}

View File

@@ -137,6 +137,30 @@ func (c *Manager) GetTag(ctx context.Context, id string) (*Tag, error) {
}
// GetTagForCategory fetches the tag information for the given identifier in the given category.
func (c *Manager) GetTagForCategory(ctx context.Context, id, category string) (*Tag, error) {
if category == "" {
return c.GetTag(ctx, id)
}
ids, err := c.ListTagsForCategory(ctx, category)
if err != nil {
return nil, err
}
for _, id := range ids {
tag, err := c.GetTag(ctx, id)
if err != nil {
return nil, fmt.Errorf("get tag for category %s %s: %s", category, id, err)
}
if tag.ID == id || tag.Name == id {
return tag, nil
}
}
return nil, fmt.Errorf("tag %q not found in category %q", id, category)
}
// ListTags returns all tag IDs in the system.
func (c *Manager) ListTags(ctx context.Context) ([]string, error) {
url := internal.URL(c, internal.TagPath)