checker: reorder Status with negatives for good, JSON as string

Make StatusUnknown the zero value (0) so an uninitialized CheckState
reads as "no signal yet" rather than as healthy. Push StatusOK and
StatusInfo to negative values so the natural int ordering matches
severity ordering: aggregators can simply take max() to compute the
worst status, and Unknown correctly sits above OK/Info but below Warn.

Status now (un)marshals as its string name ("OK", "WARN", ...) so the
wire format is stable across any future renumbering. UnmarshalJSON
still accepts raw ints for backward compatibility with older snapshots
and clients.
This commit is contained in:
nemunaire 2026-04-08 22:16:39 +07:00
commit 6be3578c33

View file

@ -155,15 +155,21 @@ type CheckerOptionsDocumentation struct {
}
// Status represents the result status of a check evaluation.
//
// Numeric ordering is severity ordering: lower = better, higher = worse.
// StatusUnknown is intentionally the zero value, so an uninitialized
// CheckState reads as "no signal yet" rather than as a healthy OK.
// "Good" statuses are negative so that aggregators can simply take the
// max() of a set of statuses to compute the worst one.
type Status int
const (
StatusUnknown Status = iota
StatusOK
StatusInfo
StatusWarn
StatusCrit
StatusError
StatusOK Status = -2
StatusInfo Status = -1
StatusUnknown Status = 0 // zero value: not initialized / no signal yet
StatusWarn Status = 1
StatusCrit Status = 2
StatusError Status = 3
)
// String returns the human-readable name of the status.
@ -186,6 +192,46 @@ func (s Status) String() string {
}
}
// MarshalJSON serializes Status as its string name so the wire format
// is stable across any future reordering of the underlying int values.
func (s Status) MarshalJSON() ([]byte, error) {
return json.Marshal(s.String())
}
// UnmarshalJSON accepts either the string name (preferred) or a raw int
// (for backward compatibility with older clients/snapshots).
func (s *Status) UnmarshalJSON(data []byte) error {
if len(data) > 0 && data[0] == '"' {
var name string
if err := json.Unmarshal(data, &name); err != nil {
return err
}
switch name {
case "OK":
*s = StatusOK
case "INFO":
*s = StatusInfo
case "UNKNOWN", "":
*s = StatusUnknown
case "WARN":
*s = StatusWarn
case "CRIT":
*s = StatusCrit
case "ERROR":
*s = StatusError
default:
return fmt.Errorf("unknown status %q", name)
}
return nil
}
var n int
if err := json.Unmarshal(data, &n); err != nil {
return err
}
*s = Status(n)
return nil
}
// CheckState is the result of evaluating a single rule.
type CheckState struct {
Status Status `json:"status"`