sigs.k8s.io/cluster-api-provider-azure@v1.14.3/azure/services/groups/groups.go (about)

     1  /*
     2  Copyright 2023 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 groups
    18  
    19  import (
    20  	"context"
    21  
    22  	asoresourcesv1 "github.com/Azure/azure-service-operator/v2/api/resources/v1api20200601"
    23  	asoannotations "github.com/Azure/azure-service-operator/v2/pkg/common/annotations"
    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/aso"
    27  	"sigs.k8s.io/cluster-api-provider-azure/util/slice"
    28  	"sigs.k8s.io/controller-runtime/pkg/client"
    29  )
    30  
    31  // ServiceName is the name of this service.
    32  const ServiceName = "group"
    33  
    34  // Service provides operations on Azure resources.
    35  type Service struct {
    36  	Scope GroupScope
    37  	*aso.Service[*asoresourcesv1.ResourceGroup, GroupScope]
    38  }
    39  
    40  // GroupScope defines the scope interface for a group service.
    41  type GroupScope interface {
    42  	aso.Scope
    43  	GroupSpecs() []azure.ASOResourceSpecGetter[*asoresourcesv1.ResourceGroup]
    44  }
    45  
    46  // New creates a new service.
    47  func New(scope GroupScope) *Service {
    48  	svc := aso.NewService[*asoresourcesv1.ResourceGroup](ServiceName, scope)
    49  	svc.ListFunc = list
    50  	svc.Specs = scope.GroupSpecs()
    51  	svc.ConditionType = infrav1.ResourceGroupReadyCondition
    52  	return &Service{
    53  		Scope:   scope,
    54  		Service: svc,
    55  	}
    56  }
    57  
    58  // IsManaged returns true if all resource groups are
    59  // managed and reconciled by ASO, meaning that we can rely on a single resource
    60  // group delete operation as opposed to deleting every individual resource.
    61  func (s *Service) IsManaged(ctx context.Context) (bool, error) {
    62  	// Unless all resource groups are managed by CAPZ and reconciled by ASO, resources need to be deleted individually.
    63  	for _, spec := range s.Specs {
    64  		managed, err := aso.IsManaged(ctx, s.Scope.GetClient(), spec.ResourceRef(), s.Scope.ASOOwner())
    65  		if err != nil || !managed {
    66  			return managed, err
    67  		}
    68  
    69  		// For ASO, "managed" only tells us that we're allowed to delete the ASO
    70  		// resource. We also need to check that deleting the ASO resource will really
    71  		// delete the underlying resource group by checking the ASO reconcile-policy.
    72  		group := spec.ResourceRef()
    73  		groupName := group.Name
    74  		groupNamespace := s.Scope.ASOOwner().GetNamespace()
    75  		err = s.Scope.GetClient().Get(ctx, client.ObjectKey{Namespace: groupNamespace, Name: groupName}, group)
    76  		if err != nil || group.GetAnnotations()[asoannotations.ReconcilePolicy] != string(asoannotations.ReconcilePolicyManage) {
    77  			return false, err
    78  		}
    79  	}
    80  	return true, nil
    81  }
    82  
    83  func list(ctx context.Context, client client.Client, opts ...client.ListOption) ([]*asoresourcesv1.ResourceGroup, error) {
    84  	list := &asoresourcesv1.ResourceGroupList{}
    85  	err := client.List(ctx, list, opts...)
    86  	return slice.ToPtrs(list.Items), err
    87  }