watcher: send the inconsistency alert once per disagreement (closes #158)
check / check (push) Successful in 1m5s
check / check (push) Successful in 1m5s
detectInconsistencies alerted for neighbouring pairs of nameservers whose records differed, on every DNS check, for as long as they differed. It now also takes the previous hostname state, compares every pair of nameservers, and alerts for a pair that differs unless both were in that state and already differed there, so a nameserver new on a check that answers differently is reported once. The state loaded at startup is the previous state for the first check, so a disagreement saved before a restart is not reported again. The choice of pairs is tested on record data, and the alert through the hostname change detection with the notifier stand-in and no resolver. The README describes the new behaviour. Model: opus-5-5
This commit was merged in pull request #161.
This commit is contained in:
+41
-15
@@ -366,7 +366,7 @@ func (w *Watcher) detectHostnameChanges(
|
||||
) {
|
||||
w.detectRecordChanges(ctx, hostname, prev, current)
|
||||
w.detectNSDisappearances(ctx, hostname, prev, current)
|
||||
w.detectInconsistencies(ctx, hostname, current)
|
||||
w.detectInconsistencies(ctx, hostname, prev, current)
|
||||
}
|
||||
|
||||
func (w *Watcher) detectRecordChanges(
|
||||
@@ -448,22 +448,11 @@ func (w *Watcher) detectNSDisappearances(
|
||||
func (w *Watcher) detectInconsistencies(
|
||||
ctx context.Context,
|
||||
hostname string,
|
||||
prev *state.HostnameState,
|
||||
current map[string]map[string][]string,
|
||||
) {
|
||||
nameservers := make([]string, 0, len(current))
|
||||
for ns := range current {
|
||||
nameservers = append(nameservers, ns)
|
||||
}
|
||||
|
||||
sort.Strings(nameservers)
|
||||
|
||||
for i := range len(nameservers) - 1 {
|
||||
ns1 := nameservers[i]
|
||||
ns2 := nameservers[i+1]
|
||||
|
||||
if recordsEqual(current[ns1], current[ns2]) {
|
||||
continue
|
||||
}
|
||||
for _, pair := range newlyDisagreeingPairs(prev, current) {
|
||||
ns1, ns2 := pair[0], pair[1]
|
||||
|
||||
msg := fmt.Sprintf(
|
||||
"Hostname: %s\n%s: %v\n%s: %v",
|
||||
@@ -481,6 +470,43 @@ func (w *Watcher) detectInconsistencies(
|
||||
}
|
||||
}
|
||||
|
||||
// newlyDisagreeingPairs returns every pair of nameservers whose records
|
||||
// differ in current, in sorted order of name, except pairs where both
|
||||
// nameservers were in prev and already differed there. A nameserver
|
||||
// missing from prev is paired with every nameserver it differs from.
|
||||
func newlyDisagreeingPairs(
|
||||
prev *state.HostnameState,
|
||||
current map[string]map[string][]string,
|
||||
) [][2]string {
|
||||
nameservers := make([]string, 0, len(current))
|
||||
for ns := range current {
|
||||
nameservers = append(nameservers, ns)
|
||||
}
|
||||
|
||||
sort.Strings(nameservers)
|
||||
|
||||
var pairs [][2]string
|
||||
|
||||
for i, ns1 := range nameservers {
|
||||
for _, ns2 := range nameservers[i+1:] {
|
||||
if recordsEqual(current[ns1], current[ns2]) {
|
||||
continue
|
||||
}
|
||||
|
||||
prev1, ok1 := prev.RecordsByNameserver[ns1]
|
||||
prev2, ok2 := prev.RecordsByNameserver[ns2]
|
||||
|
||||
if ok1 && ok2 && !recordsEqual(prev1.Records, prev2.Records) {
|
||||
continue
|
||||
}
|
||||
|
||||
pairs = append(pairs, [2]string{ns1, ns2})
|
||||
}
|
||||
}
|
||||
|
||||
return pairs
|
||||
}
|
||||
|
||||
func (w *Watcher) checkAllPorts(ctx context.Context) {
|
||||
// Phase 1: Build current IP:port → hostname associations
|
||||
// from fresh DNS data.
|
||||
|
||||
Reference in New Issue
Block a user