Skip to content

Commit 5041208

Browse files
authored
Refactor tag manager rest client (kubernetes-sigs#3412)
Force re-login of rest session in case of expired session
1 parent da03502 commit 5041208

10 files changed

Lines changed: 126 additions & 113 deletions

File tree

hack/release.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,9 @@ function build_driver_images_linux() {
147147

148148
function build_syncer_image_linux() {
149149
echo "building ${SYNCER_IMAGE_NAME}:${VERSION} for linux"
150-
docker buildx build --platform "linux/$ARCH"\
150+
docker buildx build \
151+
--platform "linux/$ARCH" \
152+
--output "${LINUX_IMAGE_OUTPUT}" \
151153
-f images/syncer/Dockerfile \
152154
-t "${SYNCER_IMAGE_NAME}":"${VERSION}" \
153155
--build-arg "VERSION=${VERSION}" \
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package vsphere
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/vmware/govmomi/vapi/tags"
8+
"sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/service/logger"
9+
)
10+
11+
// GetTagManager returns tagManager connected to given VirtualCenter.
12+
func (vc *VirtualCenter) GetTagManager(ctx context.Context) (*tags.Manager, error) {
13+
log := logger.GetLogger(ctx)
14+
// Validate input.
15+
if vc == nil || vc.Client == nil || vc.Client.Client == nil {
16+
return nil, fmt.Errorf("vCenter not initialized")
17+
}
18+
19+
if err := vc.Connect(ctx); err != nil {
20+
return nil, fmt.Errorf("error connecting to VC: %w", err)
21+
}
22+
23+
vc.tagManager = tags.NewManager(vc.RestClient)
24+
if vc.tagManager == nil {
25+
return nil, fmt.Errorf("failed to create a tagManager")
26+
}
27+
log.Infof("New tag manager with useragent '%s'", vc.tagManager.UserAgent)
28+
return vc.tagManager, nil
29+
}

pkg/common/cns-lib/vsphere/utils.go

Lines changed: 0 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,15 @@ package vsphere
22

33
import (
44
"context"
5-
"crypto/tls"
6-
"encoding/pem"
75
"errors"
86
"fmt"
9-
"net/url"
107
"reflect"
118
"strconv"
129
"strings"
1310

1411
"github.com/davecgh/go-spew/spew"
1512
"github.com/vmware/govmomi/cns"
1613
cnstypes "github.com/vmware/govmomi/cns/types"
17-
"github.com/vmware/govmomi/sts"
18-
"github.com/vmware/govmomi/vapi/rest"
19-
"github.com/vmware/govmomi/vapi/tags"
20-
"github.com/vmware/govmomi/vim25"
2114
"github.com/vmware/govmomi/vim25/soap"
2215
"github.com/vmware/govmomi/vim25/types"
2316
"sigs.k8s.io/vsphere-csi-driver/v3/pkg/common/config"
@@ -304,62 +297,6 @@ func CompareKubernetesMetadata(ctx context.Context, k8sMetaData *cnstypes.CnsKub
304297
return labelsMatch
305298
}
306299

307-
// Signer decodes the certificate and private key and returns SAML token needed
308-
// for authentication.
309-
func signer(ctx context.Context, client *vim25.Client, username string, password string) (*sts.Signer, error) {
310-
pemBlock, _ := pem.Decode([]byte(username))
311-
if pemBlock == nil {
312-
return nil, nil
313-
}
314-
certificate, err := tls.X509KeyPair([]byte(username), []byte(password))
315-
if err != nil {
316-
return nil, fmt.Errorf("failed to load X509 key pair. Error: %+v", err)
317-
}
318-
tokens, err := sts.NewClient(ctx, client)
319-
if err != nil {
320-
return nil, fmt.Errorf("failed to create STS client. err: %+v", err)
321-
}
322-
req := sts.TokenRequest{
323-
Certificate: &certificate,
324-
Delegatable: true,
325-
}
326-
signer, err := tokens.Issue(ctx, req)
327-
if err != nil {
328-
return nil, fmt.Errorf("failed to issue SAML token. err: %+v", err)
329-
}
330-
return signer, nil
331-
}
332-
333-
// GetTagManager returns tagManager connected to given VirtualCenter.
334-
func GetTagManager(ctx context.Context, vc *VirtualCenter) (*tags.Manager, error) {
335-
log := logger.GetLogger(ctx)
336-
// Validate input.
337-
if vc == nil || vc.Client == nil || vc.Client.Client == nil {
338-
return nil, fmt.Errorf("vCenter not initialized")
339-
}
340-
341-
restClient := rest.NewClient(vc.Client.Client)
342-
signer, err := signer(ctx, vc.Client.Client, vc.Config.Username, vc.Config.Password)
343-
if err != nil {
344-
return nil, fmt.Errorf("failed to create the Signer. Error: %v", err)
345-
}
346-
if signer == nil {
347-
user := url.UserPassword(vc.Config.Username, vc.Config.Password)
348-
err = restClient.Login(ctx, user)
349-
} else {
350-
err = restClient.LoginByToken(restClient.WithSigner(ctx, signer))
351-
}
352-
if err != nil {
353-
return nil, fmt.Errorf("failed to login for the rest client. Error: %v", err)
354-
}
355-
tagManager := tags.NewManager(restClient)
356-
if tagManager == nil {
357-
return nil, fmt.Errorf("failed to create a tagManager")
358-
}
359-
log.Infof("New tag manager with useragent '%s'", tagManager.UserAgent)
360-
return tagManager, nil
361-
}
362-
363300
// GetCandidateDatastoresInClusters gets the shared datastores and vSAN-direct
364301
// managed datastores of given VC clusters from GetCandidateDatastoresInCluster and
365302
// returns a map of clusterID -> array of datastores

pkg/common/cns-lib/vsphere/virtualcenter.go

Lines changed: 82 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ import (
3737
"github.com/vmware/govmomi/property"
3838
"github.com/vmware/govmomi/session"
3939
"github.com/vmware/govmomi/sts"
40+
"github.com/vmware/govmomi/vapi/rest"
41+
"github.com/vmware/govmomi/vapi/tags"
4042
"github.com/vmware/govmomi/vim25"
4143
"github.com/vmware/govmomi/vim25/mo"
4244
"github.com/vmware/govmomi/vim25/soap"
@@ -68,6 +70,8 @@ type VirtualCenter struct {
6870
Config *VirtualCenterConfig
6971
// Client represents the govmomi client instance for the connection.
7072
Client *govmomi.Client
73+
// RestClient represents the govmomi rest client
74+
RestClient *rest.Client
7175
// PbmClient represents the govmomi PBM Client instance.
7276
PbmClient *pbm.Client
7377
// CnsClient represents the CNS client instance.
@@ -76,6 +80,9 @@ type VirtualCenter struct {
7680
VsanClient *vsan.Client
7781
// VslmClient represents the Vslm client instance.
7882
VslmClient *vslm.Client
83+
// tagManager represents the tagmanager client instance.
84+
tagManager *tags.Manager
85+
7986
// ClientMutex is used for exclusive connection creation.
8087
ClientMutex *sync.Mutex
8188
}
@@ -151,7 +158,7 @@ type VirtualCenterConfig struct {
151158
}
152159

153160
// NewClient creates a new govmomi Client instance.
154-
func (vc *VirtualCenter) NewClient(ctx context.Context, useragent string) (*govmomi.Client, error) {
161+
func (vc *VirtualCenter) NewClient(ctx context.Context, useragent string) (*govmomi.Client, *rest.Client, error) {
155162
log := logger.GetLogger(ctx)
156163
if vc.Config.Scheme == "" {
157164
vc.Config.Scheme = DefaultScheme
@@ -160,14 +167,14 @@ func (vc *VirtualCenter) NewClient(ctx context.Context, useragent string) (*govm
160167
url, err := soap.ParseURL(net.JoinHostPort(vc.Config.Host, strconv.Itoa(vc.Config.Port)))
161168
if err != nil {
162169
log.Errorf("failed to parse URL %s with err: %v", url, err)
163-
return nil, err
170+
return nil, nil, err
164171
}
165172

166173
soapClient := soap.NewClient(url, vc.Config.Insecure)
167174
if len(vc.Config.CAFile) > 0 && !vc.Config.Insecure {
168175
if err := soapClient.SetRootCAs(vc.Config.CAFile); err != nil {
169176
log.Errorf("failed to load CA file: %v", err)
170-
return nil, err
177+
return nil, nil, err
171178
}
172179
} else if len(vc.Config.Thumbprint) > 0 && !vc.Config.Insecure {
173180
soapClient.SetThumbprint(url.Host, vc.Config.Thumbprint)
@@ -179,36 +186,38 @@ func (vc *VirtualCenter) NewClient(ctx context.Context, useragent string) (*govm
179186
vimClient, err := vim25.NewClient(ctx, soapClient)
180187
if err != nil {
181188
log.Errorf("failed to create new client with err: %v", err)
182-
return nil, err
189+
return nil, nil, err
183190
}
184191
err = vimClient.UseServiceVersion("vsan")
185192
if err != nil && vc.Config.Host != "127.0.0.1" {
186193
// Skipping error for simulator connection for unit tests.
187194
log.Errorf("Failed to set vimClient service version to vsan. err: %v", err)
188-
return nil, err
195+
return nil, nil, err
189196
}
190197
vimClient.UserAgent = useragent
191198
client := &govmomi.Client{
192199
Client: vimClient,
193200
SessionManager: session.NewManager(vimClient),
194201
}
195202

196-
err = vc.login(ctx, client)
203+
restClient := rest.NewClient(client.Client)
204+
205+
err = vc.login(ctx, client, restClient)
197206
if err != nil {
198207
log.Errorf("failed to login to vc. err: %v", err)
199-
return nil, err
208+
return nil, nil, err
200209
}
201210

202211
s, err := client.SessionManager.UserSession(ctx)
203212
if err != nil {
204213
log.Errorf("failed to get UserSession. err: %v", err)
205-
return nil, err
214+
return nil, nil, err
206215
}
207216
// Refer to this issue - https://github.com/vmware/govmomi/issues/2922
208217
// When Session Manager -> UserSession can return nil user session with nil error
209218
// so handling the case for nil session.
210219
if s == nil {
211-
return nil, errors.New("nil session obtained from session manager")
220+
return nil, nil, errors.New("nil session obtained from session manager")
212221
}
213222
log.Infof("New session ID for '%s' = %s", s.UserName, s.Key)
214223

@@ -217,19 +226,22 @@ func (vc *VirtualCenter) NewClient(ctx context.Context, useragent string) (*govm
217226
}
218227
rt := vim25.Retry(client.RoundTripper, vim25.TemporaryNetworkError(vc.Config.RoundTripperCount))
219228
client.RoundTripper = &MetricRoundTripper{"soap", rt}
220-
return client, nil
229+
return client, restClient, nil
221230
}
222231

223232
// login calls SessionManager.LoginByToken if certificate and private key are
224233
// configured. Otherwise, calls SessionManager.Login with user and password.
225-
func (vc *VirtualCenter) login(ctx context.Context, client *govmomi.Client) error {
234+
func (vc *VirtualCenter) login(ctx context.Context, client *govmomi.Client, restClient *rest.Client) error {
226235
log := logger.GetLogger(ctx)
227236
var err error
228237

229238
b, _ := pem.Decode([]byte(vc.Config.Username))
230239
if b == nil {
231-
return client.SessionManager.Login(ctx,
232-
neturl.UserPassword(vc.Config.Username, vc.Config.Password))
240+
if err := client.SessionManager.Login(ctx, neturl.UserPassword(vc.Config.Username, vc.Config.Password)); err != nil {
241+
log.Errorf("error logging soap client: %v", err)
242+
return err
243+
}
244+
return restClient.Login(ctx, neturl.UserPassword(vc.Config.Username, vc.Config.Password))
233245
}
234246

235247
cert, err := tls.X509KeyPair([]byte(vc.Config.Username), []byte(vc.Config.Password))
@@ -246,6 +258,7 @@ func (vc *VirtualCenter) login(ctx context.Context, client *govmomi.Client) erro
246258

247259
req := sts.TokenRequest{
248260
Certificate: &cert,
261+
Delegatable: true,
249262
}
250263

251264
signer, err := tokens.Issue(ctx, req)
@@ -255,7 +268,11 @@ func (vc *VirtualCenter) login(ctx context.Context, client *govmomi.Client) erro
255268
}
256269

257270
header := soap.Header{Security: signer}
258-
return client.SessionManager.LoginByToken(client.Client.WithHeader(ctx, header))
271+
if err := client.SessionManager.LoginByToken(client.Client.WithHeader(ctx, header)); err != nil {
272+
return err
273+
}
274+
275+
return restClient.LoginByToken(restClient.WithSigner(ctx, signer))
259276
}
260277

261278
// Connect establishes a new connection with vSphere with updated credentials.
@@ -279,6 +296,14 @@ func (vc *VirtualCenter) Connect(ctx context.Context) error {
279296
log.Errorf("Could not logout of VC session. Error: %v", logoutErr)
280297
}
281298
}
299+
if vc.RestClient != nil {
300+
logoutErr := vc.RestClient.Logout(ctx)
301+
if logoutErr != nil {
302+
// TODO: On vSphere U3, with a shared session this may return an error as logging
303+
// out from Soap also logs out from rest.
304+
log.Errorf("Could not logout of VC rest session. Error: %v", logoutErr)
305+
}
306+
}
282307
}()
283308
}
284309
return err
@@ -295,21 +320,22 @@ func (vc *VirtualCenter) connect(ctx context.Context) error {
295320
log.Errorf("failed to get useragent for vCenter session. error: %+v", err)
296321
return err
297322
}
298-
if vc.Client == nil {
323+
if vc.Client == nil || vc.RestClient == nil {
299324
if vc.Config.ReloadVCConfigForNewClient {
300325
err = ReadVCConfigs(ctx, vc)
301326
if err != nil {
302327
return err
303328
}
304329
}
305330
log.Infof("VirtualCenter.connect() creating new client")
306-
if vc.Client, err = vc.NewClient(ctx, useragent); err != nil {
331+
if vc.Client, vc.RestClient, err = vc.NewClient(ctx, useragent); err != nil {
307332
log.Errorf("failed to create govmomi client with err: %v", err)
308333
if !vc.Config.Insecure {
309334
log.Errorf("failed to connect to vCenter using CA file: %q", vc.Config.CAFile)
310335
}
311336
return err
312337
}
338+
313339
log.Infof("VirtualCenter.connect() successfully created new client")
314340
return nil
315341
}
@@ -319,10 +345,25 @@ func (vc *VirtualCenter) connect(ctx context.Context) error {
319345
// SessionMgr.UserSession(ctx) retrieves and returns the SessionManager's
320346
// CurrentSession field. Nil is returned if the session is not
321347
// authenticated or timed out.
322-
if userSession, err := sessionMgr.UserSession(ctx); err != nil {
348+
shouldLogin := false
349+
350+
userSession, err := sessionMgr.UserSession(ctx)
351+
if err != nil {
323352
log.Errorf("failed to obtain user session with err: %v", err)
324-
return err
325-
} else if userSession != nil {
353+
// Login again if session is invalid
354+
shouldLogin = true
355+
//return err <- Check if we really want to return error here or follow with re-login
356+
}
357+
358+
restSession, err := vc.RestClient.Session(ctx)
359+
if err != nil {
360+
log.Errorf("failed to obtain rest user session with err: %v", err)
361+
// Login again if session is invalid
362+
shouldLogin = true
363+
}
364+
365+
// No need to re-login
366+
if userSession != nil && restSession != nil && !shouldLogin {
326367
return nil
327368
}
328369

@@ -335,6 +376,14 @@ func (vc *VirtualCenter) connect(ctx context.Context) error {
335376
}
336377
}
337378

379+
if vc.RestClient != nil {
380+
// TODO: On U3 shared session this may return an error, if the Soap logout
381+
// happened correctly, but can be safely ignored
382+
if err := vc.RestClient.Logout(ctx); err != nil {
383+
log.Errorf("failed to logout current rest session. still clearing idle sessions. err: %v", err)
384+
}
385+
}
386+
338387
// If session has expired, create a new instance.
339388
log.Infof("Creating a new client session as the existing one isn't valid or not authenticated")
340389
if vc.Config.ReloadVCConfigForNewClient {
@@ -343,7 +392,7 @@ func (vc *VirtualCenter) connect(ctx context.Context) error {
343392
return err
344393
}
345394
}
346-
if vc.Client, err = vc.NewClient(ctx, useragent); err != nil {
395+
if vc.Client, vc.RestClient, err = vc.NewClient(ctx, useragent); err != nil {
347396
log.Errorf("failed to create govmomi client with err: %v", err)
348397
if !vc.Config.Insecure {
349398
log.Errorf("failed to connect to vCenter using CA file: %q", vc.Config.CAFile)
@@ -382,6 +431,12 @@ func (vc *VirtualCenter) connect(ctx context.Context) error {
382431
}
383432
vc.VsanClient.RoundTripper = &MetricRoundTripper{"vsan", vc.VsanClient.RoundTripper}
384433
}
434+
435+
// Recreate the Tag Manager client if created using timed out Rest client
436+
if vc.tagManager != nil {
437+
vc.tagManager = tags.NewManager(vc.RestClient)
438+
}
439+
385440
return nil
386441
}
387442

@@ -485,7 +540,14 @@ func (vc *VirtualCenter) Disconnect(ctx context.Context) error {
485540
log.Errorf("failed to logout with err: %v", err)
486541
return err
487542
}
543+
544+
// We don't return an error here because logging out from Rest failing
545+
// can happen on VC shared sessions
546+
if err := vc.RestClient.Logout(ctx); err != nil {
547+
log.Errorf("failed to logout rest with err: %v", err)
548+
}
488549
vc.Client = nil
550+
vc.RestClient = nil
489551
return nil
490552
}
491553

0 commit comments

Comments
 (0)