github.com/dbernstein1/tyk@v2.9.0-beta9-dl-apic+incompatible/dnscache/storage_test.go (about) 1 package dnscache 2 3 import ( 4 "net" 5 "reflect" 6 "testing" 7 "time" 8 9 "github.com/miekg/dns" 10 11 "github.com/TykTechnologies/tyk/test" 12 ) 13 14 var ( 15 expiration = 10 16 checkInterval = 5 17 ) 18 19 const ( 20 host = "orig-host.com." 21 singleRecordHost = "single.orig-host.com." 22 host2 = "orig-host2.com." 23 host3 = "some.orig-host3.com" 24 host4 = "some.orig-host4.com" 25 hostErrorable = "unknown.orig-host.com." 26 wsHost = "ws.orig-host.com." 27 ) 28 29 var ( 30 etcHostsMap = map[string][]string{ 31 singleRecordHost: {"10.0.2.10"}, 32 host: {"127.0.0.1", "127.0.0.2", "127.0.0.3"}, 33 host2: {"10.0.2.0", "10.0.2.1", "10.0.2.2"}, 34 host3: {"10.0.2.15", "10.0.2.16"}, 35 host4: {"10.0.2.11", "10.0.2.10"}, 36 wsHost: {"127.0.0.1", "127.0.0.1"}, 37 } 38 39 etcHostsErrorMap = map[string]int{ 40 hostErrorable: dns.RcodeServerFailure, 41 } 42 ) 43 44 type configTestStorageFetchItem struct { 45 *testing.T 46 etcHostsMap map[string][]string 47 etcHostsErrorsMap map[string]int 48 } 49 50 func setupTestStorageFetchItem(cfg *configTestStorageFetchItem) func() { 51 handle, err := test.InitDNSMock(cfg.etcHostsMap, cfg.etcHostsErrorsMap) 52 if err != nil { 53 cfg.T.Error(err.Error()) 54 } 55 56 return func() { 57 if err := handle.ShutdownDnsMock(); err != nil { 58 cfg.T.Error(err.Error()) 59 } 60 } 61 } 62 63 func TestStorageFetchItem(t *testing.T) { 64 dnsCache := NewDnsCacheStorage(time.Duration(expiration)*time.Second, time.Duration(checkInterval)*time.Second) 65 66 tearDownTestStorageFetchItem := setupTestStorageFetchItem(&configTestStorageFetchItem{t, etcHostsMap, etcHostsErrorMap}) 67 defer func() { 68 tearDownTestStorageFetchItem() 69 dnsCache.Clear() 70 dnsCache = nil 71 }() 72 73 cases := []struct { 74 name string 75 76 Host string 77 ExpectedIPs []string 78 79 expectedErrorType reflect.Type 80 shouldExistInCache bool 81 shouldBeAddedToCache bool 82 }{ 83 { 84 "Should cache first dns record first fetch", 85 host, etcHostsMap[host], 86 nil, false, true, 87 }, 88 { 89 "Should cache second dns record first fetch", 90 host2, etcHostsMap[host2], 91 nil, false, true, 92 }, 93 { 94 "Should populate from cache first dns record second fetch", 95 host, etcHostsMap[host], 96 nil, true, false, 97 }, 98 { 99 "Should populate from cache first dns record third fetch", 100 host, etcHostsMap[host], 101 nil, true, false, 102 }, 103 { 104 "Should populate from cache second dns record second fetch", 105 host2, etcHostsMap[host2], 106 nil, true, false, 107 }, 108 { 109 "Shouldn't cache dns record fetch in case error", 110 hostErrorable, nil, 111 reflect.TypeOf(&net.DNSError{}), false, false, 112 }, 113 } 114 115 for _, tc := range cases { 116 t.Run(tc.name, func(t *testing.T) { 117 got, err := dnsCache.FetchItem(tc.Host) 118 119 if tc.expectedErrorType != nil { 120 if err == nil || tc.expectedErrorType != reflect.TypeOf(err) { 121 t.Fatalf("wanted FetchItem error type %v, got %v. Error=%#v", tc.expectedErrorType, reflect.TypeOf(err), err) 122 } 123 124 if _, ok := dnsCache.Get(tc.Host); got != nil || ok { 125 t.Fatalf("wanted FetchItem error to omit cache. got %#v, ok=%t", got, ok) 126 } 127 return 128 } 129 130 if err != nil || !reflect.DeepEqual(got, tc.ExpectedIPs) { 131 t.Fatalf("wanted ips %q, got %q. Error: %v", tc.ExpectedIPs, got, err) 132 } 133 134 if tc.shouldExistInCache || tc.shouldBeAddedToCache { 135 record, ok := dnsCache.Get(tc.Host) 136 137 if !ok { 138 t.Fatalf("Host addresses weren't found in cache; host %q", tc.Host) 139 } 140 141 if !test.IsDnsRecordsAddrsEqualsTo(record.Addrs, tc.ExpectedIPs) { 142 t.Fatalf("wanted cached ips %v, got record %v", tc.ExpectedIPs, record) 143 } 144 } else { 145 if got, ok := dnsCache.Get(tc.Host); !test.IsDnsRecordsAddrsEqualsTo(got.Addrs, nil) || ok { 146 t.Fatalf("wanted FetchItem to omit write to cache. got %#v, ok=%t", got, ok) 147 } 148 } 149 }) 150 } 151 } 152 153 func TestStorageRecordExpiration(t *testing.T) { 154 var ( 155 expiration = 2000 156 checkInterval = 1500 157 ) 158 159 type testRecord struct { 160 dns string 161 addrs []string 162 addDelay time.Duration 163 } 164 165 cases := []struct { 166 name string 167 168 records []testRecord 169 sleepBeforeCleanup time.Duration 170 notExpiredAfterDelay []testRecord 171 checkInterval int 172 }{ 173 { 174 "Shouldn't remove dns record when ttl/expiration < 1", 175 []testRecord{ 176 {dns: host, addrs: etcHostsMap[host]}, 177 }, 178 time.Duration(checkInterval+10) * time.Millisecond, 179 []testRecord{ 180 {dns: host, addrs: etcHostsMap[host]}, 181 }, 182 checkInterval, 183 }, 184 { 185 "Should remove single dns record after expiration", 186 []testRecord{ 187 {dns: host, addrs: etcHostsMap[host]}, 188 }, 189 time.Duration(expiration+10) * time.Millisecond, 190 []testRecord{}, 191 checkInterval, 192 }, 193 { 194 "Should leave as expired dns records if check_interval=-1", 195 []testRecord{ 196 {dns: host, addrs: etcHostsMap[host]}, 197 {dns: host2, addrs: etcHostsMap[host2]}, 198 {dns: wsHost, addrs: etcHostsMap[wsHost]}, 199 }, 200 time.Duration(checkInterval+10) * time.Millisecond, 201 []testRecord{ 202 {dns: host, addrs: etcHostsMap[host]}, 203 {dns: host2, addrs: etcHostsMap[host2]}, 204 {dns: wsHost, addrs: etcHostsMap[wsHost]}, 205 }, 206 -1, 207 }, 208 { 209 "Should remove all(>1) dns records after expiration", 210 []testRecord{ 211 {dns: host2, addrs: etcHostsMap[host]}, 212 {dns: host2, addrs: etcHostsMap[host2]}, 213 {dns: host2, addrs: etcHostsMap[wsHost]}, 214 }, 215 time.Duration(expiration+10) * time.Millisecond, 216 []testRecord{}, 217 checkInterval, 218 }, 219 { 220 "Should remove only expired record after expiration", 221 []testRecord{ 222 {dns: host, addrs: etcHostsMap[host]}, 223 {dns: host2, addrs: etcHostsMap[host2], addDelay: 500 * time.Millisecond}, 224 {dns: wsHost, addrs: etcHostsMap[wsHost]}, 225 }, 226 time.Duration(expiration-400) * time.Millisecond, 227 []testRecord{ 228 {dns: host2, addrs: etcHostsMap[host2]}, 229 {dns: wsHost, addrs: etcHostsMap[wsHost]}, 230 }, 231 checkInterval, 232 }, 233 { 234 "Should remove only expired records after expiration", 235 []testRecord{ 236 {dns: host, addrs: etcHostsMap[host]}, 237 {dns: host2, addrs: etcHostsMap[host2], addDelay: 250 * time.Millisecond}, 238 {dns: host3, addrs: etcHostsMap[host3], addDelay: 500 * time.Millisecond}, 239 {dns: host4, addrs: etcHostsMap[host4], addDelay: 100 * time.Millisecond}, 240 {dns: wsHost, addrs: etcHostsMap[wsHost]}, 241 }, 242 time.Duration(expiration-350) * time.Millisecond, 243 []testRecord{ 244 {dns: host3, addrs: etcHostsMap[host3]}, 245 {dns: host4, addrs: etcHostsMap[host4]}, 246 {dns: wsHost, addrs: etcHostsMap[wsHost]}, 247 }, 248 checkInterval, 249 }, 250 } 251 252 for _, tc := range cases { 253 t.Run(tc.name, func(t *testing.T) { 254 dnsCache := NewDnsCacheStorage(time.Duration(expiration)*time.Millisecond, time.Duration(tc.checkInterval)*time.Millisecond) 255 256 for _, r := range tc.records { 257 if r.addDelay > 0 { 258 time.Sleep(r.addDelay) 259 } 260 dnsCache.Set(r.dns, r.addrs) 261 } 262 263 if tc.sleepBeforeCleanup > 0 { 264 time.Sleep(tc.sleepBeforeCleanup) 265 } 266 if lenNonExpired, lenCurrent := len(tc.notExpiredAfterDelay), len(dnsCache.Items(tc.checkInterval == -1)); lenNonExpired != lenCurrent { 267 t.Fatalf("wanted len(nonExpiredItems) %d, got %d. items=%+v", lenNonExpired, lenCurrent, dnsCache.Items(tc.checkInterval == -1)) 268 } 269 270 if tc.checkInterval == -1 { 271 for _, r := range tc.records { 272 if item, ok := dnsCache.Items(true)[r.dns]; !ok || !test.IsDnsRecordsAddrsEqualsTo(item.Addrs, r.addrs) { 273 t.Fatalf("wanted expired cached ips %v, got item %#v. items=%+v, ok=%t", r.addrs, item, dnsCache.Items(true), ok) 274 } 275 } 276 } else { 277 for _, r := range tc.notExpiredAfterDelay { 278 if item, ok := dnsCache.Get(r.dns); !ok || !test.IsDnsRecordsAddrsEqualsTo(item.Addrs, r.addrs) { 279 t.Fatalf("wanted cached ips %v, got item %#v. items=%+v, ok=%t", r.addrs, item, dnsCache.Items(false), ok) 280 } 281 } 282 } 283 284 dnsCache.Clear() 285 dnsCache = nil 286 }) 287 } 288 }