cmd/derper: fix TestLookupMetric to pass when run alone
TestLookupMetric was added in e8d140654 (2023-08-17) without
initializing the dnsCache and dnsCacheBytes globals. When run in
isolation, handleBootstrapDNS writes a nil body (from the
uninitialized dnsCacheBytes), causing getBootstrapDNS to fail
decoding an empty response with EOF.
Add a setDNSCache test helper that stores the dnsEntryMap, marshals
dnsCacheBytes, and registers a t.Cleanup to nil both out, so tests
that forget to call it will hit the dnsCache-nil fatal in
getBootstrapDNS rather than silently depending on prior test state.
Also add AssertNotParallel and a dnsCache-nil fatal check to
getBootstrapDNS, the central helper all bootstrap DNS tests flow
through, to prevent future tests from running in parallel (they
all mutate package-level DNS caches and metrics) and to give a
clear error if a test forgets to initialize the DNS caches.
Fixes #19388
Change-Id: I8ad454ec6026c71f13ecfa14d25925df5478b908
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Co-authored-by: Avery Pennarun <apenwarr@tailscale.com>
This commit is contained in:
committed by
Brad Fitzpatrick
parent
7dcb378875
commit
a97850f7e2
@@ -41,8 +41,28 @@ func (b *bitbucketResponseWriter) Write(p []byte) (int, error) { return len(p),
|
|||||||
|
|
||||||
func (b *bitbucketResponseWriter) WriteHeader(statusCode int) {}
|
func (b *bitbucketResponseWriter) WriteHeader(statusCode int) {}
|
||||||
|
|
||||||
|
// setDNSCache sets the published DNS cache for tests.
|
||||||
|
func setDNSCache(tb testing.TB, m *dnsEntryMap) {
|
||||||
|
tb.Helper()
|
||||||
|
j, err := json.Marshal(m.IPs)
|
||||||
|
if err != nil {
|
||||||
|
tb.Fatal(err)
|
||||||
|
}
|
||||||
|
tstest.AssertNotParallel(tb)
|
||||||
|
dnsCache.Store(m)
|
||||||
|
dnsCacheBytes.Store(j)
|
||||||
|
tb.Cleanup(func() {
|
||||||
|
dnsCache.Store(nil)
|
||||||
|
dnsCacheBytes.Store(nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func getBootstrapDNS(t *testing.T, q string) map[string][]net.IP {
|
func getBootstrapDNS(t *testing.T, q string) map[string][]net.IP {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
tstest.AssertNotParallel(t)
|
||||||
|
if dnsCache.Load() == nil {
|
||||||
|
t.Fatal("dnsCache not initialized; call setDNSCache before getBootstrapDNS")
|
||||||
|
}
|
||||||
req, _ := http.NewRequest("GET", "https://localhost/bootstrap-dns?q="+url.QueryEscape(q), nil)
|
req, _ := http.NewRequest("GET", "https://localhost/bootstrap-dns?q="+url.QueryEscape(q), nil)
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
handleBootstrapDNS(w, req)
|
handleBootstrapDNS(w, req)
|
||||||
@@ -100,7 +120,8 @@ func TestUnpublishedDNS(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func resetMetrics() {
|
func resetMetrics(tb testing.TB) {
|
||||||
|
tstest.AssertNotParallel(tb)
|
||||||
publishedDNSHits.Set(0)
|
publishedDNSHits.Set(0)
|
||||||
publishedDNSMisses.Set(0)
|
publishedDNSMisses.Set(0)
|
||||||
unpublishedDNSHits.Set(0)
|
unpublishedDNSHits.Set(0)
|
||||||
@@ -114,8 +135,7 @@ func TestUnpublishedDNSEmptyList(t *testing.T) {
|
|||||||
pub := &dnsEntryMap{
|
pub := &dnsEntryMap{
|
||||||
IPs: map[string][]net.IP{"tailscale.com": {net.IPv4(10, 10, 10, 10)}},
|
IPs: map[string][]net.IP{"tailscale.com": {net.IPv4(10, 10, 10, 10)}},
|
||||||
}
|
}
|
||||||
dnsCache.Store(pub)
|
setDNSCache(t, pub)
|
||||||
dnsCacheBytes.Store([]byte(`{"tailscale.com":["10.10.10.10"]}`))
|
|
||||||
|
|
||||||
unpublishedDNSCache.Store(&dnsEntryMap{
|
unpublishedDNSCache.Store(&dnsEntryMap{
|
||||||
IPs: map[string][]net.IP{
|
IPs: map[string][]net.IP{
|
||||||
@@ -131,7 +151,7 @@ func TestUnpublishedDNSEmptyList(t *testing.T) {
|
|||||||
t.Run("CacheMiss", func(t *testing.T) {
|
t.Run("CacheMiss", func(t *testing.T) {
|
||||||
// One domain in map but empty, one not in map at all
|
// One domain in map but empty, one not in map at all
|
||||||
for _, q := range []string{"log.tailscale.com", "login.tailscale.com"} {
|
for _, q := range []string{"log.tailscale.com", "login.tailscale.com"} {
|
||||||
resetMetrics()
|
resetMetrics(t)
|
||||||
ips := getBootstrapDNS(t, q)
|
ips := getBootstrapDNS(t, q)
|
||||||
|
|
||||||
// Expected our public map to be returned on a cache miss
|
// Expected our public map to be returned on a cache miss
|
||||||
@@ -149,7 +169,7 @@ func TestUnpublishedDNSEmptyList(t *testing.T) {
|
|||||||
|
|
||||||
// Verify that we do get a valid response and metric.
|
// Verify that we do get a valid response and metric.
|
||||||
t.Run("CacheHit", func(t *testing.T) {
|
t.Run("CacheHit", func(t *testing.T) {
|
||||||
resetMetrics()
|
resetMetrics(t)
|
||||||
ips := getBootstrapDNS(t, "controlplane.tailscale.com")
|
ips := getBootstrapDNS(t, "controlplane.tailscale.com")
|
||||||
want := map[string][]net.IP{"controlplane.tailscale.com": {net.IPv4(1, 2, 3, 4)}}
|
want := map[string][]net.IP{"controlplane.tailscale.com": {net.IPv4(1, 2, 3, 4)}}
|
||||||
if !reflect.DeepEqual(ips, want) {
|
if !reflect.DeepEqual(ips, want) {
|
||||||
@@ -166,8 +186,10 @@ func TestUnpublishedDNSEmptyList(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestLookupMetric(t *testing.T) {
|
func TestLookupMetric(t *testing.T) {
|
||||||
|
setDNSCache(t, &dnsEntryMap{})
|
||||||
|
|
||||||
d := []string{"a.io", "b.io", "c.io", "d.io", "e.io", "e.io", "e.io", "a.io"}
|
d := []string{"a.io", "b.io", "c.io", "d.io", "e.io", "e.io", "e.io", "a.io"}
|
||||||
resetMetrics()
|
resetMetrics(t)
|
||||||
for _, q := range d {
|
for _, q := range d {
|
||||||
_ = getBootstrapDNS(t, q)
|
_ = getBootstrapDNS(t, q)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user