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}}
|
{{if .States}}
|
||||||
<h2>Check states</h2>
|
<h2>Check states</h2>
|
||||||
<table>
|
<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>
|
<tbody>
|
||||||
{{range .States}}
|
{{range .States}}
|
||||||
<tr>
|
<tr>
|
||||||
<td><span class="badge {{statusClass .Status}}">{{statusString .Status}}</span></td>
|
<td><span class="badge {{statusClass .Status}}">{{statusString .Status}}</span></td>
|
||||||
|
<td>{{.RuleName}}</td>
|
||||||
<td>{{.Code}}</td>
|
<td>{{.Code}}</td>
|
||||||
<td>{{.Subject}}</td>
|
<td>{{.Subject}}</td>
|
||||||
<td>{{.Message}}</td>
|
<td>{{.Message}}</td>
|
||||||
|
|
|
||||||
|
|
@ -312,9 +312,7 @@ func (s *Server) evaluateRules(ctx context.Context, obs ObservationGetter, opts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
for _, state := range ruleStates {
|
for _, state := range ruleStates {
|
||||||
if state.Code == "" {
|
state.RuleName = rule.Name()
|
||||||
state.Code = rule.Name()
|
|
||||||
}
|
|
||||||
states = append(states, state)
|
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"}}
|
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 ---
|
// --- helpers ---
|
||||||
|
|
||||||
func newTestServer(p *testProvider) *Server {
|
func newTestServer(p *testProvider) *Server {
|
||||||
|
|
@ -349,8 +361,11 @@ func TestServer_Evaluate(t *testing.T) {
|
||||||
if len(resp.States) != 2 {
|
if len(resp.States) != 2 {
|
||||||
t.Fatalf("evaluate states = %d, want 2", len(resp.States))
|
t.Fatalf("evaluate states = %d, want 2", len(resp.States))
|
||||||
}
|
}
|
||||||
if resp.States[0].Code != "rule1" {
|
if resp.States[0].RuleName != "rule1" {
|
||||||
t.Errorf("evaluate state[0].Code = %q, want \"rule1\"", resp.States[0].Code)
|
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 {
|
if len(resp.States) != 1 {
|
||||||
t.Fatalf("evaluate with disabled rule: states = %d, want 1", len(resp.States))
|
t.Fatalf("evaluate with disabled rule: states = %d, want 1", len(resp.States))
|
||||||
}
|
}
|
||||||
if resp.States[0].Code != "rule2" {
|
if resp.States[0].RuleName != "rule2" {
|
||||||
t.Errorf("remaining state code = %q, want \"rule2\"", resp.States[0].Code)
|
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
|
// (a hostname, a record key, a serial, …). Leave Subject empty for rules
|
||||||
// that produce a single, global result.
|
// that produce a single, global result.
|
||||||
type CheckState struct {
|
type CheckState struct {
|
||||||
Status Status `json:"status"`
|
Status Status `json:"status"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
Code string `json:"code,omitempty"`
|
RuleName string `json:"rule,omitempty"`
|
||||||
Subject string `json:"subject,omitempty"`
|
Code string `json:"code,omitempty"`
|
||||||
Meta map[string]any `json:"meta,omitempty"`
|
Subject string `json:"subject,omitempty"`
|
||||||
|
Meta map[string]any `json:"meta,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckMetric represents a single metric produced by a check.
|
// CheckMetric represents a single metric produced by a check.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue