Repository: vmware/govmomi
Pull request #1930 (2020-04-13 17:34:34)

vcsim: fixes for PowerCLI
Commit 08adb5d6 (#1740) removed the obsolute sso:sts type in favor of cs.identity,
but PowerCLI still uses sso:sts.

Commit 43d69860 (#1595) changed endpoint registration to happen before vcsim's TLS cert is known,
leaving ServiceEndpoints.SslTrust empty, but PowerCLI requires this field be non-empty.


Patch:
diff --git a/lookup/simulator/registration_info.go b/lookup/simulator/registration_info.go
index 02ff9440..b08c9c18 100644
--- a/lookup/simulator/registration_info.go
+++ b/lookup/simulator/registration_info.go
@@ -53,7 +53,7 @@ func registrationInfo() []types.LookupServiceRegistrationInfo {
 	instance := opts["VirtualCenter.InstanceName"]
 
 	// Real PSC has 30+ services by default, we just provide a few that are useful for vmomi interaction..
-	return []types.LookupServiceRegistrationInfo{
+	info := []types.LookupServiceRegistrationInfo{
 		{
 			LookupServiceRegistrationCommonServiceInfo: types.LookupServiceRegistrationCommonServiceInfo{
 				LookupServiceRegistrationMutableServiceInfo: types.LookupServiceRegistrationMutableServiceInfo{
@@ -130,4 +130,9 @@ func registrationInfo() []types.LookupServiceRegistrationInfo {
 			SiteId:    siteID,
 		},
 	}
+
+	sts := info[0]
+	sts.ServiceType.Type = "sso:sts" // obsolete service type, but still used by PowerCLI
+
+	return append(info, sts)
 }
diff --git a/lookup/simulator/simulator.go b/lookup/simulator/simulator.go
index 0bfde774..30232e65 100644
--- a/lookup/simulator/simulator.go
+++ b/lookup/simulator/simulator.go
@@ -17,6 +17,8 @@ limitations under the License.
 package simulator
 
 import (
+	"sync"
+
 	"github.com/vmware/govmomi/lookup"
 	"github.com/vmware/govmomi/lookup/methods"
 	"github.com/vmware/govmomi/lookup/types"
@@ -40,6 +42,11 @@ func init() {
 	})
 }
 
+var (
+	instance sync.Once
+	register = func() {}
+)
+
 func New() *simulator.Registry {
 	r := simulator.NewRegistry()
 	r.Namespace = lookup.Namespace
@@ -49,10 +56,13 @@ func New() *simulator.Registry {
 		ManagedObjectReference: lookup.ServiceInstance,
 		Content:                content,
 	})
-	r.Put(&ServiceRegistration{
-		ManagedObjectReference: *content.ServiceRegistration,
-		Info:                   registrationInfo(),
-	})
+
+	register = func() {
+		r.Put(&ServiceRegistration{
+			ManagedObjectReference: *content.ServiceRegistration,
+			Info:                   registrationInfo(),
+		})
+	}
 
 	return r
 }
@@ -64,6 +74,10 @@ type ServiceInstance struct {
 }
 
 func (s *ServiceInstance) RetrieveServiceContent(_ *types.RetrieveServiceContent) soap.HasFault {
+	// defer register to this point to ensure we can include vcsim's cert in ServiceEndpoints.SslTrust
+	// TODO: we should be able to register within New(), but this is the only place that currently depends on vcsim's cert.
+	instance.Do(register)
+
 	return &methods.RetrieveServiceContentBody{
 		Res: &types.RetrieveServiceContentResponse{
 			Returnval: s.Content,
diff --git a/session/cache/session.go b/session/cache/session.go
index cd5a8b19..0aa21388 100644
--- a/session/cache/session.go
+++ b/session/cache/session.go
@@ -18,7 +18,7 @@ package cache
 
 import (
 	"context"
-	"crypto/sha1"
+	"crypto/sha256"
 	"encoding/json"
 	"fmt"
 	"io/ioutil"
@@ -52,8 +52,7 @@ type Client interface {
 // such as SAML token authentication (see govc session.login for example).
 type Session struct {
 	URL         *url.URL // URL of a vCenter or ESXi instance
-	DirSOAP     string   // DirSOAP cache directory. Defaults to "$HOME/.govmomi/sessions"
-	DirREST     string   // DirREST cache directory. Defaults to "$HOME/.govmomi/rest_sessions"
+	Dir         string   // Dir defaults to "$HOME/.govmomi/sessions"
 	Insecure    bool     // Insecure param for soap.NewClient (tls.Config.InsecureSkipVerify)
 	Passthrough bool     // Passthrough disables caching when set to true
 
@@ -99,26 +98,18 @@ func (s *Session) key(path string) string {
 	// Key session file off of full URI and insecure setting.
 	// Hash key to get a predictable, canonical format.
 	key := fmt.Sprintf("%s#insecure=%t", p.String(), s.Insecure)
-	return fmt.Sprintf("%040x", sha1.Sum([]byte(key)))
+	return fmt.Sprintf("%064x", sha256.Sum256([]byte(key)))
 }
 
-func (s *Session) file(p string) string {
-	dir := ""
-
-	switch p {
-	case rest.Path:
-		dir = s.DirREST
-		if dir == "" {
-			dir = filepath.Join(home, "rest_sessions")
-		}
-	default:
-		dir = s.DirSOAP
-		if dir == "" {
-			dir = filepath.Join(home, "sessions")
-		}
+func (s *Session) dir() string {
+	if s.Dir != "" {
+		return s.Dir
 	}
+	return filepath.Join(home, "sessions")
+}
 
-	return filepath.Join(dir, s.key(p))
+func (s *Session) file(p string) string {
+	return filepath.Join(s.dir(), s.key(p))
 }
 
 // Save a Client in the file cache.
diff --git a/simulator/event_manager.go b/simulator/event_manager.go
index bea38ba4..b7d5d6b2 100644
--- a/simulator/event_manager.go
+++ b/simulator/event_manager.go
@@ -392,6 +392,14 @@ func (c *EventHistoryCollector) SetCollectorPageSize(ctx *Context, req *types.Se
 	return body
 }
 
+func (c *EventHistoryCollector) ResetCollector(ctx *Context, req *types.ResetCollector) soap.HasFault {
+	c.pos = len(c.GetLatestPage())
+
+	return &methods.ResetCollectorBody{
+		Res: new(types.ResetCollectorResponse),
+	}
+}
+
 func (c *EventHistoryCollector) RewindCollector(ctx *Context, req *types.RewindCollector) soap.HasFault {
 	c.pos = 0
 	return &methods.RewindCollectorBody{


Test patch:
diff --git a/govc/test/sso.bats b/govc/test/sso.bats
index 790cb083..dd19ed8f 100755
--- a/govc/test/sso.bats
+++ b/govc/test/sso.bats
@@ -27,6 +27,13 @@ load test_helper
   run govc sso.service.ls -t cs.identity -U
   assert_success "$sts"
 
+  run govc sso.service.ls -t sso:sts -U
+  assert_success "$sts"
+
+  cert=$(govc about.cert -show | grep -v CERTIFICATE | tr -d '\n')
+  trust=$(govc sso.service.ls -json -t sso:sts | jq -r .[].ServiceEndpoints[].SslTrust[0])
+  assert_equal "$cert" "$trust"
+
   govc sso.service.ls -t cs.identity | grep com.vmware.cis | grep -v https:
   govc sso.service.ls -t cs.identity -l | grep https:
   govc sso.service.ls -p com.vmware.cis -t cs.identity -P wsTrust -T com.vmware.cis.cs.identity.sso -l | grep wsTrust
diff --git a/lookup/simulator/simulator_test.go b/lookup/simulator/simulator_test.go
index 39092aa1..ded5bc82 100644
--- a/lookup/simulator/simulator_test.go
+++ b/lookup/simulator/simulator_test.go
@@ -136,7 +136,7 @@ func TestClient(t *testing.T) {
 			t.Fatal(err)
 		}
 
-		if len(info) != 2 {
+		if len(info) != 3 {
 			t.Errorf("len=%d", len(info))
 		}
 	}
diff --git a/simulator/event_manager_test.go b/simulator/event_manager_test.go
index d1d87182..3699e40d 100644
--- a/simulator/event_manager_test.go
+++ b/simulator/event_manager_test.go
@@ -168,9 +168,9 @@ func TestEventManagerRead(t *testing.T) {
 	}
 	nevents := len(page)
 	tests := []struct {
-		max   int
-		reset bool
-		read  func(context.Context, int32) ([]types.BaseEvent, error)
+		max    int
+		rewind bool
+		read   func(context.Context, int32) ([]types.BaseEvent, error)
 	}{
 		{nevents, true, c.ReadNextEvents},
 		{nevents / 3, true, c.ReadNextEvents},
@@ -204,10 +204,38 @@ func TestEventManagerRead(t *testing.T) {
 		if count < len(page) {
 			t.Errorf("expected at least %d events, got: %d", len(page), count)
 		}
-		if test.reset {
+		if test.rewind {
 			if err = c.Rewind(ctx); err != nil {
 				t.Error(err)
 			}
 		}
 	}
+
+	// after Reset() we should only get events via ReadPreviousEvents
+	if err = c.Reset(ctx); err != nil {
+		t.Fatal(err)
+	}
+
+	events, err := c.ReadNextEvents(ctx, int32(nevents))
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(events) != 0 {
+		t.Errorf("expected 0 events, got %d", len(events))
+	}
+
+	count := 0
+	for {
+		events, err = c.ReadPreviousEvents(ctx, 3)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if len(events) == 0 {
+			break
+		}
+		count += len(events)
+	}
+	if nevents != count {
+		t.Errorf("expected %d events, got %d", nevents, count)
+	}
 }