diff --git a/nameserver/server_cache_test.go b/nameserver/server_cache_test.go index 3e3be2d9..3ad13f17 100644 --- a/nameserver/server_cache_test.go +++ b/nameserver/server_cache_test.go @@ -11,6 +11,160 @@ import ( wt "github.com/weaveworks/weave/testing" ) +// Check that AddRecord/DeleteRecord/... in the Zone database lead to cache invalidations +func TestServerDbCacheInvalidation(t *testing.T) { + const ( + containerID = "somecontainer" + testName1 = "first.weave.local." + testName2 = "second.weave.local." + ) + + InitDefaultLogging(testing.Verbose()) + Info.Println("TestServerDbCacheInvalidation starting") + + clk := newMockedClock() + + Debug.Printf("Creating mocked mDNS client and server") + mdnsServer1 := newMockedMDNSServerWithRecord(Record{testName1, net.ParseIP("10.2.2.9"), 0, 0, 0}) + mdnsCli1 := newMockedMDNSClient([]*mockedMDNSServer{mdnsServer1}) + + Debug.Printf("Creating zone database with the mocked mDNS client and server") + zoneConfig := ZoneConfig{ + MDNSServer: mdnsServer1, + MDNSClient: mdnsCli1, + Clock: clk, + } + zone, err := NewZoneDb(zoneConfig) + wt.AssertNoErr(t, err) + err = zone.Start() + wt.AssertNoErr(t, err) + defer zone.Stop() + + Debug.Printf("Creating a cache") + cache, err := NewCache(1024, clk) + wt.AssertNoErr(t, err) + + fallbackHandler := func(w dns.ResponseWriter, req *dns.Msg) { + m := new(dns.Msg) + m.SetReply(req) + if len(req.Question) == 1 { + m.Rcode = dns.RcodeNameError + } + w.WriteMsg(m) + } + + // Run another DNS server for fallback + fallback, err := newMockedFallback(fallbackHandler, nil) + wt.AssertNoErr(t, err) + fallback.Start() + defer fallback.Stop() + + Debug.Printf("Creating a real DNS server with a mocked cache") + srv, err := NewDNSServer(DNSServerConfig{ + Zone: zone, + Cache: cache, + Clock: clk, + ListenReadTimeout: testSocketTimeout, + UpstreamCfg: fallback.CliConfig, + MaxAnswers: 4, + }) + wt.AssertNoErr(t, err) + defer srv.Stop() + go srv.Start() + time.Sleep(100 * time.Millisecond) // Allow server goroutine to start + + testPort, err := srv.GetPort() + wt.AssertNoErr(t, err) + wt.AssertNotEqualInt(t, testPort, 0, "invalid listen port") + + Debug.Printf("Adding two IPs to %s", testName1) + zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.1")) + zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.2")) + q, _ := assertExchange(t, testName1, dns.TypeA, testPort, 2, 2, 0) + assertInCache(t, cache, q, fmt.Sprintf("after asking for %s", testName1)) + + // Zone database at this point: + // first.weave.local = 10.2.2.1 10.2.2.2 + + zone.AddRecord(containerID, testName2, net.ParseIP("10.9.9.1")) + assertInCache(t, cache, q, fmt.Sprintf("after adding a new IP for %s", testName2)) + + // we should have an entry in the cache for this query + // if we add another IP, that cache entry should be removed + Debug.Printf("Adding a new IP to %s: the cache entry should be removed", testName1) + zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.3")) + assertNotInCache(t, cache, q, fmt.Sprintf("after adding a new IP for %s", testName1)) + + // Zone database at this point: + // first.weave.local = 10.2.2.1 10.2.2.2 10.2.2.3 + // second.weave.local = 10.9.9.1 + + Debug.Printf("Querying again (so a cache entry will be created)") + q, _ = assertExchange(t, testName1, dns.TypeA, testPort, 3, 4, 0) + assertInCache(t, cache, q, "after asking about the name") + Debug.Printf("... and removing one of the IP addresses") + zone.DeleteRecord(containerID, net.ParseIP("10.2.2.2")) + assertNotInCache(t, cache, q, "after deleting IP for 10.2.2.2") + + // Zone database at this point: + // first.weave.local = 10.2.2.1 10.2.2.3 + // second.weave.local = 10.9.9.1 + + // generate cache responses + Debug.Printf("Querying for a raddr") + qname, _ := assertExchange(t, testName1, dns.TypeA, testPort, 2, 2, 0) + qptr, _ := assertExchange(t, "1.2.2.10.in-addr.arpa.", dns.TypePTR, testPort, 1, 1, 0) + qotherName, _ := assertExchange(t, testName2, dns.TypeA, testPort, 1, 1, 0) + qotherPtr, _ := assertExchange(t, "1.9.9.10.in-addr.arpa.", dns.TypePTR, testPort, 1, 1, 0) + qwrongName, _ := assertExchange(t, "wrong.weave.local.", dns.TypeA, testPort, 0, 0, dns.RcodeNameError) + assertInCache(t, cache, qname, "after asking for name") + assertInCache(t, cache, qptr, "after asking for address") + assertInCache(t, cache, qotherName, "after asking for second name") + assertInCache(t, cache, qotherPtr, "after asking for second address") + assertNotLocalInCache(t, cache, qwrongName, "after asking for a wrong name") + + // now we will check if a removal affects all the responses + Debug.Printf("... and removing an IP should invalidate both the cached responses for name and raddr") + zone.DeleteRecord(containerID, net.ParseIP("10.2.2.1")) + assertNotInCache(t, cache, qptr, "after deleting record") + assertNotInCache(t, cache, qname, "after deleting record") + assertInCache(t, cache, qotherName, "after deleting record") + + // Zone database at this point: + // first.weave.local = 10.2.2.3 + // second.weave.local = 10.9.9.1 + + // generate cache responses + Debug.Printf("Querying for a raddr") + qptr, _ = assertExchange(t, "3.2.2.10.in-addr.arpa.", dns.TypePTR, testPort, 1, 1, 0) + qname, _ = assertExchange(t, testName1, dns.TypeA, testPort, 1, 1, 0) + qotherName, _ = assertExchange(t, testName2, dns.TypeA, testPort, 1, 1, 0) + qotherPtr, _ = assertExchange(t, "1.9.9.10.in-addr.arpa.", dns.TypePTR, testPort, 1, 1, 0) + assertInCache(t, cache, qname, "after asking for name") + assertInCache(t, cache, qptr, "after asking for PTR") + assertInCache(t, cache, qotherName, "after asking for second name") + assertInCache(t, cache, qotherPtr, "after asking for second address") + + // let's repeat this, but adding an IP + Debug.Printf("... and adding a new IP should invalidate both the cached responses for the name") + zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.7")) + assertNotInCache(t, cache, qname, "after adding a new IP") + assertInCache(t, cache, qotherName, "after adding a new IP") + assertInCache(t, cache, qotherPtr, "after adding a new IP") + + // check that after some time, the cache entry is expired + clk.Forward(int(localTTL) + 1) + assertNotInCache(t, cache, qotherName, "after passing some time") + assertNotInCache(t, cache, qwrongName, "after passing some time") + + // Zone database at this point: + // first.weave.local = 10.2.2.3 10.2.2.7 + // second.weave.local = 10.9.9.1 + + zone.DeleteRecordsFor(containerID) + assertNotInCache(t, cache, qotherName, "after removing container") + assertNotInCache(t, cache, qotherPtr, "after removing container") +} // Check if the names updates lead to cache invalidations func TestServerCacheRefresh(t *testing.T) { const (