Add Stub resolving
SkyDNS can forward requests from one instance to another. Add this base infrastructure for this feature to CoreDNS. Add more tests as well.
This commit is contained in:
parent
163e5d8e71
commit
78d2e31ec1
6 changed files with 380 additions and 241 deletions
247
middleware/etcd/setup_test.go
Normal file
247
middleware/etcd/setup_test.go
Normal file
|
@ -0,0 +1,247 @@
|
|||
package etcd
|
||||
|
||||
// etcd needs to be running on http://127.0.0.1:2379
|
||||
// *and* needs connectivity to the internet for remotely resolving
|
||||
// names.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/coredns/middleware"
|
||||
"github.com/miekg/coredns/middleware/etcd/msg"
|
||||
"github.com/miekg/coredns/middleware/etcd/singleflight"
|
||||
"github.com/miekg/coredns/middleware/proxy"
|
||||
"github.com/miekg/dns"
|
||||
|
||||
etcdc "github.com/coreos/etcd/client"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var (
|
||||
etc Etcd
|
||||
client etcdc.KeysAPI
|
||||
ctx context.Context
|
||||
)
|
||||
|
||||
type Section int
|
||||
|
||||
const (
|
||||
Answer Section = iota
|
||||
Ns
|
||||
Extra
|
||||
)
|
||||
|
||||
func init() {
|
||||
ctx = context.TODO()
|
||||
|
||||
etcdCfg := etcdc.Config{
|
||||
Endpoints: []string{"http://localhost:2379"},
|
||||
}
|
||||
cli, _ := etcdc.New(etcdCfg)
|
||||
etc = Etcd{
|
||||
Proxy: proxy.New([]string{"8.8.8.8:53"}),
|
||||
PathPrefix: "skydns",
|
||||
Ctx: context.Background(),
|
||||
Inflight: &singleflight.Group{},
|
||||
Zones: []string{"skydns.test."},
|
||||
Client: etcdc.NewKeysAPI(cli),
|
||||
}
|
||||
}
|
||||
|
||||
func set(t *testing.T, e Etcd, k string, ttl time.Duration, m *msg.Service) {
|
||||
b, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
path, _ := e.PathWithWildcard(k)
|
||||
e.Client.Set(ctx, path, string(b), &etcdc.SetOptions{TTL: ttl})
|
||||
}
|
||||
|
||||
func delete(t *testing.T, e Etcd, k string) {
|
||||
path, _ := e.PathWithWildcard(k)
|
||||
e.Client.Delete(ctx, path, &etcdc.DeleteOptions{Recursive: false})
|
||||
}
|
||||
|
||||
type rrSet []dns.RR
|
||||
|
||||
func (p rrSet) Len() int { return len(p) }
|
||||
func (p rrSet) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
func (p rrSet) Less(i, j int) bool { return p[i].String() < p[j].String() }
|
||||
|
||||
func TestLookup(t *testing.T) {
|
||||
for _, serv := range services {
|
||||
set(t, etc, serv.Key, 0, serv)
|
||||
defer delete(t, etc, serv.Key)
|
||||
}
|
||||
for _, tc := range dnsTestCases {
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion(dns.Fqdn(tc.Qname), tc.Qtype)
|
||||
|
||||
rec := middleware.NewResponseRecorder(&middleware.TestResponseWriter{})
|
||||
_, err := etc.ServeDNS(ctx, rec, m)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v\n", err)
|
||||
return
|
||||
}
|
||||
resp := rec.Reply()
|
||||
|
||||
sort.Sort(rrSet(resp.Answer))
|
||||
sort.Sort(rrSet(resp.Ns))
|
||||
sort.Sort(rrSet(resp.Extra))
|
||||
|
||||
if resp.Rcode != tc.Rcode {
|
||||
t.Errorf("rcode is %q, expected %q", dns.RcodeToString[resp.Rcode], dns.RcodeToString[tc.Rcode])
|
||||
t.Logf("%v\n", resp)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(resp.Answer) != len(tc.Answer) {
|
||||
t.Errorf("answer for %q contained %d results, %d expected", tc.Qname, len(resp.Answer), len(tc.Answer))
|
||||
t.Logf("%v\n", resp)
|
||||
continue
|
||||
}
|
||||
if len(resp.Ns) != len(tc.Ns) {
|
||||
t.Errorf("authority for %q contained %d results, %d expected", tc.Qname, len(resp.Ns), len(tc.Ns))
|
||||
t.Logf("%v\n", resp)
|
||||
continue
|
||||
}
|
||||
if len(resp.Extra) != len(tc.Extra) {
|
||||
t.Errorf("additional for %q contained %d results, %d expected", tc.Qname, len(resp.Extra), len(tc.Extra))
|
||||
t.Logf("%v\n", resp)
|
||||
continue
|
||||
}
|
||||
|
||||
if !checkSection(t, tc, Answer, resp.Answer) {
|
||||
t.Logf("%v\n", resp)
|
||||
}
|
||||
if !checkSection(t, tc, Ns, resp.Ns) {
|
||||
t.Logf("%v\n", resp)
|
||||
|
||||
}
|
||||
if !checkSection(t, tc, Extra, resp.Extra) {
|
||||
t.Logf("%v\n", resp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type dnsTestCase struct {
|
||||
Qname string
|
||||
Qtype uint16
|
||||
Rcode int
|
||||
Answer []dns.RR
|
||||
Ns []dns.RR
|
||||
Extra []dns.RR
|
||||
}
|
||||
|
||||
func newA(rr string) *dns.A { r, _ := dns.NewRR(rr); return r.(*dns.A) }
|
||||
func newAAAA(rr string) *dns.AAAA { r, _ := dns.NewRR(rr); return r.(*dns.AAAA) }
|
||||
func newCNAME(rr string) *dns.CNAME { r, _ := dns.NewRR(rr); return r.(*dns.CNAME) }
|
||||
func newSRV(rr string) *dns.SRV { r, _ := dns.NewRR(rr); return r.(*dns.SRV) }
|
||||
func newSOA(rr string) *dns.SOA { r, _ := dns.NewRR(rr); return r.(*dns.SOA) }
|
||||
func newNS(rr string) *dns.NS { r, _ := dns.NewRR(rr); return r.(*dns.NS) }
|
||||
func newPTR(rr string) *dns.PTR { r, _ := dns.NewRR(rr); return r.(*dns.PTR) }
|
||||
func newTXT(rr string) *dns.TXT { r, _ := dns.NewRR(rr); return r.(*dns.TXT) }
|
||||
func newMX(rr string) *dns.MX { r, _ := dns.NewRR(rr); return r.(*dns.MX) }
|
||||
|
||||
func checkSection(t *testing.T, tc dnsTestCase, sect Section, rr []dns.RR) bool {
|
||||
section := []dns.RR{}
|
||||
switch sect {
|
||||
case 0:
|
||||
section = tc.Answer
|
||||
case 1:
|
||||
section = tc.Ns
|
||||
case 2:
|
||||
section = tc.Extra
|
||||
}
|
||||
|
||||
for i, a := range rr {
|
||||
if a.Header().Name != section[i].Header().Name {
|
||||
t.Errorf("answer %d should have a Header Name of %q, but has %q", i, section[i].Header().Name, a.Header().Name)
|
||||
return false
|
||||
}
|
||||
// 303 signals: don't care what the ttl is.
|
||||
if section[i].Header().Ttl != 303 && a.Header().Ttl != section[i].Header().Ttl {
|
||||
t.Errorf("Answer %d should have a Header TTL of %d, but has %d", i, section[i].Header().Ttl, a.Header().Ttl)
|
||||
return false
|
||||
}
|
||||
if a.Header().Rrtype != section[i].Header().Rrtype {
|
||||
t.Errorf("answer %d should have a header rr type of %d, but has %d", i, section[i].Header().Rrtype, a.Header().Rrtype)
|
||||
return false
|
||||
}
|
||||
|
||||
switch x := a.(type) {
|
||||
case *dns.SRV:
|
||||
if x.Priority != section[i].(*dns.SRV).Priority {
|
||||
t.Errorf("answer %d should have a Priority of %d, but has %d", i, section[i].(*dns.SRV).Priority, x.Priority)
|
||||
return false
|
||||
}
|
||||
if x.Weight != section[i].(*dns.SRV).Weight {
|
||||
t.Errorf("answer %d should have a Weight of %d, but has %d", i, section[i].(*dns.SRV).Weight, x.Weight)
|
||||
return false
|
||||
}
|
||||
if x.Port != section[i].(*dns.SRV).Port {
|
||||
t.Errorf("answer %d should have a Port of %d, but has %d", i, section[i].(*dns.SRV).Port, x.Port)
|
||||
return false
|
||||
}
|
||||
if x.Target != section[i].(*dns.SRV).Target {
|
||||
t.Errorf("answer %d should have a Target of %q, but has %q", i, section[i].(*dns.SRV).Target, x.Target)
|
||||
return false
|
||||
}
|
||||
case *dns.A:
|
||||
if x.A.String() != section[i].(*dns.A).A.String() {
|
||||
t.Errorf("answer %d should have a Address of %q, but has %q", i, section[i].(*dns.A).A.String(), x.A.String())
|
||||
return false
|
||||
}
|
||||
case *dns.AAAA:
|
||||
if x.AAAA.String() != section[i].(*dns.AAAA).AAAA.String() {
|
||||
t.Errorf("answer %d should have a Address of %q, but has %q", i, section[i].(*dns.AAAA).AAAA.String(), x.AAAA.String())
|
||||
return false
|
||||
}
|
||||
case *dns.TXT:
|
||||
for j, txt := range x.Txt {
|
||||
if txt != section[i].(*dns.TXT).Txt[j] {
|
||||
t.Errorf("answer %d should have a Txt of %q, but has %q", i, section[i].(*dns.TXT).Txt[j], txt)
|
||||
return false
|
||||
}
|
||||
}
|
||||
case *dns.SOA:
|
||||
tt := section[i].(*dns.SOA)
|
||||
if x.Ns != tt.Ns {
|
||||
t.Errorf("SOA nameserver should be %q, but is %q", x.Ns, tt.Ns)
|
||||
return false
|
||||
}
|
||||
case *dns.PTR:
|
||||
tt := section[i].(*dns.PTR)
|
||||
if x.Ptr != tt.Ptr {
|
||||
t.Errorf("PTR ptr should be %q, but is %q", x.Ptr, tt.Ptr)
|
||||
return false
|
||||
}
|
||||
case *dns.CNAME:
|
||||
tt := section[i].(*dns.CNAME)
|
||||
if x.Target != tt.Target {
|
||||
t.Errorf("CNAME target should be %q, but is %q", x.Target, tt.Target)
|
||||
return false
|
||||
}
|
||||
case *dns.MX:
|
||||
tt := section[i].(*dns.MX)
|
||||
if x.Mx != tt.Mx {
|
||||
t.Errorf("MX Mx should be %q, but is %q", x.Mx, tt.Mx)
|
||||
return false
|
||||
}
|
||||
if x.Preference != tt.Preference {
|
||||
t.Errorf("MX Preference should be %q, but is %q", x.Preference, tt.Preference)
|
||||
return false
|
||||
}
|
||||
case *dns.NS:
|
||||
tt := section[i].(*dns.NS)
|
||||
if x.Ns != tt.Ns {
|
||||
t.Errorf("NS nameserver should be %q, but is %q", x.Ns, tt.Ns)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue