@@ -67,60 +67,93 @@ const additionalTargetGroupPrefix = "additional-listener-"
6767// cantAttachSGToNLBRegions is a set of regions that do not support Security Groups in NLBs.
6868var cantAttachSGToNLBRegions = sets .New ("us-iso-east-1" , "us-iso-west-1" , "us-isob-east-1" )
6969
70+ type lbReconciler func () error
71+
7072// ReconcileLoadbalancers reconciles the load balancers for the given cluster.
7173func (s * Service ) ReconcileLoadbalancers () error {
7274 s .scope .Debug ("Reconciling load balancers" )
7375
7476 var errs []error
77+ var lbReconcilers []lbReconciler
78+
79+ // The following splits load balancer reconciliation into 2 phases:
80+ // 1. Get or create the load balancer
81+ // 2. Reconcile the load balancer
82+ // We ensure that we only wait for the load balancer to become available in
83+ // the reconcile phase. This is useful when creating multiple load
84+ // balancers, as they can take several minutes to become available.
7585
7686 for _ , lbSpec := range s .scope .ControlPlaneLoadBalancers () {
7787 if lbSpec == nil {
7888 continue
7989 }
8090 switch lbSpec .LoadBalancerType {
8191 case infrav1 .LoadBalancerTypeClassic :
82- errs = append (errs , s .reconcileClassicLoadBalancer ())
92+ reconciler , err := s .getOrCreateClassicLoadBalancer ()
93+ if err != nil {
94+ errs = append (errs , err )
95+ } else {
96+ lbReconcilers = append (lbReconcilers , reconciler )
97+ }
8398 case infrav1 .LoadBalancerTypeNLB , infrav1 .LoadBalancerTypeALB , infrav1 .LoadBalancerTypeELB :
84- errs = append (errs , s .reconcileV2LB (lbSpec ))
99+ reconciler , err := s .getOrCreateV2LB (lbSpec )
100+ if err != nil {
101+ errs = append (errs , err )
102+ } else {
103+ lbReconcilers = append (lbReconcilers , reconciler )
104+ }
85105 default :
86106 errs = append (errs , fmt .Errorf ("unknown or unsupported load balancer type on primary load balancer: %s" , lbSpec .LoadBalancerType ))
87107 }
88108 }
89109
110+ // Reconcile all load balancers
111+ for _ , reconciler := range lbReconcilers {
112+ if err := reconciler (); err != nil {
113+ errs = append (errs , err )
114+ }
115+ }
116+
90117 return kerrors .NewAggregate (errs )
91118}
92119
93- // reconcileV2LB creates a load balancer. It also takes care of generating unique names across
120+ // getOrCreateV2LB creates a load balancer. It also takes care of generating unique names across
94121// namespaces by appending the namespace to the name.
95- func (s * Service ) reconcileV2LB (lbSpec * infrav1.AWSLoadBalancerSpec ) error {
122+ func (s * Service ) getOrCreateV2LB (lbSpec * infrav1.AWSLoadBalancerSpec ) ( lbReconciler , error ) {
96123 name , err := LBName (s .scope , lbSpec )
97124 if err != nil {
98- return errors .Wrap (err , "failed to get control plane load balancer name" )
125+ return nil , errors .Wrap (err , "failed to get control plane load balancer name" )
99126 }
100127
101128 // Get default api server spec.
102129 desiredLB , err := s .getAPIServerLBSpec (name , lbSpec )
103130 if err != nil {
104- return err
131+ return nil , err
105132 }
106133 lb , err := s .describeLB (name , lbSpec )
107134 switch {
108135 case IsNotFound (err ) && s .scope .ControlPlaneEndpoint ().IsValid ():
109136 // if elb is not found and owner cluster ControlPlaneEndpoint is already populated, then we should not recreate the elb.
110- return errors .Wrapf (err , "no loadbalancer exists for the AWSCluster %s, the cluster has become unrecoverable and should be deleted manually" , s .scope .InfraClusterName ())
137+ return nil , errors .Wrapf (err , "no loadbalancer exists for the AWSCluster %s, the cluster has become unrecoverable and should be deleted manually" , s .scope .InfraClusterName ())
111138 case IsNotFound (err ):
112139 lb , err = s .createLB (desiredLB , lbSpec )
113140 if err != nil {
114141 s .scope .Error (err , "failed to create LB" )
115- return err
142+ return nil , err
116143 }
117144
118145 s .scope .Debug ("Created new network load balancer for apiserver" , "api-server-lb-name" , lb .Name )
119146 case err != nil :
120147 // Failed to describe the classic ELB
121- return err
148+ return nil , err
122149 }
123150
151+ return func () error {
152+ return s .reconcileV2LB (lb , desiredLB , lbSpec )
153+ }, nil
154+ }
155+
156+ func (s * Service ) reconcileV2LB (lb * infrav1.LoadBalancer , desiredLB * infrav1.LoadBalancer , lbSpec * infrav1.AWSLoadBalancerSpec ) error {
124157 wReq := & elbv2.DescribeLoadBalancersInput {
125158 LoadBalancerArns : aws .StringSlice ([]string {lb .ARN }),
126159 }
@@ -507,35 +540,61 @@ func (s *Service) describeLB(name string, lbSpec *infrav1.AWSLoadBalancerSpec) (
507540 return fromSDKTypeToLB (out .LoadBalancers [0 ], outAtt .Attributes , tags ), nil
508541}
509542
510- func (s * Service ) reconcileClassicLoadBalancer () error {
543+ func (s * Service ) getOrCreateClassicLoadBalancer () ( lbReconciler , error ) {
511544 // Generate a default control plane load balancer name. The load balancer name cannot be
512545 // generated by the defaulting webhook, because it is derived from the cluster name, and that
513546 // name is undefined at defaulting time when generateName is used.
514547 name , err := ELBName (s .scope )
515548 if err != nil {
516- return errors .Wrap (err , "failed to get control plane load balancer name" )
549+ return nil , errors .Wrap (err , "failed to get control plane load balancer name" )
517550 }
518551
519552 // Get default api server spec.
520553 spec , err := s .getAPIServerClassicELBSpec (name )
521554 if err != nil {
522- return err
555+ return nil , err
523556 }
524557
525558 apiELB , err := s .describeClassicELB (spec .Name )
526559 switch {
527560 case IsNotFound (err ) && s .scope .ControlPlaneEndpoint ().IsValid ():
528561 // if elb is not found and owner cluster ControlPlaneEndpoint is already populated, then we should not recreate the elb.
529- return errors .Wrapf (err , "no loadbalancer exists for the AWSCluster %s, the cluster has become unrecoverable and should be deleted manually" , s .scope .InfraClusterName ())
562+ return nil , errors .Wrapf (err , "no loadbalancer exists for the AWSCluster %s, the cluster has become unrecoverable and should be deleted manually" , s .scope .InfraClusterName ())
530563 case IsNotFound (err ):
531564 apiELB , err = s .createClassicELB (spec )
532565 if err != nil {
533- return err
566+ return nil , err
534567 }
535568 s .scope .Debug ("Created new classic load balancer for apiserver" , "api-server-elb-name" , apiELB .Name )
536569 case err != nil :
537570 // Failed to describe the classic ELB
538- return err
571+ return nil , err
572+ }
573+
574+ return func () error {
575+ return s .reconcileClassicLoadBalancer (apiELB , spec )
576+ }, nil
577+ }
578+
579+ func (s * Service ) reconcileClassicLoadBalancer (apiELB * infrav1.LoadBalancer , spec * infrav1.LoadBalancer ) error {
580+ if spec .HealthCheck != nil {
581+ if err := wait .WaitForWithRetryable (wait .NewBackoff (), func () (bool , error ) {
582+ if _ , err := s .ELBClient .ConfigureHealthCheck (& elb.ConfigureHealthCheckInput {
583+ LoadBalancerName : aws .String (spec .Name ),
584+ HealthCheck : & elb.HealthCheck {
585+ Target : aws .String (spec .HealthCheck .Target ),
586+ Interval : aws .Int64 (int64 (spec .HealthCheck .Interval .Seconds ())),
587+ Timeout : aws .Int64 (int64 (spec .HealthCheck .Timeout .Seconds ())),
588+ HealthyThreshold : aws .Int64 (spec .HealthCheck .HealthyThreshold ),
589+ UnhealthyThreshold : aws .Int64 (spec .HealthCheck .UnhealthyThreshold ),
590+ },
591+ }); err != nil {
592+ return false , err
593+ }
594+ return true , nil
595+ }, awserrors .LoadBalancerNotFound ); err != nil {
596+ return errors .Wrapf (err , "failed to configure health check for classic load balancer: %v" , spec )
597+ }
539598 }
540599
541600 if apiELB .IsManaged (s .scope .Name ()) {
@@ -1193,26 +1252,6 @@ func (s *Service) createClassicELB(spec *infrav1.LoadBalancer) (*infrav1.LoadBal
11931252 return nil , errors .Wrapf (err , "failed to create classic load balancer: %v" , spec )
11941253 }
11951254
1196- if spec .HealthCheck != nil {
1197- if err := wait .WaitForWithRetryable (wait .NewBackoff (), func () (bool , error ) {
1198- if _ , err := s .ELBClient .ConfigureHealthCheck (& elb.ConfigureHealthCheckInput {
1199- LoadBalancerName : aws .String (spec .Name ),
1200- HealthCheck : & elb.HealthCheck {
1201- Target : aws .String (spec .HealthCheck .Target ),
1202- Interval : aws .Int64 (int64 (spec .HealthCheck .Interval .Seconds ())),
1203- Timeout : aws .Int64 (int64 (spec .HealthCheck .Timeout .Seconds ())),
1204- HealthyThreshold : aws .Int64 (spec .HealthCheck .HealthyThreshold ),
1205- UnhealthyThreshold : aws .Int64 (spec .HealthCheck .UnhealthyThreshold ),
1206- },
1207- }); err != nil {
1208- return false , err
1209- }
1210- return true , nil
1211- }, awserrors .LoadBalancerNotFound ); err != nil {
1212- return nil , errors .Wrapf (err , "failed to configure health check for classic load balancer: %v" , spec )
1213- }
1214- }
1215-
12161255 s .scope .Info ("Created classic load balancer" , "dns-name" , * out .DNSName )
12171256
12181257 res := spec .DeepCopy ()
0 commit comments