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

     1  /*
     2  Copyright 2020 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  	"net/http"
    22  	"testing"
    23  
    24  	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
    25  	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
    26  	. "github.com/onsi/gomega"
    27  	"github.com/pkg/errors"
    28  	"go.uber.org/mock/gomock"
    29  	"k8s.io/utils/ptr"
    30  	infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
    31  	"sigs.k8s.io/cluster-api-provider-azure/azure"
    32  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/async/mock_async"
    33  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/privatedns/mock_privatedns"
    34  	gomockinternal "sigs.k8s.io/cluster-api-provider-azure/internal/test/matchers/gomock"
    35  	"sigs.k8s.io/cluster-api-provider-azure/util/reconciler"
    36  )
    37  
    38  const (
    39  	zoneName          = "my-zone"
    40  	resourceGroup     = "my-rg"
    41  	vnetName          = "my-vnet"
    42  	vnetResourceGroup = "my-vnet-rg"
    43  	linkName1         = "my-link-1"
    44  	linkName2         = "my-link-2"
    45  	clusterName       = "my-cluster"
    46  	subscriptionID    = "my-subscription-id"
    47  )
    48  
    49  var (
    50  	fakeZone = ZoneSpec{
    51  		Name:           zoneName,
    52  		ResourceGroup:  resourceGroup,
    53  		ClusterName:    clusterName,
    54  		AdditionalTags: nil,
    55  	}
    56  
    57  	fakeLink1 = LinkSpec{
    58  		Name:              linkName1,
    59  		ZoneName:          zoneName,
    60  		SubscriptionID:    subscriptionID,
    61  		VNetResourceGroup: vnetResourceGroup,
    62  		VNetName:          vnetName,
    63  		ResourceGroup:     resourceGroup,
    64  		ClusterName:       clusterName,
    65  		AdditionalTags:    nil,
    66  	}
    67  
    68  	fakeLink2 = LinkSpec{
    69  		Name:              linkName2,
    70  		ZoneName:          zoneName,
    71  		SubscriptionID:    subscriptionID,
    72  		VNetResourceGroup: vnetResourceGroup,
    73  		VNetName:          vnetName,
    74  		ResourceGroup:     resourceGroup,
    75  		ClusterName:       clusterName,
    76  		AdditionalTags:    nil,
    77  	}
    78  
    79  	fakeRecord1 = RecordSpec{
    80  		Record:        infrav1.AddressRecord{Hostname: "my-host", IP: "10.0.0.8"},
    81  		ZoneName:      zoneName,
    82  		ResourceGroup: resourceGroup,
    83  	}
    84  
    85  	managedTags = armresources.TagsResource{
    86  		Properties: &armresources.Tags{
    87  			Tags: map[string]*string{
    88  				"foo": ptr.To("bar"),
    89  				"sigs.k8s.io_cluster-api-provider-azure_cluster_" + clusterName: ptr.To("owned"),
    90  			},
    91  		},
    92  	}
    93  
    94  	notDoneError  = azure.NewOperationNotDoneError(&infrav1.Future{Type: "resourceType", ResourceGroup: resourceGroup, Name: "resourceName"})
    95  	errFake       = errors.New("this is an error")
    96  	notFoundError = &azcore.ResponseError{StatusCode: http.StatusNotFound}
    97  )
    98  
    99  func TestReconcilePrivateDNS(t *testing.T) {
   100  	testcases := []struct {
   101  		name          string
   102  		expectedError string
   103  		expect        func(s *mock_privatedns.MockScopeMockRecorder, zoneReconiler, linksReconciler, recordsReconciler *mock_async.MockReconcilerMockRecorder,
   104  			tagsGetter *mock_async.MockTagsGetterMockRecorder)
   105  	}{
   106  		{
   107  			name:          "no private dns",
   108  			expectedError: "",
   109  			expect: func(s *mock_privatedns.MockScopeMockRecorder, z, l, r *mock_async.MockReconcilerMockRecorder, tg *mock_async.MockTagsGetterMockRecorder) {
   110  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   111  				s.PrivateDNSSpec().Return(nil, nil, nil)
   112  			},
   113  		},
   114  		{
   115  			name:          "create private dns with multiple links successfully",
   116  			expectedError: "",
   117  			expect: func(s *mock_privatedns.MockScopeMockRecorder, z, l, r *mock_async.MockReconcilerMockRecorder, tg *mock_async.MockTagsGetterMockRecorder) {
   118  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   119  				s.PrivateDNSSpec().Return(fakeZone, []azure.ResourceSpecGetter{fakeLink1, fakeLink2}, []azure.ResourceSpecGetter{fakeRecord1}).Times(2)
   120  
   121  				s.SubscriptionID().Return("123")
   122  				tg.GetAtScope(gomockinternal.AContext(), azure.PrivateDNSZoneID("123", fakeZone.ResourceGroupName(), fakeZone.ResourceName())).Return(armresources.TagsResource{}, notFoundError)
   123  
   124  				s.SubscriptionID().Return("123")
   125  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink1.ResourceGroupName(), fakeLink1.OwnerResourceName(), fakeLink1.ResourceName())).Return(armresources.TagsResource{}, notFoundError)
   126  
   127  				s.SubscriptionID().Return("123")
   128  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink2.ResourceGroupName(), fakeLink2.OwnerResourceName(), fakeLink2.ResourceName())).Return(armresources.TagsResource{}, notFoundError)
   129  
   130  				z.CreateOrUpdateResource(gomockinternal.AContext(), fakeZone, serviceName).Return(nil, nil)
   131  				l.CreateOrUpdateResource(gomockinternal.AContext(), fakeLink1, serviceName).Return(nil, nil)
   132  				l.CreateOrUpdateResource(gomockinternal.AContext(), fakeLink2, serviceName).Return(nil, nil)
   133  				r.CreateOrUpdateResource(gomockinternal.AContext(), fakeRecord1, serviceName).Return(nil, nil)
   134  				s.UpdatePutStatus(infrav1.PrivateDNSZoneReadyCondition, serviceName, nil)
   135  				s.UpdatePutStatus(infrav1.PrivateDNSLinkReadyCondition, serviceName, nil)
   136  				s.UpdatePutStatus(infrav1.PrivateDNSRecordReadyCondition, serviceName, nil)
   137  			},
   138  		},
   139  		{
   140  			name:          "zone creation in progress",
   141  			expectedError: "operation type resourceType on Azure resource my-rg/resourceName is not done",
   142  			expect: func(s *mock_privatedns.MockScopeMockRecorder, z, l, r *mock_async.MockReconcilerMockRecorder, tg *mock_async.MockTagsGetterMockRecorder) {
   143  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   144  				s.PrivateDNSSpec().Return(fakeZone, []azure.ResourceSpecGetter{fakeLink1, fakeLink2}, []azure.ResourceSpecGetter{fakeRecord1}).Times(2)
   145  
   146  				s.SubscriptionID().Return("123")
   147  				tg.GetAtScope(gomockinternal.AContext(), azure.PrivateDNSZoneID("123", fakeZone.ResourceGroupName(), fakeZone.ResourceName())).Return(armresources.TagsResource{}, notFoundError)
   148  
   149  				z.CreateOrUpdateResource(gomockinternal.AContext(), fakeZone, serviceName).Return(nil, notDoneError)
   150  				s.UpdatePutStatus(infrav1.PrivateDNSZoneReadyCondition, serviceName, notDoneError)
   151  			},
   152  		},
   153  		{
   154  			name:          "zone creation fails",
   155  			expectedError: "this is an error",
   156  			expect: func(s *mock_privatedns.MockScopeMockRecorder, z, l, r *mock_async.MockReconcilerMockRecorder, tg *mock_async.MockTagsGetterMockRecorder) {
   157  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   158  				s.PrivateDNSSpec().Return(fakeZone, []azure.ResourceSpecGetter{fakeLink1, fakeLink2}, []azure.ResourceSpecGetter{fakeRecord1}).Times(2)
   159  
   160  				s.SubscriptionID().Return("123")
   161  				tg.GetAtScope(gomockinternal.AContext(), azure.PrivateDNSZoneID("123", fakeZone.ResourceGroupName(), fakeZone.ResourceName())).Return(armresources.TagsResource{}, notFoundError)
   162  
   163  				z.CreateOrUpdateResource(gomockinternal.AContext(), fakeZone, serviceName).Return(nil, errFake)
   164  				s.UpdatePutStatus(infrav1.PrivateDNSZoneReadyCondition, serviceName, errFake)
   165  			},
   166  		},
   167  		{
   168  			name: "unmanaged zone does not update ready condition",
   169  			expect: func(s *mock_privatedns.MockScopeMockRecorder, z, l, r *mock_async.MockReconcilerMockRecorder, tg *mock_async.MockTagsGetterMockRecorder) {
   170  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   171  				s.PrivateDNSSpec().Return(fakeZone, []azure.ResourceSpecGetter{fakeLink1, fakeLink2}, []azure.ResourceSpecGetter{fakeRecord1}).Times(2)
   172  
   173  				s.SubscriptionID().Return("123")
   174  				tg.GetAtScope(gomockinternal.AContext(), azure.PrivateDNSZoneID("123", fakeZone.ResourceGroupName(), fakeZone.ResourceName())).Return(armresources.TagsResource{}, nil)
   175  				s.ClusterName().Return(clusterName)
   176  
   177  				s.SubscriptionID().Return("123")
   178  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink1.ResourceGroupName(), fakeLink1.OwnerResourceName(), fakeLink1.ResourceName())).Return(armresources.TagsResource{}, notFoundError)
   179  
   180  				s.SubscriptionID().Return("123")
   181  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink2.ResourceGroupName(), fakeLink2.OwnerResourceName(), fakeLink2.ResourceName())).Return(armresources.TagsResource{}, notFoundError)
   182  
   183  				l.CreateOrUpdateResource(gomockinternal.AContext(), fakeLink1, serviceName).Return(nil, nil)
   184  				l.CreateOrUpdateResource(gomockinternal.AContext(), fakeLink2, serviceName).Return(nil, nil)
   185  				r.CreateOrUpdateResource(gomockinternal.AContext(), fakeRecord1, serviceName).Return(nil, nil)
   186  				s.UpdatePutStatus(infrav1.PrivateDNSLinkReadyCondition, serviceName, nil)
   187  				s.UpdatePutStatus(infrav1.PrivateDNSRecordReadyCondition, serviceName, nil)
   188  			},
   189  		},
   190  		{
   191  			name:          "link 1 creation fails but still proceeds to link 2, and returns the error",
   192  			expectedError: "this is an error",
   193  			expect: func(s *mock_privatedns.MockScopeMockRecorder, z, l, r *mock_async.MockReconcilerMockRecorder, tg *mock_async.MockTagsGetterMockRecorder) {
   194  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   195  				s.PrivateDNSSpec().Return(fakeZone, []azure.ResourceSpecGetter{fakeLink1, fakeLink2}, []azure.ResourceSpecGetter{fakeRecord1}).Times(2)
   196  
   197  				s.SubscriptionID().Return("123")
   198  				tg.GetAtScope(gomockinternal.AContext(), azure.PrivateDNSZoneID("123", fakeZone.ResourceGroupName(), fakeZone.ResourceName())).Return(armresources.TagsResource{}, notFoundError)
   199  
   200  				s.SubscriptionID().Return("123")
   201  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink1.ResourceGroupName(), fakeLink1.OwnerResourceName(), fakeLink1.ResourceName())).Return(armresources.TagsResource{}, notFoundError)
   202  
   203  				s.SubscriptionID().Return("123")
   204  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink2.ResourceGroupName(), fakeLink2.OwnerResourceName(), fakeLink2.ResourceName())).Return(armresources.TagsResource{}, notFoundError)
   205  
   206  				z.CreateOrUpdateResource(gomockinternal.AContext(), fakeZone, serviceName).Return(nil, nil)
   207  				l.CreateOrUpdateResource(gomockinternal.AContext(), fakeLink1, serviceName).Return(nil, errFake)
   208  				l.CreateOrUpdateResource(gomockinternal.AContext(), fakeLink2, serviceName).Return(nil, nil)
   209  				s.UpdatePutStatus(infrav1.PrivateDNSZoneReadyCondition, serviceName, nil)
   210  				s.UpdatePutStatus(infrav1.PrivateDNSLinkReadyCondition, serviceName, errFake)
   211  			},
   212  		},
   213  		{
   214  			name:          "link 2 creation fails",
   215  			expectedError: "this is an error",
   216  			expect: func(s *mock_privatedns.MockScopeMockRecorder, z, l, r *mock_async.MockReconcilerMockRecorder, tg *mock_async.MockTagsGetterMockRecorder) {
   217  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   218  				s.PrivateDNSSpec().Return(fakeZone, []azure.ResourceSpecGetter{fakeLink1, fakeLink2}, []azure.ResourceSpecGetter{fakeRecord1}).Times(2)
   219  
   220  				s.SubscriptionID().Return("123")
   221  				tg.GetAtScope(gomockinternal.AContext(), azure.PrivateDNSZoneID("123", fakeZone.ResourceGroupName(), fakeZone.ResourceName())).Return(armresources.TagsResource{}, notFoundError)
   222  
   223  				s.SubscriptionID().Return("123")
   224  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink1.ResourceGroupName(), fakeLink1.OwnerResourceName(), fakeLink1.ResourceName())).Return(armresources.TagsResource{}, notFoundError)
   225  
   226  				s.SubscriptionID().Return("123")
   227  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink2.ResourceGroupName(), fakeLink2.OwnerResourceName(), fakeLink2.ResourceName())).Return(armresources.TagsResource{}, notFoundError)
   228  
   229  				z.CreateOrUpdateResource(gomockinternal.AContext(), fakeZone, serviceName).Return(nil, nil)
   230  				l.CreateOrUpdateResource(gomockinternal.AContext(), fakeLink1, serviceName).Return(nil, nil)
   231  				l.CreateOrUpdateResource(gomockinternal.AContext(), fakeLink2, serviceName).Return(nil, errFake)
   232  				s.UpdatePutStatus(infrav1.PrivateDNSZoneReadyCondition, serviceName, nil)
   233  				s.UpdatePutStatus(infrav1.PrivateDNSLinkReadyCondition, serviceName, errFake)
   234  			},
   235  		},
   236  		{
   237  			name:          "link 1 is long running, link 2 fails, it returns the failure of link2",
   238  			expectedError: "this is an error",
   239  			expect: func(s *mock_privatedns.MockScopeMockRecorder, z, l, r *mock_async.MockReconcilerMockRecorder, tg *mock_async.MockTagsGetterMockRecorder) {
   240  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   241  				s.PrivateDNSSpec().Return(fakeZone, []azure.ResourceSpecGetter{fakeLink1, fakeLink2}, []azure.ResourceSpecGetter{fakeRecord1}).Times(2)
   242  
   243  				s.SubscriptionID().Return("123")
   244  				tg.GetAtScope(gomockinternal.AContext(), azure.PrivateDNSZoneID("123", fakeZone.ResourceGroupName(), fakeZone.ResourceName())).Return(armresources.TagsResource{}, notFoundError)
   245  
   246  				s.SubscriptionID().Return("123")
   247  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink1.ResourceGroupName(), fakeLink1.OwnerResourceName(), fakeLink1.ResourceName())).Return(armresources.TagsResource{}, notFoundError)
   248  
   249  				s.SubscriptionID().Return("123")
   250  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink2.ResourceGroupName(), fakeLink2.OwnerResourceName(), fakeLink2.ResourceName())).Return(armresources.TagsResource{}, notFoundError)
   251  
   252  				z.CreateOrUpdateResource(gomockinternal.AContext(), fakeZone, serviceName).Return(nil, nil)
   253  				l.CreateOrUpdateResource(gomockinternal.AContext(), fakeLink1, serviceName).Return(nil, notDoneError)
   254  				l.CreateOrUpdateResource(gomockinternal.AContext(), fakeLink2, serviceName).Return(nil, errFake)
   255  				s.UpdatePutStatus(infrav1.PrivateDNSZoneReadyCondition, serviceName, nil)
   256  				s.UpdatePutStatus(infrav1.PrivateDNSLinkReadyCondition, serviceName, errFake)
   257  			},
   258  		},
   259  		{
   260  			name: "unmanaged link does not update ready condition",
   261  			expect: func(s *mock_privatedns.MockScopeMockRecorder, z, l, r *mock_async.MockReconcilerMockRecorder, tg *mock_async.MockTagsGetterMockRecorder) {
   262  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   263  				s.PrivateDNSSpec().Return(fakeZone, []azure.ResourceSpecGetter{fakeLink1, fakeLink2}, []azure.ResourceSpecGetter{fakeRecord1}).Times(2)
   264  
   265  				s.SubscriptionID().Return("123")
   266  				tg.GetAtScope(gomockinternal.AContext(), azure.PrivateDNSZoneID("123", fakeZone.ResourceGroupName(), fakeZone.ResourceName())).Return(armresources.TagsResource{}, notFoundError)
   267  
   268  				s.SubscriptionID().Return("123")
   269  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink1.ResourceGroupName(), fakeLink1.OwnerResourceName(), fakeLink1.ResourceName())).Return(armresources.TagsResource{}, nil)
   270  				s.ClusterName().Return(clusterName)
   271  
   272  				s.SubscriptionID().Return("123")
   273  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink2.ResourceGroupName(), fakeLink2.OwnerResourceName(), fakeLink2.ResourceName())).Return(armresources.TagsResource{}, nil)
   274  				s.ClusterName().Return(clusterName)
   275  
   276  				z.CreateOrUpdateResource(gomockinternal.AContext(), fakeZone, serviceName).Return(nil, nil)
   277  				r.CreateOrUpdateResource(gomockinternal.AContext(), fakeRecord1, serviceName).Return(nil, nil)
   278  				s.UpdatePutStatus(infrav1.PrivateDNSZoneReadyCondition, serviceName, nil)
   279  				s.UpdatePutStatus(infrav1.PrivateDNSRecordReadyCondition, serviceName, nil)
   280  			},
   281  		},
   282  		{
   283  			name: "vnet link is considered managed if at least one of the links is managed",
   284  			expect: func(s *mock_privatedns.MockScopeMockRecorder, z, l, r *mock_async.MockReconcilerMockRecorder, tg *mock_async.MockTagsGetterMockRecorder) {
   285  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   286  				s.PrivateDNSSpec().Return(fakeZone, []azure.ResourceSpecGetter{fakeLink1, fakeLink2}, []azure.ResourceSpecGetter{fakeRecord1}).Times(2)
   287  
   288  				s.SubscriptionID().Return("123")
   289  				tg.GetAtScope(gomockinternal.AContext(), azure.PrivateDNSZoneID("123", fakeZone.ResourceGroupName(), fakeZone.ResourceName())).Return(armresources.TagsResource{}, notFoundError)
   290  
   291  				s.SubscriptionID().Return("123")
   292  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink1.ResourceGroupName(), fakeLink1.OwnerResourceName(), fakeLink1.ResourceName())).Return(armresources.TagsResource{}, nil)
   293  				s.ClusterName().Return(clusterName)
   294  
   295  				s.SubscriptionID().Return("123")
   296  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink2.ResourceGroupName(), fakeLink2.OwnerResourceName(), fakeLink2.ResourceName())).Return(armresources.TagsResource{}, notFoundError)
   297  
   298  				z.CreateOrUpdateResource(gomockinternal.AContext(), fakeZone, serviceName).Return(nil, nil)
   299  				l.CreateOrUpdateResource(gomockinternal.AContext(), fakeLink2, serviceName).Return(nil, nil)
   300  				r.CreateOrUpdateResource(gomockinternal.AContext(), fakeRecord1, serviceName).Return(nil, nil)
   301  				s.UpdatePutStatus(infrav1.PrivateDNSZoneReadyCondition, serviceName, nil)
   302  				s.UpdatePutStatus(infrav1.PrivateDNSLinkReadyCondition, serviceName, nil)
   303  				s.UpdatePutStatus(infrav1.PrivateDNSRecordReadyCondition, serviceName, nil)
   304  			},
   305  		},
   306  		{
   307  			name:          "record creation fails",
   308  			expectedError: "this is an error",
   309  			expect: func(s *mock_privatedns.MockScopeMockRecorder, z, l, r *mock_async.MockReconcilerMockRecorder, tg *mock_async.MockTagsGetterMockRecorder) {
   310  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   311  				s.PrivateDNSSpec().Return(fakeZone, []azure.ResourceSpecGetter{fakeLink1, fakeLink2}, []azure.ResourceSpecGetter{fakeRecord1}).Times(2)
   312  
   313  				s.SubscriptionID().Return("123")
   314  				tg.GetAtScope(gomockinternal.AContext(), azure.PrivateDNSZoneID("123", fakeZone.ResourceGroupName(), fakeZone.ResourceName())).Return(armresources.TagsResource{}, notFoundError)
   315  
   316  				s.SubscriptionID().Return("123")
   317  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink1.ResourceGroupName(), fakeLink1.OwnerResourceName(), fakeLink1.ResourceName())).Return(armresources.TagsResource{}, notFoundError)
   318  
   319  				s.SubscriptionID().Return("123")
   320  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink2.ResourceGroupName(), fakeLink2.OwnerResourceName(), fakeLink2.ResourceName())).Return(armresources.TagsResource{}, notFoundError)
   321  
   322  				z.CreateOrUpdateResource(gomockinternal.AContext(), fakeZone, serviceName).Return(nil, nil)
   323  				l.CreateOrUpdateResource(gomockinternal.AContext(), fakeLink1, serviceName).Return(nil, nil)
   324  				l.CreateOrUpdateResource(gomockinternal.AContext(), fakeLink2, serviceName).Return(nil, nil)
   325  				r.CreateOrUpdateResource(gomockinternal.AContext(), fakeRecord1, serviceName).Return(nil, errFake)
   326  				s.UpdatePutStatus(infrav1.PrivateDNSZoneReadyCondition, serviceName, nil)
   327  				s.UpdatePutStatus(infrav1.PrivateDNSLinkReadyCondition, serviceName, nil)
   328  				s.UpdatePutStatus(infrav1.PrivateDNSRecordReadyCondition, serviceName, errFake)
   329  			},
   330  		},
   331  	}
   332  
   333  	for _, tc := range testcases {
   334  		tc := tc
   335  		t.Run(tc.name, func(t *testing.T) {
   336  			g := NewWithT(t)
   337  
   338  			t.Parallel()
   339  			mockCtrl := gomock.NewController(t)
   340  			defer mockCtrl.Finish()
   341  			scopeMock := mock_privatedns.NewMockScope(mockCtrl)
   342  			zoneReconcilerMock := mock_async.NewMockReconciler(mockCtrl)
   343  			vnetLinkReconcilerMock := mock_async.NewMockReconciler(mockCtrl)
   344  			recordReconcilerMock := mock_async.NewMockReconciler(mockCtrl)
   345  			tagsGetterMock := mock_async.NewMockTagsGetter(mockCtrl)
   346  
   347  			tc.expect(scopeMock.EXPECT(), zoneReconcilerMock.EXPECT(), vnetLinkReconcilerMock.EXPECT(), recordReconcilerMock.EXPECT(), tagsGetterMock.EXPECT())
   348  
   349  			s := &Service{
   350  				Scope:              scopeMock,
   351  				zoneReconciler:     zoneReconcilerMock,
   352  				vnetLinkReconciler: vnetLinkReconcilerMock,
   353  				recordReconciler:   recordReconcilerMock,
   354  				TagsGetter:         tagsGetterMock,
   355  			}
   356  
   357  			err := s.Reconcile(context.TODO())
   358  			if tc.expectedError != "" {
   359  				g.Expect(err).To(HaveOccurred())
   360  				g.Expect(err).To(MatchError(tc.expectedError))
   361  			} else {
   362  				g.Expect(err).NotTo(HaveOccurred())
   363  			}
   364  		})
   365  	}
   366  }
   367  
   368  func TestDeletePrivateDNS(t *testing.T) {
   369  	testcases := []struct {
   370  		name          string
   371  		expectedError string
   372  		expect        func(s *mock_privatedns.MockScopeMockRecorder, linkReconciler, zoneReconciler *mock_async.MockReconcilerMockRecorder, tagsGetter *mock_async.MockTagsGetterMockRecorder)
   373  	}{
   374  		{
   375  			name:          "no private dns",
   376  			expectedError: "",
   377  			expect: func(s *mock_privatedns.MockScopeMockRecorder, lr, zr *mock_async.MockReconcilerMockRecorder, tg *mock_async.MockTagsGetterMockRecorder) {
   378  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   379  				s.PrivateDNSSpec().Return(nil, nil, nil)
   380  			},
   381  		},
   382  		{
   383  			name:          "dns and links deletion succeeds",
   384  			expectedError: "",
   385  			expect: func(s *mock_privatedns.MockScopeMockRecorder, lr, zr *mock_async.MockReconcilerMockRecorder, tg *mock_async.MockTagsGetterMockRecorder) {
   386  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   387  				s.PrivateDNSSpec().Return(fakeZone, []azure.ResourceSpecGetter{fakeLink1, fakeLink2}, []azure.ResourceSpecGetter{fakeRecord1}).Times(2)
   388  
   389  				s.SubscriptionID().Return("123")
   390  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink1.ResourceGroupName(), fakeLink1.OwnerResourceName(), fakeLink1.ResourceName())).Return(managedTags, nil)
   391  				s.ClusterName().Return(clusterName)
   392  
   393  				lr.DeleteResource(gomockinternal.AContext(), fakeLink1, serviceName).Return(nil)
   394  
   395  				s.SubscriptionID().Return("123")
   396  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink2.ResourceGroupName(), fakeLink2.OwnerResourceName(), fakeLink2.ResourceName())).Return(managedTags, nil)
   397  				s.ClusterName().Return(clusterName)
   398  
   399  				lr.DeleteResource(gomockinternal.AContext(), fakeLink2, serviceName).Return(nil)
   400  
   401  				s.SubscriptionID().Return("123")
   402  				tg.GetAtScope(gomockinternal.AContext(), azure.PrivateDNSZoneID("123", fakeZone.ResourceGroupName(), fakeZone.ResourceName())).Return(managedTags, nil)
   403  				s.ClusterName().Return(clusterName)
   404  
   405  				zr.DeleteResource(gomockinternal.AContext(), fakeZone, serviceName).Return(nil)
   406  				s.UpdateDeleteStatus(infrav1.PrivateDNSLinkReadyCondition, serviceName, nil)
   407  				s.UpdateDeleteStatus(infrav1.PrivateDNSZoneReadyCondition, serviceName, nil)
   408  				s.UpdateDeleteStatus(infrav1.PrivateDNSRecordReadyCondition, serviceName, nil)
   409  			},
   410  		},
   411  		{
   412  			name:          "skips if zone and links are unmanaged",
   413  			expectedError: "",
   414  			expect: func(s *mock_privatedns.MockScopeMockRecorder, lr, zr *mock_async.MockReconcilerMockRecorder, tg *mock_async.MockTagsGetterMockRecorder) {
   415  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   416  				s.PrivateDNSSpec().Return(fakeZone, []azure.ResourceSpecGetter{fakeLink1, fakeLink2}, []azure.ResourceSpecGetter{fakeRecord1}).Times(2)
   417  
   418  				s.SubscriptionID().Return("123")
   419  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink1.ResourceGroupName(), fakeLink1.OwnerResourceName(), fakeLink1.ResourceName())).Return(armresources.TagsResource{}, nil)
   420  				s.ClusterName().Return(clusterName)
   421  
   422  				s.SubscriptionID().Return("123")
   423  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink2.ResourceGroupName(), fakeLink2.OwnerResourceName(), fakeLink2.ResourceName())).Return(armresources.TagsResource{}, nil)
   424  				s.ClusterName().Return(clusterName)
   425  
   426  				s.SubscriptionID().Return("123")
   427  				tg.GetAtScope(gomockinternal.AContext(), azure.PrivateDNSZoneID("123", fakeZone.ResourceGroupName(), fakeZone.ResourceName())).Return(armresources.TagsResource{}, nil)
   428  				s.ClusterName().Return(clusterName)
   429  			},
   430  		},
   431  		{
   432  			name:          "skips if unmanaged, but deletes the next resource if it is managed",
   433  			expectedError: "",
   434  			expect: func(s *mock_privatedns.MockScopeMockRecorder, lr, zr *mock_async.MockReconcilerMockRecorder, tg *mock_async.MockTagsGetterMockRecorder) {
   435  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   436  				s.PrivateDNSSpec().Return(fakeZone, []azure.ResourceSpecGetter{fakeLink1, fakeLink2}, []azure.ResourceSpecGetter{fakeRecord1}).Times(2)
   437  
   438  				s.SubscriptionID().Return("123")
   439  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink1.ResourceGroupName(), fakeLink1.OwnerResourceName(), fakeLink1.ResourceName())).Return(armresources.TagsResource{}, nil)
   440  				s.ClusterName().Return(clusterName)
   441  
   442  				s.SubscriptionID().Return("123")
   443  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink2.ResourceGroupName(), fakeLink2.OwnerResourceName(), fakeLink2.ResourceName())).Return(managedTags, nil)
   444  				s.ClusterName().Return(clusterName)
   445  				lr.DeleteResource(gomockinternal.AContext(), fakeLink2, serviceName).Return(nil)
   446  
   447  				s.SubscriptionID().Return("123")
   448  				tg.GetAtScope(gomockinternal.AContext(), azure.PrivateDNSZoneID("123", fakeZone.ResourceGroupName(), fakeZone.ResourceName())).Return(managedTags, nil)
   449  				s.ClusterName().Return(clusterName)
   450  
   451  				zr.DeleteResource(gomockinternal.AContext(), fakeZone, serviceName).Return(nil)
   452  				s.UpdateDeleteStatus(infrav1.PrivateDNSLinkReadyCondition, serviceName, nil)
   453  				s.UpdateDeleteStatus(infrav1.PrivateDNSZoneReadyCondition, serviceName, nil)
   454  				s.UpdateDeleteStatus(infrav1.PrivateDNSRecordReadyCondition, serviceName, nil)
   455  			},
   456  		},
   457  		{
   458  			name:          "link1 is deleted, link2 is long running. It returns not done error",
   459  			expectedError: "operation type resourceType on Azure resource my-rg/resourceName is not done",
   460  			expect: func(s *mock_privatedns.MockScopeMockRecorder, lr, zr *mock_async.MockReconcilerMockRecorder, tg *mock_async.MockTagsGetterMockRecorder) {
   461  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   462  				s.PrivateDNSSpec().Return(fakeZone, []azure.ResourceSpecGetter{fakeLink1, fakeLink2}, []azure.ResourceSpecGetter{fakeRecord1})
   463  
   464  				s.SubscriptionID().Return("123")
   465  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink1.ResourceGroupName(), fakeLink1.OwnerResourceName(), fakeLink1.ResourceName())).Return(managedTags, nil)
   466  				s.ClusterName().Return(clusterName)
   467  
   468  				lr.DeleteResource(gomockinternal.AContext(), fakeLink1, serviceName).Return(notDoneError)
   469  
   470  				s.SubscriptionID().Return("123")
   471  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink2.ResourceGroupName(), fakeLink2.OwnerResourceName(), fakeLink2.ResourceName())).Return(managedTags, nil)
   472  				s.ClusterName().Return(clusterName)
   473  
   474  				lr.DeleteResource(gomockinternal.AContext(), fakeLink2, serviceName).Return(nil)
   475  				s.UpdateDeleteStatus(infrav1.PrivateDNSLinkReadyCondition, serviceName, notDoneError)
   476  			},
   477  		},
   478  		{
   479  			name:          "link1 deletion fails and link2 is long running, returns the more pressing error",
   480  			expectedError: "this is an error",
   481  			expect: func(s *mock_privatedns.MockScopeMockRecorder, lr, zr *mock_async.MockReconcilerMockRecorder, tg *mock_async.MockTagsGetterMockRecorder) {
   482  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   483  				s.PrivateDNSSpec().Return(fakeZone, []azure.ResourceSpecGetter{fakeLink1, fakeLink2}, []azure.ResourceSpecGetter{fakeRecord1})
   484  
   485  				s.SubscriptionID().Return("123")
   486  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink1.ResourceGroupName(), fakeLink1.OwnerResourceName(), fakeLink1.ResourceName())).Return(managedTags, nil)
   487  				s.ClusterName().Return(clusterName)
   488  
   489  				lr.DeleteResource(gomockinternal.AContext(), fakeLink1, serviceName).Return(errFake)
   490  
   491  				s.SubscriptionID().Return("123")
   492  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink2.ResourceGroupName(), fakeLink2.OwnerResourceName(), fakeLink2.ResourceName())).Return(managedTags, nil)
   493  				s.ClusterName().Return(clusterName)
   494  
   495  				lr.DeleteResource(gomockinternal.AContext(), fakeLink2, serviceName).Return(notDoneError)
   496  				s.UpdateDeleteStatus(infrav1.PrivateDNSLinkReadyCondition, serviceName, errFake)
   497  			},
   498  		},
   499  		{
   500  			name:          "links are deleted, zone is long running",
   501  			expectedError: "operation type resourceType on Azure resource my-rg/resourceName is not done",
   502  			expect: func(s *mock_privatedns.MockScopeMockRecorder, lr, zr *mock_async.MockReconcilerMockRecorder, tg *mock_async.MockTagsGetterMockRecorder) {
   503  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   504  				s.PrivateDNSSpec().Return(fakeZone, []azure.ResourceSpecGetter{fakeLink1, fakeLink2}, []azure.ResourceSpecGetter{fakeRecord1}).Times(2)
   505  
   506  				s.SubscriptionID().Return("123")
   507  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink1.ResourceGroupName(), fakeLink1.OwnerResourceName(), fakeLink1.ResourceName())).Return(managedTags, nil)
   508  				s.ClusterName().Return(clusterName)
   509  
   510  				lr.DeleteResource(gomockinternal.AContext(), fakeLink1, serviceName).Return(nil)
   511  
   512  				s.SubscriptionID().Return("123")
   513  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink2.ResourceGroupName(), fakeLink2.OwnerResourceName(), fakeLink2.ResourceName())).Return(managedTags, nil)
   514  				s.ClusterName().Return(clusterName)
   515  
   516  				lr.DeleteResource(gomockinternal.AContext(), fakeLink2, serviceName).Return(nil)
   517  
   518  				s.SubscriptionID().Return("123")
   519  				tg.GetAtScope(gomockinternal.AContext(), azure.PrivateDNSZoneID("123", fakeZone.ResourceGroupName(), fakeZone.ResourceName())).Return(managedTags, nil)
   520  				s.ClusterName().Return(clusterName)
   521  
   522  				zr.DeleteResource(gomockinternal.AContext(), fakeZone, serviceName).Return(notDoneError)
   523  
   524  				s.UpdateDeleteStatus(infrav1.PrivateDNSLinkReadyCondition, serviceName, nil)
   525  				s.UpdateDeleteStatus(infrav1.PrivateDNSZoneReadyCondition, serviceName, notDoneError)
   526  				s.UpdateDeleteStatus(infrav1.PrivateDNSRecordReadyCondition, serviceName, notDoneError)
   527  			},
   528  		},
   529  		{
   530  			name:          "links are deleted, zone deletion fails with error",
   531  			expectedError: "this is an error",
   532  			expect: func(s *mock_privatedns.MockScopeMockRecorder, lr, zr *mock_async.MockReconcilerMockRecorder, tg *mock_async.MockTagsGetterMockRecorder) {
   533  				s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout)
   534  				s.PrivateDNSSpec().Return(fakeZone, []azure.ResourceSpecGetter{fakeLink1, fakeLink2}, []azure.ResourceSpecGetter{fakeRecord1}).Times(2)
   535  
   536  				s.SubscriptionID().Return("123")
   537  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink1.ResourceGroupName(), fakeLink1.OwnerResourceName(), fakeLink1.ResourceName())).Return(managedTags, nil)
   538  				s.ClusterName().Return(clusterName)
   539  
   540  				lr.DeleteResource(gomockinternal.AContext(), fakeLink1, serviceName).Return(nil)
   541  
   542  				s.SubscriptionID().Return("123")
   543  				tg.GetAtScope(gomockinternal.AContext(), azure.VirtualNetworkLinkID("123", fakeLink2.ResourceGroupName(), fakeLink2.OwnerResourceName(), fakeLink2.ResourceName())).Return(managedTags, nil)
   544  				s.ClusterName().Return(clusterName)
   545  
   546  				lr.DeleteResource(gomockinternal.AContext(), fakeLink2, serviceName).Return(nil)
   547  
   548  				s.SubscriptionID().Return("123")
   549  				tg.GetAtScope(gomockinternal.AContext(), azure.PrivateDNSZoneID("123", fakeZone.ResourceGroupName(), fakeZone.ResourceName())).Return(managedTags, nil)
   550  				s.ClusterName().Return(clusterName)
   551  
   552  				zr.DeleteResource(gomockinternal.AContext(), fakeZone, serviceName).Return(errFake)
   553  
   554  				s.UpdateDeleteStatus(infrav1.PrivateDNSLinkReadyCondition, serviceName, nil)
   555  				s.UpdateDeleteStatus(infrav1.PrivateDNSZoneReadyCondition, serviceName, errFake)
   556  				s.UpdateDeleteStatus(infrav1.PrivateDNSRecordReadyCondition, serviceName, errFake)
   557  			},
   558  		},
   559  	}
   560  
   561  	for _, tc := range testcases {
   562  		tc := tc
   563  		t.Run(tc.name, func(t *testing.T) {
   564  			g := NewWithT(t)
   565  
   566  			t.Parallel()
   567  			mockCtrl := gomock.NewController(t)
   568  			defer mockCtrl.Finish()
   569  			scopeMock := mock_privatedns.NewMockScope(mockCtrl)
   570  			vnetLinkReconcilerMock := mock_async.NewMockReconciler(mockCtrl)
   571  			zoneReconcilerMock := mock_async.NewMockReconciler(mockCtrl)
   572  			tagsGetterMock := mock_async.NewMockTagsGetter(mockCtrl)
   573  
   574  			tc.expect(scopeMock.EXPECT(), vnetLinkReconcilerMock.EXPECT(), zoneReconcilerMock.EXPECT(), tagsGetterMock.EXPECT())
   575  
   576  			s := &Service{
   577  				Scope:              scopeMock,
   578  				zoneReconciler:     zoneReconcilerMock,
   579  				vnetLinkReconciler: vnetLinkReconcilerMock,
   580  				TagsGetter:         tagsGetterMock,
   581  			}
   582  
   583  			err := s.Delete(context.TODO())
   584  			if tc.expectedError != "" {
   585  				g.Expect(err).To(HaveOccurred())
   586  				g.Expect(err).To(MatchError(tc.expectedError))
   587  			} else {
   588  				g.Expect(err).NotTo(HaveOccurred())
   589  			}
   590  		})
   591  	}
   592  }