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 }