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 }