checker-xmpp/checker/rules_reachable.go

94 lines
3 KiB
Go

package checker
import (
"context"
sdk "git.happydns.org/checker-sdk-go/checker"
)
// c2sReachableRule verifies that at least one client-to-server endpoint
// is reachable (TCP + TLS) and that no discovered c2s endpoint is down.
type c2sReachableRule struct{}
func (r *c2sReachableRule) Name() string { return "xmpp.c2s_reachable" }
func (r *c2sReachableRule) Description() string {
return "Verifies that at least one client-to-server endpoint accepts TCP and completes TLS."
}
func (r *c2sReachableRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) []sdk.CheckState {
return evaluateReachable(ctx, obs, opts, ModeClient)
}
// s2sReachableRule is the s2s counterpart.
type s2sReachableRule struct{}
func (r *s2sReachableRule) Name() string { return "xmpp.s2s_reachable" }
func (r *s2sReachableRule) Description() string {
return "Verifies that at least one server-to-server endpoint accepts TCP and completes TLS."
}
func (r *s2sReachableRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) []sdk.CheckState {
return evaluateReachable(ctx, obs, opts, ModeServer)
}
func evaluateReachable(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions, mode XMPPMode) []sdk.CheckState {
data, errSt := loadXMPPData(ctx, obs)
if errSt != nil {
return []sdk.CheckState{*errSt}
}
wantC2S, wantS2S := wantsFromOpts(opts)
if mode == ModeClient && !wantC2S {
return []sdk.CheckState{notTestedState("xmpp.c2s_reachable.skipped", "c2s not in scope for the selected mode.")}
}
if mode == ModeServer && !wantS2S {
return []sdk.CheckState{notTestedState("xmpp.s2s_reachable.skipped", "s2s not in scope for the selected mode.")}
}
// Per-endpoint TCP unreachable states for this mode.
var states []sdk.CheckState
anyForMode := false
for _, ep := range data.Endpoints {
if ep.Mode != mode {
continue
}
anyForMode = true
if !ep.TCPConnected && ep.Error != "" {
states = append(states, sdk.CheckState{
Status: sdk.StatusWarn,
Message: "Cannot reach " + ep.Address + ": " + ep.Error + ".",
Code: CodeTCPUnreachable,
Subject: ep.Address,
Meta: map[string]any{"fix": "Verify firewall rules and that the XMPP server is listening on this address."},
})
}
}
if !anyForMode {
return []sdk.CheckState{{
Status: sdk.StatusCrit,
Message: "No " + string(mode) + " endpoint discovered to probe.",
Code: CodeNoSRV,
}}
}
working := data.Coverage.WorkingC2S
if mode == ModeServer {
working = data.Coverage.WorkingS2S
}
if !working {
states = append(states, sdk.CheckState{
Status: sdk.StatusCrit,
Message: "No working " + string(mode) + " endpoint (TCP + TLS).",
Code: CodeAllEndpointsDown,
})
}
if len(states) == 0 {
code := "xmpp.c2s_reachable.ok"
if mode == ModeServer {
code = "xmpp.s2s_reachable.ok"
}
return []sdk.CheckState{passState(code, "At least one "+string(mode)+" endpoint is reachable and completes TLS.")}
}
return states
}