checker: add RuleName field to CheckState instead of overloading Code
Rules can now set Code freely without the server clobbering it; the originating rule is reported separately via RuleName.
This commit is contained in:
parent
199c7dea3f
commit
c9ee6655ca
4 changed files with 57 additions and 13 deletions
|
|
@ -302,11 +302,12 @@ var checkResultTemplate = template.Must(template.New("result").Funcs(templateFun
|
|||
{{if .States}}
|
||||
<h2>Check states</h2>
|
||||
<table>
|
||||
<thead><tr><th>Status</th><th>Code</th><th>Subject</th><th>Message</th></tr></thead>
|
||||
<thead><tr><th>Status</th><th>Rule</th><th>Code</th><th>Subject</th><th>Message</th></tr></thead>
|
||||
<tbody>
|
||||
{{range .States}}
|
||||
<tr>
|
||||
<td><span class="badge {{statusClass .Status}}">{{statusString .Status}}</span></td>
|
||||
<td>{{.RuleName}}</td>
|
||||
<td>{{.Code}}</td>
|
||||
<td>{{.Subject}}</td>
|
||||
<td>{{.Message}}</td>
|
||||
|
|
|
|||
|
|
@ -312,9 +312,7 @@ func (s *Server) evaluateRules(ctx context.Context, obs ObservationGetter, opts
|
|||
}}
|
||||
}
|
||||
for _, state := range ruleStates {
|
||||
if state.Code == "" {
|
||||
state.Code = rule.Name()
|
||||
}
|
||||
state.RuleName = rule.Name()
|
||||
states = append(states, state)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,6 +69,18 @@ func (r *dummyRule) Evaluate(ctx context.Context, obs ObservationGetter, opts Ch
|
|||
return []CheckState{{Status: StatusOK, Message: r.name + " passed"}}
|
||||
}
|
||||
|
||||
// codedRule emits a CheckState with a pre-set Code, to verify the server
|
||||
// stamps RuleName without clobbering rule-provided codes.
|
||||
type codedRule struct {
|
||||
name, code string
|
||||
}
|
||||
|
||||
func (r *codedRule) Name() string { return r.name }
|
||||
func (r *codedRule) Description() string { return "" }
|
||||
func (r *codedRule) Evaluate(ctx context.Context, obs ObservationGetter, opts CheckerOptions) []CheckState {
|
||||
return []CheckState{{Status: StatusWarn, Code: r.code, Message: "coded finding"}}
|
||||
}
|
||||
|
||||
// --- helpers ---
|
||||
|
||||
func newTestServer(p *testProvider) *Server {
|
||||
|
|
@ -349,8 +361,11 @@ func TestServer_Evaluate(t *testing.T) {
|
|||
if len(resp.States) != 2 {
|
||||
t.Fatalf("evaluate states = %d, want 2", len(resp.States))
|
||||
}
|
||||
if resp.States[0].Code != "rule1" {
|
||||
t.Errorf("evaluate state[0].Code = %q, want \"rule1\"", resp.States[0].Code)
|
||||
if resp.States[0].RuleName != "rule1" {
|
||||
t.Errorf("evaluate state[0].RuleName = %q, want \"rule1\"", resp.States[0].RuleName)
|
||||
}
|
||||
if resp.States[0].Code != "" {
|
||||
t.Errorf("evaluate state[0].Code = %q, want empty (rule did not set one)", resp.States[0].Code)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -379,8 +394,37 @@ func TestServer_Evaluate_DisabledRule(t *testing.T) {
|
|||
if len(resp.States) != 1 {
|
||||
t.Fatalf("evaluate with disabled rule: states = %d, want 1", len(resp.States))
|
||||
}
|
||||
if resp.States[0].Code != "rule2" {
|
||||
t.Errorf("remaining state code = %q, want \"rule2\"", resp.States[0].Code)
|
||||
if resp.States[0].RuleName != "rule2" {
|
||||
t.Errorf("remaining state rule name = %q, want \"rule2\"", resp.States[0].RuleName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_Evaluate_RulePreservesCode(t *testing.T) {
|
||||
def := &CheckerDefinition{
|
||||
ID: "test-checker",
|
||||
Rules: []CheckRule{
|
||||
&codedRule{name: "ruleA", code: "too_many_lookups"},
|
||||
},
|
||||
}
|
||||
p := &testProvider{key: "test", definition: def}
|
||||
srv := newTestServer(p)
|
||||
|
||||
rec := doRequest(srv.Handler(), "POST", "/evaluate", ExternalEvaluateRequest{
|
||||
Observations: map[ObservationKey]json.RawMessage{"test": json.RawMessage(`{}`)},
|
||||
}, nil)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("POST /evaluate = %d, want %d", rec.Code, http.StatusOK)
|
||||
}
|
||||
var resp ExternalEvaluateResponse
|
||||
json.NewDecoder(rec.Body).Decode(&resp)
|
||||
if len(resp.States) != 1 {
|
||||
t.Fatalf("states = %d, want 1", len(resp.States))
|
||||
}
|
||||
if resp.States[0].RuleName != "ruleA" {
|
||||
t.Errorf("state.RuleName = %q, want \"ruleA\"", resp.States[0].RuleName)
|
||||
}
|
||||
if resp.States[0].Code != "too_many_lookups" {
|
||||
t.Errorf("state.Code = %q, want \"too_many_lookups\" (rule-set code must be preserved)", resp.States[0].Code)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -187,11 +187,12 @@ func (s Status) String() string {
|
|||
// (a hostname, a record key, a serial, …). Leave Subject empty for rules
|
||||
// that produce a single, global result.
|
||||
type CheckState struct {
|
||||
Status Status `json:"status"`
|
||||
Message string `json:"message"`
|
||||
Code string `json:"code,omitempty"`
|
||||
Subject string `json:"subject,omitempty"`
|
||||
Meta map[string]any `json:"meta,omitempty"`
|
||||
Status Status `json:"status"`
|
||||
Message string `json:"message"`
|
||||
RuleName string `json:"rule,omitempty"`
|
||||
Code string `json:"code,omitempty"`
|
||||
Subject string `json:"subject,omitempty"`
|
||||
Meta map[string]any `json:"meta,omitempty"`
|
||||
}
|
||||
|
||||
// CheckMetric represents a single metric produced by a check.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue