sigs.k8s.io/cluster-api-provider-azure@v1.14.3/azure/services/privatedns/privatedns.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 privatedns
    18  
    19  import (
    20  	"context"
    21  
    22  	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns"
    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/converters"
    27  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/async"
    28  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/tags"
    29  	"sigs.k8s.io/cluster-api-provider-azure/util/tele"
    30  )
    31  
    32  const serviceName = "privatedns"
    33  
    34  // Scope defines the scope interface for a private dns service.
    35  type Scope interface {
    36  	azure.ClusterDescriber
    37  	azure.Authorizer
    38  	azure.AsyncStatusUpdater
    39  	PrivateDNSSpec() (zoneSpec azure.ResourceSpecGetter, linksSpec, recordsSpec []azure.ResourceSpecGetter)
    40  }
    41  
    42  // Service provides operations on Azure resources.
    43  type Service struct {
    44  	Scope              Scope
    45  	TagsGetter         async.TagsGetter
    46  	zoneReconciler     async.Reconciler
    47  	vnetLinkReconciler async.Reconciler
    48  	recordReconciler   async.Reconciler
    49  }
    50  
    51  // New creates a new private dns service.
    52  func New(scope Scope) (*Service, error) {
    53  	zoneClient, err := newPrivateZonesClient(scope, scope.DefaultedAzureCallTimeout())
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  	vnetLinkClient, err := newVirtualNetworkLinksClient(scope, scope.DefaultedAzureCallTimeout())
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	recordSetsClient, err := newRecordSetsClient(scope)
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  	tagsClient, err := tags.NewClient(scope)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  	return &Service{
    70  		Scope:      scope,
    71  		TagsGetter: tagsClient,
    72  		zoneReconciler: async.New[armprivatedns.PrivateZonesClientCreateOrUpdateResponse,
    73  			armprivatedns.PrivateZonesClientDeleteResponse](scope, zoneClient, zoneClient),
    74  		vnetLinkReconciler: async.New[armprivatedns.VirtualNetworkLinksClientCreateOrUpdateResponse,
    75  			armprivatedns.VirtualNetworkLinksClientDeleteResponse](scope, vnetLinkClient, vnetLinkClient),
    76  		recordReconciler: async.New[armprivatedns.RecordSetsClientCreateOrUpdateResponse,
    77  			armprivatedns.RecordSetsClientDeleteResponse](scope, recordSetsClient, recordSetsClient),
    78  	}, nil
    79  }
    80  
    81  // Name returns the service name.
    82  func (s *Service) Name() string {
    83  	return serviceName
    84  }
    85  
    86  // Reconcile creates or updates the private zone, links it to the vnet, and creates DNS records.
    87  func (s *Service) Reconcile(ctx context.Context) error {
    88  	ctx, _, done := tele.StartSpanWithLogger(ctx, "privatedns.Service.Reconcile")
    89  	defer done()
    90  
    91  	ctx, cancel := context.WithTimeout(ctx, s.Scope.DefaultedAzureServiceReconcileTimeout())
    92  	defer cancel()
    93  
    94  	zoneSpec, links, records := s.Scope.PrivateDNSSpec()
    95  	if zoneSpec == nil {
    96  		return nil
    97  	}
    98  
    99  	managed, err := s.reconcileZone(ctx, zoneSpec)
   100  	if managed {
   101  		s.Scope.UpdatePutStatus(infrav1.PrivateDNSZoneReadyCondition, serviceName, err)
   102  	}
   103  	if err != nil {
   104  		return err
   105  	}
   106  
   107  	managed, err = s.reconcileLinks(ctx, links)
   108  	if managed {
   109  		s.Scope.UpdatePutStatus(infrav1.PrivateDNSLinkReadyCondition, serviceName, err)
   110  	}
   111  	if err != nil {
   112  		return err
   113  	}
   114  
   115  	err = s.reconcileRecords(ctx, records)
   116  	s.Scope.UpdatePutStatus(infrav1.PrivateDNSRecordReadyCondition, serviceName, err)
   117  	return err
   118  }
   119  
   120  // Delete deletes the private zone and vnet links.
   121  func (s *Service) Delete(ctx context.Context) error {
   122  	ctx, _, done := tele.StartSpanWithLogger(ctx, "privatedns.Service.Delete")
   123  	defer done()
   124  
   125  	ctx, cancel := context.WithTimeout(ctx, s.Scope.DefaultedAzureServiceReconcileTimeout())
   126  	defer cancel()
   127  
   128  	zoneSpec, links, _ := s.Scope.PrivateDNSSpec()
   129  	if zoneSpec == nil {
   130  		return nil
   131  	}
   132  
   133  	managed, err := s.deleteLinks(ctx, links)
   134  	if managed {
   135  		s.Scope.UpdateDeleteStatus(infrav1.PrivateDNSLinkReadyCondition, serviceName, err)
   136  	}
   137  	if err != nil {
   138  		return err
   139  	}
   140  
   141  	managed, err = s.deleteZone(ctx, zoneSpec)
   142  	if managed {
   143  		s.Scope.UpdateDeleteStatus(infrav1.PrivateDNSZoneReadyCondition, serviceName, err)
   144  		s.Scope.UpdateDeleteStatus(infrav1.PrivateDNSRecordReadyCondition, serviceName, err)
   145  	}
   146  
   147  	return err
   148  }
   149  
   150  // isVnetLinkManaged returns true if the vnet link has an owned tag with the cluster name as value,
   151  // meaning that the vnet link lifecycle is managed.
   152  func (s *Service) isVnetLinkManaged(ctx context.Context, spec azure.ResourceSpecGetter) (bool, error) {
   153  	scope := azure.VirtualNetworkLinkID(s.Scope.SubscriptionID(), spec.ResourceGroupName(), spec.OwnerResourceName(), spec.ResourceName())
   154  	result, err := s.TagsGetter.GetAtScope(ctx, scope)
   155  	if err != nil {
   156  		return false, err
   157  	}
   158  
   159  	tagsMap := make(map[string]*string)
   160  	if result.Properties != nil && result.Properties.Tags != nil {
   161  		tagsMap = result.Properties.Tags
   162  	}
   163  
   164  	tags := converters.MapToTags(tagsMap)
   165  	return tags.HasOwned(s.Scope.ClusterName()), nil
   166  }
   167  
   168  // IsManaged returns true if the private DNS has an owned tag with the cluster name as value,
   169  // meaning that the DNS lifecycle is managed.
   170  func (s *Service) IsManaged(ctx context.Context) (bool, error) {
   171  	zoneSpec, _, _ := s.Scope.PrivateDNSSpec()
   172  	if zoneSpec == nil {
   173  		return false, errors.Errorf("no private dns zone spec available")
   174  	}
   175  
   176  	scope := azure.PrivateDNSZoneID(s.Scope.SubscriptionID(), zoneSpec.ResourceGroupName(), zoneSpec.ResourceName())
   177  	result, err := s.TagsGetter.GetAtScope(ctx, scope)
   178  	if err != nil {
   179  		return false, err
   180  	}
   181  
   182  	tagsMap := make(map[string]*string)
   183  	if result.Properties != nil && result.Properties.Tags != nil {
   184  		tagsMap = result.Properties.Tags
   185  	}
   186  
   187  	tags := converters.MapToTags(tagsMap)
   188  	return tags.HasOwned(s.Scope.ClusterName()), nil
   189  }