resolver, watcher: a domain's nameservers are only its own delegation (closes #222)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
When a domain's parent zone's servers answer NXDOMAIN, LookupNS returns ErrNXDomain. The watcher then saves the domain with no nameservers and nxdomain set, shown on the dashboard and in /api/v1/status, asks for none of its records and removes those saved, so its old nameservers go in one NS Change. A domain with no delegation of its own gets an empty set and its records are still asked at the zone it is in. FindAuthoritativeNameservers moves to a parent name only on one of those two answers; when the servers do not answer, it returns the error. After an upgrade, a domain without its own delegation that was saved with its parent zone's nameservers gets one NS Change; the README says so. Model: opus-5-5
This commit was merged in pull request #250.
This commit is contained in:
@@ -191,21 +191,39 @@ func TestDashboardShowsDomainRecordsUnderDomains(t *testing.T) {
|
||||
|
||||
words := strings.Join(strings.Fields(page), " ")
|
||||
|
||||
footer := "monitoring 1 domains + 1 hostnames"
|
||||
footer := "monitoring 2 domains + 1 hostnames"
|
||||
if !strings.Contains(words, footer) {
|
||||
t.Errorf("dashboard does not say %q", footer)
|
||||
}
|
||||
|
||||
// With the tags taken out, the summary bar starts "Domains 1
|
||||
// With the tags taken out, the summary bar starts "Domains 2
|
||||
// Hostnames 1".
|
||||
text := regexp.MustCompile(`<[^>]*>`).ReplaceAllString(page, " ")
|
||||
summary := "Domains 1 Hostnames 1"
|
||||
summary := "Domains 2 Hostnames 1"
|
||||
|
||||
if !strings.Contains(strings.Join(strings.Fields(text), " "), summary) {
|
||||
t.Errorf("summary bar does not say %q", summary)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDashboardMarksDomainThatDoesNotExist checks that the Domains
|
||||
// section says a domain that does not exist does not exist, and does
|
||||
// not say so of a domain that exists.
|
||||
func TestDashboardMarksDomainThatDoesNotExist(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
page := get(t, newHandlersWithFailures(t).HandleDashboard())
|
||||
domains := dashboardSection(t, page, "Domains")
|
||||
|
||||
if !strings.Contains(dashboardRow(t, domains, missingDomain), "does not exist") {
|
||||
t.Errorf("row of %s does not say it does not exist", missingDomain)
|
||||
}
|
||||
|
||||
if strings.Contains(dashboardRow(t, domains, testDomain), "does not exist") {
|
||||
t.Errorf("row of %s says it does not exist", testDomain)
|
||||
}
|
||||
}
|
||||
|
||||
// rowCells returns the text of each cell of a dashboard table row
|
||||
// whose cells start with tag, "<th" or "<td".
|
||||
func rowCells(row string, tag string) []string {
|
||||
|
||||
@@ -10,10 +10,12 @@ import (
|
||||
|
||||
// statusDomainInfo holds status information for a monitored domain.
|
||||
// RecordsByNameserver holds the domain's own records, in the form a
|
||||
// hostname's Nameservers holds the hostname's.
|
||||
// hostname's Nameservers holds the hostname's. NXDomain is true when
|
||||
// the domain's parent zone's servers answered that it does not exist.
|
||||
type statusDomainInfo struct {
|
||||
Nameservers []string `json:"nameservers"`
|
||||
RecordsByNameserver map[string]*statusHostnameNSInfo `json:"recordsByNameserver"`
|
||||
NXDomain bool `json:"nxdomain"`
|
||||
LastChecked time.Time `json:"lastChecked"`
|
||||
}
|
||||
|
||||
@@ -155,6 +157,7 @@ func buildDomains(
|
||||
resp.Domains[name] = &statusDomainInfo{
|
||||
Nameservers: ns,
|
||||
RecordsByNameserver: records,
|
||||
NXDomain: ds.NXDomain,
|
||||
LastChecked: ds.LastChecked,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,10 @@ import (
|
||||
// failed. example.net is an apex domain, whose own records are saved
|
||||
// with the hostnames' records, as the watcher saves them. Both names
|
||||
// resolve to domainAddress, whose port 443 entry lists them.
|
||||
// missingDomain is an apex domain whose parent zone's servers answered
|
||||
// that it does not exist, saved with no nameservers and no records.
|
||||
const (
|
||||
missingDomain = "does-not-exist.example"
|
||||
testHostname = "www.example.com"
|
||||
answeringNS = "ns1.example.com."
|
||||
failedNS = "ns2.example.com."
|
||||
@@ -132,6 +135,12 @@ func setTestState(st *state.State) {
|
||||
Hostnames: []string{testDomain, testHostname},
|
||||
LastChecked: now,
|
||||
})
|
||||
|
||||
st.SetDomainState(missingDomain, &state.DomainState{
|
||||
Nameservers: []string{},
|
||||
NXDomain: true,
|
||||
LastChecked: now,
|
||||
})
|
||||
}
|
||||
|
||||
// get serves one GET request to handler and returns the response body.
|
||||
@@ -233,6 +242,39 @@ func TestStatusGivesDomainRecordsUnderTheDomain(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestStatusMarksDomainThatDoesNotExist checks that /api/v1/status sets
|
||||
// nxdomain for a domain that does not exist, with no nameservers or
|
||||
// records, and not for a domain that exists.
|
||||
func TestStatusMarksDomainThatDoesNotExist(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
body := get(t, newHandlersWithFailures(t).HandleStatus())
|
||||
|
||||
var resp struct {
|
||||
Domains map[string]struct {
|
||||
Nameservers []string `json:"nameservers"`
|
||||
RecordsByNameserver map[string]any `json:"recordsByNameserver"`
|
||||
NXDomain bool `json:"nxdomain"`
|
||||
} `json:"domains"`
|
||||
}
|
||||
|
||||
err := json.Unmarshal([]byte(body), &resp)
|
||||
if err != nil {
|
||||
t.Fatalf("decoding response: %v", err)
|
||||
}
|
||||
|
||||
missing := resp.Domains[missingDomain]
|
||||
if !missing.NXDomain || len(missing.Nameservers) != 0 ||
|
||||
len(missing.RecordsByNameserver) != 0 {
|
||||
t.Errorf("domain %s = %+v, want nxdomain and nothing else",
|
||||
missingDomain, missing)
|
||||
}
|
||||
|
||||
if resp.Domains[testDomain].NXDomain {
|
||||
t.Errorf("domain %s has nxdomain set", testDomain)
|
||||
}
|
||||
}
|
||||
|
||||
// TestStatusPortsTellDomainsFromHostnames checks that a port entry in
|
||||
// /api/v1/status lists an apex domain in domains and a hostname in
|
||||
// hostnames when both resolve to its address.
|
||||
|
||||
@@ -84,7 +84,11 @@
|
||||
{{ $name }}
|
||||
</td>
|
||||
<td class="py-2 px-3 text-slate-400 break-all">
|
||||
{{ if $ds.NXDomain }}
|
||||
<span class="text-red-400">does not exist</span>
|
||||
{{ else }}
|
||||
{{ joinStrings $ds.Nameservers ", " }}
|
||||
{{ end }}
|
||||
</td>
|
||||
<td class="py-2 px-3 text-slate-500 whitespace-nowrap">
|
||||
{{ relTime $ds.LastChecked }}
|
||||
|
||||
Reference in New Issue
Block a user