From b87b9423639dc15975d52dff74bc1a88b1de7ff3 Mon Sep 17 00:00:00 2001 From: Alvaro Saurin Date: Mon, 27 Apr 2015 13:40:48 +0200 Subject: [PATCH] Check we can use our mDNS client with our mDNS server --- nameserver/addrs.go | 2 + nameserver/mdns_test.go | 169 ++++++++++++++++++++++++++++++++ nameserver/mocks_test.go | 19 +++- nameserver/server_cache_test.go | 15 +-- nameserver/zone.go | 28 +++--- nameserver/zone_lookup.go | 152 ++++++++++++---------------- nameserver/zone_lookup_test.go | 22 ++--- 7 files changed, 276 insertions(+), 131 deletions(-) create mode 100644 nameserver/mdns_test.go diff --git a/nameserver/addrs.go b/nameserver/addrs.go index 6d6e3abd..bb4ac27c 100644 --- a/nameserver/addrs.go +++ b/nameserver/addrs.go @@ -25,6 +25,8 @@ type ZoneLookup interface { LookupInaddr(inaddr string) ([]ZoneRecord, error) } +type ZoneLookupFunc func(target string) ([]ZoneRecord, error) + type ZoneObservable interface { // Observe anything that affects a particular name in the zone ObserveName(name string, observer ZoneRecordObserver) error diff --git a/nameserver/mdns_test.go b/nameserver/mdns_test.go new file mode 100644 index 00000000..0ed34f93 --- /dev/null +++ b/nameserver/mdns_test.go @@ -0,0 +1,169 @@ +package nameserver + +import ( + "github.com/miekg/dns" + . "github.com/weaveworks/weave/common" + wt "github.com/weaveworks/weave/testing" + "net" + "testing" + "time" +) + +// Check that we can use a regular mDNS server with a regular mDNS client +func TestClientServerSimpleQuery(t *testing.T) { + InitDefaultLogging(testing.Verbose()) + + testRecord1 := Record{"test.weave.local.", net.ParseIP("10.2.2.1"), 0, 0, 0} + testInAddr1 := "1.2.2.10.in-addr.arpa." + + mzone := newMockedZoneWithRecords([]ZoneRecord{testRecord1}) + mdnsServer, err := NewMDNSServer(mzone, true) + wt.AssertNoErr(t, err) + err = mdnsServer.Start(nil) + wt.AssertNoErr(t, err) + defer mdnsServer.Stop() + + var receivedAddr net.IP + var receivedName string + receivedCount := 0 + + mdnsCli, err := NewMDNSClient() + wt.AssertNoErr(t, err) + err = mdnsCli.Start(nil) + wt.AssertNoErr(t, err) + + sendQuery := func(name string, querytype uint16) { + receivedAddr = nil + receivedName = "" + receivedCount = 0 + Debug.Printf("Sending query...") + switch querytype { + case dns.TypeA: + r, err := mdnsCli.LookupName(name) + if len(r) > 0 { + receivedAddr = r[0].IP() + if err == nil { + receivedCount++ + } + } + case dns.TypePTR: + r, err := mdnsCli.LookupInaddr(name) + if len(r) > 0 { + receivedName = r[0].Name() + if err == nil { + receivedCount++ + } + } + } + } + + time.Sleep(100 * time.Millisecond) // Allow for server to get going + + Debug.Printf("Query: %s dns.TypeA", testRecord1.Name()) + sendQuery(testRecord1.Name(), dns.TypeA) + if receivedCount != 1 { + t.Fatalf("Unexpected result count %d for %s", receivedCount, testRecord1.Name()) + } + if !receivedAddr.Equal(testRecord1.IP()) { + t.Fatalf("Unexpected result %s for %s", receivedAddr, testRecord1.Name()) + } + + Debug.Printf("Query: testfail.weave. dns.TypeA") + sendQuery("testfail.weave.", dns.TypeA) + if receivedCount != 0 { + t.Fatalf("Unexpected result count %d for testfail.weave", receivedCount) + } + + Debug.Printf("Query: %s dns.TypePTR", testInAddr1) + sendQuery(testInAddr1, dns.TypePTR) + if receivedCount != 1 { + t.Fatalf("Expected an answer to %s, got %d answers", testInAddr1, receivedCount) + } else if !(testRecord1.Name() == receivedName) { + t.Fatalf("Expected answer %s to query for %s, got %s", testRecord1.Name(), testInAddr1, receivedName) + } +} + +// Check that we can use a use "insistent" queries +func TestClientServerInsistentQuery(t *testing.T) { + InitDefaultLogging(testing.Verbose()) + + testRecord1 := Record{"test.weave.local.", net.ParseIP("10.2.2.1"), 0, 0, 0} + testInAddr1 := "1.2.2.10.in-addr.arpa." + testRecord2 := Record{"test.weave.local.", net.ParseIP("10.2.2.2"), 0, 0, 0} + testInAddr2 := "2.2.2.10.in-addr.arpa." + + mzone1 := newMockedZoneWithRecords([]ZoneRecord{testRecord1}) + mdnsServer1, err := NewMDNSServer(mzone1, true) + wt.AssertNoErr(t, err) + err = mdnsServer1.Start(nil) + wt.AssertNoErr(t, err) + defer mdnsServer1.Stop() + + mzone2 := newMockedZoneWithRecords([]ZoneRecord{testRecord2}) + mdnsServer2, err := NewMDNSServer(mzone2, true) + wt.AssertNoErr(t, err) + err = mdnsServer2.Start(nil) + wt.AssertNoErr(t, err) + defer mdnsServer2.Stop() + + // create a third server with exactly the same info as the second server (so we can test duplicates removals) + mdnsServer3, err := NewMDNSServer(mzone2, true) + wt.AssertNoErr(t, err) + err = mdnsServer3.Start(nil) + wt.AssertNoErr(t, err) + defer mdnsServer3.Stop() + + var receivedAddrs []ZoneRecord + var receivedNames []ZoneRecord + receivedCount := 0 + + mdnsCli, err := NewMDNSClient() + wt.AssertNoErr(t, err) + err = mdnsCli.Start(nil) + wt.AssertNoErr(t, err) + + sendQuery := func(name string, querytype uint16) { + receivedAddrs = nil + receivedNames = nil + receivedCount = 0 + Debug.Printf("Sending query...") + switch querytype { + case dns.TypeA: + receivedAddrs, err = mdnsCli.InsistentLookupName(name) + if err == nil { + receivedCount = len(receivedAddrs) + } + case dns.TypePTR: + receivedNames, err = mdnsCli.InsistentLookupInaddr(name) + if err == nil { + receivedCount = len(receivedNames) + } + } + } + + time.Sleep(100 * time.Millisecond) // Allow for server to get going + + Debug.Printf("Query: %s dns.TypeA", testRecord1.Name()) + sendQuery(testRecord1.Name(), dns.TypeA) + if receivedCount != 2 { + t.Fatalf("Unexpected result count %d for %s", receivedCount, testRecord1.Name()) + } + + Debug.Printf("Query: testfail.weave. dns.TypeA") + sendQuery("testfail.weave.", dns.TypeA) + if receivedCount != 0 { + t.Fatalf("Unexpected result count %d for testfail.weave", receivedCount) + } + + Debug.Printf("Query: %s dns.TypePTR", testInAddr1) + sendQuery(testInAddr1, dns.TypePTR) + if receivedCount != 1 { + t.Fatalf("Expected an answer to %s, got %d answers", testInAddr1, receivedCount) + } + + Debug.Printf("Query: %s dns.TypePTR", testInAddr2) + sendQuery(testInAddr2, dns.TypePTR) + if receivedCount != 1 { + t.Fatalf("Expected an answer to %s, got %d answers", testInAddr2, receivedCount) + } +} diff --git a/nameserver/mocks_test.go b/nameserver/mocks_test.go index 55fabdcb..7cafafd3 100644 --- a/nameserver/mocks_test.go +++ b/nameserver/mocks_test.go @@ -2,6 +2,7 @@ package nameserver import ( "fmt" + "github.com/benbjohnson/clock" "github.com/miekg/dns" . "github.com/weaveworks/weave/common" wt "github.com/weaveworks/weave/testing" @@ -423,6 +424,22 @@ func runLocalTCPServer(laddr string, handler dns.HandlerFunc) (*dns.Server, stri ////////////////////////////////////////////////////////////////// +type mockedClock struct { + *clock.Mock +} + +func newMockedClock() *mockedClock { + return &mockedClock{clock.NewMock()} +} + +func (clk *mockedClock) Forward(secs int) { + Debug.Printf(">>>>>>> Moving clock forward %d seconds - Time traveling >>>>>>>", secs) + clk.Add(time.Duration(secs) * time.Second) + Debug.Printf("<<<<<<< Time travel finished! We are at %s <<<<<<<", clk.Now()) +} + +////////////////////////////////////////////////////////////////// + // Perform a DNS query and assert the reply code, number or answers, etc func assertExchange(t *testing.T, z string, ty uint16, port int, minAnswers int, maxAnswers int, expErr int) (*dns.Msg, *dns.Msg) { wt.AssertNotEqualInt(t, port, 0, "invalid DNS server port") @@ -440,7 +457,7 @@ func assertExchange(t *testing.T, z string, ty uint16, port int, minAnswers int, r, _, err := c.Exchange(m, lstAddr) t.Logf("Response from '%s':\n%+v\n", lstAddr, r) if err != nil { - t.Fatalf("Error when querying DNS server at %s: %s", lstAddr, err) + t.Errorf("Error when querying DNS server at %s: %s", lstAddr, err) } wt.AssertNoErr(t, err) if minAnswers == 0 && maxAnswers == 0 { diff --git a/nameserver/server_cache_test.go b/nameserver/server_cache_test.go index fe920a0e..c06eae1c 100644 --- a/nameserver/server_cache_test.go +++ b/nameserver/server_cache_test.go @@ -2,7 +2,6 @@ package nameserver import ( "fmt" - "github.com/benbjohnson/clock" "github.com/miekg/dns" . "github.com/weaveworks/weave/common" wt "github.com/weaveworks/weave/testing" @@ -22,13 +21,7 @@ func TestServerCacheRefresh(t *testing.T) { InitDefaultLogging(testing.Verbose()) Info.Println("TestServerCacheRefresh starting") - - clk := clock.NewMock() - forwardClock := func(secs int) { - Debug.Printf(">>>>>>> Moving clock forward %d seconds - Time traveling >>>>>>>", secs) - clk.Add(time.Duration(secs) * time.Second) - Debug.Printf("<<<<<<< Time travel finished! We are at %s <<<<<<<", clk.Now()) - } + clk := newMockedClock() Debug.Printf("Creating 2 zone databases") zoneConfig := ZoneConfig{ @@ -71,7 +64,7 @@ func TestServerCacheRefresh(t *testing.T) { assertInCache(t, cache, qName1, "after asking for first name") assertNotLocalInCache(t, cache, qName2, "after asking for second name") - forwardClock(refreshInterval / 2) + clk.Forward(refreshInterval / 2) Debug.Printf("Adding an IP to %s and to %s", testName1, testName2) dbs[1].Zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.2")) @@ -80,7 +73,7 @@ func TestServerCacheRefresh(t *testing.T) { // Zone database #2 at this point: // first.weave.local = 10.2.2.1 10.2.2.2 // second.weave.local = 10.9.9.2 - forwardClock(refreshInterval/2 + 2) + clk.Forward(refreshInterval/2 + 2) // at this point, the testName1 should have been refreshed // so it should have two IPs, and the cache entry should have been invalidated @@ -95,7 +88,7 @@ func TestServerCacheRefresh(t *testing.T) { // delete the IPs, and some time passes by so the cache should be purged... dbs[1].Zone.DeleteRecord(containerID, net.ParseIP("10.2.2.1")) dbs[1].Zone.DeleteRecord(containerID, net.ParseIP("10.2.2.2")) - forwardClock(refreshInterval + 1) + clk.Forward(refreshInterval + 1) qName1, _ = assertExchange(t, testName1, dns.TypeA, testPort, 0, 0, dns.RcodeNameError) qName2, _ = assertExchange(t, testName2, dns.TypeA, testPort, 0, 0, dns.RcodeNameError) diff --git a/nameserver/zone.go b/nameserver/zone.go index 85adae3c..6f22ea79 100644 --- a/nameserver/zone.go +++ b/nameserver/zone.go @@ -122,7 +122,7 @@ func (re *recordEntry) notifyIPObservers() { } } -func (re recordEntry) TTL() int { +func (re *recordEntry) TTL() int { // if we never set the TTL (eg, when using AddRecord()), return the standard value... if re.ttl == 0 { return int(localTTL) @@ -131,11 +131,11 @@ func (re recordEntry) TTL() int { } // Check if the info in this record is still valid according to the TTL -func (re recordEntry) hasExpired(now time.Time) bool { +func (re *recordEntry) hasExpired(now time.Time) bool { return re.ttl > 0 && now.Sub(re.insTime).Seconds() > float64(re.ttl) } -func (re recordEntry) String() string { +func (re *recordEntry) String() string { var buf bytes.Buffer fmt.Fprintf(&buf, "%s", re.Record) lobs := len(re.observers) @@ -163,10 +163,10 @@ func newName(n string) *name { observers: make([]ZoneRecordObserver, 0), } } -func (n name) len() int { return len(n.ipv4) } -func (n name) empty() bool { return n.len() == 0 } -func (n name) hasIP(ip net.IP) bool { return n.hasIPv4(ipToIPv4(ip)) } -func (n name) hasIPv4(ip IPv4) bool { _, found := n.ipv4[ip]; return found } +func (n *name) len() int { return len(n.ipv4) } +func (n *name) empty() bool { return n.len() == 0 } +func (n *name) hasIP(ip net.IP) bool { return n.hasIPv4(ipToIPv4(ip)) } +func (n *name) hasIPv4(ip IPv4) bool { _, found := n.ipv4[ip]; return found } // Get all the entries for this name func (n *name) getAllEntries() (res []*recordEntry) { @@ -257,7 +257,7 @@ func (n *name) notifyNameObservers() { } // Convert a set of IP records to a comma-separated string -func (n name) String() string { +func (n *name) String() string { var buf bytes.Buffer for _, ip := range n.ipv4 { fmt.Fprintf(&buf, "%s, ", ip) @@ -276,10 +276,10 @@ type namesSet struct { names map[string]*name } -func newNamesSet() *namesSet { return &namesSet{names: make(map[string]*name)} } -func (ns namesSet) len() int { return len(ns.names) } -func (ns namesSet) empty() bool { return ns.len() == 0 } -func (ns namesSet) hasName(name string) bool { +func newNamesSet() *namesSet { return &namesSet{names: make(map[string]*name)} } +func (ns *namesSet) len() int { return len(ns.names) } +func (ns *namesSet) empty() bool { return ns.len() == 0 } +func (ns *namesSet) hasName(name string) bool { if n, found := ns.names[name]; !found { return false } else { @@ -365,7 +365,7 @@ func (ns *namesSet) deleteIP(ip IPv4) error { } // Get the name query time -func (ns namesSet) getNameLastAccess(n string) time.Time { +func (ns *namesSet) getNameLastAccess(n string) time.Time { if name, found := ns.names[n]; found { return name.lastAccessTime } @@ -383,7 +383,7 @@ func (ns *namesSet) touchName(name string, now time.Time) { } } -func (ns namesSet) String() string { +func (ns *namesSet) String() string { var buf bytes.Buffer for _, name := range ns.names { fmt.Fprintf(&buf, "%s, ", name) diff --git a/nameserver/zone_lookup.go b/nameserver/zone_lookup.go index 247df6bd..2042caf9 100644 --- a/nameserver/zone_lookup.go +++ b/nameserver/zone_lookup.go @@ -6,6 +6,20 @@ import ( "time" ) +func (zone *zoneDb) lookup(target string, lfun func(ns *namesSet) []*recordEntry) (res []ZoneRecord, err error) { + for identName, nameset := range zone.idents { + if identName != defaultRemoteIdent { + for _, ze := range lfun(nameset) { + res = append(res, ze) + } + } + } + if len(res) == 0 { + err = LookupError(target) + } + return +} + // Perform a lookup for a name in the zone // The name can be resolved locally with the local database func (zone *zoneDb) LookupName(name string) (res []ZoneRecord, err error) { @@ -15,17 +29,50 @@ func (zone *zoneDb) LookupName(name string) (res []ZoneRecord, err error) { // note: LookupName() is usually called from the mDNS server, so we do not touch the name name = dns.Fqdn(name) Debug.Printf("[zonedb] Looking for name '%s' in local database", name) - for identName, nameset := range zone.idents { - if identName != defaultRemoteIdent { - for _, ze := range nameset.getEntriesForName(name) { - res = append(res, ze) - } - } + return zone.lookup(name, func(ns *namesSet) []*recordEntry { return ns.getEntriesForName(name) }) +} + +// Perform a lookup for a IP address in the zone +// The address can be resolved locally with the local database +func (zone *zoneDb) LookupInaddr(inaddr string) (res []ZoneRecord, err error) { + zone.mx.RLock() + defer zone.mx.RUnlock() + + // note: LookupInaddr() is usually called from the mDNS server, so we do not touch the name + revIPv4, err := raddrToIPv4(inaddr) + if err != nil { + return nil, newParseError("lookup address", inaddr) + } + Debug.Printf("[zonedb] Looking for address in local database: '%s' (%s)", revIPv4, inaddr) + return zone.lookup(inaddr, func(ns *namesSet) []*recordEntry { return ns.getEntriesForIP(revIPv4) }) +} + +// Perform a domain lookup with mDNS +func (zone *zoneDb) domainLookup(target string, lfun ZoneLookupFunc) (res []ZoneRecord, err error) { + // no local results have been obtained in the local database: try with a mDNS query + Debug.Printf("[zonedb] '%s' not in local database... trying with mDNS", target) + lanswers, err := lfun(target) + if err != nil { + Debug.Printf("[zonedb] mDNS lookup error for '%s': %s", target, err) + return nil, err } - if len(res) == 0 { - err = LookupError(name) + // if the request has been successful, save the IP in the local database and return the corresponding ZoneRecord + // (we do not get the remote ident in the mDNS reply, so we save it in a "remote" ident) + Debug.Printf("[zonedb] adding '%s' (obtained with mDNS) to '%s'", lanswers, target) + res = make([]ZoneRecord, len(lanswers)) + zone.mx.Lock() + now := zone.clock.Now() + remoteIdent := zone.getNamesSet(defaultRemoteIdent) + for i, answer := range lanswers { + res[i], err = remoteIdent.addIPToName(answer, now) + if err != nil { + zone.mx.Unlock() + Warning.Printf("[zonedb] '%s' insertion for %s failed: %s", answer, target, err) + return nil, err + } } + zone.mx.Unlock() return } @@ -40,7 +87,7 @@ func (zone *zoneDb) DomainLookupName(name string) (res []ZoneRecord, err error) for identName, nameset := range zone.idents { for _, ze := range nameset.getEntriesForName(name) { // filter the entries with expired TTL - // locally introduced entries are nver expired as the always have TTL=0 + // locally introduced entries are never expired: they always have TTL=0 if ze.hasExpired(now) { Debug.Printf("[zonedb] '%s': expired entry '%s' ignored: removing", name, ze) nameset.deleteName(name) @@ -57,69 +104,16 @@ func (zone *zoneDb) DomainLookupName(name string) (res []ZoneRecord, err error) if len(res) > 0 { Debug.Printf("[zonedb] '%s' resolved in local database", name) } else { - // no local results have been obtained in the local database: try with a mDNS query - // (this request is not a background query, so we cannot delay the response) - Debug.Printf("[zonedb] name '%s' not in local database: trying with mDNS", name) - ips, err := zone.mdnsCli.LookupName(name) - if err != nil { - Debug.Printf("[zonedb] mDNS lookup error for '%s': %s", name, err) - return nil, err - } - - // if the request has been successful, save the IP in the local database and return the corresponding ZoneRecord - // (we do not get the remote ident in the mDNS reply, so we save it in a "remote" ident) - Debug.Printf("[zonedb] adding '%s' (obtained with mDNS) to '%s'", ips, name) - res = make([]ZoneRecord, len(ips)) - zone.mx.Lock() - now = zone.clock.Now() - for i, zr := range ips { - res[i], err = zone.getNamesSet(defaultRemoteIdent).addIPToName(zr, now) - if err != nil { - zone.mx.Unlock() - Warning.Printf("[zonedb] IP [%s] insertion for '%s' failed: %s", ips, name, err) - return nil, err - } - } - zone.mx.Unlock() + res, err = zone.domainLookup(name, zone.mdnsCli.LookupName) } if len(res) > 0 { - // start (if we need to) a background refresh for the name - // note: we do not spend time trying to update names that did not return an initial response... zone.startUpdatingName(name) - return res, nil } - return nil, LookupError(name) } -// Perform a lookup for a IP address in the zone -// The address can be resolved locally with the local database -func (zone *zoneDb) LookupInaddr(inaddr string) (res []ZoneRecord, err error) { - zone.mx.RLock() - defer zone.mx.RUnlock() - - // note: LookupInaddr() is usually called from the mDNS server, so we do not touch the name - - revIPv4, err := raddrToIPv4(inaddr) - if err != nil { - return nil, newParseError("lookup address", inaddr) - } - Debug.Printf("[zonedb] Looking for address in local database: '%s' (%s)", revIPv4, inaddr) - for identName, nameset := range zone.idents { - if identName != defaultRemoteIdent { - for _, ze := range nameset.getEntriesForIP(revIPv4) { - res = append(res, ZoneRecord(ze)) - } - } - } - if len(res) == 0 { - err = LookupError(inaddr) - } - return -} - // Perform a lookup for a IP address in the zone // The address can be resolved either with the local database or // with some other resolution method (eg, a mDNS query) @@ -136,7 +130,7 @@ func (zone *zoneDb) DomainLookupInaddr(inaddr string) (res []ZoneRecord, err err for identName, nameset := range zone.idents { for _, ze := range nameset.getEntriesForIP(revIPv4) { // filter the entries with expired TTL - // locally introduced entries are nver expired as the always have TTL=0 + // locally introduced entries are never expired: they always have TTL=0 if ze.hasExpired(now) { Debug.Printf("[zonedb] '%s': expired entry '%s' ignored: removing", revIPv4, ze) nameset.deleteIP(revIPv4) @@ -153,44 +147,20 @@ func (zone *zoneDb) DomainLookupInaddr(inaddr string) (res []ZoneRecord, err err if len(res) > 0 { Debug.Printf("[zonedb] '%s' resolved in local database", inaddr) } else { - // no local results have been obtained in the local database: try with a mDNS query - Debug.Printf("[zonedb] '%s'(%+v) not in local database... trying with mDNS", inaddr, revIPv4) - names, err := zone.mdnsCli.LookupInaddr(inaddr) - if err != nil { - Debug.Printf("[zonedb] mDNS lookup error for '%s': %s", inaddr, err) - return nil, err - } - - // if the request has been successful, save the IP in the local database and return the corresponding ZoneRecord - // (we do not get the remote ident in the mDNS reply, so we save it in a "remote" ident) - Debug.Printf("[zonedb] adding '%s' (obtained with mDNS) to '%s'", names, revIPv4) - res = make([]ZoneRecord, len(names)) - zone.mx.Lock() - now = zone.clock.Now() - for i, name := range names { - res[i], err = zone.getNamesSet(defaultRemoteIdent).addIPToName(name, now) - if err != nil { - zone.mx.Unlock() - Warning.Printf("[zonedb] Name '%s' insertion for %s failed: %s", name.Name(), revIPv4, err) - return nil, err - } - } - zone.mx.Unlock() + res, err = zone.domainLookup(inaddr, zone.mdnsCli.LookupInaddr) } if len(res) > 0 { // note: even for reverse addresses, we perform the background updates in the name, not in the IP // this simplifies the process and produces basically the same results... // note: we do not spend time trying to update names that did not return an initial response... - for _, name := range res { - zone.startUpdatingName(name.Name()) + for _, r := range res { + zone.startUpdatingName(r.Name()) } return res, nil } - return nil, LookupError(inaddr) - } ////////////////////////////////////////////////////////////////////////////// diff --git a/nameserver/zone_lookup_test.go b/nameserver/zone_lookup_test.go index 8593086b..6c09ab6b 100644 --- a/nameserver/zone_lookup_test.go +++ b/nameserver/zone_lookup_test.go @@ -1,7 +1,6 @@ package nameserver import ( - "github.com/benbjohnson/clock" . "github.com/weaveworks/weave/common" wt "github.com/weaveworks/weave/testing" "net" @@ -26,12 +25,7 @@ func TestZoneRefresh(t *testing.T) { InitDefaultLogging(testing.Verbose()) Info.Println("TestZoneRefresh starting") - clk := clock.NewMock() - forwardClock := func(secs int) { - Debug.Printf(">>>>>>> Moving clock forward %d seconds - Time traveling >>>>>>>", secs) - clk.Add(time.Duration(secs) * time.Second) - Debug.Printf("<<<<<<< Time travel finished! We are at %s <<<<<<<", clk.Now()) - } + clk := newMockedClock() // Create multiple zone databases, linked through mocked mDNS connections zc := ZoneConfig{ @@ -60,14 +54,14 @@ func TestZoneRefresh(t *testing.T) { t.Logf("Db #1 after the lookup:\n%s", dbs[0].Zone) wt.AssertEqualInt(t, len(res), 1, "lookup result") - forwardClock(refreshInterval / 2) + clk.Forward(refreshInterval / 2) Debug.Printf("A couple of seconds later, we should still have one IP for that name") res, err = dbs[0].Zone.DomainLookupName(name) wt.AssertNoErr(t, err) wt.AssertEqualInt(t, len(res), 1, "lookup result") Debug.Printf("And then we add 2 IPs for that name at ZoneDb 2") - forwardClock(1) + clk.Forward(1) Debug.Printf("Adding 2 IPs to '%s' in Db #2", name) dbs[1].Zone.AddRecord("someident", name, net.ParseIP(addr2)) dbs[1].Zone.AddRecord("someident", name, net.ParseIP(addr3)) @@ -76,7 +70,7 @@ func TestZoneRefresh(t *testing.T) { dbs[0].Zone.DomainLookupName(name) Debug.Printf("Wait for a while, until a refresh is performed...") - forwardClock(refreshInterval + 1) + clk.Forward(refreshInterval + 1) Debug.Printf("A refresh should have been scheduled now: we should have 3 IPs:") Debug.Printf("the first (local) IP and the others obtained from zone2 with a mDNS query") @@ -87,7 +81,7 @@ func TestZoneRefresh(t *testing.T) { wt.AssertEqualInt(t, len(res), 3, "lookup result length") Debug.Printf("We will not ask for `name` for a while, so it will become irrelevant and will be removed...") - forwardClock(relevantTime + refreshInterval + 1) + clk.Forward(relevantTime + refreshInterval + 1) // the name should be irrelevant now, and all remote info should have been // removed from the zone database @@ -97,17 +91,17 @@ func TestZoneRefresh(t *testing.T) { wt.AssertFalse(t, dbs[0].Zone.HasNameRemoteInfo(name), "remote name info") Debug.Printf("There is no remote info about this name at zone 1: a new IP appears remotely meanwhile...") - forwardClock(1) + clk.Forward(1) Debug.Printf("Adding '%s' to Db #2", name) dbs[1].Zone.AddRecord("someident", name, net.ParseIP(addr4)) Debug.Printf("When we ask about this name again, we get 4 IPs (1 local, 3 remote)") - forwardClock(1) + clk.Forward(1) Debug.Printf("Asking for '%s' again... the first lookup will return only the local results", name) res, err = dbs[0].Zone.DomainLookupName(name) wt.AssertEqualInt(t, len(res), 1, "lookup result length") Debug.Printf("... but a second lookup should return all the results in the network") - forwardClock(1) + clk.Forward(1) res, err = dbs[0].Zone.DomainLookupName(name) Debug.Printf("Got: %s", res) wt.AssertEqualInt(t, len(res), 4, "lookup result length") // TODO: this fails 1% of the runs... !!??