sigs.k8s.io/external-dns@v0.14.1/provider/rfc2136/rfc2136_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 rfc2136 18 19 import ( 20 "context" 21 "crypto/tls" 22 "fmt" 23 "os" 24 "regexp" 25 "sort" 26 "strings" 27 "testing" 28 "time" 29 30 "github.com/miekg/dns" 31 log "github.com/sirupsen/logrus" 32 "github.com/stretchr/testify/assert" 33 34 "sigs.k8s.io/external-dns/endpoint" 35 "sigs.k8s.io/external-dns/plan" 36 "sigs.k8s.io/external-dns/provider" 37 ) 38 39 type rfc2136Stub struct { 40 output []*dns.Envelope 41 updateMsgs []*dns.Msg 42 createMsgs []*dns.Msg 43 } 44 45 func newStub() *rfc2136Stub { 46 return &rfc2136Stub{ 47 output: make([]*dns.Envelope, 0), 48 updateMsgs: make([]*dns.Msg, 0), 49 createMsgs: make([]*dns.Msg, 0), 50 } 51 } 52 53 func getSortedChanges(msgs []*dns.Msg) []string { 54 r := []string{} 55 for _, d := range msgs { 56 // only care about section after the ZONE SECTION: as the id: needs stripped out in order to sort and grantee the order when sorting 57 r = append(r, strings.Split(d.String(), "ZONE SECTION:")[1]) 58 } 59 sort.Strings(r) 60 return r 61 } 62 63 func (r *rfc2136Stub) SendMessage(msg *dns.Msg) error { 64 zone := extractZoneFromMessage(msg.String()) 65 // Make sure the zone starts with . to make sure HasSuffix does not match forbar.com for zone bar.com 66 if !strings.HasPrefix(zone, ".") { 67 zone = "." + zone 68 } 69 log.Infof("zone=%s", zone) 70 lines := extractUpdateSectionFromMessage(msg) 71 for _, line := range lines { 72 // break at first empty line 73 if len(strings.TrimSpace(line)) == 0 { 74 break 75 } 76 77 line = strings.Replace(line, "\t", " ", -1) 78 log.Info(line) 79 record := strings.Split(line, " ")[0] 80 if !strings.HasSuffix(record, zone) { 81 err := fmt.Errorf("Message contains updates outside of it's zone. zone=%v record=%v", zone, record) 82 log.Error(err) 83 return err 84 } 85 86 if strings.Contains(line, " NONE ") { 87 r.updateMsgs = append(r.updateMsgs, msg) 88 } else if strings.Contains(line, " IN ") { 89 r.createMsgs = append(r.createMsgs, msg) 90 } 91 } 92 93 return nil 94 } 95 96 func (r *rfc2136Stub) setOutput(output []string) error { 97 r.output = make([]*dns.Envelope, len(output)) 98 for i, e := range output { 99 rr, err := dns.NewRR(e) 100 if err != nil { 101 return err 102 } 103 r.output[i] = &dns.Envelope{ 104 RR: []dns.RR{rr}, 105 } 106 } 107 return nil 108 } 109 110 func (r *rfc2136Stub) IncomeTransfer(m *dns.Msg, a string) (env chan *dns.Envelope, err error) { 111 outChan := make(chan *dns.Envelope) 112 go func() { 113 for _, e := range r.output { 114 outChan <- e 115 } 116 close(outChan) 117 }() 118 119 return outChan, nil 120 } 121 122 func createRfc2136StubProvider(stub *rfc2136Stub) (provider.Provider, error) { 123 tlsConfig := TLSConfig{ 124 UseTLS: false, 125 SkipTLSVerify: false, 126 CAFilePath: "", 127 ClientCertFilePath: "", 128 ClientCertKeyFilePath: "", 129 } 130 return NewRfc2136Provider("", 0, nil, false, "key", "secret", "hmac-sha512", true, endpoint.DomainFilter{}, false, 300*time.Second, false, "", "", "", 50, tlsConfig, stub) 131 } 132 133 func createRfc2136TLSStubProvider(stub *rfc2136Stub, tlsConfig TLSConfig) (provider.Provider, error) { 134 return NewRfc2136Provider("rfc2136-host", 0, nil, false, "key", "secret", "hmac-sha512", true, endpoint.DomainFilter{}, false, 300*time.Second, false, "", "", "", 50, tlsConfig, stub) 135 } 136 137 func createRfc2136StubProviderWithZones(stub *rfc2136Stub) (provider.Provider, error) { 138 tlsConfig := TLSConfig{ 139 UseTLS: false, 140 SkipTLSVerify: false, 141 CAFilePath: "", 142 ClientCertFilePath: "", 143 ClientCertKeyFilePath: "", 144 } 145 zones := []string{"foo.com", "foobar.com"} 146 return NewRfc2136Provider("", 0, zones, false, "key", "secret", "hmac-sha512", true, endpoint.DomainFilter{}, false, 300*time.Second, false, "", "", "", 50, tlsConfig, stub) 147 } 148 149 func createRfc2136StubProviderWithZonesFilters(stub *rfc2136Stub) (provider.Provider, error) { 150 tlsConfig := TLSConfig{ 151 UseTLS: false, 152 SkipTLSVerify: false, 153 CAFilePath: "", 154 ClientCertFilePath: "", 155 ClientCertKeyFilePath: "", 156 } 157 zones := []string{"foo.com", "foobar.com"} 158 return NewRfc2136Provider("", 0, zones, false, "key", "secret", "hmac-sha512", true, endpoint.DomainFilter{Filters: zones}, false, 300*time.Second, false, "", "", "", 50, tlsConfig, stub) 159 } 160 161 func extractUpdateSectionFromMessage(msg fmt.Stringer) []string { 162 const searchPattern = "UPDATE SECTION:" 163 updateSectionOffset := strings.Index(msg.String(), searchPattern) 164 return strings.Split(strings.TrimSpace(msg.String()[updateSectionOffset+len(searchPattern):]), "\n") 165 } 166 167 func extractZoneFromMessage(msg string) string { 168 re := regexp.MustCompile(`ZONE SECTION:\n;(?P<ZONE>[\.,\-,\w,\d]+)\t`) 169 matches := re.FindStringSubmatch(msg) 170 return matches[re.SubexpIndex("ZONE")] 171 } 172 173 // TestRfc2136GetRecordsMultipleTargets simulates a single record with multiple targets. 174 func TestRfc2136GetRecordsMultipleTargets(t *testing.T) { 175 stub := newStub() 176 err := stub.setOutput([]string{ 177 "foo.com 3600 IN A 1.1.1.1", 178 "foo.com 3600 IN A 2.2.2.2", 179 }) 180 assert.NoError(t, err) 181 182 provider, err := createRfc2136StubProvider(stub) 183 assert.NoError(t, err) 184 185 recs, err := provider.Records(context.Background()) 186 assert.NoError(t, err) 187 188 assert.Equal(t, 1, len(recs), "expected single record") 189 assert.Equal(t, recs[0].DNSName, "foo.com") 190 assert.Equal(t, 2, len(recs[0].Targets), "expected two targets") 191 assert.True(t, recs[0].Targets[0] == "1.1.1.1" || recs[0].Targets[1] == "1.1.1.1") // ignore order 192 assert.True(t, recs[0].Targets[0] == "2.2.2.2" || recs[0].Targets[1] == "2.2.2.2") // ignore order 193 assert.Equal(t, recs[0].RecordType, "A") 194 assert.Equal(t, recs[0].RecordTTL, endpoint.TTL(3600)) 195 assert.Equal(t, 0, len(recs[0].Labels), "expected no labels") 196 assert.Equal(t, 0, len(recs[0].ProviderSpecific), "expected no provider specific config") 197 } 198 199 func TestRfc2136TLSConfig(t *testing.T) { 200 stub := newStub() 201 202 caFile, err := os.CreateTemp("", "rfc2136-test-XXXXXXXX.crt") 203 assert.NoError(t, err) 204 defer os.Remove(caFile.Name()) 205 _, err = caFile.Write([]byte( 206 `-----BEGIN CERTIFICATE----- 207 MIH+MIGxAhR2n1aQk0ONrQ8QQfa6GCzFWLmTXTAFBgMrZXAwITELMAkGA1UEBhMC 208 REUxEjAQBgNVBAMMCWxvY2FsaG9zdDAgFw0yMzEwMjQwNzI5NDNaGA8yMTIzMDkz 209 MDA3Mjk0M1owITELMAkGA1UEBhMCREUxEjAQBgNVBAMMCWxvY2FsaG9zdDAqMAUG 210 AytlcAMhAA1FzGJXuQdOpKv02SEl7SIA8SP8RVRI0QTi1bUFiFBLMAUGAytlcANB 211 ADiCKRUGDMyafSSYhl0KXoiXrFOxvhrGM5l15L4q82JM5Qb8wv0gNrnbGTZlInuv 212 ouB5ZN+05DzKCQhBekMnygQ= 213 -----END CERTIFICATE----- 214 `)) 215 216 tlsConfig := TLSConfig{ 217 UseTLS: true, 218 SkipTLSVerify: false, 219 CAFilePath: caFile.Name(), 220 ClientCertFilePath: "", 221 ClientCertKeyFilePath: "", 222 } 223 224 provider, err := createRfc2136TLSStubProvider(stub, tlsConfig) 225 assert.NoError(t, err) 226 227 rawProvider := provider.(*rfc2136Provider) 228 229 client, err := makeClient(*rawProvider) 230 assert.NoError(t, err) 231 232 assert.Equal(t, "tcp-tls", client.Net) 233 assert.Equal(t, false, client.TLSConfig.InsecureSkipVerify) 234 assert.Equal(t, "rfc2136-host", client.TLSConfig.ServerName) 235 assert.Equal(t, uint16(tls.VersionTLS13), client.TLSConfig.MinVersion) 236 assert.Equal(t, []string{"dot"}, client.TLSConfig.NextProtos) 237 } 238 239 func TestRfc2136TLSConfigNoVerify(t *testing.T) { 240 stub := newStub() 241 242 caFile, err := os.CreateTemp("", "rfc2136-test-XXXXXXXX.crt") 243 assert.NoError(t, err) 244 defer os.Remove(caFile.Name()) 245 _, err = caFile.Write([]byte( 246 `-----BEGIN CERTIFICATE----- 247 MIH+MIGxAhR2n1aQk0ONrQ8QQfa6GCzFWLmTXTAFBgMrZXAwITELMAkGA1UEBhMC 248 REUxEjAQBgNVBAMMCWxvY2FsaG9zdDAgFw0yMzEwMjQwNzI5NDNaGA8yMTIzMDkz 249 MDA3Mjk0M1owITELMAkGA1UEBhMCREUxEjAQBgNVBAMMCWxvY2FsaG9zdDAqMAUG 250 AytlcAMhAA1FzGJXuQdOpKv02SEl7SIA8SP8RVRI0QTi1bUFiFBLMAUGAytlcANB 251 ADiCKRUGDMyafSSYhl0KXoiXrFOxvhrGM5l15L4q82JM5Qb8wv0gNrnbGTZlInuv 252 ouB5ZN+05DzKCQhBekMnygQ= 253 -----END CERTIFICATE----- 254 `)) 255 256 tlsConfig := TLSConfig{ 257 UseTLS: true, 258 SkipTLSVerify: true, 259 CAFilePath: caFile.Name(), 260 ClientCertFilePath: "", 261 ClientCertKeyFilePath: "", 262 } 263 264 provider, err := createRfc2136TLSStubProvider(stub, tlsConfig) 265 assert.NoError(t, err) 266 267 rawProvider := provider.(*rfc2136Provider) 268 269 client, err := makeClient(*rawProvider) 270 assert.NoError(t, err) 271 272 assert.Equal(t, "tcp-tls", client.Net) 273 assert.Equal(t, true, client.TLSConfig.InsecureSkipVerify) 274 assert.Equal(t, "rfc2136-host", client.TLSConfig.ServerName) 275 assert.Equal(t, uint16(tls.VersionTLS13), client.TLSConfig.MinVersion) 276 assert.Equal(t, []string{"dot"}, client.TLSConfig.NextProtos) 277 } 278 279 func TestRfc2136TLSConfigClientAuth(t *testing.T) { 280 stub := newStub() 281 282 caFile, err := os.CreateTemp("", "rfc2136-test-XXXXXXXX.crt") 283 assert.NoError(t, err) 284 defer os.Remove(caFile.Name()) 285 _, err = caFile.Write([]byte( 286 `-----BEGIN CERTIFICATE----- 287 MIH+MIGxAhR2n1aQk0ONrQ8QQfa6GCzFWLmTXTAFBgMrZXAwITELMAkGA1UEBhMC 288 REUxEjAQBgNVBAMMCWxvY2FsaG9zdDAgFw0yMzEwMjQwNzI5NDNaGA8yMTIzMDkz 289 MDA3Mjk0M1owITELMAkGA1UEBhMCREUxEjAQBgNVBAMMCWxvY2FsaG9zdDAqMAUG 290 AytlcAMhAA1FzGJXuQdOpKv02SEl7SIA8SP8RVRI0QTi1bUFiFBLMAUGAytlcANB 291 ADiCKRUGDMyafSSYhl0KXoiXrFOxvhrGM5l15L4q82JM5Qb8wv0gNrnbGTZlInuv 292 ouB5ZN+05DzKCQhBekMnygQ= 293 -----END CERTIFICATE----- 294 `)) 295 296 certFile, err := os.CreateTemp("", "rfc2136-test-XXXXXXXX-client.crt") 297 assert.NoError(t, err) 298 defer os.Remove(certFile.Name()) 299 _, err = certFile.Write([]byte( 300 `-----BEGIN CERTIFICATE----- 301 MIIBfDCCAQICFANNDjPVDMTPm63C0jZ9M3H5I7GJMAoGCCqGSM49BAMCMCExCzAJ 302 BgNVBAYTAkRFMRIwEAYDVQQDDAlsb2NhbGhvc3QwIBcNMjMxMDI0MDcyMTU1WhgP 303 MjEyMzA5MzAwNzIxNTVaMCExCzAJBgNVBAYTAkRFMRIwEAYDVQQDDAlsb2NhbGhv 304 c3QwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQj7rjkeUEvjBT++IBMnIWgmI9VIjFx 305 4VUGFmzPEawOckdnKW4fBdePiItsgePDVK4Oys5bzfSDhl6aAPCe16pwvljB7yIm 306 xLJ+ytWk7OV/s10cmlaczrEtNeUjV1X9MTMwCgYIKoZIzj0EAwIDaAAwZQIwcZl8 307 TrwwsyX3A0enXB1ih+nruF8Q9f9Rmm2pNcbEv24QIW/P2HGQm9qfx4lrYa7hAjEA 308 goRP/fRfTTTLwLg8UBpUAmALX8A8HBSBaUlTTQcaImbcwU4DRSbv5JEA8tM1mWrA 309 -----END CERTIFICATE----- 310 `)) 311 312 keyFile, err := os.CreateTemp("", "rfc2136-test-XXXXXXXX-client.key") 313 assert.NoError(t, err) 314 defer os.Remove(keyFile.Name()) 315 _, err = keyFile.Write([]byte( 316 `-----BEGIN PRIVATE KEY----- 317 MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDD5B+aPE+TuHCvW1f7L 318 U8jEPVXHv1fvCR8uBSsf1qdPo929XGpt5y5QfIGdW3NUeHWhZANiAAQj7rjkeUEv 319 jBT++IBMnIWgmI9VIjFx4VUGFmzPEawOckdnKW4fBdePiItsgePDVK4Oys5bzfSD 320 hl6aAPCe16pwvljB7yImxLJ+ytWk7OV/s10cmlaczrEtNeUjV1X9MTM= 321 -----END PRIVATE KEY----- 322 `)) 323 324 tlsConfig := TLSConfig{ 325 UseTLS: true, 326 SkipTLSVerify: false, 327 CAFilePath: caFile.Name(), 328 ClientCertFilePath: certFile.Name(), 329 ClientCertKeyFilePath: keyFile.Name(), 330 } 331 332 provider, err := createRfc2136TLSStubProvider(stub, tlsConfig) 333 log.Infof("provider, err is: %s", err) 334 assert.NoError(t, err) 335 336 rawProvider := provider.(*rfc2136Provider) 337 338 client, err := makeClient(*rawProvider) 339 log.Infof("client, err is: %v", client) 340 log.Infof("client, err is: %s", err) 341 assert.NoError(t, err) 342 343 assert.Equal(t, "tcp-tls", client.Net) 344 assert.Equal(t, false, client.TLSConfig.InsecureSkipVerify) 345 assert.Equal(t, "rfc2136-host", client.TLSConfig.ServerName) 346 assert.Equal(t, uint16(tls.VersionTLS13), client.TLSConfig.MinVersion) 347 assert.Equal(t, []string{"dot"}, client.TLSConfig.NextProtos) 348 } 349 350 func TestRfc2136GetRecords(t *testing.T) { 351 stub := newStub() 352 err := stub.setOutput([]string{ 353 "v4.barfoo.com 3600 TXT test1", 354 "v1.foo.com 3600 TXT test2", 355 "v2.bar.com 3600 A 8.8.8.8", 356 "v3.bar.com 3600 TXT bbbb", 357 "v2.foo.com 3600 CNAME cccc", 358 "v1.foobar.com 3600 TXT dddd", 359 }) 360 assert.NoError(t, err) 361 362 provider, err := createRfc2136StubProvider(stub) 363 assert.NoError(t, err) 364 365 recs, err := provider.Records(context.Background()) 366 assert.NoError(t, err) 367 368 assert.Equal(t, 6, len(recs)) 369 assert.True(t, contains(recs, "v1.foo.com")) 370 assert.True(t, contains(recs, "v2.bar.com")) 371 assert.True(t, contains(recs, "v2.foo.com")) 372 } 373 374 // Make sure the test version of SendMessage raises an error 375 // if a zone update ever contains records outside of it's zone 376 // as the TestRfc2136ApplyChanges tests all assume this 377 func TestRfc2136SendMessage(t *testing.T) { 378 stub := newStub() 379 380 m := new(dns.Msg) 381 m.SetUpdate("foo.com.") 382 rr, err := dns.NewRR(fmt.Sprintf("%s %d %s %s", "v1.foo.com.", 0, "A", "1.2.3.4")) 383 m.Insert([]dns.RR{rr}) 384 385 err = stub.SendMessage(m) 386 assert.NoError(t, err) 387 388 rr, err = dns.NewRR(fmt.Sprintf("%s %d %s %s", "v1.bar.com.", 0, "A", "1.2.3.4")) 389 m.Insert([]dns.RR{rr}) 390 391 err = stub.SendMessage(m) 392 assert.Error(t, err) 393 394 m.SetUpdate(".") 395 err = stub.SendMessage(m) 396 assert.NoError(t, err) 397 } 398 399 // These tests are use the . root zone with no filters 400 func TestRfc2136ApplyChanges(t *testing.T) { 401 stub := newStub() 402 provider, err := createRfc2136StubProvider(stub) 403 assert.NoError(t, err) 404 405 p := &plan.Changes{ 406 Create: []*endpoint.Endpoint{ 407 { 408 DNSName: "v1.foo.com", 409 RecordType: "A", 410 Targets: []string{"1.2.3.4"}, 411 RecordTTL: endpoint.TTL(400), 412 }, 413 { 414 DNSName: "v1.foobar.com", 415 RecordType: "TXT", 416 Targets: []string{"boom"}, 417 }, 418 { 419 DNSName: "ns.foobar.com", 420 RecordType: "NS", 421 Targets: []string{"boom"}, 422 }, 423 }, 424 Delete: []*endpoint.Endpoint{ 425 { 426 DNSName: "v2.foo.com", 427 RecordType: "A", 428 Targets: []string{"1.2.3.4"}, 429 }, 430 { 431 DNSName: "v2.foobar.com", 432 RecordType: "TXT", 433 Targets: []string{"boom2"}, 434 }, 435 }, 436 } 437 438 err = provider.ApplyChanges(context.Background(), p) 439 assert.NoError(t, err) 440 441 assert.Equal(t, 3, len(stub.createMsgs)) 442 assert.True(t, strings.Contains(stub.createMsgs[0].String(), "v1.foo.com")) 443 assert.True(t, strings.Contains(stub.createMsgs[0].String(), "1.2.3.4")) 444 445 assert.True(t, strings.Contains(stub.createMsgs[1].String(), "v1.foobar.com")) 446 assert.True(t, strings.Contains(stub.createMsgs[1].String(), "boom")) 447 448 assert.True(t, strings.Contains(stub.createMsgs[2].String(), "ns.foobar.com")) 449 assert.True(t, strings.Contains(stub.createMsgs[2].String(), "boom")) 450 451 assert.Equal(t, 2, len(stub.updateMsgs)) 452 assert.True(t, strings.Contains(stub.updateMsgs[0].String(), "v2.foo.com")) 453 assert.True(t, strings.Contains(stub.updateMsgs[1].String(), "v2.foobar.com")) 454 } 455 456 // These tests all use the foo.com and foobar.com zones with no filters 457 // createMsgs and updateMsgs need sorted when are are used 458 func TestRfc2136ApplyChangesWithZones(t *testing.T) { 459 stub := newStub() 460 provider, err := createRfc2136StubProviderWithZones(stub) 461 assert.NoError(t, err) 462 463 p := &plan.Changes{ 464 Create: []*endpoint.Endpoint{ 465 { 466 DNSName: "v1.foo.com", 467 RecordType: "A", 468 Targets: []string{"1.2.3.4"}, 469 RecordTTL: endpoint.TTL(400), 470 }, 471 { 472 DNSName: "v1.foobar.com", 473 RecordType: "TXT", 474 Targets: []string{"boom"}, 475 }, 476 { 477 DNSName: "ns.foobar.com", 478 RecordType: "NS", 479 Targets: []string{"boom"}, 480 }, 481 }, 482 Delete: []*endpoint.Endpoint{ 483 { 484 DNSName: "v2.foo.com", 485 RecordType: "A", 486 Targets: []string{"1.2.3.4"}, 487 }, 488 { 489 DNSName: "v2.foobar.com", 490 RecordType: "TXT", 491 Targets: []string{"boom2"}, 492 }, 493 }, 494 } 495 496 err = provider.ApplyChanges(context.Background(), p) 497 assert.NoError(t, err) 498 499 assert.Equal(t, 3, len(stub.createMsgs)) 500 createMsgs := getSortedChanges(stub.createMsgs) 501 assert.Equal(t, 3, len(createMsgs)) 502 503 assert.True(t, strings.Contains(createMsgs[0], "v1.foo.com")) 504 assert.True(t, strings.Contains(createMsgs[0], "1.2.3.4")) 505 506 assert.True(t, strings.Contains(createMsgs[1], "v1.foobar.com")) 507 assert.True(t, strings.Contains(createMsgs[1], "boom")) 508 509 assert.True(t, strings.Contains(createMsgs[2], "ns.foobar.com")) 510 assert.True(t, strings.Contains(createMsgs[2], "boom")) 511 512 assert.Equal(t, 2, len(stub.updateMsgs)) 513 updateMsgs := getSortedChanges(stub.updateMsgs) 514 assert.Equal(t, 2, len(updateMsgs)) 515 516 assert.True(t, strings.Contains(updateMsgs[0], "v2.foo.com")) 517 assert.True(t, strings.Contains(updateMsgs[1], "v2.foobar.com")) 518 } 519 520 // These tests use the foo.com and foobar.com zones and with filters set to both zones 521 // createMsgs and updateMsgs need sorted when are are used 522 func TestRfc2136ApplyChangesWithZonesFilters(t *testing.T) { 523 stub := newStub() 524 provider, err := createRfc2136StubProviderWithZonesFilters(stub) 525 assert.NoError(t, err) 526 527 p := &plan.Changes{ 528 Create: []*endpoint.Endpoint{ 529 { 530 DNSName: "v1.foo.com", 531 RecordType: "A", 532 Targets: []string{"1.2.3.4"}, 533 RecordTTL: endpoint.TTL(400), 534 }, 535 { 536 DNSName: "v1.foobar.com", 537 RecordType: "TXT", 538 Targets: []string{"boom"}, 539 }, 540 { 541 DNSName: "ns.foobar.com", 542 RecordType: "NS", 543 Targets: []string{"boom"}, 544 }, 545 { 546 DNSName: "filtered-out.foo.bar", 547 RecordType: "A", 548 Targets: []string{"1.2.3.4"}, 549 RecordTTL: endpoint.TTL(400), 550 }, 551 }, 552 Delete: []*endpoint.Endpoint{ 553 { 554 DNSName: "v2.foo.com", 555 RecordType: "A", 556 Targets: []string{"1.2.3.4"}, 557 }, 558 { 559 DNSName: "v2.foobar.com", 560 RecordType: "TXT", 561 Targets: []string{"boom2"}, 562 }, 563 }, 564 } 565 566 err = provider.ApplyChanges(context.Background(), p) 567 assert.NoError(t, err) 568 569 assert.Equal(t, 3, len(stub.createMsgs)) 570 createMsgs := getSortedChanges(stub.createMsgs) 571 assert.Equal(t, 3, len(createMsgs)) 572 573 assert.True(t, strings.Contains(createMsgs[0], "v1.foo.com")) 574 assert.True(t, strings.Contains(createMsgs[0], "1.2.3.4")) 575 576 assert.True(t, strings.Contains(createMsgs[1], "v1.foobar.com")) 577 assert.True(t, strings.Contains(createMsgs[1], "boom")) 578 579 assert.True(t, strings.Contains(createMsgs[2], "ns.foobar.com")) 580 assert.True(t, strings.Contains(createMsgs[2], "boom")) 581 582 for _, s := range createMsgs { 583 assert.False(t, strings.Contains(s, "filtered-out.foo.bar")) 584 } 585 586 assert.Equal(t, 2, len(stub.updateMsgs)) 587 updateMsgs := getSortedChanges(stub.updateMsgs) 588 assert.Equal(t, 2, len(updateMsgs)) 589 590 assert.True(t, strings.Contains(updateMsgs[0], "v2.foo.com")) 591 assert.True(t, strings.Contains(updateMsgs[1], "v2.foobar.com")) 592 593 } 594 595 func TestRfc2136ApplyChangesWithDifferentTTLs(t *testing.T) { 596 stub := newStub() 597 598 provider, err := createRfc2136StubProvider(stub) 599 assert.NoError(t, err) 600 601 p := &plan.Changes{ 602 Create: []*endpoint.Endpoint{ 603 { 604 DNSName: "v1.foo.com", 605 RecordType: "A", 606 Targets: []string{"2.1.1.1"}, 607 RecordTTL: endpoint.TTL(400), 608 }, 609 { 610 DNSName: "v2.foo.com", 611 RecordType: "A", 612 Targets: []string{"3.2.2.2"}, 613 RecordTTL: endpoint.TTL(200), 614 }, 615 { 616 DNSName: "v3.foo.com", 617 RecordType: "A", 618 Targets: []string{"4.3.3.3"}, 619 }, 620 }, 621 } 622 623 err = provider.ApplyChanges(context.Background(), p) 624 assert.NoError(t, err) 625 626 createRecords := extractUpdateSectionFromMessage(stub.createMsgs[0]) 627 assert.Equal(t, 3, len(createRecords)) 628 assert.True(t, strings.Contains(createRecords[0], "v1.foo.com")) 629 assert.True(t, strings.Contains(createRecords[0], "2.1.1.1")) 630 assert.True(t, strings.Contains(createRecords[0], "400")) 631 assert.True(t, strings.Contains(createRecords[1], "v2.foo.com")) 632 assert.True(t, strings.Contains(createRecords[1], "3.2.2.2")) 633 assert.True(t, strings.Contains(createRecords[1], "300")) 634 assert.True(t, strings.Contains(createRecords[2], "v3.foo.com")) 635 assert.True(t, strings.Contains(createRecords[2], "4.3.3.3")) 636 assert.True(t, strings.Contains(createRecords[2], "300")) 637 } 638 639 func TestRfc2136ApplyChangesWithUpdate(t *testing.T) { 640 stub := newStub() 641 642 provider, err := createRfc2136StubProvider(stub) 643 assert.NoError(t, err) 644 645 p := &plan.Changes{ 646 Create: []*endpoint.Endpoint{ 647 { 648 DNSName: "v1.foo.com", 649 RecordType: "A", 650 Targets: []string{"1.2.3.4"}, 651 RecordTTL: endpoint.TTL(400), 652 }, 653 { 654 DNSName: "v1.foobar.com", 655 RecordType: "TXT", 656 Targets: []string{"boom"}, 657 }, 658 }, 659 } 660 661 err = provider.ApplyChanges(context.Background(), p) 662 assert.NoError(t, err) 663 664 p = &plan.Changes{ 665 UpdateOld: []*endpoint.Endpoint{ 666 { 667 DNSName: "v1.foo.com", 668 RecordType: "A", 669 Targets: []string{"1.2.3.4"}, 670 RecordTTL: endpoint.TTL(400), 671 }, 672 { 673 DNSName: "v1.foobar.com", 674 RecordType: "TXT", 675 Targets: []string{"boom"}, 676 }, 677 }, 678 UpdateNew: []*endpoint.Endpoint{ 679 { 680 DNSName: "v1.foo.com", 681 RecordType: "A", 682 Targets: []string{"1.2.3.5"}, 683 RecordTTL: endpoint.TTL(400), 684 }, 685 { 686 DNSName: "v1.foobar.com", 687 RecordType: "TXT", 688 Targets: []string{"kablui"}, 689 }, 690 }, 691 } 692 693 err = provider.ApplyChanges(context.Background(), p) 694 assert.NoError(t, err) 695 696 assert.Equal(t, 4, len(stub.createMsgs)) 697 assert.Equal(t, 2, len(stub.updateMsgs)) 698 699 assert.True(t, strings.Contains(stub.createMsgs[0].String(), "v1.foo.com")) 700 assert.True(t, strings.Contains(stub.createMsgs[0].String(), "1.2.3.4")) 701 assert.True(t, strings.Contains(stub.createMsgs[2].String(), "v1.foo.com")) 702 assert.True(t, strings.Contains(stub.createMsgs[2].String(), "1.2.3.5")) 703 704 assert.True(t, strings.Contains(stub.updateMsgs[0].String(), "v1.foo.com")) 705 assert.True(t, strings.Contains(stub.updateMsgs[0].String(), "1.2.3.4")) 706 707 assert.True(t, strings.Contains(stub.createMsgs[1].String(), "v1.foobar.com")) 708 assert.True(t, strings.Contains(stub.createMsgs[1].String(), "boom")) 709 assert.True(t, strings.Contains(stub.createMsgs[3].String(), "v1.foobar.com")) 710 assert.True(t, strings.Contains(stub.createMsgs[3].String(), "kablui")) 711 712 assert.True(t, strings.Contains(stub.updateMsgs[1].String(), "v1.foobar.com")) 713 assert.True(t, strings.Contains(stub.updateMsgs[1].String(), "boom")) 714 } 715 716 func TestChunkBy(t *testing.T) { 717 var records []*endpoint.Endpoint 718 719 for i := 0; i < 10; i++ { 720 records = append(records, &endpoint.Endpoint{ 721 DNSName: "v1.foo.com", 722 RecordType: "A", 723 Targets: []string{"1.1.2.2"}, 724 RecordTTL: endpoint.TTL(400), 725 }) 726 } 727 728 chunks := chunkBy(records, 2) 729 if len(chunks) != 5 { 730 t.Errorf("incorrect number of chunks returned") 731 } 732 } 733 734 func contains(arr []*endpoint.Endpoint, name string) bool { 735 for _, a := range arr { 736 if a.DNSName == name { 737 return true 738 } 739 } 740 return false 741 }