Add more tests and include the forgotten chaos_test.go as well

This commit is contained in:
Miek Gieben 2016-03-25 10:32:12 +00:00
parent b3a52c6988
commit e408c81a8c
7 changed files with 372 additions and 5 deletions

View file

@ -0,0 +1,83 @@
package chaos
import (
"testing"
"github.com/miekg/coredns/middleware"
"github.com/miekg/dns"
"golang.org/x/net/context"
)
func TestChaos(t *testing.T) {
em := Chaos{
Version: version,
Authors: map[string]bool{"Miek Gieben": true},
}
tests := []struct {
next middleware.Handler
qname string
qtype uint16
expectedCode int
expectedReply string
expectedErr error
}{
{
next: genHandler(dns.RcodeSuccess, nil),
qname: "version.bind",
expectedCode: dns.RcodeSuccess,
expectedReply: version,
expectedErr: nil,
},
{
next: genHandler(dns.RcodeSuccess, nil),
qname: "authors.bind",
expectedCode: dns.RcodeSuccess,
expectedReply: "Miek Gieben",
expectedErr: nil,
},
{
next: genHandler(dns.RcodeSuccess, nil),
qname: "authors.bind",
qtype: dns.TypeSRV,
expectedCode: dns.RcodeSuccess,
expectedErr: nil,
},
}
ctx := context.TODO()
for i, test := range tests {
req := new(dns.Msg)
if test.qtype == 0 {
test.qtype = dns.TypeTXT
}
req.SetQuestion(dns.Fqdn(test.qname), test.qtype)
em.Next = test.next
rec := middleware.NewResponseRecorder(&middleware.TestResponseWriter{})
code, err := em.ServeDNS(ctx, rec, req)
if err != test.expectedErr {
t.Errorf("Test %d: Expected error %v, but got %v", i, test.expectedErr, err)
}
if code != int(test.expectedCode) {
t.Errorf("Test %d: Expected status code %d, but got %d", i, test.expectedCode, code)
}
if test.expectedReply != "" {
answer := rec.Msg().Answer[0].(*dns.TXT).Txt[0]
if answer != test.expectedReply {
t.Errorf("Test %d: Expected answer %s, but got %s", i, test.expectedReply, answer)
}
}
}
}
func genHandler(rcode int, err error) middleware.Handler {
return middleware.HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
return rcode, err
})
}
const version = "CoreDNS-001"

View file

@ -0,0 +1,112 @@
// +build etcd
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 (
"sort"
"testing"
"github.com/miekg/coredns/middleware"
"github.com/miekg/coredns/middleware/etcd/msg"
"github.com/miekg/dns"
)
func TestGroupLookup(t *testing.T) {
for _, serv := range servicesGroup {
set(t, etc, serv.Key, 0, serv)
defer delete(t, etc, serv.Key)
}
for _, tc := range dnsTestCasesGroup {
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.Msg()
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)
}
}
}
// Note the key is encoded as DNS name, while in "reality" it is a etcd path.
var servicesGroup = []*msg.Service{
{Host: "127.0.0.1", Key: "a.dom.skydns.test.", Group: "g1"},
{Host: "127.0.0.2", Key: "b.sub.dom.skydns.test.", Group: "g1"},
{Host: "127.0.0.1", Key: "a.dom2.skydns.test.", Group: "g1"},
{Host: "127.0.0.2", Key: "b.sub.dom2.skydns.test.", Group: ""},
{Host: "127.0.0.1", Key: "a.dom1.skydns.test.", Group: "g1"},
{Host: "127.0.0.2", Key: "b.sub.dom1.skydns.test.", Group: "g2"},
}
var dnsTestCasesGroup = []dnsTestCase{
// Groups
{
// hits the group 'g1' and only includes those records
Qname: "dom.skydns.test.", Qtype: dns.TypeA,
Answer: []dns.RR{
newA("dom.skydns.test. 300 IN A 127.0.0.1"),
newA("dom.skydns.test. 300 IN A 127.0.0.2"),
},
},
{
// One has group, the other has not... Include the non-group always.
Qname: "dom2.skydns.test.", Qtype: dns.TypeA,
Answer: []dns.RR{
newA("dom2.skydns.test. 300 IN A 127.0.0.1"),
newA("dom2.skydns.test. 300 IN A 127.0.0.2"),
},
},
{
// The groups differ.
Qname: "dom1.skydns.test.", Qtype: dns.TypeA,
Answer: []dns.RR{
newA("dom1.skydns.test. 300 IN A 127.0.0.1"),
},
},
}

View file

@ -41,6 +41,7 @@ func (e Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i
// rwrite and return // rwrite and return
// Nodata response // Nodata response
// also catch other types, so that they return NODATA // also catch other types, so that they return NODATA
// TODO(miek) nodata function see below
return 0, nil return 0, nil
} }
if isEtcdNameError(err) { if isEtcdNameError(err) {
@ -55,11 +56,12 @@ func (e Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i
} }
if len(records) == 0 { if len(records) == 0 {
// NODATE function, see below
m := new(dns.Msg) m := new(dns.Msg)
m.SetReply(state.Req) m.SetReply(state.Req)
m.Ns = []dns.RR{e.SOA(zone, state)} m.Ns = []dns.RR{e.SOA(zone, state)}
state.W.WriteMsg(m) state.W.WriteMsg(m)
return dns.RcodeNameError, nil return dns.RcodeSuccess, nil
} }
if len(records) > 0 { if len(records) > 0 {
@ -68,6 +70,9 @@ func (e Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i
if len(extra) > 0 { if len(extra) > 0 {
m.Extra = append(m.Extra, extra...) m.Extra = append(m.Extra, extra...)
} }
m = dedup(m)
state.W.WriteMsg(m) state.W.WriteMsg(m)
return 0, nil return 0, nil
} }
@ -76,3 +81,11 @@ func (e Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i
func (e Etcd) NoData(zone string, state middleware.State) { func (e Etcd) NoData(zone string, state middleware.State) {
// TODO(miek): write it // TODO(miek): write it
} }
func dedup(m *dns.Msg) *dns.Msg {
ma := make(map[string]dns.RR)
m.Answer = dns.Dedup(m.Answer, ma)
m.Ns = dns.Dedup(m.Ns, ma)
m.Extra = dns.Dedup(m.Extra, ma)
return m
}

View file

@ -36,6 +36,11 @@ var dnsTestCases = []dnsTestCase{
Qname: "a.server1.dev.region1.skydns.test.", Qtype: dns.TypeSRV, Qname: "a.server1.dev.region1.skydns.test.", Qtype: dns.TypeSRV,
Answer: []dns.RR{newSRV("a.server1.dev.region1.skydns.test. 300 SRV 10 100 8080 dev.server1.")}, Answer: []dns.RR{newSRV("a.server1.dev.region1.skydns.test. 300 SRV 10 100 8080 dev.server1.")},
}, },
// SRV Test (case test)
{
Qname: "a.SERVer1.dEv.region1.skydns.tEst.", Qtype: dns.TypeSRV,
Answer: []dns.RR{newSRV("a.SERVer1.dEv.region1.skydns.tEst. 300 SRV 10 100 8080 dev.server1.")},
},
// NXDOMAIN Test // NXDOMAIN Test
{ {
Qname: "doesnotexist.skydns.test.", Qtype: dns.TypeA, Qname: "doesnotexist.skydns.test.", Qtype: dns.TypeA,

View file

@ -0,0 +1,154 @@
// +build etcd
// tests mx and txt records
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 (
"sort"
"testing"
"github.com/miekg/coredns/middleware"
"github.com/miekg/coredns/middleware/etcd/msg"
"github.com/miekg/dns"
)
func TestOtherLookup(t *testing.T) {
for _, serv := range servicesOther {
set(t, etc, serv.Key, 0, serv)
defer delete(t, etc, serv.Key)
}
for _, tc := range dnsTestCasesOther {
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.Msg()
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)
}
}
}
// Note the key is encoded as DNS name, while in "reality" it is a etcd path.
var servicesOther = []*msg.Service{
{Host: "dev.server1", Port: 8080, Key: "a.server1.dev.region1.skydns.test."},
// mx
{Host: "mx.skydns.test", Priority: 50, Mail: true, Key: "a.mail.skydns.test."},
{Host: "mx.miek.nl", Priority: 50, Mail: true, Key: "b.mail.skydns.test."},
{Host: "a.ipaddr.skydns.test", Priority: 30, Mail: true, Key: "a.mx.skydns.test."},
{Host: "a.ipaddr.skydns.test", Mail: true, Key: "a.mx2.skydns.test."},
{Host: "b.ipaddr.skydns.test", Mail: true, Key: "b.mx2.skydns.test."},
{Host: "172.16.1.1", Key: "a.ipaddr.skydns.test."},
{Host: "172.16.1.2", Key: "b.ipaddr.skydns.test."},
// txt
{Text: "abc", Key: "a1.txt.skydns.test."},
{Text: "abc abc", Key: "a2.txt.skydns.test."},
// duplicate ip address
{Host: "10.11.11.10", Key: "http.multiport.http.skydns.test.", Port: 80},
{Host: "10.11.11.10", Key: "https.multiport.http.skydns.test.", Port: 443},
}
var dnsTestCasesOther = []dnsTestCase{
// MX Tests
{
// NODATA as this is not an Mail: true record.
Qname: "a.server1.dev.region1.skydns.test.", Qtype: dns.TypeMX,
Ns: []dns.RR{
newSOA("skydns.test. 300 SOA ns.dns.skydns.test. hostmaster.skydns.test. 0 0 0 0 0"),
},
},
{
Qname: "a.mail.skydns.test.", Qtype: dns.TypeMX,
Answer: []dns.RR{newMX("a.mail.skydns.test. 300 IN MX 50 mx.skydns.test.")},
Extra: []dns.RR{
newA("a.ipaddr.skydns.test. 300 IN A 172.16.1.1"),
newCNAME("mx.skydns.test. 300 IN CNAME a.ipaddr.skydns.test."),
},
},
{
Qname: "mx2.skydns.test.", Qtype: dns.TypeMX,
Answer: []dns.RR{
newMX("mx2.skydns.test. 300 IN MX 10 a.ipaddr.skydns.test."),
newMX("mx2.skydns.test. 300 IN MX 10 b.ipaddr.skydns.test."),
},
Extra: []dns.RR{
newA("a.ipaddr.skydns.test. 300 A 172.16.1.1"),
newA("b.ipaddr.skydns.test. 300 A 172.16.1.2"),
},
},
// Txt
{
Qname: "a1.txt.skydns.test.", Qtype: dns.TypeTXT,
Answer: []dns.RR{
newTXT("a1.txt.skydns.test. 300 IN TXT \"abc\""),
},
},
{
Qname: "a2.txt.skydns.test.", Qtype: dns.TypeTXT,
Answer: []dns.RR{
newTXT("a2.txt.skydns.test. 300 IN TXT \"abc abc\""),
},
},
{
Qname: "txt.skydns.test.", Qtype: dns.TypeTXT,
Answer: []dns.RR{
newTXT("txt.skydns.test. 300 IN TXT \"abc abc\""),
newTXT("txt.skydns.test. 300 IN TXT \"abc\""),
},
},
// Duplicate IP address test
{
Qname: "multiport.http.skydns.test.", Qtype: dns.TypeA,
Answer: []dns.RR{newA("multiport.http.skydns.test. 300 IN A 10.11.11.10")},
},
}

View file

@ -37,7 +37,7 @@ const (
) )
func init() { func init() {
ctx, _ = context.WithTimeout(ctx.Background(), etcdTimeout) ctx, _ = context.WithTimeout(context.Background(), etcdTimeout)
etcdCfg := etcdc.Config{ etcdCfg := etcdc.Config{
Endpoints: []string{"http://localhost:2379"}, Endpoints: []string{"http://localhost:2379"},
@ -88,7 +88,7 @@ func TestLookup(t *testing.T) {
t.Errorf("expected no error, got %v\n", err) t.Errorf("expected no error, got %v\n", err)
return return
} }
resp := rec.Reply() resp := rec.Msg()
sort.Sort(rrSet(resp.Answer)) sort.Sort(rrSet(resp.Answer))
sort.Sort(rrSet(resp.Ns)) sort.Sort(rrSet(resp.Ns))

View file

@ -37,8 +37,8 @@ func addStubEdns0(m *dns.Msg) *dns.Msg {
return m return m
} }
// Look in .../dns/stub/<domain>/xx for msg.Services. Loop through them // Look in .../dns/stub/<zone>/xx for msg.Services. Loop through them
// extract <domain> and add them as forwarders (ip:port-combos) for // extract <zone> and add them as forwarders (ip:port-combos) for
// the stub zones. Only numeric (i.e. IP address) hosts are used. // the stub zones. Only numeric (i.e. IP address) hosts are used.
// TODO(miek): makes this Startup Function. // TODO(miek): makes this Startup Function.
func (e Etcd) UpdateStubZones(zone string) error { func (e Etcd) UpdateStubZones(zone string) error {