sigs.k8s.io/external-dns@v0.14.1/registry/txt_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 registry 18 19 import ( 20 "context" 21 "reflect" 22 "strings" 23 "testing" 24 "time" 25 26 "github.com/stretchr/testify/assert" 27 "github.com/stretchr/testify/require" 28 29 "sigs.k8s.io/external-dns/endpoint" 30 "sigs.k8s.io/external-dns/internal/testutils" 31 "sigs.k8s.io/external-dns/plan" 32 "sigs.k8s.io/external-dns/provider" 33 "sigs.k8s.io/external-dns/provider/inmemory" 34 ) 35 36 const ( 37 testZone = "test-zone.example.org" 38 ) 39 40 func TestTXTRegistry(t *testing.T) { 41 t.Run("TestNewTXTRegistry", testTXTRegistryNew) 42 t.Run("TestRecords", testTXTRegistryRecords) 43 t.Run("TestApplyChanges", testTXTRegistryApplyChanges) 44 t.Run("TestMissingRecords", testTXTRegistryMissingRecords) 45 } 46 47 func testTXTRegistryNew(t *testing.T) { 48 p := inmemory.NewInMemoryProvider() 49 _, err := NewTXTRegistry(p, "txt", "", "", time.Hour, "", []string{}, []string{}, false, nil) 50 require.Error(t, err) 51 52 _, err = NewTXTRegistry(p, "", "txt", "", time.Hour, "", []string{}, []string{}, false, nil) 53 require.Error(t, err) 54 55 r, err := NewTXTRegistry(p, "txt", "", "owner", time.Hour, "", []string{}, []string{}, false, nil) 56 require.NoError(t, err) 57 assert.Equal(t, p, r.provider) 58 59 r, err = NewTXTRegistry(p, "", "txt", "owner", time.Hour, "", []string{}, []string{}, false, nil) 60 require.NoError(t, err) 61 62 _, err = NewTXTRegistry(p, "txt", "txt", "owner", time.Hour, "", []string{}, []string{}, false, nil) 63 require.Error(t, err) 64 65 _, ok := r.mapper.(affixNameMapper) 66 require.True(t, ok) 67 assert.Equal(t, "owner", r.ownerID) 68 assert.Equal(t, p, r.provider) 69 70 aesKey := []byte(";k&l)nUC/33:{?d{3)54+,AD?]SX%yh^") 71 _, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil) 72 require.NoError(t, err) 73 74 _, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, aesKey) 75 require.NoError(t, err) 76 77 _, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, nil) 78 require.Error(t, err) 79 80 r, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, true, aesKey) 81 require.NoError(t, err) 82 83 _, ok = r.mapper.(affixNameMapper) 84 assert.True(t, ok) 85 } 86 87 func testTXTRegistryRecords(t *testing.T) { 88 t.Run("With prefix", testTXTRegistryRecordsPrefixed) 89 t.Run("With suffix", testTXTRegistryRecordsSuffixed) 90 t.Run("No prefix", testTXTRegistryRecordsNoPrefix) 91 t.Run("With templated prefix", testTXTRegistryRecordsPrefixedTemplated) 92 t.Run("With templated suffix", testTXTRegistryRecordsSuffixedTemplated) 93 } 94 95 func testTXTRegistryRecordsPrefixed(t *testing.T) { 96 ctx := context.Background() 97 p := inmemory.NewInMemoryProvider() 98 p.CreateZone(testZone) 99 p.ApplyChanges(ctx, &plan.Changes{ 100 Create: []*endpoint.Endpoint{ 101 newEndpointWithOwnerAndLabels("foo.test-zone.example.org", "foo.loadbalancer.com", endpoint.RecordTypeCNAME, "", endpoint.Labels{"foo": "somefoo"}), 102 newEndpointWithOwnerAndLabels("bar.test-zone.example.org", "my-domain.com", endpoint.RecordTypeCNAME, "", endpoint.Labels{"bar": "somebar"}), 103 newEndpointWithOwner("txt.bar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 104 newEndpointWithOwner("txt.bar.test-zone.example.org", "baz.test-zone.example.org", endpoint.RecordTypeCNAME, ""), 105 newEndpointWithOwner("qux.test-zone.example.org", "random", endpoint.RecordTypeTXT, ""), 106 newEndpointWithOwnerAndLabels("tar.test-zone.example.org", "tar.loadbalancer.com", endpoint.RecordTypeCNAME, "", endpoint.Labels{"tar": "sometar"}), 107 newEndpointWithOwner("TxT.tar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner-2\"", endpoint.RecordTypeTXT, ""), // case-insensitive TXT prefix 108 newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, ""), 109 newEndpointWithOwner("foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 110 newEndpointWithOwner("multiple.test-zone.example.org", "lb1.loadbalancer.com", endpoint.RecordTypeCNAME, "").WithSetIdentifier("test-set-1"), 111 newEndpointWithOwner("multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "").WithSetIdentifier("test-set-1"), 112 newEndpointWithOwner("multiple.test-zone.example.org", "lb2.loadbalancer.com", endpoint.RecordTypeCNAME, "").WithSetIdentifier("test-set-2"), 113 newEndpointWithOwner("multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "").WithSetIdentifier("test-set-2"), 114 newEndpointWithOwner("*.wildcard.test-zone.example.org", "foo.loadbalancer.com", endpoint.RecordTypeCNAME, ""), 115 newEndpointWithOwner("txt.wc.wildcard.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 116 newEndpointWithOwner("dualstack.test-zone.example.org", "1.1.1.1", endpoint.RecordTypeA, ""), 117 newEndpointWithOwner("txt.dualstack.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 118 newEndpointWithOwner("dualstack.test-zone.example.org", "2001:DB8::1", endpoint.RecordTypeAAAA, ""), 119 newEndpointWithOwner("txt.aaaa-dualstack.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner-2\"", endpoint.RecordTypeTXT, ""), 120 }, 121 }) 122 expectedRecords := []*endpoint.Endpoint{ 123 { 124 DNSName: "foo.test-zone.example.org", 125 Targets: endpoint.Targets{"foo.loadbalancer.com"}, 126 RecordType: endpoint.RecordTypeCNAME, 127 Labels: map[string]string{ 128 endpoint.OwnerLabelKey: "", 129 "foo": "somefoo", 130 }, 131 }, 132 { 133 DNSName: "bar.test-zone.example.org", 134 Targets: endpoint.Targets{"my-domain.com"}, 135 RecordType: endpoint.RecordTypeCNAME, 136 Labels: map[string]string{ 137 endpoint.OwnerLabelKey: "owner", 138 "bar": "somebar", 139 }, 140 }, 141 { 142 DNSName: "txt.bar.test-zone.example.org", 143 Targets: endpoint.Targets{"baz.test-zone.example.org"}, 144 RecordType: endpoint.RecordTypeCNAME, 145 Labels: map[string]string{ 146 endpoint.OwnerLabelKey: "", 147 }, 148 }, 149 { 150 DNSName: "qux.test-zone.example.org", 151 Targets: endpoint.Targets{"random"}, 152 RecordType: endpoint.RecordTypeTXT, 153 Labels: map[string]string{ 154 endpoint.OwnerLabelKey: "", 155 }, 156 }, 157 { 158 DNSName: "tar.test-zone.example.org", 159 Targets: endpoint.Targets{"tar.loadbalancer.com"}, 160 RecordType: endpoint.RecordTypeCNAME, 161 Labels: map[string]string{ 162 endpoint.OwnerLabelKey: "owner-2", 163 "tar": "sometar", 164 }, 165 }, 166 { 167 DNSName: "foobar.test-zone.example.org", 168 Targets: endpoint.Targets{"foobar.loadbalancer.com"}, 169 RecordType: endpoint.RecordTypeCNAME, 170 Labels: map[string]string{ 171 endpoint.OwnerLabelKey: "", 172 }, 173 }, 174 { 175 DNSName: "multiple.test-zone.example.org", 176 Targets: endpoint.Targets{"lb1.loadbalancer.com"}, 177 SetIdentifier: "test-set-1", 178 RecordType: endpoint.RecordTypeCNAME, 179 Labels: map[string]string{ 180 endpoint.OwnerLabelKey: "", 181 }, 182 }, 183 { 184 DNSName: "multiple.test-zone.example.org", 185 Targets: endpoint.Targets{"lb2.loadbalancer.com"}, 186 SetIdentifier: "test-set-2", 187 RecordType: endpoint.RecordTypeCNAME, 188 Labels: map[string]string{ 189 endpoint.OwnerLabelKey: "", 190 }, 191 }, 192 { 193 DNSName: "*.wildcard.test-zone.example.org", 194 Targets: endpoint.Targets{"foo.loadbalancer.com"}, 195 RecordType: endpoint.RecordTypeCNAME, 196 Labels: map[string]string{ 197 endpoint.OwnerLabelKey: "owner", 198 }, 199 }, 200 { 201 DNSName: "dualstack.test-zone.example.org", 202 Targets: endpoint.Targets{"1.1.1.1"}, 203 RecordType: endpoint.RecordTypeA, 204 Labels: map[string]string{ 205 endpoint.OwnerLabelKey: "owner", 206 }, 207 }, 208 { 209 DNSName: "dualstack.test-zone.example.org", 210 Targets: endpoint.Targets{"2001:DB8::1"}, 211 RecordType: endpoint.RecordTypeAAAA, 212 Labels: map[string]string{ 213 endpoint.OwnerLabelKey: "owner-2", 214 }, 215 }, 216 } 217 218 r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "wc", []string{}, []string{}, false, nil) 219 records, _ := r.Records(ctx) 220 221 assert.True(t, testutils.SameEndpoints(records, expectedRecords)) 222 223 // Ensure prefix is case-insensitive 224 r, _ = NewTXTRegistry(p, "TxT.", "", "owner", time.Hour, "wc", []string{}, []string{}, false, nil) 225 records, _ = r.Records(ctx) 226 227 assert.True(t, testutils.SameEndpoints(records, expectedRecords)) 228 } 229 230 func testTXTRegistryRecordsSuffixed(t *testing.T) { 231 ctx := context.Background() 232 p := inmemory.NewInMemoryProvider() 233 p.CreateZone(testZone) 234 p.ApplyChanges(ctx, &plan.Changes{ 235 Create: []*endpoint.Endpoint{ 236 newEndpointWithOwnerAndLabels("foo.test-zone.example.org", "foo.loadbalancer.com", endpoint.RecordTypeCNAME, "", endpoint.Labels{"foo": "somefoo"}), 237 newEndpointWithOwnerAndLabels("bar.test-zone.example.org", "my-domain.com", endpoint.RecordTypeCNAME, "", endpoint.Labels{"bar": "somebar"}), 238 newEndpointWithOwner("bar-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 239 newEndpointWithOwner("bar-txt.test-zone.example.org", "baz.test-zone.example.org", endpoint.RecordTypeCNAME, ""), 240 newEndpointWithOwner("qux.test-zone.example.org", "random", endpoint.RecordTypeTXT, ""), 241 newEndpointWithOwnerAndLabels("tar.test-zone.example.org", "tar.loadbalancer.com", endpoint.RecordTypeCNAME, "", endpoint.Labels{"tar": "sometar"}), 242 newEndpointWithOwner("tar-TxT.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner-2\"", endpoint.RecordTypeTXT, ""), // case-insensitive TXT prefix 243 newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, ""), 244 newEndpointWithOwner("foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 245 newEndpointWithOwner("multiple.test-zone.example.org", "lb1.loadbalancer.com", endpoint.RecordTypeCNAME, "").WithSetIdentifier("test-set-1"), 246 newEndpointWithOwner("multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "").WithSetIdentifier("test-set-1"), 247 newEndpointWithOwner("multiple.test-zone.example.org", "lb2.loadbalancer.com", endpoint.RecordTypeCNAME, "").WithSetIdentifier("test-set-2"), 248 newEndpointWithOwner("multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "").WithSetIdentifier("test-set-2"), 249 newEndpointWithOwner("dualstack.test-zone.example.org", "1.1.1.1", endpoint.RecordTypeA, ""), 250 newEndpointWithOwner("dualstack-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 251 newEndpointWithOwner("dualstack.test-zone.example.org", "2001:DB8::1", endpoint.RecordTypeAAAA, ""), 252 newEndpointWithOwner("aaaa-dualstack-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner-2\"", endpoint.RecordTypeTXT, ""), 253 }, 254 }) 255 expectedRecords := []*endpoint.Endpoint{ 256 { 257 DNSName: "foo.test-zone.example.org", 258 Targets: endpoint.Targets{"foo.loadbalancer.com"}, 259 RecordType: endpoint.RecordTypeCNAME, 260 Labels: map[string]string{ 261 endpoint.OwnerLabelKey: "", 262 "foo": "somefoo", 263 }, 264 }, 265 { 266 DNSName: "bar.test-zone.example.org", 267 Targets: endpoint.Targets{"my-domain.com"}, 268 RecordType: endpoint.RecordTypeCNAME, 269 Labels: map[string]string{ 270 endpoint.OwnerLabelKey: "owner", 271 "bar": "somebar", 272 }, 273 }, 274 { 275 DNSName: "bar-txt.test-zone.example.org", 276 Targets: endpoint.Targets{"baz.test-zone.example.org"}, 277 RecordType: endpoint.RecordTypeCNAME, 278 Labels: map[string]string{ 279 endpoint.OwnerLabelKey: "", 280 }, 281 }, 282 { 283 DNSName: "qux.test-zone.example.org", 284 Targets: endpoint.Targets{"random"}, 285 RecordType: endpoint.RecordTypeTXT, 286 Labels: map[string]string{ 287 endpoint.OwnerLabelKey: "", 288 }, 289 }, 290 { 291 DNSName: "tar.test-zone.example.org", 292 Targets: endpoint.Targets{"tar.loadbalancer.com"}, 293 RecordType: endpoint.RecordTypeCNAME, 294 Labels: map[string]string{ 295 endpoint.OwnerLabelKey: "owner-2", 296 "tar": "sometar", 297 }, 298 }, 299 { 300 DNSName: "foobar.test-zone.example.org", 301 Targets: endpoint.Targets{"foobar.loadbalancer.com"}, 302 RecordType: endpoint.RecordTypeCNAME, 303 Labels: map[string]string{ 304 endpoint.OwnerLabelKey: "", 305 }, 306 }, 307 { 308 DNSName: "multiple.test-zone.example.org", 309 Targets: endpoint.Targets{"lb1.loadbalancer.com"}, 310 SetIdentifier: "test-set-1", 311 RecordType: endpoint.RecordTypeCNAME, 312 Labels: map[string]string{ 313 endpoint.OwnerLabelKey: "", 314 }, 315 }, 316 { 317 DNSName: "multiple.test-zone.example.org", 318 Targets: endpoint.Targets{"lb2.loadbalancer.com"}, 319 SetIdentifier: "test-set-2", 320 RecordType: endpoint.RecordTypeCNAME, 321 Labels: map[string]string{ 322 endpoint.OwnerLabelKey: "", 323 }, 324 }, 325 { 326 DNSName: "dualstack.test-zone.example.org", 327 Targets: endpoint.Targets{"1.1.1.1"}, 328 RecordType: endpoint.RecordTypeA, 329 Labels: map[string]string{ 330 endpoint.OwnerLabelKey: "owner", 331 }, 332 }, 333 { 334 DNSName: "dualstack.test-zone.example.org", 335 Targets: endpoint.Targets{"2001:DB8::1"}, 336 RecordType: endpoint.RecordTypeAAAA, 337 Labels: map[string]string{ 338 endpoint.OwnerLabelKey: "owner-2", 339 }, 340 }, 341 } 342 343 r, _ := NewTXTRegistry(p, "", "-txt", "owner", time.Hour, "", []string{}, []string{}, false, nil) 344 records, _ := r.Records(ctx) 345 346 assert.True(t, testutils.SameEndpoints(records, expectedRecords)) 347 348 // Ensure prefix is case-insensitive 349 r, _ = NewTXTRegistry(p, "", "-TxT", "owner", time.Hour, "", []string{}, []string{}, false, nil) 350 records, _ = r.Records(ctx) 351 352 assert.True(t, testutils.SameEndpointLabels(records, expectedRecords)) 353 } 354 355 func testTXTRegistryRecordsNoPrefix(t *testing.T) { 356 p := inmemory.NewInMemoryProvider() 357 ctx := context.Background() 358 p.CreateZone(testZone) 359 p.ApplyChanges(ctx, &plan.Changes{ 360 Create: []*endpoint.Endpoint{ 361 newEndpointWithOwner("foo.test-zone.example.org", "foo.loadbalancer.com", endpoint.RecordTypeCNAME, ""), 362 newEndpointWithOwner("bar.test-zone.example.org", "my-domain.com", endpoint.RecordTypeCNAME, ""), 363 newEndpointWithOwner("alias.test-zone.example.org", "my-domain.com", endpoint.RecordTypeA, "").WithProviderSpecific("alias", "true"), 364 newEndpointWithOwner("cname-alias.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 365 newEndpointWithOwner("txt.bar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, ""), 366 newEndpointWithOwner("txt.bar.test-zone.example.org", "baz.test-zone.example.org", endpoint.RecordTypeCNAME, ""), 367 newEndpointWithOwner("qux.test-zone.example.org", "random", endpoint.RecordTypeTXT, ""), 368 newEndpointWithOwner("tar.test-zone.example.org", "tar.loadbalancer.com", endpoint.RecordTypeCNAME, ""), 369 newEndpointWithOwner("txt.tar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner-2\"", endpoint.RecordTypeTXT, ""), 370 newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, ""), 371 newEndpointWithOwner("foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 372 newEndpointWithOwner("dualstack.test-zone.example.org", "1.1.1.1", endpoint.RecordTypeA, ""), 373 newEndpointWithOwner("dualstack.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 374 newEndpointWithOwner("dualstack.test-zone.example.org", "2001:DB8::1", endpoint.RecordTypeAAAA, ""), 375 newEndpointWithOwner("aaaa-dualstack.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner-2\"", endpoint.RecordTypeTXT, ""), 376 }, 377 }) 378 expectedRecords := []*endpoint.Endpoint{ 379 { 380 DNSName: "foo.test-zone.example.org", 381 Targets: endpoint.Targets{"foo.loadbalancer.com"}, 382 RecordType: endpoint.RecordTypeCNAME, 383 Labels: map[string]string{ 384 endpoint.OwnerLabelKey: "", 385 }, 386 }, 387 { 388 DNSName: "bar.test-zone.example.org", 389 Targets: endpoint.Targets{"my-domain.com"}, 390 RecordType: endpoint.RecordTypeCNAME, 391 Labels: map[string]string{ 392 endpoint.OwnerLabelKey: "", 393 }, 394 }, 395 { 396 DNSName: "alias.test-zone.example.org", 397 Targets: endpoint.Targets{"my-domain.com"}, 398 RecordType: endpoint.RecordTypeA, 399 Labels: map[string]string{ 400 endpoint.OwnerLabelKey: "owner", 401 }, 402 ProviderSpecific: []endpoint.ProviderSpecificProperty{ 403 { 404 Name: "alias", 405 Value: "true", 406 }, 407 }, 408 }, 409 { 410 DNSName: "txt.bar.test-zone.example.org", 411 Targets: endpoint.Targets{"baz.test-zone.example.org"}, 412 RecordType: endpoint.RecordTypeCNAME, 413 Labels: map[string]string{ 414 endpoint.OwnerLabelKey: "owner", 415 endpoint.ResourceLabelKey: "ingress/default/my-ingress", 416 }, 417 }, 418 { 419 DNSName: "qux.test-zone.example.org", 420 Targets: endpoint.Targets{"random"}, 421 RecordType: endpoint.RecordTypeTXT, 422 Labels: map[string]string{ 423 endpoint.OwnerLabelKey: "", 424 }, 425 }, 426 { 427 DNSName: "tar.test-zone.example.org", 428 Targets: endpoint.Targets{"tar.loadbalancer.com"}, 429 RecordType: endpoint.RecordTypeCNAME, 430 Labels: map[string]string{ 431 endpoint.OwnerLabelKey: "", 432 }, 433 }, 434 { 435 DNSName: "foobar.test-zone.example.org", 436 Targets: endpoint.Targets{"foobar.loadbalancer.com"}, 437 RecordType: endpoint.RecordTypeCNAME, 438 Labels: map[string]string{ 439 endpoint.OwnerLabelKey: "owner", 440 }, 441 }, 442 { 443 DNSName: "dualstack.test-zone.example.org", 444 Targets: endpoint.Targets{"1.1.1.1"}, 445 RecordType: endpoint.RecordTypeA, 446 Labels: map[string]string{ 447 endpoint.OwnerLabelKey: "owner", 448 }, 449 }, 450 { 451 DNSName: "dualstack.test-zone.example.org", 452 Targets: endpoint.Targets{"2001:DB8::1"}, 453 RecordType: endpoint.RecordTypeAAAA, 454 Labels: map[string]string{ 455 endpoint.OwnerLabelKey: "owner-2", 456 }, 457 }, 458 } 459 460 r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil) 461 records, _ := r.Records(ctx) 462 463 assert.True(t, testutils.SameEndpoints(records, expectedRecords)) 464 } 465 466 func testTXTRegistryRecordsPrefixedTemplated(t *testing.T) { 467 ctx := context.Background() 468 p := inmemory.NewInMemoryProvider() 469 p.CreateZone(testZone) 470 p.ApplyChanges(ctx, &plan.Changes{ 471 Create: []*endpoint.Endpoint{ 472 newEndpointWithOwner("foo.test-zone.example.org", "1.1.1.1", endpoint.RecordTypeA, ""), 473 newEndpointWithOwner("txt-a.foo.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 474 }, 475 }) 476 expectedRecords := []*endpoint.Endpoint{ 477 { 478 DNSName: "foo.test-zone.example.org", 479 Targets: endpoint.Targets{"1.1.1.1"}, 480 RecordType: endpoint.RecordTypeA, 481 Labels: map[string]string{ 482 endpoint.OwnerLabelKey: "owner", 483 }, 484 }, 485 } 486 487 r, _ := NewTXTRegistry(p, "txt-%{record_type}.", "", "owner", time.Hour, "wc", []string{}, []string{}, false, nil) 488 records, _ := r.Records(ctx) 489 490 assert.True(t, testutils.SameEndpoints(records, expectedRecords)) 491 492 r, _ = NewTXTRegistry(p, "TxT-%{record_type}.", "", "owner", time.Hour, "wc", []string{}, []string{}, false, nil) 493 records, _ = r.Records(ctx) 494 495 assert.True(t, testutils.SameEndpoints(records, expectedRecords)) 496 } 497 498 func testTXTRegistryRecordsSuffixedTemplated(t *testing.T) { 499 ctx := context.Background() 500 p := inmemory.NewInMemoryProvider() 501 p.CreateZone(testZone) 502 p.ApplyChanges(ctx, &plan.Changes{ 503 Create: []*endpoint.Endpoint{ 504 newEndpointWithOwner("bar.test-zone.example.org", "8.8.8.8", endpoint.RecordTypeCNAME, ""), 505 newEndpointWithOwner("bartxtcname.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 506 }, 507 }) 508 expectedRecords := []*endpoint.Endpoint{ 509 { 510 DNSName: "bar.test-zone.example.org", 511 Targets: endpoint.Targets{"8.8.8.8"}, 512 RecordType: endpoint.RecordTypeCNAME, 513 Labels: map[string]string{ 514 endpoint.OwnerLabelKey: "owner", 515 }, 516 }, 517 } 518 519 r, _ := NewTXTRegistry(p, "", "txt%{record_type}", "owner", time.Hour, "wc", []string{}, []string{}, false, nil) 520 records, _ := r.Records(ctx) 521 522 assert.True(t, testutils.SameEndpoints(records, expectedRecords)) 523 524 r, _ = NewTXTRegistry(p, "", "TxT%{record_type}", "owner", time.Hour, "wc", []string{}, []string{}, false, nil) 525 records, _ = r.Records(ctx) 526 527 assert.True(t, testutils.SameEndpoints(records, expectedRecords)) 528 } 529 530 func testTXTRegistryApplyChanges(t *testing.T) { 531 t.Run("With Prefix", testTXTRegistryApplyChangesWithPrefix) 532 t.Run("With Templated Prefix", testTXTRegistryApplyChangesWithTemplatedPrefix) 533 t.Run("With Templated Suffix", testTXTRegistryApplyChangesWithTemplatedSuffix) 534 t.Run("With Suffix", testTXTRegistryApplyChangesWithSuffix) 535 t.Run("No prefix", testTXTRegistryApplyChangesNoPrefix) 536 } 537 538 func testTXTRegistryApplyChangesWithPrefix(t *testing.T) { 539 p := inmemory.NewInMemoryProvider() 540 p.CreateZone(testZone) 541 ctxEndpoints := []*endpoint.Endpoint{} 542 ctx := context.WithValue(context.Background(), provider.RecordsContextKey, ctxEndpoints) 543 p.OnApplyChanges = func(ctx context.Context, got *plan.Changes) { 544 assert.Equal(t, ctxEndpoints, ctx.Value(provider.RecordsContextKey)) 545 } 546 p.ApplyChanges(ctx, &plan.Changes{ 547 Create: []*endpoint.Endpoint{ 548 newEndpointWithOwner("foo.test-zone.example.org", "foo.loadbalancer.com", endpoint.RecordTypeCNAME, ""), 549 newEndpointWithOwner("bar.test-zone.example.org", "my-domain.com", endpoint.RecordTypeCNAME, ""), 550 newEndpointWithOwner("txt.bar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 551 newEndpointWithOwner("txt.bar.test-zone.example.org", "baz.test-zone.example.org", endpoint.RecordTypeCNAME, ""), 552 newEndpointWithOwner("qux.test-zone.example.org", "random", endpoint.RecordTypeTXT, ""), 553 newEndpointWithOwner("tar.test-zone.example.org", "tar.loadbalancer.com", endpoint.RecordTypeCNAME, ""), 554 newEndpointWithOwner("txt.tar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 555 newEndpointWithOwner("txt.cname-tar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 556 newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, ""), 557 newEndpointWithOwner("txt.foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 558 newEndpointWithOwner("txt.cname-foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 559 newEndpointWithOwner("multiple.test-zone.example.org", "lb1.loadbalancer.com", endpoint.RecordTypeCNAME, "").WithSetIdentifier("test-set-1"), 560 newEndpointWithOwner("txt.multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "").WithSetIdentifier("test-set-1"), 561 newEndpointWithOwner("txt.cname-multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "").WithSetIdentifier("test-set-1"), 562 newEndpointWithOwner("multiple.test-zone.example.org", "lb2.loadbalancer.com", endpoint.RecordTypeCNAME, "").WithSetIdentifier("test-set-2"), 563 newEndpointWithOwner("txt.multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "").WithSetIdentifier("test-set-2"), 564 newEndpointWithOwner("txt.cname-multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "").WithSetIdentifier("test-set-2"), 565 }, 566 }) 567 r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "", []string{}, []string{}, false, nil) 568 569 changes := &plan.Changes{ 570 Create: []*endpoint.Endpoint{ 571 newEndpointWithOwnerResource("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "", "ingress/default/my-ingress"), 572 newEndpointWithOwnerResource("multiple.test-zone.example.org", "lb3.loadbalancer.com", endpoint.RecordTypeCNAME, "", "ingress/default/my-ingress").WithSetIdentifier("test-set-3"), 573 newEndpointWithOwnerResource("example", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "", "ingress/default/my-ingress"), 574 }, 575 Delete: []*endpoint.Endpoint{ 576 newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"), 577 newEndpointWithOwner("multiple.test-zone.example.org", "lb1.loadbalancer.com", endpoint.RecordTypeCNAME, "owner").WithSetIdentifier("test-set-1"), 578 }, 579 UpdateNew: []*endpoint.Endpoint{ 580 newEndpointWithOwnerResource("tar.test-zone.example.org", "new-tar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress-2"), 581 newEndpointWithOwnerResource("multiple.test-zone.example.org", "new.loadbalancer.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress-2").WithSetIdentifier("test-set-2"), 582 }, 583 UpdateOld: []*endpoint.Endpoint{ 584 newEndpointWithOwner("tar.test-zone.example.org", "tar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"), 585 newEndpointWithOwner("multiple.test-zone.example.org", "lb2.loadbalancer.com", endpoint.RecordTypeCNAME, "owner").WithSetIdentifier("test-set-2"), 586 }, 587 } 588 expected := &plan.Changes{ 589 Create: []*endpoint.Endpoint{ 590 newEndpointWithOwnerResource("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress"), 591 newEndpointWithOwnerAndOwnedRecord("txt.new-record-1.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "new-record-1.test-zone.example.org"), 592 newEndpointWithOwnerAndOwnedRecord("txt.cname-new-record-1.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "new-record-1.test-zone.example.org"), 593 newEndpointWithOwnerResource("multiple.test-zone.example.org", "lb3.loadbalancer.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress").WithSetIdentifier("test-set-3"), 594 newEndpointWithOwnerAndOwnedRecord("txt.multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-3"), 595 newEndpointWithOwnerAndOwnedRecord("txt.cname-multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-3"), 596 newEndpointWithOwnerResource("example", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress"), 597 newEndpointWithOwnerAndOwnedRecord("txt.example", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "example"), 598 newEndpointWithOwnerAndOwnedRecord("txt.cname-example", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "example"), 599 }, 600 Delete: []*endpoint.Endpoint{ 601 newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"), 602 newEndpointWithOwnerAndOwnedRecord("txt.foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "foobar.test-zone.example.org"), 603 newEndpointWithOwnerAndOwnedRecord("txt.cname-foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "foobar.test-zone.example.org"), 604 newEndpointWithOwner("multiple.test-zone.example.org", "lb1.loadbalancer.com", endpoint.RecordTypeCNAME, "owner").WithSetIdentifier("test-set-1"), 605 newEndpointWithOwnerAndOwnedRecord("txt.multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-1"), 606 newEndpointWithOwnerAndOwnedRecord("txt.cname-multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-1"), 607 }, 608 UpdateNew: []*endpoint.Endpoint{ 609 newEndpointWithOwnerResource("tar.test-zone.example.org", "new-tar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress-2"), 610 newEndpointWithOwnerAndOwnedRecord("txt.tar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress-2\"", endpoint.RecordTypeTXT, "", "tar.test-zone.example.org"), 611 newEndpointWithOwnerAndOwnedRecord("txt.cname-tar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress-2\"", endpoint.RecordTypeTXT, "", "tar.test-zone.example.org"), 612 newEndpointWithOwnerResource("multiple.test-zone.example.org", "new.loadbalancer.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress-2").WithSetIdentifier("test-set-2"), 613 newEndpointWithOwnerAndOwnedRecord("txt.multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress-2\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-2"), 614 newEndpointWithOwnerAndOwnedRecord("txt.cname-multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress-2\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-2"), 615 }, 616 UpdateOld: []*endpoint.Endpoint{ 617 newEndpointWithOwner("tar.test-zone.example.org", "tar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"), 618 newEndpointWithOwnerAndOwnedRecord("txt.tar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "tar.test-zone.example.org"), 619 newEndpointWithOwnerAndOwnedRecord("txt.cname-tar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "tar.test-zone.example.org"), 620 newEndpointWithOwner("multiple.test-zone.example.org", "lb2.loadbalancer.com", endpoint.RecordTypeCNAME, "owner").WithSetIdentifier("test-set-2"), 621 newEndpointWithOwnerAndOwnedRecord("txt.multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-2"), 622 newEndpointWithOwnerAndOwnedRecord("txt.cname-multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-2"), 623 }, 624 } 625 p.OnApplyChanges = func(ctx context.Context, got *plan.Changes) { 626 mExpected := map[string][]*endpoint.Endpoint{ 627 "Create": expected.Create, 628 "UpdateNew": expected.UpdateNew, 629 "UpdateOld": expected.UpdateOld, 630 "Delete": expected.Delete, 631 } 632 mGot := map[string][]*endpoint.Endpoint{ 633 "Create": got.Create, 634 "UpdateNew": got.UpdateNew, 635 "UpdateOld": got.UpdateOld, 636 "Delete": got.Delete, 637 } 638 assert.True(t, testutils.SamePlanChanges(mGot, mExpected)) 639 assert.Equal(t, nil, ctx.Value(provider.RecordsContextKey)) 640 } 641 err := r.ApplyChanges(ctx, changes) 642 require.NoError(t, err) 643 } 644 645 func testTXTRegistryApplyChangesWithTemplatedPrefix(t *testing.T) { 646 p := inmemory.NewInMemoryProvider() 647 p.CreateZone(testZone) 648 ctxEndpoints := []*endpoint.Endpoint{} 649 ctx := context.WithValue(context.Background(), provider.RecordsContextKey, ctxEndpoints) 650 p.OnApplyChanges = func(ctx context.Context, got *plan.Changes) { 651 assert.Equal(t, ctxEndpoints, ctx.Value(provider.RecordsContextKey)) 652 } 653 p.ApplyChanges(ctx, &plan.Changes{ 654 Create: []*endpoint.Endpoint{}, 655 }) 656 r, _ := NewTXTRegistry(p, "prefix%{record_type}.", "", "owner", time.Hour, "", []string{}, []string{}, false, nil) 657 changes := &plan.Changes{ 658 Create: []*endpoint.Endpoint{ 659 newEndpointWithOwnerResource("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "", "ingress/default/my-ingress"), 660 }, 661 Delete: []*endpoint.Endpoint{}, 662 UpdateOld: []*endpoint.Endpoint{}, 663 UpdateNew: []*endpoint.Endpoint{}, 664 } 665 expected := &plan.Changes{ 666 Create: []*endpoint.Endpoint{ 667 newEndpointWithOwnerResource("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress"), 668 newEndpointWithOwnerAndOwnedRecord("prefixcname.new-record-1.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "new-record-1.test-zone.example.org"), 669 }, 670 } 671 p.OnApplyChanges = func(ctx context.Context, got *plan.Changes) { 672 mExpected := map[string][]*endpoint.Endpoint{ 673 "Create": expected.Create, 674 "UpdateNew": expected.UpdateNew, 675 "UpdateOld": expected.UpdateOld, 676 "Delete": expected.Delete, 677 } 678 mGot := map[string][]*endpoint.Endpoint{ 679 "Create": got.Create, 680 "UpdateNew": got.UpdateNew, 681 "UpdateOld": got.UpdateOld, 682 "Delete": got.Delete, 683 } 684 assert.True(t, testutils.SamePlanChanges(mGot, mExpected)) 685 assert.Equal(t, nil, ctx.Value(provider.RecordsContextKey)) 686 } 687 err := r.ApplyChanges(ctx, changes) 688 require.NoError(t, err) 689 } 690 691 func testTXTRegistryApplyChangesWithTemplatedSuffix(t *testing.T) { 692 p := inmemory.NewInMemoryProvider() 693 p.CreateZone(testZone) 694 ctxEndpoints := []*endpoint.Endpoint{} 695 ctx := context.WithValue(context.Background(), provider.RecordsContextKey, ctxEndpoints) 696 p.OnApplyChanges = func(ctx context.Context, got *plan.Changes) { 697 assert.Equal(t, ctxEndpoints, ctx.Value(provider.RecordsContextKey)) 698 } 699 r, _ := NewTXTRegistry(p, "", "-%{record_type}suffix", "owner", time.Hour, "", []string{}, []string{}, false, nil) 700 changes := &plan.Changes{ 701 Create: []*endpoint.Endpoint{ 702 newEndpointWithOwnerResource("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "", "ingress/default/my-ingress"), 703 }, 704 Delete: []*endpoint.Endpoint{}, 705 UpdateOld: []*endpoint.Endpoint{}, 706 UpdateNew: []*endpoint.Endpoint{}, 707 } 708 expected := &plan.Changes{ 709 Create: []*endpoint.Endpoint{ 710 newEndpointWithOwnerResource("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress"), 711 newEndpointWithOwnerAndOwnedRecord("new-record-1-cnamesuffix.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "new-record-1.test-zone.example.org"), 712 }, 713 } 714 p.OnApplyChanges = func(ctx context.Context, got *plan.Changes) { 715 mExpected := map[string][]*endpoint.Endpoint{ 716 "Create": expected.Create, 717 "UpdateNew": expected.UpdateNew, 718 "UpdateOld": expected.UpdateOld, 719 "Delete": expected.Delete, 720 } 721 mGot := map[string][]*endpoint.Endpoint{ 722 "Create": got.Create, 723 "UpdateNew": got.UpdateNew, 724 "UpdateOld": got.UpdateOld, 725 "Delete": got.Delete, 726 } 727 assert.True(t, testutils.SamePlanChanges(mGot, mExpected)) 728 assert.Equal(t, nil, ctx.Value(provider.RecordsContextKey)) 729 } 730 err := r.ApplyChanges(ctx, changes) 731 require.NoError(t, err) 732 } 733 734 func testTXTRegistryApplyChangesWithSuffix(t *testing.T) { 735 p := inmemory.NewInMemoryProvider() 736 p.CreateZone(testZone) 737 ctxEndpoints := []*endpoint.Endpoint{} 738 ctx := context.WithValue(context.Background(), provider.RecordsContextKey, ctxEndpoints) 739 p.OnApplyChanges = func(ctx context.Context, got *plan.Changes) { 740 assert.Equal(t, ctxEndpoints, ctx.Value(provider.RecordsContextKey)) 741 } 742 p.ApplyChanges(ctx, &plan.Changes{ 743 Create: []*endpoint.Endpoint{ 744 newEndpointWithOwner("foo.test-zone.example.org", "foo.loadbalancer.com", endpoint.RecordTypeCNAME, ""), 745 newEndpointWithOwner("bar.test-zone.example.org", "my-domain.com", endpoint.RecordTypeCNAME, ""), 746 newEndpointWithOwner("bar-txt.test-zone.example.org", "baz.test-zone.example.org", endpoint.RecordTypeCNAME, ""), 747 newEndpointWithOwner("bar-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 748 newEndpointWithOwner("cname-bar-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 749 newEndpointWithOwner("qux.test-zone.example.org", "random", endpoint.RecordTypeTXT, ""), 750 newEndpointWithOwner("tar.test-zone.example.org", "tar.loadbalancer.com", endpoint.RecordTypeCNAME, ""), 751 newEndpointWithOwner("tar-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 752 newEndpointWithOwner("cname-tar-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 753 newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, ""), 754 newEndpointWithOwner("foobar-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 755 newEndpointWithOwner("cname-foobar-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 756 newEndpointWithOwner("multiple.test-zone.example.org", "lb1.loadbalancer.com", endpoint.RecordTypeCNAME, "").WithSetIdentifier("test-set-1"), 757 newEndpointWithOwner("multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "").WithSetIdentifier("test-set-1"), 758 newEndpointWithOwner("cname-multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "").WithSetIdentifier("test-set-1"), 759 newEndpointWithOwner("multiple.test-zone.example.org", "lb2.loadbalancer.com", endpoint.RecordTypeCNAME, "").WithSetIdentifier("test-set-2"), 760 newEndpointWithOwner("multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "").WithSetIdentifier("test-set-2"), 761 newEndpointWithOwner("cname-multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "").WithSetIdentifier("test-set-2"), 762 }, 763 }) 764 r, _ := NewTXTRegistry(p, "", "-txt", "owner", time.Hour, "wildcard", []string{}, []string{}, false, nil) 765 766 changes := &plan.Changes{ 767 Create: []*endpoint.Endpoint{ 768 newEndpointWithOwnerResource("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "", "ingress/default/my-ingress"), 769 newEndpointWithOwnerResource("multiple.test-zone.example.org", "lb3.loadbalancer.com", endpoint.RecordTypeCNAME, "", "ingress/default/my-ingress").WithSetIdentifier("test-set-3"), 770 newEndpointWithOwnerResource("example", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "", "ingress/default/my-ingress"), 771 newEndpointWithOwnerResource("*.wildcard.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "", "ingress/default/my-ingress"), 772 }, 773 Delete: []*endpoint.Endpoint{ 774 newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"), 775 newEndpointWithOwner("multiple.test-zone.example.org", "lb1.loadbalancer.com", endpoint.RecordTypeCNAME, "owner").WithSetIdentifier("test-set-1"), 776 }, 777 UpdateNew: []*endpoint.Endpoint{ 778 newEndpointWithOwnerResource("tar.test-zone.example.org", "new-tar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress-2"), 779 newEndpointWithOwnerResource("multiple.test-zone.example.org", "new.loadbalancer.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress-2").WithSetIdentifier("test-set-2"), 780 }, 781 UpdateOld: []*endpoint.Endpoint{ 782 newEndpointWithOwner("tar.test-zone.example.org", "tar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"), 783 newEndpointWithOwner("multiple.test-zone.example.org", "lb2.loadbalancer.com", endpoint.RecordTypeCNAME, "owner").WithSetIdentifier("test-set-2"), 784 }, 785 } 786 expected := &plan.Changes{ 787 Create: []*endpoint.Endpoint{ 788 newEndpointWithOwnerResource("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress"), 789 newEndpointWithOwnerAndOwnedRecord("new-record-1-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "new-record-1.test-zone.example.org"), 790 newEndpointWithOwnerAndOwnedRecord("cname-new-record-1-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "new-record-1.test-zone.example.org"), 791 newEndpointWithOwnerResource("multiple.test-zone.example.org", "lb3.loadbalancer.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress").WithSetIdentifier("test-set-3"), 792 newEndpointWithOwnerAndOwnedRecord("multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-3"), 793 newEndpointWithOwnerAndOwnedRecord("cname-multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-3"), 794 newEndpointWithOwnerResource("example", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress"), 795 newEndpointWithOwnerAndOwnedRecord("example-txt", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "example"), 796 newEndpointWithOwnerAndOwnedRecord("cname-example-txt", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "example"), 797 newEndpointWithOwnerResource("*.wildcard.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress"), 798 newEndpointWithOwnerAndOwnedRecord("wildcard-txt.wildcard.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "*.wildcard.test-zone.example.org"), 799 newEndpointWithOwnerAndOwnedRecord("cname-wildcard-txt.wildcard.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress\"", endpoint.RecordTypeTXT, "", "*.wildcard.test-zone.example.org"), 800 }, 801 Delete: []*endpoint.Endpoint{ 802 newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"), 803 newEndpointWithOwnerAndOwnedRecord("foobar-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "foobar.test-zone.example.org"), 804 newEndpointWithOwnerAndOwnedRecord("cname-foobar-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "foobar.test-zone.example.org"), 805 newEndpointWithOwner("multiple.test-zone.example.org", "lb1.loadbalancer.com", endpoint.RecordTypeCNAME, "owner").WithSetIdentifier("test-set-1"), 806 newEndpointWithOwnerAndOwnedRecord("multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-1"), 807 newEndpointWithOwnerAndOwnedRecord("cname-multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-1"), 808 }, 809 UpdateNew: []*endpoint.Endpoint{ 810 newEndpointWithOwnerResource("tar.test-zone.example.org", "new-tar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress-2"), 811 newEndpointWithOwnerAndOwnedRecord("tar-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress-2\"", endpoint.RecordTypeTXT, "", "tar.test-zone.example.org"), 812 newEndpointWithOwnerAndOwnedRecord("cname-tar-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress-2\"", endpoint.RecordTypeTXT, "", "tar.test-zone.example.org"), 813 newEndpointWithOwnerResource("multiple.test-zone.example.org", "new.loadbalancer.com", endpoint.RecordTypeCNAME, "owner", "ingress/default/my-ingress-2").WithSetIdentifier("test-set-2"), 814 newEndpointWithOwnerAndOwnedRecord("multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress-2\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-2"), 815 newEndpointWithOwnerAndOwnedRecord("cname-multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner,external-dns/resource=ingress/default/my-ingress-2\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-2"), 816 }, 817 UpdateOld: []*endpoint.Endpoint{ 818 newEndpointWithOwner("tar.test-zone.example.org", "tar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"), 819 newEndpointWithOwnerAndOwnedRecord("tar-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "tar.test-zone.example.org"), 820 newEndpointWithOwnerAndOwnedRecord("cname-tar-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "tar.test-zone.example.org"), 821 newEndpointWithOwner("multiple.test-zone.example.org", "lb2.loadbalancer.com", endpoint.RecordTypeCNAME, "owner").WithSetIdentifier("test-set-2"), 822 newEndpointWithOwnerAndOwnedRecord("multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-2"), 823 newEndpointWithOwnerAndOwnedRecord("cname-multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "multiple.test-zone.example.org").WithSetIdentifier("test-set-2"), 824 }, 825 } 826 p.OnApplyChanges = func(ctx context.Context, got *plan.Changes) { 827 mExpected := map[string][]*endpoint.Endpoint{ 828 "Create": expected.Create, 829 "UpdateNew": expected.UpdateNew, 830 "UpdateOld": expected.UpdateOld, 831 "Delete": expected.Delete, 832 } 833 mGot := map[string][]*endpoint.Endpoint{ 834 "Create": got.Create, 835 "UpdateNew": got.UpdateNew, 836 "UpdateOld": got.UpdateOld, 837 "Delete": got.Delete, 838 } 839 assert.True(t, testutils.SamePlanChanges(mGot, mExpected)) 840 assert.Equal(t, nil, ctx.Value(provider.RecordsContextKey)) 841 } 842 err := r.ApplyChanges(ctx, changes) 843 require.NoError(t, err) 844 } 845 846 func testTXTRegistryApplyChangesNoPrefix(t *testing.T) { 847 p := inmemory.NewInMemoryProvider() 848 p.CreateZone(testZone) 849 ctxEndpoints := []*endpoint.Endpoint{} 850 ctx := context.WithValue(context.Background(), provider.RecordsContextKey, ctxEndpoints) 851 p.OnApplyChanges = func(ctx context.Context, got *plan.Changes) { 852 assert.Equal(t, ctxEndpoints, ctx.Value(provider.RecordsContextKey)) 853 } 854 p.ApplyChanges(ctx, &plan.Changes{ 855 Create: []*endpoint.Endpoint{ 856 newEndpointWithOwner("foo.test-zone.example.org", "foo.loadbalancer.com", endpoint.RecordTypeCNAME, ""), 857 newEndpointWithOwner("bar.test-zone.example.org", "my-domain.com", endpoint.RecordTypeCNAME, ""), 858 newEndpointWithOwner("txt.bar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 859 newEndpointWithOwner("txt.bar.test-zone.example.org", "baz.test-zone.example.org", endpoint.RecordTypeCNAME, ""), 860 newEndpointWithOwner("qux.test-zone.example.org", "random", endpoint.RecordTypeTXT, ""), 861 newEndpointWithOwner("tar.test-zone.example.org", "tar.loadbalancer.com", endpoint.RecordTypeCNAME, ""), 862 newEndpointWithOwner("txt.tar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 863 newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, ""), 864 newEndpointWithOwner("foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 865 newEndpointWithOwner("cname-foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 866 }, 867 }) 868 r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil) 869 870 changes := &plan.Changes{ 871 Create: []*endpoint.Endpoint{ 872 newEndpointWithOwner("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, ""), 873 newEndpointWithOwner("example", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, ""), 874 newEndpointWithOwner("new-alias.test-zone.example.org", "my-domain.com", endpoint.RecordTypeA, "").WithProviderSpecific("alias", "true"), 875 }, 876 Delete: []*endpoint.Endpoint{ 877 newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"), 878 }, 879 UpdateNew: []*endpoint.Endpoint{ 880 newEndpointWithOwner("tar.test-zone.example.org", "new-tar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner-2"), 881 }, 882 UpdateOld: []*endpoint.Endpoint{ 883 newEndpointWithOwner("tar.test-zone.example.org", "tar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner-2"), 884 }, 885 } 886 expected := &plan.Changes{ 887 Create: []*endpoint.Endpoint{ 888 newEndpointWithOwner("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner"), 889 newEndpointWithOwnerAndOwnedRecord("new-record-1.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "new-record-1.test-zone.example.org"), 890 newEndpointWithOwnerAndOwnedRecord("cname-new-record-1.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "new-record-1.test-zone.example.org"), 891 newEndpointWithOwner("example", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner"), 892 newEndpointWithOwnerAndOwnedRecord("example", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "example"), 893 newEndpointWithOwnerAndOwnedRecord("cname-example", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "example"), 894 newEndpointWithOwner("new-alias.test-zone.example.org", "my-domain.com", endpoint.RecordTypeA, "owner").WithProviderSpecific("alias", "true"), 895 // TODO: It's not clear why the TXT registry copies ProviderSpecificProperties to ownership records; that doesn't seem correct. 896 newEndpointWithOwnerAndOwnedRecord("new-alias.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "new-alias.test-zone.example.org").WithProviderSpecific("alias", "true"), 897 newEndpointWithOwnerAndOwnedRecord("cname-new-alias.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "new-alias.test-zone.example.org").WithProviderSpecific("alias", "true"), 898 }, 899 Delete: []*endpoint.Endpoint{ 900 newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"), 901 newEndpointWithOwnerAndOwnedRecord("foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "foobar.test-zone.example.org"), 902 newEndpointWithOwnerAndOwnedRecord("cname-foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "foobar.test-zone.example.org"), 903 }, 904 UpdateNew: []*endpoint.Endpoint{}, 905 UpdateOld: []*endpoint.Endpoint{}, 906 } 907 p.OnApplyChanges = func(ctx context.Context, got *plan.Changes) { 908 mExpected := map[string][]*endpoint.Endpoint{ 909 "Create": expected.Create, 910 "UpdateNew": expected.UpdateNew, 911 "UpdateOld": expected.UpdateOld, 912 "Delete": expected.Delete, 913 } 914 mGot := map[string][]*endpoint.Endpoint{ 915 "Create": got.Create, 916 "UpdateNew": got.UpdateNew, 917 "UpdateOld": got.UpdateOld, 918 "Delete": got.Delete, 919 } 920 assert.True(t, testutils.SamePlanChanges(mGot, mExpected)) 921 assert.Equal(t, nil, ctx.Value(provider.RecordsContextKey)) 922 } 923 err := r.ApplyChanges(ctx, changes) 924 require.NoError(t, err) 925 } 926 927 func testTXTRegistryMissingRecords(t *testing.T) { 928 t.Run("No prefix", testTXTRegistryMissingRecordsNoPrefix) 929 t.Run("With Prefix", testTXTRegistryMissingRecordsWithPrefix) 930 } 931 932 func testTXTRegistryMissingRecordsNoPrefix(t *testing.T) { 933 ctx := context.Background() 934 p := inmemory.NewInMemoryProvider() 935 p.CreateZone(testZone) 936 p.ApplyChanges(ctx, &plan.Changes{ 937 Create: []*endpoint.Endpoint{ 938 newEndpointWithOwner("oldformat.test-zone.example.org", "foo.loadbalancer.com", endpoint.RecordTypeCNAME, ""), 939 newEndpointWithOwner("oldformat.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 940 newEndpointWithOwner("oldformat2.test-zone.example.org", "bar.loadbalancer.com", endpoint.RecordTypeA, ""), 941 newEndpointWithOwner("oldformat2.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 942 newEndpointWithOwner("newformat.test-zone.example.org", "foobar.nameserver.com", endpoint.RecordTypeNS, ""), 943 newEndpointWithOwner("ns-newformat.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 944 newEndpointWithOwner("newformat.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 945 newEndpointWithOwner("noheritage.test-zone.example.org", "random", endpoint.RecordTypeTXT, ""), 946 newEndpointWithOwner("oldformat-otherowner.test-zone.example.org", "bar.loadbalancer.com", endpoint.RecordTypeA, ""), 947 newEndpointWithOwner("oldformat-otherowner.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=otherowner\"", endpoint.RecordTypeTXT, ""), 948 endpoint.NewEndpoint("unmanaged1.test-zone.example.org", endpoint.RecordTypeA, "unmanaged1.loadbalancer.com"), 949 endpoint.NewEndpoint("unmanaged2.test-zone.example.org", endpoint.RecordTypeCNAME, "unmanaged2.loadbalancer.com"), 950 newEndpointWithOwner("this-is-a-63-characters-long-label-that-we-do-expect-will-work.test-zone.example.org", "foo.loadbalancer.com", endpoint.RecordTypeCNAME, ""), 951 newEndpointWithOwner("this-is-a-63-characters-long-label-that-we-do-expect-will-work.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 952 }, 953 }) 954 expectedRecords := []*endpoint.Endpoint{ 955 { 956 DNSName: "oldformat.test-zone.example.org", 957 Targets: endpoint.Targets{"foo.loadbalancer.com"}, 958 RecordType: endpoint.RecordTypeCNAME, 959 Labels: map[string]string{ 960 // owner was added from the TXT record's target 961 endpoint.OwnerLabelKey: "owner", 962 }, 963 ProviderSpecific: []endpoint.ProviderSpecificProperty{ 964 { 965 Name: "txt/force-update", 966 Value: "true", 967 }, 968 }, 969 }, 970 { 971 DNSName: "oldformat2.test-zone.example.org", 972 Targets: endpoint.Targets{"bar.loadbalancer.com"}, 973 RecordType: endpoint.RecordTypeA, 974 Labels: map[string]string{ 975 endpoint.OwnerLabelKey: "owner", 976 }, 977 ProviderSpecific: []endpoint.ProviderSpecificProperty{ 978 { 979 Name: "txt/force-update", 980 Value: "true", 981 }, 982 }, 983 }, 984 { 985 DNSName: "newformat.test-zone.example.org", 986 Targets: endpoint.Targets{"foobar.nameserver.com"}, 987 RecordType: endpoint.RecordTypeNS, 988 Labels: map[string]string{ 989 endpoint.OwnerLabelKey: "owner", 990 }, 991 }, 992 // Only TXT records with the wrong heritage are returned by Records() 993 { 994 DNSName: "noheritage.test-zone.example.org", 995 Targets: endpoint.Targets{"random"}, 996 RecordType: endpoint.RecordTypeTXT, 997 Labels: map[string]string{ 998 // No owner because it's not external-dns heritage 999 endpoint.OwnerLabelKey: "", 1000 }, 1001 }, 1002 { 1003 DNSName: "oldformat-otherowner.test-zone.example.org", 1004 Targets: endpoint.Targets{"bar.loadbalancer.com"}, 1005 RecordType: endpoint.RecordTypeA, 1006 Labels: map[string]string{ 1007 // Records() retrieves all the records of the zone, no matter the owner 1008 endpoint.OwnerLabelKey: "otherowner", 1009 }, 1010 }, 1011 { 1012 DNSName: "unmanaged1.test-zone.example.org", 1013 Targets: endpoint.Targets{"unmanaged1.loadbalancer.com"}, 1014 RecordType: endpoint.RecordTypeA, 1015 }, 1016 { 1017 DNSName: "unmanaged2.test-zone.example.org", 1018 Targets: endpoint.Targets{"unmanaged2.loadbalancer.com"}, 1019 RecordType: endpoint.RecordTypeCNAME, 1020 }, 1021 { 1022 DNSName: "this-is-a-63-characters-long-label-that-we-do-expect-will-work.test-zone.example.org", 1023 Targets: endpoint.Targets{"foo.loadbalancer.com"}, 1024 RecordType: endpoint.RecordTypeCNAME, 1025 Labels: map[string]string{ 1026 endpoint.OwnerLabelKey: "owner", 1027 }, 1028 }, 1029 } 1030 1031 r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "wc", []string{endpoint.RecordTypeCNAME, endpoint.RecordTypeA, endpoint.RecordTypeNS}, []string{}, false, nil) 1032 records, _ := r.Records(ctx) 1033 1034 assert.True(t, testutils.SameEndpoints(records, expectedRecords)) 1035 } 1036 1037 func testTXTRegistryMissingRecordsWithPrefix(t *testing.T) { 1038 ctx := context.Background() 1039 p := inmemory.NewInMemoryProvider() 1040 p.CreateZone(testZone) 1041 p.ApplyChanges(ctx, &plan.Changes{ 1042 Create: []*endpoint.Endpoint{ 1043 newEndpointWithOwner("oldformat.test-zone.example.org", "foo.loadbalancer.com", endpoint.RecordTypeCNAME, ""), 1044 newEndpointWithOwner("txt.oldformat.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 1045 newEndpointWithOwner("oldformat2.test-zone.example.org", "bar.loadbalancer.com", endpoint.RecordTypeA, ""), 1046 newEndpointWithOwner("txt.oldformat2.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 1047 newEndpointWithOwner("newformat.test-zone.example.org", "foobar.nameserver.com", endpoint.RecordTypeNS, ""), 1048 newEndpointWithOwner("txt.ns-newformat.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 1049 newEndpointWithOwner("oldformat3.test-zone.example.org", "random", endpoint.RecordTypeTXT, ""), 1050 newEndpointWithOwner("txt.oldformat3.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 1051 newEndpointWithOwner("txt.newformat.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 1052 newEndpointWithOwner("noheritage.test-zone.example.org", "random", endpoint.RecordTypeTXT, ""), 1053 newEndpointWithOwner("oldformat-otherowner.test-zone.example.org", "bar.loadbalancer.com", endpoint.RecordTypeA, ""), 1054 newEndpointWithOwner("txt.oldformat-otherowner.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=otherowner\"", endpoint.RecordTypeTXT, ""), 1055 endpoint.NewEndpoint("unmanaged1.test-zone.example.org", endpoint.RecordTypeA, "unmanaged1.loadbalancer.com"), 1056 endpoint.NewEndpoint("unmanaged2.test-zone.example.org", endpoint.RecordTypeCNAME, "unmanaged2.loadbalancer.com"), 1057 }, 1058 }) 1059 expectedRecords := []*endpoint.Endpoint{ 1060 { 1061 DNSName: "oldformat.test-zone.example.org", 1062 Targets: endpoint.Targets{"foo.loadbalancer.com"}, 1063 RecordType: endpoint.RecordTypeCNAME, 1064 Labels: map[string]string{ 1065 // owner was added from the TXT record's target 1066 endpoint.OwnerLabelKey: "owner", 1067 }, 1068 ProviderSpecific: []endpoint.ProviderSpecificProperty{ 1069 { 1070 Name: "txt/force-update", 1071 Value: "true", 1072 }, 1073 }, 1074 }, 1075 { 1076 DNSName: "oldformat2.test-zone.example.org", 1077 Targets: endpoint.Targets{"bar.loadbalancer.com"}, 1078 RecordType: endpoint.RecordTypeA, 1079 Labels: map[string]string{ 1080 endpoint.OwnerLabelKey: "owner", 1081 }, 1082 ProviderSpecific: []endpoint.ProviderSpecificProperty{ 1083 { 1084 Name: "txt/force-update", 1085 Value: "true", 1086 }, 1087 }, 1088 }, 1089 { 1090 DNSName: "oldformat3.test-zone.example.org", 1091 Targets: endpoint.Targets{"random"}, 1092 RecordType: endpoint.RecordTypeTXT, 1093 Labels: map[string]string{ 1094 endpoint.OwnerLabelKey: "owner", 1095 }, 1096 ProviderSpecific: []endpoint.ProviderSpecificProperty{ 1097 { 1098 Name: "txt/force-update", 1099 Value: "true", 1100 }, 1101 }, 1102 }, 1103 { 1104 DNSName: "newformat.test-zone.example.org", 1105 Targets: endpoint.Targets{"foobar.nameserver.com"}, 1106 RecordType: endpoint.RecordTypeNS, 1107 Labels: map[string]string{ 1108 endpoint.OwnerLabelKey: "owner", 1109 }, 1110 }, 1111 { 1112 DNSName: "noheritage.test-zone.example.org", 1113 Targets: endpoint.Targets{"random"}, 1114 RecordType: endpoint.RecordTypeTXT, 1115 Labels: map[string]string{ 1116 // No owner because it's not external-dns heritage 1117 endpoint.OwnerLabelKey: "", 1118 }, 1119 }, 1120 { 1121 DNSName: "oldformat-otherowner.test-zone.example.org", 1122 Targets: endpoint.Targets{"bar.loadbalancer.com"}, 1123 RecordType: endpoint.RecordTypeA, 1124 Labels: map[string]string{ 1125 // All the records of the zone are retrieved, no matter the owner 1126 endpoint.OwnerLabelKey: "otherowner", 1127 }, 1128 }, 1129 { 1130 DNSName: "unmanaged1.test-zone.example.org", 1131 Targets: endpoint.Targets{"unmanaged1.loadbalancer.com"}, 1132 RecordType: endpoint.RecordTypeA, 1133 }, 1134 { 1135 DNSName: "unmanaged2.test-zone.example.org", 1136 Targets: endpoint.Targets{"unmanaged2.loadbalancer.com"}, 1137 RecordType: endpoint.RecordTypeCNAME, 1138 }, 1139 } 1140 1141 r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "wc", []string{endpoint.RecordTypeCNAME, endpoint.RecordTypeA, endpoint.RecordTypeNS, endpoint.RecordTypeTXT}, []string{}, false, nil) 1142 records, _ := r.Records(ctx) 1143 1144 assert.True(t, testutils.SameEndpoints(records, expectedRecords)) 1145 } 1146 1147 func TestCacheMethods(t *testing.T) { 1148 cache := []*endpoint.Endpoint{ 1149 newEndpointWithOwner("thing.com", "1.2.3.4", "A", "owner"), 1150 newEndpointWithOwner("thing1.com", "1.2.3.6", "A", "owner"), 1151 newEndpointWithOwner("thing2.com", "1.2.3.4", "CNAME", "owner"), 1152 newEndpointWithOwner("thing3.com", "1.2.3.4", "A", "owner"), 1153 newEndpointWithOwner("thing4.com", "1.2.3.4", "A", "owner"), 1154 } 1155 registry := &TXTRegistry{ 1156 recordsCache: cache, 1157 cacheInterval: time.Hour, 1158 } 1159 1160 expectedCacheAfterAdd := []*endpoint.Endpoint{ 1161 newEndpointWithOwner("thing.com", "1.2.3.4", "A", "owner"), 1162 newEndpointWithOwner("thing1.com", "1.2.3.6", "A", "owner"), 1163 newEndpointWithOwner("thing2.com", "1.2.3.4", "CNAME", "owner"), 1164 newEndpointWithOwner("thing3.com", "1.2.3.4", "A", "owner"), 1165 newEndpointWithOwner("thing4.com", "1.2.3.4", "A", "owner"), 1166 newEndpointWithOwner("thing4.com", "2001:DB8::1", "AAAA", "owner"), 1167 newEndpointWithOwner("thing5.com", "1.2.3.5", "A", "owner"), 1168 } 1169 1170 expectedCacheAfterUpdate := []*endpoint.Endpoint{ 1171 newEndpointWithOwner("thing1.com", "1.2.3.6", "A", "owner"), 1172 newEndpointWithOwner("thing2.com", "1.2.3.4", "CNAME", "owner"), 1173 newEndpointWithOwner("thing3.com", "1.2.3.4", "A", "owner"), 1174 newEndpointWithOwner("thing4.com", "1.2.3.4", "A", "owner"), 1175 newEndpointWithOwner("thing5.com", "1.2.3.5", "A", "owner"), 1176 newEndpointWithOwner("thing.com", "1.2.3.6", "A", "owner2"), 1177 newEndpointWithOwner("thing4.com", "2001:DB8::2", "AAAA", "owner"), 1178 } 1179 1180 expectedCacheAfterDelete := []*endpoint.Endpoint{ 1181 newEndpointWithOwner("thing1.com", "1.2.3.6", "A", "owner"), 1182 newEndpointWithOwner("thing2.com", "1.2.3.4", "CNAME", "owner"), 1183 newEndpointWithOwner("thing3.com", "1.2.3.4", "A", "owner"), 1184 newEndpointWithOwner("thing4.com", "1.2.3.4", "A", "owner"), 1185 newEndpointWithOwner("thing5.com", "1.2.3.5", "A", "owner"), 1186 } 1187 // test add cache 1188 registry.addToCache(newEndpointWithOwner("thing4.com", "2001:DB8::1", "AAAA", "owner")) 1189 registry.addToCache(newEndpointWithOwner("thing5.com", "1.2.3.5", "A", "owner")) 1190 1191 if !reflect.DeepEqual(expectedCacheAfterAdd, registry.recordsCache) { 1192 t.Fatalf("expected endpoints should match endpoints from cache: expected %v, but got %v", expectedCacheAfterAdd, registry.recordsCache) 1193 } 1194 1195 // test update cache 1196 registry.removeFromCache(newEndpointWithOwner("thing.com", "1.2.3.4", "A", "owner")) 1197 registry.addToCache(newEndpointWithOwner("thing.com", "1.2.3.6", "A", "owner2")) 1198 registry.removeFromCache(newEndpointWithOwner("thing4.com", "2001:DB8::1", "AAAA", "owner")) 1199 registry.addToCache(newEndpointWithOwner("thing4.com", "2001:DB8::2", "AAAA", "owner")) 1200 // ensure it was updated 1201 if !reflect.DeepEqual(expectedCacheAfterUpdate, registry.recordsCache) { 1202 t.Fatalf("expected endpoints should match endpoints from cache: expected %v, but got %v", expectedCacheAfterUpdate, registry.recordsCache) 1203 } 1204 1205 // test deleting a record 1206 registry.removeFromCache(newEndpointWithOwner("thing.com", "1.2.3.6", "A", "owner2")) 1207 registry.removeFromCache(newEndpointWithOwner("thing4.com", "2001:DB8::2", "AAAA", "owner")) 1208 // ensure it was deleted 1209 if !reflect.DeepEqual(expectedCacheAfterDelete, registry.recordsCache) { 1210 t.Fatalf("expected endpoints should match endpoints from cache: expected %v, but got %v", expectedCacheAfterDelete, registry.recordsCache) 1211 } 1212 } 1213 1214 func TestDropPrefix(t *testing.T) { 1215 mapper := newaffixNameMapper("foo-%{record_type}-", "", "") 1216 expectedOutput := "test.example.com" 1217 1218 tests := []string{ 1219 "foo-cname-test.example.com", 1220 "foo-a-test.example.com", 1221 "foo--test.example.com", 1222 } 1223 1224 for _, tc := range tests { 1225 t.Run(tc, func(t *testing.T) { 1226 actualOutput, _ := mapper.dropAffixExtractType(tc) 1227 assert.Equal(t, expectedOutput, actualOutput) 1228 }) 1229 } 1230 } 1231 1232 func TestDropSuffix(t *testing.T) { 1233 mapper := newaffixNameMapper("", "-%{record_type}-foo", "") 1234 expectedOutput := "test.example.com" 1235 1236 tests := []string{ 1237 "test-a-foo.example.com", 1238 "test--foo.example.com", 1239 } 1240 1241 for _, tc := range tests { 1242 t.Run(tc, func(t *testing.T) { 1243 r := strings.SplitN(tc, ".", 2) 1244 rClean, _ := mapper.dropAffixExtractType(r[0]) 1245 actualOutput := rClean + "." + r[1] 1246 assert.Equal(t, expectedOutput, actualOutput) 1247 }) 1248 } 1249 } 1250 1251 func TestExtractRecordTypeDefaultPosition(t *testing.T) { 1252 tests := []struct { 1253 input string 1254 expectedName string 1255 expectedType string 1256 }{ 1257 { 1258 input: "ns-zone.example.com", 1259 expectedName: "zone.example.com", 1260 expectedType: "NS", 1261 }, 1262 { 1263 input: "aaaa-zone.example.com", 1264 expectedName: "zone.example.com", 1265 expectedType: "AAAA", 1266 }, 1267 { 1268 input: "ptr-zone.example.com", 1269 expectedName: "ptr-zone.example.com", 1270 expectedType: "", 1271 }, 1272 { 1273 input: "zone.example.com", 1274 expectedName: "zone.example.com", 1275 expectedType: "", 1276 }, 1277 } 1278 1279 for _, tc := range tests { 1280 t.Run(tc.input, func(t *testing.T) { 1281 actualName, actualType := extractRecordTypeDefaultPosition(tc.input) 1282 assert.Equal(t, tc.expectedName, actualName) 1283 assert.Equal(t, tc.expectedType, actualType) 1284 }) 1285 } 1286 } 1287 1288 func TestToEndpointNameNewTXT(t *testing.T) { 1289 tests := []struct { 1290 name string 1291 mapper affixNameMapper 1292 domain string 1293 txtDomain string 1294 recordType string 1295 }{ 1296 { 1297 name: "prefix", 1298 mapper: newaffixNameMapper("foo", "", ""), 1299 domain: "example.com", 1300 recordType: "A", 1301 txtDomain: "fooa-example.com", 1302 }, 1303 { 1304 name: "suffix", 1305 mapper: newaffixNameMapper("", "foo", ""), 1306 domain: "example.com", 1307 recordType: "AAAA", 1308 txtDomain: "aaaa-examplefoo.com", 1309 }, 1310 { 1311 name: "prefix with dash", 1312 mapper: newaffixNameMapper("foo-", "", ""), 1313 domain: "example.com", 1314 recordType: "A", 1315 txtDomain: "foo-a-example.com", 1316 }, 1317 { 1318 name: "suffix with dash", 1319 mapper: newaffixNameMapper("", "-foo", ""), 1320 domain: "example.com", 1321 recordType: "CNAME", 1322 txtDomain: "cname-example-foo.com", 1323 }, 1324 { 1325 name: "prefix with dot", 1326 mapper: newaffixNameMapper("foo.", "", ""), 1327 domain: "example.com", 1328 recordType: "CNAME", 1329 txtDomain: "foo.cname-example.com", 1330 }, 1331 { 1332 name: "suffix with dot", 1333 mapper: newaffixNameMapper("", ".foo", ""), 1334 domain: "example.com", 1335 recordType: "CNAME", 1336 txtDomain: "cname-example.foo.com", 1337 }, 1338 { 1339 name: "prefix with multiple dots", 1340 mapper: newaffixNameMapper("foo.bar.", "", ""), 1341 domain: "example.com", 1342 recordType: "CNAME", 1343 txtDomain: "foo.bar.cname-example.com", 1344 }, 1345 { 1346 name: "suffix with multiple dots", 1347 mapper: newaffixNameMapper("", ".foo.bar.test", ""), 1348 domain: "example.com", 1349 recordType: "CNAME", 1350 txtDomain: "cname-example.foo.bar.test.com", 1351 }, 1352 { 1353 name: "templated prefix", 1354 mapper: newaffixNameMapper("%{record_type}-foo", "", ""), 1355 domain: "example.com", 1356 recordType: "A", 1357 txtDomain: "a-fooexample.com", 1358 }, 1359 { 1360 name: "templated suffix", 1361 mapper: newaffixNameMapper("", "foo-%{record_type}", ""), 1362 domain: "example.com", 1363 recordType: "A", 1364 txtDomain: "examplefoo-a.com", 1365 }, 1366 { 1367 name: "templated prefix with dot", 1368 mapper: newaffixNameMapper("%{record_type}foo.", "", ""), 1369 domain: "example.com", 1370 recordType: "CNAME", 1371 txtDomain: "cnamefoo.example.com", 1372 }, 1373 { 1374 name: "templated suffix with dot", 1375 mapper: newaffixNameMapper("", ".foo%{record_type}", ""), 1376 domain: "example.com", 1377 recordType: "A", 1378 txtDomain: "example.fooa.com", 1379 }, 1380 { 1381 name: "templated prefix with multiple dots", 1382 mapper: newaffixNameMapper("bar.%{record_type}.foo.", "", ""), 1383 domain: "example.com", 1384 recordType: "CNAME", 1385 txtDomain: "bar.cname.foo.example.com", 1386 }, 1387 { 1388 name: "templated suffix with multiple dots", 1389 mapper: newaffixNameMapper("", ".foo%{record_type}.bar", ""), 1390 domain: "example.com", 1391 recordType: "A", 1392 txtDomain: "example.fooa.bar.com", 1393 }, 1394 } 1395 1396 for _, tc := range tests { 1397 t.Run(tc.name, func(t *testing.T) { 1398 txtDomain := tc.mapper.toNewTXTName(tc.domain, tc.recordType) 1399 assert.Equal(t, tc.txtDomain, txtDomain) 1400 1401 domain, _ := tc.mapper.toEndpointName(txtDomain) 1402 assert.Equal(t, tc.domain, domain) 1403 }) 1404 } 1405 } 1406 1407 func TestNewTXTScheme(t *testing.T) { 1408 p := inmemory.NewInMemoryProvider() 1409 p.CreateZone(testZone) 1410 ctxEndpoints := []*endpoint.Endpoint{} 1411 ctx := context.WithValue(context.Background(), provider.RecordsContextKey, ctxEndpoints) 1412 p.OnApplyChanges = func(ctx context.Context, got *plan.Changes) { 1413 assert.Equal(t, ctxEndpoints, ctx.Value(provider.RecordsContextKey)) 1414 } 1415 p.ApplyChanges(ctx, &plan.Changes{ 1416 Create: []*endpoint.Endpoint{ 1417 newEndpointWithOwner("foo.test-zone.example.org", "foo.loadbalancer.com", endpoint.RecordTypeCNAME, ""), 1418 newEndpointWithOwner("bar.test-zone.example.org", "my-domain.com", endpoint.RecordTypeCNAME, ""), 1419 newEndpointWithOwner("txt.bar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 1420 newEndpointWithOwner("txt.bar.test-zone.example.org", "baz.test-zone.example.org", endpoint.RecordTypeCNAME, ""), 1421 newEndpointWithOwner("qux.test-zone.example.org", "random", endpoint.RecordTypeTXT, ""), 1422 newEndpointWithOwner("tar.test-zone.example.org", "tar.loadbalancer.com", endpoint.RecordTypeCNAME, ""), 1423 newEndpointWithOwner("txt.tar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 1424 newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, ""), 1425 newEndpointWithOwner("foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 1426 newEndpointWithOwner("cname-foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), 1427 }, 1428 }) 1429 r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil) 1430 1431 changes := &plan.Changes{ 1432 Create: []*endpoint.Endpoint{ 1433 newEndpointWithOwner("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, ""), 1434 newEndpointWithOwner("example", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, ""), 1435 }, 1436 Delete: []*endpoint.Endpoint{ 1437 newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"), 1438 }, 1439 UpdateNew: []*endpoint.Endpoint{ 1440 newEndpointWithOwner("tar.test-zone.example.org", "new-tar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner-2"), 1441 }, 1442 UpdateOld: []*endpoint.Endpoint{ 1443 newEndpointWithOwner("tar.test-zone.example.org", "tar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner-2"), 1444 }, 1445 } 1446 expected := &plan.Changes{ 1447 Create: []*endpoint.Endpoint{ 1448 newEndpointWithOwner("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner"), 1449 newEndpointWithOwnerAndOwnedRecord("new-record-1.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "new-record-1.test-zone.example.org"), 1450 newEndpointWithOwnerAndOwnedRecord("cname-new-record-1.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "new-record-1.test-zone.example.org"), 1451 newEndpointWithOwner("example", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "owner"), 1452 newEndpointWithOwnerAndOwnedRecord("example", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "example"), 1453 newEndpointWithOwnerAndOwnedRecord("cname-example", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "example"), 1454 }, 1455 Delete: []*endpoint.Endpoint{ 1456 newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"), 1457 newEndpointWithOwnerAndOwnedRecord("foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "foobar.test-zone.example.org"), 1458 newEndpointWithOwnerAndOwnedRecord("cname-foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "", "foobar.test-zone.example.org"), 1459 }, 1460 UpdateNew: []*endpoint.Endpoint{}, 1461 UpdateOld: []*endpoint.Endpoint{}, 1462 } 1463 p.OnApplyChanges = func(ctx context.Context, got *plan.Changes) { 1464 mExpected := map[string][]*endpoint.Endpoint{ 1465 "Create": expected.Create, 1466 "UpdateNew": expected.UpdateNew, 1467 "UpdateOld": expected.UpdateOld, 1468 "Delete": expected.Delete, 1469 } 1470 mGot := map[string][]*endpoint.Endpoint{ 1471 "Create": got.Create, 1472 "UpdateNew": got.UpdateNew, 1473 "UpdateOld": got.UpdateOld, 1474 "Delete": got.Delete, 1475 } 1476 assert.True(t, testutils.SamePlanChanges(mGot, mExpected)) 1477 assert.Equal(t, nil, ctx.Value(provider.RecordsContextKey)) 1478 } 1479 err := r.ApplyChanges(ctx, changes) 1480 require.NoError(t, err) 1481 } 1482 1483 func TestGenerateTXT(t *testing.T) { 1484 record := newEndpointWithOwner("foo.test-zone.example.org", "new-foo.loadbalancer.com", endpoint.RecordTypeCNAME, "owner") 1485 expectedTXT := []*endpoint.Endpoint{ 1486 { 1487 DNSName: "foo.test-zone.example.org", 1488 Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=owner\""}, 1489 RecordType: endpoint.RecordTypeTXT, 1490 Labels: map[string]string{ 1491 endpoint.OwnedRecordLabelKey: "foo.test-zone.example.org", 1492 }, 1493 }, 1494 { 1495 DNSName: "cname-foo.test-zone.example.org", 1496 Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=owner\""}, 1497 RecordType: endpoint.RecordTypeTXT, 1498 Labels: map[string]string{ 1499 endpoint.OwnedRecordLabelKey: "foo.test-zone.example.org", 1500 }, 1501 }, 1502 } 1503 p := inmemory.NewInMemoryProvider() 1504 p.CreateZone(testZone) 1505 r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil) 1506 gotTXT := r.generateTXTRecord(record) 1507 assert.Equal(t, expectedTXT, gotTXT) 1508 } 1509 1510 func TestGenerateTXTForAAAA(t *testing.T) { 1511 record := newEndpointWithOwner("foo.test-zone.example.org", "2001:DB8::1", endpoint.RecordTypeAAAA, "owner") 1512 expectedTXT := []*endpoint.Endpoint{ 1513 { 1514 DNSName: "aaaa-foo.test-zone.example.org", 1515 Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=owner\""}, 1516 RecordType: endpoint.RecordTypeTXT, 1517 Labels: map[string]string{ 1518 endpoint.OwnedRecordLabelKey: "foo.test-zone.example.org", 1519 }, 1520 }, 1521 } 1522 p := inmemory.NewInMemoryProvider() 1523 p.CreateZone(testZone) 1524 r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil) 1525 gotTXT := r.generateTXTRecord(record) 1526 assert.Equal(t, expectedTXT, gotTXT) 1527 } 1528 1529 func TestFailGenerateTXT(t *testing.T) { 1530 1531 cnameRecord := &endpoint.Endpoint{ 1532 DNSName: "foo-some-really-big-name-not-supported-and-will-fail-000000000000000000.test-zone.example.org", 1533 Targets: endpoint.Targets{"new-foo.loadbalancer.com"}, 1534 RecordType: endpoint.RecordTypeCNAME, 1535 Labels: map[string]string{}, 1536 } 1537 // A bad DNS name returns empty expected TXT 1538 expectedTXT := []*endpoint.Endpoint{} 1539 p := inmemory.NewInMemoryProvider() 1540 p.CreateZone(testZone) 1541 r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil) 1542 gotTXT := r.generateTXTRecord(cnameRecord) 1543 assert.Equal(t, expectedTXT, gotTXT) 1544 } 1545 1546 func TestTXTRegistryApplyChangesEncrypt(t *testing.T) { 1547 p := inmemory.NewInMemoryProvider() 1548 p.CreateZone(testZone) 1549 ctxEndpoints := []*endpoint.Endpoint{} 1550 ctx := context.WithValue(context.Background(), provider.RecordsContextKey, ctxEndpoints) 1551 1552 p.ApplyChanges(ctx, &plan.Changes{ 1553 Create: []*endpoint.Endpoint{ 1554 newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, ""), 1555 newEndpointWithOwnerAndOwnedRecord("txt.cname-foobar.test-zone.example.org", "\"h8UQ6jelUFUsEIn7SbFktc2MYXPx/q8lySqI4VwfVtVaIbb2nkHWV/88KKbuLtu7fJNzMir8ELVeVnRSY01KdiIuj7ledqZe5ailEjQaU5Z6uEKd5pgs6sH8\"", endpoint.RecordTypeTXT, "", "foobar.test-zone.example.org"), 1556 }, 1557 }) 1558 1559 r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "", []string{}, []string{}, true, []byte("12345678901234567890123456789012")) 1560 records, _ := r.Records(ctx) 1561 changes := &plan.Changes{ 1562 Delete: records, 1563 } 1564 1565 // ensure that encryption nonce gets reused when deleting records 1566 expected := &plan.Changes{ 1567 Delete: []*endpoint.Endpoint{ 1568 newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, "owner"), 1569 newEndpointWithOwnerAndOwnedRecord("txt.cname-foobar.test-zone.example.org", "\"h8UQ6jelUFUsEIn7SbFktc2MYXPx/q8lySqI4VwfVtVaIbb2nkHWV/88KKbuLtu7fJNzMir8ELVeVnRSY01KdiIuj7ledqZe5ailEjQaU5Z6uEKd5pgs6sH8\"", endpoint.RecordTypeTXT, "", "foobar.test-zone.example.org"), 1570 }, 1571 } 1572 1573 p.OnApplyChanges = func(ctx context.Context, got *plan.Changes) { 1574 mExpected := map[string][]*endpoint.Endpoint{ 1575 "Delete": expected.Delete, 1576 } 1577 mGot := map[string][]*endpoint.Endpoint{ 1578 "Delete": got.Delete, 1579 } 1580 assert.True(t, testutils.SamePlanChanges(mGot, mExpected)) 1581 assert.Equal(t, nil, ctx.Value(provider.RecordsContextKey)) 1582 } 1583 err := r.ApplyChanges(ctx, changes) 1584 require.NoError(t, err) 1585 } 1586 1587 // TestMultiClusterDifferentRecordTypeOwnership validates the registry handles environments where the same zone is managed by 1588 // external-dns in different clusters and the ingress record type is different. For example one uses A records and the other 1589 // uses CNAME. In this environment the first cluster that establishes the owner record should maintain ownership even 1590 // if the same ingress host is deployed to the other. With the introduction of Dual Record support each record type 1591 // was treated independently and would cause each cluster to fight over ownership. This tests ensure that the default 1592 // Dual Stack record support only treats AAAA records independently and while keeping A and CNAME record ownership intact. 1593 func TestMultiClusterDifferentRecordTypeOwnership(t *testing.T) { 1594 ctx := context.Background() 1595 p := inmemory.NewInMemoryProvider() 1596 p.CreateZone(testZone) 1597 p.ApplyChanges(ctx, &plan.Changes{ 1598 Create: []*endpoint.Endpoint{ 1599 // records on cluster using A record for ingress address 1600 newEndpointWithOwner("bar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=cat,external-dns/resource=ingress/default/foo\"", endpoint.RecordTypeTXT, ""), 1601 newEndpointWithOwner("bar.test-zone.example.org", "1.2.3.4", endpoint.RecordTypeA, ""), 1602 }, 1603 }) 1604 1605 r, _ := NewTXTRegistry(p, "_owner.", "", "bar", time.Hour, "", []string{}, []string{}, false, nil) 1606 records, _ := r.Records(ctx) 1607 1608 // new cluster has same ingress host as other cluster and uses CNAME ingress address 1609 cname := &endpoint.Endpoint{ 1610 DNSName: "bar.test-zone.example.org", 1611 Targets: endpoint.Targets{"cluster-b"}, 1612 RecordType: "CNAME", 1613 Labels: map[string]string{ 1614 endpoint.ResourceLabelKey: "ingress/default/foo-127", 1615 }, 1616 } 1617 desired := []*endpoint.Endpoint{cname} 1618 1619 pl := &plan.Plan{ 1620 Policies: []plan.Policy{&plan.SyncPolicy{}}, 1621 Current: records, 1622 Desired: desired, 1623 ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}, 1624 } 1625 1626 changes := pl.Calculate() 1627 p.OnApplyChanges = func(ctx context.Context, changes *plan.Changes) { 1628 got := map[string][]*endpoint.Endpoint{ 1629 "Create": changes.Create, 1630 "UpdateNew": changes.UpdateNew, 1631 "UpdateOld": changes.UpdateOld, 1632 "Delete": changes.Delete, 1633 } 1634 expected := map[string][]*endpoint.Endpoint{ 1635 "Create": {}, 1636 "UpdateNew": {}, 1637 "UpdateOld": {}, 1638 "Delete": {}, 1639 } 1640 testutils.SamePlanChanges(got, expected) 1641 } 1642 1643 err := r.ApplyChanges(ctx, changes.Changes) 1644 if err != nil { 1645 t.Error(err) 1646 } 1647 } 1648 1649 /** 1650 1651 helper methods 1652 1653 */ 1654 1655 func newEndpointWithOwner(dnsName, target, recordType, ownerID string) *endpoint.Endpoint { 1656 return newEndpointWithOwnerAndLabels(dnsName, target, recordType, ownerID, nil) 1657 } 1658 1659 func newEndpointWithOwnerAndOwnedRecord(dnsName, target, recordType, ownerID, ownedRecord string) *endpoint.Endpoint { 1660 return newEndpointWithOwnerAndLabels(dnsName, target, recordType, ownerID, endpoint.Labels{endpoint.OwnedRecordLabelKey: ownedRecord}) 1661 } 1662 1663 func newEndpointWithOwnerAndLabels(dnsName, target, recordType, ownerID string, labels endpoint.Labels) *endpoint.Endpoint { 1664 e := endpoint.NewEndpoint(dnsName, recordType, target) 1665 e.Labels[endpoint.OwnerLabelKey] = ownerID 1666 for k, v := range labels { 1667 e.Labels[k] = v 1668 } 1669 return e 1670 } 1671 1672 func newEndpointWithOwnerResource(dnsName, target, recordType, ownerID, resource string) *endpoint.Endpoint { 1673 e := endpoint.NewEndpoint(dnsName, recordType, target) 1674 e.Labels[endpoint.OwnerLabelKey] = ownerID 1675 e.Labels[endpoint.ResourceLabelKey] = resource 1676 return e 1677 }