sigs.k8s.io/external-dns@v0.14.1/plan/plan_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 plan 18 19 import ( 20 "testing" 21 22 "github.com/stretchr/testify/assert" 23 "github.com/stretchr/testify/suite" 24 25 "sigs.k8s.io/external-dns/endpoint" 26 "sigs.k8s.io/external-dns/internal/testutils" 27 ) 28 29 type PlanTestSuite struct { 30 suite.Suite 31 fooV1Cname *endpoint.Endpoint 32 fooV2Cname *endpoint.Endpoint 33 fooV2CnameUppercase *endpoint.Endpoint 34 fooV2TXT *endpoint.Endpoint 35 fooV2CnameNoLabel *endpoint.Endpoint 36 fooV3CnameSameResource *endpoint.Endpoint 37 fooA5 *endpoint.Endpoint 38 fooAAAA *endpoint.Endpoint 39 dsA *endpoint.Endpoint 40 dsAAAA *endpoint.Endpoint 41 bar127A *endpoint.Endpoint 42 bar127AWithTTL *endpoint.Endpoint 43 bar127AWithProviderSpecificTrue *endpoint.Endpoint 44 bar127AWithProviderSpecificFalse *endpoint.Endpoint 45 bar127AWithProviderSpecificUnset *endpoint.Endpoint 46 bar192A *endpoint.Endpoint 47 multiple1 *endpoint.Endpoint 48 multiple2 *endpoint.Endpoint 49 multiple3 *endpoint.Endpoint 50 domainFilterFiltered1 *endpoint.Endpoint 51 domainFilterFiltered2 *endpoint.Endpoint 52 domainFilterFiltered3 *endpoint.Endpoint 53 domainFilterExcluded *endpoint.Endpoint 54 } 55 56 func (suite *PlanTestSuite) SetupTest() { 57 suite.fooV1Cname = &endpoint.Endpoint{ 58 DNSName: "foo", 59 Targets: endpoint.Targets{"v1"}, 60 RecordType: "CNAME", 61 Labels: map[string]string{ 62 endpoint.ResourceLabelKey: "ingress/default/foo-v1", 63 endpoint.OwnerLabelKey: "pwner", 64 }, 65 } 66 // same resource as fooV1Cname, but target is different. It will never be picked because its target lexicographically bigger than "v1" 67 suite.fooV3CnameSameResource = &endpoint.Endpoint{ // TODO: remove this once endpoint can support multiple targets 68 DNSName: "foo", 69 Targets: endpoint.Targets{"v3"}, 70 RecordType: "CNAME", 71 Labels: map[string]string{ 72 endpoint.ResourceLabelKey: "ingress/default/foo-v1", 73 endpoint.OwnerLabelKey: "pwner", 74 }, 75 } 76 suite.fooV2Cname = &endpoint.Endpoint{ 77 DNSName: "foo", 78 Targets: endpoint.Targets{"v2"}, 79 RecordType: "CNAME", 80 Labels: map[string]string{ 81 endpoint.ResourceLabelKey: "ingress/default/foo-v2", 82 }, 83 } 84 suite.fooV2CnameUppercase = &endpoint.Endpoint{ 85 DNSName: "foo", 86 Targets: endpoint.Targets{"V2"}, 87 RecordType: "CNAME", 88 Labels: map[string]string{ 89 endpoint.ResourceLabelKey: "ingress/default/foo-v2", 90 }, 91 } 92 suite.fooV2TXT = &endpoint.Endpoint{ 93 DNSName: "foo", 94 RecordType: "TXT", 95 } 96 suite.fooV2CnameNoLabel = &endpoint.Endpoint{ 97 DNSName: "foo", 98 Targets: endpoint.Targets{"v2"}, 99 RecordType: "CNAME", 100 } 101 suite.fooA5 = &endpoint.Endpoint{ 102 DNSName: "foo", 103 Targets: endpoint.Targets{"5.5.5.5"}, 104 RecordType: "A", 105 Labels: map[string]string{ 106 endpoint.ResourceLabelKey: "ingress/default/foo-5", 107 }, 108 } 109 suite.fooAAAA = &endpoint.Endpoint{ 110 DNSName: "foo", 111 Targets: endpoint.Targets{"2001:DB8::1"}, 112 RecordType: "AAAA", 113 Labels: map[string]string{ 114 endpoint.ResourceLabelKey: "ingress/default/foo-AAAA", 115 }, 116 } 117 suite.dsA = &endpoint.Endpoint{ 118 DNSName: "ds", 119 Targets: endpoint.Targets{"1.1.1.1"}, 120 RecordType: "A", 121 Labels: map[string]string{ 122 endpoint.ResourceLabelKey: "ingress/default/ds", 123 }, 124 } 125 suite.dsAAAA = &endpoint.Endpoint{ 126 DNSName: "ds", 127 Targets: endpoint.Targets{"2001:DB8::1"}, 128 RecordType: "AAAA", 129 Labels: map[string]string{ 130 endpoint.ResourceLabelKey: "ingress/default/ds-AAAAA", 131 }, 132 } 133 suite.bar127A = &endpoint.Endpoint{ 134 DNSName: "bar", 135 Targets: endpoint.Targets{"127.0.0.1"}, 136 RecordType: "A", 137 Labels: map[string]string{ 138 endpoint.ResourceLabelKey: "ingress/default/bar-127", 139 }, 140 } 141 suite.bar127AWithTTL = &endpoint.Endpoint{ 142 DNSName: "bar", 143 Targets: endpoint.Targets{"127.0.0.1"}, 144 RecordType: "A", 145 RecordTTL: 300, 146 Labels: map[string]string{ 147 endpoint.ResourceLabelKey: "ingress/default/bar-127", 148 }, 149 } 150 suite.bar127AWithProviderSpecificTrue = &endpoint.Endpoint{ 151 DNSName: "bar", 152 Targets: endpoint.Targets{"127.0.0.1"}, 153 RecordType: "A", 154 Labels: map[string]string{ 155 endpoint.ResourceLabelKey: "ingress/default/bar-127", 156 }, 157 ProviderSpecific: endpoint.ProviderSpecific{ 158 endpoint.ProviderSpecificProperty{ 159 Name: "alias", 160 Value: "false", 161 }, 162 endpoint.ProviderSpecificProperty{ 163 Name: "external-dns.alpha.kubernetes.io/cloudflare-proxied", 164 Value: "true", 165 }, 166 }, 167 } 168 suite.bar127AWithProviderSpecificFalse = &endpoint.Endpoint{ 169 DNSName: "bar", 170 Targets: endpoint.Targets{"127.0.0.1"}, 171 RecordType: "A", 172 Labels: map[string]string{ 173 endpoint.ResourceLabelKey: "ingress/default/bar-127", 174 }, 175 ProviderSpecific: endpoint.ProviderSpecific{ 176 endpoint.ProviderSpecificProperty{ 177 Name: "external-dns.alpha.kubernetes.io/cloudflare-proxied", 178 Value: "false", 179 }, 180 endpoint.ProviderSpecificProperty{ 181 Name: "alias", 182 Value: "false", 183 }, 184 }, 185 } 186 suite.bar127AWithProviderSpecificUnset = &endpoint.Endpoint{ 187 DNSName: "bar", 188 Targets: endpoint.Targets{"127.0.0.1"}, 189 RecordType: "A", 190 Labels: map[string]string{ 191 endpoint.ResourceLabelKey: "ingress/default/bar-127", 192 }, 193 ProviderSpecific: endpoint.ProviderSpecific{ 194 endpoint.ProviderSpecificProperty{ 195 Name: "alias", 196 Value: "false", 197 }, 198 }, 199 } 200 suite.bar192A = &endpoint.Endpoint{ 201 DNSName: "bar", 202 Targets: endpoint.Targets{"192.168.0.1"}, 203 RecordType: "A", 204 Labels: map[string]string{ 205 endpoint.ResourceLabelKey: "ingress/default/bar-192", 206 }, 207 } 208 suite.multiple1 = &endpoint.Endpoint{ 209 DNSName: "multiple", 210 Targets: endpoint.Targets{"192.168.0.1"}, 211 RecordType: "A", 212 SetIdentifier: "test-set-1", 213 } 214 suite.multiple2 = &endpoint.Endpoint{ 215 DNSName: "multiple", 216 Targets: endpoint.Targets{"192.168.0.2"}, 217 RecordType: "A", 218 SetIdentifier: "test-set-1", 219 } 220 suite.multiple3 = &endpoint.Endpoint{ 221 DNSName: "multiple", 222 Targets: endpoint.Targets{"192.168.0.2"}, 223 RecordType: "A", 224 SetIdentifier: "test-set-2", 225 } 226 suite.domainFilterFiltered1 = &endpoint.Endpoint{ 227 DNSName: "foo.domain.tld", 228 Targets: endpoint.Targets{"1.2.3.4"}, 229 RecordType: "A", 230 } 231 suite.domainFilterFiltered2 = &endpoint.Endpoint{ 232 DNSName: "bar.domain.tld", 233 Targets: endpoint.Targets{"1.2.3.5"}, 234 RecordType: "A", 235 } 236 suite.domainFilterFiltered3 = &endpoint.Endpoint{ 237 DNSName: "baz.domain.tld", 238 Targets: endpoint.Targets{"1.2.3.6"}, 239 RecordType: "A", 240 } 241 suite.domainFilterExcluded = &endpoint.Endpoint{ 242 DNSName: "foo.ex.domain.tld", 243 Targets: endpoint.Targets{"1.1.1.1"}, 244 RecordType: "A", 245 } 246 } 247 248 func (suite *PlanTestSuite) TestSyncFirstRound() { 249 current := []*endpoint.Endpoint{} 250 desired := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooV2Cname, suite.bar127A} 251 expectedCreate := []*endpoint.Endpoint{suite.fooV1Cname, suite.bar127A} // v1 is chosen because of resolver taking "min" 252 expectedUpdateOld := []*endpoint.Endpoint{} 253 expectedUpdateNew := []*endpoint.Endpoint{} 254 expectedDelete := []*endpoint.Endpoint{} 255 256 p := &Plan{ 257 Policies: []Policy{&SyncPolicy{}}, 258 Current: current, 259 Desired: desired, 260 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, 261 } 262 263 changes := p.Calculate().Changes 264 validateEntries(suite.T(), changes.Create, expectedCreate) 265 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 266 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 267 validateEntries(suite.T(), changes.Delete, expectedDelete) 268 } 269 270 func (suite *PlanTestSuite) TestSyncSecondRound() { 271 current := []*endpoint.Endpoint{suite.fooV1Cname} 272 desired := []*endpoint.Endpoint{suite.fooV2Cname, suite.fooV1Cname, suite.bar127A} 273 expectedCreate := []*endpoint.Endpoint{suite.bar127A} 274 expectedUpdateOld := []*endpoint.Endpoint{} 275 expectedUpdateNew := []*endpoint.Endpoint{} 276 expectedDelete := []*endpoint.Endpoint{} 277 278 p := &Plan{ 279 Policies: []Policy{&SyncPolicy{}}, 280 Current: current, 281 Desired: desired, 282 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, 283 } 284 285 changes := p.Calculate().Changes 286 validateEntries(suite.T(), changes.Create, expectedCreate) 287 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 288 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 289 validateEntries(suite.T(), changes.Delete, expectedDelete) 290 } 291 292 func (suite *PlanTestSuite) TestSyncSecondRoundMigration() { 293 current := []*endpoint.Endpoint{suite.fooV2CnameNoLabel} 294 desired := []*endpoint.Endpoint{suite.fooV2Cname, suite.fooV1Cname, suite.bar127A} 295 expectedCreate := []*endpoint.Endpoint{suite.bar127A} 296 expectedUpdateOld := []*endpoint.Endpoint{suite.fooV2CnameNoLabel} 297 expectedUpdateNew := []*endpoint.Endpoint{suite.fooV1Cname} 298 expectedDelete := []*endpoint.Endpoint{} 299 300 p := &Plan{ 301 Policies: []Policy{&SyncPolicy{}}, 302 Current: current, 303 Desired: desired, 304 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, 305 } 306 307 changes := p.Calculate().Changes 308 validateEntries(suite.T(), changes.Create, expectedCreate) 309 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 310 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 311 validateEntries(suite.T(), changes.Delete, expectedDelete) 312 } 313 314 func (suite *PlanTestSuite) TestSyncSecondRoundWithTTLChange() { 315 current := []*endpoint.Endpoint{suite.bar127A} 316 desired := []*endpoint.Endpoint{suite.bar127AWithTTL} 317 expectedCreate := []*endpoint.Endpoint{} 318 expectedUpdateOld := []*endpoint.Endpoint{suite.bar127A} 319 expectedUpdateNew := []*endpoint.Endpoint{suite.bar127AWithTTL} 320 expectedDelete := []*endpoint.Endpoint{} 321 322 p := &Plan{ 323 Policies: []Policy{&SyncPolicy{}}, 324 Current: current, 325 Desired: desired, 326 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, 327 } 328 329 changes := p.Calculate().Changes 330 validateEntries(suite.T(), changes.Create, expectedCreate) 331 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 332 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 333 validateEntries(suite.T(), changes.Delete, expectedDelete) 334 } 335 336 func (suite *PlanTestSuite) TestSyncSecondRoundWithProviderSpecificChange() { 337 current := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificTrue} 338 desired := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificFalse} 339 expectedCreate := []*endpoint.Endpoint{} 340 expectedUpdateOld := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificTrue} 341 expectedUpdateNew := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificFalse} 342 expectedDelete := []*endpoint.Endpoint{} 343 344 p := &Plan{ 345 Policies: []Policy{&SyncPolicy{}}, 346 Current: current, 347 Desired: desired, 348 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, 349 } 350 351 changes := p.Calculate().Changes 352 validateEntries(suite.T(), changes.Create, expectedCreate) 353 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 354 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 355 validateEntries(suite.T(), changes.Delete, expectedDelete) 356 } 357 358 func (suite *PlanTestSuite) TestSyncSecondRoundWithProviderSpecificRemoval() { 359 current := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificFalse} 360 desired := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificUnset} 361 expectedCreate := []*endpoint.Endpoint{} 362 expectedUpdateOld := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificFalse} 363 expectedUpdateNew := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificUnset} 364 expectedDelete := []*endpoint.Endpoint{} 365 366 p := &Plan{ 367 Policies: []Policy{&SyncPolicy{}}, 368 Current: current, 369 Desired: desired, 370 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, 371 } 372 373 changes := p.Calculate().Changes 374 validateEntries(suite.T(), changes.Create, expectedCreate) 375 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 376 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 377 validateEntries(suite.T(), changes.Delete, expectedDelete) 378 } 379 380 func (suite *PlanTestSuite) TestSyncSecondRoundWithProviderSpecificAddition() { 381 current := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificUnset} 382 desired := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificTrue} 383 expectedCreate := []*endpoint.Endpoint{} 384 expectedUpdateOld := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificUnset} 385 expectedUpdateNew := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificTrue} 386 expectedDelete := []*endpoint.Endpoint{} 387 388 p := &Plan{ 389 Policies: []Policy{&SyncPolicy{}}, 390 Current: current, 391 Desired: desired, 392 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, 393 } 394 395 changes := p.Calculate().Changes 396 validateEntries(suite.T(), changes.Create, expectedCreate) 397 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 398 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 399 validateEntries(suite.T(), changes.Delete, expectedDelete) 400 } 401 402 func (suite *PlanTestSuite) TestSyncSecondRoundWithOwnerInherited() { 403 current := []*endpoint.Endpoint{suite.fooV1Cname} 404 desired := []*endpoint.Endpoint{suite.fooV2Cname} 405 406 expectedCreate := []*endpoint.Endpoint{} 407 expectedUpdateOld := []*endpoint.Endpoint{suite.fooV1Cname} 408 expectedUpdateNew := []*endpoint.Endpoint{{ 409 DNSName: suite.fooV2Cname.DNSName, 410 Targets: suite.fooV2Cname.Targets, 411 RecordType: suite.fooV2Cname.RecordType, 412 RecordTTL: suite.fooV2Cname.RecordTTL, 413 Labels: map[string]string{ 414 endpoint.ResourceLabelKey: suite.fooV2Cname.Labels[endpoint.ResourceLabelKey], 415 endpoint.OwnerLabelKey: suite.fooV1Cname.Labels[endpoint.OwnerLabelKey], 416 }, 417 }} 418 expectedDelete := []*endpoint.Endpoint{} 419 420 p := &Plan{ 421 Policies: []Policy{&SyncPolicy{}}, 422 Current: current, 423 Desired: desired, 424 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, 425 } 426 427 changes := p.Calculate().Changes 428 validateEntries(suite.T(), changes.Create, expectedCreate) 429 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 430 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 431 validateEntries(suite.T(), changes.Delete, expectedDelete) 432 } 433 434 func (suite *PlanTestSuite) TestIdempotency() { 435 current := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooV2Cname} 436 desired := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooV2Cname} 437 expectedCreate := []*endpoint.Endpoint{} 438 expectedUpdateOld := []*endpoint.Endpoint{} 439 expectedUpdateNew := []*endpoint.Endpoint{} 440 expectedDelete := []*endpoint.Endpoint{} 441 442 p := &Plan{ 443 Policies: []Policy{&SyncPolicy{}}, 444 Current: current, 445 Desired: desired, 446 } 447 448 changes := p.Calculate().Changes 449 validateEntries(suite.T(), changes.Create, expectedCreate) 450 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 451 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 452 validateEntries(suite.T(), changes.Delete, expectedDelete) 453 } 454 455 func (suite *PlanTestSuite) TestRecordTypeChange() { 456 current := []*endpoint.Endpoint{suite.fooV1Cname} 457 desired := []*endpoint.Endpoint{suite.fooA5} 458 expectedCreate := []*endpoint.Endpoint{suite.fooA5} 459 expectedUpdateOld := []*endpoint.Endpoint{} 460 expectedUpdateNew := []*endpoint.Endpoint{} 461 expectedDelete := []*endpoint.Endpoint{suite.fooV1Cname} 462 463 p := &Plan{ 464 Policies: []Policy{&SyncPolicy{}}, 465 Current: current, 466 Desired: desired, 467 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}, 468 OwnerID: suite.fooV1Cname.Labels[endpoint.OwnerLabelKey], 469 } 470 471 changes := p.Calculate().Changes 472 validateEntries(suite.T(), changes.Create, expectedCreate) 473 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 474 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 475 validateEntries(suite.T(), changes.Delete, expectedDelete) 476 } 477 478 func (suite *PlanTestSuite) TestExistingCNameWithDualStackDesired() { 479 current := []*endpoint.Endpoint{suite.fooV1Cname} 480 desired := []*endpoint.Endpoint{suite.fooA5, suite.fooAAAA} 481 expectedCreate := []*endpoint.Endpoint{suite.fooA5, suite.fooAAAA} 482 expectedUpdateOld := []*endpoint.Endpoint{} 483 expectedUpdateNew := []*endpoint.Endpoint{} 484 expectedDelete := []*endpoint.Endpoint{suite.fooV1Cname} 485 486 p := &Plan{ 487 Policies: []Policy{&SyncPolicy{}}, 488 Current: current, 489 Desired: desired, 490 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}, 491 OwnerID: suite.fooV1Cname.Labels[endpoint.OwnerLabelKey], 492 } 493 494 changes := p.Calculate().Changes 495 validateEntries(suite.T(), changes.Create, expectedCreate) 496 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 497 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 498 validateEntries(suite.T(), changes.Delete, expectedDelete) 499 } 500 501 func (suite *PlanTestSuite) TestExistingDualStackWithCNameDesired() { 502 suite.fooA5.Labels[endpoint.OwnerLabelKey] = "nerf" 503 suite.fooAAAA.Labels[endpoint.OwnerLabelKey] = "nerf" 504 current := []*endpoint.Endpoint{suite.fooA5, suite.fooAAAA} 505 desired := []*endpoint.Endpoint{suite.fooV2Cname} 506 expectedCreate := []*endpoint.Endpoint{suite.fooV2Cname} 507 expectedUpdateOld := []*endpoint.Endpoint{} 508 expectedUpdateNew := []*endpoint.Endpoint{} 509 expectedDelete := []*endpoint.Endpoint{suite.fooA5, suite.fooAAAA} 510 511 p := &Plan{ 512 Policies: []Policy{&SyncPolicy{}}, 513 Current: current, 514 Desired: desired, 515 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}, 516 OwnerID: suite.fooA5.Labels[endpoint.OwnerLabelKey], 517 } 518 519 changes := p.Calculate().Changes 520 validateEntries(suite.T(), changes.Create, expectedCreate) 521 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 522 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 523 validateEntries(suite.T(), changes.Delete, expectedDelete) 524 } 525 526 // TestExistingOwnerNotMatchingDualStackDesired validates that if there is an existing 527 // record for a domain but there is no ownership claim over it and there are desired 528 // records no changes are planed. Only domains that have explicit ownership claims should 529 // be updated. 530 func (suite *PlanTestSuite) TestExistingOwnerNotMatchingDualStackDesired() { 531 suite.fooA5.Labels = nil 532 current := []*endpoint.Endpoint{suite.fooA5} 533 desired := []*endpoint.Endpoint{suite.fooV2Cname} 534 expectedCreate := []*endpoint.Endpoint{} 535 expectedUpdateOld := []*endpoint.Endpoint{} 536 expectedUpdateNew := []*endpoint.Endpoint{} 537 expectedDelete := []*endpoint.Endpoint{} 538 539 p := &Plan{ 540 Policies: []Policy{&SyncPolicy{}}, 541 Current: current, 542 Desired: desired, 543 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}, 544 OwnerID: "pwner", 545 } 546 547 changes := p.Calculate().Changes 548 validateEntries(suite.T(), changes.Create, expectedCreate) 549 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 550 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 551 validateEntries(suite.T(), changes.Delete, expectedDelete) 552 } 553 554 // TestConflictingCurrentNonConflictingDesired is a bit of a corner case as it would indicate 555 // that the provider is not following valid DNS rules or there may be some 556 // caching issues. In this case since the desired records are not conflicting 557 // the updates will end up with the conflict resolved. 558 func (suite *PlanTestSuite) TestConflictingCurrentNonConflictingDesired() { 559 suite.fooA5.Labels[endpoint.OwnerLabelKey] = suite.fooV1Cname.Labels[endpoint.OwnerLabelKey] 560 current := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooA5} 561 desired := []*endpoint.Endpoint{suite.fooA5} 562 expectedCreate := []*endpoint.Endpoint{} 563 expectedUpdateOld := []*endpoint.Endpoint{} 564 expectedUpdateNew := []*endpoint.Endpoint{} 565 expectedDelete := []*endpoint.Endpoint{suite.fooV1Cname} 566 567 p := &Plan{ 568 Policies: []Policy{&SyncPolicy{}}, 569 Current: current, 570 Desired: desired, 571 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}, 572 OwnerID: suite.fooV1Cname.Labels[endpoint.OwnerLabelKey], 573 } 574 575 changes := p.Calculate().Changes 576 validateEntries(suite.T(), changes.Create, expectedCreate) 577 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 578 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 579 validateEntries(suite.T(), changes.Delete, expectedDelete) 580 } 581 582 // TestConflictingCurrentNoDesired is a bit of a corner case as it would indicate 583 // that the provider is not following valid DNS rules or there may be some 584 // caching issues. In this case there are no desired enpoint candidates so plan 585 // on deleting the records. 586 func (suite *PlanTestSuite) TestConflictingCurrentNoDesired() { 587 suite.fooA5.Labels[endpoint.OwnerLabelKey] = suite.fooV1Cname.Labels[endpoint.OwnerLabelKey] 588 current := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooA5} 589 desired := []*endpoint.Endpoint{} 590 expectedCreate := []*endpoint.Endpoint{} 591 expectedUpdateOld := []*endpoint.Endpoint{} 592 expectedUpdateNew := []*endpoint.Endpoint{} 593 expectedDelete := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooA5} 594 595 p := &Plan{ 596 Policies: []Policy{&SyncPolicy{}}, 597 Current: current, 598 Desired: desired, 599 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}, 600 OwnerID: suite.fooV1Cname.Labels[endpoint.OwnerLabelKey], 601 } 602 603 changes := p.Calculate().Changes 604 validateEntries(suite.T(), changes.Create, expectedCreate) 605 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 606 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 607 validateEntries(suite.T(), changes.Delete, expectedDelete) 608 } 609 610 // TestCurrentWithConflictingDesired simulates where the desired records result in conflicting records types. 611 // This could be the result of multiple sources generating conflicting records types. In this case the conflict 612 // resolver should prefer the A and AAAA record candidate and delete the other records. 613 func (suite *PlanTestSuite) TestCurrentWithConflictingDesired() { 614 suite.fooV1Cname.Labels[endpoint.OwnerLabelKey] = "nerf" 615 current := []*endpoint.Endpoint{suite.fooV1Cname} 616 desired := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooA5, suite.fooAAAA} 617 expectedCreate := []*endpoint.Endpoint{suite.fooA5, suite.fooAAAA} 618 expectedUpdateOld := []*endpoint.Endpoint{} 619 expectedUpdateNew := []*endpoint.Endpoint{} 620 expectedDelete := []*endpoint.Endpoint{suite.fooV1Cname} 621 622 p := &Plan{ 623 Policies: []Policy{&SyncPolicy{}}, 624 Current: current, 625 Desired: desired, 626 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}, 627 OwnerID: suite.fooV1Cname.Labels[endpoint.OwnerLabelKey], 628 } 629 630 changes := p.Calculate().Changes 631 validateEntries(suite.T(), changes.Create, expectedCreate) 632 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 633 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 634 validateEntries(suite.T(), changes.Delete, expectedDelete) 635 } 636 637 // TestNoCurrentWithConflictingDesired simulates where the desired records result in conflicting records types. 638 // This could be the result of multiple sources generating conflicting records types. In this case there the 639 // conflict resolver should prefer the A and AAAA record and drop the other candidate record types. 640 func (suite *PlanTestSuite) TestNoCurrentWithConflictingDesired() { 641 current := []*endpoint.Endpoint{} 642 desired := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooA5, suite.fooAAAA} 643 expectedCreate := []*endpoint.Endpoint{suite.fooA5, suite.fooAAAA} 644 expectedUpdateOld := []*endpoint.Endpoint{} 645 expectedUpdateNew := []*endpoint.Endpoint{} 646 expectedDelete := []*endpoint.Endpoint{} 647 648 p := &Plan{ 649 Policies: []Policy{&SyncPolicy{}}, 650 Current: current, 651 Desired: desired, 652 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}, 653 } 654 655 changes := p.Calculate().Changes 656 validateEntries(suite.T(), changes.Create, expectedCreate) 657 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 658 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 659 validateEntries(suite.T(), changes.Delete, expectedDelete) 660 } 661 662 func (suite *PlanTestSuite) TestIgnoreTXT() { 663 current := []*endpoint.Endpoint{suite.fooV2TXT} 664 desired := []*endpoint.Endpoint{suite.fooV2Cname} 665 expectedCreate := []*endpoint.Endpoint{suite.fooV2Cname} 666 expectedUpdateOld := []*endpoint.Endpoint{} 667 expectedUpdateNew := []*endpoint.Endpoint{} 668 expectedDelete := []*endpoint.Endpoint{} 669 670 p := &Plan{ 671 Policies: []Policy{&SyncPolicy{}}, 672 Current: current, 673 Desired: desired, 674 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, 675 } 676 677 changes := p.Calculate().Changes 678 validateEntries(suite.T(), changes.Create, expectedCreate) 679 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 680 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 681 validateEntries(suite.T(), changes.Delete, expectedDelete) 682 } 683 684 func (suite *PlanTestSuite) TestExcludeTXT() { 685 current := []*endpoint.Endpoint{suite.fooV2TXT} 686 desired := []*endpoint.Endpoint{suite.fooV2Cname} 687 expectedCreate := []*endpoint.Endpoint{suite.fooV2Cname} 688 expectedUpdateOld := []*endpoint.Endpoint{} 689 expectedUpdateNew := []*endpoint.Endpoint{} 690 expectedDelete := []*endpoint.Endpoint{} 691 692 p := &Plan{ 693 Policies: []Policy{&SyncPolicy{}}, 694 Current: current, 695 Desired: desired, 696 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME, endpoint.RecordTypeTXT}, 697 ExcludeRecords: []string{endpoint.RecordTypeTXT}, 698 } 699 700 changes := p.Calculate().Changes 701 validateEntries(suite.T(), changes.Create, expectedCreate) 702 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 703 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 704 validateEntries(suite.T(), changes.Delete, expectedDelete) 705 } 706 707 func (suite *PlanTestSuite) TestIgnoreTargetCase() { 708 current := []*endpoint.Endpoint{suite.fooV2Cname} 709 desired := []*endpoint.Endpoint{suite.fooV2CnameUppercase} 710 expectedCreate := []*endpoint.Endpoint{} 711 expectedUpdateOld := []*endpoint.Endpoint{} 712 expectedUpdateNew := []*endpoint.Endpoint{} 713 expectedDelete := []*endpoint.Endpoint{} 714 715 p := &Plan{ 716 Policies: []Policy{&SyncPolicy{}}, 717 Current: current, 718 Desired: desired, 719 } 720 721 changes := p.Calculate().Changes 722 validateEntries(suite.T(), changes.Create, expectedCreate) 723 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 724 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 725 validateEntries(suite.T(), changes.Delete, expectedDelete) 726 } 727 728 func (suite *PlanTestSuite) TestRemoveEndpoint() { 729 current := []*endpoint.Endpoint{suite.fooV1Cname, suite.bar192A} 730 desired := []*endpoint.Endpoint{suite.fooV1Cname} 731 expectedCreate := []*endpoint.Endpoint{} 732 expectedUpdateOld := []*endpoint.Endpoint{} 733 expectedUpdateNew := []*endpoint.Endpoint{} 734 expectedDelete := []*endpoint.Endpoint{suite.bar192A} 735 736 p := &Plan{ 737 Policies: []Policy{&SyncPolicy{}}, 738 Current: current, 739 Desired: desired, 740 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, 741 } 742 743 changes := p.Calculate().Changes 744 validateEntries(suite.T(), changes.Create, expectedCreate) 745 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 746 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 747 validateEntries(suite.T(), changes.Delete, expectedDelete) 748 } 749 750 func (suite *PlanTestSuite) TestRemoveEndpointWithUpsert() { 751 current := []*endpoint.Endpoint{suite.fooV1Cname, suite.bar192A} 752 desired := []*endpoint.Endpoint{suite.fooV1Cname} 753 expectedCreate := []*endpoint.Endpoint{} 754 expectedUpdateOld := []*endpoint.Endpoint{} 755 expectedUpdateNew := []*endpoint.Endpoint{} 756 expectedDelete := []*endpoint.Endpoint{} 757 758 p := &Plan{ 759 Policies: []Policy{&UpsertOnlyPolicy{}}, 760 Current: current, 761 Desired: desired, 762 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, 763 } 764 765 changes := p.Calculate().Changes 766 validateEntries(suite.T(), changes.Create, expectedCreate) 767 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 768 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 769 validateEntries(suite.T(), changes.Delete, expectedDelete) 770 } 771 772 func (suite *PlanTestSuite) TestMultipleRecordsSameNameDifferentSetIdentifier() { 773 current := []*endpoint.Endpoint{suite.multiple1} 774 desired := []*endpoint.Endpoint{suite.multiple2, suite.multiple3} 775 expectedCreate := []*endpoint.Endpoint{suite.multiple3} 776 expectedUpdateOld := []*endpoint.Endpoint{suite.multiple1} 777 expectedUpdateNew := []*endpoint.Endpoint{suite.multiple2} 778 expectedDelete := []*endpoint.Endpoint{} 779 780 p := &Plan{ 781 Policies: []Policy{&SyncPolicy{}}, 782 Current: current, 783 Desired: desired, 784 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, 785 } 786 787 changes := p.Calculate().Changes 788 validateEntries(suite.T(), changes.Create, expectedCreate) 789 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 790 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 791 validateEntries(suite.T(), changes.Delete, expectedDelete) 792 } 793 794 func (suite *PlanTestSuite) TestSetIdentifierUpdateCreatesAndDeletes() { 795 current := []*endpoint.Endpoint{suite.multiple2} 796 desired := []*endpoint.Endpoint{suite.multiple3} 797 expectedCreate := []*endpoint.Endpoint{suite.multiple3} 798 expectedUpdateOld := []*endpoint.Endpoint{} 799 expectedUpdateNew := []*endpoint.Endpoint{} 800 expectedDelete := []*endpoint.Endpoint{suite.multiple2} 801 802 p := &Plan{ 803 Policies: []Policy{&SyncPolicy{}}, 804 Current: current, 805 Desired: desired, 806 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, 807 } 808 809 changes := p.Calculate().Changes 810 validateEntries(suite.T(), changes.Create, expectedCreate) 811 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 812 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 813 validateEntries(suite.T(), changes.Delete, expectedDelete) 814 } 815 816 func (suite *PlanTestSuite) TestDomainFiltersInitial() { 817 current := []*endpoint.Endpoint{suite.domainFilterExcluded} 818 desired := []*endpoint.Endpoint{suite.domainFilterExcluded, suite.domainFilterFiltered1, suite.domainFilterFiltered2, suite.domainFilterFiltered3} 819 expectedCreate := []*endpoint.Endpoint{suite.domainFilterFiltered1, suite.domainFilterFiltered2, suite.domainFilterFiltered3} 820 expectedUpdateOld := []*endpoint.Endpoint{} 821 expectedUpdateNew := []*endpoint.Endpoint{} 822 expectedDelete := []*endpoint.Endpoint{} 823 824 domainFilter := endpoint.NewDomainFilterWithExclusions([]string{"domain.tld"}, []string{"ex.domain.tld"}) 825 p := &Plan{ 826 Policies: []Policy{&SyncPolicy{}}, 827 Current: current, 828 Desired: desired, 829 DomainFilter: endpoint.MatchAllDomainFilters{&domainFilter}, 830 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, 831 } 832 833 changes := p.Calculate().Changes 834 validateEntries(suite.T(), changes.Create, expectedCreate) 835 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 836 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 837 validateEntries(suite.T(), changes.Delete, expectedDelete) 838 } 839 840 func (suite *PlanTestSuite) TestDomainFiltersUpdate() { 841 current := []*endpoint.Endpoint{suite.domainFilterExcluded, suite.domainFilterFiltered1, suite.domainFilterFiltered2} 842 desired := []*endpoint.Endpoint{suite.domainFilterExcluded, suite.domainFilterFiltered1, suite.domainFilterFiltered2, suite.domainFilterFiltered3} 843 expectedCreate := []*endpoint.Endpoint{suite.domainFilterFiltered3} 844 expectedUpdateOld := []*endpoint.Endpoint{} 845 expectedUpdateNew := []*endpoint.Endpoint{} 846 expectedDelete := []*endpoint.Endpoint{} 847 848 domainFilter := endpoint.NewDomainFilterWithExclusions([]string{"domain.tld"}, []string{"ex.domain.tld"}) 849 p := &Plan{ 850 Policies: []Policy{&SyncPolicy{}}, 851 Current: current, 852 Desired: desired, 853 DomainFilter: endpoint.MatchAllDomainFilters{&domainFilter}, 854 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, 855 } 856 857 changes := p.Calculate().Changes 858 validateEntries(suite.T(), changes.Create, expectedCreate) 859 validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) 860 validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) 861 validateEntries(suite.T(), changes.Delete, expectedDelete) 862 } 863 864 func (suite *PlanTestSuite) TestAAAARecords() { 865 current := []*endpoint.Endpoint{} 866 desired := []*endpoint.Endpoint{suite.fooAAAA} 867 expectedCreate := []*endpoint.Endpoint{suite.fooAAAA} 868 expectNoChanges := []*endpoint.Endpoint{} 869 870 p := &Plan{ 871 Policies: []Policy{&SyncPolicy{}}, 872 Current: current, 873 Desired: desired, 874 ManagedRecords: []string{endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}, 875 } 876 877 changes := p.Calculate().Changes 878 validateEntries(suite.T(), changes.Create, expectedCreate) 879 validateEntries(suite.T(), changes.Delete, expectNoChanges) 880 validateEntries(suite.T(), changes.UpdateOld, expectNoChanges) 881 validateEntries(suite.T(), changes.UpdateNew, expectNoChanges) 882 } 883 884 func (suite *PlanTestSuite) TestDualStackRecords() { 885 current := []*endpoint.Endpoint{} 886 desired := []*endpoint.Endpoint{suite.dsA, suite.dsAAAA} 887 expectedCreate := []*endpoint.Endpoint{suite.dsA, suite.dsAAAA} 888 expectNoChanges := []*endpoint.Endpoint{} 889 890 p := &Plan{ 891 Policies: []Policy{&SyncPolicy{}}, 892 Current: current, 893 Desired: desired, 894 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}, 895 } 896 897 changes := p.Calculate().Changes 898 validateEntries(suite.T(), changes.Create, expectedCreate) 899 validateEntries(suite.T(), changes.Delete, expectNoChanges) 900 validateEntries(suite.T(), changes.UpdateOld, expectNoChanges) 901 validateEntries(suite.T(), changes.UpdateNew, expectNoChanges) 902 } 903 904 func (suite *PlanTestSuite) TestDualStackRecordsDelete() { 905 current := []*endpoint.Endpoint{suite.dsA, suite.dsAAAA} 906 desired := []*endpoint.Endpoint{} 907 expectedDelete := []*endpoint.Endpoint{suite.dsA, suite.dsAAAA} 908 expectNoChanges := []*endpoint.Endpoint{} 909 910 p := &Plan{ 911 Policies: []Policy{&SyncPolicy{}}, 912 Current: current, 913 Desired: desired, 914 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}, 915 } 916 917 changes := p.Calculate().Changes 918 validateEntries(suite.T(), changes.Delete, expectedDelete) 919 validateEntries(suite.T(), changes.Create, expectNoChanges) 920 validateEntries(suite.T(), changes.UpdateOld, expectNoChanges) 921 validateEntries(suite.T(), changes.UpdateNew, expectNoChanges) 922 } 923 924 func (suite *PlanTestSuite) TestDualStackToSingleStack() { 925 current := []*endpoint.Endpoint{suite.dsA, suite.dsAAAA} 926 desired := []*endpoint.Endpoint{suite.dsA} 927 expectedDelete := []*endpoint.Endpoint{suite.dsAAAA} 928 expectNoChanges := []*endpoint.Endpoint{} 929 930 p := &Plan{ 931 Policies: []Policy{&SyncPolicy{}}, 932 Current: current, 933 Desired: desired, 934 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}, 935 } 936 937 changes := p.Calculate().Changes 938 validateEntries(suite.T(), changes.Delete, expectedDelete) 939 validateEntries(suite.T(), changes.Create, expectNoChanges) 940 validateEntries(suite.T(), changes.UpdateOld, expectNoChanges) 941 validateEntries(suite.T(), changes.UpdateNew, expectNoChanges) 942 } 943 944 func TestPlan(t *testing.T) { 945 suite.Run(t, new(PlanTestSuite)) 946 } 947 948 // validateEntries validates that the list of entries matches expected. 949 func validateEntries(t *testing.T, entries, expected []*endpoint.Endpoint) { 950 if !testutils.SameEndpoints(entries, expected) { 951 t.Fatalf("expected %q to match %q", entries, expected) 952 } 953 } 954 955 func TestNormalizeDNSName(t *testing.T) { 956 records := []struct { 957 dnsName string 958 expect string 959 }{ 960 { 961 "3AAAA.FOO.BAR.COM ", 962 "3aaaa.foo.bar.com.", 963 }, 964 { 965 " example.foo.com.", 966 "example.foo.com.", 967 }, 968 { 969 "example123.foo.com ", 970 "example123.foo.com.", 971 }, 972 { 973 "foo", 974 "foo.", 975 }, 976 { 977 "123foo.bar", 978 "123foo.bar.", 979 }, 980 { 981 "foo.com", 982 "foo.com.", 983 }, 984 { 985 "foo.com.", 986 "foo.com.", 987 }, 988 { 989 "foo123.COM", 990 "foo123.com.", 991 }, 992 { 993 "my-exaMple3.FOO.BAR.COM", 994 "my-example3.foo.bar.com.", 995 }, 996 { 997 " my-example1214.FOO-1235.BAR-foo.COM ", 998 "my-example1214.foo-1235.bar-foo.com.", 999 }, 1000 { 1001 "my-example-my-example-1214.FOO-1235.BAR-foo.COM", 1002 "my-example-my-example-1214.foo-1235.bar-foo.com.", 1003 }, 1004 } 1005 for _, r := range records { 1006 gotName := normalizeDNSName(r.dnsName) 1007 assert.Equal(t, r.expect, gotName) 1008 } 1009 } 1010 1011 func TestShouldUpdateProviderSpecific(tt *testing.T) { 1012 for _, test := range []struct { 1013 name string 1014 current *endpoint.Endpoint 1015 desired *endpoint.Endpoint 1016 shouldUpdate bool 1017 }{ 1018 { 1019 name: "skip AWS target health", 1020 current: &endpoint.Endpoint{ 1021 DNSName: "foo.com", 1022 ProviderSpecific: []endpoint.ProviderSpecificProperty{ 1023 {Name: "aws/evaluate-target-health", Value: "true"}, 1024 }, 1025 }, 1026 desired: &endpoint.Endpoint{ 1027 DNSName: "bar.com", 1028 ProviderSpecific: []endpoint.ProviderSpecificProperty{ 1029 {Name: "aws/evaluate-target-health", Value: "true"}, 1030 }, 1031 }, 1032 shouldUpdate: false, 1033 }, 1034 { 1035 name: "custom property unchanged", 1036 current: &endpoint.Endpoint{ 1037 ProviderSpecific: []endpoint.ProviderSpecificProperty{ 1038 {Name: "custom/property", Value: "true"}, 1039 }, 1040 }, 1041 desired: &endpoint.Endpoint{ 1042 ProviderSpecific: []endpoint.ProviderSpecificProperty{ 1043 {Name: "custom/property", Value: "true"}, 1044 }, 1045 }, 1046 shouldUpdate: false, 1047 }, 1048 { 1049 name: "custom property value changed", 1050 current: &endpoint.Endpoint{ 1051 ProviderSpecific: []endpoint.ProviderSpecificProperty{ 1052 {Name: "custom/property", Value: "true"}, 1053 }, 1054 }, 1055 desired: &endpoint.Endpoint{ 1056 ProviderSpecific: []endpoint.ProviderSpecificProperty{ 1057 {Name: "custom/property", Value: "false"}, 1058 }, 1059 }, 1060 shouldUpdate: true, 1061 }, 1062 { 1063 name: "custom property key changed", 1064 current: &endpoint.Endpoint{ 1065 ProviderSpecific: []endpoint.ProviderSpecificProperty{ 1066 {Name: "custom/property", Value: "true"}, 1067 }, 1068 }, 1069 desired: &endpoint.Endpoint{ 1070 ProviderSpecific: []endpoint.ProviderSpecificProperty{ 1071 {Name: "new/property", Value: "true"}, 1072 }, 1073 }, 1074 shouldUpdate: true, 1075 }, 1076 } { 1077 tt.Run(test.name, func(t *testing.T) { 1078 plan := &Plan{ 1079 Current: []*endpoint.Endpoint{test.current}, 1080 Desired: []*endpoint.Endpoint{test.desired}, 1081 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, 1082 } 1083 b := plan.shouldUpdateProviderSpecific(test.desired, test.current) 1084 assert.Equal(t, test.shouldUpdate, b) 1085 }) 1086 } 1087 }