2019-09-27 21:51:53 +00:00
// +build !providerless
/ *
Copyright 2017 The Kubernetes Authors .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package azure
import (
"errors"
"fmt"
"regexp"
"sort"
"strconv"
"strings"
2019-10-16 05:42:28 +00:00
"sync"
2019-09-27 21:51:53 +00:00
2020-08-10 17:43:49 +00:00
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute"
2019-09-27 21:51:53 +00:00
"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network"
"github.com/Azure/go-autorest/autorest/to"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
cloudprovider "k8s.io/cloud-provider"
2020-08-10 17:43:49 +00:00
"k8s.io/klog/v2"
2020-03-26 21:07:15 +00:00
azcache "k8s.io/legacy-cloud-providers/azure/cache"
2019-09-27 21:51:53 +00:00
utilnet "k8s.io/utils/net"
)
var (
2020-03-26 21:07:15 +00:00
// virtualMachineScaleSetsDeallocating indicates VMSS instances are in Deallocating state.
virtualMachineScaleSetsDeallocating = "Deallocating"
2020-08-10 17:43:49 +00:00
// ErrorNotVmssInstance indicates an instance is not belonging to any vmss.
2019-09-27 21:51:53 +00:00
ErrorNotVmssInstance = errors . New ( "not a vmss instance" )
scaleSetNameRE = regexp . MustCompile ( ` .*/subscriptions/(?:.*)/Microsoft.Compute/virtualMachineScaleSets/(.+)/virtualMachines(?:.*) ` )
resourceGroupRE = regexp . MustCompile ( ` .*/subscriptions/(?:.*)/resourceGroups/(.+)/providers/Microsoft.Compute/virtualMachineScaleSets/(?:.*)/virtualMachines(?:.*) ` )
vmssMachineIDTemplate = "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachineScaleSets/%s/virtualMachines/%s"
vmssIPConfigurationRE = regexp . MustCompile ( ` .*/subscriptions/(?:.*)/resourceGroups/(.+)/providers/Microsoft.Compute/virtualMachineScaleSets/(.+)/virtualMachines/(.+)/networkInterfaces(?:.*) ` )
vmssPIPConfigurationRE = regexp . MustCompile ( ` .*/subscriptions/(?:.*)/resourceGroups/(.+)/providers/Microsoft.Compute/virtualMachineScaleSets/(.+)/virtualMachines/(.+)/networkInterfaces/(.+)/ipConfigurations/(.+)/publicIPAddresses/(.+) ` )
vmssVMProviderIDRE = regexp . MustCompile ( ` azure:///subscriptions/(?:.*)/resourceGroups/(.+)/providers/Microsoft.Compute/virtualMachineScaleSets/(.+)/virtualMachines/(?:\d+) ` )
)
2020-03-26 21:07:15 +00:00
// vmssMetaInfo contains the metadata for a VMSS.
type vmssMetaInfo struct {
vmssName string
resourceGroup string
}
2020-03-13 19:24:50 +00:00
2020-08-13 18:59:02 +00:00
// nodeIdentity identifies a node within a subscription.
type nodeIdentity struct {
resourceGroup string
vmssName string
nodeName string
}
2019-09-27 21:51:53 +00:00
// scaleSet implements VMSet interface for Azure scale set.
type scaleSet struct {
* Cloud
// availabilitySet is also required for scaleSet because some instances
// (e.g. master nodes) may not belong to any scale sets.
availabilitySet VMSet
2020-03-26 21:07:15 +00:00
vmssCache * azcache . TimedCache
2020-08-13 18:59:02 +00:00
vmssVMCache * sync . Map // [resourcegroup/vmssname]*azcache.TimedCache
2020-03-26 21:07:15 +00:00
availabilitySetNodesCache * azcache . TimedCache
2019-09-27 21:51:53 +00:00
}
// newScaleSet creates a new scaleSet.
func newScaleSet ( az * Cloud ) ( VMSet , error ) {
2020-08-10 17:43:49 +00:00
if az . Config . VmssVirtualMachinesCacheTTLInSeconds == 0 {
az . Config . VmssVirtualMachinesCacheTTLInSeconds = vmssVirtualMachinesCacheTTLDefaultInSeconds
}
2019-09-27 21:51:53 +00:00
ss := & scaleSet {
Cloud : az ,
availabilitySet : newAvailabilitySet ( az ) ,
2020-08-13 18:59:02 +00:00
vmssVMCache : & sync . Map { } ,
2019-09-27 21:51:53 +00:00
}
2020-08-10 17:43:49 +00:00
var err error
2020-03-26 21:07:15 +00:00
if ! ss . DisableAvailabilitySetNodes {
ss . availabilitySetNodesCache , err = ss . newAvailabilitySetNodesCache ( )
if err != nil {
return nil , err
}
2019-09-27 21:51:53 +00:00
}
2020-01-15 01:52:20 +00:00
ss . vmssCache , err = ss . newVMSSCache ( )
if err != nil {
return nil , err
}
2019-09-27 21:51:53 +00:00
return ss , nil
}
2020-03-26 21:07:15 +00:00
func ( ss * scaleSet ) getVMSS ( vmssName string , crt azcache . AzureCacheReadType ) ( * compute . VirtualMachineScaleSet , error ) {
2020-01-15 01:52:20 +00:00
getter := func ( vmssName string ) ( * compute . VirtualMachineScaleSet , error ) {
cached , err := ss . vmssCache . Get ( vmssKey , crt )
if err != nil {
return nil , err
}
vmsses := cached . ( * sync . Map )
if vmss , ok := vmsses . Load ( vmssName ) ; ok {
result := vmss . ( * vmssEntry )
return result . vmss , nil
}
return nil , nil
}
vmss , err := getter ( vmssName )
if err != nil {
return nil , err
}
if vmss != nil {
return vmss , nil
}
2020-02-14 00:18:16 +00:00
klog . V ( 2 ) . Infof ( "Couldn't find VMSS with name %s, refreshing the cache" , vmssName )
2020-01-15 01:52:20 +00:00
ss . vmssCache . Delete ( vmssKey )
vmss , err = getter ( vmssName )
if err != nil {
return nil , err
}
if vmss == nil {
return nil , cloudprovider . InstanceNotFound
}
return vmss , nil
}
2020-08-13 18:59:02 +00:00
// getVmssVMByNodeIdentity find virtualMachineScaleSetVM by nodeIdentity, using node's parent VMSS cache.
// Returns cloudprovider.InstanceNotFound if the node does not belong to the scale set named in nodeIdentity.
func ( ss * scaleSet ) getVmssVMByNodeIdentity ( node * nodeIdentity , crt azcache . AzureCacheReadType ) ( string , string , * compute . VirtualMachineScaleSetVM , error ) {
cacheKey , cache , err := ss . getVMSSVMCache ( node . resourceGroup , node . vmssName )
if err != nil {
return "" , "" , nil , err
}
2020-03-26 21:07:15 +00:00
getter := func ( nodeName string , crt azcache . AzureCacheReadType ) ( string , string , * compute . VirtualMachineScaleSetVM , bool , error ) {
2020-02-14 00:18:16 +00:00
var found bool
2020-08-13 18:59:02 +00:00
cached , err := cache . Get ( cacheKey , crt )
2019-10-16 05:42:28 +00:00
if err != nil {
2020-02-14 00:18:16 +00:00
return "" , "" , nil , found , err
2019-10-16 05:42:28 +00:00
}
2019-09-27 21:51:53 +00:00
2019-10-16 05:42:28 +00:00
virtualMachines := cached . ( * sync . Map )
if vm , ok := virtualMachines . Load ( nodeName ) ; ok {
result := vm . ( * vmssVirtualMachinesEntry )
2020-02-14 00:18:16 +00:00
found = true
return result . vmssName , result . instanceID , result . virtualMachine , found , nil
2019-10-16 05:42:28 +00:00
}
2019-09-27 21:51:53 +00:00
2020-02-14 00:18:16 +00:00
return "" , "" , nil , found , nil
2019-09-27 21:51:53 +00:00
}
2020-08-13 18:59:02 +00:00
_ , err = getScaleSetVMInstanceID ( node . nodeName )
2019-09-27 21:51:53 +00:00
if err != nil {
2019-10-16 05:42:28 +00:00
return "" , "" , nil , err
2019-09-27 21:51:53 +00:00
}
2020-08-13 18:59:02 +00:00
vmssName , instanceID , vm , found , err := getter ( node . nodeName , crt )
2019-09-27 21:51:53 +00:00
if err != nil {
2019-10-16 05:42:28 +00:00
return "" , "" , nil , err
}
2020-02-14 00:18:16 +00:00
if ! found {
2020-08-13 18:59:02 +00:00
klog . V ( 2 ) . Infof ( "Couldn't find VMSS VM with nodeName %s, refreshing the cache" , node . nodeName )
vmssName , instanceID , vm , found , err = getter ( node . nodeName , azcache . CacheReadTypeForceRefresh )
2020-02-14 00:18:16 +00:00
if err != nil {
return "" , "" , nil , err
}
2019-09-27 21:51:53 +00:00
}
2020-02-14 00:18:16 +00:00
if found && vm != nil {
return vmssName , instanceID , vm , nil
2019-09-27 21:51:53 +00:00
}
2020-02-14 00:18:16 +00:00
if ! found || vm == nil {
2019-10-16 05:42:28 +00:00
return "" , "" , nil , cloudprovider . InstanceNotFound
}
return vmssName , instanceID , vm , nil
2019-09-27 21:51:53 +00:00
}
2020-08-13 18:59:02 +00:00
// getVmssVM gets virtualMachineScaleSetVM by nodeName from cache.
// Returns cloudprovider.InstanceNotFound if nodeName does not belong to any scale set.
func ( ss * scaleSet ) getVmssVM ( nodeName string , crt azcache . AzureCacheReadType ) ( string , string , * compute . VirtualMachineScaleSetVM , error ) {
node , err := ss . getNodeIdentityByNodeName ( nodeName , crt )
if err != nil {
return "" , "" , nil , err
}
return ss . getVmssVMByNodeIdentity ( node , crt )
}
2019-09-27 21:51:53 +00:00
// GetPowerStatusByNodeName returns the power state of the specified node.
func ( ss * scaleSet ) GetPowerStatusByNodeName ( name string ) ( powerState string , err error ) {
2020-05-26 22:59:35 +00:00
managedByAS , err := ss . isNodeManagedByAvailabilitySet ( name , azcache . CacheReadTypeUnsafe )
if err != nil {
klog . Errorf ( "Failed to check isNodeManagedByAvailabilitySet: %v" , err )
return "" , err
}
if managedByAS {
// vm is managed by availability set.
return ss . availabilitySet . GetPowerStatusByNodeName ( name )
}
2020-03-26 21:07:15 +00:00
_ , _ , vm , err := ss . getVmssVM ( name , azcache . CacheReadTypeDefault )
2019-09-27 21:51:53 +00:00
if err != nil {
return powerState , err
}
if vm . InstanceView != nil && vm . InstanceView . Statuses != nil {
statuses := * vm . InstanceView . Statuses
for _ , status := range statuses {
state := to . String ( status . Code )
if strings . HasPrefix ( state , vmPowerStatePrefix ) {
return strings . TrimPrefix ( state , vmPowerStatePrefix ) , nil
}
}
}
// vm.InstanceView or vm.InstanceView.Statuses are nil when the VM is under deleting.
klog . V ( 3 ) . Infof ( "InstanceView for node %q is nil, assuming it's stopped" , name )
return vmPowerStateStopped , nil
}
// getCachedVirtualMachineByInstanceID gets scaleSetVMInfo from cache.
// The node must belong to one of scale sets.
2020-03-26 21:07:15 +00:00
func ( ss * scaleSet ) getVmssVMByInstanceID ( resourceGroup , scaleSetName , instanceID string , crt azcache . AzureCacheReadType ) ( * compute . VirtualMachineScaleSetVM , error ) {
2020-08-13 18:59:02 +00:00
cacheKey , cache , err := ss . getVMSSVMCache ( resourceGroup , scaleSetName )
if err != nil {
return nil , err
}
2020-03-26 21:07:15 +00:00
getter := func ( crt azcache . AzureCacheReadType ) ( vm * compute . VirtualMachineScaleSetVM , found bool , err error ) {
2020-08-13 18:59:02 +00:00
cached , err := cache . Get ( cacheKey , crt )
2019-10-16 05:42:28 +00:00
if err != nil {
return nil , false , err
}
virtualMachines := cached . ( * sync . Map )
virtualMachines . Range ( func ( key , value interface { } ) bool {
vmEntry := value . ( * vmssVirtualMachinesEntry )
if strings . EqualFold ( vmEntry . resourceGroup , resourceGroup ) &&
strings . EqualFold ( vmEntry . vmssName , scaleSetName ) &&
strings . EqualFold ( vmEntry . instanceID , instanceID ) {
vm = vmEntry . virtualMachine
found = true
return false
}
return true
} )
return vm , found , nil
}
2020-02-14 00:18:16 +00:00
vm , found , err := getter ( crt )
2019-09-27 21:51:53 +00:00
if err != nil {
2019-10-16 05:42:28 +00:00
return nil , err
}
2020-02-14 00:18:16 +00:00
if ! found {
klog . V ( 2 ) . Infof ( "Couldn't find VMSS VM with scaleSetName %q and instanceID %q, refreshing the cache" , scaleSetName , instanceID )
2020-03-26 21:07:15 +00:00
vm , found , err = getter ( azcache . CacheReadTypeForceRefresh )
2020-02-14 00:18:16 +00:00
if err != nil {
return nil , err
}
2019-09-27 21:51:53 +00:00
}
2020-02-14 00:18:16 +00:00
if found && vm != nil {
return vm , nil
2019-10-16 05:42:28 +00:00
}
2020-08-13 18:59:02 +00:00
if found && vm == nil {
klog . V ( 2 ) . Infof ( "Couldn't find VMSS VM with scaleSetName %q and instanceID %q, refreshing the cache if it is expired" , scaleSetName , instanceID )
vm , found , err = getter ( azcache . CacheReadTypeDefault )
if err != nil {
return nil , err
}
}
2020-02-14 00:18:16 +00:00
if ! found || vm == nil {
2019-10-16 05:42:28 +00:00
return nil , cloudprovider . InstanceNotFound
2019-09-27 21:51:53 +00:00
}
2019-10-16 05:42:28 +00:00
return vm , nil
2019-09-27 21:51:53 +00:00
}
// GetInstanceIDByNodeName gets the cloud provider ID by node name.
// It must return ("", cloudprovider.InstanceNotFound) if the instance does
// not exist or is no longer running.
func ( ss * scaleSet ) GetInstanceIDByNodeName ( name string ) ( string , error ) {
2020-03-26 21:07:15 +00:00
managedByAS , err := ss . isNodeManagedByAvailabilitySet ( name , azcache . CacheReadTypeUnsafe )
2019-09-27 21:51:53 +00:00
if err != nil {
klog . Errorf ( "Failed to check isNodeManagedByAvailabilitySet: %v" , err )
return "" , err
}
if managedByAS {
// vm is managed by availability set.
return ss . availabilitySet . GetInstanceIDByNodeName ( name )
}
2020-03-26 21:07:15 +00:00
_ , _ , vm , err := ss . getVmssVM ( name , azcache . CacheReadTypeUnsafe )
2019-09-27 21:51:53 +00:00
if err != nil {
return "" , err
}
resourceID := * vm . ID
convertedResourceID , err := convertResourceGroupNameToLower ( resourceID )
if err != nil {
klog . Errorf ( "convertResourceGroupNameToLower failed with error: %v" , err )
return "" , err
}
return convertedResourceID , nil
}
// GetNodeNameByProviderID gets the node name by provider ID.
2020-05-26 22:59:35 +00:00
// providerID example:
// 1. vmas providerID: azure:///subscriptions/subsid/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/aks-nodepool1-27053986-0
// 2. vmss providerID:
// azure:///subscriptions/subsid/resourceGroups/rg/providers/Microsoft.Compute/virtualMachineScaleSets/aks-agentpool-22126781-vmss/virtualMachines/1
// /subscriptions/subsid/resourceGroups/rg/providers/Microsoft.Compute/virtualMachineScaleSets/aks-agentpool-22126781-vmss/virtualMachines/k8s-agentpool-36841236-vmss_1
2019-09-27 21:51:53 +00:00
func ( ss * scaleSet ) GetNodeNameByProviderID ( providerID string ) ( types . NodeName , error ) {
// NodeName is not part of providerID for vmss instances.
scaleSetName , err := extractScaleSetNameByProviderID ( providerID )
if err != nil {
2020-08-10 17:43:49 +00:00
klog . V ( 4 ) . Infof ( "Can not extract scale set name from providerID (%s), assuming it is managed by availability set: %v" , providerID , err )
2019-09-27 21:51:53 +00:00
return ss . availabilitySet . GetNodeNameByProviderID ( providerID )
}
resourceGroup , err := extractResourceGroupByProviderID ( providerID )
if err != nil {
return "" , fmt . Errorf ( "error of extracting resource group for node %q" , providerID )
}
2020-05-26 22:59:35 +00:00
instanceID , err := getLastSegment ( providerID , "/" )
2019-09-27 21:51:53 +00:00
if err != nil {
2020-08-10 17:43:49 +00:00
klog . V ( 4 ) . Infof ( "Can not extract instanceID from providerID (%s), assuming it is managed by availability set: %v" , providerID , err )
2019-09-27 21:51:53 +00:00
return ss . availabilitySet . GetNodeNameByProviderID ( providerID )
}
2020-05-26 22:59:35 +00:00
// instanceID contains scaleSetName (returned by disk.ManagedBy), e.g. k8s-agentpool-36841236-vmss_1
if strings . HasPrefix ( strings . ToLower ( instanceID ) , strings . ToLower ( scaleSetName ) ) {
instanceID , err = getLastSegment ( instanceID , "_" )
if err != nil {
return "" , err
}
}
2020-03-26 21:07:15 +00:00
vm , err := ss . getVmssVMByInstanceID ( resourceGroup , scaleSetName , instanceID , azcache . CacheReadTypeUnsafe )
2019-09-27 21:51:53 +00:00
if err != nil {
return "" , err
}
if vm . OsProfile != nil && vm . OsProfile . ComputerName != nil {
nodeName := strings . ToLower ( * vm . OsProfile . ComputerName )
return types . NodeName ( nodeName ) , nil
}
return "" , nil
}
// GetInstanceTypeByNodeName gets the instance type by node name.
func ( ss * scaleSet ) GetInstanceTypeByNodeName ( name string ) ( string , error ) {
2020-03-26 21:07:15 +00:00
managedByAS , err := ss . isNodeManagedByAvailabilitySet ( name , azcache . CacheReadTypeUnsafe )
2019-09-27 21:51:53 +00:00
if err != nil {
klog . Errorf ( "Failed to check isNodeManagedByAvailabilitySet: %v" , err )
return "" , err
}
if managedByAS {
// vm is managed by availability set.
return ss . availabilitySet . GetInstanceTypeByNodeName ( name )
}
2020-03-26 21:07:15 +00:00
_ , _ , vm , err := ss . getVmssVM ( name , azcache . CacheReadTypeUnsafe )
2019-09-27 21:51:53 +00:00
if err != nil {
return "" , err
}
if vm . Sku != nil && vm . Sku . Name != nil {
return * vm . Sku . Name , nil
}
return "" , nil
}
// GetZoneByNodeName gets availability zone for the specified node. If the node is not running
// with availability zone, then it returns fault domain.
func ( ss * scaleSet ) GetZoneByNodeName ( name string ) ( cloudprovider . Zone , error ) {
2020-03-26 21:07:15 +00:00
managedByAS , err := ss . isNodeManagedByAvailabilitySet ( name , azcache . CacheReadTypeUnsafe )
2019-09-27 21:51:53 +00:00
if err != nil {
klog . Errorf ( "Failed to check isNodeManagedByAvailabilitySet: %v" , err )
return cloudprovider . Zone { } , err
}
if managedByAS {
// vm is managed by availability set.
return ss . availabilitySet . GetZoneByNodeName ( name )
}
2020-03-26 21:07:15 +00:00
_ , _ , vm , err := ss . getVmssVM ( name , azcache . CacheReadTypeUnsafe )
2019-09-27 21:51:53 +00:00
if err != nil {
return cloudprovider . Zone { } , err
}
var failureDomain string
if vm . Zones != nil && len ( * vm . Zones ) > 0 {
// Get availability zone for the node.
zones := * vm . Zones
zoneID , err := strconv . Atoi ( zones [ 0 ] )
if err != nil {
return cloudprovider . Zone { } , fmt . Errorf ( "failed to parse zone %q: %v" , zones , err )
}
failureDomain = ss . makeZone ( to . String ( vm . Location ) , zoneID )
} else if vm . InstanceView != nil && vm . InstanceView . PlatformFaultDomain != nil {
// Availability zone is not used for the node, falling back to fault domain.
failureDomain = strconv . Itoa ( int ( * vm . InstanceView . PlatformFaultDomain ) )
2019-11-14 18:56:24 +00:00
} else {
err = fmt . Errorf ( "failed to get zone info" )
klog . Errorf ( "GetZoneByNodeName: got unexpected error %v" , err )
ss . deleteCacheForNode ( name )
return cloudprovider . Zone { } , err
2019-09-27 21:51:53 +00:00
}
return cloudprovider . Zone {
2020-04-19 06:53:00 +00:00
FailureDomain : strings . ToLower ( failureDomain ) ,
Region : strings . ToLower ( to . String ( vm . Location ) ) ,
2019-09-27 21:51:53 +00:00
} , nil
}
// GetPrimaryVMSetName returns the VM set name depending on the configured vmType.
// It returns config.PrimaryScaleSetName for vmss and config.PrimaryAvailabilitySetName for standard vmType.
func ( ss * scaleSet ) GetPrimaryVMSetName ( ) string {
return ss . Config . PrimaryScaleSetName
}
// GetIPByNodeName gets machine private IP and public IP by node name.
func ( ss * scaleSet ) GetIPByNodeName ( nodeName string ) ( string , string , error ) {
nic , err := ss . GetPrimaryInterface ( nodeName )
if err != nil {
klog . Errorf ( "error: ss.GetIPByNodeName(%s), GetPrimaryInterface(%q), err=%v" , nodeName , nodeName , err )
return "" , "" , err
}
ipConfig , err := getPrimaryIPConfig ( nic )
if err != nil {
klog . Errorf ( "error: ss.GetIPByNodeName(%s), getPrimaryIPConfig(%v), err=%v" , nodeName , nic , err )
return "" , "" , err
}
internalIP := * ipConfig . PrivateIPAddress
publicIP := ""
if ipConfig . PublicIPAddress != nil && ipConfig . PublicIPAddress . ID != nil {
pipID := * ipConfig . PublicIPAddress . ID
matches := vmssPIPConfigurationRE . FindStringSubmatch ( pipID )
if len ( matches ) == 7 {
resourceGroupName := matches [ 1 ]
virtualMachineScaleSetName := matches [ 2 ]
2020-08-10 17:43:49 +00:00
virtualMachineIndex := matches [ 3 ]
2019-09-27 21:51:53 +00:00
networkInterfaceName := matches [ 4 ]
IPConfigurationName := matches [ 5 ]
publicIPAddressName := matches [ 6 ]
2020-08-10 17:43:49 +00:00
pip , existsPip , err := ss . getVMSSPublicIPAddress ( resourceGroupName , virtualMachineScaleSetName , virtualMachineIndex , networkInterfaceName , IPConfigurationName , publicIPAddressName )
2019-09-27 21:51:53 +00:00
if err != nil {
klog . Errorf ( "ss.getVMSSPublicIPAddress() failed with error: %v" , err )
return "" , "" , err
}
if existsPip && pip . IPAddress != nil {
publicIP = * pip . IPAddress
}
} else {
klog . Warningf ( "Failed to get VMSS Public IP with ID %s" , pipID )
}
}
return internalIP , publicIP , nil
}
2020-08-10 17:43:49 +00:00
func ( ss * scaleSet ) getVMSSPublicIPAddress ( resourceGroupName string , virtualMachineScaleSetName string , virtualMachineIndex string , networkInterfaceName string , IPConfigurationName string , publicIPAddressName string ) ( network . PublicIPAddress , bool , error ) {
2019-09-27 21:51:53 +00:00
ctx , cancel := getContextWithCancel ( )
defer cancel ( )
2020-03-26 21:07:15 +00:00
2020-08-10 17:43:49 +00:00
pip , err := ss . PublicIPAddressesClient . GetVirtualMachineScaleSetPublicIPAddress ( ctx , resourceGroupName , virtualMachineScaleSetName , virtualMachineIndex , networkInterfaceName , IPConfigurationName , publicIPAddressName , "" )
2020-03-26 21:07:15 +00:00
exists , rerr := checkResourceExistsFromError ( err )
if rerr != nil {
return pip , false , rerr . Error ( )
2019-09-27 21:51:53 +00:00
}
if ! exists {
2020-03-26 21:07:15 +00:00
klog . V ( 2 ) . Infof ( "Public IP %q not found" , publicIPAddressName )
2019-09-27 21:51:53 +00:00
return pip , false , nil
}
2020-03-26 21:07:15 +00:00
return pip , exists , nil
2019-09-27 21:51:53 +00:00
}
// returns a list of private ips assigned to node
// TODO (khenidak): This should read all nics, not just the primary
// allowing users to split ipv4/v6 on multiple nics
func ( ss * scaleSet ) GetPrivateIPsByNodeName ( nodeName string ) ( [ ] string , error ) {
ips := make ( [ ] string , 0 )
nic , err := ss . GetPrimaryInterface ( nodeName )
if err != nil {
klog . Errorf ( "error: ss.GetIPByNodeName(%s), GetPrimaryInterface(%q), err=%v" , nodeName , nodeName , err )
return ips , err
}
if nic . IPConfigurations == nil {
return ips , fmt . Errorf ( "nic.IPConfigurations for nic (nicname=%q) is nil" , * nic . Name )
}
for _ , ipConfig := range * ( nic . IPConfigurations ) {
if ipConfig . PrivateIPAddress != nil {
ips = append ( ips , * ( ipConfig . PrivateIPAddress ) )
}
}
return ips , nil
}
// This returns the full identifier of the primary NIC for the given VM.
func ( ss * scaleSet ) getPrimaryInterfaceID ( machine compute . VirtualMachineScaleSetVM ) ( string , error ) {
if len ( * machine . NetworkProfile . NetworkInterfaces ) == 1 {
return * ( * machine . NetworkProfile . NetworkInterfaces ) [ 0 ] . ID , nil
}
for _ , ref := range * machine . NetworkProfile . NetworkInterfaces {
if * ref . Primary {
return * ref . ID , nil
}
}
return "" , fmt . Errorf ( "failed to find a primary nic for the vm. vmname=%q" , * machine . Name )
}
// getVmssMachineID returns the full identifier of a vmss virtual machine.
func ( az * Cloud ) getVmssMachineID ( subscriptionID , resourceGroup , scaleSetName , instanceID string ) string {
return fmt . Sprintf (
vmssMachineIDTemplate ,
subscriptionID ,
strings . ToLower ( resourceGroup ) ,
scaleSetName ,
instanceID )
}
// machineName is composed of computerNamePrefix and 36-based instanceID.
// And instanceID part if in fixed length of 6 characters.
// Refer https://msftstack.wordpress.com/2017/05/10/figuring-out-azure-vm-scale-set-machine-names/.
func getScaleSetVMInstanceID ( machineName string ) ( string , error ) {
nameLength := len ( machineName )
if nameLength < 6 {
return "" , ErrorNotVmssInstance
}
instanceID , err := strconv . ParseUint ( machineName [ nameLength - 6 : ] , 36 , 64 )
if err != nil {
return "" , ErrorNotVmssInstance
}
return fmt . Sprintf ( "%d" , instanceID ) , nil
}
// extractScaleSetNameByProviderID extracts the scaleset name by vmss node's ProviderID.
func extractScaleSetNameByProviderID ( providerID string ) ( string , error ) {
matches := scaleSetNameRE . FindStringSubmatch ( providerID )
if len ( matches ) != 2 {
return "" , ErrorNotVmssInstance
}
return matches [ 1 ] , nil
}
// extractResourceGroupByProviderID extracts the resource group name by vmss node's ProviderID.
func extractResourceGroupByProviderID ( providerID string ) ( string , error ) {
matches := resourceGroupRE . FindStringSubmatch ( providerID )
if len ( matches ) != 2 {
return "" , ErrorNotVmssInstance
}
return matches [ 1 ] , nil
}
2020-08-10 17:43:49 +00:00
// listScaleSets lists all scale sets with orchestrationMode ScaleSetVM.
func ( ss * scaleSet ) listScaleSets ( resourceGroup string ) ( [ ] string , error ) {
ctx , cancel := getContextWithCancel ( )
defer cancel ( )
allScaleSets , rerr := ss . VirtualMachineScaleSetsClient . List ( ctx , resourceGroup )
if rerr != nil {
klog . Errorf ( "VirtualMachineScaleSetsClient.List failed: %v" , rerr )
return nil , rerr . Error ( )
}
ssNames := make ( [ ] string , 0 )
for _ , vmss := range allScaleSets {
name := * vmss . Name
if vmss . Sku != nil && to . Int64 ( vmss . Sku . Capacity ) == 0 {
klog . V ( 3 ) . Infof ( "Capacity of VMSS %q is 0, skipping" , name )
continue
}
if vmss . VirtualMachineScaleSetProperties == nil || vmss . VirtualMachineScaleSetProperties . VirtualMachineProfile == nil {
klog . V ( 3 ) . Infof ( "VMSS %q orchestrationMode is VirtualMachine, skipping" , name )
continue
}
ssNames = append ( ssNames , name )
}
return ssNames , nil
}
2020-08-13 18:59:02 +00:00
// getNodeIdentityByNodeName use the VMSS cache to find a node's resourcegroup and vmss, returned in a nodeIdentity.
func ( ss * scaleSet ) getNodeIdentityByNodeName ( nodeName string , crt azcache . AzureCacheReadType ) ( * nodeIdentity , error ) {
getter := func ( nodeName string , crt azcache . AzureCacheReadType ) ( * nodeIdentity , error ) {
node := & nodeIdentity {
nodeName : nodeName ,
}
2019-09-27 21:51:53 +00:00
2020-08-13 18:59:02 +00:00
cached , err := ss . vmssCache . Get ( vmssKey , crt )
if err != nil {
return nil , err
}
vmsses := cached . ( * sync . Map )
vmsses . Range ( func ( key , value interface { } ) bool {
v := value . ( * vmssEntry )
if v . vmss . Name == nil {
return true
}
vmssPrefix := * v . vmss . Name
if v . vmss . VirtualMachineProfile != nil &&
v . vmss . VirtualMachineProfile . OsProfile != nil &&
v . vmss . VirtualMachineProfile . OsProfile . ComputerNamePrefix != nil {
vmssPrefix = * v . vmss . VirtualMachineProfile . OsProfile . ComputerNamePrefix
}
if strings . EqualFold ( vmssPrefix , nodeName [ : len ( nodeName ) - 6 ] ) {
node . vmssName = * v . vmss . Name
node . resourceGroup = v . resourceGroup
return false
}
return true
} )
return node , nil
2019-09-27 21:51:53 +00:00
}
2020-08-13 18:59:02 +00:00
if _ , err := getScaleSetVMInstanceID ( nodeName ) ; err != nil {
return nil , err
}
2019-10-16 05:42:28 +00:00
2020-08-13 18:59:02 +00:00
node , err := getter ( nodeName , crt )
if err != nil {
return nil , err
}
if node . vmssName != "" {
return node , nil
2019-09-27 21:51:53 +00:00
}
2020-08-13 18:59:02 +00:00
klog . V ( 2 ) . Infof ( "Couldn't find VMSS for node %s, refreshing the cache" , nodeName )
node , err = getter ( nodeName , azcache . CacheReadTypeForceRefresh )
if err != nil {
return nil , err
}
if node . vmssName == "" {
return nil , cloudprovider . InstanceNotFound
}
return node , nil
2019-09-27 21:51:53 +00:00
}
// listScaleSetVMs lists VMs belonging to the specified scale set.
func ( ss * scaleSet ) listScaleSetVMs ( scaleSetName , resourceGroup string ) ( [ ] compute . VirtualMachineScaleSetVM , error ) {
ctx , cancel := getContextWithCancel ( )
defer cancel ( )
2020-03-26 21:07:15 +00:00
allVMs , rerr := ss . VirtualMachineScaleSetVMsClient . List ( ctx , resourceGroup , scaleSetName , string ( compute . InstanceView ) )
if rerr != nil {
klog . Errorf ( "VirtualMachineScaleSetVMsClient.List failed: %v" , rerr )
return nil , rerr . Error ( )
2019-09-27 21:51:53 +00:00
}
return allVMs , nil
}
// getAgentPoolScaleSets lists the virtual machines for the resource group and then builds
// a list of scale sets that match the nodes available to k8s.
func ( ss * scaleSet ) getAgentPoolScaleSets ( nodes [ ] * v1 . Node ) ( * [ ] string , error ) {
agentPoolScaleSets := & [ ] string { }
for nx := range nodes {
if isMasterNode ( nodes [ nx ] ) {
continue
}
if ss . ShouldNodeExcludedFromLoadBalancer ( nodes [ nx ] ) {
continue
}
nodeName := nodes [ nx ] . Name
2020-03-26 21:07:15 +00:00
ssName , _ , _ , err := ss . getVmssVM ( nodeName , azcache . CacheReadTypeDefault )
2019-09-27 21:51:53 +00:00
if err != nil {
return nil , err
}
if ssName == "" {
klog . V ( 3 ) . Infof ( "Node %q is not belonging to any known scale sets" , nodeName )
continue
}
* agentPoolScaleSets = append ( * agentPoolScaleSets , ssName )
}
return agentPoolScaleSets , nil
}
// GetVMSetNames selects all possible availability sets or scale sets
// (depending vmType configured) for service load balancer. If the service has
// no loadbalancer mode annotation returns the primary VMSet. If service annotation
// for loadbalancer exists then return the eligible VMSet.
func ( ss * scaleSet ) GetVMSetNames ( service * v1 . Service , nodes [ ] * v1 . Node ) ( vmSetNames * [ ] string , err error ) {
hasMode , isAuto , serviceVMSetNames := getServiceLoadBalancerMode ( service )
if ! hasMode {
// no mode specified in service annotation default to PrimaryScaleSetName.
scaleSetNames := & [ ] string { ss . Config . PrimaryScaleSetName }
return scaleSetNames , nil
}
scaleSetNames , err := ss . getAgentPoolScaleSets ( nodes )
if err != nil {
klog . Errorf ( "ss.GetVMSetNames - getAgentPoolScaleSets failed err=(%v)" , err )
return nil , err
}
if len ( * scaleSetNames ) == 0 {
klog . Errorf ( "ss.GetVMSetNames - No scale sets found for nodes in the cluster, node count(%d)" , len ( nodes ) )
return nil , fmt . Errorf ( "No scale sets found for nodes, node count(%d)" , len ( nodes ) )
}
// sort the list to have deterministic selection
sort . Strings ( * scaleSetNames )
if ! isAuto {
if serviceVMSetNames == nil || len ( serviceVMSetNames ) == 0 {
return nil , fmt . Errorf ( "service annotation for LoadBalancerMode is empty, it should have __auto__ or availability sets value" )
}
// validate scale set exists
var found bool
for sasx := range serviceVMSetNames {
for asx := range * scaleSetNames {
if strings . EqualFold ( ( * scaleSetNames ) [ asx ] , serviceVMSetNames [ sasx ] ) {
found = true
serviceVMSetNames [ sasx ] = ( * scaleSetNames ) [ asx ]
break
}
}
if ! found {
klog . Errorf ( "ss.GetVMSetNames - scale set (%s) in service annotation not found" , serviceVMSetNames [ sasx ] )
return nil , fmt . Errorf ( "scale set (%s) - not found" , serviceVMSetNames [ sasx ] )
}
}
vmSetNames = & serviceVMSetNames
}
return vmSetNames , nil
}
// extractResourceGroupByVMSSNicID extracts the resource group name by vmss nicID.
func extractResourceGroupByVMSSNicID ( nicID string ) ( string , error ) {
matches := vmssIPConfigurationRE . FindStringSubmatch ( nicID )
if len ( matches ) != 4 {
return "" , fmt . Errorf ( "error of extracting resourceGroup from nicID %q" , nicID )
}
return matches [ 1 ] , nil
}
// GetPrimaryInterface gets machine primary network interface by node name and vmSet.
func ( ss * scaleSet ) GetPrimaryInterface ( nodeName string ) ( network . Interface , error ) {
2020-03-26 21:07:15 +00:00
managedByAS , err := ss . isNodeManagedByAvailabilitySet ( nodeName , azcache . CacheReadTypeDefault )
2019-09-27 21:51:53 +00:00
if err != nil {
klog . Errorf ( "Failed to check isNodeManagedByAvailabilitySet: %v" , err )
return network . Interface { } , err
}
if managedByAS {
// vm is managed by availability set.
return ss . availabilitySet . GetPrimaryInterface ( nodeName )
}
2020-03-26 21:07:15 +00:00
ssName , instanceID , vm , err := ss . getVmssVM ( nodeName , azcache . CacheReadTypeDefault )
2019-09-27 21:51:53 +00:00
if err != nil {
// VM is availability set, but not cached yet in availabilitySetNodesCache.
if err == ErrorNotVmssInstance {
return ss . availabilitySet . GetPrimaryInterface ( nodeName )
}
klog . Errorf ( "error: ss.GetPrimaryInterface(%s), ss.getVmssVM(%s), err=%v" , nodeName , nodeName , err )
return network . Interface { } , err
}
2019-10-16 05:42:28 +00:00
primaryInterfaceID , err := ss . getPrimaryInterfaceID ( * vm )
2019-09-27 21:51:53 +00:00
if err != nil {
klog . Errorf ( "error: ss.GetPrimaryInterface(%s), ss.getPrimaryInterfaceID(), err=%v" , nodeName , err )
return network . Interface { } , err
}
2020-05-26 22:59:35 +00:00
nicName , err := getLastSegment ( primaryInterfaceID , "/" )
2019-09-27 21:51:53 +00:00
if err != nil {
klog . Errorf ( "error: ss.GetPrimaryInterface(%s), getLastSegment(%s), err=%v" , nodeName , primaryInterfaceID , err )
return network . Interface { } , err
}
resourceGroup , err := extractResourceGroupByVMSSNicID ( primaryInterfaceID )
if err != nil {
return network . Interface { } , err
}
ctx , cancel := getContextWithCancel ( )
defer cancel ( )
2020-03-26 21:07:15 +00:00
nic , rerr := ss . InterfacesClient . GetVirtualMachineScaleSetNetworkInterface ( ctx , resourceGroup , ssName , instanceID , nicName , "" )
if rerr != nil {
exists , realErr := checkResourceExistsFromError ( rerr )
2019-09-27 21:51:53 +00:00
if realErr != nil {
klog . Errorf ( "error: ss.GetPrimaryInterface(%s), ss.GetVirtualMachineScaleSetNetworkInterface.Get(%s, %s, %s), err=%v" , nodeName , resourceGroup , ssName , nicName , realErr )
2020-08-10 17:43:49 +00:00
return network . Interface { } , realErr . Error ( )
2019-09-27 21:51:53 +00:00
}
if ! exists {
return network . Interface { } , cloudprovider . InstanceNotFound
}
}
// Fix interface's location, which is required when updating the interface.
// TODO: is this a bug of azure SDK?
if nic . Location == nil || * nic . Location == "" {
nic . Location = vm . Location
}
return nic , nil
}
// getPrimaryNetworkInterfaceConfiguration gets primary network interface configuration for scale set virtual machine.
func ( ss * scaleSet ) getPrimaryNetworkInterfaceConfiguration ( networkConfigurations [ ] compute . VirtualMachineScaleSetNetworkConfiguration , nodeName string ) ( * compute . VirtualMachineScaleSetNetworkConfiguration , error ) {
if len ( networkConfigurations ) == 1 {
return & networkConfigurations [ 0 ] , nil
}
for idx := range networkConfigurations {
networkConfig := & networkConfigurations [ idx ]
if networkConfig . Primary != nil && * networkConfig . Primary == true {
return networkConfig , nil
}
}
return nil , fmt . Errorf ( "failed to find a primary network configuration for the scale set VM %q" , nodeName )
}
// getPrimaryNetworkInterfaceConfigurationForScaleSet gets primary network interface configuration for scale set.
func ( ss * scaleSet ) getPrimaryNetworkInterfaceConfigurationForScaleSet ( networkConfigurations [ ] compute . VirtualMachineScaleSetNetworkConfiguration , vmssName string ) ( * compute . VirtualMachineScaleSetNetworkConfiguration , error ) {
if len ( networkConfigurations ) == 1 {
return & networkConfigurations [ 0 ] , nil
}
for idx := range networkConfigurations {
networkConfig := & networkConfigurations [ idx ]
if networkConfig . Primary != nil && * networkConfig . Primary == true {
return networkConfig , nil
}
}
return nil , fmt . Errorf ( "failed to find a primary network configuration for the scale set %q" , vmssName )
}
func getPrimaryIPConfigFromVMSSNetworkConfig ( config * compute . VirtualMachineScaleSetNetworkConfiguration ) ( * compute . VirtualMachineScaleSetIPConfiguration , error ) {
ipConfigurations := * config . IPConfigurations
if len ( ipConfigurations ) == 1 {
return & ipConfigurations [ 0 ] , nil
}
for idx := range ipConfigurations {
ipConfig := & ipConfigurations [ idx ]
if ipConfig . Primary != nil && * ipConfig . Primary == true {
return ipConfig , nil
}
}
return nil , fmt . Errorf ( "failed to find a primary IP configuration" )
}
func ( ss * scaleSet ) getConfigForScaleSetByIPFamily ( config * compute . VirtualMachineScaleSetNetworkConfiguration , nodeName string , IPv6 bool ) ( * compute . VirtualMachineScaleSetIPConfiguration , error ) {
ipConfigurations := * config . IPConfigurations
var ipVersion compute . IPVersion
if IPv6 {
ipVersion = compute . IPv6
} else {
ipVersion = compute . IPv4
}
for idx := range ipConfigurations {
ipConfig := & ipConfigurations [ idx ]
if ipConfig . PrivateIPAddressVersion == ipVersion {
return ipConfig , nil
}
}
2020-08-10 17:43:49 +00:00
return nil , fmt . Errorf ( "failed to find a IPconfiguration(IPv6=%v) for the scale set VM %q" , IPv6 , nodeName )
2019-09-27 21:51:53 +00:00
}
// EnsureHostInPool ensures the given VM's Primary NIC's Primary IP Configuration is
2020-03-26 21:07:15 +00:00
// participating in the specified LoadBalancer Backend Pool, which returns (resourceGroup, vmssName, instanceID, vmssVM, error).
func ( ss * scaleSet ) EnsureHostInPool ( service * v1 . Service , nodeName types . NodeName , backendPoolID string , vmSetName string , isInternal bool ) ( string , string , string , * compute . VirtualMachineScaleSetVM , error ) {
2019-09-27 21:51:53 +00:00
klog . V ( 3 ) . Infof ( "ensuring node %q of scaleset %q in LB backendpool %q" , nodeName , vmSetName , backendPoolID )
vmName := mapNodeNameToVMName ( nodeName )
2020-03-26 21:07:15 +00:00
ssName , instanceID , vm , err := ss . getVmssVM ( vmName , azcache . CacheReadTypeDefault )
2019-09-27 21:51:53 +00:00
if err != nil {
2020-03-26 21:07:15 +00:00
return "" , "" , "" , nil , err
2019-09-27 21:51:53 +00:00
}
// Check scale set name:
// - For basic SKU load balancer, return nil if the node's scale set is mismatched with vmSetName.
// - For standard SKU load balancer, backend could belong to multiple VMSS, so we
// don't check vmSet for it.
if vmSetName != "" && ! ss . useStandardLoadBalancer ( ) && ! strings . EqualFold ( vmSetName , ssName ) {
klog . V ( 3 ) . Infof ( "EnsureHostInPool skips node %s because it is not in the scaleSet %s" , vmName , vmSetName )
2020-03-26 21:07:15 +00:00
return "" , "" , "" , nil , nil
2019-09-27 21:51:53 +00:00
}
// Find primary network interface configuration.
if vm . NetworkProfileConfiguration . NetworkInterfaceConfigurations == nil {
klog . V ( 4 ) . Infof ( "EnsureHostInPool: cannot obtain the primary network interface configuration, of vm %s, probably because the vm's being deleted" , vmName )
2020-03-26 21:07:15 +00:00
return "" , "" , "" , nil , nil
2019-09-27 21:51:53 +00:00
}
networkInterfaceConfigurations := * vm . NetworkProfileConfiguration . NetworkInterfaceConfigurations
primaryNetworkInterfaceConfiguration , err := ss . getPrimaryNetworkInterfaceConfiguration ( networkInterfaceConfigurations , vmName )
if err != nil {
2020-03-26 21:07:15 +00:00
return "" , "" , "" , nil , err
2019-09-27 21:51:53 +00:00
}
var primaryIPConfiguration * compute . VirtualMachineScaleSetIPConfiguration
2020-03-26 21:07:15 +00:00
ipv6 := utilnet . IsIPv6String ( service . Spec . ClusterIP )
2019-09-27 21:51:53 +00:00
// Find primary network interface configuration.
2020-03-26 21:07:15 +00:00
if ! ss . Cloud . ipv6DualStackEnabled && ! ipv6 {
2019-09-27 21:51:53 +00:00
// Find primary IP configuration.
primaryIPConfiguration , err = getPrimaryIPConfigFromVMSSNetworkConfig ( primaryNetworkInterfaceConfiguration )
if err != nil {
2020-03-26 21:07:15 +00:00
return "" , "" , "" , nil , err
2019-09-27 21:51:53 +00:00
}
} else {
2020-08-10 17:43:49 +00:00
// For IPv6 or dualstack service, we need to pick the right IP configuration based on the cluster ip family
// IPv6 configuration is only supported as non-primary, so we need to fetch the ip configuration where the
// privateIPAddressVersion matches the clusterIP family
2019-09-27 21:51:53 +00:00
primaryIPConfiguration , err = ss . getConfigForScaleSetByIPFamily ( primaryNetworkInterfaceConfiguration , vmName , ipv6 )
if err != nil {
2020-03-26 21:07:15 +00:00
return "" , "" , "" , nil , err
2019-09-27 21:51:53 +00:00
}
}
// Update primary IP configuration's LoadBalancerBackendAddressPools.
foundPool := false
newBackendPools := [ ] compute . SubResource { }
if primaryIPConfiguration . LoadBalancerBackendAddressPools != nil {
newBackendPools = * primaryIPConfiguration . LoadBalancerBackendAddressPools
}
for _ , existingPool := range newBackendPools {
if strings . EqualFold ( backendPoolID , * existingPool . ID ) {
foundPool = true
break
}
}
// The backendPoolID has already been found from existing LoadBalancerBackendAddressPools.
if foundPool {
2020-03-26 21:07:15 +00:00
return "" , "" , "" , nil , nil
2019-09-27 21:51:53 +00:00
}
if ss . useStandardLoadBalancer ( ) && len ( newBackendPools ) > 0 {
// Although standard load balancer supports backends from multiple scale
// sets, the same network interface couldn't be added to more than one load balancer of
// the same type. Omit those nodes (e.g. masters) so Azure ARM won't complain
// about this.
newBackendPoolsIDs := make ( [ ] string , 0 , len ( newBackendPools ) )
for _ , pool := range newBackendPools {
if pool . ID != nil {
newBackendPoolsIDs = append ( newBackendPoolsIDs , * pool . ID )
}
}
isSameLB , oldLBName , err := isBackendPoolOnSameLB ( backendPoolID , newBackendPoolsIDs )
if err != nil {
2020-03-26 21:07:15 +00:00
return "" , "" , "" , nil , err
2019-09-27 21:51:53 +00:00
}
if ! isSameLB {
klog . V ( 4 ) . Infof ( "Node %q has already been added to LB %q, omit adding it to a new one" , nodeName , oldLBName )
2020-03-26 21:07:15 +00:00
return "" , "" , "" , nil , nil
2019-09-27 21:51:53 +00:00
}
}
// Compose a new vmssVM with added backendPoolID.
newBackendPools = append ( newBackendPools ,
compute . SubResource {
ID : to . StringPtr ( backendPoolID ) ,
} )
primaryIPConfiguration . LoadBalancerBackendAddressPools = & newBackendPools
2020-03-26 21:07:15 +00:00
newVM := & compute . VirtualMachineScaleSetVM {
2019-09-27 21:51:53 +00:00
Sku : vm . Sku ,
Location : vm . Location ,
VirtualMachineScaleSetVMProperties : & compute . VirtualMachineScaleSetVMProperties {
HardwareProfile : vm . HardwareProfile ,
NetworkProfileConfiguration : & compute . VirtualMachineScaleSetVMNetworkProfileConfiguration {
NetworkInterfaceConfigurations : & networkInterfaceConfigurations ,
} ,
} ,
}
// Get the node resource group.
nodeResourceGroup , err := ss . GetNodeResourceGroup ( vmName )
if err != nil {
2020-03-26 21:07:15 +00:00
return "" , "" , "" , nil , err
2019-09-27 21:51:53 +00:00
}
2020-03-26 21:07:15 +00:00
return nodeResourceGroup , ssName , instanceID , newVM , nil
2019-09-27 21:51:53 +00:00
}
func getVmssAndResourceGroupNameByVMProviderID ( providerID string ) ( string , string , error ) {
matches := vmssVMProviderIDRE . FindStringSubmatch ( providerID )
if len ( matches ) != 3 {
return "" , "" , ErrorNotVmssInstance
}
return matches [ 1 ] , matches [ 2 ] , nil
}
func ( ss * scaleSet ) ensureVMSSInPool ( service * v1 . Service , nodes [ ] * v1 . Node , backendPoolID string , vmSetName string ) error {
2020-03-26 21:07:15 +00:00
klog . V ( 2 ) . Infof ( "ensureVMSSInPool: ensuring VMSS with backendPoolID %s" , backendPoolID )
2019-09-27 21:51:53 +00:00
vmssNamesMap := make ( map [ string ] bool )
// the standard load balancer supports multiple vmss in its backend while the basic sku doesn't
if ss . useStandardLoadBalancer ( ) {
for _ , node := range nodes {
if ss . excludeMasterNodesFromStandardLB ( ) && isMasterNode ( node ) {
continue
}
2020-08-13 18:59:02 +00:00
if ss . ShouldNodeExcludedFromLoadBalancer ( node ) {
klog . V ( 4 ) . Infof ( "Excluding unmanaged/external-resource-group node %q" , node . Name )
continue
}
2019-09-27 21:51:53 +00:00
// in this scenario the vmSetName is an empty string and the name of vmss should be obtained from the provider IDs of nodes
2020-03-26 21:07:15 +00:00
resourceGroupName , vmssName , err := getVmssAndResourceGroupNameByVMProviderID ( node . Spec . ProviderID )
2019-09-27 21:51:53 +00:00
if err != nil {
klog . V ( 4 ) . Infof ( "ensureVMSSInPool: found VMAS node %s, will skip checking and continue" , node . Name )
continue
}
// only vmsses in the resource group same as it's in azure config are included
if strings . EqualFold ( resourceGroupName , ss . ResourceGroup ) {
vmssNamesMap [ vmssName ] = true
}
}
} else {
vmssNamesMap [ vmSetName ] = true
}
2020-03-26 21:07:15 +00:00
klog . V ( 2 ) . Infof ( "ensureVMSSInPool begins to update VMSS %v with backendPoolID %s" , vmssNamesMap , backendPoolID )
2019-09-27 21:51:53 +00:00
for vmssName := range vmssNamesMap {
2020-03-26 21:07:15 +00:00
vmss , err := ss . getVMSS ( vmssName , azcache . CacheReadTypeDefault )
2019-09-27 21:51:53 +00:00
if err != nil {
return err
}
// When vmss is being deleted, CreateOrUpdate API would report "the vmss is being deleted" error.
// Since it is being deleted, we shouldn't send more CreateOrUpdate requests for it.
if vmss . ProvisioningState != nil && strings . EqualFold ( * vmss . ProvisioningState , virtualMachineScaleSetsDeallocating ) {
klog . V ( 3 ) . Infof ( "ensureVMSSInPool: found vmss %s being deleted, skipping" , vmssName )
continue
}
if vmss . VirtualMachineProfile . NetworkProfile . NetworkInterfaceConfigurations == nil {
2020-08-10 17:43:49 +00:00
klog . V ( 4 ) . Infof ( "EnsureHostInPool: cannot obtain the primary network interface configuration of vmss %s" , vmssName )
2019-09-27 21:51:53 +00:00
continue
}
vmssNIC := * vmss . VirtualMachineProfile . NetworkProfile . NetworkInterfaceConfigurations
primaryNIC , err := ss . getPrimaryNetworkInterfaceConfigurationForScaleSet ( vmssNIC , vmssName )
if err != nil {
return err
}
2020-08-10 17:43:49 +00:00
var primaryIPConfig * compute . VirtualMachineScaleSetIPConfiguration
ipv6 := utilnet . IsIPv6String ( service . Spec . ClusterIP )
// Find primary network interface configuration.
if ! ss . Cloud . ipv6DualStackEnabled && ! ipv6 {
// Find primary IP configuration.
primaryIPConfig , err = getPrimaryIPConfigFromVMSSNetworkConfig ( primaryNIC )
if err != nil {
return err
}
} else {
primaryIPConfig , err = ss . getConfigForScaleSetByIPFamily ( primaryNIC , "" , ipv6 )
if err != nil {
return err
}
2019-09-27 21:51:53 +00:00
}
2020-08-10 17:43:49 +00:00
2019-09-27 21:51:53 +00:00
loadBalancerBackendAddressPools := [ ] compute . SubResource { }
if primaryIPConfig . LoadBalancerBackendAddressPools != nil {
loadBalancerBackendAddressPools = * primaryIPConfig . LoadBalancerBackendAddressPools
}
var found bool
for _ , loadBalancerBackendAddressPool := range loadBalancerBackendAddressPools {
if strings . EqualFold ( * loadBalancerBackendAddressPool . ID , backendPoolID ) {
found = true
break
}
}
if found {
continue
}
if ss . useStandardLoadBalancer ( ) && len ( loadBalancerBackendAddressPools ) > 0 {
// Although standard load balancer supports backends from multiple scale
// sets, the same network interface couldn't be added to more than one load balancer of
// the same type. Omit those nodes (e.g. masters) so Azure ARM won't complain
// about this.
newBackendPoolsIDs := make ( [ ] string , 0 , len ( loadBalancerBackendAddressPools ) )
for _ , pool := range loadBalancerBackendAddressPools {
if pool . ID != nil {
newBackendPoolsIDs = append ( newBackendPoolsIDs , * pool . ID )
}
}
isSameLB , oldLBName , err := isBackendPoolOnSameLB ( backendPoolID , newBackendPoolsIDs )
if err != nil {
return err
}
if ! isSameLB {
klog . V ( 4 ) . Infof ( "VMSS %q has already been added to LB %q, omit adding it to a new one" , vmssName , oldLBName )
return nil
}
}
// Compose a new vmss with added backendPoolID.
loadBalancerBackendAddressPools = append ( loadBalancerBackendAddressPools ,
compute . SubResource {
ID : to . StringPtr ( backendPoolID ) ,
} )
primaryIPConfig . LoadBalancerBackendAddressPools = & loadBalancerBackendAddressPools
newVMSS := compute . VirtualMachineScaleSet {
Sku : vmss . Sku ,
Location : vmss . Location ,
VirtualMachineScaleSetProperties : & compute . VirtualMachineScaleSetProperties {
VirtualMachineProfile : & compute . VirtualMachineScaleSetVMProfile {
NetworkProfile : & compute . VirtualMachineScaleSetNetworkProfile {
NetworkInterfaceConfigurations : & vmssNIC ,
} ,
} ,
} ,
}
klog . V ( 2 ) . Infof ( "ensureVMSSInPool begins to update vmss(%s) with new backendPoolID %s" , vmssName , backendPoolID )
2020-03-26 21:07:15 +00:00
rerr := ss . CreateOrUpdateVMSS ( ss . ResourceGroup , vmssName , newVMSS )
if rerr != nil {
klog . Errorf ( "ensureVMSSInPool CreateOrUpdateVMSS(%s) with new backendPoolID %s, err: %v" , vmssName , backendPoolID , err )
return rerr . Error ( )
2019-09-27 21:51:53 +00:00
}
}
return nil
}
// EnsureHostsInPool ensures the given Node's primary IP configurations are
// participating in the specified LoadBalancer Backend Pool.
func ( ss * scaleSet ) EnsureHostsInPool ( service * v1 . Service , nodes [ ] * v1 . Node , backendPoolID string , vmSetName string , isInternal bool ) error {
hostUpdates := make ( [ ] func ( ) error , 0 , len ( nodes ) )
2020-03-26 21:07:15 +00:00
nodeUpdates := make ( map [ vmssMetaInfo ] map [ string ] compute . VirtualMachineScaleSetVM )
errors := make ( [ ] error , 0 )
2019-09-27 21:51:53 +00:00
for _ , node := range nodes {
localNodeName := node . Name
if ss . useStandardLoadBalancer ( ) && ss . excludeMasterNodesFromStandardLB ( ) && isMasterNode ( node ) {
klog . V ( 4 ) . Infof ( "Excluding master node %q from load balancer backendpool %q" , localNodeName , backendPoolID )
continue
}
if ss . ShouldNodeExcludedFromLoadBalancer ( node ) {
klog . V ( 4 ) . Infof ( "Excluding unmanaged/external-resource-group node %q" , localNodeName )
continue
}
2020-03-26 21:07:15 +00:00
// Check whether the node is VMAS virtual machine.
managedByAS , err := ss . isNodeManagedByAvailabilitySet ( localNodeName , azcache . CacheReadTypeDefault )
if err != nil {
klog . Errorf ( "Failed to check isNodeManagedByAvailabilitySet(%s): %v" , localNodeName , err )
errors = append ( errors , err )
continue
}
if managedByAS {
// VMAS nodes should also be added to the SLB backends.
if ss . useStandardLoadBalancer ( ) {
hostUpdates = append ( hostUpdates , func ( ) error {
_ , _ , _ , _ , err := ss . availabilitySet . EnsureHostInPool ( service , types . NodeName ( localNodeName ) , backendPoolID , vmSetName , isInternal )
return err
} )
continue
2019-09-27 21:51:53 +00:00
}
2020-03-26 21:07:15 +00:00
klog . V ( 3 ) . Infof ( "EnsureHostsInPool skips node %s because VMAS nodes couldn't be added to basic LB with VMSS backends" , localNodeName )
continue
}
2019-09-27 21:51:53 +00:00
2020-03-26 21:07:15 +00:00
nodeResourceGroup , nodeVMSS , nodeInstanceID , nodeVMSSVM , err := ss . EnsureHostInPool ( service , types . NodeName ( localNodeName ) , backendPoolID , vmSetName , isInternal )
if err != nil {
klog . Errorf ( "EnsureHostInPool(%s): backendPoolID(%s) - failed to ensure host in pool: %q" , getServiceName ( service ) , backendPoolID , err )
errors = append ( errors , err )
continue
}
2019-09-27 21:51:53 +00:00
2020-03-26 21:07:15 +00:00
// No need to update if nodeVMSSVM is nil.
if nodeVMSSVM == nil {
continue
}
nodeVMSSMetaInfo := vmssMetaInfo { vmssName : nodeVMSS , resourceGroup : nodeResourceGroup }
if v , ok := nodeUpdates [ nodeVMSSMetaInfo ] ; ok {
v [ nodeInstanceID ] = * nodeVMSSVM
} else {
nodeUpdates [ nodeVMSSMetaInfo ] = map [ string ] compute . VirtualMachineScaleSetVM {
nodeInstanceID : * nodeVMSSVM ,
2019-09-27 21:51:53 +00:00
}
}
2020-03-26 21:07:15 +00:00
// Invalidate the cache since the VMSS VM would be updated.
defer ss . deleteCacheForNode ( localNodeName )
2019-09-27 21:51:53 +00:00
}
2020-03-26 21:07:15 +00:00
// Update VMs with best effort that have already been added to nodeUpdates.
for meta , update := range nodeUpdates {
2020-04-19 06:53:00 +00:00
// create new instance of meta and update for passing to anonymous function
meta := meta
update := update
2020-03-26 21:07:15 +00:00
hostUpdates = append ( hostUpdates , func ( ) error {
ctx , cancel := getContextWithCancel ( )
defer cancel ( )
klog . V ( 2 ) . Infof ( "EnsureHostInPool begins to UpdateVMs for VMSS(%s, %s) with new backendPoolID %s" , meta . resourceGroup , meta . vmssName , backendPoolID )
rerr := ss . VirtualMachineScaleSetVMsClient . UpdateVMs ( ctx , meta . resourceGroup , meta . vmssName , update , "network_update" )
if rerr != nil {
klog . Errorf ( "EnsureHostInPool UpdateVMs for VMSS(%s, %s) failed with error %v" , meta . resourceGroup , meta . vmssName , rerr . Error ( ) )
return rerr . Error ( )
}
return nil
} )
}
errs := utilerrors . AggregateGoroutines ( hostUpdates ... )
2019-09-27 21:51:53 +00:00
if errs != nil {
return utilerrors . Flatten ( errs )
}
2020-03-26 21:07:15 +00:00
// Fail if there're other errors.
if len ( errors ) > 0 {
return utilerrors . Flatten ( utilerrors . NewAggregate ( errors ) )
}
// Ensure the backendPoolID is also added on VMSS itself.
// Refer to issue kubernetes/kubernetes#80365 for detailed information
2019-09-27 21:51:53 +00:00
err := ss . ensureVMSSInPool ( service , nodes , backendPoolID , vmSetName )
if err != nil {
return err
}
return nil
}
2020-03-26 21:07:15 +00:00
// ensureBackendPoolDeletedFromNode ensures the loadBalancer backendAddressPools deleted
// from the specified node, which returns (resourceGroup, vmssName, instanceID, vmssVM, error).
func ( ss * scaleSet ) ensureBackendPoolDeletedFromNode ( service * v1 . Service , nodeName , backendPoolID string ) ( string , string , string , * compute . VirtualMachineScaleSetVM , error ) {
ssName , instanceID , vm , err := ss . getVmssVM ( nodeName , azcache . CacheReadTypeDefault )
2019-09-27 21:51:53 +00:00
if err != nil {
2020-03-26 21:07:15 +00:00
return "" , "" , "" , nil , err
2019-09-27 21:51:53 +00:00
}
// Find primary network interface configuration.
if vm . NetworkProfileConfiguration . NetworkInterfaceConfigurations == nil {
klog . V ( 4 ) . Infof ( "EnsureHostInPool: cannot obtain the primary network interface configuration, of vm %s, probably because the vm's being deleted" , nodeName )
2020-03-26 21:07:15 +00:00
return "" , "" , "" , nil , nil
2019-09-27 21:51:53 +00:00
}
networkInterfaceConfigurations := * vm . NetworkProfileConfiguration . NetworkInterfaceConfigurations
primaryNetworkInterfaceConfiguration , err := ss . getPrimaryNetworkInterfaceConfiguration ( networkInterfaceConfigurations , nodeName )
if err != nil {
2020-03-26 21:07:15 +00:00
return "" , "" , "" , nil , err
2019-09-27 21:51:53 +00:00
}
// Find primary IP configuration.
primaryIPConfiguration , err := getPrimaryIPConfigFromVMSSNetworkConfig ( primaryNetworkInterfaceConfiguration )
if err != nil {
2020-03-26 21:07:15 +00:00
return "" , "" , "" , nil , err
2019-09-27 21:51:53 +00:00
}
if primaryIPConfiguration . LoadBalancerBackendAddressPools == nil || len ( * primaryIPConfiguration . LoadBalancerBackendAddressPools ) == 0 {
2020-03-26 21:07:15 +00:00
return "" , "" , "" , nil , nil
2019-09-27 21:51:53 +00:00
}
// Construct new loadBalancerBackendAddressPools and remove backendAddressPools from primary IP configuration.
existingBackendPools := * primaryIPConfiguration . LoadBalancerBackendAddressPools
newBackendPools := [ ] compute . SubResource { }
foundPool := false
for i := len ( existingBackendPools ) - 1 ; i >= 0 ; i -- {
curPool := existingBackendPools [ i ]
if strings . EqualFold ( backendPoolID , * curPool . ID ) {
klog . V ( 10 ) . Infof ( "ensureBackendPoolDeletedFromNode gets unwanted backend pool %q for node %s" , backendPoolID , nodeName )
foundPool = true
newBackendPools = append ( existingBackendPools [ : i ] , existingBackendPools [ i + 1 : ] ... )
}
}
// Pool not found, assume it has been already removed.
if ! foundPool {
2020-03-26 21:07:15 +00:00
return "" , "" , "" , nil , nil
2019-09-27 21:51:53 +00:00
}
// Compose a new vmssVM with added backendPoolID.
primaryIPConfiguration . LoadBalancerBackendAddressPools = & newBackendPools
2020-03-26 21:07:15 +00:00
newVM := & compute . VirtualMachineScaleSetVM {
2019-09-27 21:51:53 +00:00
Sku : vm . Sku ,
Location : vm . Location ,
VirtualMachineScaleSetVMProperties : & compute . VirtualMachineScaleSetVMProperties {
HardwareProfile : vm . HardwareProfile ,
NetworkProfileConfiguration : & compute . VirtualMachineScaleSetVMNetworkProfileConfiguration {
NetworkInterfaceConfigurations : & networkInterfaceConfigurations ,
} ,
} ,
}
// Get the node resource group.
nodeResourceGroup , err := ss . GetNodeResourceGroup ( nodeName )
if err != nil {
2020-03-26 21:07:15 +00:00
return "" , "" , "" , nil , err
2019-09-27 21:51:53 +00:00
}
2020-03-26 21:07:15 +00:00
return nodeResourceGroup , ssName , instanceID , newVM , nil
2019-09-27 21:51:53 +00:00
}
// getNodeNameByIPConfigurationID gets the node name by IP configuration ID.
func ( ss * scaleSet ) getNodeNameByIPConfigurationID ( ipConfigurationID string ) ( string , error ) {
matches := vmssIPConfigurationRE . FindStringSubmatch ( ipConfigurationID )
if len ( matches ) != 4 {
klog . V ( 4 ) . Infof ( "Can not extract scale set name from ipConfigurationID (%s), assuming it is mananaged by availability set" , ipConfigurationID )
return "" , ErrorNotVmssInstance
}
resourceGroup := matches [ 1 ]
scaleSetName := matches [ 2 ]
instanceID := matches [ 3 ]
2020-03-26 21:07:15 +00:00
vm , err := ss . getVmssVMByInstanceID ( resourceGroup , scaleSetName , instanceID , azcache . CacheReadTypeUnsafe )
2019-09-27 21:51:53 +00:00
if err != nil {
return "" , err
}
if vm . OsProfile != nil && vm . OsProfile . ComputerName != nil {
return strings . ToLower ( * vm . OsProfile . ComputerName ) , nil
}
return "" , nil
}
func getScaleSetAndResourceGroupNameByIPConfigurationID ( ipConfigurationID string ) ( string , string , error ) {
matches := vmssIPConfigurationRE . FindStringSubmatch ( ipConfigurationID )
if len ( matches ) != 4 {
klog . V ( 4 ) . Infof ( "Can not extract scale set name from ipConfigurationID (%s), assuming it is mananaged by availability set" , ipConfigurationID )
return "" , "" , ErrorNotVmssInstance
}
resourceGroup := matches [ 1 ]
scaleSetName := matches [ 2 ]
return scaleSetName , resourceGroup , nil
}
func ( ss * scaleSet ) ensureBackendPoolDeletedFromVMSS ( service * v1 . Service , backendPoolID , vmSetName string , ipConfigurationIDs [ ] string ) error {
vmssNamesMap := make ( map [ string ] bool )
// the standard load balancer supports multiple vmss in its backend while the basic sku doesn't
if ss . useStandardLoadBalancer ( ) {
for _ , ipConfigurationID := range ipConfigurationIDs {
// in this scenario the vmSetName is an empty string and the name of vmss should be obtained from the provider IDs of nodes
vmssName , resourceGroupName , err := getScaleSetAndResourceGroupNameByIPConfigurationID ( ipConfigurationID )
if err != nil {
klog . V ( 4 ) . Infof ( "ensureBackendPoolDeletedFromVMSS: found VMAS ipcConfigurationID %s, will skip checking and continue" , ipConfigurationID )
continue
}
// only vmsses in the resource group same as it's in azure config are included
if strings . EqualFold ( resourceGroupName , ss . ResourceGroup ) {
vmssNamesMap [ vmssName ] = true
}
}
} else {
vmssNamesMap [ vmSetName ] = true
}
for vmssName := range vmssNamesMap {
2020-03-26 21:07:15 +00:00
vmss , err := ss . getVMSS ( vmssName , azcache . CacheReadTypeDefault )
2019-09-27 21:51:53 +00:00
// When vmss is being deleted, CreateOrUpdate API would report "the vmss is being deleted" error.
// Since it is being deleted, we shouldn't send more CreateOrUpdate requests for it.
if vmss . ProvisioningState != nil && strings . EqualFold ( * vmss . ProvisioningState , virtualMachineScaleSetsDeallocating ) {
klog . V ( 3 ) . Infof ( "ensureVMSSInPool: found vmss %s being deleted, skipping" , vmssName )
continue
}
if err != nil {
return err
}
if vmss . VirtualMachineProfile . NetworkProfile . NetworkInterfaceConfigurations == nil {
klog . V ( 4 ) . Infof ( "EnsureHostInPool: cannot obtain the primary network interface configuration, of vmss %s" , vmssName )
continue
}
vmssNIC := * vmss . VirtualMachineProfile . NetworkProfile . NetworkInterfaceConfigurations
primaryNIC , err := ss . getPrimaryNetworkInterfaceConfigurationForScaleSet ( vmssNIC , vmssName )
if err != nil {
return err
}
primaryIPConfig , err := getPrimaryIPConfigFromVMSSNetworkConfig ( primaryNIC )
if err != nil {
return err
}
loadBalancerBackendAddressPools := [ ] compute . SubResource { }
if primaryIPConfig . LoadBalancerBackendAddressPools != nil {
loadBalancerBackendAddressPools = * primaryIPConfig . LoadBalancerBackendAddressPools
}
var found bool
var newBackendPools [ ] compute . SubResource
for i := len ( loadBalancerBackendAddressPools ) - 1 ; i >= 0 ; i -- {
curPool := loadBalancerBackendAddressPools [ i ]
if strings . EqualFold ( backendPoolID , * curPool . ID ) {
klog . V ( 10 ) . Infof ( "ensureBackendPoolDeletedFromVMSS gets unwanted backend pool %q for VMSS %s" , backendPoolID , vmssName )
found = true
newBackendPools = append ( loadBalancerBackendAddressPools [ : i ] , loadBalancerBackendAddressPools [ i + 1 : ] ... )
}
}
if ! found {
continue
}
// Compose a new vmss with added backendPoolID.
primaryIPConfig . LoadBalancerBackendAddressPools = & newBackendPools
newVMSS := compute . VirtualMachineScaleSet {
Sku : vmss . Sku ,
Location : vmss . Location ,
VirtualMachineScaleSetProperties : & compute . VirtualMachineScaleSetProperties {
VirtualMachineProfile : & compute . VirtualMachineScaleSetVMProfile {
NetworkProfile : & compute . VirtualMachineScaleSetNetworkProfile {
NetworkInterfaceConfigurations : & vmssNIC ,
} ,
} ,
} ,
}
klog . V ( 2 ) . Infof ( "ensureBackendPoolDeletedFromVMSS begins to update vmss(%s) with backendPoolID %s" , vmssName , backendPoolID )
2020-03-26 21:07:15 +00:00
rerr := ss . CreateOrUpdateVMSS ( ss . ResourceGroup , vmssName , newVMSS )
if rerr != nil {
klog . Errorf ( "ensureBackendPoolDeletedFromVMSS CreateOrUpdateVMSS(%s) with new backendPoolID %s, err: %v" , vmssName , backendPoolID , err )
return rerr . Error ( )
2019-09-27 21:51:53 +00:00
}
}
return nil
}
// EnsureBackendPoolDeleted ensures the loadBalancer backendAddressPools deleted from the specified nodes.
func ( ss * scaleSet ) EnsureBackendPoolDeleted ( service * v1 . Service , backendPoolID , vmSetName string , backendAddressPools * [ ] network . BackendAddressPool ) error {
// Returns nil if backend address pools already deleted.
if backendAddressPools == nil {
return nil
}
ipConfigurationIDs := [ ] string { }
for _ , backendPool := range * backendAddressPools {
if strings . EqualFold ( * backendPool . ID , backendPoolID ) && backendPool . BackendIPConfigurations != nil {
for _ , ipConf := range * backendPool . BackendIPConfigurations {
if ipConf . ID == nil {
continue
}
ipConfigurationIDs = append ( ipConfigurationIDs , * ipConf . ID )
}
}
}
hostUpdates := make ( [ ] func ( ) error , 0 , len ( ipConfigurationIDs ) )
2020-03-26 21:07:15 +00:00
nodeUpdates := make ( map [ vmssMetaInfo ] map [ string ] compute . VirtualMachineScaleSetVM )
errors := make ( [ ] error , 0 )
2019-09-27 21:51:53 +00:00
for i := range ipConfigurationIDs {
ipConfigurationID := ipConfigurationIDs [ i ]
2020-03-26 21:07:15 +00:00
var scaleSetName string
var err error
if scaleSetName , err = extractScaleSetNameByProviderID ( ipConfigurationID ) ; err == nil {
// Only remove nodes belonging to specified vmSet to basic LB backends.
if ! ss . useStandardLoadBalancer ( ) && ! strings . EqualFold ( scaleSetName , vmSetName ) {
continue
2019-09-27 21:51:53 +00:00
}
2020-03-26 21:07:15 +00:00
}
2019-09-27 21:51:53 +00:00
2020-03-26 21:07:15 +00:00
nodeName , err := ss . getNodeNameByIPConfigurationID ( ipConfigurationID )
if err != nil {
if err == ErrorNotVmssInstance { // Do nothing for the VMAS nodes.
continue
2019-09-27 21:51:53 +00:00
}
2020-03-26 21:07:15 +00:00
klog . Errorf ( "Failed to getNodeNameByIPConfigurationID(%s): %v" , ipConfigurationID , err )
errors = append ( errors , err )
continue
}
2019-09-27 21:51:53 +00:00
2020-03-26 21:07:15 +00:00
nodeResourceGroup , nodeVMSS , nodeInstanceID , nodeVMSSVM , err := ss . ensureBackendPoolDeletedFromNode ( service , nodeName , backendPoolID )
if err != nil {
klog . Errorf ( "EnsureBackendPoolDeleted(%s): backendPoolID(%s) - failed with error %v" , getServiceName ( service ) , backendPoolID , err )
errors = append ( errors , err )
continue
2019-09-27 21:51:53 +00:00
}
2020-03-26 21:07:15 +00:00
// No need to update if nodeVMSSVM is nil.
if nodeVMSSVM == nil {
continue
}
nodeVMSSMetaInfo := vmssMetaInfo { vmssName : nodeVMSS , resourceGroup : nodeResourceGroup }
if v , ok := nodeUpdates [ nodeVMSSMetaInfo ] ; ok {
v [ nodeInstanceID ] = * nodeVMSSVM
} else {
nodeUpdates [ nodeVMSSMetaInfo ] = map [ string ] compute . VirtualMachineScaleSetVM {
nodeInstanceID : * nodeVMSSVM ,
}
}
// Invalidate the cache since the VMSS VM would be updated.
defer ss . deleteCacheForNode ( nodeName )
2019-09-27 21:51:53 +00:00
}
2020-03-26 21:07:15 +00:00
// Update VMs with best effort that have already been added to nodeUpdates.
for meta , update := range nodeUpdates {
2020-04-19 06:53:00 +00:00
// create new instance of meta and update for passing to anonymous function
meta := meta
update := update
2020-03-26 21:07:15 +00:00
hostUpdates = append ( hostUpdates , func ( ) error {
ctx , cancel := getContextWithCancel ( )
defer cancel ( )
klog . V ( 2 ) . Infof ( "EnsureBackendPoolDeleted begins to UpdateVMs for VMSS(%s, %s) with backendPoolID %s" , meta . resourceGroup , meta . vmssName , backendPoolID )
rerr := ss . VirtualMachineScaleSetVMsClient . UpdateVMs ( ctx , meta . resourceGroup , meta . vmssName , update , "network_update" )
if rerr != nil {
klog . Errorf ( "EnsureBackendPoolDeleted UpdateVMs for VMSS(%s, %s) failed with error %v" , meta . resourceGroup , meta . vmssName , rerr . Error ( ) )
return rerr . Error ( )
}
return nil
} )
}
errs := utilerrors . AggregateGoroutines ( hostUpdates ... )
2019-09-27 21:51:53 +00:00
if errs != nil {
return utilerrors . Flatten ( errs )
}
2020-03-26 21:07:15 +00:00
// Fail if there're other errors.
if len ( errors ) > 0 {
return utilerrors . Flatten ( utilerrors . NewAggregate ( errors ) )
}
// Ensure the backendPoolID is also deleted on VMSS itself.
2019-09-27 21:51:53 +00:00
err := ss . ensureBackendPoolDeletedFromVMSS ( service , backendPoolID , vmSetName , ipConfigurationIDs )
if err != nil {
return err
}
return nil
}