Initial commit
This commit is contained in:
commit
5ffc3ab4df
19 changed files with 2095 additions and 0 deletions
145
checker/rule.go
Normal file
145
checker/rule.go
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
package checker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
sdk "git.happydns.org/checker-sdk-go/checker"
|
||||
)
|
||||
|
||||
func NewSectionRule(section, label string, defaultMin float64) sdk.CheckRule {
|
||||
return §ionRule{section: section, label: label, defaultMin: defaultMin}
|
||||
}
|
||||
|
||||
type sectionRule struct {
|
||||
section string
|
||||
label string
|
||||
defaultMin float64
|
||||
}
|
||||
|
||||
func (r *sectionRule) Name() string { return "happydeliver.score." + r.section }
|
||||
|
||||
func (r *sectionRule) Description() string {
|
||||
return fmt.Sprintf("Verify happyDeliver's %s score is above the configured minimum.", r.label)
|
||||
}
|
||||
|
||||
func (r *sectionRule) optionID() string { return "min_score_" + r.section }
|
||||
|
||||
func (r *sectionRule) Options() sdk.CheckerOptionsDocumentation {
|
||||
return sdk.CheckerOptionsDocumentation{
|
||||
UserOpts: []sdk.CheckerOptionDocumentation{
|
||||
{
|
||||
Id: r.optionID(),
|
||||
Type: "number",
|
||||
Label: fmt.Sprintf("Minimum %s score", r.label),
|
||||
Description: fmt.Sprintf("Minimum acceptable score (0-100) for the %s section.", r.label),
|
||||
Default: r.defaultMin,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *sectionRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) []sdk.CheckState {
|
||||
var data HappyDeliverData
|
||||
if err := obs.Get(ctx, ObservationKeyHappyDeliver, &data); err != nil {
|
||||
return []sdk.CheckState{{
|
||||
Status: sdk.StatusError,
|
||||
Message: fmt.Sprintf("Failed to read happyDeliver observation: %v", err),
|
||||
Code: "happydeliver.observation.error",
|
||||
}}
|
||||
}
|
||||
if data.Phase != "ok" || data.Scores == nil {
|
||||
return []sdk.CheckState{{
|
||||
Status: sdk.StatusInfo,
|
||||
Message: "No happyDeliver report available yet",
|
||||
Code: "happydeliver.score.unavailable",
|
||||
Subject: r.section,
|
||||
}}
|
||||
}
|
||||
score, ok := data.Scores[r.section]
|
||||
if !ok {
|
||||
return []sdk.CheckState{{
|
||||
Status: sdk.StatusInfo,
|
||||
Message: fmt.Sprintf("happyDeliver report has no %s score", r.label),
|
||||
Code: "happydeliver.score.missing",
|
||||
Subject: r.section,
|
||||
}}
|
||||
}
|
||||
threshold := sdk.GetFloatOption(opts, r.optionID(), r.defaultMin)
|
||||
status := sdk.StatusOK
|
||||
if float64(score) < threshold {
|
||||
status = sdk.StatusCrit
|
||||
}
|
||||
return []sdk.CheckState{{
|
||||
Status: status,
|
||||
Message: fmt.Sprintf("%s score: %d (min %.0f, grade %s)", r.label, score, threshold, data.Grades[r.section]),
|
||||
Code: "happydeliver.score." + r.section,
|
||||
Subject: r.section,
|
||||
RuleName: r.Name(),
|
||||
Meta: map[string]any{
|
||||
"score": score,
|
||||
"grade": data.Grades[r.section],
|
||||
"min": threshold,
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
||||
// Surfaces lifecycle failures in the UI when no per-section scores exist yet.
|
||||
func NewLifecycleRule() sdk.CheckRule { return &lifecycleRule{} }
|
||||
|
||||
type lifecycleRule struct{}
|
||||
|
||||
func (r *lifecycleRule) Name() string { return "happydeliver.lifecycle" }
|
||||
|
||||
func (r *lifecycleRule) Description() string {
|
||||
return "Reports happyDeliver lifecycle errors (allocation, send, timeout, fetch)."
|
||||
}
|
||||
|
||||
func (r *lifecycleRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) []sdk.CheckState {
|
||||
var data HappyDeliverData
|
||||
if err := obs.Get(ctx, ObservationKeyHappyDeliver, &data); err != nil {
|
||||
return []sdk.CheckState{{
|
||||
Status: sdk.StatusError,
|
||||
Message: fmt.Sprintf("Failed to read happyDeliver observation: %v", err),
|
||||
Code: "happydeliver.observation.error",
|
||||
}}
|
||||
}
|
||||
switch data.Phase {
|
||||
case "ok":
|
||||
return []sdk.CheckState{{
|
||||
Status: sdk.StatusOK,
|
||||
Message: fmt.Sprintf("Message analysed in %.1fs", data.LatencySeconds),
|
||||
Code: "happydeliver.lifecycle.ok",
|
||||
}}
|
||||
case "allocate":
|
||||
return []sdk.CheckState{{
|
||||
Status: sdk.StatusCrit,
|
||||
Message: "Failed to allocate a happyDeliver test address: " + data.Error,
|
||||
Code: "happydeliver.api.unavailable",
|
||||
}}
|
||||
case "send":
|
||||
return []sdk.CheckState{{
|
||||
Status: sdk.StatusCrit,
|
||||
Message: "Failed to send the test email: " + data.Error,
|
||||
Code: "happydeliver.send.failed",
|
||||
}}
|
||||
case "timeout":
|
||||
return []sdk.CheckState{{
|
||||
Status: sdk.StatusWarn,
|
||||
Message: "happyDeliver did not analyse the message before the timeout",
|
||||
Code: "happydeliver.no_message_received",
|
||||
}}
|
||||
case "wait", "fetch", "parse":
|
||||
return []sdk.CheckState{{
|
||||
Status: sdk.StatusCrit,
|
||||
Message: "happyDeliver lifecycle error: " + data.Error,
|
||||
Code: "happydeliver." + data.Phase + ".failed",
|
||||
}}
|
||||
default:
|
||||
return []sdk.CheckState{{
|
||||
Status: sdk.StatusInfo,
|
||||
Message: "happyDeliver run in unknown phase: " + data.Phase,
|
||||
Code: "happydeliver.lifecycle.unknown",
|
||||
}}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue