@@ -31,24 +31,18 @@ import (
3131type Spec struct {
3232 Name string
3333 SubnetName string
34+ SubnetCidr string
3435 VnetName string
3536 IPAddress string
3637}
3738
3839// Get provides information about an internal load balancer.
39- func (s * Service ) Get (ctx context.Context , spec interface {}) (interface {} , error ) {
40+ func (s * Service ) Get (ctx context.Context , spec interface {}) (network. LoadBalancer , error ) {
4041 internalLBSpec , ok := spec .(* Spec )
4142 if ! ok {
4243 return network.LoadBalancer {}, errors .New ("invalid internal load balancer specification" )
4344 }
44- //lbName := fmt.Sprintf("%s-api-internallb", s.Scope.Cluster.Name)
45- lb , err := s .Client .Get (ctx , s .Scope .AzureCluster .Spec .ResourceGroup , internalLBSpec .Name )
46- if err != nil && azure .ResourceNotFound (err ) {
47- return nil , errors .Wrapf (err , "load balancer %s not found" , internalLBSpec .Name )
48- } else if err != nil {
49- return lb , err
50- }
51- return lb , nil
45+ return s .Client .Get (ctx , s .Scope .ResourceGroup (), internalLBSpec .Name )
5246}
5347
5448// Reconcile gets/creates/updates an internal load balancer.
@@ -61,20 +55,38 @@ func (s *Service) Reconcile(ctx context.Context, spec interface{}) error {
6155 probeName := "tcpHTTPSProbe"
6256 frontEndIPConfigName := "controlplane-internal-lbFrontEnd"
6357 backEndAddressPoolName := "controlplane-internal-backEndPool"
64- idPrefix := fmt .Sprintf ("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/loadBalancers" , s .Scope .SubscriptionID , s .Scope .AzureCluster . Spec . ResourceGroup )
58+ idPrefix := fmt .Sprintf ("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/loadBalancers" , s .Scope .SubscriptionID , s .Scope .ResourceGroup () )
6559 lbName := internalLBSpec .Name
60+ var privateIP string
61+
62+ internalLB , err := s .Get (ctx , internalLBSpec )
63+ if err == nil {
64+ ipConfigs := internalLB .LoadBalancerPropertiesFormat .FrontendIPConfigurations
65+ if ipConfigs != nil && len (* ipConfigs ) > 0 {
66+ privateIP = to .String ((* ipConfigs )[0 ].FrontendIPConfigurationPropertiesFormat .PrivateIPAddress )
67+ }
68+ } else if azure .ResourceNotFound (err ) {
69+ klog .V (2 ).Infof ("internalLB %s not found in RG %s" , internalLBSpec .Name , s .Scope .ResourceGroup ())
70+ privateIP , err = s .getAvailablePrivateIP (ctx , s .Scope .Vnet ().ResourceGroup , internalLBSpec .VnetName , internalLBSpec .SubnetCidr , internalLBSpec .IPAddress )
71+ if err != nil {
72+ return err
73+ }
74+ klog .V (2 ).Infof ("setting internal load balancer IP to %s" , privateIP )
75+ } else {
76+ return errors .Wrap (err , "failed to look for existing internal LB" )
77+ }
6678
6779 klog .V (2 ).Infof ("getting subnet %s" , internalLBSpec .SubnetName )
68- subnet , err := s .SubnetsClient .Get (ctx , s .Scope .AzureCluster . Spec .ResourceGroup , internalLBSpec .VnetName , internalLBSpec .SubnetName )
80+ subnet , err := s .SubnetsClient .Get (ctx , s .Scope .Vnet () .ResourceGroup , internalLBSpec .VnetName , internalLBSpec .SubnetName )
6981 if err != nil {
70- return err
82+ return errors . Wrap ( err , "failed to get subnet" )
7183 }
7284
7385 klog .V (2 ).Infof ("successfully got subnet %s" , internalLBSpec .SubnetName )
7486
7587 // https://docs.microsoft.com/en-us/azure/load-balancer/load-balancer-standard-availability-zones#zone-redundant-by-default
7688 err = s .Client .CreateOrUpdate (ctx ,
77- s .Scope .AzureCluster . Spec . ResourceGroup ,
89+ s .Scope .ResourceGroup () ,
7890 lbName ,
7991 network.LoadBalancer {
8092 Sku : & network.LoadBalancerSku {Name : network .LoadBalancerSkuNameStandard },
@@ -86,7 +98,7 @@ func (s *Service) Reconcile(ctx context.Context, spec interface{}) error {
8698 FrontendIPConfigurationPropertiesFormat : & network.FrontendIPConfigurationPropertiesFormat {
8799 PrivateIPAllocationMethod : network .Static ,
88100 Subnet : & subnet ,
89- PrivateIPAddress : to .StringPtr (internalLBSpec . IPAddress ),
101+ PrivateIPAddress : to .StringPtr (privateIP ),
90102 },
91103 },
92104 },
@@ -146,14 +158,38 @@ func (s *Service) Delete(ctx context.Context, spec interface{}) error {
146158 return errors .New ("invalid internal load balancer specification" )
147159 }
148160 klog .V (2 ).Infof ("deleting internal load balancer %s" , internalLBSpec .Name )
149- err := s .Client .Delete (ctx , s .Scope .AzureCluster . Spec . ResourceGroup , internalLBSpec .Name )
161+ err := s .Client .Delete (ctx , s .Scope .ResourceGroup () , internalLBSpec .Name )
150162 if err != nil && azure .ResourceNotFound (err ) {
151163 // already deleted
152164 return nil
153165 }
154166 if err != nil {
155- return errors .Wrapf (err , "failed to delete internal load balancer %s in resource group %s" , internalLBSpec .Name , s .Scope .AzureCluster . Spec . ResourceGroup )
167+ return errors .Wrapf (err , "failed to delete internal load balancer %s in resource group %s" , internalLBSpec .Name , s .Scope .ResourceGroup () )
156168 }
157169 klog .V (2 ).Infof ("successfully deleted internal load balancer %s" , internalLBSpec .Name )
158170 return nil
159171}
172+
173+ // getAvailablePrivateIP checks if the desired private IP address is available in a virtual network.
174+ // If the IP address is taken or empty, it will make an attempt to find an available IP in the same subnet
175+ func (s * Service ) getAvailablePrivateIP (ctx context.Context , resourceGroup , vnetName , subnetCIDR , PreferredIPAddress string ) (string , error ) {
176+ ip := PreferredIPAddress
177+ if ip == "" {
178+ ip = azure .DefaultInternalLBIPAddress
179+ if subnetCIDR != azure .DefaultControlPlaneSubnetCIDR {
180+ // If the user provided a custom subnet CIDR without providing a private IP, try finding an available IP in the subnet space
181+ ip = subnetCIDR [0 :7 ] + "0"
182+ }
183+ }
184+ result , err := s .VirtualNetworksClient .CheckIPAddressAvailability (ctx , resourceGroup , vnetName , ip )
185+ if err != nil {
186+ return "" , errors .Wrap (err , "failed to check IP availability" )
187+ }
188+ if ! to .Bool (result .Available ) {
189+ if len (to .StringSlice (result .AvailableIPAddresses )) == 0 {
190+ return "" , errors .Errorf ("IP %s is not available in vnet %s and there were no other available IPs found" , ip , vnetName )
191+ }
192+ ip = to .StringSlice (result .AvailableIPAddresses )[0 ]
193+ }
194+ return ip , nil
195+ }
0 commit comments