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 }