sigs.k8s.io/cluster-api-provider-azure@v1.14.3/azure/services/securitygroups/securitygroups.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 securitygroups 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 = "securitygroups" 31 32 // NSGScope defines the scope interface for a security groups service. 33 type NSGScope interface { 34 azure.Authorizer 35 azure.AsyncStatusUpdater 36 NSGSpecs() []azure.ResourceSpecGetter 37 IsVnetManaged() bool 38 UpdateAnnotationJSON(string, map[string]interface{}) error 39 } 40 41 // Service provides operations on Azure resources. 42 type Service struct { 43 Scope NSGScope 44 async.Reconciler 45 } 46 47 // New creates a new service. 48 func New(scope NSGScope) (*Service, error) { 49 client, err := newClient(scope, scope.DefaultedAzureCallTimeout()) 50 if err != nil { 51 return nil, err 52 } 53 return &Service{ 54 Scope: scope, 55 Reconciler: async.New[armnetwork.SecurityGroupsClientCreateOrUpdateResponse, 56 armnetwork.SecurityGroupsClientDeleteResponse](scope, client, client), 57 }, nil 58 } 59 60 // Name returns the service name. 61 func (s *Service) Name() string { 62 return serviceName 63 } 64 65 // Reconcile idempotently creates or updates a set of network security groups. 66 func (s *Service) Reconcile(ctx context.Context) error { 67 ctx, log, done := tele.StartSpanWithLogger(ctx, "securitygroups.Service.Reconcile") 68 defer done() 69 70 ctx, cancel := context.WithTimeout(ctx, s.Scope.DefaultedAzureServiceReconcileTimeout()) 71 defer cancel() 72 73 // Only create the NSGs if their lifecycle is managed by this controller. 74 if managed, err := s.IsManaged(ctx); err == nil && !managed { 75 log.V(4).Info("Skipping network security groups reconcile in custom VNet mode") 76 return nil 77 } else if err != nil { 78 return errors.Wrap(err, "failed to check if security groups are managed") 79 } 80 81 specs := s.Scope.NSGSpecs() 82 if len(specs) == 0 { 83 return nil 84 } 85 86 var resErr error 87 88 newAnnotation := make(map[string]interface{}) 89 90 // We go through the list of security groups to reconcile each one, independently of the result of the previous one. 91 // If multiple errors occur, we return the most pressing one. 92 // 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) 93 for _, resourceSpec := range specs { 94 nsgSpec := resourceSpec.(*NSGSpec) 95 currentAnnotation := make(map[string]string) 96 97 if _, err := s.CreateOrUpdateResource(ctx, nsgSpec, serviceName); err != nil { 98 if !azure.IsOperationNotDoneError(err) || resErr == nil { 99 resErr = err 100 } 101 } 102 103 for _, rule := range nsgSpec.SecurityRules { 104 currentAnnotation[rule.Name] = rule.Description 105 } 106 107 if len(currentAnnotation) > 0 { 108 newAnnotation[nsgSpec.Name] = currentAnnotation 109 } 110 } 111 112 if err := s.Scope.UpdateAnnotationJSON(azure.SecurityRuleLastAppliedAnnotation, newAnnotation); err != nil { 113 return err 114 } 115 116 s.Scope.UpdatePutStatus(infrav1.SecurityGroupsReadyCondition, serviceName, resErr) 117 return resErr 118 } 119 120 // Delete deletes network security groups. 121 func (s *Service) Delete(ctx context.Context) error { 122 ctx, log, done := tele.StartSpanWithLogger(ctx, "securitygroups.Service.Delete") 123 defer done() 124 125 ctx, cancel := context.WithTimeout(ctx, s.Scope.DefaultedAzureServiceReconcileTimeout()) 126 defer cancel() 127 128 // Only delete the security groups if their lifecycle is managed by this controller. 129 if managed, err := s.IsManaged(ctx); err == nil && !managed { 130 log.V(4).Info("Skipping network security groups delete in custom VNet mode") 131 return nil 132 } else if err != nil { 133 return errors.Wrap(err, "failed to check if security groups are managed") 134 } 135 136 specs := s.Scope.NSGSpecs() 137 if len(specs) == 0 { 138 return nil 139 } 140 141 var result error 142 143 // We go through the list of security groups to delete each one, independently of the result of the previous one. 144 // If multiple errors occur, we return the most pressing one. 145 // 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) 146 for _, nsgSpec := range specs { 147 if err := s.DeleteResource(ctx, nsgSpec, serviceName); err != nil { 148 if !azure.IsOperationNotDoneError(err) || result == nil { 149 result = err 150 } 151 } 152 } 153 154 s.Scope.UpdateDeleteStatus(infrav1.SecurityGroupsReadyCondition, serviceName, result) 155 return result 156 } 157 158 // IsManaged returns true if the security groups' lifecycles are managed. 159 func (s *Service) IsManaged(ctx context.Context) (bool, error) { 160 _, _, done := tele.StartSpanWithLogger(ctx, "securitygroups.Service.IsManaged") 161 defer done() 162 163 return s.Scope.IsVnetManaged(), nil 164 }