|
| 1 | +/* |
| 2 | +Copyright 2022 The Kubernetes Authors. |
| 3 | +
|
| 4 | +Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | +you may not use this file except in compliance with the License. |
| 6 | +You may obtain a copy of the License at |
| 7 | +
|
| 8 | + http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | +
|
| 10 | +Unless required by applicable law or agreed to in writing, software |
| 11 | +distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | +See the License for the specific language governing permissions and |
| 14 | +limitations under the License. |
| 15 | +*/ |
| 16 | + |
| 17 | +package azure |
| 18 | + |
| 19 | +import ( |
| 20 | + "context" |
| 21 | + "strings" |
| 22 | + |
| 23 | + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2022-08-01/compute" |
| 24 | + "github.com/Azure/go-autorest/autorest/azure" |
| 25 | + |
| 26 | + "k8s.io/klog/v2" |
| 27 | + "sigs.k8s.io/cloud-provider-azure/pkg/retry" |
| 28 | +) |
| 29 | + |
| 30 | +// When Azure Dedicated Host is enabled or using isolated vm skus, force deleting a VMSS fails with the following error: |
| 31 | +// |
| 32 | +// "predominantErrorDetail": { |
| 33 | +// "innererror": { |
| 34 | +// "internalErrorCode": "OperationNotAllowedOnResourceThatManagesUpdatesWithMaintenanceControl" |
| 35 | +// }, |
| 36 | +// "code": "OperationNotAllowed", |
| 37 | +// "message": "Operation 'ForceDelete' is not allowed on resource 'aks-newnp-11436513-vmss' since it manages updates using maintenance control." |
| 38 | +// }, |
| 39 | +// |
| 40 | +// A programmatically way to determine if a VM size is isolated or not has not been found. The isolated VM documentation: |
| 41 | +// https://docs.microsoft.com/en-us/azure/virtual-machines/isolation |
| 42 | +// has the current list of isolated VM sizes, but new isolated VM size could be introduced in the future. |
| 43 | +// |
| 44 | +// As a result of not being able to find out if a VM size is isolated or not, we'll do the following: |
| 45 | +// - if scaleSet has isolated vm size or dedicated host, disable forDelete |
| 46 | +// - else use forceDelete |
| 47 | +// - if new isolated sku were added or dedicatedHost was not updated properly, this forceDelete call will fail with above error. |
| 48 | +// In that case, call normal delete (fall-back) |
| 49 | + |
| 50 | +var isolatedVMSizes = map[string]bool{ |
| 51 | + strings.ToLower("Standard_E80ids_v4"): true, |
| 52 | + strings.ToLower("Standard_E80is_v4"): true, |
| 53 | + strings.ToLower("Standard_E104i_v5"): true, |
| 54 | + strings.ToLower("Standard_E104is_v5"): true, |
| 55 | + strings.ToLower("Standard_E104id_v5"): true, |
| 56 | + strings.ToLower("Standard_E104ids_v5"): true, |
| 57 | + strings.ToLower("Standard_M192is_v2"): true, |
| 58 | + strings.ToLower("Standard_M192ims_v2"): true, |
| 59 | + strings.ToLower("Standard_M192ids_v2"): true, |
| 60 | + strings.ToLower("Standard_M192idms_v2"): true, |
| 61 | + strings.ToLower("Standard_F72s_v2"): true, |
| 62 | + strings.ToLower("Standard_M128ms"): true, |
| 63 | +} |
| 64 | + |
| 65 | +func (scaleSet *ScaleSet) deleteInstances(ctx context.Context, requiredIds *compute.VirtualMachineScaleSetVMInstanceRequiredIDs, commonAsgId string) (*azure.Future, *retry.Error) { |
| 66 | + scaleSet.instanceMutex.Lock() |
| 67 | + defer scaleSet.instanceMutex.Unlock() |
| 68 | + |
| 69 | + skuName := scaleSet.getSKU() |
| 70 | + resourceGroup := scaleSet.manager.config.ResourceGroup |
| 71 | + forceDelete := shouldForceDelete(skuName, scaleSet) |
| 72 | + future, rerr := scaleSet.manager.azClient.virtualMachineScaleSetsClient.DeleteInstancesAsync(ctx, resourceGroup, commonAsgId, *requiredIds, forceDelete) |
| 73 | + if forceDelete && isOperationNotAllowed(rerr) { |
| 74 | + klog.Infof("falling back to normal delete for instances %v for %s", requiredIds.InstanceIds, scaleSet.Name) |
| 75 | + return scaleSet.manager.azClient.virtualMachineScaleSetsClient.DeleteInstancesAsync(ctx, resourceGroup, commonAsgId, *requiredIds, false) |
| 76 | + } |
| 77 | + return future, rerr |
| 78 | +} |
| 79 | + |
| 80 | +func shouldForceDelete(skuName string, scaleSet *ScaleSet) bool { |
| 81 | + return scaleSet.enableForceDelete && !isolatedVMSizes[strings.ToLower(skuName)] && !scaleSet.dedicatedHost |
| 82 | +} |
| 83 | + |
| 84 | +func isOperationNotAllowed(rerr *retry.Error) bool { |
| 85 | + return rerr != nil && rerr.ServiceErrorCode() == retry.OperationNotAllowed |
| 86 | +} |
0 commit comments