sigs.k8s.io/external-dns@v0.14.1/provider/scaleway/scaleway_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 scaleway 18 19 import ( 20 "context" 21 "os" 22 "reflect" 23 "testing" 24 25 domain "github.com/scaleway/scaleway-sdk-go/api/domain/v2beta1" 26 "github.com/scaleway/scaleway-sdk-go/scw" 27 "github.com/stretchr/testify/assert" 28 "github.com/stretchr/testify/require" 29 "sigs.k8s.io/external-dns/endpoint" 30 "sigs.k8s.io/external-dns/plan" 31 ) 32 33 type mockScalewayDomain struct { 34 *domain.API 35 } 36 37 func (m *mockScalewayDomain) ListDNSZones(req *domain.ListDNSZonesRequest, opts ...scw.RequestOption) (*domain.ListDNSZonesResponse, error) { 38 return &domain.ListDNSZonesResponse{ 39 DNSZones: []*domain.DNSZone{ 40 { 41 Domain: "example.com", 42 Subdomain: "", 43 }, 44 { 45 Domain: "example.com", 46 Subdomain: "test", 47 }, 48 { 49 Domain: "dummy.me", 50 Subdomain: "", 51 }, 52 { 53 Domain: "dummy.me", 54 Subdomain: "test", 55 }, 56 }, 57 }, nil 58 } 59 60 func (m *mockScalewayDomain) ListDNSZoneRecords(req *domain.ListDNSZoneRecordsRequest, opts ...scw.RequestOption) (*domain.ListDNSZoneRecordsResponse, error) { 61 records := []*domain.Record{} 62 if req.DNSZone == "example.com" { 63 records = []*domain.Record{ 64 { 65 Data: "1.1.1.1", 66 Name: "one", 67 TTL: 300, 68 Priority: 0, 69 Type: domain.RecordTypeA, 70 }, 71 { 72 Data: "1.1.1.2", 73 Name: "two", 74 TTL: 300, 75 Priority: 0, 76 Type: domain.RecordTypeA, 77 }, 78 { 79 Data: "1.1.1.3", 80 Name: "two", 81 TTL: 300, 82 Priority: 0, 83 Type: domain.RecordTypeA, 84 }, 85 } 86 } else if req.DNSZone == "test.example.com" { 87 records = []*domain.Record{ 88 { 89 Data: "1.1.1.1", 90 Name: "", 91 TTL: 300, 92 Priority: 0, 93 Type: domain.RecordTypeA, 94 }, 95 { 96 Data: "test.example.com.", 97 Name: "two", 98 TTL: 600, 99 Priority: 30, 100 Type: domain.RecordTypeCNAME, 101 }, 102 } 103 } 104 return &domain.ListDNSZoneRecordsResponse{ 105 Records: records, 106 }, nil 107 } 108 109 func (m *mockScalewayDomain) UpdateDNSZoneRecords(req *domain.UpdateDNSZoneRecordsRequest, opts ...scw.RequestOption) (*domain.UpdateDNSZoneRecordsResponse, error) { 110 return nil, nil 111 } 112 113 func TestScalewayProvider_NewScalewayProvider(t *testing.T) { 114 profile := `profiles: 115 foo: 116 access_key: SCWXXXXXXXXXXXXXXXXX 117 secret_key: 11111111-1111-1111-1111-111111111111 118 ` 119 tmpDir := t.TempDir() 120 err := os.WriteFile(tmpDir+"/config.yaml", []byte(profile), 0600) 121 if err != nil { 122 t.Errorf("failed : %s", err) 123 } 124 _ = os.Setenv(scw.ScwActiveProfileEnv, "foo") 125 _ = os.Setenv(scw.ScwConfigPathEnv, tmpDir+"/config.yaml") 126 _, err = NewScalewayProvider(context.TODO(), endpoint.NewDomainFilter([]string{"example.com"}), true) 127 if err != nil { 128 t.Errorf("failed : %s", err) 129 } 130 131 _ = os.Setenv(scw.ScwAccessKeyEnv, "SCWXXXXXXXXXXXXXXXXX") 132 _ = os.Setenv(scw.ScwSecretKeyEnv, "11111111-1111-1111-1111-111111111111") 133 _, err = NewScalewayProvider(context.TODO(), endpoint.NewDomainFilter([]string{"example.com"}), true) 134 if err != nil { 135 t.Errorf("failed : %s", err) 136 } 137 138 _ = os.Unsetenv(scw.ScwSecretKeyEnv) 139 _, err = NewScalewayProvider(context.TODO(), endpoint.NewDomainFilter([]string{"example.com"}), true) 140 if err == nil { 141 t.Errorf("expected to fail") 142 } 143 144 _ = os.Setenv(scw.ScwSecretKeyEnv, "dummy") 145 _, err = NewScalewayProvider(context.TODO(), endpoint.NewDomainFilter([]string{"example.com"}), true) 146 if err == nil { 147 t.Errorf("expected to fail") 148 } 149 150 _ = os.Unsetenv(scw.ScwAccessKeyEnv) 151 _ = os.Setenv(scw.ScwSecretKeyEnv, "11111111-1111-1111-1111-111111111111") 152 _, err = NewScalewayProvider(context.TODO(), endpoint.NewDomainFilter([]string{"example.com"}), true) 153 if err == nil { 154 t.Errorf("expected to fail") 155 } 156 157 _ = os.Setenv(scw.ScwAccessKeyEnv, "dummy") 158 _, err = NewScalewayProvider(context.TODO(), endpoint.NewDomainFilter([]string{"example.com"}), true) 159 if err == nil { 160 t.Errorf("expected to fail") 161 } 162 } 163 164 func TestScalewayProvider_OptionnalConfigFile(t *testing.T) { 165 _ = os.Setenv(scw.ScwAccessKeyEnv, "SCWXXXXXXXXXXXXXXXXX") 166 _ = os.Setenv(scw.ScwSecretKeyEnv, "11111111-1111-1111-1111-111111111111") 167 168 _, err := NewScalewayProvider(context.TODO(), endpoint.NewDomainFilter([]string{"example.com"}), true) 169 assert.NoError(t, err) 170 } 171 172 func TestScalewayProvider_AdjustEndpoints(t *testing.T) { 173 provider := &ScalewayProvider{} 174 175 before := []*endpoint.Endpoint{ 176 { 177 DNSName: "one.example.com", 178 RecordTTL: 300, 179 RecordType: "A", 180 Targets: []string{"1.1.1.1"}, 181 ProviderSpecific: endpoint.ProviderSpecific{ 182 { 183 Name: scalewayPriorityKey, 184 Value: "0", 185 }, 186 }, 187 }, 188 { 189 DNSName: "two.example.com", 190 RecordTTL: 0, 191 RecordType: "A", 192 Targets: []string{"1.1.1.1"}, 193 ProviderSpecific: endpoint.ProviderSpecific{ 194 { 195 Name: scalewayPriorityKey, 196 Value: "10", 197 }, 198 }, 199 }, 200 { 201 DNSName: "three.example.com", 202 RecordTTL: 600, 203 RecordType: "A", 204 Targets: []string{"1.1.1.1"}, 205 ProviderSpecific: endpoint.ProviderSpecific{}, 206 }, 207 } 208 209 expected := []*endpoint.Endpoint{ 210 { 211 DNSName: "one.example.com", 212 RecordTTL: 300, 213 RecordType: "A", 214 Targets: []string{"1.1.1.1"}, 215 ProviderSpecific: endpoint.ProviderSpecific{ 216 { 217 Name: scalewayPriorityKey, 218 Value: "0", 219 }, 220 }, 221 }, 222 { 223 DNSName: "two.example.com", 224 RecordTTL: 300, 225 RecordType: "A", 226 Targets: []string{"1.1.1.1"}, 227 ProviderSpecific: endpoint.ProviderSpecific{ 228 { 229 Name: scalewayPriorityKey, 230 Value: "10", 231 }, 232 }, 233 }, 234 { 235 DNSName: "three.example.com", 236 RecordTTL: 600, 237 RecordType: "A", 238 Targets: []string{"1.1.1.1"}, 239 ProviderSpecific: endpoint.ProviderSpecific{ 240 { 241 Name: scalewayPriorityKey, 242 Value: "0", 243 }, 244 }, 245 }, 246 } 247 248 after, err := provider.AdjustEndpoints(before) 249 assert.NoError(t, err) 250 for i := range after { 251 if !checkRecordEquality(after[i], expected[i]) { 252 t.Errorf("got record %s instead of %s", after[i], expected[i]) 253 } 254 } 255 } 256 257 func TestScalewayProvider_Zones(t *testing.T) { 258 mocked := mockScalewayDomain{nil} 259 provider := &ScalewayProvider{ 260 domainAPI: &mocked, 261 domainFilter: endpoint.NewDomainFilter([]string{"example.com"}), 262 } 263 264 expected := []*domain.DNSZone{ 265 { 266 Domain: "example.com", 267 Subdomain: "", 268 }, 269 { 270 Domain: "example.com", 271 Subdomain: "test", 272 }, 273 } 274 zones, err := provider.Zones(context.Background()) 275 if err != nil { 276 t.Fatal(err) 277 } 278 require.Len(t, zones, len(expected)) 279 for i, zone := range zones { 280 assert.Equal(t, expected[i], zone) 281 } 282 } 283 284 func TestScalewayProvider_Records(t *testing.T) { 285 mocked := mockScalewayDomain{nil} 286 provider := &ScalewayProvider{ 287 domainAPI: &mocked, 288 domainFilter: endpoint.NewDomainFilter([]string{"example.com"}), 289 } 290 291 expected := []*endpoint.Endpoint{ 292 { 293 DNSName: "one.example.com", 294 RecordTTL: 300, 295 RecordType: "A", 296 Targets: []string{"1.1.1.1"}, 297 ProviderSpecific: endpoint.ProviderSpecific{ 298 { 299 Name: scalewayPriorityKey, 300 Value: "0", 301 }, 302 }, 303 }, 304 { 305 DNSName: "two.example.com", 306 RecordTTL: 300, 307 RecordType: "A", 308 Targets: []string{"1.1.1.2", "1.1.1.3"}, 309 ProviderSpecific: endpoint.ProviderSpecific{ 310 { 311 Name: scalewayPriorityKey, 312 Value: "0", 313 }, 314 }, 315 }, 316 { 317 DNSName: "test.example.com", 318 RecordTTL: 300, 319 RecordType: "A", 320 Targets: []string{"1.1.1.1"}, 321 ProviderSpecific: endpoint.ProviderSpecific{ 322 { 323 Name: scalewayPriorityKey, 324 Value: "0", 325 }, 326 }, 327 }, 328 { 329 DNSName: "two.test.example.com", 330 RecordTTL: 600, 331 RecordType: "CNAME", 332 Targets: []string{"test.example.com"}, 333 ProviderSpecific: endpoint.ProviderSpecific{ 334 { 335 Name: scalewayPriorityKey, 336 Value: "30", 337 }, 338 }, 339 }, 340 } 341 342 records, err := provider.Records(context.TODO()) 343 if err != nil { 344 t.Fatal(err) 345 } 346 347 require.Len(t, records, len(expected)) 348 for _, record := range records { 349 found := false 350 for _, expectedRecord := range expected { 351 if checkRecordEquality(record, expectedRecord) { 352 found = true 353 } 354 } 355 assert.Equal(t, true, found) 356 } 357 } 358 359 // this test is really ugly since we are working on maps, so array are randomly sorted 360 // feel free to modify if you have a better idea 361 func TestScalewayProvider_generateApplyRequests(t *testing.T) { 362 mocked := mockScalewayDomain{nil} 363 provider := &ScalewayProvider{ 364 domainAPI: &mocked, 365 domainFilter: endpoint.NewDomainFilter([]string{"example.com"}), 366 } 367 368 expected := []*domain.UpdateDNSZoneRecordsRequest{ 369 { 370 DNSZone: "example.com", 371 Changes: []*domain.RecordChange{ 372 { 373 Add: &domain.RecordChangeAdd{ 374 Records: []*domain.Record{ 375 { 376 Data: "1.1.1.1", 377 Name: "", 378 TTL: 300, 379 Type: domain.RecordTypeA, 380 Priority: 0, 381 }, 382 { 383 Data: "1.1.1.2", 384 Name: "", 385 TTL: 300, 386 Type: domain.RecordTypeA, 387 Priority: 0, 388 }, 389 { 390 Data: "2.2.2.2", 391 Name: "me", 392 TTL: 600, 393 Type: domain.RecordTypeA, 394 Priority: 30, 395 }, 396 }, 397 }, 398 }, 399 { 400 Delete: &domain.RecordChangeDelete{ 401 IDFields: &domain.RecordIdentifier{ 402 Data: scw.StringPtr("3.3.3.3"), 403 Name: "me", 404 Type: domain.RecordTypeA, 405 }, 406 }, 407 }, 408 { 409 Delete: &domain.RecordChangeDelete{ 410 IDFields: &domain.RecordIdentifier{ 411 Data: scw.StringPtr("1.1.1.1"), 412 Name: "here", 413 Type: domain.RecordTypeA, 414 }, 415 }, 416 }, 417 { 418 Delete: &domain.RecordChangeDelete{ 419 IDFields: &domain.RecordIdentifier{ 420 Data: scw.StringPtr("1.1.1.2"), 421 Name: "here", 422 Type: domain.RecordTypeA, 423 }, 424 }, 425 }, 426 }, 427 }, 428 { 429 DNSZone: "test.example.com", 430 Changes: []*domain.RecordChange{ 431 { 432 Add: &domain.RecordChangeAdd{ 433 Records: []*domain.Record{ 434 { 435 Data: "example.com.", 436 Name: "", 437 TTL: 600, 438 Type: domain.RecordTypeCNAME, 439 Priority: 20, 440 }, 441 { 442 Data: "1.2.3.4", 443 Name: "my", 444 TTL: 300, 445 Type: domain.RecordTypeA, 446 Priority: 0, 447 }, 448 { 449 Data: "5.6.7.8", 450 Name: "my", 451 TTL: 300, 452 Type: domain.RecordTypeA, 453 Priority: 0, 454 }, 455 }, 456 }, 457 }, 458 { 459 Delete: &domain.RecordChangeDelete{ 460 IDFields: &domain.RecordIdentifier{ 461 Data: scw.StringPtr("1.1.1.1"), 462 Name: "here.is.my", 463 Type: domain.RecordTypeA, 464 }, 465 }, 466 }, 467 { 468 Delete: &domain.RecordChangeDelete{ 469 IDFields: &domain.RecordIdentifier{ 470 Data: scw.StringPtr("4.4.4.4"), 471 Name: "my", 472 Type: domain.RecordTypeA, 473 }, 474 }, 475 }, 476 { 477 Delete: &domain.RecordChangeDelete{ 478 IDFields: &domain.RecordIdentifier{ 479 Data: scw.StringPtr("5.5.5.5"), 480 Name: "my", 481 Type: domain.RecordTypeA, 482 }, 483 }, 484 }, 485 }, 486 }, 487 } 488 489 changes := &plan.Changes{ 490 Create: []*endpoint.Endpoint{ 491 { 492 DNSName: "example.com", 493 RecordType: "A", 494 Targets: []string{"1.1.1.1", "1.1.1.2"}, 495 }, 496 { 497 DNSName: "test.example.com", 498 RecordType: "CNAME", 499 ProviderSpecific: endpoint.ProviderSpecific{ 500 { 501 Name: scalewayPriorityKey, 502 Value: "20", 503 }, 504 }, 505 RecordTTL: 600, 506 Targets: []string{"example.com"}, 507 }, 508 }, 509 Delete: []*endpoint.Endpoint{ 510 { 511 DNSName: "here.example.com", 512 RecordType: "A", 513 Targets: []string{"1.1.1.1", "1.1.1.2"}, 514 }, 515 { 516 DNSName: "here.is.my.test.example.com", 517 RecordType: "A", 518 Targets: []string{"1.1.1.1"}, 519 }, 520 }, 521 UpdateNew: []*endpoint.Endpoint{ 522 { 523 DNSName: "me.example.com", 524 ProviderSpecific: endpoint.ProviderSpecific{ 525 { 526 Name: scalewayPriorityKey, 527 Value: "30", 528 }, 529 }, 530 RecordType: "A", 531 RecordTTL: 600, 532 Targets: []string{"2.2.2.2"}, 533 }, 534 { 535 DNSName: "my.test.example.com", 536 RecordType: "A", 537 Targets: []string{"1.2.3.4", "5.6.7.8"}, 538 }, 539 }, 540 UpdateOld: []*endpoint.Endpoint{ 541 { 542 DNSName: "me.example.com", 543 ProviderSpecific: endpoint.ProviderSpecific{ 544 { 545 Name: scalewayPriorityKey, 546 Value: "1234", 547 }, 548 }, 549 RecordType: "A", 550 Targets: []string{"3.3.3.3"}, 551 }, 552 { 553 DNSName: "my.test.example.com", 554 RecordType: "A", 555 Targets: []string{"4.4.4.4", "5.5.5.5"}, 556 }, 557 }, 558 } 559 560 requests, err := provider.generateApplyRequests(context.TODO(), changes) 561 if err != nil { 562 t.Fatal(err) 563 } 564 565 require.Len(t, requests, len(expected)) 566 total := int(len(expected)) 567 for _, req := range requests { 568 for _, exp := range expected { 569 if checkScalewayReqChanges(req, exp) { 570 total-- 571 } 572 } 573 } 574 assert.Equal(t, 0, total) 575 } 576 577 func checkRecordEquality(record1, record2 *endpoint.Endpoint) bool { 578 return record1.Targets.Same(record2.Targets) && 579 record1.DNSName == record2.DNSName && 580 record1.RecordTTL == record2.RecordTTL && 581 record1.RecordType == record2.RecordType && 582 reflect.DeepEqual(record1.ProviderSpecific, record2.ProviderSpecific) 583 } 584 585 func checkScalewayReqChanges(r1, r2 *domain.UpdateDNSZoneRecordsRequest) bool { 586 if r1.DNSZone != r2.DNSZone { 587 return false 588 } 589 if len(r1.Changes) != len(r2.Changes) { 590 return false 591 } 592 total := int(len(r1.Changes)) 593 for _, c1 := range r1.Changes { 594 for _, c2 := range r2.Changes { 595 // we only have 1 add per request 596 if c1.Add != nil && c2.Add != nil && checkScalewayRecords(c1.Add.Records, c2.Add.Records) { 597 total-- 598 } else if c1.Delete != nil && c2.Delete != nil { 599 if *c1.Delete.IDFields.Data == *c2.Delete.IDFields.Data && c1.Delete.IDFields.Name == c2.Delete.IDFields.Name && c1.Delete.IDFields.Type == c2.Delete.IDFields.Type { 600 total-- 601 } 602 } 603 } 604 } 605 return total == 0 606 } 607 608 func checkScalewayRecords(rs1, rs2 []*domain.Record) bool { 609 if len(rs1) != len(rs2) { 610 return false 611 } 612 total := int(len(rs1)) 613 for _, r1 := range rs1 { 614 for _, r2 := range rs2 { 615 if r1.Data == r2.Data && 616 r1.Name == r2.Name && 617 r1.Priority == r2.Priority && 618 r1.TTL == r2.TTL && 619 r1.Type == r2.Type { 620 total-- 621 } 622 } 623 } 624 return total == 0 625 }