sigs.k8s.io/external-dns@v0.14.1/provider/azure/azure_test.go (about) 1 /* 2 Copyright 2017 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 azure 18 19 import ( 20 "context" 21 "testing" 22 23 azcoreruntime "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" 24 "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" 25 dns "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns" 26 "github.com/stretchr/testify/assert" 27 28 "sigs.k8s.io/external-dns/endpoint" 29 "sigs.k8s.io/external-dns/internal/testutils" 30 "sigs.k8s.io/external-dns/plan" 31 "sigs.k8s.io/external-dns/provider" 32 ) 33 34 // mockZonesClient implements the methods of the Azure DNS Zones Client which are used in the Azure Provider 35 // and returns static results which are defined per test 36 type mockZonesClient struct { 37 pagingHandler azcoreruntime.PagingHandler[dns.ZonesClientListByResourceGroupResponse] 38 } 39 40 func newMockZonesClient(zones []*dns.Zone) mockZonesClient { 41 pagingHandler := azcoreruntime.PagingHandler[dns.ZonesClientListByResourceGroupResponse]{ 42 More: func(resp dns.ZonesClientListByResourceGroupResponse) bool { 43 return false 44 }, 45 Fetcher: func(context.Context, *dns.ZonesClientListByResourceGroupResponse) (dns.ZonesClientListByResourceGroupResponse, error) { 46 return dns.ZonesClientListByResourceGroupResponse{ 47 ZoneListResult: dns.ZoneListResult{ 48 Value: zones, 49 }, 50 }, nil 51 }, 52 } 53 return mockZonesClient{ 54 pagingHandler: pagingHandler, 55 } 56 } 57 58 func (client *mockZonesClient) NewListByResourceGroupPager(resourceGroupName string, options *dns.ZonesClientListByResourceGroupOptions) *azcoreruntime.Pager[dns.ZonesClientListByResourceGroupResponse] { 59 return azcoreruntime.NewPager(client.pagingHandler) 60 } 61 62 // mockZonesClient implements the methods of the Azure DNS RecordSet Client which are used in the Azure Provider 63 // and returns static results which are defined per test 64 type mockRecordSetsClient struct { 65 pagingHandler azcoreruntime.PagingHandler[dns.RecordSetsClientListAllByDNSZoneResponse] 66 deletedEndpoints []*endpoint.Endpoint 67 updatedEndpoints []*endpoint.Endpoint 68 } 69 70 func newMockRecordSetsClient(recordSets []*dns.RecordSet) mockRecordSetsClient { 71 pagingHandler := azcoreruntime.PagingHandler[dns.RecordSetsClientListAllByDNSZoneResponse]{ 72 More: func(resp dns.RecordSetsClientListAllByDNSZoneResponse) bool { 73 return false 74 }, 75 Fetcher: func(context.Context, *dns.RecordSetsClientListAllByDNSZoneResponse) (dns.RecordSetsClientListAllByDNSZoneResponse, error) { 76 return dns.RecordSetsClientListAllByDNSZoneResponse{ 77 RecordSetListResult: dns.RecordSetListResult{ 78 Value: recordSets, 79 }, 80 }, nil 81 }, 82 } 83 return mockRecordSetsClient{ 84 pagingHandler: pagingHandler, 85 } 86 } 87 88 func (client *mockRecordSetsClient) NewListAllByDNSZonePager(resourceGroupName string, zoneName string, options *dns.RecordSetsClientListAllByDNSZoneOptions) *azcoreruntime.Pager[dns.RecordSetsClientListAllByDNSZoneResponse] { 89 return azcoreruntime.NewPager(client.pagingHandler) 90 } 91 92 func (client *mockRecordSetsClient) Delete(ctx context.Context, resourceGroupName string, zoneName string, relativeRecordSetName string, recordType dns.RecordType, options *dns.RecordSetsClientDeleteOptions) (dns.RecordSetsClientDeleteResponse, error) { 93 client.deletedEndpoints = append( 94 client.deletedEndpoints, 95 endpoint.NewEndpoint( 96 formatAzureDNSName(relativeRecordSetName, zoneName), 97 string(recordType), 98 "", 99 ), 100 ) 101 return dns.RecordSetsClientDeleteResponse{}, nil 102 } 103 104 func (client *mockRecordSetsClient) CreateOrUpdate(ctx context.Context, resourceGroupName string, zoneName string, relativeRecordSetName string, recordType dns.RecordType, parameters dns.RecordSet, options *dns.RecordSetsClientCreateOrUpdateOptions) (dns.RecordSetsClientCreateOrUpdateResponse, error) { 105 var ttl endpoint.TTL 106 if parameters.Properties.TTL != nil { 107 ttl = endpoint.TTL(*parameters.Properties.TTL) 108 } 109 client.updatedEndpoints = append( 110 client.updatedEndpoints, 111 endpoint.NewEndpointWithTTL( 112 formatAzureDNSName(relativeRecordSetName, zoneName), 113 string(recordType), 114 ttl, 115 extractAzureTargets(¶meters)..., 116 ), 117 ) 118 return dns.RecordSetsClientCreateOrUpdateResponse{}, nil 119 } 120 121 func createMockZone(zone string, id string) *dns.Zone { 122 return &dns.Zone{ 123 ID: to.Ptr(id), 124 Name: to.Ptr(zone), 125 } 126 } 127 128 func aRecordSetPropertiesGetter(values []string, ttl int64) *dns.RecordSetProperties { 129 aRecords := make([]*dns.ARecord, len(values)) 130 for i, value := range values { 131 aRecords[i] = &dns.ARecord{ 132 IPv4Address: to.Ptr(value), 133 } 134 } 135 return &dns.RecordSetProperties{ 136 TTL: to.Ptr(ttl), 137 ARecords: aRecords, 138 } 139 } 140 141 func aaaaRecordSetPropertiesGetter(values []string, ttl int64) *dns.RecordSetProperties { 142 aaaaRecords := make([]*dns.AaaaRecord, len(values)) 143 for i, value := range values { 144 aaaaRecords[i] = &dns.AaaaRecord{ 145 IPv6Address: to.Ptr(value), 146 } 147 } 148 return &dns.RecordSetProperties{ 149 TTL: to.Ptr(ttl), 150 AaaaRecords: aaaaRecords, 151 } 152 } 153 154 func cNameRecordSetPropertiesGetter(values []string, ttl int64) *dns.RecordSetProperties { 155 return &dns.RecordSetProperties{ 156 TTL: to.Ptr(ttl), 157 CnameRecord: &dns.CnameRecord{ 158 Cname: to.Ptr(values[0]), 159 }, 160 } 161 } 162 163 func mxRecordSetPropertiesGetter(values []string, ttl int64) *dns.RecordSetProperties { 164 mxRecords := make([]*dns.MxRecord, len(values)) 165 for i, target := range values { 166 mxRecord, _ := parseMxTarget[dns.MxRecord](target) 167 mxRecords[i] = &mxRecord 168 } 169 return &dns.RecordSetProperties{ 170 TTL: to.Ptr(ttl), 171 MxRecords: mxRecords, 172 } 173 } 174 175 func txtRecordSetPropertiesGetter(values []string, ttl int64) *dns.RecordSetProperties { 176 return &dns.RecordSetProperties{ 177 TTL: to.Ptr(ttl), 178 TxtRecords: []*dns.TxtRecord{ 179 { 180 Value: []*string{to.Ptr(values[0])}, 181 }, 182 }, 183 } 184 } 185 186 func othersRecordSetPropertiesGetter(values []string, ttl int64) *dns.RecordSetProperties { 187 return &dns.RecordSetProperties{ 188 TTL: to.Ptr(ttl), 189 } 190 } 191 192 func createMockRecordSet(name, recordType string, values ...string) *dns.RecordSet { 193 return createMockRecordSetMultiWithTTL(name, recordType, 0, values...) 194 } 195 196 func createMockRecordSetWithTTL(name, recordType, value string, ttl int64) *dns.RecordSet { 197 return createMockRecordSetMultiWithTTL(name, recordType, ttl, value) 198 } 199 200 func createMockRecordSetMultiWithTTL(name, recordType string, ttl int64, values ...string) *dns.RecordSet { 201 var getterFunc func(values []string, ttl int64) *dns.RecordSetProperties 202 203 switch recordType { 204 case endpoint.RecordTypeA: 205 getterFunc = aRecordSetPropertiesGetter 206 case endpoint.RecordTypeAAAA: 207 getterFunc = aaaaRecordSetPropertiesGetter 208 case endpoint.RecordTypeCNAME: 209 getterFunc = cNameRecordSetPropertiesGetter 210 case endpoint.RecordTypeMX: 211 getterFunc = mxRecordSetPropertiesGetter 212 case endpoint.RecordTypeTXT: 213 getterFunc = txtRecordSetPropertiesGetter 214 default: 215 getterFunc = othersRecordSetPropertiesGetter 216 } 217 return &dns.RecordSet{ 218 Name: to.Ptr(name), 219 Type: to.Ptr("Microsoft.Network/dnszones/" + recordType), 220 Properties: getterFunc(values, ttl), 221 } 222 } 223 224 // newMockedAzureProvider creates an AzureProvider comprising the mocked clients for zones and recordsets 225 func newMockedAzureProvider(domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, resourceGroup string, userAssignedIdentityClientID string, zones []*dns.Zone, recordSets []*dns.RecordSet) (*AzureProvider, error) { 226 zonesClient := newMockZonesClient(zones) 227 recordSetsClient := newMockRecordSetsClient(recordSets) 228 return newAzureProvider(domainFilter, zoneNameFilter, zoneIDFilter, dryRun, resourceGroup, userAssignedIdentityClientID, &zonesClient, &recordSetsClient), nil 229 } 230 231 func newAzureProvider(domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, resourceGroup string, userAssignedIdentityClientID string, zonesClient ZonesClient, recordsClient RecordSetsClient) *AzureProvider { 232 return &AzureProvider{ 233 domainFilter: domainFilter, 234 zoneNameFilter: zoneNameFilter, 235 zoneIDFilter: zoneIDFilter, 236 dryRun: dryRun, 237 resourceGroup: resourceGroup, 238 userAssignedIdentityClientID: userAssignedIdentityClientID, 239 zonesClient: zonesClient, 240 recordSetsClient: recordsClient, 241 } 242 } 243 244 func validateAzureEndpoints(t *testing.T, endpoints []*endpoint.Endpoint, expected []*endpoint.Endpoint) { 245 assert.True(t, testutils.SameEndpoints(endpoints, expected), "expected and actual endpoints don't match. %s:%s", endpoints, expected) 246 } 247 248 func TestAzureRecord(t *testing.T) { 249 provider, err := newMockedAzureProvider(endpoint.NewDomainFilter([]string{"example.com"}), endpoint.NewDomainFilter([]string{}), provider.NewZoneIDFilter([]string{""}), true, "k8s", "", 250 []*dns.Zone{ 251 createMockZone("example.com", "/dnszones/example.com"), 252 }, 253 []*dns.RecordSet{ 254 createMockRecordSet("@", "NS", "ns1-03.azure-dns.com."), 255 createMockRecordSet("@", "SOA", "Email: azuredns-hostmaster.microsoft.com"), 256 createMockRecordSet("@", endpoint.RecordTypeA, "123.123.123.122"), 257 createMockRecordSet("@", endpoint.RecordTypeAAAA, "2001::123:123:123:122"), 258 createMockRecordSet("@", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"), 259 createMockRecordSetWithTTL("nginx", endpoint.RecordTypeA, "123.123.123.123", 3600), 260 createMockRecordSetWithTTL("nginx", endpoint.RecordTypeAAAA, "2001::123:123:123:123", 3600), 261 createMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL), 262 createMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10), 263 createMockRecordSetMultiWithTTL("mail", endpoint.RecordTypeMX, 4000, "10 example.com"), 264 }) 265 if err != nil { 266 t.Fatal(err) 267 } 268 269 ctx := context.Background() 270 actual, err := provider.Records(ctx) 271 if err != nil { 272 t.Fatal(err) 273 } 274 expected := []*endpoint.Endpoint{ 275 endpoint.NewEndpoint("example.com", endpoint.RecordTypeA, "123.123.123.122"), 276 endpoint.NewEndpoint("example.com", endpoint.RecordTypeAAAA, "2001::123:123:123:122"), 277 endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"), 278 endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123"), 279 endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeAAAA, 3600, "2001::123:123:123:123"), 280 endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeTXT, recordTTL, "heritage=external-dns,external-dns/owner=default"), 281 endpoint.NewEndpointWithTTL("hack.example.com", endpoint.RecordTypeCNAME, 10, "hack.azurewebsites.net"), 282 endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeMX, 4000, "10 example.com"), 283 } 284 285 validateAzureEndpoints(t, actual, expected) 286 } 287 288 func TestAzureMultiRecord(t *testing.T) { 289 provider, err := newMockedAzureProvider(endpoint.NewDomainFilter([]string{"example.com"}), endpoint.NewDomainFilter([]string{}), provider.NewZoneIDFilter([]string{""}), true, "k8s", "", 290 []*dns.Zone{ 291 createMockZone("example.com", "/dnszones/example.com"), 292 }, 293 []*dns.RecordSet{ 294 createMockRecordSet("@", "NS", "ns1-03.azure-dns.com."), 295 createMockRecordSet("@", "SOA", "Email: azuredns-hostmaster.microsoft.com"), 296 createMockRecordSet("@", endpoint.RecordTypeA, "123.123.123.122", "234.234.234.233"), 297 createMockRecordSet("@", endpoint.RecordTypeAAAA, "2001::123:123:123:122", "2001::234:234:234:233"), 298 createMockRecordSet("@", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"), 299 createMockRecordSetMultiWithTTL("nginx", endpoint.RecordTypeA, 3600, "123.123.123.123", "234.234.234.234"), 300 createMockRecordSetMultiWithTTL("nginx", endpoint.RecordTypeAAAA, 3600, "2001::123:123:123:123", "2001::234:234:234:234"), 301 createMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL), 302 createMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10), 303 createMockRecordSetMultiWithTTL("mail", endpoint.RecordTypeMX, 4000, "10 example.com", "20 backup.example.com"), 304 }) 305 if err != nil { 306 t.Fatal(err) 307 } 308 309 ctx := context.Background() 310 actual, err := provider.Records(ctx) 311 if err != nil { 312 t.Fatal(err) 313 } 314 expected := []*endpoint.Endpoint{ 315 endpoint.NewEndpoint("example.com", endpoint.RecordTypeA, "123.123.123.122", "234.234.234.233"), 316 endpoint.NewEndpoint("example.com", endpoint.RecordTypeAAAA, "2001::123:123:123:122", "2001::234:234:234:233"), 317 endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"), 318 endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123", "234.234.234.234"), 319 endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeAAAA, 3600, "2001::123:123:123:123", "2001::234:234:234:234"), 320 endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeTXT, recordTTL, "heritage=external-dns,external-dns/owner=default"), 321 endpoint.NewEndpointWithTTL("hack.example.com", endpoint.RecordTypeCNAME, 10, "hack.azurewebsites.net"), 322 endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeMX, 4000, "10 example.com", "20 backup.example.com"), 323 } 324 325 validateAzureEndpoints(t, actual, expected) 326 } 327 328 func TestAzureApplyChanges(t *testing.T) { 329 recordsClient := mockRecordSetsClient{} 330 331 testAzureApplyChangesInternal(t, false, &recordsClient) 332 333 validateAzureEndpoints(t, recordsClient.deletedEndpoints, []*endpoint.Endpoint{ 334 endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypeA, ""), 335 endpoint.NewEndpoint("deletedaaaa.example.com", endpoint.RecordTypeAAAA, ""), 336 endpoint.NewEndpoint("deletedcname.example.com", endpoint.RecordTypeCNAME, ""), 337 }) 338 339 validateAzureEndpoints(t, recordsClient.updatedEndpoints, []*endpoint.Endpoint{ 340 endpoint.NewEndpointWithTTL("example.com", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4"), 341 endpoint.NewEndpointWithTTL("example.com", endpoint.RecordTypeAAAA, endpoint.TTL(recordTTL), "2001::1:2:3:4"), 342 endpoint.NewEndpointWithTTL("example.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"), 343 endpoint.NewEndpointWithTTL("foo.example.com", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4", "1.2.3.5"), 344 endpoint.NewEndpointWithTTL("foo.example.com", endpoint.RecordTypeAAAA, endpoint.TTL(recordTTL), "2001::1:2:3:4", "2001::1:2:3:5"), 345 endpoint.NewEndpointWithTTL("foo.example.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"), 346 endpoint.NewEndpointWithTTL("bar.example.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "other.com"), 347 endpoint.NewEndpointWithTTL("bar.example.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"), 348 endpoint.NewEndpointWithTTL("other.com", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "5.6.7.8"), 349 endpoint.NewEndpointWithTTL("other.com", endpoint.RecordTypeAAAA, endpoint.TTL(recordTTL), "2001::5:6:7:8"), 350 endpoint.NewEndpointWithTTL("other.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"), 351 endpoint.NewEndpointWithTTL("new.example.com", endpoint.RecordTypeA, 3600, "111.222.111.222"), 352 endpoint.NewEndpointWithTTL("new.example.com", endpoint.RecordTypeAAAA, 3600, "2001::111:222:111:222"), 353 endpoint.NewEndpointWithTTL("newcname.example.com", endpoint.RecordTypeCNAME, 10, "other.com"), 354 endpoint.NewEndpointWithTTL("newmail.example.com", endpoint.RecordTypeMX, 7200, "40 bar.other.com"), 355 endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 other.com"), 356 endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"), 357 }) 358 } 359 360 func TestAzureApplyChangesDryRun(t *testing.T) { 361 recordsClient := mockRecordSetsClient{} 362 363 testAzureApplyChangesInternal(t, true, &recordsClient) 364 365 validateAzureEndpoints(t, recordsClient.deletedEndpoints, []*endpoint.Endpoint{}) 366 367 validateAzureEndpoints(t, recordsClient.updatedEndpoints, []*endpoint.Endpoint{}) 368 } 369 370 func testAzureApplyChangesInternal(t *testing.T, dryRun bool, client RecordSetsClient) { 371 zones := []*dns.Zone{ 372 createMockZone("example.com", "/dnszones/example.com"), 373 createMockZone("other.com", "/dnszones/other.com"), 374 } 375 zonesClient := newMockZonesClient(zones) 376 377 provider := newAzureProvider( 378 endpoint.NewDomainFilter([]string{""}), 379 endpoint.NewDomainFilter([]string{""}), 380 provider.NewZoneIDFilter([]string{""}), 381 dryRun, 382 "group", 383 "", 384 &zonesClient, 385 client, 386 ) 387 388 createRecords := []*endpoint.Endpoint{ 389 endpoint.NewEndpoint("example.com", endpoint.RecordTypeA, "1.2.3.4"), 390 endpoint.NewEndpoint("example.com", endpoint.RecordTypeAAAA, "2001::1:2:3:4"), 391 endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "tag"), 392 endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "1.2.3.5", "1.2.3.4"), 393 endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeAAAA, "2001::1:2:3:5", "2001::1:2:3:4"), 394 endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeTXT, "tag"), 395 endpoint.NewEndpoint("bar.example.com", endpoint.RecordTypeCNAME, "other.com"), 396 endpoint.NewEndpoint("bar.example.com", endpoint.RecordTypeTXT, "tag"), 397 endpoint.NewEndpoint("other.com", endpoint.RecordTypeA, "5.6.7.8"), 398 endpoint.NewEndpoint("other.com", endpoint.RecordTypeAAAA, "2001::5:6:7:8"), 399 endpoint.NewEndpoint("other.com", endpoint.RecordTypeTXT, "tag"), 400 endpoint.NewEndpoint("nope.com", endpoint.RecordTypeA, "4.4.4.4"), 401 endpoint.NewEndpoint("nope.com", endpoint.RecordTypeAAAA, "2001::4:4:4:4"), 402 endpoint.NewEndpoint("nope.com", endpoint.RecordTypeTXT, "tag"), 403 endpoint.NewEndpoint("mail.example.com", endpoint.RecordTypeMX, "10 other.com"), 404 endpoint.NewEndpoint("mail.example.com", endpoint.RecordTypeTXT, "tag"), 405 } 406 407 currentRecords := []*endpoint.Endpoint{ 408 endpoint.NewEndpoint("old.example.com", endpoint.RecordTypeA, "121.212.121.212"), 409 endpoint.NewEndpoint("oldcname.example.com", endpoint.RecordTypeCNAME, "other.com"), 410 endpoint.NewEndpoint("old.nope.com", endpoint.RecordTypeA, "121.212.121.212"), 411 endpoint.NewEndpoint("oldmail.example.com", endpoint.RecordTypeMX, "20 foo.other.com"), 412 } 413 updatedRecords := []*endpoint.Endpoint{ 414 endpoint.NewEndpointWithTTL("new.example.com", endpoint.RecordTypeA, 3600, "111.222.111.222"), 415 endpoint.NewEndpointWithTTL("new.example.com", endpoint.RecordTypeAAAA, 3600, "2001::111:222:111:222"), 416 endpoint.NewEndpointWithTTL("newcname.example.com", endpoint.RecordTypeCNAME, 10, "other.com"), 417 endpoint.NewEndpoint("new.nope.com", endpoint.RecordTypeA, "222.111.222.111"), 418 endpoint.NewEndpoint("new.nope.com", endpoint.RecordTypeAAAA, "2001::222:111:222:111"), 419 endpoint.NewEndpointWithTTL("newmail.example.com", endpoint.RecordTypeMX, 7200, "40 bar.other.com"), 420 } 421 422 deleteRecords := []*endpoint.Endpoint{ 423 endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypeA, "111.222.111.222"), 424 endpoint.NewEndpoint("deletedaaaa.example.com", endpoint.RecordTypeAAAA, "2001::111:222:111:222"), 425 endpoint.NewEndpoint("deletedcname.example.com", endpoint.RecordTypeCNAME, "other.com"), 426 endpoint.NewEndpoint("deleted.nope.com", endpoint.RecordTypeA, "222.111.222.111"), 427 endpoint.NewEndpoint("deleted.nope.com", endpoint.RecordTypeAAAA, "2001::222:111:222:111"), 428 } 429 430 changes := &plan.Changes{ 431 Create: createRecords, 432 UpdateNew: updatedRecords, 433 UpdateOld: currentRecords, 434 Delete: deleteRecords, 435 } 436 437 if err := provider.ApplyChanges(context.Background(), changes); err != nil { 438 t.Fatal(err) 439 } 440 } 441 442 func TestAzureNameFilter(t *testing.T) { 443 provider, err := newMockedAzureProvider(endpoint.NewDomainFilter([]string{"nginx.example.com"}), endpoint.NewDomainFilter([]string{"example.com"}), provider.NewZoneIDFilter([]string{""}), true, "k8s", "", 444 []*dns.Zone{ 445 createMockZone("example.com", "/dnszones/example.com"), 446 }, 447 448 []*dns.RecordSet{ 449 createMockRecordSet("@", "NS", "ns1-03.azure-dns.com."), 450 createMockRecordSet("@", "SOA", "Email: azuredns-hostmaster.microsoft.com"), 451 createMockRecordSet("@", endpoint.RecordTypeA, "123.123.123.122"), 452 createMockRecordSet("@", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"), 453 createMockRecordSetWithTTL("test.nginx", endpoint.RecordTypeA, "123.123.123.123", 3600), 454 createMockRecordSetWithTTL("nginx", endpoint.RecordTypeA, "123.123.123.123", 3600), 455 createMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL), 456 createMockRecordSetWithTTL("mail.nginx", endpoint.RecordTypeMX, "20 example.com", recordTTL), 457 createMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10), 458 }) 459 if err != nil { 460 t.Fatal(err) 461 } 462 463 ctx := context.Background() 464 actual, err := provider.Records(ctx) 465 if err != nil { 466 t.Fatal(err) 467 } 468 expected := []*endpoint.Endpoint{ 469 endpoint.NewEndpointWithTTL("test.nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123"), 470 endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123"), 471 endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeTXT, recordTTL, "heritage=external-dns,external-dns/owner=default"), 472 endpoint.NewEndpointWithTTL("mail.nginx.example.com", endpoint.RecordTypeMX, recordTTL, "20 example.com"), 473 } 474 475 validateAzureEndpoints(t, actual, expected) 476 } 477 478 func TestAzureApplyChangesZoneName(t *testing.T) { 479 recordsClient := mockRecordSetsClient{} 480 481 testAzureApplyChangesInternalZoneName(t, false, &recordsClient) 482 483 validateAzureEndpoints(t, recordsClient.deletedEndpoints, []*endpoint.Endpoint{ 484 endpoint.NewEndpoint("deleted.foo.example.com", endpoint.RecordTypeA, ""), 485 endpoint.NewEndpoint("deletedaaaa.foo.example.com", endpoint.RecordTypeAAAA, ""), 486 endpoint.NewEndpoint("deletedcname.foo.example.com", endpoint.RecordTypeCNAME, ""), 487 }) 488 489 validateAzureEndpoints(t, recordsClient.updatedEndpoints, []*endpoint.Endpoint{ 490 endpoint.NewEndpointWithTTL("foo.example.com", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4", "1.2.3.5"), 491 endpoint.NewEndpointWithTTL("foo.example.com", endpoint.RecordTypeAAAA, endpoint.TTL(recordTTL), "2001::1:2:3:4", "2001::1:2:3:5"), 492 endpoint.NewEndpointWithTTL("foo.example.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"), 493 endpoint.NewEndpointWithTTL("new.foo.example.com", endpoint.RecordTypeA, 3600, "111.222.111.222"), 494 endpoint.NewEndpointWithTTL("new.foo.example.com", endpoint.RecordTypeAAAA, 3600, "2001::111:222:111:222"), 495 endpoint.NewEndpointWithTTL("newcname.foo.example.com", endpoint.RecordTypeCNAME, 10, "other.com"), 496 }) 497 } 498 499 func testAzureApplyChangesInternalZoneName(t *testing.T, dryRun bool, client RecordSetsClient) { 500 zonesClient := newMockZonesClient([]*dns.Zone{createMockZone("example.com", "/dnszones/example.com")}) 501 502 provider := newAzureProvider( 503 endpoint.NewDomainFilter([]string{"foo.example.com"}), 504 endpoint.NewDomainFilter([]string{"example.com"}), 505 provider.NewZoneIDFilter([]string{""}), 506 dryRun, 507 "group", 508 "", 509 &zonesClient, 510 client, 511 ) 512 513 createRecords := []*endpoint.Endpoint{ 514 endpoint.NewEndpoint("example.com", endpoint.RecordTypeA, "1.2.3.4"), 515 endpoint.NewEndpoint("example.com", endpoint.RecordTypeAAAA, "2001::1:2:3:4"), 516 endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "tag"), 517 endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "1.2.3.5", "1.2.3.4"), 518 endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeAAAA, "2001::1:2:3:5", "2001::1:2:3:4"), 519 endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeTXT, "tag"), 520 endpoint.NewEndpoint("bar.example.com", endpoint.RecordTypeCNAME, "other.com"), 521 endpoint.NewEndpoint("bar.example.com", endpoint.RecordTypeTXT, "tag"), 522 endpoint.NewEndpoint("other.com", endpoint.RecordTypeA, "5.6.7.8"), 523 endpoint.NewEndpoint("other.com", endpoint.RecordTypeTXT, "tag"), 524 endpoint.NewEndpoint("nope.com", endpoint.RecordTypeA, "4.4.4.4"), 525 endpoint.NewEndpoint("nope.com", endpoint.RecordTypeTXT, "tag"), 526 } 527 528 currentRecords := []*endpoint.Endpoint{ 529 endpoint.NewEndpoint("old.foo.example.com", endpoint.RecordTypeA, "121.212.121.212"), 530 endpoint.NewEndpoint("oldcname.foo.example.com", endpoint.RecordTypeCNAME, "other.com"), 531 endpoint.NewEndpoint("old.nope.example.com", endpoint.RecordTypeA, "121.212.121.212"), 532 } 533 updatedRecords := []*endpoint.Endpoint{ 534 endpoint.NewEndpointWithTTL("new.foo.example.com", endpoint.RecordTypeA, 3600, "111.222.111.222"), 535 endpoint.NewEndpointWithTTL("new.foo.example.com", endpoint.RecordTypeAAAA, 3600, "2001::111:222:111:222"), 536 endpoint.NewEndpointWithTTL("newcname.foo.example.com", endpoint.RecordTypeCNAME, 10, "other.com"), 537 endpoint.NewEndpoint("new.nope.example.com", endpoint.RecordTypeA, "222.111.222.111"), 538 endpoint.NewEndpoint("new.nope.example.com", endpoint.RecordTypeAAAA, "2001::222:111:222:111"), 539 } 540 541 deleteRecords := []*endpoint.Endpoint{ 542 endpoint.NewEndpoint("deleted.foo.example.com", endpoint.RecordTypeA, "111.222.111.222"), 543 endpoint.NewEndpoint("deletedaaaa.foo.example.com", endpoint.RecordTypeAAAA, "2001::111:222:111:222"), 544 endpoint.NewEndpoint("deletedcname.foo.example.com", endpoint.RecordTypeCNAME, "other.com"), 545 endpoint.NewEndpoint("deleted.nope.example.com", endpoint.RecordTypeA, "222.111.222.111"), 546 } 547 548 changes := &plan.Changes{ 549 Create: createRecords, 550 UpdateNew: updatedRecords, 551 UpdateOld: currentRecords, 552 Delete: deleteRecords, 553 } 554 555 if err := provider.ApplyChanges(context.Background(), changes); err != nil { 556 t.Fatal(err) 557 } 558 }