github.com/elfadel/cilium@v1.6.12/pkg/fqdn/cache_test.go (about)

     1  // Copyright 2018 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // +build !privileged_tests
    16  
    17  package fqdn
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"math/rand"
    23  	"net"
    24  	"regexp"
    25  	"sort"
    26  	"time"
    27  
    28  	"github.com/cilium/cilium/pkg/checker"
    29  	. "gopkg.in/check.v1"
    30  )
    31  
    32  type DNSCacheTestSuite struct{}
    33  
    34  var _ = Suite(&DNSCacheTestSuite{})
    35  
    36  // TestUpdateLookup tests that we can insert DNS data and retrieve it. We
    37  // iterate through time, ensuring that data is expired as appropriate. We also
    38  // insert redundant DNS entries that should not change the output.
    39  func (ds *DNSCacheTestSuite) TestUpdateLookup(c *C) {
    40  	name := "test.com"
    41  	now := time.Now()
    42  	cache := NewDNSCache(0)
    43  	endTimeSeconds := 4
    44  
    45  	// Add 1 new entry "per second", and one with a redundant IP (with ttl/2).
    46  	// The IP reflects the second in which it will expire, and should show up for
    47  	// all now+ttl that is less than it.
    48  	for i := 1; i <= endTimeSeconds; i++ {
    49  		ttl := i
    50  		cache.Update(now,
    51  			name,
    52  			[]net.IP{net.ParseIP(fmt.Sprintf("1.1.1.%d", i)), net.ParseIP(fmt.Sprintf("2.2.2.%d", i))},
    53  			ttl)
    54  
    55  		cache.Update(now,
    56  			name,
    57  			[]net.IP{net.ParseIP(fmt.Sprintf("1.1.1.%d", i))},
    58  			ttl/2)
    59  	}
    60  
    61  	// lookup our entries
    62  	//  - no redundant entries (the 1.1.1.x is not repeated)
    63  	//  - with each step of secondsPastNow, fewer entries are returned
    64  	for secondsPastNow := 1; secondsPastNow <= endTimeSeconds; secondsPastNow++ {
    65  		ips := cache.lookupByTime(now.Add(time.Duration(secondsPastNow)*time.Second), name)
    66  		c.Assert(len(ips), Equals, 2*(endTimeSeconds-secondsPastNow+1), Commentf("Incorrect number of IPs returned"))
    67  
    68  		// Check that we returned each 1.1.1.x entry where x={1..endTimeSeconds}
    69  		// These are sorted, and are in the first half of the array
    70  		// Similarly, check the 2.2.2.x entries in the second half of the array
    71  		j := secondsPastNow
    72  		halfIndex := endTimeSeconds - secondsPastNow + 1
    73  		for _, ip := range ips[:halfIndex] {
    74  			c.Assert(ip.String(), Equals, fmt.Sprintf("1.1.1.%d", j), Commentf("Incorrect IP returned (j=%d, secondsPastNow=%d)", j, secondsPastNow))
    75  			j++
    76  		}
    77  		j = secondsPastNow
    78  		for _, ip := range ips[halfIndex:] {
    79  			c.Assert(ip.String(), Equals, fmt.Sprintf("2.2.2.%d", j), Commentf("Incorrect IP returned (j=%d, secondsPastNow=%d)", j, secondsPastNow))
    80  			j++
    81  		}
    82  	}
    83  }
    84  
    85  // TestDelete tests that we can forcibly clear parts of the cache.
    86  func (ds *DNSCacheTestSuite) TestDelete(c *C) {
    87  	names := map[string]net.IP{
    88  		"test1.com": net.ParseIP("2.2.2.1"),
    89  		"test2.com": net.ParseIP("2.2.2.2"),
    90  		"test3.com": net.ParseIP("2.2.2.3")}
    91  	sharedIP := net.ParseIP("1.1.1.1")
    92  	now := time.Now()
    93  	cache := NewDNSCache(0)
    94  
    95  	// Insert 3 records with 1 shared IP and 3 with different IPs
    96  	cache.Update(now, "test1.com", []net.IP{sharedIP, names["test1.com"]}, 5)
    97  	cache.Update(now, "test2.com", []net.IP{sharedIP, names["test2.com"]}, 5)
    98  	cache.Update(now, "test3.com", []net.IP{sharedIP, names["test3.com"]}, 5)
    99  
   100  	now = now.Add(time.Second)
   101  
   102  	// Test that a non-matching ForceExpire doesn't do anything. All data should
   103  	// still be present.
   104  	nameMatch, err := regexp.Compile("^notatest.com$")
   105  	c.Assert(err, IsNil)
   106  	namesAffected := cache.ForceExpire(now, nameMatch)
   107  	c.Assert(len(namesAffected), Equals, 0, Commentf("Incorrect count of names removed %v", namesAffected))
   108  	for _, name := range []string{"test1.com", "test2.com", "test3.com"} {
   109  		ips := cache.lookupByTime(now, name)
   110  		c.Assert(len(ips), Equals, 2, Commentf("Wrong count of IPs returned (%v) for non-deleted name '%s'", ips, name))
   111  	}
   112  
   113  	// Delete a single name and check that
   114  	// - It is returned in namesAffected
   115  	// - Lookups for it show no data, but data remains for other names
   116  	nameMatch, err = regexp.Compile("^test1.com$")
   117  	c.Assert(err, IsNil)
   118  	namesAffected = cache.ForceExpire(now, nameMatch)
   119  	c.Assert(len(namesAffected), Equals, 1, Commentf("Incorrect count of names removed %v", namesAffected))
   120  	c.Assert(namesAffected[0], Equals, "test1.com", Commentf("Incorrect affected name returned on forced expire: %s", namesAffected))
   121  	ips := cache.lookupByTime(now, "test1.com")
   122  	c.Assert(len(ips), Equals, 0, Commentf("IPs returned (%v) for deleted name 'test1.com'", ips))
   123  	for _, name := range []string{"test2.com", "test3.com"} {
   124  		ips = cache.lookupByTime(now, name)
   125  		c.Assert(len(ips), Equals, 2, Commentf("Wrong count of IPs returned (%v) for non-deleted name '%s'", ips, name))
   126  	}
   127  
   128  	// Delete the whole cache. This should leave no data.
   129  	namesAffected = cache.ForceExpire(now, nil)
   130  	sort.Strings(namesAffected) // simplify the checks below
   131  	c.Assert(len(namesAffected), Equals, 2, Commentf("Incorrect count of names removed %v", namesAffected))
   132  	for i, name := range []string{"test2.com", "test3.com"} {
   133  		c.Assert(namesAffected[i], Equals, name, Commentf("Incorrect affected name returned on forced expire"))
   134  	}
   135  	for name := range names {
   136  		ips = cache.lookupByTime(now, name)
   137  		c.Assert(len(ips), Equals, 0, Commentf("Returned IP data for %s after the cache was fully cleared: %v", name, ips))
   138  	}
   139  	dump := cache.Dump()
   140  	c.Assert(len(dump), Equals, 0, Commentf("Returned cache entries from cache dump after the cache was fully cleared: %v", dump))
   141  }
   142  
   143  func (ds *DNSCacheTestSuite) TestForceExpiredByNames(c *C) {
   144  	names := []string{"test1.com", "test2.com"}
   145  	cache := NewDNSCache(0)
   146  	for i := 1; i < 4; i++ {
   147  		cache.Update(
   148  			now,
   149  			fmt.Sprintf("test%d.com", i),
   150  			[]net.IP{net.ParseIP(fmt.Sprintf("1.1.1.%d", i))},
   151  			5)
   152  	}
   153  
   154  	c.Assert(cache.forward, HasLen, 3)
   155  	result := cache.ForceExpireByNames(time.Now(), names)
   156  	c.Assert(result, checker.DeepEquals, names)
   157  	c.Assert(result, HasLen, 2)
   158  	c.Assert(cache.forward["test3.com"], Not(IsNil))
   159  
   160  	invalidName := cache.ForceExpireByNames(now, []string{"invalid.name"})
   161  	c.Assert(invalidName, HasLen, 0)
   162  }
   163  
   164  func (ds *DNSCacheTestSuite) TestReverseUpdateLookup(c *C) {
   165  	names := map[string]net.IP{
   166  		"test1.com": net.ParseIP("2.2.2.1"),
   167  		"test2.com": net.ParseIP("2.2.2.2"),
   168  		"test3.com": net.ParseIP("2.2.2.3")}
   169  	sharedIP := net.ParseIP("1.1.1.1")
   170  	now := time.Now()
   171  	cache := NewDNSCache(0)
   172  
   173  	// insert 2 records, with 1 shared IP
   174  	cache.Update(now, "test1.com", []net.IP{sharedIP, names["test1.com"]}, 2)
   175  	cache.Update(now, "test2.com", []net.IP{sharedIP, names["test2.com"]}, 4)
   176  
   177  	// lookup within the TTL for both names should return 2 names for sharedIPs,
   178  	// and one name for the 2.2.2.* IPs
   179  	currentTime := now.Add(time.Second)
   180  	lookupNames := cache.lookupIPByTime(currentTime, sharedIP)
   181  	c.Assert(len(lookupNames), Equals, 2, Commentf("Incorrect number of names returned"))
   182  	for _, name := range lookupNames {
   183  		_, found := names[name]
   184  		c.Assert(found, Equals, true, Commentf("Returned a DNS name that doesn't match IP"))
   185  	}
   186  
   187  	lookupNames = cache.lookupIPByTime(currentTime, names["test1.com"])
   188  	c.Assert(len(lookupNames), Equals, 1, Commentf("Incorrect number of names returned"))
   189  	c.Assert(lookupNames[0], Equals, "test1.com", Commentf("Returned a DNS name that doesn't match IP"))
   190  
   191  	lookupNames = cache.lookupIPByTime(currentTime, names["test2.com"])
   192  	c.Assert(len(lookupNames), Equals, 1, Commentf("Incorrect number of names returned"))
   193  	c.Assert(lookupNames[0], Equals, "test2.com", Commentf("Returned a DNS name that doesn't match IP"))
   194  
   195  	lookupNames = cache.lookupIPByTime(currentTime, names["test3.com"])
   196  	c.Assert(len(lookupNames), Equals, 0, Commentf("Returned names for IP not in cache"))
   197  
   198  	// lookup between 2-4 seconds later (test1.com has expired) for both names
   199  	// should return 2 names for sharedIPs, and one name for the 2.2.2.* IPs
   200  	currentTime = now.Add(3 * time.Second)
   201  	lookupNames = cache.lookupIPByTime(currentTime, sharedIP)
   202  	c.Assert(len(lookupNames), Equals, 1, Commentf("Incorrect number of names returned"))
   203  	c.Assert(lookupNames[0], Equals, "test2.com", Commentf("Returned a DNS name that doesn't match IP"))
   204  
   205  	lookupNames = cache.lookupIPByTime(currentTime, names["test1.com"])
   206  	c.Assert(len(lookupNames), Equals, 0, Commentf("Incorrect number of names returned"))
   207  
   208  	lookupNames = cache.lookupIPByTime(currentTime, names["test2.com"])
   209  	c.Assert(len(lookupNames), Equals, 1, Commentf("Incorrect number of names returned"))
   210  	c.Assert(lookupNames[0], Equals, "test2.com", Commentf("Returned a DNS name that doesn't match IP"))
   211  
   212  	lookupNames = cache.lookupIPByTime(currentTime, names["test3.com"])
   213  	c.Assert(len(lookupNames), Equals, 0, Commentf("Returned names for IP not in cache"))
   214  
   215  	// lookup between after 4 seconds later (all have expired) for both names
   216  	// should return no names in all cases.
   217  	currentTime = now.Add(5 * time.Second)
   218  	lookupNames = cache.lookupIPByTime(currentTime, sharedIP)
   219  	c.Assert(len(lookupNames), Equals, 0, Commentf("Incorrect number of names returned"))
   220  
   221  	lookupNames = cache.lookupIPByTime(currentTime, names["test1.com"])
   222  	c.Assert(len(lookupNames), Equals, 0, Commentf("Incorrect number of names returned"))
   223  
   224  	lookupNames = cache.lookupIPByTime(currentTime, names["test2.com"])
   225  	c.Assert(len(lookupNames), Equals, 0, Commentf("Incorrect number of names returned"))
   226  
   227  	lookupNames = cache.lookupIPByTime(currentTime, names["test3.com"])
   228  	c.Assert(len(lookupNames), Equals, 0, Commentf("Returned names for IP not in cache"))
   229  }
   230  
   231  func (ds *DNSCacheTestSuite) TestJSONMarshal(c *C) {
   232  	names := map[string]net.IP{
   233  		"test1.com": net.ParseIP("2.2.2.1"),
   234  		"test2.com": net.ParseIP("2.2.2.2"),
   235  		"test3.com": net.ParseIP("2.2.2.3")}
   236  	sharedIP := net.ParseIP("1.1.1.1")
   237  	now := time.Now()
   238  	cache := NewDNSCache(0)
   239  
   240  	// insert 3 records with 1 shared IP and 3 with different IPs
   241  	cache.Update(now, "test1.com", []net.IP{sharedIP}, 5)
   242  	cache.Update(now, "test2.com", []net.IP{sharedIP}, 5)
   243  	cache.Update(now, "test3.com", []net.IP{sharedIP}, 5)
   244  	cache.Update(now, "test1.com", []net.IP{names["test1.com"]}, 5)
   245  	cache.Update(now, "test2.com", []net.IP{names["test2.com"]}, 5)
   246  	cache.Update(now, "test3.com", []net.IP{names["test3.com"]}, 5)
   247  
   248  	// Marshal and unmarshal
   249  	data, err := cache.MarshalJSON()
   250  	c.Assert(err, IsNil)
   251  
   252  	newCache := NewDNSCache(0)
   253  	err = newCache.UnmarshalJSON(data)
   254  	c.Assert(err, IsNil)
   255  
   256  	// Marshalled data should have no duplicate entries Note: this is tightly
   257  	// coupled with the implementation of DNSCache.MarshalJSON because the
   258  	// unmarshalled instance will hide duplicates. We simply check the length
   259  	// since we control the inserted data, and we test its correctness below.
   260  	rawList := make([]*cacheEntry, 0)
   261  	err = json.Unmarshal(data, &rawList)
   262  	c.Assert(err, IsNil)
   263  	c.Assert(len(rawList), Equals, 6)
   264  
   265  	// Check that the unmarshalled instance contains all the data at now
   266  	currentTime := now
   267  	for name := range names {
   268  		IPs := cache.lookupByTime(currentTime, name)
   269  		c.Assert(len(IPs), Equals, 2, Commentf("Incorrect number of IPs returned for %s", name))
   270  		c.Assert(IPs[0].String(), Equals, sharedIP.String(), Commentf("Returned an IP that doesn't match %s", name))
   271  		c.Assert(IPs[1].String(), Equals, names[name].String(), Commentf("Returned an IP name that doesn't match %s", name))
   272  	}
   273  
   274  	// Check that the unmarshalled data expires correctly
   275  	currentTime = now.Add(10 * time.Second)
   276  	for name := range names {
   277  		IPs := cache.lookupByTime(currentTime, name)
   278  		c.Assert(len(IPs), Equals, 0, Commentf("Returned IPs that should be expired for %s", name))
   279  	}
   280  }
   281  
   282  /* Benchmarks
   283   * These are here to help gauge the relative costs of operations in DNSCache.
   284   * Note: some are on arrays `size` elements, so the benchmark "op time" is too
   285   * large.
   286   */
   287  
   288  var (
   289  	now         = time.Now()
   290  	size        = uint32(1000) // size of array to operate on
   291  	entriesOrig = makeEntries(now, 1+size/3, 1+size/3, 1+size/3)
   292  	ipsOrig     = makeIPs(size)
   293  )
   294  
   295  // makeIPs generates count sequential IPv4 IPs
   296  func makeIPs(count uint32) []net.IP {
   297  	ips := make([]net.IP, 0, count)
   298  	for i := uint32(0); i < count; i++ {
   299  		ips = append(ips, net.IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i>>0)))
   300  	}
   301  	return ips
   302  }
   303  
   304  func makeEntries(now time.Time, live, redundant, expired uint32) (entries []*cacheEntry) {
   305  	liveTTL := 120
   306  	redundantTTL := 60
   307  
   308  	for ; live > 0; live-- {
   309  		ip := net.IPv4(byte(live>>24), byte(live>>16), byte(live>>8), byte(live>>0))
   310  
   311  		entries = append(entries, &cacheEntry{
   312  			Name:           fmt.Sprintf("live-%s", ip.String()),
   313  			LookupTime:     now,
   314  			ExpirationTime: now.Add(time.Duration(liveTTL) * time.Second),
   315  			TTL:            liveTTL,
   316  			IPs:            []net.IP{ip}})
   317  
   318  		if redundant > 0 {
   319  			redundant--
   320  			entries = append(entries, &cacheEntry{
   321  				Name:           fmt.Sprintf("redundant-%s", ip.String()),
   322  				LookupTime:     now,
   323  				ExpirationTime: now.Add(time.Duration(redundantTTL) * time.Second),
   324  				TTL:            redundantTTL,
   325  				IPs:            []net.IP{ip}})
   326  		}
   327  
   328  		if expired > 0 {
   329  			expired--
   330  			entries = append(entries, &cacheEntry{
   331  				Name:           fmt.Sprintf("expired-%s", ip.String()),
   332  				LookupTime:     now.Add(-time.Duration(liveTTL) * time.Second),
   333  				ExpirationTime: now.Add(-time.Second),
   334  				TTL:            liveTTL,
   335  				IPs:            []net.IP{ip}})
   336  		}
   337  	}
   338  
   339  	rand.Shuffle(len(entries), func(i, j int) {
   340  		entries[i], entries[j] = entries[j], entries[i]
   341  	})
   342  
   343  	return entries
   344  }
   345  
   346  // Note: each "op" works on size things
   347  func (ds *DNSCacheTestSuite) BenchmarkGetIPs(c *C) {
   348  	c.StopTimer()
   349  	now := time.Now()
   350  	cache := NewDNSCache(0)
   351  	cache.Update(now, "test.com", []net.IP{net.ParseIP("1.2.3.4")}, 60)
   352  	entries := cache.forward["test.com"]
   353  	for _, entry := range entriesOrig {
   354  		cache.updateWithEntryIPs(entries, entry)
   355  	}
   356  	c.StartTimer()
   357  
   358  	for i := 0; i < c.N; i++ {
   359  		entries.getIPs(now)
   360  	}
   361  }
   362  
   363  // Note: each "op" works on size things
   364  func (ds *DNSCacheTestSuite) BenchmarkUpdateIPs(c *C) {
   365  	for i := 0; i < c.N; i++ {
   366  		c.StopTimer()
   367  		now := time.Now()
   368  		cache := NewDNSCache(0)
   369  		cache.Update(now, "test.com", []net.IP{net.ParseIP("1.2.3.4")}, 60)
   370  		entries := cache.forward["test.com"]
   371  		c.StartTimer()
   372  
   373  		for _, entry := range entriesOrig {
   374  			cache.updateWithEntryIPs(entries, entry)
   375  			cache.removeExpired(entries, now, time.Time{})
   376  		}
   377  	}
   378  }
   379  
   380  func (ds *DNSCacheTestSuite) BenchmarkIPString(c *C) {
   381  	for i := 0; i < c.N; i++ {
   382  		_ = net.IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i>>0)).String()
   383  	}
   384  }
   385  
   386  func (ds *DNSCacheTestSuite) BenchmarkParseIPSimple(c *C) {
   387  	ip := ipsOrig[0].String()
   388  	for i := 0; i < c.N; i++ {
   389  		_ = net.ParseIP(ip)
   390  	}
   391  }
   392  
   393  // Note: each "op" works on size things
   394  func (ds *DNSCacheTestSuite) BenchmarkParseIP(c *C) {
   395  	c.StopTimer()
   396  	ips := make([]string, 0, len(ipsOrig))
   397  	for _, ip := range ipsOrig {
   398  		ips = append(ips, ip.String())
   399  	}
   400  	c.StartTimer()
   401  
   402  	for i := 0; i < c.N; i++ {
   403  		for _, ipStr := range ips {
   404  			_ = net.ParseIP(ipStr)
   405  		}
   406  	}
   407  }
   408  
   409  // JSON Marshal/Unmarshal benchmarks
   410  var numIPsPerEntry = 10 // number of IPs to generate in each entry
   411  
   412  func (ds *DNSCacheTestSuite) BenchmarkMarshalJSON10(c *C)    { benchmarkMarshalJSON(c, 10) }
   413  func (ds *DNSCacheTestSuite) BenchmarkMarshalJSON100(c *C)   { benchmarkMarshalJSON(c, 100) }
   414  func (ds *DNSCacheTestSuite) BenchmarkMarshalJSON1000(c *C)  { benchmarkMarshalJSON(c, 1000) }
   415  func (ds *DNSCacheTestSuite) BenchmarkMarshalJSON10000(c *C) { benchmarkMarshalJSON(c, 10000) }
   416  
   417  func (ds *DNSCacheTestSuite) BenchmarkUnmarshalJSON10(c *C)    { benchmarkUnmarshalJSON(c, 10) }
   418  func (ds *DNSCacheTestSuite) BenchmarkUnmarshalJSON100(c *C)   { benchmarkUnmarshalJSON(c, 100) }
   419  func (ds *DNSCacheTestSuite) BenchmarkUnmarshalJSON1000(c *C)  { benchmarkUnmarshalJSON(c, 1000) }
   420  func (ds *DNSCacheTestSuite) BenchmarkUnmarshalJSON10000(c *C) { benchmarkUnmarshalJSON(c, 10000) }
   421  
   422  // BenchmarkMarshalJSON100Repeat2 tests whether repeating the whole
   423  // serialization is notably slower than a single run.
   424  func (ds *DNSCacheTestSuite) BenchmarkMarshalJSON100Repeat2(c *C) {
   425  	benchmarkMarshalJSON(c, 50)
   426  	benchmarkMarshalJSON(c, 50)
   427  }
   428  
   429  func (ds *DNSCacheTestSuite) BenchmarkMarshalJSON1000Repeat2(c *C) {
   430  	benchmarkMarshalJSON(c, 500)
   431  	benchmarkMarshalJSON(c, 500)
   432  }
   433  
   434  // benchmarkMarshalJSON benchmarks the cost of creating a json representation
   435  // of DNSCache. Each benchmark "op" is on numDNSEntries.
   436  // Note: It assumes the JSON only uses data in DNSCache.forward when generating
   437  // the data. Changes to the implementation need to also change this benchmark.
   438  func benchmarkMarshalJSON(c *C, numDNSEntries int) {
   439  	c.StopTimer()
   440  	ips := makeIPs(uint32(numIPsPerEntry))
   441  
   442  	cache := NewDNSCache(0)
   443  	for i := 0; i < numDNSEntries; i++ {
   444  		// TTL needs to be far enough in the future that the entry is serialized
   445  		cache.Update(time.Now(), fmt.Sprintf("domain-%v.com", i), ips, 86400)
   446  	}
   447  	c.StartTimer()
   448  
   449  	for i := 0; i < c.N; i++ {
   450  		_, err := cache.MarshalJSON()
   451  		c.Assert(err, IsNil)
   452  	}
   453  }
   454  
   455  // benchmarkUnmarshalJSON benchmarks the cost of parsing a json representation
   456  // of DNSCache. Each benchmark "op" is on numDNSEntries.
   457  // Note: It assumes the JSON only uses data in DNSCache.forward when generating
   458  // the data. Changes to the implementation need to also change this benchmark.
   459  func benchmarkUnmarshalJSON(c *C, numDNSEntries int) {
   460  	c.StopTimer()
   461  	ips := makeIPs(uint32(numIPsPerEntry))
   462  
   463  	cache := NewDNSCache(0)
   464  	for i := 0; i < numDNSEntries; i++ {
   465  		// TTL needs to be far enough in the future that the entry is serialized
   466  		cache.Update(time.Now(), fmt.Sprintf("domain-%v.com", i), ips, 86400)
   467  	}
   468  
   469  	data, err := cache.MarshalJSON()
   470  	c.Assert(err, IsNil)
   471  
   472  	emptyCaches := make([]*DNSCache, c.N)
   473  	for i := 0; i < c.N; i++ {
   474  		emptyCaches[i] = NewDNSCache(0)
   475  	}
   476  	c.StartTimer()
   477  
   478  	for i := 0; i < c.N; i++ {
   479  		err := emptyCaches[i].UnmarshalJSON(data)
   480  		c.Assert(err, IsNil)
   481  	}
   482  }
   483  
   484  func (ds *DNSCacheTestSuite) TestTTLInsertWithMinValue(c *C) {
   485  	now := time.Now()
   486  	cache := NewDNSCache(60)
   487  	cache.Update(now, "test.com", []net.IP{net.ParseIP("1.2.3.4")}, 3)
   488  
   489  	// Checking just now to validate that is inserted correctly
   490  	res := cache.lookupByTime(now, "test.com")
   491  	c.Assert(res, HasLen, 1)
   492  	c.Assert(res[0].String(), Equals, "1.2.3.4")
   493  
   494  	// Checking the latest match
   495  	res = cache.lookupByTime(now.Add(time.Second*3), "test.com")
   496  	c.Assert(res, HasLen, 1)
   497  	c.Assert(res[0].String(), Equals, "1.2.3.4")
   498  
   499  	// Validate that in future time the value is correct
   500  	future := time.Now().Add(time.Second * 70)
   501  	res = cache.lookupByTime(future, "test.com")
   502  	c.Assert(res, HasLen, 0)
   503  }
   504  
   505  func (ds *DNSCacheTestSuite) TestTTLInsertWithZeroValue(c *C) {
   506  	now := time.Now()
   507  	cache := NewDNSCache(0)
   508  	cache.Update(now, "test.com", []net.IP{net.ParseIP("1.2.3.4")}, 10)
   509  
   510  	// Checking just now to validate that is inserted correctly
   511  	res := cache.lookupByTime(now, "test.com")
   512  	c.Assert(res, HasLen, 1)
   513  	c.Assert(res[0].String(), Equals, "1.2.3.4")
   514  
   515  	// Checking the latest match
   516  	res = cache.lookupByTime(now.Add(time.Second*10), "test.com")
   517  	c.Assert(res, HasLen, 1)
   518  	c.Assert(res[0].String(), Equals, "1.2.3.4")
   519  
   520  	// Checking that expires correctly
   521  	future := now.Add(time.Second * 11)
   522  	res = cache.lookupByTime(future, "test.com")
   523  	c.Assert(res, HasLen, 0)
   524  }
   525  
   526  func (ds *DNSCacheTestSuite) TestTTLCleanupEntries(c *C) {
   527  	cache := NewDNSCache(0)
   528  	cache.Update(now, "test.com", []net.IP{net.ParseIP("1.2.3.4")}, 3)
   529  	c.Assert(len(cache.cleanup), Equals, 1)
   530  	entries, _ := cache.cleanupExpiredEntries(time.Now().Add(5 * time.Second))
   531  	c.Assert(entries, HasLen, 1)
   532  	c.Assert(cache.cleanup, HasLen, 0)
   533  	c.Assert(cache.Lookup("test.com"), HasLen, 0)
   534  }
   535  
   536  func (ds *DNSCacheTestSuite) TestTTLCleanupWithoutForward(c *C) {
   537  	cache := NewDNSCache(0)
   538  	now := time.Now()
   539  	cache.cleanup[now.Unix()] = []string{"test.com"}
   540  	// To make sure that all entries are validated correctly
   541  	cache.lastCleanup = time.Now().Add(-1 * time.Minute)
   542  	entries, _ := cache.cleanupExpiredEntries(time.Now().Add(5 * time.Second))
   543  	c.Assert(entries, HasLen, 0)
   544  	c.Assert(cache.cleanup, HasLen, 0)
   545  }
   546  
   547  func (ds *DNSCacheTestSuite) TestOverlimitEntriesWithValidLimit(c *C) {
   548  	limit := 5
   549  	cache := NewDNSCacheWithLimit(0, limit)
   550  
   551  	cache.Update(now, "foo.bar", []net.IP{net.ParseIP("1.1.1.1")}, 1)
   552  	cache.Update(now, "bar.foo", []net.IP{net.ParseIP("2.1.1.1")}, 1)
   553  	for i := 1; i < limit+2; i++ {
   554  		cache.Update(now, "test.com", []net.IP{net.ParseIP(fmt.Sprintf("1.1.1.%d", i))}, i)
   555  	}
   556  	c.Assert(cache.cleanupOverLimitEntries(), checker.DeepEquals, []string{"test.com"})
   557  	c.Assert(cache.Lookup("test.com"), HasLen, limit)
   558  	c.Assert(cache.LookupIP(net.ParseIP("1.1.1.1")), checker.DeepEquals, []string{"foo.bar"})
   559  	c.Assert(cache.forward["test.com"]["1.1.1.1"], IsNil)
   560  	c.Assert(cache.Lookup("foo.bar"), HasLen, 1)
   561  	c.Assert(cache.Lookup("bar.foo"), HasLen, 1)
   562  	c.Assert(cache.overLimit, HasLen, 0)
   563  }
   564  
   565  func (ds *DNSCacheTestSuite) TestOverlimitEntriesWithoutLimit(c *C) {
   566  	limit := 0
   567  	cache := NewDNSCacheWithLimit(0, limit)
   568  	for i := 0; i < 5; i++ {
   569  		cache.Update(now, "test.com", []net.IP{net.ParseIP(fmt.Sprintf("1.1.1.%d", i))}, i)
   570  	}
   571  	c.Assert(cache.cleanupOverLimitEntries(), checker.DeepEquals, []string{})
   572  	c.Assert(cache.Lookup("test.com"), HasLen, 4)
   573  }
   574  
   575  func (ds *DNSCacheTestSuite) TestGCOverlimitAfterTTLCleanup(c *C) {
   576  	limit := 5
   577  	cache := NewDNSCacheWithLimit(0, limit)
   578  
   579  	// Make sure that the cleanup takes all the changes from 1 minute ago.
   580  	cache.lastCleanup = time.Now().Add(-1 * time.Minute)
   581  	for i := 1; i < limit+2; i++ {
   582  		cache.Update(now, "test.com", []net.IP{net.ParseIP(fmt.Sprintf("1.1.1.%d", i))}, 1)
   583  	}
   584  
   585  	c.Assert(cache.Lookup("test.com"), HasLen, limit+1)
   586  	c.Assert(cache.overLimit, HasLen, 1)
   587  
   588  	result, _ := cache.cleanupExpiredEntries(time.Now().Add(5 * time.Second))
   589  	c.Assert(result, checker.DeepEquals, []string{"test.com"})
   590  
   591  	// Due all entries are deleted on TTL, the overlimit should return 0 entries.
   592  	c.Assert(cache.cleanupOverLimitEntries(), checker.DeepEquals, []string{})
   593  }
   594  
   595  func (ds *DNSCacheTestSuite) TestOverlimitAfterDeleteForwardEntry(c *C) {
   596  	// Validate if something delete the forward entry no invalid key access on
   597  	// CG operation
   598  	dnsCache := NewDNSCache(0)
   599  	dnsCache.overLimit["test.com"] = true
   600  	c.Assert(dnsCache.cleanupOverLimitEntries(), checker.DeepEquals, []string{})
   601  }