Repository: kubernetes-sigs/cloud-provider-azure
Pull request #2949 (2022-12-15 07:12:18)

Use TimedCache.Get() for read-only resources
Signed-off-by: Zhecheng Li <zhechengli@microsoft.com>

<!--  Thanks for sending a pull request!  Here are some tips for you:

1. If this is your first time, please read our contributor guidelines: https://git.k8s.io/community/contributors/guide/first-contribution.md#your-first-contribution and developer guide https://git.k8s.io/community/contributors/devel/development.md#development-guide
2. Please label this pull request according to what type of issue you are addressing, especially if this is a release targeted pull request. For reference on required PR/issue labels, read here:
https://git.k8s.io/community/contributors/devel/sig-release/release.md#issuepr-kind-label
3. Ensure you have added or ran the appropriate tests for your PR: https://git.k8s.io/community/contributors/devel/sig-testing/testing.md
4. If you want *faster* PR reviews, read how: https://git.k8s.io/community/contributors/guide/pull-requests.md#best-practices-for-faster-reviews
5. If the PR is unfinished, see how to mark it: https://git.k8s.io/community/contributors/guide/pull-requests.md#marking-unfinished-pull-requests
-->

#### What type of PR is this?

<!--
Add one of the following kinds:
/kind bug
/kind cleanup
/kind documentation
/kind feature
/kind design

Optionally add one or more of the following kinds if applicable:
/kind api-change
/kind deprecation
/kind failing-test
/kind flake
/kind regression
-->

#### What this PR does / why we need it:

#### Which issue(s) this PR fixes:
<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
_If PR is about `failing-tests or flakes`, please post the related issues/tests in a comment and do not use `Fixes`_*
-->
Fixes #

#### Special notes for your reviewer:

#### Does this PR introduce a user-facing change?
<!--
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".

For more information on release notes see: https://git.k8s.io/community/contributors/guide/release-notes.md
-->
```release-note

```

#### Additional documentation e.g., KEPs (Kubernetes Enhancement Proposals), usage docs, etc.:

<!--
This section can be blank if this pull request does not require a release note.

When adding links which point to resources within git repositories, like
KEPs or supporting documentation, please reference a specific commit and avoid
linking directly to the master branch. This ensures that links reference a
specific point in time, rather than a document that may change over time.

See here for guidance on getting permanent links to files: https://help.github.com/en/articles/getting-permanent-links-to-files

Please use the following format for linking documentation:
- [KEP]: <link>
- [Usage]: <link>
- [Other doc]: <link>
-->
```docs

```



Patch:
diff --git a/go.mod b/go.mod
index 5093bd7b7..a96bf9fc6 100644
--- a/go.mod
+++ b/go.mod
@@ -13,12 +13,12 @@ require (
 	github.com/fsnotify/fsnotify v1.6.0
 	github.com/go-logr/logr v1.2.3
 	github.com/golang/mock v1.6.0
-	github.com/onsi/ginkgo/v2 v2.6.0
+	github.com/onsi/ginkgo/v2 v2.5.1
 	github.com/onsi/gomega v1.24.1
 	github.com/spf13/cobra v1.6.1
 	github.com/spf13/pflag v1.0.5
 	github.com/stretchr/testify v1.8.1
-	golang.org/x/crypto v0.4.0
+	golang.org/x/crypto v0.1.0
 	golang.org/x/sys v0.3.0
 	golang.org/x/text v0.5.0
 	k8s.io/api v0.26.0
diff --git a/go.sum b/go.sum
index 76598c0e5..80dba01a5 100644
--- a/go.sum
+++ b/go.sum
@@ -294,8 +294,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/onsi/ginkgo/v2 v2.6.0 h1:9t9b9vRUbFq3C4qKFCGkVuq/fIHji802N1nrtkh1mNc=
-github.com/onsi/ginkgo/v2 v2.6.0/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc=
+github.com/onsi/ginkgo/v2 v2.5.1 h1:auzK7OI497k6x4OvWq+TKAcpcSAlod0doAH72oIN0Jw=
+github.com/onsi/ginkgo/v2 v2.5.1/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc=
 github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E=
 github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -422,8 +422,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
-golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
+golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
+golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
diff --git a/pkg/cache/azure_cache.go b/pkg/cache/azure_cache.go
index b9e545bb7..d165c1f5f 100644
--- a/pkg/cache/azure_cache.go
+++ b/pkg/cache/azure_cache.go
@@ -122,8 +122,13 @@ func (t *TimedCache) getInternal(key string) (*AzureCacheEntry, error) {
 	return newEntry, nil
 }
 
-// Get returns the requested item by key with deep copy.
+// Get returns the requested item by key.
 func (t *TimedCache) Get(key string, crt AzureCacheReadType) (interface{}, error) {
+	return t.get(key, crt)
+}
+
+// Get returns the requested item by key with deep copy.
+func (t *TimedCache) GetWithDeepCopy(key string, crt AzureCacheReadType) (interface{}, error) {
 	data, err := t.get(key, crt)
 	copied := deepcopy.Copy(data)
 	return copied, err
diff --git a/pkg/consts/consts.go b/pkg/consts/consts.go
index 80d26c353..04838b5d1 100644
--- a/pkg/consts/consts.go
+++ b/pkg/consts/consts.go
@@ -383,8 +383,6 @@ const (
 	CannotUpdateVMBeingDeletedMessagePrefix = "'Put on Virtual Machine Scale Set VM Instance' is not allowed on Virtual Machine Scale Set"
 	// CannotUpdateVMBeingDeletedMessageSuffix is the suffix of the error message that the request failed due to delete a VM that is being deleted
 	CannotUpdateVMBeingDeletedMessageSuffix = "since it is marked for deletion"
-	// OperationPreemptedErrorCode is the error code returned for vm operation preempted errors
-	OperationPreemptedErrorCode = "OperationPreempted"
 )
 
 // node ipam controller
diff --git a/pkg/nodeipam/node_ipam_controller.go b/pkg/nodeipam/node_ipam_controller.go
index 82e49ca77..b31aa5ed7 100644
--- a/pkg/nodeipam/node_ipam_controller.go
+++ b/pkg/nodeipam/node_ipam_controller.go
@@ -80,7 +80,7 @@ func registerRateLimiterMetric(ownerName string) error {
 		StabilityLevel: metrics.ALPHA,
 	})
 	if err := legacyregistry.Register(metric); err != nil {
-		return fmt.Errorf("error registering rate limiter usage metric: %w", err)
+		return fmt.Errorf("error registering rate limiter usage metric: %v", err)
 	}
 	stopCh := make(chan struct{})
 	rateLimiterMetrics[ownerName] = &rateLimiterMetric{
diff --git a/pkg/provider/azure_controller_common.go b/pkg/provider/azure_controller_common.go
index 149282324..883e30e0c 100644
--- a/pkg/provider/azure_controller_common.go
+++ b/pkg/provider/azure_controller_common.go
@@ -28,7 +28,6 @@ import (
 	"time"
 
 	"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2022-03-01/compute"
-	"github.com/Azure/go-autorest/autorest/azure"
 
 	"k8s.io/apimachinery/pkg/types"
 	kwait "k8s.io/apimachinery/pkg/util/wait"
@@ -39,7 +38,6 @@ import (
 
 	azcache "sigs.k8s.io/cloud-provider-azure/pkg/cache"
 	"sigs.k8s.io/cloud-provider-azure/pkg/consts"
-	"sigs.k8s.io/cloud-provider-azure/pkg/retry"
 )
 
 const (
@@ -56,10 +54,6 @@ const (
 	attachDiskMapKeySuffix = "attachdiskmap"
 	detachDiskMapKeySuffix = "detachdiskmap"
 
-	updateVMRetryDuration = time.Duration(1) * time.Second
-	updateVMRetryFactor   = 3.0
-	updateVMRetrySteps    = 5
-
 	// WriteAcceleratorEnabled support for Azure Write Accelerator on Azure Disks
 	// https://docs.microsoft.com/azure/virtual-machines/windows/how-to-enable-write-accelerator
 	WriteAcceleratorEnabled = "writeacceleratorenabled"
@@ -78,16 +72,9 @@ var defaultBackOff = kwait.Backoff{
 	Jitter:   0.0,
 }
 
-var updateVMBackoff = kwait.Backoff{
-	Duration: updateVMRetryDuration,
-	Factor:   updateVMRetryFactor,
-	Steps:    updateVMRetrySteps,
-}
-
 var (
 	managedDiskPathRE  = regexp.MustCompile(`.*/subscriptions/(?:.*)/resourceGroups/(?:.*)/providers/Microsoft.Compute/disks/(.+)`)
 	diskSnapshotPathRE = regexp.MustCompile(`.*/subscriptions/(?:.*)/resourceGroups/(?:.*)/providers/Microsoft.Compute/snapshots/(.+)`)
-	errorCodeRE        = regexp.MustCompile(`Code="(.*?)".*`)
 )
 
 type controllerCommon struct {
@@ -267,10 +254,9 @@ func (c *controllerCommon) AttachDisk(ctx context.Context, async bool, diskName,
 	c.diskStateMap.Store(disk, "attaching")
 	defer c.diskStateMap.Delete(disk)
 	future, err := vmset.AttachDisk(ctx, nodeName, diskMap)
-	if future == nil {
-		return -1, fmt.Errorf("nil future was returned: %w", err)
+	if err != nil {
+		return -1, err
 	}
-	// err will be handled by waitForUpdateResult below
 
 	if async && c.diskOpRateLimiter.TryAccept() {
 		// unlock and wait for attach disk complete
@@ -281,40 +267,7 @@ func (c *controllerCommon) AttachDisk(ctx context.Context, async bool, diskName,
 			klog.Warningf("azureDisk - switch to batch operation due to rate limited, QPS: %f", c.diskOpRateLimiter.QPS())
 		}
 	}
-
-	if err = c.waitForUpdateResult(ctx, vmset, nodeName, future, err); err != nil {
-		return -1, err
-	}
-	return lun, nil
-}
-
-// waitForUpdateResult handles asynchronous VM update operations and retries with backoff if OperationPreempted error is observed
-func (c *controllerCommon) waitForUpdateResult(ctx context.Context, vmset VMSet, nodeName types.NodeName, future *azure.Future, updateErr error) (err error) {
-	err = updateErr
-
-	if err == nil {
-		err = vmset.WaitForUpdateResult(ctx, future, nodeName, "attach_disk")
-	}
-
-	if vmUpdateRequired(future, err) {
-		if derr := kwait.ExponentialBackoffWithContext(ctx, updateVMBackoff, func() (bool, error) {
-			klog.Errorf("Retry VM Update on node (%s) due to error (%v)", nodeName, err)
-			future, err = vmset.UpdateVMAsync(ctx, nodeName)
-			if err == nil {
-				err = vmset.WaitForUpdateResult(ctx, future, nodeName, "attach_disk")
-			}
-			return !vmUpdateRequired(future, err), nil
-		}); derr != nil {
-			err = derr
-			return
-		}
-	}
-
-	if err != nil && configAccepted(future) {
-		err = retry.NewPartialUpdateError(err.Error())
-	}
-
-	return
+	return lun, vmset.WaitForUpdateResult(ctx, future, nodeName, "attach_disk")
 }
 
 func (c *controllerCommon) insertAttachDiskRequest(diskURI, nodeName string, options *AttachDiskOptions) error {
@@ -667,11 +620,6 @@ func (c *controllerCommon) checkDiskExists(ctx context.Context, diskURI string)
 	return true, nil
 }
 
-func vmUpdateRequired(future *azure.Future, err error) bool {
-	errCode := getAzureErrorCode(err)
-	return configAccepted(future) && errCode == consts.OperationPreemptedErrorCode
-}
-
 func getValidCreationData(subscriptionID, resourceGroup, sourceResourceID, sourceType string) (compute.CreationData, error) {
 	if sourceResourceID == "" {
 		return compute.CreationData{
@@ -715,21 +663,3 @@ func isInstanceNotFoundError(err error) bool {
 	}
 	return strings.Contains(errMsg, errStatusCode400) && strings.Contains(errMsg, errInvalidParameter) && strings.Contains(errMsg, errTargetInstanceIds)
 }
-
-// getAzureErrorCode uses regex to parse out the error code encapsulated in the error string.
-func getAzureErrorCode(err error) string {
-	if err == nil {
-		return ""
-	}
-	matches := errorCodeRE.FindStringSubmatch(err.Error())
-	if matches == nil {
-		return ""
-	}
-	return matches[1]
-}
-
-// configAccepted returns true if storage profile change had been committed (i.e. HTTP status code == 2xx) and returns false otherwise.
-func configAccepted(future *azure.Future) bool {
-	// if status code indicates success, the storage profile change was committed
-	return future != nil && future.Response() != nil && future.Response().StatusCode/100 == 2
-}
diff --git a/pkg/provider/azure_controller_standard.go b/pkg/provider/azure_controller_standard.go
index c67172eb4..16ae72ec9 100644
--- a/pkg/provider/azure_controller_standard.go
+++ b/pkg/provider/azure_controller_standard.go
@@ -223,20 +223,10 @@ func (as *availabilitySet) DetachDisk(ctx context.Context, nodeName types.NodeNa
 
 // UpdateVM updates a vm
 func (as *availabilitySet) UpdateVM(ctx context.Context, nodeName types.NodeName) error {
-	future, err := as.UpdateVMAsync(ctx, nodeName)
-	if err != nil {
-		return err
-	}
-
-	return as.WaitForUpdateResult(ctx, future, nodeName, "update_vm")
-}
-
-// UpdateVMAsync updates a vm asynchronously
-func (as *availabilitySet) UpdateVMAsync(ctx context.Context, nodeName types.NodeName) (*azure.Future, error) {
 	vmName := mapNodeNameToVMName(nodeName)
 	nodeResourceGroup, err := as.GetNodeResourceGroup(vmName)
 	if err != nil {
-		return nil, err
+		return err
 	}
 	klog.V(2).Infof("azureDisk - update(%s): vm(%s)", nodeResourceGroup, vmName)
 	// Invalidate the cache right after updating
@@ -244,12 +234,12 @@ func (as *availabilitySet) UpdateVMAsync(ctx context.Context, nodeName types.Nod
 		_ = as.DeleteCacheForNode(vmName)
 	}()
 
-	future, rerr := as.VirtualMachinesClient.UpdateAsync(ctx, nodeResourceGroup, vmName, compute.VirtualMachineUpdate{}, "update_vm")
+	rerr := as.VirtualMachinesClient.Update(ctx, nodeResourceGroup, vmName, compute.VirtualMachineUpdate{}, "update_vm")
 	klog.V(2).Infof("azureDisk - update(%s): vm(%s) - returned with %v", nodeResourceGroup, vmName, rerr)
 	if rerr != nil {
-		return future, rerr.Error()
+		return rerr.Error()
 	}
-	return future, nil
+	return nil
 }
 
 // GetDataDisks gets a list of data disks attached to the node.
diff --git a/pkg/provider/azure_controller_vmss.go b/pkg/provider/azure_controller_vmss.go
index 4178913f7..922bae588 100644
--- a/pkg/provider/azure_controller_vmss.go
+++ b/pkg/provider/azure_controller_vmss.go
@@ -263,31 +263,20 @@ func (ss *ScaleSet) DetachDisk(ctx context.Context, nodeName types.NodeName, dis
 
 // UpdateVM updates a vm
 func (ss *ScaleSet) UpdateVM(ctx context.Context, nodeName types.NodeName) error {
-	future, err := ss.UpdateVMAsync(ctx, nodeName)
-	if err != nil {
-		return err
-	}
-
-	return ss.WaitForUpdateResult(ctx, future, nodeName, "update_vm")
-}
-
-// UpdateVMAsync updates a vm asynchronously
-func (ss *ScaleSet) UpdateVMAsync(ctx context.Context, nodeName types.NodeName) (*azure.Future, error) {
 	vmName := mapNodeNameToVMName(nodeName)
 	vm, err := ss.getVmssVM(vmName, azcache.CacheReadTypeDefault)
 	if err != nil {
-		return nil, err
+		return err
 	}
 
 	nodeResourceGroup, err := ss.GetNodeResourceGroup(vmName)
 	if err != nil {
-		return nil, err
+		return err
 	}
 
-	var future *azure.Future
+	var updateResult *compute.VirtualMachineScaleSetVM
 	var rerr *retry.Error
 
-	// Invalidate the cache right after updating
 	defer func() {
 		// If there is an error with Update operation,
 		// invalidate the cache
@@ -295,16 +284,26 @@ func (ss *ScaleSet) UpdateVMAsync(ctx context.Context, nodeName types.NodeName)
 			_ = ss.DeleteCacheForNode(vmName)
 			return
 		}
+
+		// Update the cache with the updated result only if its not nil
+		// and contains the VirtualMachineScaleSetVMProperties
+		if updateResult != nil && updateResult.VirtualMachineScaleSetVMProperties != nil {
+			if err := ss.updateCache(vmName, nodeResourceGroup, vm.VMSSName, vm.InstanceID, updateResult); err != nil {
+				klog.Errorf("updateCache(%s, %s, %s) failed with error: %v", vmName, nodeResourceGroup, vm.VMSSName, vm.InstanceID, err)
+				// if err faced during updating cache, invalidate the cache
+				_ = ss.DeleteCacheForNode(vmName)
+			}
+		}
 	}()
 
 	klog.V(2).Infof("azureDisk - update(%s): vm(%s)", nodeResourceGroup, nodeName)
-	future, rerr = ss.VirtualMachineScaleSetVMsClient.UpdateAsync(ctx, nodeResourceGroup, vm.VMSSName, vm.InstanceID, compute.VirtualMachineScaleSetVM{}, "update_vmss_instance")
+	updateResult, rerr = ss.VirtualMachineScaleSetVMsClient.Update(ctx, nodeResourceGroup, vm.VMSSName, vm.InstanceID, compute.VirtualMachineScaleSetVM{}, "update_vmss_instance")
 
 	klog.V(2).Infof("azureDisk - update(%s): vm(%s) - returned with %v", nodeResourceGroup, nodeName, rerr)
 	if rerr != nil {
-		return future, rerr.Error()
+		return rerr.Error()
 	}
-	return future, nil
+	return nil
 }
 
 // GetDataDisks gets a list of data disks attached to the node.
diff --git a/pkg/provider/azure_controller_vmssflex.go b/pkg/provider/azure_controller_vmssflex.go
index d526f4719..e8cab8553 100644
--- a/pkg/provider/azure_controller_vmssflex.go
+++ b/pkg/provider/azure_controller_vmssflex.go
@@ -218,27 +218,16 @@ func (fs *FlexScaleSet) WaitForUpdateResult(ctx context.Context, future *azure.F
 
 // UpdateVM updates a vm
 func (fs *FlexScaleSet) UpdateVM(ctx context.Context, nodeName types.NodeName) error {
-	future, err := fs.UpdateVMAsync(ctx, nodeName)
-	if err != nil {
-		return err
-	}
-
-	return fs.WaitForUpdateResult(ctx, future, nodeName, "update_vm")
-}
-
-// UpdateVMAsync updates a vm asynchronously
-func (fs *FlexScaleSet) UpdateVMAsync(ctx context.Context, nodeName types.NodeName) (*azure.Future, error) {
 	vmName := mapNodeNameToVMName(nodeName)
-
 	vm, err := fs.getVmssFlexVM(vmName, azcache.CacheReadTypeDefault)
 	if err != nil {
 		// if host doesn't exist, no need to update
 		klog.Warningf("azureDisk - cannot find node %s, skip updating vm)", nodeName)
-		return nil, nil
+		return nil
 	}
 	nodeResourceGroup, err := fs.GetNodeResourceGroup(vmName)
 	if err != nil {
-		return nil, err
+		return err
 	}
 
 	defer func() {
@@ -247,12 +236,12 @@ func (fs *FlexScaleSet) UpdateVMAsync(ctx context.Context, nodeName types.NodeNa
 
 	klog.V(2).Infof("azureDisk - update(%s): vm(%s)", nodeResourceGroup, vmName)
 
-	future, rerr := fs.VirtualMachinesClient.UpdateAsync(ctx, nodeResourceGroup, *vm.Name, compute.VirtualMachineUpdate{}, "update_vm")
+	rerr := fs.VirtualMachinesClient.Update(ctx, nodeResourceGroup, *vm.Name, compute.VirtualMachineUpdate{}, "update_vm")
 	klog.V(2).Infof("azureDisk - update(%s): vm(%s) - returned with %v", nodeResourceGroup, vmName, rerr)
 	if rerr != nil {
-		return future, rerr.Error()
+		return rerr.Error()
 	}
-	return future, nil
+	return nil
 }
 
 // GetDataDisks gets a list of data disks attached to the node.
diff --git a/pkg/provider/azure_mock_vmsets.go b/pkg/provider/azure_mock_vmsets.go
index ee14278f3..be51095b0 100644
--- a/pkg/provider/azure_mock_vmsets.go
+++ b/pkg/provider/azure_mock_vmsets.go
@@ -415,21 +415,6 @@ func (mr *MockVMSetMockRecorder) UpdateVM(ctx, nodeName interface{}) *gomock.Cal
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateVM", reflect.TypeOf((*MockVMSet)(nil).UpdateVM), ctx, nodeName)
 }
 
-// UpdateVMAsync mocks base method
-func (m *MockVMSet) UpdateVMAsync(ctx context.Context, nodeName types.NodeName) (*azure.Future, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "UpdateVMAsync", ctx, nodeName)
-	ret0, _ := ret[0].(*azure.Future)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// UpdateVMAsync indicates an expected call of UpdateVMAsync
-func (mr *MockVMSetMockRecorder) UpdateVMAsync(ctx, nodeName interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateVMAsync", reflect.TypeOf((*MockVMSet)(nil).UpdateVMAsync), ctx, nodeName)
-}
-
 // WaitForUpdateResult mocks base method.
 func (m *MockVMSet) WaitForUpdateResult(ctx context.Context, future *azure.Future, nodeName types.NodeName, source string) error {
 	m.ctrl.T.Helper()
diff --git a/pkg/provider/azure_vmsets.go b/pkg/provider/azure_vmsets.go
index e2935ffdc..d82958796 100644
--- a/pkg/provider/azure_vmsets.go
+++ b/pkg/provider/azure_vmsets.go
@@ -88,9 +88,6 @@ type VMSet interface {
 	// UpdateVM updates a vm
 	UpdateVM(ctx context.Context, nodeName types.NodeName) error
 
-	// UpdateVMAsync updates a vm asynchronously
-	UpdateVMAsync(ctx context.Context, nodeName types.NodeName) (*azure.Future, error)
-
 	// GetPowerStatusByNodeName returns the powerState for the specified node.
 	GetPowerStatusByNodeName(name string) (string, error)
 
diff --git a/pkg/provider/azure_vmss.go b/pkg/provider/azure_vmss.go
index 8bf079129..03cd09d7f 100644
--- a/pkg/provider/azure_vmss.go
+++ b/pkg/provider/azure_vmss.go
@@ -81,11 +81,14 @@ type ScaleSet struct {
 	// the same cluster.
 	flexScaleSet VMSet
 
+	// vmssCache is timed cache where the Store in the cache is a map of
+	// Key: consts.VMSSKey
+	// Value: sync.Map of [vmssName]*VMSSEntry
 	vmssCache *azcache.TimedCache
 
 	// vmssVMCache is timed cache where the Store in the cache is a map of
 	// Key: [resourcegroup/vmssName]
-	// Value: sync.Map of [vmName]*VMSSVirtualMachinesEntry
+	// Value: sync.Map of [vmName]*VMSSVirtualMachineEntry
 	vmssVMCache *azcache.TimedCache
 
 	// nonVmssUniformNodesCache is used to store node names from non uniform vm.
@@ -195,7 +198,7 @@ func (ss *ScaleSet) getVmssVMByNodeIdentity(node *nodeIdentity, crt azcache.Azur
 		}
 
 		if entry, ok := virtualMachines.Load(node.nodeName); ok {
-			result := entry.(*VMSSVirtualMachinesEntry)
+			result := entry.(*VMSSVirtualMachineEntry)
 			if result.VirtualMachine == nil {
 				klog.Warningf("VM is nil on Node %q, VM is in deleting state", node.nodeName)
 				return nil, true, nil
@@ -331,7 +334,7 @@ func (ss *ScaleSet) getVmssVMByInstanceID(resourceGroup, scaleSetName, instanceI
 		}
 
 		virtualMachines.Range(func(key, value interface{}) bool {
-			vmEntry := value.(*VMSSVirtualMachinesEntry)
+			vmEntry := value.(*VMSSVirtualMachineEntry)
 			if strings.EqualFold(vmEntry.ResourceGroup, resourceGroup) &&
 				strings.EqualFold(vmEntry.VMSSName, scaleSetName) &&
 				strings.EqualFold(vmEntry.InstanceID, instanceID) {
diff --git a/pkg/provider/azure_vmss_cache.go b/pkg/provider/azure_vmss_cache.go
index 903f9a157..47ff879cb 100644
--- a/pkg/provider/azure_vmss_cache.go
+++ b/pkg/provider/azure_vmss_cache.go
@@ -33,7 +33,7 @@ import (
 	"sigs.k8s.io/cloud-provider-azure/pkg/consts"
 )
 
-type VMSSVirtualMachinesEntry struct {
+type VMSSVirtualMachineEntry struct {
 	ResourceGroup  string
 	VMSSName       string
 	InstanceID     string
@@ -162,9 +162,9 @@ func (ss *ScaleSet) newVMSSVirtualMachinesCache() (*azcache.TimedCache, error) {
 	vmssVirtualMachinesCacheTTL := time.Duration(ss.Config.VmssVirtualMachinesCacheTTLInSeconds) * time.Second
 
 	getter := func(cacheKey string) (interface{}, error) {
-		localCache := &sync.Map{} // [nodeName]*VMSSVirtualMachinesEntry
+		localCache := &sync.Map{} // [nodeName]*VMSSVirtualMachineEntry
 
-		oldCache := make(map[string]*VMSSVirtualMachinesEntry)
+		oldCache := make(map[string]*VMSSVirtualMachineEntry)
 
 		entry, exists, err := ss.vmssVMCache.Store.GetByKey(cacheKey)
 		if err != nil {
@@ -175,7 +175,7 @@ func (ss *ScaleSet) newVMSSVirtualMachinesCache() (*azcache.TimedCache, error) {
 			if cached != nil {
 				virtualMachines := cached.(*sync.Map)
 				virtualMachines.Range(func(key, value interface{}) bool {
-					oldCache[key.(string)] = value.(*VMSSVirtualMachinesEntry)
+					oldCache[key.(string)] = value.(*VMSSVirtualMachineEntry)
 					return true
 				})
 			}
@@ -207,7 +207,7 @@ func (ss *ScaleSet) newVMSSVirtualMachinesCache() (*azcache.TimedCache, error) {
 				continue
 			}
 
-			vmssVMCacheEntry := &VMSSVirtualMachinesEntry{
+			vmssVMCacheEntry := &VMSSVirtualMachineEntry{
 				ResourceGroup:  resourceGroupName,
 				VMSSName:       vmssName,
 				InstanceID:     to.String(vm.InstanceID),
@@ -242,7 +242,7 @@ func (ss *ScaleSet) newVMSSVirtualMachinesCache() (*azcache.TimedCache, error) {
 			}
 
 			klog.V(5).Infof("adding old entries to new cache for %s", name)
-			localCache.Store(name, &VMSSVirtualMachinesEntry{
+			localCache.Store(name, &VMSSVirtualMachineEntry{
 				ResourceGroup:  vmEntry.ResourceGroup,
 				VMSSName:       vmEntry.VMSSName,
 				InstanceID:     vmEntry.InstanceID,
@@ -286,7 +286,6 @@ func (ss *ScaleSet) DeleteCacheForNode(nodeName string) error {
 		return err
 	}
 
-	// Delete in VMSS VM cache
 	if err := ss.gcVMSSVMCache(); err != nil {
 		klog.Errorf("DeleteCacheForNode(%s) failed to gc stale vmss caches: %v", nodeName, err)
 	}
@@ -308,7 +307,7 @@ func (ss *ScaleSet) updateCache(nodeName, resourceGroupName, vmssName, instanceI
 
 	localCache := &sync.Map{}
 
-	vmssVMCacheEntry := &VMSSVirtualMachinesEntry{
+	vmssVMCacheEntry := &VMSSVirtualMachineEntry{
 		ResourceGroup:  resourceGroupName,
 		VMSSName:       vmssName,
 		InstanceID:     instanceID,
@@ -320,7 +319,7 @@ func (ss *ScaleSet) updateCache(nodeName, resourceGroupName, vmssName, instanceI
 
 	virtualMachines.Range(func(key, value interface{}) bool {
 		if key.(string) != nodeName {
-			localCache.Store(key.(string), value.(*VMSSVirtualMachinesEntry))
+			localCache.Store(key.(string), value.(*VMSSVirtualMachineEntry))
 		}
 		return true
 	})
diff --git a/pkg/provider/azure_wrap.go b/pkg/provider/azure_wrap.go
index 71a86852d..05e965ea8 100644
--- a/pkg/provider/azure_wrap.go
+++ b/pkg/provider/azure_wrap.go
@@ -86,7 +86,7 @@ func (az *Cloud) getRouteTable(crt azcache.AzureCacheReadType) (routeTable netwo
 		return routeTable, false, fmt.Errorf("Route table name is not configured")
 	}
 
-	cachedRt, err := az.rtCache.Get(az.RouteTableName, crt)
+	cachedRt, err := az.rtCache.GetWithDeepCopy(az.RouteTableName, crt)
 	if err != nil {
 		return routeTable, false, err
 	}
@@ -109,7 +109,7 @@ func (az *Cloud) getPIPCacheKey(pipResourceGroup string, pipName string) string
 func (az *Cloud) getPublicIPAddress(pipResourceGroup string, pipName string, crt azcache.AzureCacheReadType) (network.PublicIPAddress, bool, error) {
 	pip := network.PublicIPAddress{}
 	cacheKey := az.getPIPCacheKey(pipResourceGroup, pipName)
-	cachedPIP, err := az.pipCache.Get(cacheKey, crt)
+	cachedPIP, err := az.pipCache.GetWithDeepCopy(cacheKey, crt)
 	if err != nil {
 		return pip, false, err
 	}
@@ -146,7 +146,7 @@ func (az *Cloud) getSubnet(virtualNetworkName string, subnetName string) (networ
 }
 
 func (az *Cloud) getAzureLoadBalancer(name string, crt azcache.AzureCacheReadType) (lb *network.LoadBalancer, exists bool, err error) {
-	cachedLB, err := az.lbCache.Get(name, crt)
+	cachedLB, err := az.lbCache.GetWithDeepCopy(name, crt)
 	if err != nil {
 		return lb, false, err
 	}
@@ -164,7 +164,7 @@ func (az *Cloud) getSecurityGroup(crt azcache.AzureCacheReadType) (network.Secur
 		return nsg, fmt.Errorf("securityGroupName is not configured")
 	}
 
-	securityGroup, err := az.nsgCache.Get(az.SecurityGroupName, crt)
+	securityGroup, err := az.nsgCache.GetWithDeepCopy(az.SecurityGroupName, crt)
 	if err != nil {
 		return nsg, err
 	}
@@ -177,7 +177,7 @@ func (az *Cloud) getSecurityGroup(crt azcache.AzureCacheReadType) (network.Secur
 }
 
 func (az *Cloud) getPrivateLinkService(frontendIPConfigID *string, crt azcache.AzureCacheReadType) (pls network.PrivateLinkService, err error) {
-	cachedPLS, err := az.plsCache.Get(*frontendIPConfigID, crt)
+	cachedPLS, err := az.plsCache.GetWithDeepCopy(*frontendIPConfigID, crt)
 	if err != nil {
 		return pls, err
 	}
diff --git a/pkg/retry/azure_error.go b/pkg/retry/azure_error.go
index c7e9a71d1..d5ddae531 100644
--- a/pkg/retry/azure_error.go
+++ b/pkg/retry/azure_error.go
@@ -425,16 +425,3 @@ func getOperationNotAllowedReason(msg string) string {
 	}
 	return OperationNotAllowed
 }
-
-// PartialUpdateError implements error interface. It is meant to be returned for errors with http status code of 2xx
-type PartialUpdateError struct {
-	message string
-}
-
-func NewPartialUpdateError(msg string) *PartialUpdateError {
-	return &PartialUpdateError{message: msg}
-}
-
-func (e *PartialUpdateError) Error() string {
-	return e.message
-}
diff --git a/vendor/github.com/onsi/ginkgo/v2/CHANGELOG.md b/vendor/github.com/onsi/ginkgo/v2/CHANGELOG.md
index 0b5ef20b4..b723d4e59 100644
--- a/vendor/github.com/onsi/ginkgo/v2/CHANGELOG.md
+++ b/vendor/github.com/onsi/ginkgo/v2/CHANGELOG.md
@@ -1,13 +1,3 @@
-## 2.6.0
-
-### Features
-- `ReportBeforeSuite` provides access to the suite report before the suite begins.
-- Add junit config option for omitting leafnodetype (#1088) [956e6d2]
-- Add support to customize junit report config to omit spec labels (#1087) [de44005]
-
-### Fixes
-- Fix stack trace pruning so that it has a chance of working on windows [2165648]
-
 ## 2.5.1
 
 ### Fixes
diff --git a/vendor/github.com/onsi/ginkgo/v2/internal/node.go b/vendor/github.com/onsi/ginkgo/v2/internal/node.go
index 69928eed6..0878c6728 100644
--- a/vendor/github.com/onsi/ginkgo/v2/internal/node.go
+++ b/vendor/github.com/onsi/ginkgo/v2/internal/node.go
@@ -44,8 +44,8 @@ type Node struct {
 	SynchronizedAfterSuiteProc1Body              func(SpecContext)
 	SynchronizedAfterSuiteProc1BodyHasContext    bool
 
-	ReportEachBody  func(types.SpecReport)
-	ReportSuiteBody func(types.Report)
+	ReportEachBody       func(types.SpecReport)
+	ReportAfterSuiteBody func(types.Report)
 
 	MarkedFocus          bool
 	MarkedPending        bool
@@ -317,9 +317,9 @@ func NewNode(deprecationTracker *types.DeprecationTracker, nodeType types.NodeTy
 					trackedFunctionError = true
 					break
 				}
-			} else if nodeType.Is(types.NodeTypeReportBeforeSuite | types.NodeTypeReportAfterSuite) {
-				if node.ReportSuiteBody == nil {
-					node.ReportSuiteBody = arg.(func(types.Report))
+			} else if nodeType.Is(types.NodeTypeReportAfterSuite) {
+				if node.ReportAfterSuiteBody == nil {
+					node.ReportAfterSuiteBody = arg.(func(types.Report))
 				} else {
 					appendError(types.GinkgoErrors.MultipleBodyFunctions(node.CodeLocation, nodeType))
 					trackedFunctionError = true
@@ -392,7 +392,7 @@ func NewNode(deprecationTracker *types.DeprecationTracker, nodeType types.NodeTy
 		appendError(types.GinkgoErrors.InvalidTimeoutOrGracePeriodForNonContextNode(node.CodeLocation, nodeType))
 	}
 
-	if !node.NodeType.Is(types.NodeTypeReportBeforeEach|types.NodeTypeReportAfterEach|types.NodeTypeSynchronizedBeforeSuite|types.NodeTypeSynchronizedAfterSuite|types.NodeTypeReportBeforeSuite|types.NodeTypeReportAfterSuite) && node.Body == nil && !node.MarkedPending && !trackedFunctionError {
+	if !node.NodeType.Is(types.NodeTypeReportBeforeEach|types.NodeTypeReportAfterEach|types.NodeTypeSynchronizedBeforeSuite|types.NodeTypeSynchronizedAfterSuite|types.NodeTypeReportAfterSuite) && node.Body == nil && !node.MarkedPending && !trackedFunctionError {
 		appendError(types.GinkgoErrors.MissingBodyFunction(node.CodeLocation, nodeType))
 	}
 
diff --git a/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/client_server.go b/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/client_server.go
index b3cd64292..b417bf5b3 100644
--- a/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/client_server.go
+++ b/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/client_server.go
@@ -42,8 +42,6 @@ type Client interface {
 	PostSuiteWillBegin(report types.Report) error
 	PostDidRun(report types.SpecReport) error
 	PostSuiteDidEnd(report types.Report) error
-	PostReportBeforeSuiteCompleted(state types.SpecState) error
-	BlockUntilReportBeforeSuiteCompleted() (types.SpecState, error)
 	PostSynchronizedBeforeSuiteCompleted(state types.SpecState, data []byte) error
 	BlockUntilSynchronizedBeforeSuiteData() (types.SpecState, []byte, error)
 	BlockUntilNonprimaryProcsHaveFinished() error
diff --git a/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_client.go b/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_client.go
index 6547c7a66..ad9932f2a 100644
--- a/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_client.go
+++ b/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_client.go
@@ -98,19 +98,6 @@ func (client *httpClient) PostEmitProgressReport(report types.ProgressReport) er
 	return client.post("/progress-report", report)
 }
 
-func (client *httpClient) PostReportBeforeSuiteCompleted(state types.SpecState) error {
-	return client.post("/report-before-suite-completed", state)
-}
-
-func (client *httpClient) BlockUntilReportBeforeSuiteCompleted() (types.SpecState, error) {
-	var state types.SpecState
-	err := client.poll("/report-before-suite-state", &state)
-	if err == ErrorGone {
-		return types.SpecStateFailed, nil
-	}
-	return state, err
-}
-
 func (client *httpClient) PostSynchronizedBeforeSuiteCompleted(state types.SpecState, data []byte) error {
 	beforeSuiteState := BeforeSuiteState{
 		State: state,
diff --git a/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_server.go b/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_server.go
index d2c71ab1b..fa3ac682a 100644
--- a/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_server.go
+++ b/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_server.go
@@ -26,7 +26,7 @@ type httpServer struct {
 	handler  *ServerHandler
 }
 
-// Create a new server, automatically selecting a port
+//Create a new server, automatically selecting a port
 func newHttpServer(parallelTotal int, reporter reporters.Reporter) (*httpServer, error) {
 	listener, err := net.Listen("tcp", "127.0.0.1:0")
 	if err != nil {
@@ -38,7 +38,7 @@ func newHttpServer(parallelTotal int, reporter reporters.Reporter) (*httpServer,
 	}, nil
 }
 
-// Start the server.  You don't need to `go s.Start()`, just `s.Start()`
+//Start the server.  You don't need to `go s.Start()`, just `s.Start()`
 func (server *httpServer) Start() {
 	httpServer := &http.Server{}
 	mux := http.NewServeMux()
@@ -52,8 +52,6 @@ func (server *httpServer) Start() {
 	mux.HandleFunc("/progress-report", server.emitProgressReport)
 
 	//synchronization endpoints
-	mux.HandleFunc("/report-before-suite-completed", server.handleReportBeforeSuiteCompleted)
-	mux.HandleFunc("/report-before-suite-state", server.handleReportBeforeSuiteState)
 	mux.HandleFunc("/before-suite-completed", server.handleBeforeSuiteCompleted)
 	mux.HandleFunc("/before-suite-state", server.handleBeforeSuiteState)
 	mux.HandleFunc("/have-nonprimary-procs-finished", server.handleHaveNonprimaryProcsFinished)
@@ -65,12 +63,12 @@ func (server *httpServer) Start() {
 	go httpServer.Serve(server.listener)
 }
 
-// Stop the server
+//Stop the server
 func (server *httpServer) Close() {
 	server.listener.Close()
 }
 
-// The address the server can be reached it.  Pass this into the `ForwardingReporter`.
+//The address the server can be reached it.  Pass this into the `ForwardingReporter`.
 func (server *httpServer) Address() string {
 	return "http://" + server.listener.Addr().String()
 }
@@ -95,7 +93,7 @@ func (server *httpServer) RegisterAlive(node int, alive func() bool) {
 // Streaming Endpoints
 //
 
-// The server will forward all received messages to Ginkgo reporters registered with `RegisterReporters`
+//The server will forward all received messages to Ginkgo reporters registered with `RegisterReporters`
 func (server *httpServer) decode(writer http.ResponseWriter, request *http.Request, object interface{}) bool {
 	defer request.Body.Close()
 	if json.NewDecoder(request.Body).Decode(object) != nil {
@@ -166,23 +164,6 @@ func (server *httpServer) emitProgressReport(writer http.ResponseWriter, request
 	server.handleError(server.handler.EmitProgressReport(report, voidReceiver), writer)
 }
 
-func (server *httpServer) handleReportBeforeSuiteCompleted(writer http.ResponseWriter, request *http.Request) {
-	var state types.SpecState
-	if !server.decode(writer, request, &state) {
-		return
-	}
-
-	server.handleError(server.handler.ReportBeforeSuiteCompleted(state, voidReceiver), writer)
-}
-
-func (server *httpServer) handleReportBeforeSuiteState(writer http.ResponseWriter, request *http.Request) {
-	var state types.SpecState
-	if server.handleError(server.handler.ReportBeforeSuiteState(voidSender, &state), writer) {
-		return
-	}
-	json.NewEncoder(writer).Encode(state)
-}
-
 func (server *httpServer) handleBeforeSuiteCompleted(writer http.ResponseWriter, request *http.Request) {
 	var beforeSuiteState BeforeSuiteState
 	if !server.decode(writer, request, &beforeSuiteState) {
diff --git a/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_client.go b/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_client.go
index 59e8e6fd0..fe93cc2b9 100644
--- a/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_client.go
+++ b/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_client.go
@@ -76,19 +76,6 @@ func (client *rpcClient) PostEmitProgressReport(report types.ProgressReport) err
 	return client.client.Call("Server.EmitProgressReport", report, voidReceiver)
 }
 
-func (client *rpcClient) PostReportBeforeSuiteCompleted(state types.SpecState) error {
-	return client.client.Call("Server.ReportBeforeSuiteCompleted", state, voidReceiver)
-}
-
-func (client *rpcClient) BlockUntilReportBeforeSuiteCompleted() (types.SpecState, error) {
-	var state types.SpecState
-	err := client.poll("Server.ReportBeforeSuiteState", &state)
-	if err == ErrorGone {
-		return types.SpecStateFailed, nil
-	}
-	return state, err
-}
-
 func (client *rpcClient) PostSynchronizedBeforeSuiteCompleted(state types.SpecState, data []byte) error {
 	beforeSuiteState := BeforeSuiteState{
 		State: state,
diff --git a/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/server_handler.go b/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/server_handler.go
index a6d98793e..7c6e67b96 100644
--- a/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/server_handler.go
+++ b/vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/server_handler.go
@@ -18,17 +18,16 @@ var voidSender Void
 // It handles all the business logic to avoid duplication between the two servers
 
 type ServerHandler struct {
-	done                   chan interface{}
-	outputDestination      io.Writer
-	reporter               reporters.Reporter
-	alives                 []func() bool
-	lock                   *sync.Mutex
-	beforeSuiteState       BeforeSuiteState
-	reportBeforeSuiteState types.SpecState
-	parallelTotal          int
-	counter                int
-	counterLock            *sync.Mutex
-	shouldAbort            bool
+	done              chan interface{}
+	outputDestination io.Writer
+	reporter          reporters.Reporter
+	alives            []func() bool
+	lock              *sync.Mutex
+	beforeSuiteState  BeforeSuiteState
+	parallelTotal     int
+	counter           int
+	counterLock       *sync.Mutex
+	shouldAbort       bool
 
 	numSuiteDidBegins int
 	numSuiteDidEnds   int
@@ -38,12 +37,11 @@ type ServerHandler struct {
 
 func newServerHandler(parallelTotal int, reporter reporters.Reporter) *ServerHandler {
 	return &ServerHandler{
-		reporter:         reporter,
-		lock:             &sync.Mutex{},
-		counterLock:      &sync.Mutex{},
-		alives:           make([]func() bool, parallelTotal),
-		beforeSuiteState: BeforeSuiteState{Data: nil, State: types.SpecStateInvalid},
-
+		reporter:          reporter,
+		lock:              &sync.Mutex{},
+		counterLock:       &sync.Mutex{},
+		alives:            make([]func() bool, parallelTotal),
+		beforeSuiteState:  BeforeSuiteState{Data: nil, State: types.SpecStateInvalid},
 		parallelTotal:     parallelTotal,
 		outputDestination: os.Stdout,
 		done:              make(chan interface{}),
@@ -142,29 +140,6 @@ func (handler *ServerHandler) haveNonprimaryProcsFinished() bool {
 	return true
 }
 
-func (handler *ServerHandler) ReportBeforeSuiteCompleted(reportBeforeSuiteState types.SpecState, _ *Void) error {
-	handler.lock.Lock()
-	defer handler.lock.Unlock()
-	handler.reportBeforeSuiteState = reportBeforeSuiteState
-
-	return nil
-}
-
-func (handler *ServerHandler) ReportBeforeSuiteState(_ Void, reportBeforeSuiteState *types.SpecState) error {
-	proc1IsAlive := handler.procIsAlive(1)
-	handler.lock.Lock()
-	defer handler.lock.Unlock()
-	if handler.reportBeforeSuiteState == types.SpecStateInvalid {
-		if proc1IsAlive {
-			return ErrorEarly
-		} else {
-			return ErrorGone
-		}
-	}
-	*reportBeforeSuiteState = handler.reportBeforeSuiteState
-	return nil
-}
-
 func (handler *ServerHandler) BeforeSuiteCompleted(beforeSuiteState BeforeSuiteState, _ *Void) error {
 	handler.lock.Lock()
 	defer handler.lock.Unlock()
diff --git a/vendor/github.com/onsi/ginkgo/v2/internal/progress_report.go b/vendor/github.com/onsi/ginkgo/v2/internal/progress_report.go
index 11269cf1f..7dd3ca963 100644
--- a/vendor/github.com/onsi/ginkgo/v2/internal/progress_report.go
+++ b/vendor/github.com/onsi/ginkgo/v2/internal/progress_report.go
@@ -183,6 +183,7 @@ func extractRunningGoroutines() ([]types.Goroutine, error) {
 			break
 		}
 	}
+
 	r := bufio.NewReader(bytes.NewReader(stack))
 	out := []types.Goroutine{}
 	idx := -1
@@ -230,12 +231,12 @@ func extractRunningGoroutines() ([]types.Goroutine, error) {
 			return nil, types.GinkgoErrors.FailedToParseStackTrace(fmt.Sprintf("Invalid function call: %s -- missing file name and line number", functionCall.Function))
 		}
 		line = strings.TrimLeft(line, " \t")
-		delimiterIdx := strings.LastIndex(line, ":")
-		if delimiterIdx == -1 {
-			return nil, types.GinkgoErrors.FailedToParseStackTrace(fmt.Sprintf("Invalid filename and line number: %s", line))
+		fields := strings.SplitN(line, ":", 2)
+		if len(fields) != 2 {
+			return nil, types.GinkgoErrors.FailedToParseStackTrace(fmt.Sprintf("Invalid filename nad line number: %s", line))
 		}
-		functionCall.Filename = line[:delimiterIdx]
-		line = strings.Split(line[delimiterIdx+1:], " ")[0]
+		functionCall.Filename = fields[0]
+		line = strings.Split(fields[1], " ")[0]
 		lineNumber, err := strconv.ParseInt(line, 10, 64)
 		functionCall.Line = int(lineNumber)
 		if err != nil {
diff --git a/vendor/github.com/onsi/ginkgo/v2/internal/suite.go b/vendor/github.com/onsi/ginkgo/v2/internal/suite.go
index f470641a6..d3671ebcf 100644
--- a/vendor/github.com/onsi/ginkgo/v2/internal/suite.go
+++ b/vendor/github.com/onsi/ginkgo/v2/internal/suite.go
@@ -129,7 +129,7 @@ func (suite *Suite) PushNode(node Node) error {
 		return suite.pushCleanupNode(node)
 	}
 
-	if node.NodeType.Is(types.NodeTypeBeforeSuite | types.NodeTypeAfterSuite | types.NodeTypeSynchronizedBeforeSuite | types.NodeTypeSynchronizedAfterSuite | types.NodeTypeBeforeSuite | types.NodeTypeReportBeforeSuite | types.NodeTypeReportAfterSuite) {
+	if node.NodeType.Is(types.NodeTypeBeforeSuite | types.NodeTypeAfterSuite | types.NodeTypeSynchronizedBeforeSuite | types.NodeTypeSynchronizedAfterSuite | types.NodeTypeReportAfterSuite) {
 		return suite.pushSuiteNode(node)
 	}
 
@@ -222,7 +222,7 @@ func (suite *Suite) pushCleanupNode(node Node) error {
 		node.NodeType = types.NodeTypeCleanupAfterSuite
 	case types.NodeTypeBeforeAll, types.NodeTypeAfterAll:
 		node.NodeType = types.NodeTypeCleanupAfterAll
-	case types.NodeTypeReportBeforeEach, types.NodeTypeReportAfterEach, types.NodeTypeReportBeforeSuite, types.NodeTypeReportAfterSuite:
+	case types.NodeTypeReportBeforeEach, types.NodeTypeReportAfterEach, types.NodeTypeReportAfterSuite:
 		return types.GinkgoErrors.PushingCleanupInReportingNode(node.CodeLocation, suite.currentNode.NodeType)
 	case types.NodeTypeCleanupInvalid, types.NodeTypeCleanupAfterEach, types.NodeTypeCleanupAfterAll, types.NodeTypeCleanupAfterSuite:
 		return types.GinkgoErrors.PushingCleanupInCleanupNode(node.CodeLocation)
@@ -408,13 +408,7 @@ func (suite *Suite) runSpecs(description string, suiteLabels Labels, suitePath s
 	}
 
 	suite.report.SuiteSucceeded = true
-
-	suite.runReportSuiteNodesIfNeedBe(types.NodeTypeReportBeforeSuite)
-
-	ranBeforeSuite := suite.report.SuiteSucceeded
-	if suite.report.SuiteSucceeded {
-		suite.runBeforeSuite(numSpecsThatWillBeRun)
-	}
+	suite.runBeforeSuite(numSpecsThatWillBeRun)
 
 	if suite.report.SuiteSucceeded {
 		groupedSpecIndices, serialGroupedSpecIndices := OrderSpecs(specs, suite.config)
@@ -453,9 +447,7 @@ func (suite *Suite) runSpecs(description string, suiteLabels Labels, suitePath s
 		}
 	}
 
-	if ranBeforeSuite {
-		suite.runAfterSuiteCleanup(numSpecsThatWillBeRun)
-	}
+	suite.runAfterSuiteCleanup(numSpecsThatWillBeRun)
 
 	interruptStatus := suite.interruptHandler.Status()
 	if interruptStatus.Interrupted() {
@@ -469,7 +461,9 @@ func (suite *Suite) runSpecs(description string, suiteLabels Labels, suitePath s
 		suite.report.SuiteSucceeded = false
 	}
 
-	suite.runReportSuiteNodesIfNeedBe(types.NodeTypeReportAfterSuite)
+	if suite.config.ParallelProcess == 1 {
+		suite.runReportAfterSuite()
+	}
 	suite.reporter.SuiteDidEnd(suite.report)
 	if suite.isRunningInParallel() {
 		suite.client.PostSuiteDidEnd(suite.report)
@@ -536,6 +530,24 @@ func (suite *Suite) runAfterSuiteCleanup(numSpecsThatWillBeRun int) {
 	}
 }
 
+func (suite *Suite) runReportAfterSuite() {
+	for _, node := range suite.suiteNodes.WithType(types.NodeTypeReportAfterSuite) {
+		suite.selectiveLock.Lock()
+		suite.currentSpecReport = types.SpecReport{
+			LeafNodeType:      node.NodeType,
+			LeafNodeLocation:  node.CodeLocation,
+			LeafNodeText:      node.Text,
+			ParallelProcess:   suite.config.ParallelProcess,
+			RunningInParallel: suite.isRunningInParallel(),
+		}
+		suite.selectiveLock.Unlock()
+
+		suite.reporter.WillRun(suite.currentSpecReport)
+		suite.runReportAfterSuiteNode(node, suite.report)
+		suite.processCurrentSpecReport()
+	}
+}
+
 func (suite *Suite) reportEach(spec Spec, nodeType types.NodeType) {
 	nodes := spec.Nodes.WithType(nodeType)
 	if nodeType == types.NodeTypeReportAfterEach {
@@ -660,57 +672,16 @@ func (suite *Suite) runSuiteNode(node Node) {
 	suite.currentSpecReport.RunTime = suite.currentSpecReport.EndTime.Sub(suite.currentSpecReport.StartTime)
 	suite.currentSpecReport.CapturedGinkgoWriterOutput = string(suite.writer.Bytes())
 	suite.currentSpecReport.CapturedStdOutErr += suite.outputInterceptor.StopInterceptingAndReturnOutput()
-}
 
-func (suite *Suite) runReportSuiteNodesIfNeedBe(nodeType types.NodeType) {
-	nodes := suite.suiteNodes.WithType(nodeType)
-	// only run ReportAfterSuite on proc 1
-	if nodeType.Is(types.NodeTypeReportAfterSuite) && suite.config.ParallelProcess != 1 {
-		return
-	}
-	// if we're running ReportBeforeSuite on proc > 1 - we should wait until proc 1 has completed
-	if nodeType.Is(types.NodeTypeReportBeforeSuite) && suite.config.ParallelProcess != 1 && len(nodes) > 0 {
-		state, err := suite.client.BlockUntilReportBeforeSuiteCompleted()
-		if err != nil || state.Is(types.SpecStateFailed) {
-			suite.report.SuiteSucceeded = false
-		}
-		return
-	}
-
-	for _, node := range nodes {
-		suite.selectiveLock.Lock()
-		suite.currentSpecReport = types.SpecReport{
-			LeafNodeType:      node.NodeType,
-			LeafNodeLocation:  node.CodeLocation,
-			LeafNodeText:      node.Text,
-			ParallelProcess:   suite.config.ParallelProcess,
-			RunningInParallel: suite.isRunningInParallel(),
-		}
-		suite.selectiveLock.Unlock()
-
-		suite.reporter.WillRun(suite.currentSpecReport)
-		suite.runReportSuiteNode(node, suite.report)
-		suite.processCurrentSpecReport()
-	}
-
-	// if we're running ReportBeforeSuite and we're running in parallel - we shuld tell the other procs that we're done
-	if nodeType.Is(types.NodeTypeReportBeforeSuite) && suite.isRunningInParallel() && len(nodes) > 0 {
-		if suite.report.SuiteSucceeded {
-			suite.client.PostReportBeforeSuiteCompleted(types.SpecStatePassed)
-		} else {
-			suite.client.PostReportBeforeSuiteCompleted(types.SpecStateFailed)
-		}
-	}
+	return
 }
 
-func (suite *Suite) runReportSuiteNode(node Node, report types.Report) {
+func (suite *Suite) runReportAfterSuiteNode(node Node, report types.Report) {
 	suite.writer.Truncate()
 	suite.outputInterceptor.StartInterceptingOutput()
 	suite.currentSpecReport.StartTime = time.Now()
 
-	// if we're running a ReportAfterSuite in parallel (on proc 1) we (a) wait until other procs have exited and
-	// (b) always fetch the latest report as prior ReportAfterSuites will contribute to it
-	if node.NodeType.Is(types.NodeTypeReportAfterSuite) && suite.isRunningInParallel() {
+	if suite.config.ParallelTotal > 1 {
 		aggregatedReport, err := suite.client.BlockUntilAggregatedNonprimaryProcsReport()
 		if err != nil {
 			suite.currentSpecReport.State, suite.currentSpecReport.Failure = types.SpecStateFailed, suite.failureForLeafNodeWithMessage(node, err.Error())
@@ -720,13 +691,15 @@ func (suite *Suite) runReportSuiteNode(node Node, report types.Report) {
 		report = report.Add(aggregatedReport)
 	}
 
-	node.Body = func(SpecContext) { node.ReportSuiteBody(report) }
+	node.Body = func(SpecContext) { node.ReportAfterSuiteBody(report) }
 	suite.currentSpecReport.State, suite.currentSpecReport.Failure = suite.runNode(node, time.Time{}, "")
 
 	suite.currentSpecReport.EndTime = time.Now()
 	suite.currentSpecReport.RunTime = suite.currentSpecReport.EndTime.Sub(suite.currentSpecReport.StartTime)
 	suite.currentSpecReport.CapturedGinkgoWriterOutput = string(suite.writer.Bytes())
 	suite.currentSpecReport.CapturedStdOutErr = suite.outputInterceptor.StopInterceptingAndReturnOutput()
+
+	return
 }
 
 func (suite *Suite) runNode(node Node, specDeadline time.Time, text string) (types.SpecState, types.Failure) {
diff --git a/vendor/github.com/onsi/ginkgo/v2/reporters/junit_report.go b/vendor/github.com/onsi/ginkgo/v2/reporters/junit_report.go
index fb87e24d7..576962e8f 100644
--- a/vendor/github.com/onsi/ginkgo/v2/reporters/junit_report.go
+++ b/vendor/github.com/onsi/ginkgo/v2/reporters/junit_report.go
@@ -30,12 +30,6 @@ type JunitReportConfig struct {
 
 	//Enable OmitCapturedStdOutErr to prevent captured stdout/stderr appearing in system-out
 	OmitCapturedStdOutErr bool
-
-	// Enable OmitSpecLabels to prevent labels from appearing in the spec name
-	OmitSpecLabels bool
-
-	// Enable OmitLeafNodeType to prevent the spec leaf node type from appearing in the spec name
-	OmitLeafNodeType bool
 }
 
 type JUnitTestSuites struct {
@@ -178,17 +172,13 @@ func GenerateJUnitReportWithConfig(report types.Report, dst string, config Junit
 	}
 	for _, spec := range report.SpecReports {
 		name := fmt.Sprintf("[%s]", spec.LeafNodeType)
-		if config.OmitLeafNodeType {
-			name = ""
-		}
 		if spec.FullText() != "" {
 			name = name + " " + spec.FullText()
 		}
 		labels := spec.Labels()
-		if len(labels) > 0 && !config.OmitSpecLabels {
+		if len(labels) > 0 {
 			name = name + " [" + strings.Join(labels, ", ") + "]"
 		}
-		name = strings.TrimSpace(name)
 
 		test := JUnitTestCase{
 			Name:      name,
diff --git a/vendor/github.com/onsi/ginkgo/v2/reporting_dsl.go b/vendor/github.com/onsi/ginkgo/v2/reporting_dsl.go
index f33786a2d..afc151b13 100644
--- a/vendor/github.com/onsi/ginkgo/v2/reporting_dsl.go
+++ b/vendor/github.com/onsi/ginkgo/v2/reporting_dsl.go
@@ -35,7 +35,7 @@ func CurrentSpecReport() SpecReport {
 }
 
 /*
-	ReportEntryVisibility governs the visibility of ReportEntries in Ginkgo's console reporter
+ ReportEntryVisibility governs the visibility of ReportEntries in Ginkgo's console reporter
 
 - ReportEntryVisibilityAlways: the default behavior - the ReportEntry is always emitted.
 - ReportEntryVisibilityFailureOrVerbose: the ReportEntry is only emitted if the spec fails or if the tests are run with -v (similar to GinkgoWriters behavior).
@@ -50,9 +50,9 @@ const ReportEntryVisibilityAlways, ReportEntryVisibilityFailureOrVerbose, Report
 /*
 AddReportEntry generates and adds a new ReportEntry to the current spec's SpecReport.
 It can take any of the following arguments:
-  - A single arbitrary object to attach as the Value of the ReportEntry.  This object will be included in any generated reports and will be emitted to the console when the report is emitted.
-  - A ReportEntryVisibility enum to control the visibility of the ReportEntry
-  - An Offset or CodeLocation decoration to control the reported location of the ReportEntry
+   - A single arbitrary object to attach as the Value of the ReportEntry.  This object will be included in any generated reports and will be emitted to the console when the report is emitted.
+   - A ReportEntryVisibility enum to control the visibility of the ReportEntry
+   - An Offset or CodeLocation decoration to control the reported location of the ReportEntry
 
 If the Value object implements `fmt.Stringer`, it's `String()` representation is used when emitting to the console.
 
@@ -100,25 +100,6 @@ func ReportAfterEach(body func(SpecReport), args ...interface{}) bool {
 	return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeReportAfterEach, "", combinedArgs...))
 }
 
-/*
-ReportBeforeSuite nodes are run at the beginning of the suite.  ReportBeforeSuite nodes take a function that receives a suite Report.
-
-They are called at the beginning of the suite, before any specs have run and any BeforeSuite or SynchronizedBeforeSuite nodes, and are passed in the initial report for the suite.
-ReportBeforeSuite nodes must be created at the top-level (i.e. not nested in a Context/Describe/When node)
-
-# When running in parallel, Ginkgo ensures that only one of the parallel nodes runs the ReportBeforeSuite
-
-You cannot nest any other Ginkgo nodes within a ReportAfterSuite node's closure.
-You can learn more about ReportAfterSuite here: https://onsi.github.io/ginkgo/#generating-reports-programmatically
-
-You can learn more about Ginkgo's reporting infrastructure, including generating reports with the CLI here: https://onsi.github.io/ginkgo/#generating-machine-readable-reports
-*/
-func ReportBeforeSuite(body func(Report), args ...interface{}) bool {
-	combinedArgs := []interface{}{body}
-	combinedArgs = append(combinedArgs, args...)
-	return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeReportBeforeSuite, "", combinedArgs...))
-}
-
 /*
 ReportAfterSuite nodes are run at the end of the suite.  ReportAfterSuite nodes take a function that receives a suite Report.
 
@@ -132,7 +113,6 @@ In addition to using ReportAfterSuite to programmatically generate suite reports
 
 You cannot nest any other Ginkgo nodes within a ReportAfterSuite node's closure.
 You can learn more about ReportAfterSuite here: https://onsi.github.io/ginkgo/#generating-reports-programmatically
-
 You can learn more about Ginkgo's reporting infrastructure, including generating reports with the CLI here: https://onsi.github.io/ginkgo/#generating-machine-readable-reports
 */
 func ReportAfterSuite(text string, body func(Report), args ...interface{}) bool {
diff --git a/vendor/github.com/onsi/ginkgo/v2/types/errors.go b/vendor/github.com/onsi/ginkgo/v2/types/errors.go
index a0f59fb4b..b7ed5a21e 100644
--- a/vendor/github.com/onsi/ginkgo/v2/types/errors.go
+++ b/vendor/github.com/onsi/ginkgo/v2/types/errors.go
@@ -108,8 +108,8 @@ Please ensure all assertions are inside leaf nodes such as {{bold}}BeforeEach{{/
 
 func (g ginkgoErrors) SuiteNodeInNestedContext(nodeType NodeType, cl CodeLocation) error {
 	docLink := "suite-setup-and-cleanup-beforesuite-and-aftersuite"
-	if nodeType.Is(NodeTypeReportBeforeSuite | NodeTypeReportAfterSuite) {
-		docLink = "reporting-nodes---reportbeforesuite-and-reportaftersuite"
+	if nodeType.Is(NodeTypeReportAfterSuite) {
+		docLink = "reporting-nodes---reportaftersuite"
 	}
 
 	return GinkgoError{
@@ -125,8 +125,8 @@ func (g ginkgoErrors) SuiteNodeInNestedContext(nodeType NodeType, cl CodeLocatio
 
 func (g ginkgoErrors) SuiteNodeDuringRunPhase(nodeType NodeType, cl CodeLocation) error {
 	docLink := "suite-setup-and-cleanup-beforesuite-and-aftersuite"
-	if nodeType.Is(NodeTypeReportBeforeSuite | NodeTypeReportAfterSuite) {
-		docLink = "reporting-nodes---reportbeforesuite-and-reportaftersuite"
+	if nodeType.Is(NodeTypeReportAfterSuite) {
+		docLink = "reporting-nodes---reportaftersuite"
 	}
 
 	return GinkgoError{
@@ -320,7 +320,7 @@ func (g ginkgoErrors) PushingCleanupNodeDuringTreeConstruction(cl CodeLocation)
 func (g ginkgoErrors) PushingCleanupInReportingNode(cl CodeLocation, nodeType NodeType) error {
 	return GinkgoError{
 		Heading:      fmt.Sprintf("DeferCleanup cannot be called in %s", nodeType),
-		Message:      "Please inline your cleanup code - Ginkgo won't run cleanup code after a Reporting node.",
+		Message:      "Please inline your cleanup code - Ginkgo won't run cleanup code after a ReportAfterEach or ReportAfterSuite.",
 		CodeLocation: cl,
 		DocLink:      "cleaning-up-our-cleanup-code-defercleanup",
 	}
diff --git a/vendor/github.com/onsi/ginkgo/v2/types/types.go b/vendor/github.com/onsi/ginkgo/v2/types/types.go
index 3e979ba8c..a388f70c0 100644
--- a/vendor/github.com/onsi/ginkgo/v2/types/types.go
+++ b/vendor/github.com/onsi/ginkgo/v2/types/types.go
@@ -58,7 +58,6 @@ type Report struct {
 	SuiteConfig SuiteConfig
 
 	//SpecReports is a list of all SpecReports generated by this test run
-	//It is empty when the SuiteReport is provided to ReportBeforeSuite
 	SpecReports SpecReports
 }
 
@@ -762,7 +761,6 @@ const (
 
 	NodeTypeReportBeforeEach
 	NodeTypeReportAfterEach
-	NodeTypeReportBeforeSuite
 	NodeTypeReportAfterSuite
 
 	NodeTypeCleanupInvalid
@@ -772,9 +770,9 @@ const (
 )
 
 var NodeTypesForContainerAndIt = NodeTypeContainer | NodeTypeIt
-var NodeTypesForSuiteLevelNodes = NodeTypeBeforeSuite | NodeTypeSynchronizedBeforeSuite | NodeTypeAfterSuite | NodeTypeSynchronizedAfterSuite | NodeTypeReportBeforeSuite | NodeTypeReportAfterSuite | NodeTypeCleanupAfterSuite
+var NodeTypesForSuiteLevelNodes = NodeTypeBeforeSuite | NodeTypeSynchronizedBeforeSuite | NodeTypeAfterSuite | NodeTypeSynchronizedAfterSuite | NodeTypeReportAfterSuite | NodeTypeCleanupAfterSuite
 var NodeTypesAllowedDuringCleanupInterrupt = NodeTypeAfterEach | NodeTypeJustAfterEach | NodeTypeAfterAll | NodeTypeAfterSuite | NodeTypeSynchronizedAfterSuite | NodeTypeCleanupAfterEach | NodeTypeCleanupAfterAll | NodeTypeCleanupAfterSuite
-var NodeTypesAllowedDuringReportInterrupt = NodeTypeReportBeforeEach | NodeTypeReportAfterEach | NodeTypeReportBeforeSuite | NodeTypeReportAfterSuite
+var NodeTypesAllowedDuringReportInterrupt = NodeTypeReportBeforeEach | NodeTypeReportAfterEach | NodeTypeReportAfterSuite
 
 var ntEnumSupport = NewEnumSupport(map[uint]string{
 	uint(NodeTypeInvalid):                 "INVALID NODE TYPE",
@@ -792,7 +790,6 @@ var ntEnumSupport = NewEnumSupport(map[uint]string{
 	uint(NodeTypeSynchronizedAfterSuite):  "SynchronizedAfterSuite",
 	uint(NodeTypeReportBeforeEach):        "ReportBeforeEach",
 	uint(NodeTypeReportAfterEach):         "ReportAfterEach",
-	uint(NodeTypeReportBeforeSuite):       "ReportBeforeSuite",
 	uint(NodeTypeReportAfterSuite):        "ReportAfterSuite",
 	uint(NodeTypeCleanupInvalid):          "DeferCleanup",
 	uint(NodeTypeCleanupAfterEach):        "DeferCleanup (Each)",
diff --git a/vendor/github.com/onsi/ginkgo/v2/types/version.go b/vendor/github.com/onsi/ginkgo/v2/types/version.go
index b874ca0fd..7745c9a7c 100644
--- a/vendor/github.com/onsi/ginkgo/v2/types/version.go
+++ b/vendor/github.com/onsi/ginkgo/v2/types/version.go
@@ -1,3 +1,3 @@
 package types
 
-const VERSION = "2.6.0"
+const VERSION = "2.5.1"
diff --git a/vendor/golang.org/x/crypto/cryptobyte/asn1.go b/vendor/golang.org/x/crypto/cryptobyte/asn1.go
index 401414dde..3a1674a1e 100644
--- a/vendor/golang.org/x/crypto/cryptobyte/asn1.go
+++ b/vendor/golang.org/x/crypto/cryptobyte/asn1.go
@@ -264,35 +264,36 @@ func (s *String) ReadASN1Boolean(out *bool) bool {
 	return true
 }
 
+var bigIntType = reflect.TypeOf((*big.Int)(nil)).Elem()
+
 // ReadASN1Integer decodes an ASN.1 INTEGER into out and advances. If out does
-// not point to an integer, to a big.Int, or to a []byte it panics. Only
-// positive and zero values can be decoded into []byte, and they are returned as
-// big-endian binary values that share memory with s. Positive values will have
-// no leading zeroes, and zero will be returned as a single zero byte.
-// ReadASN1Integer reports whether the read was successful.
+// not point to an integer or to a big.Int, it panics. It reports whether the
+// read was successful.
 func (s *String) ReadASN1Integer(out interface{}) bool {
-	switch out := out.(type) {
-	case *int, *int8, *int16, *int32, *int64:
+	if reflect.TypeOf(out).Kind() != reflect.Ptr {
+		panic("out is not a pointer")
+	}
+	switch reflect.ValueOf(out).Elem().Kind() {
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 		var i int64
 		if !s.readASN1Int64(&i) || reflect.ValueOf(out).Elem().OverflowInt(i) {
 			return false
 		}
 		reflect.ValueOf(out).Elem().SetInt(i)
 		return true
-	case *uint, *uint8, *uint16, *uint32, *uint64:
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 		var u uint64
 		if !s.readASN1Uint64(&u) || reflect.ValueOf(out).Elem().OverflowUint(u) {
 			return false
 		}
 		reflect.ValueOf(out).Elem().SetUint(u)
 		return true
-	case *big.Int:
-		return s.readASN1BigInt(out)
-	case *[]byte:
-		return s.readASN1Bytes(out)
-	default:
-		panic("out does not point to an integer type")
+	case reflect.Struct:
+		if reflect.TypeOf(out).Elem() == bigIntType {
+			return s.readASN1BigInt(out.(*big.Int))
+		}
 	}
+	panic("out does not point to an integer type")
 }
 
 func checkASN1Integer(bytes []byte) bool {
@@ -332,21 +333,6 @@ func (s *String) readASN1BigInt(out *big.Int) bool {
 	return true
 }
 
-func (s *String) readASN1Bytes(out *[]byte) bool {
-	var bytes String
-	if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) {
-		return false
-	}
-	if bytes[0]&0x80 == 0x80 {
-		return false
-	}
-	for len(bytes) > 1 && bytes[0] == 0 {
-		bytes = bytes[1:]
-	}
-	*out = bytes
-	return true
-}
-
 func (s *String) readASN1Int64(out *int64) bool {
 	var bytes String
 	if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) || !asn1Signed(out, bytes) {
@@ -546,7 +532,7 @@ func (s *String) ReadASN1BitString(out *encoding_asn1.BitString) bool {
 		return false
 	}
 
-	paddingBits := bytes[0]
+	paddingBits := uint8(bytes[0])
 	bytes = bytes[1:]
 	if paddingBits > 7 ||
 		len(bytes) == 0 && paddingBits != 0 ||
@@ -568,7 +554,7 @@ func (s *String) ReadASN1BitStringAsBytes(out *[]byte) bool {
 		return false
 	}
 
-	paddingBits := bytes[0]
+	paddingBits := uint8(bytes[0])
 	if paddingBits != 0 {
 		return false
 	}
@@ -668,27 +654,34 @@ func (s *String) SkipOptionalASN1(tag asn1.Tag) bool {
 	return s.ReadASN1(&unused, tag)
 }
 
-// ReadOptionalASN1Integer attempts to read an optional ASN.1 INTEGER explicitly
-// tagged with tag into out and advances. If no element with a matching tag is
-// present, it writes defaultValue into out instead. Otherwise, it behaves like
-// ReadASN1Integer.
+// ReadOptionalASN1Integer attempts to read an optional ASN.1 INTEGER
+// explicitly tagged with tag into out and advances. If no element with a
+// matching tag is present, it writes defaultValue into out instead. If out
+// does not point to an integer or to a big.Int, it panics. It reports
+// whether the read was successful.
 func (s *String) ReadOptionalASN1Integer(out interface{}, tag asn1.Tag, defaultValue interface{}) bool {
+	if reflect.TypeOf(out).Kind() != reflect.Ptr {
+		panic("out is not a pointer")
+	}
 	var present bool
 	var i String
 	if !s.ReadOptionalASN1(&i, &present, tag) {
 		return false
 	}
 	if !present {
-		switch out.(type) {
-		case *int, *int8, *int16, *int32, *int64,
-			*uint, *uint8, *uint16, *uint32, *uint64, *[]byte:
+		switch reflect.ValueOf(out).Elem().Kind() {
+		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
+			reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 			reflect.ValueOf(out).Elem().Set(reflect.ValueOf(defaultValue))
-		case *big.Int:
-			if defaultValue, ok := defaultValue.(*big.Int); ok {
-				out.(*big.Int).Set(defaultValue)
-			} else {
+		case reflect.Struct:
+			if reflect.TypeOf(out).Elem() != bigIntType {
+				panic("invalid integer type")
+			}
+			if reflect.TypeOf(defaultValue).Kind() != reflect.Ptr ||
+				reflect.TypeOf(defaultValue).Elem() != bigIntType {
 				panic("out points to big.Int, but defaultValue does not")
 			}
+			out.(*big.Int).Set(defaultValue.(*big.Int))
 		default:
 			panic("invalid integer type")
 		}
diff --git a/vendor/golang.org/x/crypto/pkcs12/internal/rc2/rc2.go b/vendor/golang.org/x/crypto/pkcs12/internal/rc2/rc2.go
index 05de9cc2c..7499e3fb6 100644
--- a/vendor/golang.org/x/crypto/pkcs12/internal/rc2/rc2.go
+++ b/vendor/golang.org/x/crypto/pkcs12/internal/rc2/rc2.go
@@ -14,7 +14,6 @@ package rc2
 import (
 	"crypto/cipher"
 	"encoding/binary"
-	"math/bits"
 )
 
 // The rc2 block size in bytes
@@ -81,6 +80,10 @@ func expandKey(key []byte, t1 int) [64]uint16 {
 	return k
 }
 
+func rotl16(x uint16, b uint) uint16 {
+	return (x >> (16 - b)) | (x << b)
+}
+
 func (c *rc2Cipher) Encrypt(dst, src []byte) {
 
 	r0 := binary.LittleEndian.Uint16(src[0:])
@@ -93,22 +96,22 @@ func (c *rc2Cipher) Encrypt(dst, src []byte) {
 	for j <= 16 {
 		// mix r0
 		r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
-		r0 = bits.RotateLeft16(r0, 1)
+		r0 = rotl16(r0, 1)
 		j++
 
 		// mix r1
 		r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2)
-		r1 = bits.RotateLeft16(r1, 2)
+		r1 = rotl16(r1, 2)
 		j++
 
 		// mix r2
 		r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3)
-		r2 = bits.RotateLeft16(r2, 3)
+		r2 = rotl16(r2, 3)
 		j++
 
 		// mix r3
 		r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0)
-		r3 = bits.RotateLeft16(r3, 5)
+		r3 = rotl16(r3, 5)
 		j++
 
 	}
@@ -121,22 +124,22 @@ func (c *rc2Cipher) Encrypt(dst, src []byte) {
 	for j <= 40 {
 		// mix r0
 		r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
-		r0 = bits.RotateLeft16(r0, 1)
+		r0 = rotl16(r0, 1)
 		j++
 
 		// mix r1
 		r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2)
-		r1 = bits.RotateLeft16(r1, 2)
+		r1 = rotl16(r1, 2)
 		j++
 
 		// mix r2
 		r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3)
-		r2 = bits.RotateLeft16(r2, 3)
+		r2 = rotl16(r2, 3)
 		j++
 
 		// mix r3
 		r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0)
-		r3 = bits.RotateLeft16(r3, 5)
+		r3 = rotl16(r3, 5)
 		j++
 
 	}
@@ -149,22 +152,22 @@ func (c *rc2Cipher) Encrypt(dst, src []byte) {
 	for j <= 60 {
 		// mix r0
 		r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
-		r0 = bits.RotateLeft16(r0, 1)
+		r0 = rotl16(r0, 1)
 		j++
 
 		// mix r1
 		r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2)
-		r1 = bits.RotateLeft16(r1, 2)
+		r1 = rotl16(r1, 2)
 		j++
 
 		// mix r2
 		r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3)
-		r2 = bits.RotateLeft16(r2, 3)
+		r2 = rotl16(r2, 3)
 		j++
 
 		// mix r3
 		r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0)
-		r3 = bits.RotateLeft16(r3, 5)
+		r3 = rotl16(r3, 5)
 		j++
 	}
 
@@ -185,22 +188,22 @@ func (c *rc2Cipher) Decrypt(dst, src []byte) {
 
 	for j >= 44 {
 		// unmix r3
-		r3 = bits.RotateLeft16(r3, 16-5)
+		r3 = rotl16(r3, 16-5)
 		r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0)
 		j--
 
 		// unmix r2
-		r2 = bits.RotateLeft16(r2, 16-3)
+		r2 = rotl16(r2, 16-3)
 		r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3)
 		j--
 
 		// unmix r1
-		r1 = bits.RotateLeft16(r1, 16-2)
+		r1 = rotl16(r1, 16-2)
 		r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2)
 		j--
 
 		// unmix r0
-		r0 = bits.RotateLeft16(r0, 16-1)
+		r0 = rotl16(r0, 16-1)
 		r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1)
 		j--
 	}
@@ -212,22 +215,22 @@ func (c *rc2Cipher) Decrypt(dst, src []byte) {
 
 	for j >= 20 {
 		// unmix r3
-		r3 = bits.RotateLeft16(r3, 16-5)
+		r3 = rotl16(r3, 16-5)
 		r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0)
 		j--
 
 		// unmix r2
-		r2 = bits.RotateLeft16(r2, 16-3)
+		r2 = rotl16(r2, 16-3)
 		r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3)
 		j--
 
 		// unmix r1
-		r1 = bits.RotateLeft16(r1, 16-2)
+		r1 = rotl16(r1, 16-2)
 		r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2)
 		j--
 
 		// unmix r0
-		r0 = bits.RotateLeft16(r0, 16-1)
+		r0 = rotl16(r0, 16-1)
 		r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1)
 		j--
 
@@ -240,22 +243,22 @@ func (c *rc2Cipher) Decrypt(dst, src []byte) {
 
 	for j >= 0 {
 		// unmix r3
-		r3 = bits.RotateLeft16(r3, 16-5)
+		r3 = rotl16(r3, 16-5)
 		r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0)
 		j--
 
 		// unmix r2
-		r2 = bits.RotateLeft16(r2, 16-3)
+		r2 = rotl16(r2, 16-3)
 		r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3)
 		j--
 
 		// unmix r1
-		r1 = bits.RotateLeft16(r1, 16-2)
+		r1 = rotl16(r1, 16-2)
 		r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2)
 		j--
 
 		// unmix r0
-		r0 = bits.RotateLeft16(r0, 16-1)
+		r0 = rotl16(r0, 16-1)
 		r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1)
 		j--
 
diff --git a/vendor/golang.org/x/crypto/salsa20/salsa/hsalsa20.go b/vendor/golang.org/x/crypto/salsa20/salsa/hsalsa20.go
index 3fd05b275..4c96147c8 100644
--- a/vendor/golang.org/x/crypto/salsa20/salsa/hsalsa20.go
+++ b/vendor/golang.org/x/crypto/salsa20/salsa/hsalsa20.go
@@ -5,8 +5,6 @@
 // Package salsa provides low-level access to functions in the Salsa family.
 package salsa // import "golang.org/x/crypto/salsa20/salsa"
 
-import "math/bits"
-
 // Sigma is the Salsa20 constant for 256-bit keys.
 var Sigma = [16]byte{'e', 'x', 'p', 'a', 'n', 'd', ' ', '3', '2', '-', 'b', 'y', 't', 'e', ' ', 'k'}
 
@@ -33,76 +31,76 @@ func HSalsa20(out *[32]byte, in *[16]byte, k *[32]byte, c *[16]byte) {
 
 	for i := 0; i < 20; i += 2 {
 		u := x0 + x12
-		x4 ^= bits.RotateLeft32(u, 7)
+		x4 ^= u<<7 | u>>(32-7)
 		u = x4 + x0
-		x8 ^= bits.RotateLeft32(u, 9)
+		x8 ^= u<<9 | u>>(32-9)
 		u = x8 + x4
-		x12 ^= bits.RotateLeft32(u, 13)
+		x12 ^= u<<13 | u>>(32-13)
 		u = x12 + x8
-		x0 ^= bits.RotateLeft32(u, 18)
+		x0 ^= u<<18 | u>>(32-18)
 
 		u = x5 + x1
-		x9 ^= bits.RotateLeft32(u, 7)
+		x9 ^= u<<7 | u>>(32-7)
 		u = x9 + x5
-		x13 ^= bits.RotateLeft32(u, 9)
+		x13 ^= u<<9 | u>>(32-9)
 		u = x13 + x9
-		x1 ^= bits.RotateLeft32(u, 13)
+		x1 ^= u<<13 | u>>(32-13)
 		u = x1 + x13
-		x5 ^= bits.RotateLeft32(u, 18)
+		x5 ^= u<<18 | u>>(32-18)
 
 		u = x10 + x6
-		x14 ^= bits.RotateLeft32(u, 7)
+		x14 ^= u<<7 | u>>(32-7)
 		u = x14 + x10
-		x2 ^= bits.RotateLeft32(u, 9)
+		x2 ^= u<<9 | u>>(32-9)
 		u = x2 + x14
-		x6 ^= bits.RotateLeft32(u, 13)
+		x6 ^= u<<13 | u>>(32-13)
 		u = x6 + x2
-		x10 ^= bits.RotateLeft32(u, 18)
+		x10 ^= u<<18 | u>>(32-18)
 
 		u = x15 + x11
-		x3 ^= bits.RotateLeft32(u, 7)
+		x3 ^= u<<7 | u>>(32-7)
 		u = x3 + x15
-		x7 ^= bits.RotateLeft32(u, 9)
+		x7 ^= u<<9 | u>>(32-9)
 		u = x7 + x3
-		x11 ^= bits.RotateLeft32(u, 13)
+		x11 ^= u<<13 | u>>(32-13)
 		u = x11 + x7
-		x15 ^= bits.RotateLeft32(u, 18)
+		x15 ^= u<<18 | u>>(32-18)
 
 		u = x0 + x3
-		x1 ^= bits.RotateLeft32(u, 7)
+		x1 ^= u<<7 | u>>(32-7)
 		u = x1 + x0
-		x2 ^= bits.RotateLeft32(u, 9)
+		x2 ^= u<<9 | u>>(32-9)
 		u = x2 + x1
-		x3 ^= bits.RotateLeft32(u, 13)
+		x3 ^= u<<13 | u>>(32-13)
 		u = x3 + x2
-		x0 ^= bits.RotateLeft32(u, 18)
+		x0 ^= u<<18 | u>>(32-18)
 
 		u = x5 + x4
-		x6 ^= bits.RotateLeft32(u, 7)
+		x6 ^= u<<7 | u>>(32-7)
 		u = x6 + x5
-		x7 ^= bits.RotateLeft32(u, 9)
+		x7 ^= u<<9 | u>>(32-9)
 		u = x7 + x6
-		x4 ^= bits.RotateLeft32(u, 13)
+		x4 ^= u<<13 | u>>(32-13)
 		u = x4 + x7
-		x5 ^= bits.RotateLeft32(u, 18)
+		x5 ^= u<<18 | u>>(32-18)
 
 		u = x10 + x9
-		x11 ^= bits.RotateLeft32(u, 7)
+		x11 ^= u<<7 | u>>(32-7)
 		u = x11 + x10
-		x8 ^= bits.RotateLeft32(u, 9)
+		x8 ^= u<<9 | u>>(32-9)
 		u = x8 + x11
-		x9 ^= bits.RotateLeft32(u, 13)
+		x9 ^= u<<13 | u>>(32-13)
 		u = x9 + x8
-		x10 ^= bits.RotateLeft32(u, 18)
+		x10 ^= u<<18 | u>>(32-18)
 
 		u = x15 + x14
-		x12 ^= bits.RotateLeft32(u, 7)
+		x12 ^= u<<7 | u>>(32-7)
 		u = x12 + x15
-		x13 ^= bits.RotateLeft32(u, 9)
+		x13 ^= u<<9 | u>>(32-9)
 		u = x13 + x12
-		x14 ^= bits.RotateLeft32(u, 13)
+		x14 ^= u<<13 | u>>(32-13)
 		u = x14 + x13
-		x15 ^= bits.RotateLeft32(u, 18)
+		x15 ^= u<<18 | u>>(32-18)
 	}
 	out[0] = byte(x0)
 	out[1] = byte(x0 >> 8)
diff --git a/vendor/golang.org/x/crypto/salsa20/salsa/salsa208.go b/vendor/golang.org/x/crypto/salsa20/salsa/salsa208.go
index 7ec7bb39b..9bfc0927c 100644
--- a/vendor/golang.org/x/crypto/salsa20/salsa/salsa208.go
+++ b/vendor/golang.org/x/crypto/salsa20/salsa/salsa208.go
@@ -4,8 +4,6 @@
 
 package salsa
 
-import "math/bits"
-
 // Core208 applies the Salsa20/8 core function to the 64-byte array in and puts
 // the result into the 64-byte array out. The input and output may be the same array.
 func Core208(out *[64]byte, in *[64]byte) {
@@ -31,76 +29,76 @@ func Core208(out *[64]byte, in *[64]byte) {
 
 	for i := 0; i < 8; i += 2 {
 		u := x0 + x12
-		x4 ^= bits.RotateLeft32(u, 7)
+		x4 ^= u<<7 | u>>(32-7)
 		u = x4 + x0
-		x8 ^= bits.RotateLeft32(u, 9)
+		x8 ^= u<<9 | u>>(32-9)
 		u = x8 + x4
-		x12 ^= bits.RotateLeft32(u, 13)
+		x12 ^= u<<13 | u>>(32-13)
 		u = x12 + x8
-		x0 ^= bits.RotateLeft32(u, 18)
+		x0 ^= u<<18 | u>>(32-18)
 
 		u = x5 + x1
-		x9 ^= bits.RotateLeft32(u, 7)
+		x9 ^= u<<7 | u>>(32-7)
 		u = x9 + x5
-		x13 ^= bits.RotateLeft32(u, 9)
+		x13 ^= u<<9 | u>>(32-9)
 		u = x13 + x9
-		x1 ^= bits.RotateLeft32(u, 13)
+		x1 ^= u<<13 | u>>(32-13)
 		u = x1 + x13
-		x5 ^= bits.RotateLeft32(u, 18)
+		x5 ^= u<<18 | u>>(32-18)
 
 		u = x10 + x6
-		x14 ^= bits.RotateLeft32(u, 7)
+		x14 ^= u<<7 | u>>(32-7)
 		u = x14 + x10
-		x2 ^= bits.RotateLeft32(u, 9)
+		x2 ^= u<<9 | u>>(32-9)
 		u = x2 + x14
-		x6 ^= bits.RotateLeft32(u, 13)
+		x6 ^= u<<13 | u>>(32-13)
 		u = x6 + x2
-		x10 ^= bits.RotateLeft32(u, 18)
+		x10 ^= u<<18 | u>>(32-18)
 
 		u = x15 + x11
-		x3 ^= bits.RotateLeft32(u, 7)
+		x3 ^= u<<7 | u>>(32-7)
 		u = x3 + x15
-		x7 ^= bits.RotateLeft32(u, 9)
+		x7 ^= u<<9 | u>>(32-9)
 		u = x7 + x3
-		x11 ^= bits.RotateLeft32(u, 13)
+		x11 ^= u<<13 | u>>(32-13)
 		u = x11 + x7
-		x15 ^= bits.RotateLeft32(u, 18)
+		x15 ^= u<<18 | u>>(32-18)
 
 		u = x0 + x3
-		x1 ^= bits.RotateLeft32(u, 7)
+		x1 ^= u<<7 | u>>(32-7)
 		u = x1 + x0
-		x2 ^= bits.RotateLeft32(u, 9)
+		x2 ^= u<<9 | u>>(32-9)
 		u = x2 + x1
-		x3 ^= bits.RotateLeft32(u, 13)
+		x3 ^= u<<13 | u>>(32-13)
 		u = x3 + x2
-		x0 ^= bits.RotateLeft32(u, 18)
+		x0 ^= u<<18 | u>>(32-18)
 
 		u = x5 + x4
-		x6 ^= bits.RotateLeft32(u, 7)
+		x6 ^= u<<7 | u>>(32-7)
 		u = x6 + x5
-		x7 ^= bits.RotateLeft32(u, 9)
+		x7 ^= u<<9 | u>>(32-9)
 		u = x7 + x6
-		x4 ^= bits.RotateLeft32(u, 13)
+		x4 ^= u<<13 | u>>(32-13)
 		u = x4 + x7
-		x5 ^= bits.RotateLeft32(u, 18)
+		x5 ^= u<<18 | u>>(32-18)
 
 		u = x10 + x9
-		x11 ^= bits.RotateLeft32(u, 7)
+		x11 ^= u<<7 | u>>(32-7)
 		u = x11 + x10
-		x8 ^= bits.RotateLeft32(u, 9)
+		x8 ^= u<<9 | u>>(32-9)
 		u = x8 + x11
-		x9 ^= bits.RotateLeft32(u, 13)
+		x9 ^= u<<13 | u>>(32-13)
 		u = x9 + x8
-		x10 ^= bits.RotateLeft32(u, 18)
+		x10 ^= u<<18 | u>>(32-18)
 
 		u = x15 + x14
-		x12 ^= bits.RotateLeft32(u, 7)
+		x12 ^= u<<7 | u>>(32-7)
 		u = x12 + x15
-		x13 ^= bits.RotateLeft32(u, 9)
+		x13 ^= u<<9 | u>>(32-9)
 		u = x13 + x12
-		x14 ^= bits.RotateLeft32(u, 13)
+		x14 ^= u<<13 | u>>(32-13)
 		u = x14 + x13
-		x15 ^= bits.RotateLeft32(u, 18)
+		x15 ^= u<<18 | u>>(32-18)
 	}
 	x0 += j0
 	x1 += j1
diff --git a/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_ref.go b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_ref.go
index e5cdb9a25..68169c6d6 100644
--- a/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_ref.go
+++ b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_ref.go
@@ -4,8 +4,6 @@
 
 package salsa
 
-import "math/bits"
-
 const rounds = 20
 
 // core applies the Salsa20 core function to 16-byte input in, 32-byte key k,
@@ -33,76 +31,76 @@ func core(out *[64]byte, in *[16]byte, k *[32]byte, c *[16]byte) {
 
 	for i := 0; i < rounds; i += 2 {
 		u := x0 + x12
-		x4 ^= bits.RotateLeft32(u, 7)
+		x4 ^= u<<7 | u>>(32-7)
 		u = x4 + x0
-		x8 ^= bits.RotateLeft32(u, 9)
+		x8 ^= u<<9 | u>>(32-9)
 		u = x8 + x4
-		x12 ^= bits.RotateLeft32(u, 13)
+		x12 ^= u<<13 | u>>(32-13)
 		u = x12 + x8
-		x0 ^= bits.RotateLeft32(u, 18)
+		x0 ^= u<<18 | u>>(32-18)
 
 		u = x5 + x1
-		x9 ^= bits.RotateLeft32(u, 7)
+		x9 ^= u<<7 | u>>(32-7)
 		u = x9 + x5
-		x13 ^= bits.RotateLeft32(u, 9)
+		x13 ^= u<<9 | u>>(32-9)
 		u = x13 + x9
-		x1 ^= bits.RotateLeft32(u, 13)
+		x1 ^= u<<13 | u>>(32-13)
 		u = x1 + x13
-		x5 ^= bits.RotateLeft32(u, 18)
+		x5 ^= u<<18 | u>>(32-18)
 
 		u = x10 + x6
-		x14 ^= bits.RotateLeft32(u, 7)
+		x14 ^= u<<7 | u>>(32-7)
 		u = x14 + x10
-		x2 ^= bits.RotateLeft32(u, 9)
+		x2 ^= u<<9 | u>>(32-9)
 		u = x2 + x14
-		x6 ^= bits.RotateLeft32(u, 13)
+		x6 ^= u<<13 | u>>(32-13)
 		u = x6 + x2
-		x10 ^= bits.RotateLeft32(u, 18)
+		x10 ^= u<<18 | u>>(32-18)
 
 		u = x15 + x11
-		x3 ^= bits.RotateLeft32(u, 7)
+		x3 ^= u<<7 | u>>(32-7)
 		u = x3 + x15
-		x7 ^= bits.RotateLeft32(u, 9)
+		x7 ^= u<<9 | u>>(32-9)
 		u = x7 + x3
-		x11 ^= bits.RotateLeft32(u, 13)
+		x11 ^= u<<13 | u>>(32-13)
 		u = x11 + x7
-		x15 ^= bits.RotateLeft32(u, 18)
+		x15 ^= u<<18 | u>>(32-18)
 
 		u = x0 + x3
-		x1 ^= bits.RotateLeft32(u, 7)
+		x1 ^= u<<7 | u>>(32-7)
 		u = x1 + x0
-		x2 ^= bits.RotateLeft32(u, 9)
+		x2 ^= u<<9 | u>>(32-9)
 		u = x2 + x1
-		x3 ^= bits.RotateLeft32(u, 13)
+		x3 ^= u<<13 | u>>(32-13)
 		u = x3 + x2
-		x0 ^= bits.RotateLeft32(u, 18)
+		x0 ^= u<<18 | u>>(32-18)
 
 		u = x5 + x4
-		x6 ^= bits.RotateLeft32(u, 7)
+		x6 ^= u<<7 | u>>(32-7)
 		u = x6 + x5
-		x7 ^= bits.RotateLeft32(u, 9)
+		x7 ^= u<<9 | u>>(32-9)
 		u = x7 + x6
-		x4 ^= bits.RotateLeft32(u, 13)
+		x4 ^= u<<13 | u>>(32-13)
 		u = x4 + x7
-		x5 ^= bits.RotateLeft32(u, 18)
+		x5 ^= u<<18 | u>>(32-18)
 
 		u = x10 + x9
-		x11 ^= bits.RotateLeft32(u, 7)
+		x11 ^= u<<7 | u>>(32-7)
 		u = x11 + x10
-		x8 ^= bits.RotateLeft32(u, 9)
+		x8 ^= u<<9 | u>>(32-9)
 		u = x8 + x11
-		x9 ^= bits.RotateLeft32(u, 13)
+		x9 ^= u<<13 | u>>(32-13)
 		u = x9 + x8
-		x10 ^= bits.RotateLeft32(u, 18)
+		x10 ^= u<<18 | u>>(32-18)
 
 		u = x15 + x14
-		x12 ^= bits.RotateLeft32(u, 7)
+		x12 ^= u<<7 | u>>(32-7)
 		u = x12 + x15
-		x13 ^= bits.RotateLeft32(u, 9)
+		x13 ^= u<<9 | u>>(32-9)
 		u = x13 + x12
-		x14 ^= bits.RotateLeft32(u, 13)
+		x14 ^= u<<13 | u>>(32-13)
 		u = x14 + x13
-		x15 ^= bits.RotateLeft32(u, 18)
+		x15 ^= u<<18 | u>>(32-18)
 	}
 	x0 += j0
 	x1 += j1
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 6556f29df..4e785c917 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -204,7 +204,7 @@ github.com/modern-go/reflect2
 # github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
 ## explicit
 github.com/munnerz/goautoneg
-# github.com/onsi/ginkgo/v2 v2.6.0
+# github.com/onsi/ginkgo/v2 v2.5.1
 ## explicit; go 1.18
 github.com/onsi/ginkgo/v2
 github.com/onsi/ginkgo/v2/config
@@ -363,7 +363,7 @@ go.uber.org/zap/internal/color
 go.uber.org/zap/internal/exit
 go.uber.org/zap/zapcore
 go.uber.org/zap/zapgrpc
-# golang.org/x/crypto v0.4.0
+# golang.org/x/crypto v0.1.0
 ## explicit; go 1.17
 golang.org/x/crypto/cryptobyte
 golang.org/x/crypto/cryptobyte/asn1


Test patch:
diff --git a/.pipelines/scripts/run-e2e.sh b/.pipelines/scripts/run-e2e.sh
index e8261c2d3..3334f378f 100755
--- a/.pipelines/scripts/run-e2e.sh
+++ b/.pipelines/scripts/run-e2e.sh
@@ -69,7 +69,6 @@ rm -rf kubetest2-aks
 git clone https://github.com/kubernetes-sigs/cloud-provider-azure.git
 cp -r cloud-provider-azure/kubetest2-aks .
 rm -rf cloud-provider-azure
-git config --global --add safe.directory "$(pwd)" || true
 pushd kubetest2-aks
 go get -d sigs.k8s.io/kubetest2@latest
 go install sigs.k8s.io/kubetest2@latest
diff --git a/pkg/cache/azure_cache_test.go b/pkg/cache/azure_cache_test.go
index 50b110d59..82e30d0e6 100644
--- a/pkg/cache/azure_cache_test.go
+++ b/pkg/cache/azure_cache_test.go
@@ -98,7 +98,7 @@ func TestCacheGet(t *testing.T) {
 	for _, c := range cases {
 		dataSource, cache := newFakeCache(t)
 		dataSource.set(c.data)
-		val, err := cache.Get(c.key, CacheReadTypeDefault)
+		val, err := cache.GetWithDeepCopy(c.key, CacheReadTypeDefault)
 		assert.NoError(t, err, c.name)
 		assert.Equal(t, c.expected, val, c.name)
 	}
@@ -112,7 +112,7 @@ func TestCacheGetError(t *testing.T) {
 	cache, err := NewTimedcache(fakeCacheTTL, getter)
 	assert.NoError(t, err)
 
-	val, err := cache.Get("key", CacheReadTypeDefault)
+	val, err := cache.GetWithDeepCopy("key", CacheReadTypeDefault)
 	assert.Error(t, err)
 	assert.Equal(t, getError, err)
 	assert.Nil(t, val)
@@ -140,7 +140,7 @@ func TestCacheGetWithDeepCopy(t *testing.T) {
 			dataSource, cache := newFakeCache(t)
 			dataSource.set(c.data)
 			cache.Set(c.key, valFake)
-			val, err := cache.Get(c.key, CacheReadTypeDefault)
+			val, err := cache.GetWithDeepCopy(c.key, CacheReadTypeDefault)
 			assert.NoError(t, err)
 			assert.Equal(t, c.expected, val.(*fakeDataObj).Data)
 
@@ -160,13 +160,13 @@ func TestCacheDelete(t *testing.T) {
 	dataSource, cache := newFakeCache(t)
 	dataSource.set(data)
 
-	v, err := cache.Get(testKey, CacheReadTypeDefault)
+	v, err := cache.GetWithDeepCopy(testKey, CacheReadTypeDefault)
 	assert.NoError(t, err)
 	assert.Equal(t, val, v, "cache should get correct data")
 
 	dataSource.set(nil)
 	_ = cache.Delete(testKey)
-	v, err = cache.Get(testKey, CacheReadTypeDefault)
+	v, err = cache.GetWithDeepCopy(testKey, CacheReadTypeDefault)
 	assert.NoError(t, err)
 	assert.Equal(t, 1, dataSource.called)
 	assert.Equal(t, nil, v, "cache should get nil after data is removed")
@@ -180,13 +180,13 @@ func TestCacheExpired(t *testing.T) {
 	dataSource, cache := newFakeCache(t)
 	dataSource.set(data)
 
-	v, err := cache.Get(testKey, CacheReadTypeDefault)
+	v, err := cache.GetWithDeepCopy(testKey, CacheReadTypeDefault)
 	assert.NoError(t, err)
 	assert.Equal(t, 1, dataSource.called)
 	assert.Equal(t, val, v, "cache should get correct data")
 
 	time.Sleep(fakeCacheTTL)
-	v, err = cache.Get(testKey, CacheReadTypeDefault)
+	v, err = cache.GetWithDeepCopy(testKey, CacheReadTypeDefault)
 	assert.NoError(t, err)
 	assert.Equal(t, 2, dataSource.called)
 	assert.Equal(t, val, v, "cache should get correct data even after expired")
@@ -200,13 +200,13 @@ func TestCacheAllowUnsafeRead(t *testing.T) {
 	dataSource, cache := newFakeCache(t)
 	dataSource.set(data)
 
-	v, err := cache.Get(testKey, CacheReadTypeDefault)
+	v, err := cache.GetWithDeepCopy(testKey, CacheReadTypeDefault)
 	assert.NoError(t, err)
 	assert.Equal(t, 1, dataSource.called)
 	assert.Equal(t, val, v, "cache should get correct data")
 
 	time.Sleep(fakeCacheTTL)
-	v, err = cache.Get(testKey, CacheReadTypeUnsafe)
+	v, err = cache.GetWithDeepCopy(testKey, CacheReadTypeUnsafe)
 	assert.NoError(t, err)
 	assert.Equal(t, 1, dataSource.called)
 	assert.Equal(t, val, v, "cache should return expired as allow unsafe read is allowed")
@@ -226,10 +226,10 @@ func TestCacheNoConcurrentGet(t *testing.T) {
 		wg.Add(1)
 		go func() {
 			defer wg.Done()
-			_, _ = cache.Get(testKey, CacheReadTypeDefault)
+			_, _ = cache.GetWithDeepCopy(testKey, CacheReadTypeDefault)
 		}()
 	}
-	v, err := cache.Get(testKey, CacheReadTypeDefault)
+	v, err := cache.GetWithDeepCopy(testKey, CacheReadTypeDefault)
 	wg.Wait()
 	assert.NoError(t, err)
 	assert.Equal(t, 1, dataSource.called)
@@ -244,12 +244,12 @@ func TestCacheForceRefresh(t *testing.T) {
 	dataSource, cache := newFakeCache(t)
 	dataSource.set(data)
 
-	v, err := cache.Get(testKey, CacheReadTypeDefault)
+	v, err := cache.GetWithDeepCopy(testKey, CacheReadTypeDefault)
 	assert.NoError(t, err)
 	assert.Equal(t, 1, dataSource.called)
 	assert.Equal(t, val, v, "cache should get correct data")
 
-	v, err = cache.Get(testKey, CacheReadTypeForceRefresh)
+	v, err = cache.GetWithDeepCopy(testKey, CacheReadTypeForceRefresh)
 	assert.NoError(t, err)
 	assert.Equal(t, 2, dataSource.called)
 	assert.Equal(t, val, v, "should refetch unexpired data as forced refresh")
diff --git a/pkg/provider/azure_backoff_test.go b/pkg/provider/azure_backoff_test.go
index a661b05b0..76db3e1b7 100644
--- a/pkg/provider/azure_backoff_test.go
+++ b/pkg/provider/azure_backoff_test.go
@@ -234,7 +234,7 @@ func TestCreateOrUpdateSecurityGroupCanceled(t *testing.T) {
 	assert.EqualError(t, fmt.Errorf("Retriable: false, RetryAfter: 0s, HTTPStatusCode: 0, RawError: %w", fmt.Errorf("canceledandsupersededduetoanotheroperation")), err.Error())
 
 	// security group should be removed from cache if the operation is canceled
-	shouldBeEmpty, err := az.nsgCache.Get("sg", cache.CacheReadTypeDefault)
+	shouldBeEmpty, err := az.nsgCache.GetWithDeepCopy("sg", cache.CacheReadTypeDefault)
 	assert.NoError(t, err)
 	assert.Empty(t, shouldBeEmpty)
 }
@@ -287,12 +287,12 @@ func TestCreateOrUpdateLB(t *testing.T) {
 		assert.EqualError(t, test.expectedErr, err.Error())
 
 		// loadbalancer should be removed from cache if the etag is mismatch or the operation is canceled
-		shouldBeEmpty, err := az.lbCache.Get("lb", cache.CacheReadTypeDefault)
+		shouldBeEmpty, err := az.lbCache.GetWithDeepCopy("lb", cache.CacheReadTypeDefault)
 		assert.NoError(t, err)
 		assert.Empty(t, shouldBeEmpty)
 
 		// public ip cache should be populated since there's GetPIP
-		shouldNotBeEmpty, err := az.pipCache.Get(az.getPIPCacheKey(az.ResourceGroup, "pip"), cache.CacheReadTypeDefault)
+		shouldNotBeEmpty, err := az.pipCache.GetWithDeepCopy(az.getPIPCacheKey(az.ResourceGroup, "pip"), cache.CacheReadTypeDefault)
 		assert.NoError(t, err)
 		assert.NotEmpty(t, shouldNotBeEmpty)
 	}
@@ -417,7 +417,7 @@ func TestCreateOrUpdatePIP(t *testing.T) {
 		err := az.CreateOrUpdatePIP(&v1.Service{}, az.ResourceGroup, network.PublicIPAddress{Name: to.StringPtr("nic")})
 		assert.EqualError(t, test.expectedErr, err.Error())
 
-		cachedPIP, err := az.pipCache.Get(az.getPIPCacheKey(az.ResourceGroup, "nic"), cache.CacheReadTypeDefault)
+		cachedPIP, err := az.pipCache.GetWithDeepCopy(az.getPIPCacheKey(az.ResourceGroup, "nic"), cache.CacheReadTypeDefault)
 		assert.NoError(t, err)
 		if test.cacheExpectedEmpty {
 			assert.Empty(t, cachedPIP)
@@ -496,7 +496,7 @@ func TestCreateOrUpdateRouteTable(t *testing.T) {
 		assert.EqualError(t, test.expectedErr, err.Error())
 
 		// route table should be removed from cache if the etag is mismatch or the operation is canceled
-		shouldBeEmpty, err := az.rtCache.Get("rt", cache.CacheReadTypeDefault)
+		shouldBeEmpty, err := az.rtCache.GetWithDeepCopy("rt", cache.CacheReadTypeDefault)
 		assert.NoError(t, err)
 		assert.Empty(t, shouldBeEmpty)
 	}
@@ -542,7 +542,7 @@ func TestCreateOrUpdateRoute(t *testing.T) {
 			assert.EqualError(t, test.expectedErr, err.Error())
 		}
 
-		shouldBeEmpty, err := az.rtCache.Get("rt", cache.CacheReadTypeDefault)
+		shouldBeEmpty, err := az.rtCache.GetWithDeepCopy("rt", cache.CacheReadTypeDefault)
 		assert.NoError(t, err)
 		assert.Empty(t, shouldBeEmpty)
 	}
diff --git a/pkg/provider/azure_controller_common_test.go b/pkg/provider/azure_controller_common_test.go
index 2474360cd..6ce2110c3 100644
--- a/pkg/provider/azure_controller_common_test.go
+++ b/pkg/provider/azure_controller_common_test.go
@@ -18,19 +18,14 @@ package provider
 
 import (
 	"context"
-	"encoding/json"
-	"errors"
 	"fmt"
 	"net/http"
 	"reflect"
-	"strconv"
 	"strings"
 	"testing"
-	"time"
 
 	"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2022-03-01/compute"
 	"github.com/Azure/go-autorest/autorest/azure"
-	autorestmocks "github.com/Azure/go-autorest/autorest/mocks"
 	"github.com/Azure/go-autorest/autorest/to"
 	"github.com/golang/mock/gomock"
 	"github.com/stretchr/testify/assert"
@@ -49,41 +44,6 @@ import (
 	"sigs.k8s.io/cloud-provider-azure/pkg/retry"
 )
 
-var (
-	operationPreemptedError      = retry.NewError(false, errors.New(`Code="OperationPreempted" Message="Operation execution has been preempted by a more recent operation."`))
-	conflictingUserInputError    = retry.NewError(false, errors.New(`Code="ConflictingUserInput" Message="Cannot attach the disk pvc-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx to VM /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx/resourceGroups/test-rg/providers/Microsoft.Compute/virtualMachineScaleSets/aks-nodepool0-00000000-vmss/virtualMachines/aks-nodepool0-00000000-vmss_0 because it is already attached to VM /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx/resourceGroups/test-rg/providers/Microsoft.Compute/virtualMachineScaleSets/aks-nodepool0-00000000-vmss/virtualMachines/aks-nodepool0-00000000-vmss_1. A disk can be attached to only one VM at a time."`))
-	vmExtensionProvisioningError = retry.NewError(false, errors.New(`Code="VMExtensionProvisioningError" Message="Multiple VM extensions failed to be provisioned on the VM. Please see the VM extension instance view for other failures. The first extension failed due to the error: VM 'aks-nodepool0-00000000-vmss_00' has not reported status for VM agent or extensions. Verify that the OS is up and healthy, the VM has a running VM agent, and that it can establish outbound connections to Azure storage. Please refer to https://aka.ms/vmextensionlinuxtroubleshoot for additional VM agent troubleshooting information."`))
-)
-
-func fakeUpdateAsync(statusCode int) func(context.Context, string, string, compute.VirtualMachineUpdate, string) (*azure.Future, *retry.Error) {
-	return func(ctx context.Context, resourceGroup, nodeName string, parameters compute.VirtualMachineUpdate, source string) (*azure.Future, *retry.Error) {
-		vm := &compute.VirtualMachine{
-			Name:                     &nodeName,
-			Plan:                     parameters.Plan,
-			VirtualMachineProperties: parameters.VirtualMachineProperties,
-			Identity:                 parameters.Identity,
-			Zones:                    parameters.Zones,
-			Tags:                     parameters.Tags,
-		}
-		s, err := json.Marshal(vm)
-		if err != nil {
-			return nil, retry.NewError(false, err)
-		}
-
-		body := autorestmocks.NewBodyWithBytes(s)
-
-		r := autorestmocks.NewResponseWithBodyAndStatus(body, statusCode, strconv.Itoa(statusCode))
-		r.Request.Method = http.MethodPut
-
-		f, err := azure.NewFutureFromResponse(r)
-		if err != nil {
-			return nil, retry.NewError(false, err)
-		}
-
-		return &f, nil
-	}
-}
-
 func TestCommonAttachDisk(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
@@ -91,46 +51,22 @@ func TestCommonAttachDisk(t *testing.T) {
 	ctx, cancel := getContextWithCancel()
 	defer cancel()
 
-	initVM := func(testCloud *Cloud, expectedVMs []compute.VirtualMachine) {
-		mockVMsClient := testCloud.VirtualMachinesClient.(*mockvmclient.MockInterface)
-		for _, vm := range expectedVMs {
-			mockVMsClient.EXPECT().Get(gomock.Any(), testCloud.ResourceGroup, *vm.Name, gomock.Any()).Return(vm, nil).AnyTimes()
-		}
-		if len(expectedVMs) == 0 {
-			mockVMsClient.EXPECT().Get(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any()).Return(compute.VirtualMachine{}, &retry.Error{HTTPStatusCode: http.StatusNotFound, RawError: cloudprovider.InstanceNotFound}).AnyTimes()
-		}
-	}
-
-	defaultSetup := func(testCloud *Cloud, expectedVMs []compute.VirtualMachine, statusCode int, result *retry.Error) {
-		initVM(testCloud, expectedVMs)
-		mockVMsClient := testCloud.VirtualMachinesClient.(*mockvmclient.MockInterface)
-		mockVMsClient.EXPECT().UpdateAsync(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(fakeUpdateAsync(statusCode)).MaxTimes(1)
-		mockVMsClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).MaxTimes(1)
-		mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.resourceGroup, gomock.Any()).Return(result).MaxTimes(1)
-	}
-
 	maxShare := int32(1)
 	goodInstanceID := fmt.Sprintf("/subscriptions/subscription/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/%s", "vm1")
 	diskEncryptionSetID := fmt.Sprintf("/subscriptions/subscription/resourceGroups/rg/providers/Microsoft.Compute/diskEncryptionSets/%s", "diskEncryptionSet-name")
 	testTags := make(map[string]*string)
 	testTags[WriteAcceleratorEnabled] = to.StringPtr("true")
 	testCases := []struct {
-		desc                 string
-		diskName             string
-		existedDisk          *compute.Disk
-		nodeName             types.NodeName
-		vmList               map[string]string
-		isDataDisksFull      bool
-		isBadDiskURI         bool
-		isDiskUsed           bool
-		setup                func(testCloud *Cloud, expectedVMs []compute.VirtualMachine, statusCode int, result *retry.Error)
-		expectErr            bool
-		isAcceptedErr        bool
-		isContextDeadlineErr bool
-		statusCode           int
-		waitResult           *retry.Error
-		expectedLun          int32
-		contextDuration      time.Duration
+		desc            string
+		diskName        string
+		existedDisk     *compute.Disk
+		nodeName        types.NodeName
+		vmList          map[string]string
+		isDataDisksFull bool
+		isBadDiskURI    bool
+		isDiskUsed      bool
+		expectedErr     bool
+		expectedLun     int32
 	}{
 		{
 			desc:        "correct LUN and no error shall be returned if disk is nil",
@@ -139,8 +75,7 @@ func TestCommonAttachDisk(t *testing.T) {
 			diskName:    "disk-name",
 			existedDisk: nil,
 			expectedLun: 3,
-			expectErr:   false,
-			statusCode:  200,
+			expectedErr: false,
 		},
 		{
 			desc:        "LUN -1 and error shall be returned if there's no such instance corresponding to given nodeName",
@@ -148,7 +83,7 @@ func TestCommonAttachDisk(t *testing.T) {
 			diskName:    "disk-name",
 			existedDisk: &compute.Disk{Name: to.StringPtr("disk-name")},
 			expectedLun: -1,
-			expectErr:   true,
+			expectedErr: true,
 		},
 		{
 			desc:            "LUN -1 and error shall be returned if there's no available LUN for instance",
@@ -158,7 +93,7 @@ func TestCommonAttachDisk(t *testing.T) {
 			diskName:        "disk-name",
 			existedDisk:     &compute.Disk{Name: to.StringPtr("disk-name")},
 			expectedLun:     -1,
-			expectErr:       true,
+			expectedErr:     true,
 		},
 		{
 			desc:     "correct LUN and no error shall be returned if everything is good",
@@ -173,8 +108,7 @@ func TestCommonAttachDisk(t *testing.T) {
 				},
 				Tags: testTags},
 			expectedLun: 3,
-			expectErr:   false,
-			statusCode:  200,
+			expectedErr: false,
 		},
 		{
 			desc:     "an error shall be returned if disk state is not Unattached",
@@ -189,7 +123,7 @@ func TestCommonAttachDisk(t *testing.T) {
 				},
 				Tags: testTags},
 			expectedLun: -1,
-			expectErr:   true,
+			expectedErr: true,
 		},
 		{
 			desc:        "an error shall be returned if attach an already attached disk with good ManagedBy instance id",
@@ -198,233 +132,55 @@ func TestCommonAttachDisk(t *testing.T) {
 			diskName:    "disk-name",
 			existedDisk: &compute.Disk{Name: to.StringPtr("disk-name"), ManagedBy: to.StringPtr(goodInstanceID), DiskProperties: &compute.DiskProperties{MaxShares: &maxShare}},
 			expectedLun: -1,
-			expectErr:   true,
-		},
-		{
-			desc:          "should return a PartialUpdateError type when storage configuration was accepted but wait fails with an error",
-			vmList:        map[string]string{"vm1": "PowerState/Running"},
-			nodeName:      "vm1",
-			diskName:      "disk-name",
-			existedDisk:   nil,
-			expectedLun:   -1,
-			expectErr:     true,
-			statusCode:    200,
-			waitResult:    conflictingUserInputError,
-			isAcceptedErr: true,
-		},
-		{
-			desc:        "should not return a PartialUpdateError type when storage configuration was not accepted",
-			vmList:      map[string]string{"vm1": "PowerState/Running"},
-			nodeName:    "vm1",
-			diskName:    "disk-name",
-			existedDisk: nil,
-			expectedLun: -1,
-			expectErr:   true,
-			statusCode:  400,
-			waitResult:  conflictingUserInputError,
-		},
-		{
-			desc:        "should retry on OperationPreempted error and succeed",
-			vmList:      map[string]string{"vm1": "PowerState/Running"},
-			nodeName:    "vm1",
-			diskName:    "disk-name",
-			existedDisk: nil,
-			expectedLun: 3,
-			expectErr:   false,
-			statusCode:  200,
-			waitResult:  operationPreemptedError,
-			setup: func(testCloud *Cloud, expectedVMs []compute.VirtualMachine, statusCode int, result *retry.Error) {
-				defaultSetup(testCloud, expectedVMs, statusCode, result)
-				mockVMsClient := testCloud.VirtualMachinesClient.(*mockvmclient.MockInterface)
-				mockVMsClient.EXPECT().UpdateAsync(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(fakeUpdateAsync(200)).AnyTimes()
-				gomock.InOrder(
-					mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.resourceGroup, gomock.Any()).Return(result),
-					mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.resourceGroup, gomock.Any()).Return(nil),
-				)
-			},
+			expectedErr: true,
 		},
 		{
-			desc:        "should retry on OperationPreempted error until context deadline and return context.DeadlineExceeded error",
+			desc:        "an error shall be returned if attach an already attached disk with bad ManagedBy instance id",
 			vmList:      map[string]string{"vm1": "PowerState/Running"},
 			nodeName:    "vm1",
 			diskName:    "disk-name",
-			existedDisk: nil,
+			existedDisk: &compute.Disk{Name: to.StringPtr("disk-name"), ManagedBy: to.StringPtr("test"), DiskProperties: &compute.DiskProperties{MaxShares: &maxShare}},
 			expectedLun: -1,
-			expectErr:   true,
-			statusCode:  200,
-			waitResult:  operationPreemptedError,
-			setup: func(testCloud *Cloud, expectedVMs []compute.VirtualMachine, statusCode int, result *retry.Error) {
-				defaultSetup(testCloud, expectedVMs, statusCode, result)
-				mockVMsClient := testCloud.VirtualMachinesClient.(*mockvmclient.MockInterface)
-				mockVMsClient.EXPECT().UpdateAsync(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(fakeUpdateAsync(200)).AnyTimes()
-				mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.resourceGroup, gomock.Any()).Return(result).AnyTimes()
-			},
-			contextDuration:      time.Second,
-			isContextDeadlineErr: true,
+			expectedErr: true,
 		},
 	}
 
 	for i, test := range testCases {
-		tt := test
-		t.Run(tt.desc, func(t *testing.T) {
-			testCloud := GetTestCloud(ctrl)
-			diskURI := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/disks/%s",
+		testCloud := GetTestCloud(ctrl)
+		common := &controllerCommon{
+			location:              testCloud.Location,
+			storageEndpointSuffix: testCloud.Environment.StorageEndpointSuffix,
+			resourceGroup:         testCloud.ResourceGroup,
+			subscriptionID:        testCloud.SubscriptionID,
+			cloud:                 testCloud,
+			lockMap:               newLockMap(),
+			diskOpRateLimiter:     flowcontrol.NewTokenBucketRateLimiter(10, 20),
+		}
+		diskURI := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/disks/%s",
+			testCloud.SubscriptionID, testCloud.ResourceGroup, test.diskName)
+		if test.isBadDiskURI {
+			diskURI = fmt.Sprintf("/baduri/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/disks/%s",
 				testCloud.SubscriptionID, testCloud.ResourceGroup, test.diskName)
-			if tt.isBadDiskURI {
-				diskURI = fmt.Sprintf("/baduri/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/disks/%s",
-					testCloud.SubscriptionID, testCloud.ResourceGroup, tt.diskName)
-			}
-			expectedVMs := setTestVirtualMachines(testCloud, tt.vmList, tt.isDataDisksFull)
-			if tt.isDiskUsed {
-				vm0 := setTestVirtualMachines(testCloud, map[string]string{"vm0": "PowerState/Running"}, tt.isDataDisksFull)[0]
-				expectedVMs = append(expectedVMs, vm0)
-			}
-			if tt.setup == nil {
-				defaultSetup(testCloud, expectedVMs, tt.statusCode, tt.waitResult)
-			} else {
-				tt.setup(testCloud, expectedVMs, tt.statusCode, tt.waitResult)
-			}
-
-			if tt.contextDuration > 0 {
-				oldCtx := ctx
-				ctx, cancel = context.WithTimeout(oldCtx, tt.contextDuration)
-				defer cancel()
-				defer func() {
-					ctx = oldCtx
-				}()
-			}
-
-			lun, err := testCloud.AttachDisk(ctx, true, "", diskURI, tt.nodeName, compute.CachingTypesReadOnly, tt.existedDisk)
-
-			assert.Equal(t, tt.expectedLun, lun, "TestCase[%d]: %s", i, tt.desc)
-			assert.Equal(t, tt.expectErr, err != nil, "TestCase[%d]: %s, return error: %v", i, tt.desc, err)
-
-			if tt.isAcceptedErr {
-				assert.IsType(t, &retry.PartialUpdateError{}, err)
-			} else {
-				var partialUpdateError *retry.PartialUpdateError
-				ok := errors.As(err, &partialUpdateError)
-				assert.False(t, ok, "the returned error should not be AcceptedError type")
-			}
-
-			assert.Equal(t, tt.isContextDeadlineErr, errors.Is(err, context.DeadlineExceeded))
-		})
-	}
-}
-
-func TestWaitForUpdateResult(t *testing.T) {
-	ctrl := gomock.NewController(t)
-	defer ctrl.Finish()
-
-	ctx, cancel := context.WithCancel(context.Background())
-	defer cancel()
-
-	defaultSetup := func(testCloud *Cloud, statusCode int, result *retry.Error) {
+		}
+		expectedVMs := setTestVirtualMachines(testCloud, test.vmList, test.isDataDisksFull)
+		if test.isDiskUsed {
+			vm0 := setTestVirtualMachines(testCloud, map[string]string{"vm0": "PowerState/Running"}, test.isDataDisksFull)[0]
+			expectedVMs = append(expectedVMs, vm0)
+		}
 		mockVMsClient := testCloud.VirtualMachinesClient.(*mockvmclient.MockInterface)
-		mockVMsClient.EXPECT().UpdateAsync(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(fakeUpdateAsync(statusCode)).MaxTimes(1)
-		mockVMsClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).MaxTimes(1)
-		mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.resourceGroup, gomock.Any()).Return(result).MaxTimes(1)
-	}
-
-	testCases := []struct {
-		desc              string
-		nodeName          string
-		statusCode        int
-		waitResult        *retry.Error
-		expectErr         bool
-		expectedErrorType error
-		setup             func(testCloud *Cloud, statusCode int, result *retry.Error)
-		contextDuration   time.Duration
-	}{
-		{
-			desc:              "should return a PartialUpdateError type when storage configuration was accepted but wait fails with an error",
-			nodeName:          "vm1",
-			expectErr:         true,
-			statusCode:        200,
-			waitResult:        conflictingUserInputError,
-			expectedErrorType: &retry.PartialUpdateError{},
-		},
-		{
-			desc:              "should return a PartialUpdateError type when storage configuration was accepted but wait fails with an error",
-			nodeName:          "vm1",
-			expectErr:         true,
-			statusCode:        200,
-			waitResult:        conflictingUserInputError,
-			expectedErrorType: &retry.PartialUpdateError{},
-		},
-		{
-			desc:              "should not return a PartialUpdateError type when storage configuration was not accepted",
-			nodeName:          "vm1",
-			expectErr:         true,
-			statusCode:        400,
-			waitResult:        conflictingUserInputError,
-			expectedErrorType: conflictingUserInputError.Error(),
-		},
-		{
-			desc:       "should retry on OperationPreempted error and succeed",
-			nodeName:   "vm1",
-			expectErr:  false,
-			statusCode: 200,
-			waitResult: operationPreemptedError,
-			setup: func(testCloud *Cloud, statusCode int, result *retry.Error) {
-				defaultSetup(testCloud, statusCode, result)
-				mockVMsClient := testCloud.VirtualMachinesClient.(*mockvmclient.MockInterface)
-				mockVMsClient.EXPECT().UpdateAsync(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(fakeUpdateAsync(200))
-				gomock.InOrder(
-					mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.resourceGroup, gomock.Any()).Return(result),
-					mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.resourceGroup, gomock.Any()).Return(nil),
-				)
-			},
-		},
-		{
-			desc:     "should retry on OperationPreempted error until context deadline and return context.DeadlineExceeded error",
-			nodeName: "vm1",
-
-			expectErr:  true,
-			statusCode: 200,
-			waitResult: operationPreemptedError,
-			setup: func(testCloud *Cloud, statusCode int, result *retry.Error) {
-				defaultSetup(testCloud, statusCode, result)
-				mockVMsClient := testCloud.VirtualMachinesClient.(*mockvmclient.MockInterface)
-				mockVMsClient.EXPECT().UpdateAsync(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(fakeUpdateAsync(200)).AnyTimes()
-				mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.resourceGroup, gomock.Any()).Return(result).AnyTimes()
-			},
-			contextDuration:   time.Second,
-			expectedErrorType: context.DeadlineExceeded,
-		},
-	}
-
-	for i, test := range testCases {
-		tt := test
-		t.Run(tt.desc, func(t *testing.T) {
-			testCloud := GetTestCloud(ctrl)
-			if tt.setup == nil {
-				defaultSetup(testCloud, tt.statusCode, tt.waitResult)
-			} else {
-				tt.setup(testCloud, tt.statusCode, tt.waitResult)
-			}
-
-			if tt.contextDuration > 0 {
-				oldCtx := ctx
-				ctx, cancel = context.WithTimeout(oldCtx, tt.contextDuration)
-				defer cancel()
-				defer func() {
-					ctx = oldCtx
-				}()
-			}
-
-			r := autorestmocks.NewResponseWithStatus(strconv.Itoa(tt.statusCode), tt.statusCode)
-			r.Request.Method = http.MethodPut
-			future, _ := azure.NewFutureFromResponse(r)
-
-			err := testCloud.waitForUpdateResult(ctx, testCloud.VMSet, types.NodeName(tt.nodeName), &future, nil)
-
-			assert.Equal(t, tt.expectErr, err != nil, "TestCase[%d]: %s, return error: %v", i, tt.desc, err)
+		for _, vm := range expectedVMs {
+			mockVMsClient.EXPECT().Get(gomock.Any(), testCloud.ResourceGroup, *vm.Name, gomock.Any()).Return(vm, nil).AnyTimes()
+		}
+		if len(expectedVMs) == 0 {
+			mockVMsClient.EXPECT().Get(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any()).Return(compute.VirtualMachine{}, &retry.Error{HTTPStatusCode: http.StatusNotFound, RawError: cloudprovider.InstanceNotFound}).AnyTimes()
+		}
+		mockVMsClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
+		mockVMsClient.EXPECT().UpdateAsync(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(&azure.Future{}, nil).AnyTimes()
+		mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.ResourceGroup, gomock.Any()).Return(nil).AnyTimes()
 
-			if tt.expectErr {
-				assert.IsType(t, tt.expectedErrorType, err)
-			}
-		})
+		lun, err := common.AttachDisk(ctx, true, "", diskURI, test.nodeName, compute.CachingTypesReadOnly, test.existedDisk)
+		assert.Equal(t, test.expectedLun, lun, "TestCase[%d]: %s", i, test.desc)
+		assert.Equal(t, test.expectedErr, err != nil, "TestCase[%d]: %s, return error: %v", i, test.desc, err)
 	}
 }
 
@@ -650,22 +406,15 @@ func TestCommonUpdateVM(t *testing.T) {
 		if len(expectedVMs) == 0 {
 			mockVMsClient.EXPECT().Get(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any()).Return(compute.VirtualMachine{}, &retry.Error{HTTPStatusCode: http.StatusNotFound, RawError: cloudprovider.InstanceNotFound}).AnyTimes()
 		}
-		r := autorestmocks.NewResponseWithStatus("200", 200)
-		r.Request.Method = http.MethodPut
-
-		future, err := azure.NewFutureFromResponse(r)
-
-		mockVMsClient.EXPECT().UpdateAsync(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(&future, err).AnyTimes()
-
 		if test.isErrorRetriable {
 			testCloud.CloudProviderBackoff = true
 			testCloud.ResourceRequestBackoff = wait.Backoff{Steps: 1}
-			mockVMsClient.EXPECT().WaitForUpdateResult(ctx, &future, testCloud.ResourceGroup, gomock.Any()).Return(&retry.Error{HTTPStatusCode: http.StatusBadRequest, Retriable: true, RawError: fmt.Errorf("Retriable: true")}).AnyTimes()
+			mockVMsClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(&retry.Error{HTTPStatusCode: http.StatusBadRequest, Retriable: true, RawError: fmt.Errorf("Retriable: true")}).AnyTimes()
 		} else {
-			mockVMsClient.EXPECT().WaitForUpdateResult(ctx, &future, testCloud.ResourceGroup, gomock.Any()).Return(nil).AnyTimes()
+			mockVMsClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
 		}
 
-		err = common.UpdateVM(ctx, test.nodeName)
+		err := common.UpdateVM(ctx, test.nodeName)
 		assert.Equal(t, test.expectedErr, err != nil, "TestCase[%d]: %s, err: %v", i, test.desc, err)
 	}
 }
@@ -1337,123 +1086,3 @@ func TestDetachDiskRequestFuncs(t *testing.T) {
 		}
 	}
 }
-
-func TestGetAzureErrorCode(t *testing.T) {
-	testCases := []struct {
-		desc           string
-		err            error
-		expectedResult string
-	}{
-		{
-			desc:           "should return OperationPreempted",
-			err:            operationPreemptedError.Error(),
-			expectedResult: consts.OperationPreemptedErrorCode,
-		},
-		{
-			desc:           "should return VMExtensionprovisioning",
-			err:            vmExtensionProvisioningError.Error(),
-			expectedResult: "VMExtensionProvisioningError",
-		},
-		{
-			desc:           "should return ConflictingUserInput",
-			err:            conflictingUserInputError.Error(),
-			expectedResult: "ConflictingUserInput",
-		},
-	}
-	for i, test := range testCases {
-		tt := test
-		t.Run(tt.desc, func(t *testing.T) {
-			result := getAzureErrorCode(test.err)
-			assert.Equal(t, test.expectedResult, result, "TestCase[%d]", i, result)
-		})
-	}
-}
-
-func TestVmUpdateRequired(t *testing.T) {
-	ctrl := gomock.NewController(t)
-	defer ctrl.Finish()
-
-	r := autorestmocks.NewResponseWithStatus("200", 200)
-	r.Request.Method = http.MethodPut
-
-	acceptedFuture, _ := azure.NewFutureFromResponse(r)
-
-	r = autorestmocks.NewResponseWithStatus("400", 400)
-	r.Request.Method = http.MethodPut
-
-	rejectedFuture, _ := azure.NewFutureFromResponse(r)
-
-	testCases := []struct {
-		desc           string
-		configAccepted bool
-		err            error
-		expectedResult bool
-	}{
-		{
-			desc:           "should return true if OperationPreemption error is returned with a http status code 2xx",
-			configAccepted: true,
-			err:            operationPreemptedError.Error(),
-			expectedResult: true,
-		},
-		{
-			desc:           "should return false if OperationPreemption error is returned with a http status code != 2xx",
-			configAccepted: false,
-			err:            operationPreemptedError.Error(),
-			expectedResult: false,
-		},
-		{
-			desc:           "should return false if ConflictingUserInputError error is returned even if http status code == 2xx",
-			configAccepted: true,
-			err:            conflictingUserInputError.Error(),
-			expectedResult: false,
-		},
-	}
-	for i, test := range testCases {
-		tt := test
-		t.Run(tt.desc, func(t *testing.T) {
-			var future *azure.Future
-			if tt.configAccepted {
-				future = &acceptedFuture
-			} else {
-				future = &rejectedFuture
-			}
-			result := vmUpdateRequired(future, test.err)
-			assert.Equalf(t, test.expectedResult, result, "TestCase[%d] returned %v", i, result)
-		})
-	}
-}
-
-func TestConfigAccepted(t *testing.T) {
-	ctrl := gomock.NewController(t)
-	defer ctrl.Finish()
-
-	testCases := []struct {
-		desc           string
-		statusCode     int
-		expectedResult bool
-	}{
-		{
-			desc:           "should return true if HTTP status code is 2xx",
-			statusCode:     200,
-			expectedResult: true,
-		},
-		{
-			desc:           "should return false if HTTP status code is not 2xx",
-			statusCode:     400,
-			expectedResult: false,
-		},
-	}
-
-	for i, testCase := range testCases {
-		tt := testCase
-		t.Run(tt.desc, func(t *testing.T) {
-			r := autorestmocks.NewResponseWithStatus(strconv.Itoa(tt.statusCode), tt.statusCode)
-			r.Request.Method = http.MethodPut
-
-			future, _ := azure.NewFutureFromResponse(r)
-
-			result := configAccepted(&future)
-			assert.Equalf(t, tt.expectedResult, result, "TestCase[%d] returned %v", i, result)
-		})
-	}
-}
diff --git a/pkg/provider/azure_controller_standard_test.go b/pkg/provider/azure_controller_standard_test.go
index 3be4edba5..1c1c13a2a 100644
--- a/pkg/provider/azure_controller_standard_test.go
+++ b/pkg/provider/azure_controller_standard_test.go
@@ -23,8 +23,6 @@ import (
 	"time"
 
 	"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2022-03-01/compute"
-	"github.com/Azure/go-autorest/autorest/azure"
-	autorestmocks "github.com/Azure/go-autorest/autorest/mocks"
 	"github.com/Azure/go-autorest/autorest/to"
 	"github.com/golang/mock/gomock"
 	"github.com/stretchr/testify/assert"
@@ -258,21 +256,13 @@ func TestStandardUpdateVM(t *testing.T) {
 			mockVMsClient.EXPECT().Get(gomock.Any(), testCloud.ResourceGroup, *vm.Name, gomock.Any()).Return(vm, nil).AnyTimes()
 		}
 		mockVMsClient.EXPECT().Get(gomock.Any(), testCloud.ResourceGroup, "vm2", gomock.Any()).Return(compute.VirtualMachine{}, &retry.Error{HTTPStatusCode: http.StatusNotFound, RawError: cloudprovider.InstanceNotFound}).AnyTimes()
-
-		r := autorestmocks.NewResponseWithStatus("200", 200)
-		r.Request.Method = http.MethodPut
-
-		future, err := azure.NewFutureFromResponse(r)
-
-		mockVMsClient.EXPECT().UpdateAsync(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(&future, err).AnyTimes()
-
 		if test.isDetachFail {
-			mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), &future, testCloud.ResourceGroup, gomock.Any()).Return(&retry.Error{HTTPStatusCode: http.StatusNotFound, RawError: cloudprovider.InstanceNotFound}).AnyTimes()
+			mockVMsClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(&retry.Error{HTTPStatusCode: http.StatusNotFound, RawError: cloudprovider.InstanceNotFound}).AnyTimes()
 		} else {
-			mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), &future, testCloud.ResourceGroup, gomock.Any()).Return(nil).AnyTimes()
+			mockVMsClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
 		}
 
-		err = vmSet.UpdateVM(ctx, test.nodeName)
+		err := vmSet.UpdateVM(ctx, test.nodeName)
 		assert.Equal(t, test.expectedError, err != nil, "TestCase[%d]: %s", i, test.desc)
 		if !test.expectedError && test.diskName != "" {
 			dataDisks, _, err := vmSet.GetDataDisks(test.nodeName, azcache.CacheReadTypeDefault)
diff --git a/pkg/provider/azure_controller_vmss_test.go b/pkg/provider/azure_controller_vmss_test.go
index 17ef1931e..48b038c5c 100644
--- a/pkg/provider/azure_controller_vmss_test.go
+++ b/pkg/provider/azure_controller_vmss_test.go
@@ -23,8 +23,6 @@ import (
 	"testing"
 
 	"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2022-03-01/compute"
-	"github.com/Azure/go-autorest/autorest/azure"
-	autorestmocks "github.com/Azure/go-autorest/autorest/mocks"
 	"github.com/Azure/go-autorest/autorest/to"
 	"github.com/golang/mock/gomock"
 	"github.com/stretchr/testify/assert"
@@ -388,18 +386,10 @@ func TestUpdateVMWithVMSS(t *testing.T) {
 		}
 		mockVMSSVMClient := testCloud.VirtualMachineScaleSetVMsClient.(*mockvmssvmclient.MockInterface)
 		mockVMSSVMClient.EXPECT().List(gomock.Any(), testCloud.ResourceGroup, scaleSetName, gomock.Any()).Return(expectedVMSSVMs, nil).AnyTimes()
-
-		r := autorestmocks.NewResponseWithStatus("200", 200)
-		r.Request.Method = http.MethodPut
-
-		future, err := azure.NewFutureFromResponse(r)
-
-		mockVMSSVMClient.EXPECT().UpdateAsync(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&future, err).AnyTimes()
-
 		if scaleSetName == strings.ToLower(string(fakeStatusNotFoundVMSSName)) {
-			mockVMSSVMClient.EXPECT().WaitForUpdateResult(gomock.Any(), &future, testCloud.ResourceGroup, gomock.Any()).Return(updatedVMSSVM, &retry.Error{HTTPStatusCode: http.StatusNotFound, RawError: cloudprovider.InstanceNotFound}).AnyTimes()
+			mockVMSSVMClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, scaleSetName, gomock.Any(), gomock.Any(), gomock.Any()).Return(updatedVMSSVM, &retry.Error{HTTPStatusCode: http.StatusNotFound, RawError: cloudprovider.InstanceNotFound}).AnyTimes()
 		} else {
-			mockVMSSVMClient.EXPECT().WaitForUpdateResult(gomock.Any(), &future, testCloud.ResourceGroup, gomock.Any()).Return(updatedVMSSVM, nil).AnyTimes()
+			mockVMSSVMClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, scaleSetName, gomock.Any(), gomock.Any(), gomock.Any()).Return(updatedVMSSVM, nil).AnyTimes()
 		}
 
 		mockVMClient := testCloud.VirtualMachinesClient.(*mockvmclient.MockInterface)
diff --git a/pkg/provider/azure_controller_vmssflex_test.go b/pkg/provider/azure_controller_vmssflex_test.go
index 5eb06864a..ae1dc9e34 100644
--- a/pkg/provider/azure_controller_vmssflex_test.go
+++ b/pkg/provider/azure_controller_vmssflex_test.go
@@ -22,8 +22,6 @@ import (
 	"testing"
 
 	"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2022-03-01/compute"
-	"github.com/Azure/go-autorest/autorest/azure"
-	autorestmocks "github.com/Azure/go-autorest/autorest/mocks"
 	"github.com/Azure/go-autorest/autorest/to"
 	"github.com/golang/mock/gomock"
 	"github.com/stretchr/testify/assert"
@@ -265,14 +263,7 @@ func TestUpdateVMWithVmssFlex(t *testing.T) {
 		mockVMClient := fs.VirtualMachinesClient.(*mockvmclient.MockInterface)
 		mockVMClient.EXPECT().ListVmssFlexVMsWithoutInstanceView(gomock.Any(), gomock.Any()).Return(tc.testVMListWithoutInstanceView, tc.vmListErr).AnyTimes()
 		mockVMClient.EXPECT().ListVmssFlexVMsWithOnlyInstanceView(gomock.Any(), gomock.Any()).Return(tc.testVMListWithOnlyInstanceView, tc.vmListErr).AnyTimes()
-
-		r := autorestmocks.NewResponseWithStatus("200", 200)
-		r.Request.Method = http.MethodPut
-
-		future, err := azure.NewFutureFromResponse(r)
-
-		mockVMClient.EXPECT().UpdateAsync(gomock.Any(), gomock.Any(), tc.vmName, gomock.Any(), "update_vm").Return(&future, err).AnyTimes()
-		mockVMClient.EXPECT().WaitForUpdateResult(gomock.Any(), &future, gomock.Any(), gomock.Any()).Return(tc.vmssFlexVMUpdateError).AnyTimes()
+		mockVMClient.EXPECT().Update(gomock.Any(), gomock.Any(), tc.vmName, gomock.Any(), "update_vm").Return(tc.vmssFlexVMUpdateError).AnyTimes()
 
 		err = fs.UpdateVM(ctx, tc.nodeName)
 
diff --git a/pkg/provider/azure_vmss_test.go b/pkg/provider/azure_vmss_test.go
index c5fb0e83b..aa1a29832 100644
--- a/pkg/provider/azure_vmss_test.go
+++ b/pkg/provider/azure_vmss_test.go
@@ -959,7 +959,7 @@ func TestGetVmssVMByNodeIdentity(t *testing.T) {
 			virtualMachines, err := ss.getVMSSVMsFromCache(ss.ResourceGroup, testVMSSName, azcache.CacheReadTypeDefault)
 			assert.Nil(t, err)
 			for _, vm := range test.goneVMList {
-				entry := VMSSVirtualMachinesEntry{
+				entry := VMSSVirtualMachineEntry{
 					ResourceGroup: ss.ResourceGroup,
 					VMSSName:      testVMSSName,
 				}
diff --git a/tests/e2e/network/node.go b/tests/e2e/network/node.go
index 0460246bc..a0e7f5ede 100644
--- a/tests/e2e/network/node.go
+++ b/tests/e2e/network/node.go
@@ -322,11 +322,11 @@ var _ = Describe("Azure nodes", func() {
 
 		utils.Logf("scaling VMSS")
 		count := *vmss.Sku.Capacity
-		err = utils.ScaleVMSS(tc, *vmss.Name, tc.GetResourceGroup(), int64(vmssScaleUpCelling))
+		err = utils.ScaleVMSS(tc, *vmss.Name, int64(vmssScaleUpCelling))
 
 		defer func() {
 			utils.Logf("restoring VMSS")
-			err = utils.ScaleVMSS(tc, *vmss.Name, tc.GetResourceGroup(), count)
+			err = utils.ScaleVMSS(tc, *vmss.Name, count)
 			Expect(err).NotTo(HaveOccurred())
 		}()
 
diff --git a/tests/e2e/node/vmss.go b/tests/e2e/node/vmss.go
index 7f11e9a1a..07094fd67 100644
--- a/tests/e2e/node/vmss.go
+++ b/tests/e2e/node/vmss.go
@@ -17,9 +17,6 @@ limitations under the License.
 package node
 
 import (
-	"os"
-	"strings"
-
 	"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2022-03-01/compute"
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
@@ -30,7 +27,7 @@ import (
 	"sigs.k8s.io/cloud-provider-azure/tests/e2e/utils"
 )
 
-var _ = Describe("Lifecycle of VMSS", Label(utils.TestSuiteLabelVMSS), func() {
+var _ = Describe("Lifecycle of VMSS", Label(utils.TestSuiteLabelVMSS, utils.TestSuiteLabelVMSSScale), func() {
 	var (
 		ns     *v1.Namespace
 		k8sCli kubernetes.Interface
@@ -73,21 +70,13 @@ var _ = Describe("Lifecycle of VMSS", Label(utils.TestSuiteLabelVMSS), func() {
 		Expect(err).NotTo(HaveOccurred())
 
 		By("deallocate VMSS instance")
-		if strings.EqualFold(os.Getenv(utils.CAPZTestCCM), "true") {
-			err = utils.ScaleMachinePool(*vmss.Name, numInstance-1)
-		} else {
-			err = utils.ScaleVMSS(azCli, *vmss.Name, azCli.GetResourceGroup(), numInstance-1)
-		}
+		err = utils.Scale(azCli, *vmss.Name, numInstance-1)
 		Expect(err).NotTo(HaveOccurred())
 		expectedCap[*vmss.Name] = numInstance - 1
 
 		defer func() {
 			By("reset VMSS instance")
-			if strings.EqualFold(os.Getenv(utils.CAPZTestCCM), "true") {
-				err = utils.ScaleMachinePool(*vmss.Name, numInstance)
-			} else {
-				err = utils.ScaleVMSS(azCli, *vmss.Name, azCli.GetResourceGroup(), numInstance)
-			}
+			err = utils.Scale(azCli, *vmss.Name, numInstance)
 			Expect(err).NotTo(HaveOccurred())
 			expectedCap[*vmss.Name] = numInstance
 
@@ -117,21 +106,13 @@ var _ = Describe("Lifecycle of VMSS", Label(utils.TestSuiteLabelVMSS), func() {
 		Expect(err).NotTo(HaveOccurred())
 
 		By("allocate VMSS instance")
-		if strings.EqualFold(os.Getenv(utils.CAPZTestCCM), "true") {
-			err = utils.ScaleMachinePool(*vmss.Name, numInstance+1)
-		} else {
-			err = utils.ScaleVMSS(azCli, *vmss.Name, azCli.GetResourceGroup(), numInstance+1)
-		}
+		err = utils.Scale(azCli, *vmss.Name, numInstance+1)
 		Expect(err).NotTo(HaveOccurred())
 		expectedCap[*vmss.Name] = numInstance + 1
 
 		defer func() {
 			By("reset VMSS instance")
-			if strings.EqualFold(os.Getenv(utils.CAPZTestCCM), "true") {
-				err = utils.ScaleMachinePool(*vmss.Name, numInstance)
-			} else {
-				err = utils.ScaleVMSS(azCli, *vmss.Name, azCli.GetResourceGroup(), numInstance)
-			}
+			err = utils.Scale(azCli, *vmss.Name, numInstance)
 			Expect(err).NotTo(HaveOccurred())
 			expectedCap[*vmss.Name] = numInstance
 
diff --git a/tests/e2e/utils/consts.go b/tests/e2e/utils/consts.go
index ab9b60985..f85a125fe 100644
--- a/tests/e2e/utils/consts.go
+++ b/tests/e2e/utils/consts.go
@@ -24,6 +24,7 @@ const (
 	TestSuiteLabelMultiNodePools     = "Multi-Nodepool"
 	TestSuiteLabelSingleNodePool     = "Single-Nodepool"
 	TestSuiteLabelVMSS               = "VMSS"
+	TestSuiteLabelVMSSScale          = "VMSS-Scale"
 	TestSuiteLabelSpotVM             = "Spot-VM"
 	TestSuiteLabelKubenet            = "Kubenet"
 	TestSuiteLabelMultiGroup         = "Multi-Group"
diff --git a/tests/e2e/utils/vmss_utils.go b/tests/e2e/utils/vmss_utils.go
index 08350ffea..4a4e74c4e 100644
--- a/tests/e2e/utils/vmss_utils.go
+++ b/tests/e2e/utils/vmss_utils.go
@@ -19,6 +19,7 @@ package utils
 import (
 	"context"
 	"fmt"
+	"os"
 	"regexp"
 	"strings"
 
@@ -53,9 +54,17 @@ func FindTestVMSS(tc *AzureTestClient, rgName string) (*azcompute.VirtualMachine
 	return &vmssList[0], nil
 }
 
+func Scale(tc *AzureTestClient, vmssName string, instanceCount int64) error {
+	if strings.EqualFold(os.Getenv(CAPZTestCCM), "true") {
+		return ScaleMachinePool(vmssName, instanceCount)
+	}
+	return ScaleVMSS(tc, vmssName, instanceCount)
+}
+
 // ScaleVMSS scales the given VMSS
-func ScaleVMSS(tc *AzureTestClient, vmssName, rgName string, instanceCount int64) (err error) {
+func ScaleVMSS(tc *AzureTestClient, vmssName string, instanceCount int64) (err error) {
 	Logf("ScaleVMSS: start")
+	rgName := tc.GetResourceGroup()
 
 	vmssClient := tc.createVMSSClient()
 
@@ -178,10 +187,11 @@ func ValidateClusterNodesMatchVMSSInstances(tc *AzureTestClient, expectedCap map
 				Logf("Failed to validate VMSS instances: %s", err)
 			}
 
-			Logf("Matching cluster nodes[%s] with VMSS instances[%s]",
+			Logf("Matching cluster nodes[%s] with VMSS instances[%s]\nExpected capacity: %v, actual capacity: %v",
 				strings.Join(nodeSet.List(), ","),
-				strings.Join(instanceSet.List(), ","))
-			Logf("Expected capacity: %v, actual one: %v", expectedCap, actualCap)
+				strings.Join(instanceSet.List(), ","),
+				expectedCap,
+				actualCap)
 		}()
 
 		nodes, err = GetAgentNodes(k8sCli)
@@ -196,6 +206,10 @@ func ValidateClusterNodesMatchVMSSInstances(tc *AzureTestClient, expectedCap map
 		vmssList, _ := ListUniformVMSSes(tc)
 		capMatch := true
 		for _, vmss := range vmssList {
+			cap, ok := expectedCap[*vmss.Name]
+			if !ok {
+				continue
+			}
 			vms, err := ListVMSSVMs(tc, *vmss.Name)
 			if err != nil {
 				return false, err
@@ -210,15 +224,23 @@ func ValidateClusterNodesMatchVMSSInstances(tc *AzureTestClient, expectedCap map
 				vmssInstanceSet.Insert(strings.ToLower(nodeName))
 				instanceSet.Insert(strings.ToLower(nodeName))
 			}
-			cap, ok := expectedCap[*vmss.Name]
-			if ok {
-				actualCap[*vmss.Name] = *vmss.Sku.Capacity
-				// For autoscaling cluster, simply comparing the capacity may not work since if the number of current nodes is lower than the "minCount", a new node may be created after scaling down.
-				// In this situation, we compare the expected capacity with the length of intersection between original nodes and current nodes.
-				if cap != *vmss.Sku.Capacity && cap != int64(originalNodeSet.Intersection(vmssInstanceSet).Len()) {
+
+			actualCap[*vmss.Name] = *vmss.Sku.Capacity
+			if cap != *vmss.Sku.Capacity {
+				if !strings.Contains(os.Getenv(AKSClusterType), "autoscaling") {
 					capMatch = false
+					break
+				}
+				if cap != int64(originalNodeSet.Intersection(vmssInstanceSet).Len()) {
+					// For autoscaling cluster, simply comparing the capacity may not work since if the number of current nodes is lower than the "minCount", a new node may be created after scaling down.
+					// In this situation, we compare the expected capacity with the length of intersection between original nodes and current nodes.
 					Logf("VMSS %q sku capacity is expected to be %d, but actually %d", *vmss.Name, cap, *vmss.Sku.Capacity)
+					capMatch = false
+					break
 				}
+			} else if int64(len(nodeSet)) != cap {
+				capMatch = false
+				break
 			}
 		}