dns: replace nameserver lookup with consistent rpc call

This patch replaces the code which determines the list of servers in the
current cluster with an RPC call to get the list of active consul
service instances which only run on servers.

This replaces the previous implementation which was more complex and
relied on serf messages which can provide a different view than the
consistent response from the raft log.

As a side effect it makes the implementation independent of the server
and the agent which means it works consistently across both. Different
behavior for server and agent was the root cause for the bug in
http://github.com/hashicorp/consul/issue/3047.

Fixes #3407
This commit is contained in:
Frank Schroeder 2017-08-21 14:16:41 +02:00 committed by Frank Schröder
parent 8e1f9b9b68
commit 4bfcf7b613
1 changed files with 22 additions and 32 deletions

View File

@ -272,41 +272,31 @@ func (d *DNSServer) addSOA(msg *dns.Msg) {
// nameservers returns the names and ip addresses of up to three random servers
// in the current cluster which serve as authoritative name servers for zone.
func (d *DNSServer) nameservers(edns bool) (ns []dns.RR, extra []dns.RR) {
// get server names and store them in a map to randomize the output
servers := map[string]net.IP{}
for name, addr := range d.agent.delegate.ServerAddrs() {
host, _, err := net.SplitHostPort(addr)
if err != nil {
d.logger.Printf("[WARN] Unable to parse address %v, got error: %v", addr, err)
continue
}
ip := net.ParseIP(host)
if ip == nil {
continue
}
// Use "NODENAME.node.DC.DOMAIN" as a unique name for the server
// since we use that name in other places as well.
// 'name' is "NODENAME.DC" so we need to split it
// to construct the server name.
lastdot := strings.LastIndexByte(name, '.')
nodeName, dc := name[:lastdot], name[lastdot:]
if InvalidDnsRe.MatchString(nodeName) {
d.logger.Printf("[WARN] dns: Node name %q is not a valid dns host name, will not be added to NS record", nodeName)
continue
}
fqdn := nodeName + ".node" + dc + "." + d.domain
fqdn = dns.Fqdn(strings.ToLower(fqdn))
servers[fqdn] = ip
out, err := d.lookupServiceNodes(d.agent.config.Datacenter, structs.ConsulServiceName, "")
if err != nil {
d.logger.Printf("[WARN] dns: Unable to get list of servers: %s", err)
return nil, nil
}
if len(servers) == 0 {
if len(out.Nodes) == 0 {
d.logger.Printf("[WARN] dns: no servers found")
return
}
for name, ip := range servers {
// shuffle the nodes to randomize the output
out.Nodes.Shuffle()
for _, o := range out.Nodes {
name, addr, dc := o.Node.Node, o.Node.Address, o.Node.Datacenter
if InvalidDnsRe.MatchString(name) {
d.logger.Printf("[WARN] dns: Skipping invalid node %q for NS records", name)
continue
}
fqdn := name + ".node." + dc + "." + d.domain
fqdn = dns.Fqdn(strings.ToLower(fqdn))
// NS record
nsrr := &dns.NS{
Hdr: dns.RR_Header{
@ -315,12 +305,12 @@ func (d *DNSServer) nameservers(edns bool) (ns []dns.RR, extra []dns.RR) {
Class: dns.ClassINET,
Ttl: uint32(d.config.NodeTTL / time.Second),
},
Ns: name,
Ns: fqdn,
}
ns = append(ns, nsrr)
// A or AAAA glue record
glue := d.formatNodeRecord(ip.String(), name, dns.TypeANY, d.config.NodeTTL, edns)
glue := d.formatNodeRecord(addr, fqdn, dns.TypeANY, d.config.NodeTTL, edns)
extra = append(extra, glue...)
// don't provide more than 3 servers