middleware/etcd: Return json parsing errors (#158)

When coredns unmarshals a json value and it fails it will put the error
in the returned message iff the query was a debug query
(o-o.debug.<REST>).
This commit is contained in:
Miek Gieben 2016-06-07 20:57:45 +01:00
parent d04abdf422
commit 3110306296
4 changed files with 41 additions and 8 deletions

View file

@ -39,7 +39,7 @@ etcd [zones...] {
pointing to external names. If you want CoreDNS to act as a proxy for clients you'll need to add pointing to external names. If you want CoreDNS to act as a proxy for clients you'll need to add
the proxy middleware. the proxy middleware.
* `tls` followed the cert, key and the CA's cert filenames. * `tls` followed the cert, key and the CA's cert filenames.
* `debug` allow debug queries. Prefix the name with `o-o.debug.` to reveive extra information in the * `debug` allow debug queries. Prefix the name with `o-o.debug.` to retrieve extra information in the
additional section of the reply in the form of text records: additional section of the reply in the form of text records:
skydns.test.skydns.dom.a. 300 CH TXT "127.0.0.1:0(10,0,,false)[0,]" skydns.test.skydns.dom.a. 300 CH TXT "127.0.0.1:0(10,0,,false)[0,]"
@ -47,6 +47,13 @@ etcd [zones...] {
This shows the complete key as the owername, the rdata of the TXT record has: This shows the complete key as the owername, the rdata of the TXT record has:
`host:port(priority,weight,txt content,mail)[targetstrip,group]`. `host:port(priority,weight,txt content,mail)[targetstrip,group]`.
Any errors seen doing parsing will show up like this:
. 0 CH TXT "/skydns/local/skydns/r/a: invalid character '.' after object key:value pair"
which shows `a.r.skydns.local.` has a json encoding problem.
## Examples ## Examples
This is the default SkyDNS setup, with everying specified in full: This is the default SkyDNS setup, with everying specified in full:

View file

@ -36,3 +36,21 @@ func servicesToTxt(debug []msg.Service) []dns.RR {
} }
return rr return rr
} }
func errorToTxt(err error) dns.RR {
if err == nil {
return nil
}
msg := err.Error()
if len(msg) > 255 {
msg = msg[:255]
}
t := new(dns.TXT)
t.Hdr.Class = dns.ClassCHAOS
t.Hdr.Ttl = 0
t.Hdr.Rrtype = dns.TypeTXT
t.Hdr.Name = "."
t.Txt = []string{msg}
return t
}

View file

@ -3,6 +3,7 @@ package etcd
import ( import (
"encoding/json" "encoding/json"
"fmt"
"strings" "strings"
"time" "time"
@ -104,7 +105,7 @@ Nodes:
} }
serv := new(msg.Service) serv := new(msg.Service)
if err := json.Unmarshal([]byte(n.Value), serv); err != nil { if err := json.Unmarshal([]byte(n.Value), serv); err != nil {
return nil, err return nil, fmt.Errorf("%s: %s", n.Key, err.Error())
} }
b := msg.Service{Host: serv.Host, Port: serv.Port, Priority: serv.Priority, Weight: serv.Weight, Text: serv.Text, Key: n.Key} b := msg.Service{Host: serv.Host, Port: serv.Port, Priority: serv.Priority, Weight: serv.Weight, Text: serv.Text, Key: n.Key}
if _, ok := bx[b]; ok { if _, ok := bx[b]; ok {

View file

@ -82,14 +82,14 @@ func (e Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i
} }
if isEtcdNameError(err) { if isEtcdNameError(err) {
return e.Err(zone, dns.RcodeNameError, state, debug) return e.Err(zone, dns.RcodeNameError, state, debug, err)
} }
if err != nil { if err != nil {
return dns.RcodeServerFailure, err return e.Err(zone, dns.RcodeServerFailure, state, debug, err)
} }
if len(records) == 0 { if len(records) == 0 {
return e.Err(zone, dns.RcodeSuccess, state, debug) return e.Err(zone, dns.RcodeSuccess, state, debug, err)
} }
m := new(dns.Msg) m := new(dns.Msg)
@ -109,15 +109,22 @@ func (e Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i
} }
// Err write an error response to the client. // Err write an error response to the client.
func (e Etcd) Err(zone string, rcode int, state middleware.State, debug []msg.Service) (int, error) { func (e Etcd) Err(zone string, rcode int, state middleware.State, debug []msg.Service, err error) (int, error) {
m := new(dns.Msg) m := new(dns.Msg)
m.SetRcode(state.Req, rcode) m.SetRcode(state.Req, rcode)
m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true
m.Ns, _, _ = e.SOA(zone, state) m.Ns, _, _ = e.SOA(zone, state)
if e.debug != "" {
m.Extra = servicesToTxt(debug) m.Extra = servicesToTxt(debug)
txt := errorToTxt(err)
if txt != nil {
m.Extra = append(m.Extra, errorToTxt(err))
}
}
state.SizeAndDo(m) state.SizeAndDo(m)
state.W.WriteMsg(m) state.W.WriteMsg(m)
return rcode, nil // Return success as the rcode to signal we have written to the client.
return dns.RcodeSuccess, nil
} }
func dedup(m *dns.Msg) *dns.Msg { func dedup(m *dns.Msg) *dns.Msg {