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  }