Repository: vmware/govmomi
Pull request #2328 (2021-03-24 22:31:20)

Add chainable RoundTripper support to endpoint clients
Follow up to #2263 , the new '-verbose' flag depends on soap.RoundTripper chaining,
which was only supported by the vim25.Client.  This change enables chaining for all
other endpoint clients.


Patch:
diff --git a/cns/client.go b/cns/client.go
index 575a6e57..124d57f1 100644
--- a/cns/client.go
+++ b/cns/client.go
@@ -47,8 +47,11 @@ var (
 )
 
 type Client struct {
-	vim25Client   *vim25.Client
-	serviceClient *soap.Client
+	*soap.Client
+
+	RoundTripper soap.RoundTripper
+
+	vim25Client *vim25.Client
 }
 
 // NewClient creates a new CNS client
@@ -56,7 +59,12 @@ func NewClient(ctx context.Context, c *vim25.Client) (*Client, error) {
 	sc := c.Client.NewServiceClient(Path, Namespace)
 	sc.Namespace = c.Namespace
 	sc.Version = c.Version
-	return &Client{c, sc}, nil
+	return &Client{sc, sc, c}, nil
+}
+
+// RoundTrip dispatches to the RoundTripper field.
+func (c *Client) RoundTrip(ctx context.Context, req, res soap.HasFault) error {
+	return c.RoundTripper.RoundTrip(ctx, req, res)
 }
 
 // CreateVolume calls the CNS create API.
@@ -66,7 +74,7 @@ func (c *Client) CreateVolume(ctx context.Context, createSpecList []cnstypes.Cns
 		This:        CnsVolumeManagerInstance,
 		CreateSpecs: createSpecList,
 	}
-	res, err := methods.CnsCreateVolume(ctx, c.serviceClient, &req)
+	res, err := methods.CnsCreateVolume(ctx, c, &req)
 	if err != nil {
 		return nil, err
 	}
@@ -80,7 +88,7 @@ func (c *Client) UpdateVolumeMetadata(ctx context.Context, updateSpecList []cnst
 		This:        CnsVolumeManagerInstance,
 		UpdateSpecs: updateSpecList,
 	}
-	res, err := methods.CnsUpdateVolumeMetadata(ctx, c.serviceClient, &req)
+	res, err := methods.CnsUpdateVolumeMetadata(ctx, c, &req)
 	if err != nil {
 		return nil, err
 	}
@@ -94,7 +102,7 @@ func (c *Client) DeleteVolume(ctx context.Context, volumeIDList []cnstypes.CnsVo
 		VolumeIds:  volumeIDList,
 		DeleteDisk: deleteDisk,
 	}
-	res, err := methods.CnsDeleteVolume(ctx, c.serviceClient, &req)
+	res, err := methods.CnsDeleteVolume(ctx, c, &req)
 	if err != nil {
 		return nil, err
 	}
@@ -107,7 +115,7 @@ func (c *Client) ExtendVolume(ctx context.Context, extendSpecList []cnstypes.Cns
 		This:        CnsVolumeManagerInstance,
 		ExtendSpecs: extendSpecList,
 	}
-	res, err := methods.CnsExtendVolume(ctx, c.serviceClient, &req)
+	res, err := methods.CnsExtendVolume(ctx, c, &req)
 	if err != nil {
 		return nil, err
 	}
@@ -120,7 +128,7 @@ func (c *Client) AttachVolume(ctx context.Context, attachSpecList []cnstypes.Cns
 		This:        CnsVolumeManagerInstance,
 		AttachSpecs: attachSpecList,
 	}
-	res, err := methods.CnsAttachVolume(ctx, c.serviceClient, &req)
+	res, err := methods.CnsAttachVolume(ctx, c, &req)
 	if err != nil {
 		return nil, err
 	}
@@ -133,7 +141,7 @@ func (c *Client) DetachVolume(ctx context.Context, detachSpecList []cnstypes.Cns
 		This:        CnsVolumeManagerInstance,
 		DetachSpecs: detachSpecList,
 	}
-	res, err := methods.CnsDetachVolume(ctx, c.serviceClient, &req)
+	res, err := methods.CnsDetachVolume(ctx, c, &req)
 	if err != nil {
 		return nil, err
 	}
@@ -146,7 +154,7 @@ func (c *Client) QueryVolume(ctx context.Context, queryFilter cnstypes.CnsQueryF
 		This:   CnsVolumeManagerInstance,
 		Filter: queryFilter,
 	}
-	res, err := methods.CnsQueryVolume(ctx, c.serviceClient, &req)
+	res, err := methods.CnsQueryVolume(ctx, c, &req)
 	if err != nil {
 		return nil, err
 	}
@@ -160,7 +168,7 @@ func (c *Client) QueryVolumeInfo(ctx context.Context, volumeIDList []cnstypes.Cn
 		This:      CnsVolumeManagerInstance,
 		VolumeIds: volumeIDList,
 	}
-	res, err := methods.CnsQueryVolumeInfo(ctx, c.serviceClient, &req)
+	res, err := methods.CnsQueryVolumeInfo(ctx, c, &req)
 	if err != nil {
 		return nil, err
 	}
@@ -174,7 +182,7 @@ func (c *Client) QueryAllVolume(ctx context.Context, queryFilter cnstypes.CnsQue
 		Filter:    queryFilter,
 		Selection: querySelection,
 	}
-	res, err := methods.CnsQueryAllVolume(ctx, c.serviceClient, &req)
+	res, err := methods.CnsQueryAllVolume(ctx, c, &req)
 	if err != nil {
 		return nil, err
 	}
@@ -187,7 +195,7 @@ func (c *Client) RelocateVolume(ctx context.Context, relocateSpecs ...cnstypes.B
 		This:          CnsVolumeManagerInstance,
 		RelocateSpecs: relocateSpecs,
 	}
-	res, err := methods.CnsRelocateVolume(ctx, c.serviceClient, &req)
+	res, err := methods.CnsRelocateVolume(ctx, c, &req)
 	if err != nil {
 		return nil, err
 	}
@@ -200,7 +208,7 @@ func (c *Client) ConfigureVolumeACLs(ctx context.Context, aclConfigSpecs ...cnst
 		This:           CnsVolumeManagerInstance,
 		ACLConfigSpecs: aclConfigSpecs,
 	}
-	res, err := methods.CnsConfigureVolumeACLs(ctx, c.serviceClient, &req)
+	res, err := methods.CnsConfigureVolumeACLs(ctx, c, &req)
 	if err != nil {
 		return nil, err
 	}
diff --git a/cns/cns_util.go b/cns/cns_util.go
index bffbe476..dcaed9e2 100644
--- a/cns/cns_util.go
+++ b/cns/cns_util.go
@@ -71,7 +71,7 @@ func GetTaskResultArray(ctx context.Context, taskInfo *vim25types.TaskInfo) ([]c
 // dropUnknownCreateSpecElements helps drop newly added elements in the CnsVolumeCreateSpec, which are not known to the prior vSphere releases
 func dropUnknownCreateSpecElements(c *Client, createSpecList []cnstypes.CnsVolumeCreateSpec) []cnstypes.CnsVolumeCreateSpec {
 	updatedcreateSpecList := make([]cnstypes.CnsVolumeCreateSpec, 0, len(createSpecList))
-	switch c.serviceClient.Version {
+	switch c.Version {
 	case ReleaseVSAN67u3:
 		// Dropping optional fields not known to vSAN 6.7U3
 		for _, createSpec := range createSpecList {
@@ -130,7 +130,7 @@ func dropUnknownCreateSpecElements(c *Client, createSpecList []cnstypes.CnsVolum
 // dropUnknownVolumeMetadataUpdateSpecElements helps drop newly added elements in the CnsVolumeMetadataUpdateSpec, which are not known to the prior vSphere releases
 func dropUnknownVolumeMetadataUpdateSpecElements(c *Client, updateSpecList []cnstypes.CnsVolumeMetadataUpdateSpec) []cnstypes.CnsVolumeMetadataUpdateSpec {
 	// Dropping optional fields not known to vSAN 6.7U3
-	if c.serviceClient.Version == ReleaseVSAN67u3 {
+	if c.Version == ReleaseVSAN67u3 {
 		updatedUpdateSpecList := make([]cnstypes.CnsVolumeMetadataUpdateSpec, 0, len(updateSpecList))
 		for _, updateSpec := range updateSpecList {
 			updateSpec.Metadata.ContainerCluster.ClusterFlavor = ""
@@ -147,7 +147,7 @@ func dropUnknownVolumeMetadataUpdateSpecElements(c *Client, updateSpecList []cns
 			updatedUpdateSpecList = append(updatedUpdateSpecList, updateSpec)
 		}
 		updateSpecList = updatedUpdateSpecList
-	} else if c.serviceClient.Version == ReleaseVSAN70 || c.serviceClient.Version == ReleaseVSAN70u1 {
+	} else if c.Version == ReleaseVSAN70 || c.Version == ReleaseVSAN70u1 {
 		updatedUpdateSpecList := make([]cnstypes.CnsVolumeMetadataUpdateSpec, 0, len(updateSpecList))
 		for _, updateSpec := range updateSpecList {
 			updateSpec.Metadata.ContainerCluster.ClusterDistribution = ""
diff --git a/govc/flags/client.go b/govc/flags/client.go
index 94efddb7..c19a70b1 100644
--- a/govc/flags/client.go
+++ b/govc/flags/client.go
@@ -30,6 +30,8 @@ import (
 	"syscall"
 	"time"
 
+	"github.com/vmware/govmomi/cns"
+	"github.com/vmware/govmomi/pbm"
 	"github.com/vmware/govmomi/session"
 	"github.com/vmware/govmomi/session/cache"
 	"github.com/vmware/govmomi/session/keepalive"
@@ -406,6 +408,38 @@ func (flag *ClientFlag) RestClient() (*rest.Client, error) {
 	return flag.restClient, nil
 }
 
+func (flag *ClientFlag) PbmClient() (*pbm.Client, error) {
+	vc, err := flag.Client()
+	if err != nil {
+		return nil, err
+	}
+	c, err := pbm.NewClient(context.Background(), vc)
+	if err != nil {
+		return nil, err
+	}
+
+	c.RoundTripper = flag.RoundTripper(c.Client)
+
+	return c, nil
+}
+
+func (flag *ClientFlag) CnsClient() (*cns.Client, error) {
+	vc, err := flag.Client()
+	if err != nil {
+		return nil, err
+	}
+	_ = vc.UseServiceVersion("vsan")
+
+	c, err := cns.NewClient(context.Background(), vc)
+	if err != nil {
+		return nil, err
+	}
+
+	c.RoundTripper = flag.RoundTripper(c.Client)
+
+	return c, nil
+}
+
 func (flag *ClientFlag) KeepAlive(client cache.Client) {
 	switch c := client.(type) {
 	case *vim25.Client:
diff --git a/govc/flags/debug.go b/govc/flags/debug.go
index 418fb3e7..7ecd1a97 100644
--- a/govc/flags/debug.go
+++ b/govc/flags/debug.go
@@ -295,6 +295,12 @@ func (*verbose) str(val reflect.Value) string {
 		p = fmt.Sprintf("%s", pval)
 	case []string:
 		p = fmt.Sprintf("%v", pval)
+	case []types.ManagedObjectReference:
+		refs := make([]string, len(pval))
+		for i := range pval {
+			refs[i] = pval[i].Value
+		}
+		p = fmt.Sprintf("%v", refs)
 	default:
 		return ""
 	}
@@ -382,8 +388,13 @@ func (v *verbose) table(vals []string) {
 
 func (v *verbose) RoundTrip(ctx context.Context, req, res soap.HasFault) error {
 	vreq := reflect.ValueOf(req).Elem().FieldByName("Req").Elem()
-	this := vreq.Field(0).Interface().(types.ManagedObjectReference)
-	param := []string{v.mor(this)}
+	param := []string{""}
+	switch f := vreq.Field(0).Interface().(type) {
+	case types.ManagedObjectReference:
+		param[0] = v.mor(f)
+	default:
+		param[0] = fmt.Sprintf("%v", f)
+	}
 
 	for i := 1; i < vreq.NumField(); i++ {
 		val := vreq.Field(i)
diff --git a/govc/namespace/cluster/enable.go b/govc/namespace/cluster/enable.go
index 46d39f2e..b67b6722 100644
--- a/govc/namespace/cluster/enable.go
+++ b/govc/namespace/cluster/enable.go
@@ -26,7 +26,6 @@ import (
 	"github.com/vmware/govmomi/govc/cli"
 	"github.com/vmware/govmomi/govc/flags"
 	"github.com/vmware/govmomi/govc/storage/policy"
-	"github.com/vmware/govmomi/pbm"
 	"github.com/vmware/govmomi/vapi/namespace"
 )
 
@@ -195,12 +194,7 @@ func (cmd *enableCluster) Run(ctx context.Context, f *flag.FlagSet) error {
 	}
 
 	// Storage policy object references lookup
-	vc, err := cmd.Client()
-	if err != nil {
-		return fmt.Errorf("error creating soap client: %s", err)
-	}
-
-	pbmc, err := pbm.NewClient(ctx, vc)
+	pbmc, err := cmd.PbmClient()
 	if err != nil {
 		return fmt.Errorf("error creating client for storage policy lookup: %s", err)
 	}
diff --git a/govc/session/login.go b/govc/session/login.go
index 102dd263..2d0bbeca 100644
--- a/govc/session/login.go
+++ b/govc/session/login.go
@@ -170,6 +170,7 @@ func (cmd *login) issueToken(ctx context.Context, vc *vim25.Client) (string, err
 	if err != nil {
 		return "", err
 	}
+	c.RoundTripper = cmd.RoundTripper(c.Client)
 
 	req := sts.TokenRequest{
 		Certificate: c.Certificate(),
diff --git a/govc/sso/client.go b/govc/sso/client.go
index 57b32bbb..437df468 100644
--- a/govc/sso/client.go
+++ b/govc/sso/client.go
@@ -37,6 +37,7 @@ func WithClient(ctx context.Context, cmd *flags.ClientFlag, f func(*ssoadmin.Cli
 	if err != nil {
 		return err
 	}
+	c.RoundTripper = cmd.RoundTripper(c.Client)
 
 	// 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
diff --git a/govc/sso/service/ls.go b/govc/sso/service/ls.go
index 84f4ce6b..fa5092b4 100644
--- a/govc/sso/service/ls.go
+++ b/govc/sso/service/ls.go
@@ -143,6 +143,7 @@ func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error {
 	if err != nil {
 		return err
 	}
+	c.RoundTripper = cmd.RoundTripper(c.Client)
 
 	info, err := c.List(ctx, &cmd.LookupServiceRegistrationFilter)
 	if err != nil {
diff --git a/govc/storage/policy/create.go b/govc/storage/policy/create.go
index 3942c2aa..ed4195bc 100644
--- a/govc/storage/policy/create.go
+++ b/govc/storage/policy/create.go
@@ -23,7 +23,6 @@ import (
 
 	"github.com/vmware/govmomi/govc/cli"
 	"github.com/vmware/govmomi/govc/flags"
-	"github.com/vmware/govmomi/pbm"
 	"github.com/vmware/govmomi/pbm/types"
 	vim "github.com/vmware/govmomi/vim25/types"
 )
@@ -95,12 +94,7 @@ func (cmd *create) Run(ctx context.Context, f *flag.FlagSet) error {
 		}},
 	}
 
-	vc, err := cmd.Client()
-	if err != nil {
-		return err
-	}
-
-	c, err := pbm.NewClient(ctx, vc)
+	c, err := cmd.PbmClient()
 	if err != nil {
 		return err
 	}
diff --git a/govc/storage/policy/info.go b/govc/storage/policy/info.go
index fc2db5d5..b43903fc 100644
--- a/govc/storage/policy/info.go
+++ b/govc/storage/policy/info.go
@@ -27,7 +27,6 @@ import (
 
 	"github.com/vmware/govmomi/govc/cli"
 	"github.com/vmware/govmomi/govc/flags"
-	"github.com/vmware/govmomi/pbm"
 	"github.com/vmware/govmomi/pbm/types"
 	"github.com/vmware/govmomi/property"
 	"github.com/vmware/govmomi/view"
@@ -116,7 +115,7 @@ func (cmd *info) Run(ctx context.Context, f *flag.FlagSet) error {
 		return err
 	}
 
-	c, err := pbm.NewClient(ctx, vc)
+	c, err := cmd.PbmClient()
 	if err != nil {
 		return err
 	}
diff --git a/govc/storage/policy/ls.go b/govc/storage/policy/ls.go
index 7a633213..f914a53c 100644
--- a/govc/storage/policy/ls.go
+++ b/govc/storage/policy/ls.go
@@ -124,12 +124,7 @@ func (r *lsResult) Write(w io.Writer) error {
 }
 
 func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error {
-	vc, err := cmd.Client()
-	if err != nil {
-		return err
-	}
-
-	c, err := pbm.NewClient(ctx, vc)
+	c, err := cmd.PbmClient()
 	if err != nil {
 		return err
 	}
diff --git a/govc/storage/policy/rm.go b/govc/storage/policy/rm.go
index 891375fe..c1f82aae 100644
--- a/govc/storage/policy/rm.go
+++ b/govc/storage/policy/rm.go
@@ -23,7 +23,6 @@ import (
 
 	"github.com/vmware/govmomi/govc/cli"
 	"github.com/vmware/govmomi/govc/flags"
-	"github.com/vmware/govmomi/pbm"
 	"github.com/vmware/govmomi/pbm/types"
 )
 
@@ -57,12 +56,7 @@ func (cmd *rm) Run(ctx context.Context, f *flag.FlagSet) error {
 		return flag.ErrHelp
 	}
 
-	vc, err := cmd.Client()
-	if err != nil {
-		return err
-	}
-
-	c, err := pbm.NewClient(ctx, vc)
+	c, err := cmd.PbmClient()
 	if err != nil {
 		return err
 	}
diff --git a/govc/volume/ls.go b/govc/volume/ls.go
index f12c8685..03722d59 100644
--- a/govc/volume/ls.go
+++ b/govc/volume/ls.go
@@ -24,7 +24,6 @@ import (
 	"log"
 	"text/tabwriter"
 
-	"github.com/vmware/govmomi/cns"
 	"github.com/vmware/govmomi/cns/types"
 	"github.com/vmware/govmomi/govc/cli"
 	"github.com/vmware/govmomi/govc/flags"
@@ -136,12 +135,6 @@ func (r *lsWriter) Write(w io.Writer) error {
 }
 
 func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error {
-	vc, err := cmd.Client()
-	if err != nil {
-		return err
-	}
-	_ = vc.UseServiceVersion("vsan")
-
 	ds, err := cmd.DatastoreIfSpecified()
 	if err != nil {
 		return err
@@ -151,7 +144,7 @@ func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error {
 		cmd.Datastores = []vim.ManagedObjectReference{ds.Reference()}
 	}
 
-	c, err := cns.NewClient(ctx, vc)
+	c, err := cmd.CnsClient()
 	if err != nil {
 		return err
 	}
diff --git a/govc/volume/rm.go b/govc/volume/rm.go
index 15796b07..0657d6ad 100644
--- a/govc/volume/rm.go
+++ b/govc/volume/rm.go
@@ -21,7 +21,6 @@ import (
 	"errors"
 	"flag"
 
-	"github.com/vmware/govmomi/cns"
 	"github.com/vmware/govmomi/cns/types"
 	"github.com/vmware/govmomi/govc/cli"
 	"github.com/vmware/govmomi/govc/flags"
@@ -56,13 +55,7 @@ Examples:
 }
 
 func (cmd *rm) Run(ctx context.Context, f *flag.FlagSet) error {
-	vc, err := cmd.Client()
-	if err != nil {
-		return err
-	}
-	_ = vc.UseServiceVersion("vsan")
-
-	c, err := cns.NewClient(ctx, vc)
+	c, err := cmd.CnsClient()
 	if err != nil {
 		return err
 	}
diff --git a/lookup/client.go b/lookup/client.go
index 6a3fb38f..b3c19846 100644
--- a/lookup/client.go
+++ b/lookup/client.go
@@ -48,6 +48,8 @@ var (
 type Client struct {
 	*soap.Client
 
+	RoundTripper soap.RoundTripper
+
 	ServiceContent types.LookupServiceContent
 }
 
@@ -79,7 +81,12 @@ func NewClient(ctx context.Context, c *vim25.Client) (*Client, error) {
 		return nil, err
 	}
 
-	return &Client{sc, res.Returnval}, nil
+	return &Client{sc, sc, res.Returnval}, nil
+}
+
+// RoundTrip dispatches to the RoundTripper field.
+func (c *Client) RoundTrip(ctx context.Context, req, res soap.HasFault) error {
+	return c.RoundTripper.RoundTrip(ctx, req, res)
 }
 
 func (c *Client) List(ctx context.Context, filter *types.LookupServiceRegistrationFilter) ([]types.LookupServiceRegistrationInfo, error) {
diff --git a/pbm/client.go b/pbm/client.go
index 06a16982..ba2531ec 100644
--- a/pbm/client.go
+++ b/pbm/client.go
@@ -43,6 +43,8 @@ type Client struct {
 	*soap.Client
 
 	ServiceContent types.PbmServiceInstanceContent
+
+	RoundTripper soap.RoundTripper
 }
 
 func NewClient(ctx context.Context, c *vim25.Client) (*Client, error) {
@@ -57,7 +59,12 @@ func NewClient(ctx context.Context, c *vim25.Client) (*Client, error) {
 		return nil, err
 	}
 
-	return &Client{sc, res.Returnval}, nil
+	return &Client{sc, res.Returnval, sc}, nil
+}
+
+// RoundTrip dispatches to the RoundTripper field.
+func (c *Client) RoundTrip(ctx context.Context, req, res soap.HasFault) error {
+	return c.RoundTripper.RoundTrip(ctx, req, res)
 }
 
 func (c *Client) QueryProfile(ctx context.Context, rtype types.PbmProfileResourceType, category string) ([]types.PbmProfileId, error) {
diff --git a/ssoadmin/client.go b/ssoadmin/client.go
index b50d8a09..0d6729c1 100644
--- a/ssoadmin/client.go
+++ b/ssoadmin/client.go
@@ -47,6 +47,7 @@ var (
 type Client struct {
 	*soap.Client
 
+	RoundTripper   soap.RoundTripper
 	ServiceContent types.AdminServiceContent
 	GroupCheck     types.GroupcheckServiceContent
 	Domain         string
@@ -117,6 +118,11 @@ func NewClient(ctx context.Context, c *vim25.Client) (*Client, error) {
 	return admin, nil
 }
 
+// RoundTrip dispatches to the RoundTripper field.
+func (c *Client) RoundTrip(ctx context.Context, req, res soap.HasFault) error {
+	return c.RoundTripper.RoundTrip(ctx, req, res)
+}
+
 func (c *Client) parseID(name string) types.PrincipalId {
 	p := strings.SplitN(name, "@", 2)
 	id := types.PrincipalId{Name: p[0]}
diff --git a/sts/client.go b/sts/client.go
index 7a466a88..bcd346b7 100644
--- a/sts/client.go
+++ b/sts/client.go
@@ -38,6 +38,8 @@ const (
 // Client is a soap.Client targeting the STS (Secure Token Service) API endpoint.
 type Client struct {
 	*soap.Client
+
+	RoundTripper soap.RoundTripper
 }
 
 // NewClient returns a client targeting the STS API endpoint.
@@ -59,7 +61,12 @@ func NewClient(ctx context.Context, c *vim25.Client) (*Client, error) {
 	url := lookup.EndpointURL(ctx, c, Path, filter)
 	sc := c.Client.NewServiceClient(url, Namespace)
 
-	return &Client{sc}, nil
+	return &Client{sc, sc}, nil
+}
+
+// RoundTrip dispatches to the RoundTripper field.
+func (c *Client) RoundTrip(ctx context.Context, req, res soap.HasFault) error {
+	return c.RoundTripper.RoundTrip(ctx, req, res)
 }
 
 // TokenRequest parameters for issuing a SAML token.
diff --git a/vsan/client.go b/vsan/client.go
index 8da6926b..348eeccd 100644
--- a/vsan/client.go
+++ b/vsan/client.go
@@ -61,13 +61,18 @@ var (
 type Client struct {
 	*soap.Client
 
-	Vim25Client *vim25.Client
+	RoundTripper soap.RoundTripper
 }
 
 // NewClient creates a new VsanHealth client
 func NewClient(ctx context.Context, c *vim25.Client) (*Client, error) {
 	sc := c.Client.NewServiceClient(Path, Namespace)
-	return &Client{sc, c}, nil
+	return &Client{sc, sc}, nil
+}
+
+// RoundTrip dispatches to the RoundTripper field.
+func (c *Client) RoundTrip(ctx context.Context, req, res soap.HasFault) error {
+	return c.RoundTripper.RoundTrip(ctx, req, res)
 }
 
 // VsanClusterGetConfig calls the Vsan health's VsanClusterGetConfig API.


Test patch:
diff --git a/cns/client_test.go b/cns/client_test.go
index 7f25bf6b..18984ba8 100644
--- a/cns/client_test.go
+++ b/cns/client_test.go
@@ -159,7 +159,7 @@ func TestClient(t *testing.T) {
 	t.Logf("volumeCreateResult %+v", volumeCreateResult)
 	t.Logf("Volume created sucessfully. volumeId: %s", volumeId)
 
-	if cnsClient.serviceClient.Version != ReleaseVSAN67u3 {
+	if cnsClient.Version != ReleaseVSAN67u3 {
 		// Test creating static volume using existing CNS volume should fail
 		var staticCnsVolumeCreateSpecList []cnstypes.CnsVolumeCreateSpec
 		staticCnsVolumeCreateSpec := cnstypes.CnsVolumeCreateSpec{
@@ -226,7 +226,7 @@ func TestClient(t *testing.T) {
 	// Test QueryVolumeInfo API
 	// QueryVolumeInfo is not supported on ReleaseVSAN67u3 and ReleaseVSAN70
 	// This API is available on vSphere 7.0u1 onward
-	if cnsClient.serviceClient.Version != ReleaseVSAN67u3 && cnsClient.serviceClient.Version != ReleaseVSAN70 {
+	if cnsClient.Version != ReleaseVSAN67u3 && cnsClient.Version != ReleaseVSAN70 {
 		t.Logf("Calling QueryVolumeInfo using: %+v", pretty.Sprint(volumeIDList))
 		queryVolumeInfoTask, err := cnsClient.QueryVolumeInfo(ctx, volumeIDList)
 		if err != nil {
@@ -259,7 +259,7 @@ func TestClient(t *testing.T) {
 	// Test Relocate API
 	// Relocate API is not supported on ReleaseVSAN67u3 and ReleaseVSAN70
 	// This API is available on vSphere 7.0u1 onward
-	if cnsClient.serviceClient.Version != ReleaseVSAN67u3 && cnsClient.serviceClient.Version != ReleaseVSAN70 && datastoreForMigration != "" {
+	if cnsClient.Version != ReleaseVSAN67u3 && cnsClient.Version != ReleaseVSAN70 && datastoreForMigration != "" {
 		migrationDS, err := finder.Datastore(ctx, datastoreForMigration)
 		if err != nil {
 			t.Fatal(err)
@@ -648,7 +648,7 @@ func TestClient(t *testing.T) {
 	}
 	t.Logf("Volume: %q deleted sucessfully", volumeId)
 
-	if run_fileshare_tests == "true" && cnsClient.serviceClient.Version != ReleaseVSAN67u3 {
+	if run_fileshare_tests == "true" && cnsClient.Version != ReleaseVSAN67u3 {
 		// Test creating vSAN file-share Volume
 		var cnsFileVolumeCreateSpecList []cnstypes.CnsVolumeCreateSpec
 		vSANFileCreateSpec := &cnstypes.CnsVSANFileCreateSpec{
@@ -827,7 +827,7 @@ func TestClient(t *testing.T) {
 		}
 		t.Logf("fileshare volume:%q deleted sucessfully", filevolumeId)
 	}
-	if backingDiskURLPath != "" && cnsClient.serviceClient.Version != ReleaseVSAN67u3 && cnsClient.serviceClient.Version != ReleaseVSAN70 {
+	if backingDiskURLPath != "" && cnsClient.Version != ReleaseVSAN67u3 && cnsClient.Version != ReleaseVSAN70 {
 		// Test CreateVolume API with existing VMDK
 		var cnsVolumeCreateSpecList []cnstypes.CnsVolumeCreateSpec
 		cnsVolumeCreateSpec := cnstypes.CnsVolumeCreateSpec{
diff --git a/govc/test/cli.bats b/govc/test/cli.bats
index 3f033360..b3455868 100755
--- a/govc/test/cli.bats
+++ b/govc/test/cli.bats
@@ -272,4 +272,16 @@ load test_helper
 
   run govc metric.sample -verbose /DC0/host/DC0_C0 cpu.usage.average
   assert_success
+
+  run govc session.login -verbose -issue # sts.Client
+  assert_success
+
+  run govc sso.service.ls -verbose # lookup.Client
+  assert_success
+
+  run govc storage.policy.ls -verbose # pbm.Client
+  assert_success
+
+  run govc volume.ls -verbose # cns.Client
+  assert_success
 }
diff --git a/vsan/client_test.go b/vsan/client_test.go
index 47c76e4c..68408d14 100644
--- a/vsan/client_test.go
+++ b/vsan/client_test.go
@@ -51,7 +51,7 @@ func TestClient(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	finder := find.NewFinder(vsanHealthClient.Vim25Client, false)
+	finder := find.NewFinder(c.Client, false)
 	dc, err := finder.Datacenter(ctx, datacenter)
 	if err != nil {
 		t.Fatal(err)
@@ -125,7 +125,7 @@ func TestVsanQueryObjectIdentities(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	finder := find.NewFinder(vsanHealthClient.Vim25Client, false)
+	finder := find.NewFinder(c.Client, false)
 	dc, err := finder.Datacenter(ctx, datacenter)
 	if err != nil {
 		t.Fatal(err)