docs: health check wording follows the code (rework for #302)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
The README, the comments and TODO.md say the health check gives error only when every lookup in a round failed because no root server gave a usable reply, and the README drops the claim that the other failures are reported as notifications. Model: opus-5-5
This commit is contained in:
@@ -356,15 +356,15 @@ dnswatcher exposes a lightweight HTTP API for operational visibility:
|
||||
| `GET /metrics` | Prometheus metrics, see below |
|
||||
|
||||
The health check, at `/.well-known/healthcheck` and `/health`, follows the most
|
||||
recent round of DNS checks. When no lookup in that round got a reply from a root
|
||||
server, because none of them answered or because the network intercepts DNS
|
||||
queries (see DNS Resolution Strategy), dnswatcher could not reach the DNS at
|
||||
all. The health check then gives `status` `error` and answers
|
||||
recent round of DNS checks. When every lookup in that round failed because no
|
||||
root server gave a usable reply, as when none of them answers or the network
|
||||
intercepts DNS queries (see DNS Resolution Strategy), dnswatcher could not reach
|
||||
the DNS at all. The health check then gives `status` `error` and answers
|
||||
`503 Service Unavailable`, so the image's health check fails and Docker and
|
||||
upaas see the container as unhealthy. Otherwise it gives `ok` and answers
|
||||
`200 OK`, as it also does before the first round finishes. A round in which the
|
||||
root servers answered gives `ok` even when a name's own nameservers do not
|
||||
answer or a domain does not exist; those are reported as notifications.
|
||||
`200 OK`, as it also does before the first round finishes. A round gives `ok`
|
||||
even when a name's own nameservers do not answer or a domain does not exist,
|
||||
since the root servers gave a usable reply to those lookups.
|
||||
|
||||
In `/api/v1/status`, each nameserver entry and certificate entry whose `status`
|
||||
is `error` also has `error`, the reason, as in the state file (see State File
|
||||
|
||||
@@ -19,9 +19,10 @@ milestone: https://git.eeqj.de/sneak/dnswatcher/milestone/7
|
||||
|
||||
# Completed Steps
|
||||
|
||||
- 2026-10-18: the health check answers 503 with `status` `error` when no lookup
|
||||
in the most recent round of DNS checks got a reply from a root server, as on a
|
||||
network that intercepts DNS queries (closes #302).
|
||||
- 2026-10-18: the health check answers 503 with `status` `error` when every
|
||||
lookup in the most recent round of DNS checks failed because no root server
|
||||
gave a usable reply, as on a network that intercepts DNS queries (closes
|
||||
#302).
|
||||
- 2026-10-16: the first reply marked "recursion available" from a root server's
|
||||
address, a refusal included, stops the lookup with "this network intercepts
|
||||
DNS queries", so on a network that sends DNS to a resolver dnswatcher and its
|
||||
|
||||
@@ -15,9 +15,10 @@ import (
|
||||
// four operators mark their replies recursion available, as on a network
|
||||
// that intercepts DNS queries; one that gives no reply is passed over
|
||||
// for the next, and if none replies, no root server answered. Either
|
||||
// way no lookup got a reply from a root server, so the round could not
|
||||
// reach the DNS. TestFirstRunBaseline and TestDomainThatDoesNotExist
|
||||
// check rounds in which the root servers answered.
|
||||
// way every lookup failed because no root server gave a usable reply,
|
||||
// so the round could not reach the DNS. TestFirstRunBaseline and
|
||||
// TestDomainThatDoesNotExist check rounds in which the root servers
|
||||
// gave a usable reply.
|
||||
func TestDNSUnreachableWhenNoRootServerReplies(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -58,7 +59,7 @@ func TestDNSUnreachableWhenNoRootServerReplies(t *testing.T) {
|
||||
|
||||
if !w.DNSUnreachable() {
|
||||
t.Error("DNSUnreachable is false after a round in " +
|
||||
"which no lookup got a reply from a root server")
|
||||
"which no root server gave a usable reply")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
+11
-11
@@ -204,13 +204,13 @@ func (w *Watcher) RunOnce(ctx context.Context) {
|
||||
}
|
||||
|
||||
// DNSUnreachable reports whether the most recent round of DNS checks
|
||||
// could not reach the DNS at all: no lookup in it got a reply from a
|
||||
// root server, because none of them answered or because the network
|
||||
// intercepts DNS queries. A round in which the root servers answered
|
||||
// reached the DNS, even when a name's own nameservers did not answer or
|
||||
// a domain does not exist. It is false until the first round finishes,
|
||||
// and a round that shutdown cut short changes nothing. It may be called
|
||||
// while a round runs.
|
||||
// could not reach the DNS at all: every lookup in it failed because no
|
||||
// root server gave a usable reply, as when none of them answered or the
|
||||
// network intercepts DNS queries. A round reached the DNS even when a
|
||||
// name's own nameservers did not answer or a domain does not exist,
|
||||
// since the root servers gave a usable reply to those lookups. It is
|
||||
// false until the first round finishes, and a round that shutdown cut
|
||||
// short changes nothing. It may be called while a round runs.
|
||||
func (w *Watcher) DNSUnreachable() bool {
|
||||
return w.dnsUnreachable.Load()
|
||||
}
|
||||
@@ -304,10 +304,10 @@ func (w *Watcher) runDNSChecks(ctx context.Context) {
|
||||
}
|
||||
|
||||
// noRootServerReplied reports whether errs, the errors of the lookups in
|
||||
// a round of DNS checks, nil for a lookup that succeeded, show that no
|
||||
// lookup got a reply from a root server: each failed because none of
|
||||
// them answered, or because the network intercepts DNS queries. A round
|
||||
// with no lookups shows nothing.
|
||||
// a round of DNS checks, nil for a lookup that succeeded, show that
|
||||
// every lookup failed because no root server gave a usable reply: each
|
||||
// is ErrNoRootServerAnswered or ErrIntercepted. A round with no lookups
|
||||
// shows nothing.
|
||||
func noRootServerReplied(errs []error) bool {
|
||||
for _, err := range errs {
|
||||
if !errors.Is(err, resolver.ErrNoRootServerAnswered) &&
|
||||
|
||||
Reference in New Issue
Block a user