Skip to content

Commit 1582077

Browse files
authored
Merge pull request kubernetes#91398 from marwanad/implement-async-deletes
add method for async deletion in vmss client without waiting on future
2 parents 6d3edbc + 524a7f2 commit 1582077

File tree

4 files changed

+94
-0
lines changed

4 files changed

+94
-0
lines changed

staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmssclient/azure_vmssclient.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,53 @@ func (c *Client) DeleteInstances(ctx context.Context, resourceGroupName string,
434434
return nil
435435
}
436436

437+
// DeleteInstancesAsync sends the delete request to ARM client and DOEST NOT wait on the future
438+
func (c *Client) DeleteInstancesAsync(ctx context.Context, resourceGroupName string, vmScaleSetName string, vmInstanceIDs compute.VirtualMachineScaleSetVMInstanceRequiredIDs) (*azure.Future, *retry.Error) {
439+
mc := metrics.NewMetricContext("vmss", "delete_instances_async", resourceGroupName, c.subscriptionID, "")
440+
441+
// Report errors if the client is rate limited.
442+
if !c.rateLimiterWriter.TryAccept() {
443+
mc.RateLimitedCount()
444+
return nil, retry.GetRateLimitError(true, "VMSSDeleteInstancesAsync")
445+
}
446+
447+
// Report errors if the client is throttled.
448+
if c.RetryAfterWriter.After(time.Now()) {
449+
mc.ThrottledCount()
450+
rerr := retry.GetThrottlingError("VMSSDeleteInstancesAsync", "client throttled", c.RetryAfterWriter)
451+
return nil, rerr
452+
}
453+
454+
resourceID := armclient.GetResourceID(
455+
c.subscriptionID,
456+
resourceGroupName,
457+
"Microsoft.Compute/virtualMachineScaleSets",
458+
vmScaleSetName,
459+
)
460+
461+
response, rerr := c.armClient.PostResource(ctx, resourceID, "delete", vmInstanceIDs)
462+
defer c.armClient.CloseResponse(ctx, response)
463+
464+
if rerr != nil {
465+
klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "vmss.deletevms.request", resourceID, rerr.Error())
466+
return nil, rerr
467+
}
468+
469+
err := autorest.Respond(response, azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted))
470+
if err != nil {
471+
klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "vmss.deletevms.respond", resourceID, rerr.Error())
472+
return nil, retry.GetError(response, err)
473+
}
474+
475+
future, err := azure.NewFutureFromResponse(response)
476+
if err != nil {
477+
klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "vmss.deletevms.future", resourceID, rerr.Error())
478+
return nil, retry.NewError(false, err)
479+
}
480+
481+
return &future, nil
482+
}
483+
437484
// deleteVMSSInstances deletes the instances for a VirtualMachineScaleSet.
438485
func (c *Client) deleteVMSSInstances(ctx context.Context, resourceGroupName string, vmScaleSetName string, vmInstanceIDs compute.VirtualMachineScaleSetVMInstanceRequiredIDs) *retry.Error {
439486
resourceID := armclient.GetResourceID(

staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmssclient/azure_vmssclient_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,35 @@ func TestDeleteInstancesWaitError(t *testing.T) {
755755
assert.NotNil(t, rerr)
756756
assert.Equal(t, vmssDeleteInstancesErr, rerr)
757757
}
758+
func TestDeleteInstancesAsync(t *testing.T) {
759+
ctrl := gomock.NewController(t)
760+
defer ctrl.Finish()
761+
762+
vmss := getTestVMSS("vmss1")
763+
vmInstanceIDs := compute.VirtualMachineScaleSetVMInstanceRequiredIDs{
764+
InstanceIds: &[]string{"0", "1", "2"},
765+
}
766+
response := &http.Response{
767+
StatusCode: http.StatusOK,
768+
Request: &http.Request{Method: "POST"},
769+
Body: ioutil.NopCloser(bytes.NewReader([]byte("{}"))),
770+
}
771+
armClient := mockarmclient.NewMockInterface(ctrl)
772+
armClient.EXPECT().PostResource(gomock.Any(), to.String(vmss.ID), "delete", vmInstanceIDs).Return(response, nil).Times(1)
773+
armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1)
774+
775+
vmssClient := getTestVMSSClient(armClient)
776+
future, rerr := vmssClient.DeleteInstancesAsync(context.TODO(), "rg", "vmss1", vmInstanceIDs)
777+
assert.Nil(t, rerr)
778+
assert.Equal(t, future.Status(), "Succeeded")
779+
780+
// on error
781+
retryErr := &retry.Error{RawError: fmt.Errorf("error")}
782+
armClient.EXPECT().PostResource(gomock.Any(), to.String(vmss.ID), "delete", vmInstanceIDs).Return(&http.Response{StatusCode: http.StatusBadRequest}, retryErr).Times(1)
783+
armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1)
784+
_, rerr = vmssClient.DeleteInstancesAsync(context.TODO(), "rg", "vmss1", vmInstanceIDs)
785+
assert.Equal(t, retryErr, rerr)
786+
}
758787

759788
func getTestVMSS(name string) compute.VirtualMachineScaleSet {
760789
return compute.VirtualMachineScaleSet{

staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmssclient/interface.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,7 @@ type Interface interface {
5454

5555
// DeleteInstances deletes the instances for a VirtualMachineScaleSet.
5656
DeleteInstances(ctx context.Context, resourceGroupName string, vmScaleSetName string, vmInstanceIDs compute.VirtualMachineScaleSetVMInstanceRequiredIDs) *retry.Error
57+
58+
// DeleteInstancesAsync sends the delete request to the ARM client and DOEST NOT wait on the future
59+
DeleteInstancesAsync(ctx context.Context, resourceGroupName string, vmScaleSetName string, vmInstanceIDs compute.VirtualMachineScaleSetVMInstanceRequiredIDs) (*azure.Future, *retry.Error)
5760
}

staging/src/k8s.io/legacy-cloud-providers/azure/clients/vmssclient/mockvmssclient/interface.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,18 @@ func (mr *MockInterfaceMockRecorder) DeleteInstances(ctx, resourceGroupName, vmS
139139
mr.mock.ctrl.T.Helper()
140140
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteInstances", reflect.TypeOf((*MockInterface)(nil).DeleteInstances), ctx, resourceGroupName, vmScaleSetName, vmInstanceIDs)
141141
}
142+
143+
// DeleteInstancesAsync mocks base method
144+
func (m *MockInterface) DeleteInstancesAsync(ctx context.Context, resourceGroupName, VMScaleSetName string, vmInstanceIDs compute.VirtualMachineScaleSetVMInstanceRequiredIDs) (*azure.Future, *retry.Error) {
145+
m.ctrl.T.Helper()
146+
ret := m.ctrl.Call(m, "DeleteInstancesAsync", ctx, resourceGroupName, VMScaleSetName, vmInstanceIDs)
147+
ret0, _ := ret[0].(*azure.Future)
148+
ret1, _ := ret[1].(*retry.Error)
149+
return ret0, ret1
150+
}
151+
152+
// DeleteInstancesAsync indicates an expected call of DeleteInstancesAsync
153+
func (mr *MockInterfaceMockRecorder) DeleteInstancesAsync(ctx, resourceGroupName, VMScaleSetName, vmInstanceIDs interface{}) *gomock.Call {
154+
mr.mock.ctrl.T.Helper()
155+
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteInstancesAsync", reflect.TypeOf((*MockInterface)(nil).DeleteInstancesAsync), ctx, resourceGroupName, VMScaleSetName, vmInstanceIDs)
156+
}

0 commit comments

Comments
 (0)