@@ -9,13 +9,11 @@ import (
99 "sync"
1010 "time"
1111
12- "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
13- "k8s.io/utils/ptr"
14-
1512 "github.com/Azure/azure-sdk-for-go/sdk/azcore"
1613 "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
1714 "github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
1815 "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
16+ "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
1917 "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
2018 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute"
2119 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v6"
@@ -26,6 +24,7 @@ import (
2624 "k8s.io/apimachinery/pkg/util/sets"
2725 "k8s.io/klog/v2"
2826 utilnet "k8s.io/utils/net"
27+ "k8s.io/utils/ptr"
2928)
3029
3130const (
@@ -51,6 +50,8 @@ type Azure struct {
5150 nodeMapLock sync.Mutex
5251 nodeLockMap map [string ]* sync.Mutex
5352 azureWorkloadIdentityEnabled bool
53+ lbBackendPoolSynced map [string ]bool
54+ lbBackendPoolSyncedLock sync.Mutex
5455}
5556
5657type azureCredentialsConfig struct {
@@ -172,11 +173,11 @@ func (a *Azure) AssignPrivateIP(ip net.IP, node *corev1.Node) error {
172173 defer nodeLock .Unlock ()
173174 instance , err := a .getInstance (node )
174175 if err != nil {
175- return err
176+ return fmt . Errorf ( "error while retrieving instance details from Azure: %w" , err )
176177 }
177178 networkInterfaces , err := a .getNetworkInterfaces (instance )
178179 if err != nil {
179- return err
180+ return fmt . Errorf ( "error while retrieving interface details from Azure: %w" , err )
180181 }
181182 if networkInterfaces [0 ].Properties == nil {
182183 return fmt .Errorf ("nil network interface properties" )
@@ -219,9 +220,18 @@ func (a *Azure) AssignPrivateIP(ip net.IP, node *corev1.Node) error {
219220 "omitting backend address pool when adding secondary IP" , ipc )
220221 poller , err := a .createOrUpdate (networkInterface )
221222 if err != nil {
222- return err
223+ return fmt .Errorf ("error while updating network interface: %w" , err )
224+ }
225+ if err = a .waitForCompletion (poller ); err != nil {
226+ return fmt .Errorf ("error while updating network interface: %w" , err )
223227 }
224- return a .waitForCompletion (poller )
228+ // setting lbBackendPoolSynced to true here to make sure that we dont try to
229+ // sync LB backend for any new egress IP later
230+ cacheKey := getIPCacheKey (ip , node )
231+ a .lbBackendPoolSyncedLock .Lock ()
232+ a .lbBackendPoolSynced [cacheKey ] = true
233+ a .lbBackendPoolSyncedLock .Unlock ()
234+ return nil
225235}
226236
227237func (a * Azure ) ReleasePrivateIP (ip net.IP , node * corev1.Node ) error {
@@ -231,11 +241,11 @@ func (a *Azure) ReleasePrivateIP(ip net.IP, node *corev1.Node) error {
231241 defer nodeLock .Unlock ()
232242 instance , err := a .getInstance (node )
233243 if err != nil {
234- return err
244+ return fmt . Errorf ( "error while retrieving instance details from Azure: %w" , err )
235245 }
236246 networkInterfaces , err := a .getNetworkInterfaces (instance )
237247 if err != nil {
238- return err
248+ return fmt . Errorf ( "error while retrieving interface details from Azure: %w" , err )
239249 }
240250 // Perform the operation against the first interface listed, which will be
241251 // the primary interface (if it's defined as such) or the first one returned
@@ -262,9 +272,16 @@ func (a *Azure) ReleasePrivateIP(ip net.IP, node *corev1.Node) error {
262272 // Send the request
263273 poller , err := a .createOrUpdate (networkInterface )
264274 if err != nil {
265- return err
275+ return fmt .Errorf ("error while updating network interface: %w" , err )
276+ }
277+ if err = a .waitForCompletion (poller ); err != nil {
278+ return fmt .Errorf ("error while updating network interface: %w" , err )
266279 }
267- return a .waitForCompletion (poller )
280+ cacheKey := getIPCacheKey (ip , node )
281+ a .lbBackendPoolSyncedLock .Lock ()
282+ delete (a .lbBackendPoolSynced , cacheKey )
283+ a .lbBackendPoolSyncedLock .Unlock ()
284+ return nil
268285}
269286
270287func (a * Azure ) GetNodeEgressIPConfiguration (node * corev1.Node , cpicIPs sets.Set [string ]) ([]* NodeEgressIPConfiguration , error ) {
@@ -302,6 +319,62 @@ func (a *Azure) GetNodeEgressIPConfiguration(node *corev1.Node, cpicIPs sets.Set
302319 return []* NodeEgressIPConfiguration {config }, nil
303320}
304321
322+ // The consensus is to not add egress IP to public load balancer
323+ // backend pool regardless of the presence of an OutBoundRule.
324+ // During upgrade this function removes any egress IP added to
325+ // public load balancer backend pool previously.
326+ func (a * Azure ) SyncLBBackend (ip net.IP , node * corev1.Node ) error {
327+ ipc := ip .String ()
328+ cacheKey := getIPCacheKey (ip , node )
329+ a .lbBackendPoolSyncedLock .Lock ()
330+ defer a .lbBackendPoolSyncedLock .Unlock ()
331+ if synced , ok := a .lbBackendPoolSynced [cacheKey ]; ok && synced {
332+ // nothing to do. Return immediately if LB backend has already synced
333+ return nil
334+ }
335+ klog .Infof ("Acquiring node lock for modifying load balancer backend pool, node: %s, ip: %s" , node .Name , ipc )
336+ nodeLock := a .getNodeLock (node .Name )
337+ nodeLock .Lock ()
338+ defer nodeLock .Unlock ()
339+ instance , err := a .getInstance (node )
340+ if err != nil {
341+ return fmt .Errorf ("error while retrieving instance details from Azure: %w" , err )
342+ }
343+ networkInterfaces , err := a .getNetworkInterfaces (instance )
344+ if err != nil {
345+ return fmt .Errorf ("error while retrieving interface details from Azure: %w" , err )
346+ }
347+ if networkInterfaces [0 ].Properties == nil {
348+ return fmt .Errorf ("nil network interface properties" )
349+ }
350+ // Perform the operation against the first interface listed, which will be
351+ // the primary interface (if it's defined as such) or the first one returned
352+ // following the order Azure specifies.
353+ networkInterface := networkInterfaces [0 ]
354+ var loadBalancerBackendPoolModified bool
355+ // omit Egress IP from LB backend pool
356+ ipConfigurations := networkInterface .Properties .IPConfigurations
357+ for _ , ipCfg := range ipConfigurations {
358+ if ptr .Deref (ipCfg .Properties .PrivateIPAddress , "" ) == ipc &&
359+ ipCfg .Properties .LoadBalancerBackendAddressPools != nil {
360+ ipCfg .Properties .LoadBalancerBackendAddressPools = nil
361+ loadBalancerBackendPoolModified = true
362+ }
363+ }
364+ if loadBalancerBackendPoolModified {
365+ networkInterface .Properties .IPConfigurations = ipConfigurations
366+ poller , err := a .createOrUpdate (networkInterface )
367+ if err != nil {
368+ return fmt .Errorf ("error while updating network interface: %w" , err )
369+ }
370+ if err = a .waitForCompletion (poller ); err != nil {
371+ return fmt .Errorf ("error while updating network interface: %w" , err )
372+ }
373+ a .lbBackendPoolSynced [cacheKey ] = true
374+ }
375+ return nil
376+ }
377+
305378func (a * Azure ) createOrUpdate (networkInterface armnetwork.Interface ) (* runtime.Poller [armnetwork.InterfacesClientCreateOrUpdateResponse ], error ) {
306379 ctx , cancel := context .WithTimeout (a .ctx , defaultAzureOperationTimeout )
307380 defer cancel ()
@@ -604,3 +677,7 @@ func ParseCloudEnvironment(env azureapi.Environment) cloud.Configuration {
604677 }
605678 return cloudConfig
606679}
680+
681+ func getIPCacheKey (ip net.IP , node * corev1.Node ) string {
682+ return fmt .Sprintf ("%s|%s" , node .Name , ip .String ())
683+ }
0 commit comments