Add NS records and A records for each server. Constructs ns host names using the advertise address of the server.

This commit is contained in:
Preetha Appan 2017-08-02 16:44:40 -05:00 committed by Frank Schroeder
parent 710c4b0d41
commit c38906daad
No known key found for this signature in database
GPG Key ID: 4D65C6EAEC87DECD
7 changed files with 194 additions and 16 deletions

View File

@ -65,6 +65,7 @@ type delegate interface {
JoinLAN(addrs []string) (n int, err error) JoinLAN(addrs []string) (n int, err error)
RemoveFailedNode(node string) error RemoveFailedNode(node string) error
RPC(method string, args interface{}, reply interface{}) error RPC(method string, args interface{}, reply interface{}) error
ServerAddrs() []string
SnapshotRPC(args *structs.SnapshotRequest, in io.Reader, out io.Writer, replyFn structs.SnapshotReplyFn) error SnapshotRPC(args *structs.SnapshotRequest, in io.Reader, out io.Writer, replyFn structs.SnapshotReplyFn) error
Shutdown() error Shutdown() error
Stats() map[string]map[string]string Stats() map[string]map[string]string

View File

@ -411,6 +411,10 @@ func (c *Client) Stats() map[string]map[string]string {
return stats return stats
} }
func (c *Client) ServerAddrs() []string {
return c.servers.GetServerAddrs()
}
// GetLANCoordinate returns the network coordinate of the current node, as // GetLANCoordinate returns the network coordinate of the current node, as
// maintained by Serf. // maintained by Serf.
func (c *Client) GetLANCoordinate() (*coordinate.Coordinate, error) { func (c *Client) GetLANCoordinate() (*coordinate.Coordinate, error) {

View File

@ -1047,6 +1047,15 @@ func (s *Server) GetWANCoordinate() (*coordinate.Coordinate, error) {
return s.serfWAN.GetCoordinate() return s.serfWAN.GetCoordinate()
} }
func (s *Server) ServerAddrs() []string {
ret, err := s.router.FindServerAddrs(s.config.Datacenter)
if err != nil {
s.logger.Printf("[WARN] Unexpected state, no server addresses for datacenter %v, got error: %v", s.config.Datacenter, err)
return nil
}
return ret
}
// Atomically sets a readiness state flag when leadership is obtained, to indicate that server is past its barrier write // Atomically sets a readiness state flag when leadership is obtained, to indicate that server is past its barrier write
func (s *Server) setConsistentReadReady() { func (s *Server) setConsistentReadReady() {
atomic.StoreInt32(&s.readyForConsistentReads, 1) atomic.StoreInt32(&s.readyForConsistentReads, 1)

View File

@ -223,6 +223,15 @@ func (m *Manager) getServerList() serverList {
return m.listValue.Load().(serverList) return m.listValue.Load().(serverList)
} }
// GetServerAddrs returns a slice with all server addresses
func (m *Manager) GetServerAddrs() []string {
var ret []string
for _, server := range m.getServerList().servers {
ret = append(ret, server.Addr.String())
}
return ret
}
// saveServerList is a convenience method which hides the locking semantics // saveServerList is a convenience method which hides the locking semantics
// of atomic.Value from the caller. // of atomic.Value from the caller.
func (m *Manager) saveServerList(l serverList) { func (m *Manager) saveServerList(l serverList) {

View File

@ -489,3 +489,25 @@ func (r *Router) GetDatacenterMaps() ([]structs.DatacenterMap, error) {
} }
return maps, nil return maps, nil
} }
func (r *Router) FindServerAddrs(datacenter string) ([]string, error) {
r.RLock()
defer r.RUnlock()
// Get the list of managers for this datacenter. This will usually just
// have one entry, but it's possible to have a user-defined area + WAN.
managers, ok := r.managers[datacenter]
if !ok {
return nil, fmt.Errorf("datacenter %v not found", datacenter)
}
var ret []string
// Try each manager until we get a server.
for _, manager := range managers {
if manager.IsOffline() {
continue
}
ret = append(ret, manager.GetServerAddrs()...)
}
return ret, nil
}

View File

@ -372,6 +372,7 @@ PARSE:
INVALID: INVALID:
d.logger.Printf("[WARN] dns: QName invalid: %s", qName) d.logger.Printf("[WARN] dns: QName invalid: %s", qName)
d.addSOA(d.domain, resp) d.addSOA(d.domain, resp)
d.addNSAndARecordsForDomain(resp)
resp.SetRcode(req, dns.RcodeNameError) resp.SetRcode(req, dns.RcodeNameError)
} }
@ -414,6 +415,7 @@ RPC:
// If we have no address, return not found! // If we have no address, return not found!
if out.NodeServices == nil { if out.NodeServices == nil {
d.addSOA(d.domain, resp) d.addSOA(d.domain, resp)
d.addNSAndARecordsForDomain(resp)
resp.SetRcode(req, dns.RcodeNameError) resp.SetRcode(req, dns.RcodeNameError)
return return
} }
@ -427,6 +429,9 @@ RPC:
if records != nil { if records != nil {
resp.Answer = append(resp.Answer, records...) resp.Answer = append(resp.Answer, records...)
} }
// Add NS record and A record
d.addNSAndARecordsForDomain(resp)
} }
// formatNodeRecord takes a Node and returns an A, AAAA, or CNAME record // formatNodeRecord takes a Node and returns an A, AAAA, or CNAME record
@ -641,6 +646,7 @@ RPC:
// If we have no nodes, return not found! // If we have no nodes, return not found!
if len(out.Nodes) == 0 { if len(out.Nodes) == 0 {
d.addSOA(d.domain, resp) d.addSOA(d.domain, resp)
d.addNSAndARecordsForDomain(resp)
resp.SetRcode(req, dns.RcodeNameError) resp.SetRcode(req, dns.RcodeNameError)
return return
} }
@ -656,6 +662,9 @@ RPC:
d.serviceNodeRecords(datacenter, out.Nodes, req, resp, ttl) d.serviceNodeRecords(datacenter, out.Nodes, req, resp, ttl)
} }
// Add NS and A records
d.addNSAndARecordsForDomain(resp)
// If the network is not TCP, restrict the number of responses // If the network is not TCP, restrict the number of responses
if network != "tcp" { if network != "tcp" {
wasTrimmed := trimUDPResponse(d.config, req, resp) wasTrimmed := trimUDPResponse(d.config, req, resp)
@ -673,6 +682,40 @@ RPC:
} }
} }
// addNSAndARecordsForDomain uses the agent's advertise address to
func (d *DNSServer) addNSAndARecordsForDomain(msg *dns.Msg) {
serverAddrs := d.agent.delegate.ServerAddrs()
for _, addr := range serverAddrs {
ipAddrStr := strings.Split(addr, ":")[0]
nsName := "ns." + ipAddrStr + "." + d.domain
ip := net.ParseIP(ipAddrStr)
if ip != nil {
ns := &dns.NS{
Hdr: dns.RR_Header{
Name: d.domain,
Rrtype: dns.TypeNS,
Class: dns.ClassINET,
Ttl: 0,
},
Ns: nsName,
}
msg.Ns = append(msg.Ns, ns)
//add an A record for the NS record
a := &dns.A{
Hdr: dns.RR_Header{
Name: nsName,
Rrtype: dns.TypeA,
Class: dns.ClassINET,
Ttl: uint32(d.config.NodeTTL / time.Second),
},
A: ip,
}
msg.Extra = append(msg.Extra, a)
}
}
}
// preparedQueryLookup is used to handle a prepared query. // preparedQueryLookup is used to handle a prepared query.
func (d *DNSServer) preparedQueryLookup(network, datacenter, query string, req, resp *dns.Msg) { func (d *DNSServer) preparedQueryLookup(network, datacenter, query string, req, resp *dns.Msg) {
// Execute the prepared query. // Execute the prepared query.
@ -710,6 +753,7 @@ RPC:
// here since the RPC layer loses the type information. // here since the RPC layer loses the type information.
if err.Error() == consul.ErrQueryNotFound.Error() { if err.Error() == consul.ErrQueryNotFound.Error() {
d.addSOA(d.domain, resp) d.addSOA(d.domain, resp)
d.addNSAndARecordsForDomain(resp)
resp.SetRcode(req, dns.RcodeNameError) resp.SetRcode(req, dns.RcodeNameError)
return return
} }
@ -752,6 +796,7 @@ RPC:
// If we have no nodes, return not found! // If we have no nodes, return not found!
if len(out.Nodes) == 0 { if len(out.Nodes) == 0 {
d.addSOA(d.domain, resp) d.addSOA(d.domain, resp)
d.addNSAndARecordsForDomain(resp)
resp.SetRcode(req, dns.RcodeNameError) resp.SetRcode(req, dns.RcodeNameError)
return return
} }
@ -776,6 +821,7 @@ RPC:
// If the answer is empty and the response isn't truncated, return not found // If the answer is empty and the response isn't truncated, return not found
if len(resp.Answer) == 0 && !resp.Truncated { if len(resp.Answer) == 0 && !resp.Truncated {
d.addNSAndARecordsForDomain(resp)
d.addSOA(d.domain, resp) d.addSOA(d.domain, resp)
return return
} }

View File

@ -168,7 +168,7 @@ func TestDNS_NodeLookup(t *testing.T) {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
if len(in.Ns) != 1 { if len(in.Ns) != 2 {
t.Fatalf("Bad: %#v %#v", in, len(in.Answer)) t.Fatalf("Bad: %#v %#v", in, len(in.Answer))
} }
@ -179,6 +179,14 @@ func TestDNS_NodeLookup(t *testing.T) {
if soaRec.Hdr.Ttl != 0 { if soaRec.Hdr.Ttl != 0 {
t.Fatalf("Bad: %#v", in.Ns[0]) t.Fatalf("Bad: %#v", in.Ns[0])
} }
nsRec, ok := in.Ns[1].(*dns.NS)
if !ok {
t.Fatalf("Bad: %#v", in.Ns[1])
}
if nsRec.Hdr.Ttl != 0 {
t.Fatalf("Bad: %#v", in.Ns[1])
}
} }
func TestDNS_CaseInsensitiveNodeLookup(t *testing.T) { func TestDNS_CaseInsensitiveNodeLookup(t *testing.T) {
@ -619,7 +627,7 @@ func TestDNS_ServiceLookup(t *testing.T) {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
if len(in.Ns) != 1 { if len(in.Ns) != 2 {
t.Fatalf("Bad: %#v", in) t.Fatalf("Bad: %#v", in)
} }
@ -630,6 +638,14 @@ func TestDNS_ServiceLookup(t *testing.T) {
if soaRec.Hdr.Ttl != 0 { if soaRec.Hdr.Ttl != 0 {
t.Fatalf("Bad: %#v", in.Ns[0]) t.Fatalf("Bad: %#v", in.Ns[0])
} }
nsRec, ok := in.Ns[1].(*dns.NS)
if !ok {
t.Fatalf("Bad: %#v", in.Ns[1])
}
if nsRec.Hdr.Ttl != 0 {
t.Fatalf("Bad: %#v", in.Ns[1])
}
} }
} }
@ -679,7 +695,6 @@ func TestDNS_ServiceLookupWithInternalServiceAddress(t *testing.T) {
}, },
} }
verify.Values(t, "answer", in.Answer, wantAnswer) verify.Values(t, "answer", in.Answer, wantAnswer)
wantExtra := []dns.RR{ wantExtra := []dns.RR{
&dns.CNAME{ &dns.CNAME{
Hdr: dns.RR_Header{Name: "foo.node.dc1.consul.", Rrtype: 0x5, Class: 0x1, Rdlength: 0x2}, Hdr: dns.RR_Header{Name: "foo.node.dc1.consul.", Rrtype: 0x5, Class: 0x1, Rdlength: 0x2},
@ -689,6 +704,10 @@ func TestDNS_ServiceLookupWithInternalServiceAddress(t *testing.T) {
Hdr: dns.RR_Header{Name: "db.service.consul.", Rrtype: 0x1, Class: 0x1, Rdlength: 0x4}, Hdr: dns.RR_Header{Name: "db.service.consul.", Rrtype: 0x1, Class: 0x1, Rdlength: 0x4},
A: []byte{0x7f, 0x0, 0x0, 0x1}, // 127.0.0.1 A: []byte{0x7f, 0x0, 0x0, 0x1}, // 127.0.0.1
}, },
&dns.A{
Hdr: dns.RR_Header{Name: "ns.127.0.0.1.consul.", Rrtype: 0x1, Class: 0x1, Rdlength: 0x4},
A: []byte{0x7f, 0x0, 0x0, 0x1}, // 127.0.0.1
},
} }
verify.Values(t, "extra", in.Extra, wantExtra) verify.Values(t, "extra", in.Extra, wantExtra)
} }
@ -842,7 +861,7 @@ func TestDNS_ExternalServiceToConsulCNAMELookup(t *testing.T) {
t.Fatalf("Bad: %#v", in.Answer[0]) t.Fatalf("Bad: %#v", in.Answer[0])
} }
if len(in.Extra) != 2 { if len(in.Extra) != 3 {
t.Fatalf("Bad: %#v", in) t.Fatalf("Bad: %#v", in)
} }
@ -873,6 +892,20 @@ func TestDNS_ExternalServiceToConsulCNAMELookup(t *testing.T) {
if aRec.Hdr.Ttl != 0 { if aRec.Hdr.Ttl != 0 {
t.Fatalf("Bad: %#v", in.Extra[1]) t.Fatalf("Bad: %#v", in.Extra[1])
} }
aRec2, ok := in.Extra[2].(*dns.A)
if !ok {
t.Fatalf("Bad: %#v", in.Extra[2])
}
if aRec2.Hdr.Name != "ns.127.0.0.1.consul." {
t.Fatalf("Bad: %#v", in.Extra[2])
}
if aRec2.A.String() != "127.0.0.1" {
t.Fatalf("Bad: %#v", in.Extra[2])
}
if aRec2.Hdr.Ttl != 0 {
t.Fatalf("Bad: %#v", in.Extra[2])
}
} }
} }
@ -968,7 +1001,7 @@ func TestDNS_ExternalServiceToConsulCNAMENestedLookup(t *testing.T) {
t.Fatalf("Bad: %#v", in.Answer[0]) t.Fatalf("Bad: %#v", in.Answer[0])
} }
if len(in.Extra) != 3 { if len(in.Extra) != 4 {
t.Fatalf("Bad: %#v", in) t.Fatalf("Bad: %#v", in)
} }
@ -1013,6 +1046,20 @@ func TestDNS_ExternalServiceToConsulCNAMENestedLookup(t *testing.T) {
if aRec.Hdr.Ttl != 0 { if aRec.Hdr.Ttl != 0 {
t.Fatalf("Bad: %#v", in.Extra[2]) t.Fatalf("Bad: %#v", in.Extra[2])
} }
aRec2, ok := in.Extra[3].(*dns.A)
if !ok {
t.Fatalf("Bad: %#v", in.Extra[3])
}
if aRec2.Hdr.Name != "ns.127.0.0.1.consul." {
t.Fatalf("Bad: %#v", in.Extra[3])
}
if aRec2.A.String() != "127.0.0.1" {
t.Fatalf("Bad: %#v", in.Extra[3])
}
if aRec2.Hdr.Ttl != 0 {
t.Fatalf("Bad: %#v", in.Extra[3])
}
} }
} }
@ -3758,7 +3805,7 @@ func TestDNS_NonExistingLookup(t *testing.T) {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
if len(in.Ns) != 1 { if len(in.Ns) != 2 {
t.Fatalf("Bad: %#v %#v", in, len(in.Answer)) t.Fatalf("Bad: %#v %#v", in, len(in.Answer))
} }
@ -3769,6 +3816,14 @@ func TestDNS_NonExistingLookup(t *testing.T) {
if soaRec.Hdr.Ttl != 0 { if soaRec.Hdr.Ttl != 0 {
t.Fatalf("Bad: %#v", in.Ns[0]) t.Fatalf("Bad: %#v", in.Ns[0])
} }
nsRec, ok := in.Ns[1].(*dns.NS)
if !ok {
t.Fatalf("Bad: %#v", in.Ns[1])
}
if nsRec.Hdr.Ttl != 0 {
t.Fatalf("Bad: %#v", in.Ns[1])
}
} }
func TestDNS_NonExistingLookupEmptyAorAAAA(t *testing.T) { func TestDNS_NonExistingLookupEmptyAorAAAA(t *testing.T) {
@ -3859,21 +3914,28 @@ func TestDNS_NonExistingLookupEmptyAorAAAA(t *testing.T) {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
if len(in.Ns) != 1 { if len(in.Ns) != 2 {
t.Fatalf("Bad: %#v", in) t.Fatalf("Bad: %#v", in)
} }
soaRec, ok := in.Ns[1].(*dns.SOA)
soaRec, ok := in.Ns[0].(*dns.SOA)
if !ok { if !ok {
t.Fatalf("Bad: %#v", in.Ns[0]) t.Fatalf("Bad: %#v", in.Ns[1])
} }
if soaRec.Hdr.Ttl != 0 { if soaRec.Hdr.Ttl != 0 {
t.Fatalf("Bad: %#v", in.Ns[0]) t.Fatalf("Bad: %#v", in.Ns[1])
} }
if in.Rcode != dns.RcodeSuccess { if in.Rcode != dns.RcodeSuccess {
t.Fatalf("Bad: %#v", in) t.Fatalf("Bad: %#v", in)
} }
nsRec, ok := in.Ns[0].(*dns.NS)
if !ok {
t.Fatalf("Bad: %#v", in.Ns[0])
}
if nsRec.Hdr.Ttl != 0 {
t.Fatalf("Bad: %#v", in.Ns[0])
}
} }
// Check for ipv4 records on ipv6-only service directly and via the // Check for ipv4 records on ipv6-only service directly and via the
@ -3893,18 +3955,26 @@ func TestDNS_NonExistingLookupEmptyAorAAAA(t *testing.T) {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
if len(in.Ns) != 1 { if len(in.Ns) != 2 {
t.Fatalf("Bad: %#v", in) t.Fatalf("Bad: %#v", in)
} }
soaRec, ok := in.Ns[0].(*dns.SOA) nsRec, ok := in.Ns[0].(*dns.NS)
if !ok { if !ok {
t.Fatalf("Bad: %#v", in.Ns[0]) t.Fatalf("Bad: %#v", in.Ns[0])
} }
if soaRec.Hdr.Ttl != 0 { if nsRec.Hdr.Ttl != 0 {
t.Fatalf("Bad: %#v", in.Ns[0]) t.Fatalf("Bad: %#v", in.Ns[0])
} }
soaRec, ok := in.Ns[1].(*dns.SOA)
if !ok {
t.Fatalf("Bad: %#v", in.Ns[1])
}
if soaRec.Hdr.Ttl != 0 {
t.Fatalf("Bad: %#v", in.Ns[1])
}
if in.Rcode != dns.RcodeSuccess { if in.Rcode != dns.RcodeSuccess {
t.Fatalf("Bad: %#v", in) t.Fatalf("Bad: %#v", in)
} }
@ -3944,7 +4014,7 @@ func TestDNS_PreparedQuery_AllowStale(t *testing.T) {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
if len(in.Ns) != 1 { if len(in.Ns) != 2 {
t.Fatalf("Bad: %#v", in) t.Fatalf("Bad: %#v", in)
} }
@ -3955,6 +4025,15 @@ func TestDNS_PreparedQuery_AllowStale(t *testing.T) {
if soaRec.Hdr.Ttl != 0 { if soaRec.Hdr.Ttl != 0 {
t.Fatalf("Bad: %#v", in.Ns[0]) t.Fatalf("Bad: %#v", in.Ns[0])
} }
nsRec, ok := in.Ns[1].(*dns.NS)
if !ok {
t.Fatalf("Bad: %#v", in.Ns[1])
}
if nsRec.Hdr.Ttl != 0 {
t.Fatalf("Bad: %#v", in.Ns[1])
}
} }
} }
@ -3982,7 +4061,7 @@ func TestDNS_InvalidQueries(t *testing.T) {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
if len(in.Ns) != 1 { if len(in.Ns) != 2 {
t.Fatalf("Bad: %#v", in) t.Fatalf("Bad: %#v", in)
} }
@ -3993,6 +4072,14 @@ func TestDNS_InvalidQueries(t *testing.T) {
if soaRec.Hdr.Ttl != 0 { if soaRec.Hdr.Ttl != 0 {
t.Fatalf("Bad: %#v", in.Ns[0]) t.Fatalf("Bad: %#v", in.Ns[0])
} }
nsRec, ok := in.Ns[1].(*dns.NS)
if !ok {
t.Fatalf("Bad: %#v", in.Ns[1])
}
if nsRec.Hdr.Ttl != 0 {
t.Fatalf("Bad: %#v", in.Ns[1])
}
} }
} }