Skip to content

Commit

Permalink
Added NS requests.
Browse files Browse the repository at this point in the history
  • Loading branch information
kozyraki committed Apr 16, 2015
1 parent 47f0301 commit 7ece6ca
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 18 deletions.
3 changes: 2 additions & 1 deletion config.json.sample
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@
"dnson": true,
"httpon": true,
"httpport": 8123,
"externalon": true
"externalon": true,
"recurseon": true
}
1 change: 1 addition & 0 deletions docs/docs/configuration-parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,4 @@ It is sufficient to specify just one of the `zk` or `masters` field. If both are

`SOAMinttl` is the minimum TTL field in the SOA record for the Mesos domain. For details, see the [RFC-2308](https://tools.ietf.org/html/rfc2308). The default value is `60`.

`recurseon` controls if the DNS replies for names in the Mesos domain will indicate that recursion is available. The default value is `true`.
11 changes: 8 additions & 3 deletions docs/docs/naming.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,21 @@ _search._tcp.marathon.mesos. 60 IN SRV 0 0 31302 10.254.132.41.

SRV records are generated only for tasks that have been allocated a specific port through Mesos.

## Other Records

Mesos-DNS generates a few special records. Specifically, it creates a set of records for the leading master (A record for `leader.domain` and SRV records for `_leader._tcp.domain` and `_leader._udp.domain`). It also creates creates A records (`master.domain`) and SRV records (`_master._tcp.domain` and `_master._udp.domain`) for every Mesos master it knows about. Note that, if you configure Mesos-DNS to detect the leading master through Zookeeper, then this is the only master it knows about. If you configure Mesos-DNS using the `masters` field, it will generate master records for every master in the list. Also note that there is inherent delay between the election of a new master and the update of leader/master records in Mesos-DNS.

Mesos-DNS generates A records for itself that list all the IP addresses that Mesos-DNS is listening to. The name for Mesos-DNS can be selected using the `SOARname` [configuration parameter](configuration-parameters.html). The default name is `ns1.mesos`.

In addition to A and SRV records for Mesos tasks, Mesos-DNS supports requests for SOA and NS records for the Mesos domain. DNS requests for records of other types in the Mesos domain will return `NXDOMAIN`. Mesos-DNS does not support PTR records needed fo reserve lookups.

## Notes

If a framework launches multiple tasks with the same name, the DNS lookup will return multiple records, one per task. Mesos-DNS randomly shuffles the order of records to provide rudimentary load balancing between these tasks.

Mesos-DNS does not support other types of DNS records at this point, including the PTR records needed for reverse lookups. DNS requests for records of type`ANY`, `A`, or `SRV` will return any A or SRV records found. DNS requests for records of other types in the Mesos domain will return `NXDOMAIN`.
Mesos-DNS follows [RFC 952](https://tools.ietf.org/html/rfc952) for name formatting. All fields used to construct hostnames for A records and service names for SRV records must be up to 24 characters and drawn from the alphabet (A-Z), digits (0-9) and minus sign (-). No distinction is made between upper and lower case. If the task name does not comply with these constraints, Mesos-DNS will trim it, remove all invalid characters, and replace period (.) with sign (-) for task names. For framework names, we allow period (.) but all other constraints apply. For example, a task named `apiserver.myservice` launch by framework `marathon.prod`, will have A records associated with the name `apiserver-myservice.marathon.prod.mesos` and SRV records associated with name `_apiserver-myservice._tcp.marathon.prod.mesos`.

Some frameworks register with longer, less friendly names. For example, earlier versions of marathon may register with names like `marathon-0.7.5`, which will lead to names like `search.marathon-0.7.5.mesos`. Make sure your framework registers with the desired name. For instance, you can launch marathon with ` --framework_name marathon` to get the framework registered as `marathon`.

## Special Records

Mesos-DNS generates a few special records. Specifically, it creates a set of records for the leading master (A record for `leader.domain` and SRV records for `_leader._tcp.domain` and `_leader._udp.domain`). It also creates creates A records (`master.domain`) and SRV records (`_master._tcp.domain` and `_master._udp.domain`) for every Mesos master it knows about. Note that, if you configure Mesos-DNS to detect the leading master through Zookeeper, then this is the only master it knows about. If you configure Mesos-DNS using the `masters` field, it will generate master records for every master in the list. Also not that the is inherent delay between the election of a new master and the update of leader/master records in Mesos-DNS. Finally Mesos-DNS generates A records for itself (`mesos-dns.domain`) that list all the IP addresses that Mesos-DNS is listening to.

5 changes: 5 additions & 0 deletions records/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ type Config struct {
SOAExpire uint32 // expiration time
SOAMinttl uint32 // minimum TTL

// Value of RecursionAvailable for responses in Mesos domain
RecurseOn bool

// ListenAddr is the server listener address
Listener string

Expand Down Expand Up @@ -89,6 +92,7 @@ func SetConfig(cjson string) (c Config) {
DnsOn: true,
HttpOn: true,
ExternalOn: true,
RecurseOn: true,
}

// read configuration file
Expand Down Expand Up @@ -164,6 +168,7 @@ func SetConfig(cjson string) (c Config) {
logging.Verbose.Println(" - SOARetry: ", c.SOARetry)
logging.Verbose.Println(" - SOAExpire: ", c.SOAExpire)
logging.Verbose.Println(" - SOAExpire: ", c.SOAMinttl)
logging.Verbose.Println(" - RecurseOn: ", c.RecurseOn)
logging.Verbose.Println(" - HttpPort: ", c.HttpPort)
logging.Verbose.Println(" - HttpOn: ", c.HttpOn)
logging.Verbose.Println(" - ConfigFile: ", c.File)
Expand Down
2 changes: 1 addition & 1 deletion records/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func (rg *RecordGenerator) ParseState(leader string, c Config) error {
}

// insert state
rg.InsertState(sj, c.Domain, c.SOAMname, c.Listener, c.Masters)
rg.InsertState(sj, c.Domain, c.SOARname, c.Listener, c.Masters)
return nil
}

Expand Down
43 changes: 33 additions & 10 deletions resolver/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,8 @@ func (res *Resolver) formatSOA(dom string) (*dns.SOA, error) {
Class: dns.ClassINET,
Ttl: ttl,
},
Ns: res.config.SOAMname,
Mbox: res.config.SOARname,
Ns: res.config.SOARname,
Mbox: res.config.SOAMname,
Serial: res.config.SOASerial,
Refresh: res.config.SOARefresh,
Retry: res.config.SOARetry,
Expand All @@ -237,6 +237,23 @@ func (res *Resolver) formatSOA(dom string) (*dns.SOA, error) {
}, nil
}

// formatNS returns the NS record for the mesos domain
func (res *Resolver) formatNS(dom string) (*dns.NS, error) {
ttl := uint32(res.config.TTL)

return &dns.NS{
Hdr: dns.RR_Header{
Name: dom,
Rrtype: dns.TypeNS,
Class: dns.ClassINET,
Ttl: ttl,
},
Ns: res.config.SOAMname,
}, nil
}



// reorders answers for very basic load balancing
func shuffleAnswers(answers []dns.RR) []dns.RR {
rand.Seed(time.Now().UTC().UnixNano())
Expand Down Expand Up @@ -315,7 +332,7 @@ func (res *Resolver) HandleMesos(w dns.ResponseWriter, r *dns.Msg) {

m := new(dns.Msg)
m.Authoritative = true
m.RecursionAvailable = false
m.RecursionAvailable = res.config.RecurseOn
m.SetReply(r)

rs := res.records()
Expand Down Expand Up @@ -357,10 +374,7 @@ func (res *Resolver) HandleMesos(w dns.ResponseWriter, r *dns.Msg) {
}

// SOA requests
if qType == dns.TypeSOA {
m = new(dns.Msg)
m.SetReply(r)

if (qType == dns.TypeSOA) || (qType == dns.TypeANY) {
rr, err := res.formatSOA(r.Question[0].Name)
if err != nil {
logging.Error.Println(err)
Expand All @@ -369,6 +383,17 @@ func (res *Resolver) HandleMesos(w dns.ResponseWriter, r *dns.Msg) {
}
}

// NS requests
if (qType == dns.TypeNS) || (qType == dns.TypeANY) {
rr, err := res.formatNS(r.Question[0].Name)
logging.Error.Println("NS request")
if err != nil {
logging.Error.Println(err)
} else {
m.Ns = append(m.Ns, rr)
}
}

// shuffle answers
m.Answer = shuffleAnswers(m.Answer)
// tracing info
Expand All @@ -378,16 +403,14 @@ func (res *Resolver) HandleMesos(w dns.ResponseWriter, r *dns.Msg) {
logging.CurLog.MesosFailed.Inc()
} else if (qType == dns.TypeAAAA) && (len(rs.SRVs[dom]) > 0 || len(rs.As[dom]) > 0) {
// correct handling of AAAA if there are A or SRV records
m = new(dns.Msg)
m.Authoritative = true
// set NOERROR
m.SetRcode(r, 0)
// leave answer empty (NOERROR --> NODATA)

} else {
// no answers but not a {SOA,SRV} request
if len(m.Answer) == 0 && (qType != dns.TypeSOA) && (qType != dns.TypeSRV) {
m = new(dns.Msg)
if len(m.Answer) == 0 && (qType != dns.TypeSOA) && (qType != dns.TypeNS) && (qType != dns.TypeSRV) {
// set NXDOMAIN
m.SetRcode(r, 3)

Expand Down
17 changes: 14 additions & 3 deletions resolver/resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,15 +180,26 @@ func TestHandler(t *testing.T) {
}

// test SOA
m, err2 := fakeMsg("non-existing.mesos.", dns.TypeSOA, "udp")
if err2 != nil {
t.Error(err2)
m, err := fakeMsg("non-existing.mesos.", dns.TypeSOA, "udp")
if err != nil {
t.Error(err)
}

if m.Ns == nil {
t.Error("not serving up SOA")
}

// test NS
m, err = fakeMsg("non-existing2.mesos.", dns.TypeNS, "udp")
if err != nil {
t.Error(err)
}

if m.Ns == nil {
t.Error("not serving up NS")
}


// test non-existing host
m, err = fakeMsg("missing.mesos.", dns.TypeA, "udp")
if err != nil {
Expand Down

0 comments on commit 7ece6ca

Please sign in to comment.