Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1a687de26 |
@@ -21,8 +21,6 @@ trial run of the finished image: https://git.eeqj.de/sneak/dnswatcher/issues/149
|
|||||||
|
|
||||||
- 2026-10-02: a push to a branch cancels that branch's older CI run, and the
|
- 2026-10-02: a push to a branch cancels that branch's older CI run, and the
|
||||||
checkout leaves no token in `.git/config` (closes #216).
|
checkout leaves no token in `.git/config` (closes #216).
|
||||||
- 2026-10-02: watcher tests send far fewer queries and a live attempt may take
|
|
||||||
18s; nameserver addresses are asked only for A, AAAA, CNAME (closes #214).
|
|
||||||
- 2026-10-02: the resolver tries root servers, and every other server list it
|
- 2026-10-02: the resolver tries root servers, and every other server list it
|
||||||
walks, in a random order each time, not always from the top (closes #138).
|
walks, in a random order each time, not always from the top (closes #138).
|
||||||
- 2026-10-02: a name listed more than once in `DNSWATCHER_TARGETS`, in any
|
- 2026-10-02: a name listed more than once in `DNSWATCHER_TARGETS`, in any
|
||||||
|
|||||||
@@ -36,18 +36,11 @@ const (
|
|||||||
// before the test fails.
|
// before the test fails.
|
||||||
attempts = 3
|
attempts = 3
|
||||||
|
|
||||||
// AttemptTimeout bounds one attempt. It must fit the longest
|
// AttemptTimeout bounds one attempt. Worst case for an operation
|
||||||
// operation, a watcher check, which sends over a hundred queries one
|
// is attempts * AttemptTimeout plus the backoff — about 26
|
||||||
// after another and on a slow build host takes several times as long
|
// seconds, well inside the 90-second `go test -timeout` backstop
|
||||||
// as the few seconds it takes on a fast one. An operation whose
|
// even when several operations exhaust their attempts.
|
||||||
// every attempt fails takes attempts * AttemptTimeout plus the
|
AttemptTimeout = 8 * time.Second
|
||||||
// backoff, about 56 seconds, after it waits for one of the
|
|
||||||
// Concurrency slots that every live operation in the test binary
|
|
||||||
// shares. So when live DNS does not answer at all, a test binary
|
|
||||||
// with more live operations than slots runs into the 90-second
|
|
||||||
// `go test -timeout` backstop instead of each test failing on its
|
|
||||||
// own.
|
|
||||||
AttemptTimeout = 18 * time.Second
|
|
||||||
|
|
||||||
// backoffBase is the delay after the first failed attempt; it is
|
// backoffBase is the delay after the first failed attempt; it is
|
||||||
// multiplied by backoffFactor each time.
|
// multiplied by backoffFactor each time.
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func (r *Resolver) QueryEachNS(
|
|||||||
nameservers []string,
|
nameservers []string,
|
||||||
hostname string,
|
hostname string,
|
||||||
) (map[string]*NameserverResponse, error) {
|
) (map[string]*NameserverResponse, error) {
|
||||||
return r.queryEachNS(ctx, nameservers, hostname, recordTypes())
|
return r.queryEachNS(ctx, nameservers, hostname)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveNSIPs exports resolveNSIPs for testing.
|
// ResolveNSIPs exports resolveNSIPs for testing.
|
||||||
|
|||||||
@@ -533,39 +533,12 @@ func (r *Resolver) FindAuthoritativeNameservers(
|
|||||||
return nil, ErrNoNameservers
|
return nil, ErrNoNameservers
|
||||||
}
|
}
|
||||||
|
|
||||||
// recordTypes returns the record types a nameserver is asked for when a
|
|
||||||
// name is checked.
|
|
||||||
func recordTypes() []uint16 {
|
|
||||||
return []uint16{
|
|
||||||
dns.TypeA, dns.TypeAAAA, dns.TypeCNAME,
|
|
||||||
dns.TypeMX, dns.TypeTXT, dns.TypeSRV,
|
|
||||||
dns.TypeCAA, dns.TypeNS,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// addressTypes returns the record types ResolveIPAddresses asks for,
|
|
||||||
// the only ones it reads.
|
|
||||||
func addressTypes() []uint16 {
|
|
||||||
return []uint16{dns.TypeA, dns.TypeAAAA, dns.TypeCNAME}
|
|
||||||
}
|
|
||||||
|
|
||||||
// QueryNameserver queries a specific nameserver for all record
|
// QueryNameserver queries a specific nameserver for all record
|
||||||
// types and builds a NameserverResponse.
|
// types and builds a NameserverResponse.
|
||||||
func (r *Resolver) QueryNameserver(
|
func (r *Resolver) QueryNameserver(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
nsHostname string,
|
nsHostname string,
|
||||||
hostname string,
|
hostname string,
|
||||||
) (*NameserverResponse, error) {
|
|
||||||
return r.queryNameserver(ctx, nsHostname, hostname, recordTypes())
|
|
||||||
}
|
|
||||||
|
|
||||||
// queryNameserver queries a specific nameserver for the record types
|
|
||||||
// in qtypes and builds a NameserverResponse.
|
|
||||||
func (r *Resolver) queryNameserver(
|
|
||||||
ctx context.Context,
|
|
||||||
nsHostname string,
|
|
||||||
hostname string,
|
|
||||||
qtypes []uint16,
|
|
||||||
) (*NameserverResponse, error) {
|
) (*NameserverResponse, error) {
|
||||||
if checkCtx(ctx) != nil {
|
if checkCtx(ctx) != nil {
|
||||||
return nil, ErrContextCanceled
|
return nil, ErrContextCanceled
|
||||||
@@ -578,7 +551,7 @@ func (r *Resolver) queryNameserver(
|
|||||||
|
|
||||||
hostname = dns.Fqdn(hostname)
|
hostname = dns.Fqdn(hostname)
|
||||||
|
|
||||||
return r.queryTypes(ctx, nsHostname, nsIPs[0], hostname, qtypes)
|
return r.queryAllTypes(ctx, nsHostname, nsIPs[0], hostname)
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryNameserverIP queries a nameserver by its IP address directly,
|
// QueryNameserverIP queries a nameserver by its IP address directly,
|
||||||
@@ -595,15 +568,14 @@ func (r *Resolver) QueryNameserverIP(
|
|||||||
|
|
||||||
hostname = dns.Fqdn(hostname)
|
hostname = dns.Fqdn(hostname)
|
||||||
|
|
||||||
return r.queryTypes(ctx, nsHostname, nsIP, hostname, recordTypes())
|
return r.queryAllTypes(ctx, nsHostname, nsIP, hostname)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resolver) queryTypes(
|
func (r *Resolver) queryAllTypes(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
nsHostname string,
|
nsHostname string,
|
||||||
nsIP string,
|
nsIP string,
|
||||||
hostname string,
|
hostname string,
|
||||||
qtypes []uint16,
|
|
||||||
) (*NameserverResponse, error) {
|
) (*NameserverResponse, error) {
|
||||||
resp := &NameserverResponse{
|
resp := &NameserverResponse{
|
||||||
Nameserver: nsHostname,
|
Nameserver: nsHostname,
|
||||||
@@ -611,6 +583,12 @@ func (r *Resolver) queryTypes(
|
|||||||
Status: StatusOK,
|
Status: StatusOK,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qtypes := []uint16{
|
||||||
|
dns.TypeA, dns.TypeAAAA, dns.TypeCNAME,
|
||||||
|
dns.TypeMX, dns.TypeTXT, dns.TypeSRV,
|
||||||
|
dns.TypeCAA, dns.TypeNS,
|
||||||
|
}
|
||||||
|
|
||||||
state := r.queryEachType(ctx, nsIP, hostname, qtypes, resp)
|
state := r.queryEachType(ctx, nsIP, hostname, qtypes, resp)
|
||||||
classifyResponse(resp, state)
|
classifyResponse(resp, state)
|
||||||
|
|
||||||
@@ -801,19 +779,18 @@ func (r *Resolver) QueryAllNameservers(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.queryEachNS(ctx, nameservers, hostname, recordTypes())
|
return r.queryEachNS(ctx, nameservers, hostname)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resolver) queryEachNS(
|
func (r *Resolver) queryEachNS(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
nameservers []string,
|
nameservers []string,
|
||||||
hostname string,
|
hostname string,
|
||||||
qtypes []uint16,
|
|
||||||
) (map[string]*NameserverResponse, error) {
|
) (map[string]*NameserverResponse, error) {
|
||||||
results := make(map[string]*NameserverResponse)
|
results := make(map[string]*NameserverResponse)
|
||||||
|
|
||||||
for _, ns := range nameservers {
|
for _, ns := range nameservers {
|
||||||
resp, err := r.queryNameserver(ctx, ns, hostname, qtypes)
|
resp, err := r.QueryNameserver(ctx, ns, hostname)
|
||||||
|
|
||||||
// A query the context cut short says nothing about the
|
// A query the context cut short says nothing about the
|
||||||
// nameserver, so it must not be returned as its failure.
|
// nameserver, so it must not be returned as its failure.
|
||||||
@@ -858,10 +835,9 @@ func (r *Resolver) LookupAllRecords(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIPAddresses resolves a hostname to all IPv4 and IPv6
|
// ResolveIPAddresses resolves a hostname to all IPv4 and IPv6
|
||||||
// addresses, following CNAME chains up to MaxCNAMEDepth. It asks each
|
// addresses, following CNAME chains up to MaxCNAMEDepth. When no
|
||||||
// nameserver of the name's zone for its A, AAAA and CNAME records only.
|
// nameserver of the name's zone answered, it returns an error rather
|
||||||
// When no nameserver of the name's zone answered, it returns an error
|
// than no addresses.
|
||||||
// rather than no addresses.
|
|
||||||
func (r *Resolver) ResolveIPAddresses(
|
func (r *Resolver) ResolveIPAddresses(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
hostname string,
|
hostname string,
|
||||||
@@ -882,12 +858,7 @@ func (r *Resolver) resolveIPWithCNAME(
|
|||||||
return nil, ErrCNAMEDepthExceeded
|
return nil, ErrCNAMEDepthExceeded
|
||||||
}
|
}
|
||||||
|
|
||||||
nameservers, err := r.FindAuthoritativeNameservers(ctx, hostname)
|
results, err := r.QueryAllNameservers(ctx, hostname)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
results, err := r.queryEachNS(ctx, nameservers, hostname, addressTypes())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -562,34 +562,6 @@ func TestResolveIPAddresses_CloudflareDomain(t *testing.T) {
|
|||||||
assert.NotEmpty(t, ips)
|
assert.NotEmpty(t, ips)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestResolveIPAddresses_NameserverIPv4AndIPv6 looks up the addresses of
|
|
||||||
// one of cloudflare.com's nameservers, as a domain check does for each
|
|
||||||
// nameserver. That name has A and AAAA records, so both kinds of address
|
|
||||||
// come back.
|
|
||||||
func TestResolveIPAddresses_NameserverIPv4AndIPv6(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
r := newTestResolver(t)
|
|
||||||
ns := findOneNSForDomain(t, r, "cloudflare.com")
|
|
||||||
ips := liveResolveIPs(t, r, ns)
|
|
||||||
|
|
||||||
var ipv4, ipv6 int
|
|
||||||
|
|
||||||
for _, ip := range ips {
|
|
||||||
parsed := net.ParseIP(ip)
|
|
||||||
require.NotNil(t, parsed, "should be valid IP: %s", ip)
|
|
||||||
|
|
||||||
if parsed.To4() != nil {
|
|
||||||
ipv4++
|
|
||||||
} else {
|
|
||||||
ipv6++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Positive(t, ipv4, "no IPv4 address for %s: %v", ns, ips)
|
|
||||||
assert.Positive(t, ipv6, "no IPv6 address for %s: %v", ns, ips)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
// Context cancellation tests
|
// Context cancellation tests
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
|
|||||||
@@ -26,22 +26,18 @@ import (
|
|||||||
|
|
||||||
// The watcher looks these names up in live DNS with the real resolver,
|
// The watcher looks these names up in live DNS with the real resolver,
|
||||||
// so tests assert on what the watcher does with the answers, never on
|
// so tests assert on what the watcher does with the answers, never on
|
||||||
// the records these zones publish. The nameservers of testHost and
|
// the records these zones publish. testHost's nameservers and addresses
|
||||||
// testSmallDomain stay the same between a test looking them up and its
|
// stay the same from one check to the next, which the tests that check
|
||||||
// check. Every query a check sends is one more that can be lost, so the
|
// it twice rely on, and testSmallDomain's nameservers stay the same
|
||||||
// tests keep them few. A check asks each of a name's nameservers about
|
// between a test looking them up and its check. A domain check looks up
|
||||||
// every record type, and both names have two. A domain check also looks
|
// each nameserver's addresses, about a second per nameserver, so the
|
||||||
// up each nameserver's addresses at every nameserver of the zone that
|
// tests that check a domain use testSmallDomain, which has two
|
||||||
// nameserver is in: testSmallDomain's nameservers are in zones with two
|
// nameservers, and check it once. The tests that query testDomain's
|
||||||
// nameservers, while a domain whose nameservers are in, say,
|
// nameservers directly do no domain check.
|
||||||
// cloudflare.com, which has five, makes each domain check much longer.
|
|
||||||
// A test checks a domain only when it is about domains, and checks once,
|
|
||||||
// from saved state it builds, rather than twice. The tests that query
|
|
||||||
// testDomain's nameservers directly do no domain check.
|
|
||||||
const (
|
const (
|
||||||
testDomain = "google.com"
|
testDomain = "google.com"
|
||||||
testSmallDomain = "desec.io"
|
testSmallDomain = "example.com"
|
||||||
testHost = "example.org"
|
testHost = "cloudflare.com"
|
||||||
testIssuer = "DigiCert"
|
testIssuer = "DigiCert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -263,48 +259,55 @@ func checkOnce(
|
|||||||
|
|
||||||
// runChecks builds a watcher, lets prepare set up the saved state and
|
// runChecks builds a watcher, lets prepare set up the saved state and
|
||||||
// stand-ins it starts from, and runs its checks once against live DNS.
|
// stand-ins it starts from, and runs its checks once against live DNS.
|
||||||
// When the check finds no fresh address for a name (see checkOnce), the
|
// If change is not nil, change then alters the saved state or stand-ins
|
||||||
// watcher is thrown away and all of this runs again on a new one, so a
|
// and the checks run a second time. When either check finds no fresh
|
||||||
// failed attempt leaves nothing behind in the saved state, the
|
// address for a name (see checkOnce), the watcher is thrown away and
|
||||||
// stand-ins or the notifications.
|
// all of this runs again on a new one, so a failed attempt leaves
|
||||||
|
// nothing behind in the saved state, the stand-ins or the notifications.
|
||||||
func runChecks(
|
func runChecks(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
cfg *config.Config,
|
cfg *config.Config,
|
||||||
prepare func(deps *testDeps),
|
prepare, change func(deps *testDeps),
|
||||||
) (*watcher.Watcher, *testDeps) {
|
) *testDeps {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
var (
|
var deps *testDeps
|
||||||
w *watcher.Watcher
|
|
||||||
deps *testDeps
|
|
||||||
)
|
|
||||||
|
|
||||||
livednstest.Retry(t, "watcher checks", func(ctx context.Context) error {
|
livednstest.Retry(t, "watcher checks", func(ctx context.Context) error {
|
||||||
|
var w *watcher.Watcher
|
||||||
|
|
||||||
w, deps = newTestWatcher(t, cfg)
|
w, deps = newTestWatcher(t, cfg)
|
||||||
|
|
||||||
if prepare != nil {
|
if prepare != nil {
|
||||||
prepare(deps)
|
prepare(deps)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := checkOnce(ctx, w, deps)
|
||||||
|
if err != nil || change == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
change(deps)
|
||||||
|
|
||||||
return checkOnce(ctx, w, deps)
|
return checkOnce(ctx, w, deps)
|
||||||
})
|
})
|
||||||
|
|
||||||
return w, deps
|
return deps
|
||||||
}
|
}
|
||||||
|
|
||||||
// lookupNameservers returns the nameservers live DNS lists for name,
|
// lookupNameservers returns the nameservers live DNS lists for domain,
|
||||||
// for a test to save in the state its check starts from.
|
// for a test to save in the state its check starts from.
|
||||||
func lookupNameservers(t *testing.T, name string) []string {
|
func lookupNameservers(t *testing.T, domain string) []string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
res := resolver.NewFromLogger(slog.Default())
|
res := resolver.NewFromLogger(slog.Default())
|
||||||
|
|
||||||
var nameservers []string
|
var nameservers []string
|
||||||
|
|
||||||
livednstest.Retry(t, "LookupNS("+name+")", func(ctx context.Context) error {
|
livednstest.Retry(t, "LookupNS("+domain+")", func(ctx context.Context) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
nameservers, err = res.LookupNS(ctx, name)
|
nameservers, err = res.LookupNS(ctx, domain)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
@@ -367,7 +370,7 @@ func TestFirstRunBaseline(t *testing.T) {
|
|||||||
cfg.Domains = []string{testSmallDomain}
|
cfg.Domains = []string{testSmallDomain}
|
||||||
cfg.Hostnames = []string{testHost}
|
cfg.Hostnames = []string{testHost}
|
||||||
|
|
||||||
_, deps := runChecks(t, cfg, nil)
|
deps := runChecks(t, cfg, nil, nil)
|
||||||
|
|
||||||
assertNoNotifications(t, deps)
|
assertNoNotifications(t, deps)
|
||||||
assertStatePopulated(t, deps)
|
assertStatePopulated(t, deps)
|
||||||
@@ -419,7 +422,7 @@ func TestDomainPortAndTLSChecks(t *testing.T) {
|
|||||||
cfg := defaultTestConfig(t)
|
cfg := defaultTestConfig(t)
|
||||||
cfg.Domains = []string{testSmallDomain}
|
cfg.Domains = []string{testSmallDomain}
|
||||||
|
|
||||||
_, deps := runChecks(t, cfg, nil)
|
deps := runChecks(t, cfg, nil, nil)
|
||||||
|
|
||||||
snap := deps.state.GetSnapshot()
|
snap := deps.state.GetSnapshot()
|
||||||
|
|
||||||
@@ -459,11 +462,11 @@ func TestNSChangeDetection(t *testing.T) {
|
|||||||
cfg.Domains = []string{testSmallDomain}
|
cfg.Domains = []string{testSmallDomain}
|
||||||
|
|
||||||
// The saved state lists nameservers that live DNS does not.
|
// The saved state lists nameservers that live DNS does not.
|
||||||
_, deps := runChecks(t, cfg, func(deps *testDeps) {
|
deps := runChecks(t, cfg, func(deps *testDeps) {
|
||||||
deps.state.SetDomainState(testSmallDomain, &state.DomainState{
|
deps.state.SetDomainState(testSmallDomain, &state.DomainState{
|
||||||
Nameservers: []string{oldNS1, oldNS2},
|
Nameservers: []string{oldNS1, oldNS2},
|
||||||
})
|
})
|
||||||
})
|
}, nil)
|
||||||
|
|
||||||
assertNotified(t, deps, "NS Change: "+testSmallDomain, "warning")
|
assertNotified(t, deps, "NS Change: "+testSmallDomain, "warning")
|
||||||
|
|
||||||
@@ -483,7 +486,7 @@ func TestNSAddressChangeDetection(t *testing.T) {
|
|||||||
|
|
||||||
// The saved state lists the nameservers live DNS lists, each at an
|
// The saved state lists the nameservers live DNS lists, each at an
|
||||||
// address live DNS never returns.
|
// address live DNS never returns.
|
||||||
_, deps := runChecks(t, cfg, func(deps *testDeps) {
|
deps := runChecks(t, cfg, func(deps *testDeps) {
|
||||||
nsAddresses := make(map[string][]string, len(nameservers))
|
nsAddresses := make(map[string][]string, len(nameservers))
|
||||||
for _, ns := range nameservers {
|
for _, ns := range nameservers {
|
||||||
nsAddresses[ns] = []string{oldIP}
|
nsAddresses[ns] = []string{oldIP}
|
||||||
@@ -493,7 +496,7 @@ func TestNSAddressChangeDetection(t *testing.T) {
|
|||||||
Nameservers: nameservers,
|
Nameservers: nameservers,
|
||||||
NameserverAddresses: nsAddresses,
|
NameserverAddresses: nsAddresses,
|
||||||
})
|
})
|
||||||
})
|
}, nil)
|
||||||
|
|
||||||
title := "NS Address Change: " + testSmallDomain
|
title := "NS Address Change: " + testSmallDomain
|
||||||
ds, _ := deps.state.GetDomainState(testSmallDomain)
|
ds, _ := deps.state.GetDomainState(testSmallDomain)
|
||||||
@@ -539,12 +542,12 @@ func TestNSAddedAndRemovedIsNoAddressChange(t *testing.T) {
|
|||||||
// The saved state lists oldNS1, which live DNS does not, in place of
|
// The saved state lists oldNS1, which live DNS does not, in place of
|
||||||
// the first nameserver live DNS lists, so that the check finds that
|
// the first nameserver live DNS lists, so that the check finds that
|
||||||
// one added and oldNS1 removed. Only oldNS1 has addresses saved.
|
// one added and oldNS1 removed. Only oldNS1 has addresses saved.
|
||||||
_, deps := runChecks(t, cfg, func(deps *testDeps) {
|
deps := runChecks(t, cfg, func(deps *testDeps) {
|
||||||
deps.state.SetDomainState(testSmallDomain, &state.DomainState{
|
deps.state.SetDomainState(testSmallDomain, &state.DomainState{
|
||||||
Nameservers: append([]string{oldNS1}, nameservers[1:]...),
|
Nameservers: append([]string{oldNS1}, nameservers[1:]...),
|
||||||
NameserverAddresses: map[string][]string{oldNS1: {oldIP}},
|
NameserverAddresses: map[string][]string{oldNS1: {oldIP}},
|
||||||
})
|
})
|
||||||
})
|
}, nil)
|
||||||
|
|
||||||
if n := countNotifications(deps, "NS Change: "+testSmallDomain); n != 1 {
|
if n := countNotifications(deps, "NS Change: "+testSmallDomain); n != 1 {
|
||||||
t.Errorf("sent %d NS changes, want 1", n)
|
t.Errorf("sent %d NS changes, want 1", n)
|
||||||
@@ -562,17 +565,15 @@ func TestRecordChangeDetection(t *testing.T) {
|
|||||||
cfg := defaultTestConfig(t)
|
cfg := defaultTestConfig(t)
|
||||||
cfg.Hostnames = []string{testHost}
|
cfg.Hostnames = []string{testHost}
|
||||||
|
|
||||||
nameservers := lookupNameservers(t, testHost)
|
// Between the checks, save for every nameserver an address live DNS
|
||||||
|
// never returns.
|
||||||
// The saved state has every nameserver live DNS lists answering
|
deps := runChecks(t, cfg, nil, func(deps *testDeps) {
|
||||||
// with an address live DNS never returns.
|
hs, _ := deps.state.GetHostnameState(testHost)
|
||||||
_, deps := runChecks(t, cfg, func(deps *testDeps) {
|
for _, nsState := range hs.RecordsByNameserver {
|
||||||
byNameserver := make(map[string]*state.NameserverRecordState)
|
nsState.Records = map[string][]string{"A": {oldIP}}
|
||||||
for _, ns := range nameservers {
|
|
||||||
byNameserver[ns] = answered(map[string][]string{"A": {oldIP}})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deps.state.SetHostnameState(testHost, saved(byNameserver))
|
deps.state.SetHostnameState(testHost, hs)
|
||||||
})
|
})
|
||||||
|
|
||||||
assertNotified(t, deps, "Record Change: "+testHost, "warning")
|
assertNotified(t, deps, "Record Change: "+testHost, "warning")
|
||||||
@@ -584,15 +585,12 @@ func TestPortStateChange(t *testing.T) {
|
|||||||
cfg := defaultTestConfig(t)
|
cfg := defaultTestConfig(t)
|
||||||
cfg.Hostnames = []string{testHost}
|
cfg.Hostnames = []string{testHost}
|
||||||
|
|
||||||
w, deps := runChecks(t, cfg, nil)
|
// Between the checks, every port closes.
|
||||||
|
deps := runChecks(t, cfg, nil, func(deps *testDeps) {
|
||||||
// Every port closes, and the port checks run again. They look
|
deps.portChecker.mu.Lock()
|
||||||
// nothing up.
|
deps.portChecker.closed = true
|
||||||
deps.portChecker.mu.Lock()
|
deps.portChecker.mu.Unlock()
|
||||||
deps.portChecker.closed = true
|
})
|
||||||
deps.portChecker.mu.Unlock()
|
|
||||||
|
|
||||||
w.CheckAllPorts(t.Context())
|
|
||||||
|
|
||||||
hs, _ := deps.state.GetHostnameState(testHost)
|
hs, _ := deps.state.GetHostnameState(testHost)
|
||||||
assertNotified(
|
assertNotified(
|
||||||
@@ -612,7 +610,7 @@ func TestTLSExpiryWarning(t *testing.T) {
|
|||||||
cfg := defaultTestConfig(t)
|
cfg := defaultTestConfig(t)
|
||||||
cfg.Hostnames = []string{testHost}
|
cfg.Hostnames = []string{testHost}
|
||||||
|
|
||||||
_, deps := runChecks(t, cfg, expiresInThreeDays)
|
deps := runChecks(t, cfg, expiresInThreeDays, nil)
|
||||||
|
|
||||||
assertNotified(t, deps, "TLS Expiry Warning: "+testHost, "warning")
|
assertNotified(t, deps, "TLS Expiry Warning: "+testHost, "warning")
|
||||||
}
|
}
|
||||||
@@ -784,7 +782,7 @@ func TestDNSRunsBeforePortAndTLSChecks(t *testing.T) {
|
|||||||
cfg.Hostnames = []string{testHost}
|
cfg.Hostnames = []string{testHost}
|
||||||
|
|
||||||
// The saved state says the last check found testHost at oldIP.
|
// The saved state says the last check found testHost at oldIP.
|
||||||
_, deps := runChecks(t, cfg, func(deps *testDeps) {
|
deps := runChecks(t, cfg, func(deps *testDeps) {
|
||||||
deps.state.SetHostnameState(testHost, &state.HostnameState{
|
deps.state.SetHostnameState(testHost, &state.HostnameState{
|
||||||
RecordsByNameserver: map[string]*state.NameserverRecordState{
|
RecordsByNameserver: map[string]*state.NameserverRecordState{
|
||||||
oldNS1: {
|
oldNS1: {
|
||||||
@@ -793,7 +791,7 @@ func TestDNSRunsBeforePortAndTLSChecks(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
}, nil)
|
||||||
|
|
||||||
snap := deps.state.GetSnapshot()
|
snap := deps.state.GetSnapshot()
|
||||||
|
|
||||||
@@ -924,20 +922,21 @@ func TestNSFailureAndRecovery(t *testing.T) {
|
|||||||
cfg := defaultTestConfig(t)
|
cfg := defaultTestConfig(t)
|
||||||
cfg.Hostnames = []string{testHost}
|
cfg.Hostnames = []string{testHost}
|
||||||
|
|
||||||
nameservers := lookupNameservers(t, testHost)
|
// Between the checks, save every nameserver the first check found
|
||||||
|
// as one that did not answer, and add, as answering, one that live
|
||||||
// The saved state has every nameserver live DNS lists as one that
|
// DNS does not list, which then disappears.
|
||||||
// did not answer, and, as answering, one that live DNS does not
|
deps := runChecks(t, cfg, nil, func(deps *testDeps) {
|
||||||
// list, which then disappears.
|
hs, _ := deps.state.GetHostnameState(testHost)
|
||||||
_, deps := runChecks(t, cfg, func(deps *testDeps) {
|
for ns := range hs.RecordsByNameserver {
|
||||||
byNameserver := map[string]*state.NameserverRecordState{
|
hs.RecordsByNameserver[ns] = failed()
|
||||||
oldNS1: answered(map[string][]string{"A": {oldIP}}),
|
|
||||||
}
|
|
||||||
for _, ns := range nameservers {
|
|
||||||
byNameserver[ns] = failed()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deps.state.SetHostnameState(testHost, saved(byNameserver))
|
hs.RecordsByNameserver[oldNS1] = &state.NameserverRecordState{
|
||||||
|
Records: map[string][]string{"A": {oldIP}},
|
||||||
|
Status: "ok",
|
||||||
|
}
|
||||||
|
|
||||||
|
deps.state.SetHostnameState(testHost, hs)
|
||||||
})
|
})
|
||||||
|
|
||||||
assertNotified(t, deps, "NS Failure: "+testHost, "error")
|
assertNotified(t, deps, "NS Failure: "+testHost, "error")
|
||||||
|
|||||||
Reference in New Issue
Block a user