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

     1  /*
     2  Copyright 2019 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 routetables
    18  
    19  import (
    20  	"context"
    21  
    22  	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4"
    23  	"github.com/pkg/errors"
    24  	infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
    25  	"sigs.k8s.io/cluster-api-provider-azure/azure"
    26  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/async"
    27  	"sigs.k8s.io/cluster-api-provider-azure/util/tele"
    28  )
    29  
    30  const serviceName = "routetables"
    31  
    32  // RouteTableScope defines the scope interface for route table service.
    33  type RouteTableScope interface {
    34  	azure.Authorizer
    35  	azure.AsyncStatusUpdater
    36  	RouteTableSpecs() []azure.ResourceSpecGetter
    37  	IsVnetManaged() bool
    38  }
    39  
    40  // Service provides operations on azure resources.
    41  type Service struct {
    42  	Scope RouteTableScope
    43  	async.Reconciler
    44  }
    45  
    46  // New creates a new service.
    47  func New(scope RouteTableScope) (*Service, error) {
    48  	client, err := newClient(scope, scope.DefaultedAzureCallTimeout())
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  	return &Service{
    53  		Scope: scope,
    54  		Reconciler: async.New[armnetwork.RouteTablesClientCreateOrUpdateResponse,
    55  			armnetwork.RouteTablesClientDeleteResponse](scope, client, client),
    56  	}, nil
    57  }
    58  
    59  // Name returns the service name.
    60  func (s *Service) Name() string {
    61  	return serviceName
    62  }
    63  
    64  // Reconcile idempotently creates or updates a set of route tables.
    65  func (s *Service) Reconcile(ctx context.Context) error {
    66  	ctx, log, done := tele.StartSpanWithLogger(ctx, "routetables.Service.Reconcile")
    67  	defer done()
    68  
    69  	ctx, cancel := context.WithTimeout(ctx, s.Scope.DefaultedAzureServiceReconcileTimeout())
    70  	defer cancel()
    71  
    72  	var resErr error
    73  
    74  	if managed, err := s.IsManaged(ctx); err == nil && !managed {
    75  		log.V(4).Info("Skipping route tables reconcile in custom vnet mode")
    76  		return nil
    77  	} else if err != nil {
    78  		return errors.Wrap(err, "failed to check if route tables are managed")
    79  	}
    80  
    81  	specs := s.Scope.RouteTableSpecs()
    82  	if len(specs) == 0 {
    83  		return nil
    84  	}
    85  
    86  	// We go through the list of route tables to reconcile each one, independently of the result of the previous one.
    87  	// If multiple errors occur, we return the most pressing one.
    88  	//  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)
    89  	for _, rtSpec := range specs {
    90  		if _, err := s.CreateOrUpdateResource(ctx, rtSpec, serviceName); err != nil {
    91  			if !azure.IsOperationNotDoneError(err) || resErr == nil {
    92  				resErr = err
    93  			}
    94  		}
    95  	}
    96  
    97  	s.Scope.UpdatePutStatus(infrav1.RouteTablesReadyCondition, serviceName, resErr)
    98  	return resErr
    99  }
   100  
   101  // Delete deletes route tables.
   102  func (s *Service) Delete(ctx context.Context) error {
   103  	ctx, log, done := tele.StartSpanWithLogger(ctx, "routetables.Service.Delete")
   104  	defer done()
   105  
   106  	ctx, cancel := context.WithTimeout(ctx, s.Scope.DefaultedAzureServiceReconcileTimeout())
   107  	defer cancel()
   108  
   109  	// Only delete the route tables if their lifecycle is managed by this controller.
   110  	// route tables are managed if and only if the vnet is managed.
   111  	if managed, err := s.IsManaged(ctx); err == nil && !managed {
   112  		log.V(4).Info("Skipping route table deletion in custom vnet mode")
   113  		return nil
   114  	} else if err != nil {
   115  		return errors.Wrap(err, "failed to check if route tables are managed")
   116  	}
   117  
   118  	specs := s.Scope.RouteTableSpecs()
   119  	if len(specs) == 0 {
   120  		return nil
   121  	}
   122  
   123  	// We go through the list of RouteTableSpecs to delete each one, independently of the result of the previous one.
   124  	// If multiple errors occur, we return the most pressing one
   125  	// order of precedence is: error deleting -> deleting in progress -> deleted (no error)
   126  	var result error
   127  	for _, rtSpec := range specs {
   128  		if err := s.DeleteResource(ctx, rtSpec, serviceName); err != nil {
   129  			if !azure.IsOperationNotDoneError(err) || result == nil {
   130  				result = err
   131  			}
   132  		}
   133  	}
   134  	s.Scope.UpdateDeleteStatus(infrav1.RouteTablesReadyCondition, serviceName, result)
   135  	return result
   136  }
   137  
   138  // IsManaged returns true if the route tables' lifecycles are managed.
   139  func (s *Service) IsManaged(ctx context.Context) (bool, error) {
   140  	_, _, done := tele.StartSpanWithLogger(ctx, "routetables.Service.IsManaged")
   141  	defer done()
   142  
   143  	return s.Scope.IsVnetManaged(), nil
   144  }