package nameserver import ( "fmt" "github.com/miekg/dns" . "github.com/weaveworks/weave/common" wt "github.com/weaveworks/weave/testing" "net" "testing" "time" ) // Check that the cache keeps its intended capacity constant func TestCacheLength(t *testing.T) { InitDefaultLogging(true) const cacheLen = 128 l, err := NewCache(cacheLen) wt.AssertNoErr(t, err) insTime := time.Now() t.Logf("Inserting 256 questions in the cache at '%s', with TTL from 0 to 255", insTime) for i := 0; i < cacheLen*2; i++ { questionMsg := new(dns.Msg) questionMsg.SetQuestion(fmt.Sprintf("name%d", i), dns.TypeA) questionMsg.RecursionDesired = true question := &questionMsg.Question[0] ip := net.ParseIP(fmt.Sprintf("10.0.1.%d", i)) ips := []net.IP{ip} reply := makeAddressReply(questionMsg, question, ips) reply.Answer[0].Header().Ttl = uint32(i) l.Put(questionMsg, reply, 0, 0, insTime) } wt.AssertEqualInt(t, l.Len(), cacheLen, "cache length") minExpectedTime := insTime.Add(time.Duration(cacheLen) * time.Second) t.Logf("Checking all remaining entries expire after insert_time + %d secs='%s'", cacheLen, minExpectedTime) for _, entry := range l.entries { if entry.validUntil.Before(minExpectedTime) { t.Fatalf("Entry valid until %s", entry.validUntil) } } } // Check that the cache entries are ok func TestCacheEntries(t *testing.T) { InitDefaultLogging(true) Info.Println("Checking cache consistency") const cacheLen = 128 l, err := NewCache(cacheLen) wt.AssertNoErr(t, err) questionMsg := new(dns.Msg) questionMsg.SetQuestion("some.name", dns.TypeA) questionMsg.RecursionDesired = true question := &questionMsg.Question[0] t.Logf("Trying to get a name") resp, err := l.Get(questionMsg, minUDPSize, time.Now()) wt.AssertNoErr(t, err) if resp != nil { t.Logf("Got\n%s", resp) t.Fatalf("ERROR: Did not expect a reponse from Get() yet") } t.Logf("Trying to get it again") resp, err = l.Get(questionMsg, minUDPSize, time.Now()) wt.AssertNoErr(t, err) if resp != nil { t.Logf("Got\n%s", resp) t.Fatalf("ERROR: Did not expect a reponse from Get() yet") } t.Logf("Inserting the reply") reply1 := makeAddressReply(questionMsg, question, []net.IP{net.ParseIP("10.0.1.1")}) l.Put(questionMsg, reply1, nullTTL, 0, time.Now()) timeGet1 := time.Now() t.Logf("Checking we can Get() the reply now") resp, err = l.Get(questionMsg, minUDPSize, timeGet1) wt.AssertNoErr(t, err) wt.AssertTrue(t, resp != nil, "reponse from Get()") t.Logf("Received '%s'", resp.Answer[0]) wt.AssertType(t, resp.Answer[0], (*dns.A)(nil), "DNS record") ttlGet1 := resp.Answer[0].Header().Ttl timeGet2 := timeGet1.Add(time.Duration(1) * time.Second) t.Logf("Checking that a second Get(), after 1 second, gets the same result, but with reduced TTL") resp, err = l.Get(questionMsg, minUDPSize, timeGet2) wt.AssertNoErr(t, err) wt.AssertTrue(t, resp != nil, "reponse from a second Get()") t.Logf("Received '%s'", resp.Answer[0]) wt.AssertType(t, resp.Answer[0], (*dns.A)(nil), "DNS record") ttlGet2 := resp.Answer[0].Header().Ttl wt.AssertEqualInt(t, int(ttlGet1-ttlGet2), 1, "TTL difference") timeGet3 := timeGet1.Add(time.Duration(localTTL) * time.Second) t.Logf("Checking that a third Get(), after %d second, gets no result", localTTL) resp, err = l.Get(questionMsg, minUDPSize, timeGet3) wt.AssertNoErr(t, err) if resp != nil { t.Logf("Got\n%s", resp) t.Fatalf("ERROR: Did NOT expect a reponse from the second Get()") } t.Logf("Checking that an Remove() results in Get() returning nothing") replyTemp := makeAddressReply(questionMsg, question, []net.IP{net.ParseIP("10.0.9.9")}) l.Put(questionMsg, replyTemp, nullTTL, 0, time.Now()) lenBefore := l.Len() l.Remove(question) wt.AssertEqualInt(t, l.Len(), lenBefore-1, "cache length") l.Remove(question) // do it again: should have no effect... wt.AssertEqualInt(t, l.Len(), lenBefore-1, "cache length") resp, err = l.Get(questionMsg, minUDPSize, timeGet1) wt.AssertNoErr(t, err) wt.AssertTrue(t, resp == nil, "reponse from the Get() after a Remove()") t.Logf("Inserting a two replies for the same query") timePut2 := time.Now() reply2 := makeAddressReply(questionMsg, question, []net.IP{net.ParseIP("10.0.1.2")}) l.Put(questionMsg, reply2, nullTTL, 0, timePut2) timePut3 := timePut2.Add(time.Duration(1) * time.Second) reply3 := makeAddressReply(questionMsg, question, []net.IP{net.ParseIP("10.0.1.3")}) l.Put(questionMsg, reply3, nullTTL, 0, timePut3) t.Logf("Checking we get the last one...") resp, err = l.Get(questionMsg, minUDPSize, timePut3) wt.AssertNoErr(t, err) wt.AssertTrue(t, resp != nil, "reponse from the Get()") t.Logf("Received '%s'", resp.Answer[0]) wt.AssertType(t, resp.Answer[0], (*dns.A)(nil), "DNS record") wt.AssertEqualString(t, resp.Answer[0].(*dns.A).A.String(), "10.0.1.3", "IP address") wt.AssertEqualInt(t, int(resp.Answer[0].Header().Ttl), int(localTTL), "TTL") resp, err = l.Get(questionMsg, minUDPSize, timePut3.Add(time.Duration(localTTL-1)*time.Second)) wt.AssertNoErr(t, err) wt.AssertTrue(t, resp != nil, "reponse from the Get()") t.Logf("Received '%s'", resp.Answer[0]) wt.AssertType(t, resp.Answer[0], (*dns.A)(nil), "DNS record") wt.AssertEqualString(t, resp.Answer[0].(*dns.A).A.String(), "10.0.1.3", "IP address") wt.AssertEqualInt(t, int(resp.Answer[0].Header().Ttl), 1, "TTL") t.Logf("Checking we get empty replies when they are expired...") lenBefore = l.Len() resp, err = l.Get(questionMsg, minUDPSize, timePut3.Add(time.Duration(localTTL)*time.Second)) wt.AssertNoErr(t, err) if resp != nil { t.Logf("Got\n%s", resp.Answer[0]) t.Fatalf("ERROR: Did NOT expect a reponse from the Get()") } wt.AssertEqualInt(t, l.Len(), lenBefore-1, "cache length (after getting an expired entry)") questionMsg2 := new(dns.Msg) questionMsg2.SetQuestion("some.other.name", dns.TypeA) questionMsg2.RecursionDesired = true question2 := &questionMsg2.Question[0] t.Logf("Trying to Get() a name") resp, err = l.Get(questionMsg2, minUDPSize, time.Now()) wt.AssertNoErr(t, err) wt.AssertNil(t, resp, "reponse from Get() yet") t.Logf("Checking that an Remove() between Get() and Put() does not break things") replyTemp2 := makeAddressReply(questionMsg2, question2, []net.IP{net.ParseIP("10.0.9.9")}) l.Remove(question2) l.Put(questionMsg2, replyTemp2, nullTTL, 0, time.Now()) resp, err = l.Get(questionMsg2, minUDPSize, time.Now()) wt.AssertNoErr(t, err) wt.AssertNotNil(t, resp, "reponse from Get()") questionMsg3 := new(dns.Msg) questionMsg3.SetQuestion("some.other.name", dns.TypeA) questionMsg3.RecursionDesired = true question3 := &questionMsg3.Question[0] t.Logf("Checking that a entry with CacheNoLocalReplies return an error") timePut3 = time.Now() l.Put(questionMsg3, nil, nullTTL, CacheNoLocalReplies, timePut3) resp, err = l.Get(questionMsg3, minUDPSize, timePut3) wt.AssertNil(t, resp, "Get() response with CacheNoLocalReplies") wt.AssertNotNil(t, err, "Get() error with CacheNoLocalReplies") timeExpiredGet3 := timePut3.Add(time.Second * time.Duration(negLocalTTL+1)) t.Logf("Checking that we get an expired response after %f seconds", timeExpiredGet3.Sub(timePut3).Seconds()) resp, err = l.Get(questionMsg3, minUDPSize, timeExpiredGet3) wt.AssertNil(t, resp, "expired Get() response with CacheNoLocalReplies") wt.AssertNil(t, err, "expired Get() error with CacheNoLocalReplies") l.Remove(question3) t.Logf("Checking that Put&Get with CacheNoLocalReplies with a Remove in the middle returns nothing") l.Put(questionMsg3, nil, nullTTL, CacheNoLocalReplies, time.Now()) l.Remove(question3) resp, err = l.Get(questionMsg3, minUDPSize, time.Now()) wt.AssertNil(t, resp, "Get() reponse with CacheNoLocalReplies") wt.AssertNil(t, err, "Get() error with CacheNoLocalReplies") }