Limit the number of answers we provide to users.

This commit is contained in:
Alvaro Saurin committed 2015-05-26 11:30:35 +02:00
1 parent 0cb6a6269d
commit cfcc08a914
6 files changed
+70 -11

No files matched your search

+9 -1
View File
@@ -89,7 +89,6 @@ func getMaxReplyLen(r *dns.Msg, proto dnsProtocol) int {
return maxLen
}
func init() {
rand.Seed(time.Now().UTC().UnixNano())
}
@@ -105,3 +104,12 @@ func shuffleAnswers(answers []dns.RR) []dns.RR {
return answers
}
// only take the first `num` answers
func pruneAnswers(answers []dns.RR, num int) []dns.RR {
if num > 0 && len(answers) > num {
// TODO: we should have some prefer locally-introduced answers, etc...
return answers[:num]
}
return answers
}
+38
View File
@@ -0,0 +1,38 @@
package nameserver
import (
"github.com/miekg/dns"
. "github.com/weaveworks/weave/common"
wt "github.com/weaveworks/weave/testing"
"net"
"testing"
)
// Check that we can prune an answer
func TestPrune(t *testing.T) {
InitDefaultLogging(testing.Verbose())
Info.Println("TestPrune starting")
questionMsg := new(dns.Msg)
questionMsg.SetQuestion("name", dns.TypeA)
questionMsg.RecursionDesired = true
question := &questionMsg.Question[0]
records := []ZoneRecord{
Record{"name", net.ParseIP("10.0.1.1"), 0, 0, 0},
Record{"name", net.ParseIP("10.0.1.2"), 0, 0, 0},
Record{"name", net.ParseIP("10.0.1.3"), 0, 0, 0},
Record{"name", net.ParseIP("10.0.1.4"), 0, 0, 0},
}
reply := makeAddressReply(questionMsg, question, records)
reply.Answer[0].Header().Ttl = localTTL
pruned := pruneAnswers(reply.Answer, 1)
wt.AssertEqualInt(t, len(pruned), 1, "wrong number of answers")
pruned = pruneAnswers(reply.Answer, 2)
wt.AssertEqualInt(t, len(pruned), 2, "wrong number of answers")
pruned = pruneAnswers(reply.Answer, 0)
wt.AssertEqualInt(t, len(pruned), len(records), "wrong number of answers")
}
+13 -6
View File
@@ -18,6 +18,7 @@ const (
DefaultUDPBuflen = 4096 // bigger than the default 512
DefaultCacheLen = 8192 // default cache capacity
DefaultTimeout = 5 // default timeout for DNS resolutions
DefaultMaxAnswers = 1 // default number of answers provided to users
)
type DNSServerConfig struct {
@@ -45,6 +46,8 @@ type DNSServerConfig struct {
Clock clock.Clock
// (Optional) Listening socket read timeout (in milliseconds)
ListenReadTimeout int
// Maximum number of answers provided to users
MaxAnswers int
}
type dnsProtocol uint8
@@ -88,6 +91,7 @@ type DNSServer struct {
lst net.Listener
cache ZoneCache
cacheDisabled bool
maxAnswers int
negLocalTTL int
timeout time.Duration
readTimeout time.Duration
@@ -107,6 +111,7 @@ func NewDNSServer(config DNSServerConfig) (s *DNSServer, err error) {
timeout: time.Duration(config.Timeout) * time.Millisecond,
readTimeout: time.Duration(config.ListenReadTimeout) * time.Millisecond,
cacheDisabled: false,
maxAnswers: DefaultMaxAnswers,
negLocalTTL: negLocalTTL,
clock: config.Clock,
}
@@ -140,6 +145,9 @@ func NewDNSServer(config DNSServerConfig) (s *DNSServer, err error) {
if config.UDPBufLen > 0 {
s.udpBuf = config.UDPBufLen
}
if config.MaxAnswers > 0 {
s.maxAnswers = config.MaxAnswers
}
if config.CacheNegLocalTTL > 0 {
s.negLocalTTL = config.CacheNegLocalTTL
}
@@ -301,17 +309,16 @@ func (s *DNSServer) localHandler(proto dnsProtocol, kind string, qtype uint16,
if err == errNoLocalReplies {
Debug.Printf("[dns msgid %d] Cached no-local-replies", r.MsgHdr.Id)
fallback(w, r)
return
} else {
Debug.Printf("[dns msgid %d] Error from cache: %s", r.MsgHdr.Id, err)
w.WriteMsg(makeDNSFailResponse(r))
return
}
return
}
if reply != nil {
reply.Answer = pruneAnswers(shuffleAnswers(reply.Answer), s.maxAnswers)
Debug.Printf("[dns msgid %d] Returning reply from cache: %s/%d answers",
r.MsgHdr.Id, dns.RcodeToString[reply.MsgHdr.Rcode], len(reply.Answer))
reply.Answer = shuffleAnswers(reply.Answer)
w.WriteMsg(reply)
return
}
@@ -334,10 +341,10 @@ func (s *DNSServer) localHandler(proto dnsProtocol, kind string, qtype uint16,
} else {
m := msgBuilder(r, &q, answers)
m.Authoritative = true
m.Answer = shuffleAnswers(m.Answer)
Debug.Printf("[dns msgid %d] Sending response: %s/%s:%s [code:%s]",
m.MsgHdr.Id, dns.TypeToString[q.Qtype], q.Name, answers, dns.RcodeToString[m.Rcode])
maybeCache(m, nullTTL, 0)
m.Answer = pruneAnswers(shuffleAnswers(m.Answer), s.maxAnswers)
Debug.Printf("[dns msgid %d] Sending response: %s/%d answers [code:%s]",
m.MsgHdr.Id, dns.TypeToString[q.Qtype], len(m.Answer), dns.RcodeToString[m.Rcode])
w.WriteMsg(m)
}
}
+1
View File
@@ -42,6 +42,7 @@ func TestServerCacheRefresh(t *testing.T) {
Cache: cache,
Clock: clk,
ListenReadTimeout: testSocketTimeout,
MaxAnswers: 4,
})
wt.AssertNoErr(t, err)
go srv.Start()
+6 -4
View File
@@ -44,10 +44,9 @@ $ docker attach $shell1
```
Note that it is permissible to register multiple containers with the
same name. weaveDNS picks one address to return when asked for the
name. Since weaveDNS removes any container that dies, this is a simple
way to implement redundancy. In the current implementation it does
not attempt to do load-balancing.
same name: weaveDNS picks one address at random on each request. Since
weaveDNS removes any container that dies, this is a simple way to
implement redundancy.
Each weaveDNS container started with `launch-dns` needs to be given
its own, unique, IP address, in a subnet that is a) common to all
@@ -103,6 +102,9 @@ The inverse operation can be carried out using the `dns-remove` command:
$ weave dns-remove 10.2.1.27 $shell2
```
When queried about a name with multiple IPs, weaveDNS returns a random
result from the set of IPs available.
## <a name="hot-swapping"></a>Hot-swapping service containers
If you would like to deploy a new version of a service, keep the old
+3
View File
@@ -27,6 +27,7 @@ func main() {
refreshInterval int
refreshWorkers int
relevantTime int
maxAnswers int
cacheLen int
cacheDisabled bool
watch bool
@@ -47,6 +48,7 @@ func main() {
// advanced options
flag.IntVar(&refreshInterval, "refresh", weavedns.DefaultRefreshInterval, "refresh interval (in secs) for local names (0=disable)")
flag.IntVar(&refreshWorkers, "refresh-workers", weavedns.DefaultNumUpdaters, "default number of background updaters")
flag.IntVar(&maxAnswers, "max-answers", weavedns.DefaultMaxAnswers, "maximum number of answers returned to clients (0=unlimited)")
flag.IntVar(&relevantTime, "relevant", weavedns.DefaultRelevantTime, "life time for info in the absence of queries (in secs)")
flag.IntVar(&udpbuf, "udpbuf", weavedns.DefaultUDPBuflen, "UDP buffer length")
flag.IntVar(&timeout, "timeout", weavedns.DefaultTimeout, "timeout for resolutions (in millisecs)")
@@ -102,6 +104,7 @@ func main() {
Zone: zone,
Port: dnsPort,
CacheLen: cacheLen,
MaxAnswers: maxAnswers,
Timeout: timeout,
UDPBufLen: udpbuf,
CacheDisabled: cacheDisabled,