func TestRRShuffling(t *testing.T) { rr := rrstore.New() recs := []string{"10.0.0.1", "10.0.0.2", "10.0.0.3"} rr.Set(map[uint16]map[string][]string{ dns.TypeA: {"a.domain.": []string{"10.0.0.1", "10.0.0.2", "10.0.0.3"}}}) srv, ready := testServer(t, rr) <-ready defer srv.Shutdown() n := 50 same := true for i := 0; i < n; i++ { r, err := query(srv.Addr, "a.domain.", dns.TypeA) if err != nil { t.Fatal(err) } as := make([]string, len(r.Answer)) for i, _ := range r.Answer { as[i] = r.Answer[i].(*dns.A).A.String() if len(as) != len(recs) { t.Fatalf("wrong answer count: %d", len(as)) } } if !reflect.DeepEqual(as, recs) { same = false break } } if same { t.Fatalf("same RR ordering occurred even after %d requests", n) } }
// serve starts the DNS server and blocks. func serve(opt *Options) { dockerTLS, err := tlsConfig(opt.tlsDir, opt.tlsVerify) if err != nil { log.Fatalf("Error establishing TLS config: %v", err) } rrs := rrstore.New() cluster, err := swarm.New(opt.swarmAddr, dockerTLS) if err != nil { log.Fatalf("Error initializing Swarm: %v", err) } dns := clusterdns.New(opt.domain, rrs, cluster) cancel := make(chan struct{}) defer close(cancel) errCh, okCh := dns.StartRefreshing(opt.refreshInterval, opt.refreshTimeout, cancel) go func() { var lastSuccess time.Time var start = time.Now() var errs = 0 var ok = 0 for { // Exit if records are stale. Here we prefer consistency over // liveliness/availability. if (ok > 0 && time.Since(lastSuccess) > opt.stalenessPeriod) || (ok == 0 && time.Since(start) > opt.stalenessPeriod) { close(cancel) var last string if lastSuccess.IsZero() { last = "never" } else { last = lastSuccess.String() } log.Fatalf("Fatal: exiting rather than serving stale records. Staleness period: %v, last success: %s", opt.stalenessPeriod, last) } select { case err := <-errCh: errs++ log.Printf("Refresh error (#%d): %v ", errs, err) case <-okCh: errs = 0 // reset errs ok++ lastSuccess = time.Now() log.Printf("Successfully refreshed records.") case <-cancel: log.Fatal("Fatal: Refreshing records cancelled.") } } }() srv := server.New(opt.domain, opt.bindAddr, rrs, opt.recurse, opt.nameservers) log.Fatal(srv.ListenAndServe()) }
// testServerExternal gives a test server capable of serving only external // requests. func testServerExternal(t *testing.T) (*DnsServer, <-chan struct{}) { ns := []string{"8.8.8.8:53", "8.8.4.4:53"} srv := New("dontcare", ":8053", rrstore.New(), true, ns) ready := make(chan struct{}, 1) srv.NotifyStartedFunc = func() { close(ready) } go srv.ListenAndServe() return srv, ready }
func TestHandleExternalOff(t *testing.T) { srv, ready := testServer(t, rrstore.New()) <-ready defer srv.Shutdown() if r, err := query(srv.Addr, "example.com", dns.TypeA); err != nil { t.Fatalf("exchange failed: %v", err) } else if r.Rcode != dns.RcodeServerFailure { t.Fatalf("unexpected rcode. expected=%s got=%s", dns.TypeToString[dns.RcodeServerFailure], dns.TypeToString[uint16(r.Rcode)]) } }
func TestHandleDomain(t *testing.T) { rr := rrstore.New() rr.Set(map[uint16]map[string][]string{ dns.TypeA: { "api.domain.": []string{"10.0.0.1", "10.0.0.2"}, "blog.domain.": []string{"10.0.1.1", "10.0.1.2", "10.0.1.3"}, }, dns.TypeSRV: { "_web._tcp.domain.": []string{"10.0.0.1:80"}, "_web._udp.domain.": []string{"10.0.0.1:5001", "10.0.0.2:5002", "10.0.0.3:5003"}, }, }) srv, ready := testServer(t, rr) <-ready defer srv.Shutdown() cases := []struct { fqdn string qType uint16 expectedRCode int expectedAnswers int }{ // List all test cases for all possible DNS questions here within the // domain. {"nonexistent.domain.", dns.TypeA, dns.RcodeNameError, 0}, {"nonexistent.domain.", dns.TypeSRV, dns.RcodeNameError, 0}, {"api.domain", dns.TypeA, dns.RcodeSuccess, 2}, {"_web._tcp.domain", dns.TypeSRV, dns.RcodeSuccess, 1}, {"_WEB._UDP.domain", dns.TypeSRV, dns.RcodeSuccess, 3}, } for _, c := range cases { q := fmt.Sprintf("%s %s", dns.TypeToString[c.qType], c.fqdn) if r, err := query(srv.Addr, c.fqdn, c.qType); err != nil { t.Fatalf("exchange failed (%s): %v", q, err) } else if r.Rcode != c.expectedRCode { t.Fatalf("unexpected rcode (%s). expected=%s got=%s", q, dns.RcodeToString[c.expectedRCode], dns.RcodeToString[r.Rcode]) } else if len(r.Answer) != c.expectedAnswers { t.Fatalf("unexpected answers count. expected=%d got=%d", q, len(r.Answer), c.expectedAnswers) } } }