sigs.k8s.io/cluster-api-provider-azure@v1.17.0/azure/services/loadbalancers/loadbalancers.go (about)

     1  /*
     2  Copyright 2020 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 loadbalancers
    18  
    19  import (
    20  	"context"
    21  
    22  	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4"
    23  	infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
    24  	"sigs.k8s.io/cluster-api-provider-azure/azure"
    25  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/async"
    26  	"sigs.k8s.io/cluster-api-provider-azure/util/tele"
    27  )
    28  
    29  const (
    30  	serviceName           = "loadbalancers"
    31  	httpsProbe            = "HTTPSProbe"
    32  	httpsProbeRequestPath = "/readyz"
    33  	lbRuleHTTPS           = "LBRuleHTTPS"
    34  	outboundNAT           = "OutboundNATAllProtocols"
    35  )
    36  
    37  // LBScope defines the scope interface for a load balancer service.
    38  type LBScope interface {
    39  	azure.ClusterScoper
    40  	azure.AsyncStatusUpdater
    41  	LBSpecs() []azure.ResourceSpecGetter
    42  }
    43  
    44  // Service provides operations on Azure resources.
    45  type Service struct {
    46  	Scope LBScope
    47  	async.Reconciler
    48  }
    49  
    50  // New creates a new service.
    51  func New(scope LBScope) (*Service, error) {
    52  	client, err := newClient(scope, scope.DefaultedAzureCallTimeout())
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	return &Service{
    57  		Scope: scope,
    58  		Reconciler: async.New[armnetwork.LoadBalancersClientCreateOrUpdateResponse,
    59  			armnetwork.LoadBalancersClientDeleteResponse](scope, client, client),
    60  	}, nil
    61  }
    62  
    63  // Name returns the service name.
    64  func (s *Service) Name() string {
    65  	return serviceName
    66  }
    67  
    68  // Reconcile idempotently creates or updates a load balancer.
    69  func (s *Service) Reconcile(ctx context.Context) error {
    70  	ctx, _, done := tele.StartSpanWithLogger(ctx, "loadbalancers.Service.Reconcile")
    71  	defer done()
    72  
    73  	ctx, cancel := context.WithTimeout(ctx, s.Scope.DefaultedAzureServiceReconcileTimeout())
    74  	defer cancel()
    75  
    76  	specs := s.Scope.LBSpecs()
    77  	if len(specs) == 0 {
    78  		return nil
    79  	}
    80  
    81  	// We go through the list of LBSpecs to reconcile each one, independently of the result of the previous one.
    82  	// If multiple errors occur, we return the most pressing one.
    83  	//  Order of precedence (highest -> lowest) is: error that is not an operationNotDoneError (i.e. error creating) -> operationNotDoneError (i.e. creating in progress) -> no error (i.e. created)
    84  	var result error
    85  	for _, lbSpec := range specs {
    86  		if _, err := s.CreateOrUpdateResource(ctx, lbSpec, serviceName); err != nil {
    87  			if !azure.IsOperationNotDoneError(err) || result == nil {
    88  				result = err
    89  			}
    90  		}
    91  	}
    92  
    93  	s.Scope.UpdatePutStatus(infrav1.LoadBalancersReadyCondition, serviceName, result)
    94  	return result
    95  }
    96  
    97  // Delete deletes the public load balancer with the provided name.
    98  func (s *Service) Delete(ctx context.Context) error {
    99  	ctx, _, done := tele.StartSpanWithLogger(ctx, "loadbalancers.Service.Delete")
   100  	defer done()
   101  
   102  	ctx, cancel := context.WithTimeout(ctx, s.Scope.DefaultedAzureServiceReconcileTimeout())
   103  	defer cancel()
   104  
   105  	specs := s.Scope.LBSpecs()
   106  	if len(specs) == 0 {
   107  		return nil
   108  	}
   109  
   110  	// We go through the list of LBSpecs to delete each one, independently of the result of the previous one.
   111  	// If multiple errors occur, we return the most pressing one.
   112  	//  Order of precedence (highest -> lowest) is: error that is not an operationNotDoneError (i.e. error deleting) -> operationNotDoneError (i.e. deleting in progress) -> no error (i.e. deleted)
   113  	var result error
   114  	for _, lbSpec := range specs {
   115  		if err := s.DeleteResource(ctx, lbSpec, serviceName); err != nil {
   116  			if !azure.IsOperationNotDoneError(err) || result == nil {
   117  				result = err
   118  			}
   119  		}
   120  	}
   121  
   122  	s.Scope.UpdateDeleteStatus(infrav1.LoadBalancersReadyCondition, serviceName, result)
   123  	return result
   124  }
   125  
   126  // IsManaged returns always returns true as CAPZ does not support BYO load balancers.
   127  func (s *Service) IsManaged(ctx context.Context) (bool, error) {
   128  	return true, nil
   129  }