Initial commit
This commit is contained in:
commit
2d98ed1b5d
33 changed files with 4644 additions and 0 deletions
123
checker/consensus.go
Normal file
123
checker/consensus.go
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
package checker
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Idempotent: rules and report both call it; both must see the same grouping.
|
||||
func deriveView(data *ResolverPropagationData) {
|
||||
if data == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for key, view := range data.RRsets {
|
||||
// Reset derived fields so repeated calls stay idempotent.
|
||||
view.Groups = nil
|
||||
view.ConsensusSig = ""
|
||||
view.Agreeing = nil
|
||||
view.Dissenting = nil
|
||||
view.MatchesExpected = false
|
||||
|
||||
voteCount := map[string]int{}
|
||||
type group struct {
|
||||
rcode string
|
||||
records []string
|
||||
resolvers []string
|
||||
}
|
||||
groups := map[string]*group{}
|
||||
|
||||
for _, rv := range data.Resolvers {
|
||||
p := rv.Probes[key]
|
||||
if p == nil || p.Error != "" {
|
||||
continue
|
||||
}
|
||||
g := groups[p.Signature]
|
||||
if g == nil {
|
||||
g = &group{rcode: p.Rcode, records: p.Records}
|
||||
groups[p.Signature] = g
|
||||
}
|
||||
g.resolvers = append(g.resolvers, rv.ID)
|
||||
if !rv.Filtered {
|
||||
voteCount[p.Signature]++
|
||||
}
|
||||
}
|
||||
|
||||
// Pick the winning signature, preferring NOERROR responses.
|
||||
var winSig string
|
||||
var winVotes int
|
||||
for sig, g := range groups {
|
||||
if g.rcode != "NOERROR" && winSig != "" {
|
||||
continue
|
||||
}
|
||||
if voteCount[sig] > winVotes {
|
||||
winSig = sig
|
||||
winVotes = voteCount[sig]
|
||||
}
|
||||
}
|
||||
if winSig == "" {
|
||||
for sig := range groups {
|
||||
winSig = sig
|
||||
break
|
||||
}
|
||||
}
|
||||
view.ConsensusSig = winSig
|
||||
|
||||
type gEntry struct {
|
||||
sig string
|
||||
g *group
|
||||
}
|
||||
var entries []gEntry
|
||||
for s, g := range groups {
|
||||
sort.Strings(g.resolvers)
|
||||
entries = append(entries, gEntry{sig: s, g: g})
|
||||
}
|
||||
sort.Slice(entries, func(i, j int) bool {
|
||||
return len(entries[i].g.resolvers) > len(entries[j].g.resolvers)
|
||||
})
|
||||
for _, e := range entries {
|
||||
view.Groups = append(view.Groups, SignatureGroup{
|
||||
Signature: e.sig,
|
||||
Records: e.g.records,
|
||||
Resolvers: e.g.resolvers,
|
||||
Rcode: e.g.rcode,
|
||||
})
|
||||
if e.sig == winSig {
|
||||
view.Agreeing = append(view.Agreeing, e.g.resolvers...)
|
||||
} else {
|
||||
view.Dissenting = append(view.Dissenting, e.g.resolvers...)
|
||||
}
|
||||
}
|
||||
sort.Strings(view.Agreeing)
|
||||
sort.Strings(view.Dissenting)
|
||||
|
||||
if view.Expected != "" {
|
||||
view.MatchesExpected = view.ConsensusSig == view.Expected
|
||||
}
|
||||
}
|
||||
|
||||
// Recompute UnfilteredAgreeing from the consensus we just built.
|
||||
agree := 0
|
||||
for _, rv := range data.Resolvers {
|
||||
if rv.Filtered || !rv.Reachable {
|
||||
continue
|
||||
}
|
||||
ok := true
|
||||
for key, p := range rv.Probes {
|
||||
if p == nil || p.Error != "" {
|
||||
continue
|
||||
}
|
||||
v := data.RRsets[key]
|
||||
if v == nil || v.ConsensusSig == "" {
|
||||
continue
|
||||
}
|
||||
if p.Signature != v.ConsensusSig {
|
||||
ok = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if ok {
|
||||
agree++
|
||||
}
|
||||
}
|
||||
data.Stats.UnfilteredAgreeing = agree
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue