Say a secret given in plain words, and log words the keeper refuses
The secret-given condition's explanation carried a clock time, which the
plain rule refuses, so the operator read the scope's fallback ("needs a
look") instead of what changed. Its words now carry no time and say the
secret's name as words; a test holds them to the rule through a keeper.
With no test hook set, the keeper logs a refused or missing wording once
per kind and reason, so a fallback is never silent again (hq issue 359).
This commit is contained in:
@@ -27,6 +27,7 @@ package conditions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -135,9 +136,26 @@ func Worded(kind string) bool {
|
||||
|
||||
// Unworded is told of every observation said in borrowed words: its kind has none registered, or the
|
||||
// words it was given break the plain rule. The keeper says it plainly anyway; a test suite sets this to
|
||||
// fail the producer.
|
||||
// fail the producer. Unset, as in the serving controller, it is a line in the log (unworded).
|
||||
var Unworded func(o Observation, why string)
|
||||
|
||||
// loggedUnworded holds each kind and why already logged: once per process, since a condition observed
|
||||
// every minute would otherwise say the same line every minute.
|
||||
var loggedUnworded sync.Map
|
||||
|
||||
// unworded tells Unworded, or else logs once, that an observation is said in borrowed words — so words
|
||||
// that fell back are never silent (hq issue 359: a secret given reached the operator as "needs a look",
|
||||
// and nothing said why).
|
||||
func unworded(o Observation, why string) {
|
||||
if Unworded != nil {
|
||||
Unworded(o, why)
|
||||
return
|
||||
}
|
||||
if _, seen := loggedUnworded.LoadOrStore(o.Kind+"\x00"+why, true); !seen {
|
||||
log.Printf("the condition kind %q is said in the scope's words, not its own: %s", o.Kind, why)
|
||||
}
|
||||
}
|
||||
|
||||
// The plain rule's shapes.
|
||||
var (
|
||||
hexID = regexp.MustCompile(`\b[0-9a-f]{7,40}\b`)
|
||||
@@ -263,14 +281,10 @@ func plainly(o Observation) Observation {
|
||||
// An explanation too long is cut, never refused: what it says first is what matters.
|
||||
w.Explanation = cut(w.Explanation, ExplanationMax-len(Verdict(w.Needs, ""))-1)
|
||||
if from == "" {
|
||||
if Unworded != nil {
|
||||
Unworded(o, "the kind "+o.Kind+" has no plain words")
|
||||
}
|
||||
unworded(o, "the kind "+o.Kind+" has no plain words")
|
||||
w = scopeWords(o)
|
||||
} else if why, ok := PlainWords(w, machines...); !ok {
|
||||
if Unworded != nil {
|
||||
Unworded(o, from+" is not plain: "+why)
|
||||
}
|
||||
unworded(o, from+" is not plain: "+why)
|
||||
// The scope's words, but never a weaker verdict: what needed the operator still does, and its
|
||||
// answers are kept where they are themselves sound.
|
||||
needed, actions := w.Needs != "", soundActions(w.Actions)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package conditions
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -245,3 +248,26 @@ func TestAChangeOfWordsIsSaid(t *testing.T) {
|
||||
t.Fatalf("%+v", said)
|
||||
}
|
||||
}
|
||||
|
||||
// **Words that fall back are never silent** (hq issue 359): with no test listening, the keeper logs which
|
||||
// kind is said in borrowed words and why — once, however often the condition is observed.
|
||||
func TestBorrowedWordsAreLogged(t *testing.T) {
|
||||
k, _, _, _ := keeper(t)
|
||||
before := Unworded
|
||||
Unworded = nil
|
||||
t.Cleanup(func() { Unworded = before })
|
||||
var out bytes.Buffer
|
||||
log.SetOutput(&out)
|
||||
t.Cleanup(func() { log.SetOutput(os.Stderr) })
|
||||
for i := 0; i < 3; i++ {
|
||||
if _, err := k.Observe(t.Context(), Observation{Scope: ScopeMachine, ID: "ace", Kind: "test-clock", Machine: "ace",
|
||||
Severity: Warning, Source: "test", Summary: "s", Headline: "ace was given a secret",
|
||||
Explanation: "It was given at 23:32.", Resolved: "Seen"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if got := out.String(); strings.Count(got, "\n") != 1 || !strings.Contains(got, `"test-clock"`) ||
|
||||
!strings.Contains(got, "a clock time or date") {
|
||||
t.Errorf("the log said %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user