sigs.k8s.io/cluster-api-provider-azure@v1.14.3/controllers/azurecluster_reconciler.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 controllers
    18  
    19  import (
    20  	"context"
    21  
    22  	"github.com/pkg/errors"
    23  	"sigs.k8s.io/cluster-api-provider-azure/azure"
    24  	"sigs.k8s.io/cluster-api-provider-azure/azure/scope"
    25  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/bastionhosts"
    26  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/groups"
    27  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/loadbalancers"
    28  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/natgateways"
    29  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/privatedns"
    30  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/privateendpoints"
    31  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/publicips"
    32  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/resourceskus"
    33  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/routetables"
    34  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/securitygroups"
    35  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/subnets"
    36  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/virtualnetworks"
    37  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/vnetpeerings"
    38  	"sigs.k8s.io/cluster-api-provider-azure/util/tele"
    39  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    40  )
    41  
    42  // azureClusterService is the reconciler called by the AzureCluster controller.
    43  type azureClusterService struct {
    44  	scope *scope.ClusterScope
    45  	// services is the list of services that are reconciled by this controller.
    46  	// The order of the services is important as it determines the order in which the services are reconciled.
    47  	services  []azure.ServiceReconciler
    48  	skuCache  *resourceskus.Cache
    49  	Reconcile func(context.Context) error
    50  	Pause     func(context.Context) error
    51  	Delete    func(context.Context) error
    52  }
    53  
    54  // newAzureClusterService populates all the services based on input scope.
    55  func newAzureClusterService(scope *scope.ClusterScope) (*azureClusterService, error) {
    56  	skuCache, err := resourceskus.GetCache(scope, scope.Location())
    57  	if err != nil {
    58  		return nil, errors.Wrap(err, "failed creating a NewCache")
    59  	}
    60  	securityGroupsSvc, err := securitygroups.New(scope)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  	routeTablesSvc, err := routetables.New(scope)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  	publicIPsSvc, err := publicips.New(scope)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  	privateDNSSvc, err := privatedns.New(scope)
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  	vnetPeeringsSvc, err := vnetpeerings.New(scope)
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  	loadbalancersSvc, err := loadbalancers.New(scope)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  	acs := &azureClusterService{
    85  		scope: scope,
    86  		services: []azure.ServiceReconciler{
    87  			groups.New(scope),
    88  			virtualnetworks.New(scope),
    89  			securityGroupsSvc,
    90  			routeTablesSvc,
    91  			publicIPsSvc,
    92  			natgateways.New(scope),
    93  			subnets.New(scope),
    94  			vnetPeeringsSvc,
    95  			loadbalancersSvc,
    96  			privateDNSSvc,
    97  			privateendpoints.New(scope),
    98  			bastionhosts.New(scope),
    99  		},
   100  		skuCache: skuCache,
   101  	}
   102  	acs.Reconcile = acs.reconcile
   103  	acs.Pause = acs.pause
   104  	acs.Delete = acs.delete
   105  
   106  	return acs, nil
   107  }
   108  
   109  // Reconcile reconciles all the services in a predetermined order.
   110  func (s *azureClusterService) reconcile(ctx context.Context) error {
   111  	ctx, _, done := tele.StartSpanWithLogger(ctx, "controllers.azureClusterService.Reconcile")
   112  	defer done()
   113  
   114  	if err := s.setFailureDomainsForLocation(ctx); err != nil {
   115  		return errors.Wrap(err, "failed to get availability zones")
   116  	}
   117  
   118  	s.scope.AzureCluster.SetBackendPoolNameDefault()
   119  	s.scope.SetDNSName()
   120  	s.scope.SetControlPlaneSecurityRules()
   121  
   122  	for _, service := range s.services {
   123  		if err := service.Reconcile(ctx); err != nil {
   124  			return errors.Wrapf(err, "failed to reconcile AzureCluster service %s", service.Name())
   125  		}
   126  	}
   127  
   128  	return nil
   129  }
   130  
   131  // Pause pauses all components making up the cluster.
   132  func (s *azureClusterService) pause(ctx context.Context) error {
   133  	ctx, _, done := tele.StartSpanWithLogger(ctx, "controllers.azureClusterService.Pause")
   134  	defer done()
   135  
   136  	for _, service := range s.services {
   137  		pauser, ok := service.(azure.Pauser)
   138  		if !ok {
   139  			continue
   140  		}
   141  		if err := pauser.Pause(ctx); err != nil {
   142  			return errors.Wrapf(err, "failed to pause AzureCluster service %s", service.Name())
   143  		}
   144  	}
   145  
   146  	return nil
   147  }
   148  
   149  // Delete reconciles all the services in a predetermined order.
   150  func (s *azureClusterService) delete(ctx context.Context) error {
   151  	ctx, _, done := tele.StartSpanWithLogger(ctx, "controllers.azureClusterService.Delete")
   152  	defer done()
   153  
   154  	if !ShouldDeleteIndividualResources(ctx, s.scope) {
   155  		// If the resource group is managed, delete it.
   156  		// We need to explicitly delete vnet peerings, as it is not part of the resource group.
   157  		vnetPeeringsSvc, err := s.getService(vnetpeerings.ServiceName)
   158  		if err != nil {
   159  			return errors.Wrap(err, "failed to get vnet peerings service")
   160  		}
   161  		if err := vnetPeeringsSvc.Delete(ctx); err != nil {
   162  			return errors.Wrap(err, "failed to delete peerings")
   163  		}
   164  
   165  		groupSvc, err := s.getService(groups.ServiceName)
   166  		if err != nil {
   167  			return errors.Wrap(err, "failed to get group service")
   168  		}
   169  
   170  		// Delete the entire resource group directly.
   171  		if err := groupSvc.Delete(ctx); err != nil {
   172  			return errors.Wrap(err, "failed to delete resource group")
   173  		}
   174  	} else {
   175  		// If the resource group is not managed we need to delete resources inside the group one by one.
   176  		// services are deleted in reverse order from the order in which they are reconciled.
   177  		for i := len(s.services) - 1; i >= 0; i-- {
   178  			if err := s.services[i].Delete(ctx); err != nil {
   179  				return errors.Wrapf(err, "failed to delete AzureCluster service %s", s.services[i].Name())
   180  			}
   181  		}
   182  	}
   183  
   184  	return nil
   185  }
   186  
   187  // setFailureDomainsForLocation sets the AzureCluster Status failure domains based on which Azure Availability Zones are available in the cluster location.
   188  // Note that this is not done in a webhook as it requires API calls to fetch the availability zones.
   189  func (s *azureClusterService) setFailureDomainsForLocation(ctx context.Context) error {
   190  	if s.scope.ExtendedLocation() != nil {
   191  		return nil
   192  	}
   193  
   194  	zones, err := s.skuCache.GetZones(ctx, s.scope.Location())
   195  	if err != nil {
   196  		return errors.Wrapf(err, "failed to get zones for location %s", s.scope.Location())
   197  	}
   198  
   199  	for _, zone := range zones {
   200  		s.scope.SetFailureDomain(zone, clusterv1.FailureDomainSpec{
   201  			ControlPlane: true,
   202  		})
   203  	}
   204  
   205  	return nil
   206  }
   207  
   208  func (s *azureClusterService) getService(name string) (azure.ServiceReconciler, error) {
   209  	for _, service := range s.services {
   210  		if service.Name() == name {
   211  			return service, nil
   212  		}
   213  	}
   214  	return nil, errors.Errorf("service %s not found", name)
   215  }