vSphere Cloud Provider: update vmware/gomvomi godeps

This commit is contained in:
Doug MacEachern
2018-05-14 14:09:20 -07:00
parent 83768d286c
commit c340f6f9a4
55 changed files with 8733 additions and 596 deletions

View File

@@ -21,6 +21,7 @@ import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"flag"
@@ -28,6 +29,7 @@ import (
"io"
"io/ioutil"
"log"
"net"
"net/http"
"net/http/httptest"
"net/url"
@@ -35,6 +37,7 @@ import (
"path"
"reflect"
"sort"
"strconv"
"strings"
"github.com/vmware/govmomi/find"
@@ -51,15 +54,17 @@ var Trace = false
// Method encapsulates a decoded SOAP client request
type Method struct {
Name string
This types.ManagedObjectReference
Body types.AnyType
Name string
This types.ManagedObjectReference
Header soap.Header
Body types.AnyType
}
// Service decodes incoming requests and dispatches to a Handler
type Service struct {
client *vim25.Client
sm *SessionManager
sdk map[string]*Registry
readAll func(io.Reader) ([]byte, error)
@@ -70,7 +75,8 @@ type Service struct {
// Server provides a simulator Service over HTTP
type Server struct {
*httptest.Server
URL *url.URL
URL *url.URL
Tunnel int
caFile string
}
@@ -80,6 +86,7 @@ func New(instance *ServiceInstance) *Service {
s := &Service{
readAll: ioutil.ReadAll,
sm: Map.SessionManager(),
sdk: make(map[string]*Registry),
}
s.client, _ = vim25.NewClient(context.Background(), s)
@@ -110,12 +117,12 @@ func Fault(msg string, fault types.BaseMethodFault) *soap.Fault {
}
func (s *Service) call(ctx *Context, method *Method) soap.HasFault {
handler := Map.Get(method.This)
handler := ctx.Map.Get(method.This)
session := ctx.Session
if session == nil {
switch method.Name {
case "RetrieveServiceContent", "Login", "RetrieveProperties", "RetrievePropertiesEx", "CloneSession":
case "RetrieveServiceContent", "List", "Login", "LoginByToken", "LoginExtensionByCertificate", "RetrieveProperties", "RetrievePropertiesEx", "CloneSession":
// ok for now, TODO: authz
default:
fault := &types.NotAuthenticated{
@@ -170,7 +177,7 @@ func (s *Service) call(ctx *Context, method *Method) soap.HasFault {
args = append(args, reflect.ValueOf(ctx))
}
args = append(args, reflect.ValueOf(method.Body))
Map.WithLock(handler, func() {
ctx.Map.WithLock(handler, func() {
res = m.Call(args)
})
@@ -197,6 +204,7 @@ func (s *Service) RoundTrip(ctx context.Context, request, response soap.HasFault
}
res := s.call(&Context{
Map: Map,
Context: ctx,
Session: internalContext.Session,
}, method)
@@ -281,6 +289,16 @@ func (s *Service) About(w http.ResponseWriter, r *http.Request) {
_ = enc.Encode(&about)
}
// RegisterSDK adds an HTTP handler for the Registry's Path and Namespace.
func (s *Service) RegisterSDK(r *Registry) {
if s.ServeMux == nil {
s.ServeMux = http.NewServeMux()
}
s.sdk[r.Path] = r
s.ServeMux.HandleFunc(r.Path, s.ServeSDK)
}
// ServeSDK implements the http.Handler interface
func (s *Service) ServeSDK(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
@@ -305,17 +323,19 @@ func (s *Service) ServeSDK(w http.ResponseWriter, r *http.Request) {
res: w,
m: s.sm,
Map: s.sdk[r.URL.Path],
Context: context.Background(),
}
Map.WithLock(s.sm, ctx.mapSession)
ctx.Map.WithLock(s.sm, ctx.mapSession)
var res soap.HasFault
var soapBody interface{}
method, err := UnmarshalBody(body)
method, err := UnmarshalBody(ctx.Map.typeFunc, body)
if err != nil {
res = serverFault(err.Error())
} else {
ctx.Header = method.Header
res = s.call(ctx, method)
}
@@ -447,15 +467,10 @@ func (*Service) ServiceVersions(w http.ResponseWriter, r *http.Request) {
// NewServer returns an http Server instance for the given service
func (s *Service) NewServer() *Server {
s.RegisterSDK(Map)
mux := s.ServeMux
if mux == nil {
mux = http.NewServeMux()
}
path := "/sdk"
mux.HandleFunc(path, s.ServeSDK)
mux.HandleFunc(path+"/vimServiceVersions.xml", s.ServiceVersions)
mux.HandleFunc(Map.Path+"/vimServiceVersions.xml", s.ServiceVersions)
mux.HandleFunc(folderPrefix, s.ServeDatastore)
mux.HandleFunc("/about", s.About)
@@ -466,7 +481,7 @@ func (s *Service) NewServer() *Server {
u := &url.URL{
Scheme: "http",
Host: ts.Listener.Addr().String(),
Path: path,
Path: Map.Path,
User: url.UserPassword("user", "pass"),
}
@@ -478,14 +493,31 @@ func (s *Service) NewServer() *Server {
_ = f.Value.Set("")
}
cert := ""
if s.TLS == nil {
ts.Start()
} else {
ts.TLS = s.TLS
ts.TLS.ClientAuth = tls.RequestClientCert // Used by SessionManager.LoginExtensionByCertificate
ts.StartTLS()
u.Scheme += "s"
cert = base64.StdEncoding.EncodeToString(ts.TLS.Certificates[0].Certificate[0])
}
// Add vcsim config to OptionManager for use by SDK handlers (see lookup/simulator for example)
m := Map.OptionManager()
m.Setting = append(m.Setting,
&types.OptionValue{
Key: "vcsim.server.url",
Value: ts.URL,
},
&types.OptionValue{
Key: "vcsim.server.cert",
Value: cert,
},
)
return &Server{
Server: ts,
URL: u,
@@ -525,6 +557,60 @@ func (s *Server) CertificateFile() (string, error) {
return s.caFile, pem.Encode(f, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
}
// proxy tunnels SDK requests
func (s *Server) proxy(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodConnect {
http.Error(w, "", http.StatusMethodNotAllowed)
return
}
dst, err := net.Dial("tcp", s.URL.Host)
if err != nil {
http.Error(w, err.Error(), http.StatusBadGateway)
return
}
w.WriteHeader(http.StatusOK)
src, _, err := w.(http.Hijacker).Hijack()
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
go io.Copy(src, dst)
go func() {
_, _ = io.Copy(dst, src)
_ = dst.Close()
_ = src.Close()
}()
}
// StartTunnel runs an HTTP proxy for tunneling SDK requests that require TLS client certificate authentication.
func (s *Server) StartTunnel() error {
tunnel := &http.Server{
Addr: fmt.Sprintf("%s:%d", s.URL.Hostname(), s.Tunnel),
Handler: http.HandlerFunc(s.proxy),
}
l, err := net.Listen("tcp", tunnel.Addr)
if err != nil {
return err
}
if s.Tunnel == 0 {
s.Tunnel = l.Addr().(*net.TCPAddr).Port
}
// Set client proxy port (defaults to vCenter host port 80 in real life)
q := s.URL.Query()
q.Set("GOVMOMI_TUNNEL_PROXY_PORT", strconv.Itoa(s.Tunnel))
s.URL.RawQuery = q.Encode()
go tunnel.Serve(l)
return nil
}
// Close shuts down the server and blocks until all outstanding
// requests on this server have completed.
func (s *Server) Close() {
@@ -536,7 +622,6 @@ func (s *Server) Close() {
var (
vim25MapType = types.TypeFunc()
typeFunc = defaultMapType
)
func defaultMapType(name string) (reflect.Type, bool) {
@@ -552,14 +637,39 @@ func defaultMapType(name string) (reflect.Type, bool) {
return typ, ok
}
// UnmarshalBody extracts the Body from a soap.Envelope and unmarshals to the corresponding govmomi type
func UnmarshalBody(data []byte) (*Method, error) {
body := struct {
// Element can be used to defer decoding of an XML node.
type Element struct {
start xml.StartElement
inner struct {
Content string `xml:",innerxml"`
}{}
}
typeFunc func(string) (reflect.Type, bool)
}
func (e *Element) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
e.start = start
return d.DecodeElement(&e.inner, &start)
}
func (e *Element) decoder() *xml.Decoder {
decoder := xml.NewDecoder(strings.NewReader(e.inner.Content))
decoder.TypeFunc = e.typeFunc // required to decode interface types
return decoder
}
func (e *Element) Decode(val interface{}) error {
return e.decoder().DecodeElement(val, &e.start)
}
// UnmarshalBody extracts the Body from a soap.Envelope and unmarshals to the corresponding govmomi type
func UnmarshalBody(typeFunc func(string) (reflect.Type, bool), data []byte) (*Method, error) {
body := &Element{typeFunc: typeFunc}
req := soap.Envelope{
Body: &body,
Header: &soap.Header{
Security: new(Element),
},
Body: body,
}
err := xml.Unmarshal(data, &req)
@@ -567,40 +677,38 @@ func UnmarshalBody(data []byte) (*Method, error) {
return nil, fmt.Errorf("xml.Unmarshal: %s", err)
}
decoder := xml.NewDecoder(bytes.NewReader([]byte(body.Content)))
decoder.TypeFunc = typeFunc // required to decode interface types
var start *xml.StartElement
var start xml.StartElement
var ok bool
decoder := body.decoder()
for {
tok, derr := decoder.Token()
if derr != nil {
return nil, fmt.Errorf("decoding body: %s", err)
return nil, fmt.Errorf("decoding: %s", derr)
}
if t, ok := tok.(xml.StartElement); ok {
start = &t
if start, ok = tok.(xml.StartElement); ok {
break
}
}
kind := start.Name.Local
if !ok {
return nil, fmt.Errorf("decoding: method token not found")
}
kind := start.Name.Local
rtype, ok := typeFunc(kind)
if !ok {
return nil, fmt.Errorf("no vmomi type defined for '%s'", kind)
}
var val interface{}
if rtype != nil {
val = reflect.New(rtype).Interface()
}
val := reflect.New(rtype).Interface()
err = decoder.DecodeElement(val, start)
err = decoder.DecodeElement(val, &start)
if err != nil {
return nil, fmt.Errorf("decoding %s: %s", kind, err)
}
method := &Method{Name: kind, Body: val}
method := &Method{Name: kind, Header: *req.Header, Body: val}
field := reflect.ValueOf(val).Elem().FieldByName("This")