diff --git a/checker/collect.go b/checker/collect.go index 2b0fa74..c3892b6 100644 --- a/checker/collect.go +++ b/checker/collect.go @@ -23,9 +23,13 @@ package checker import ( "context" + "crypto/sha256" + "encoding/hex" "encoding/json" "fmt" "net" + "sort" + "strings" "time" probing "github.com/prometheus-community/pro-bing" @@ -96,6 +100,31 @@ func (p *pingProvider) Collect(ctx context.Context, opts sdk.CheckerOptions) (an return data, nil } +// ShareKey implements sdk.ObservationSharer. A ping result depends only on the +// set of target addresses and the probe count, never on which domain or service +// the addresses belong to. Returning a stable key derived from those inputs lets +// the host mutualise a single ICMP probe across every target (of the same user) +// that points at the same address set, instead of re-pinging once per record. +// +// The evaluation thresholds (warningRTT, criticalRTT, …) are intentionally +// excluded: they only affect how the shared observation is judged, not the data +// collected. Inputs that cannot be resolved yield "" so the host falls back to +// the default per-target caching. +func (p *pingProvider) ShareKey(opts sdk.CheckerOptions) (string, error) { + addresses, err := resolveAddresses(opts) + if err != nil { + return "", nil + } + + sorted := append([]string(nil), addresses...) + sort.Strings(sorted) + + count := sdk.GetIntOption(opts, "count", 5) + + h := sha256.Sum256(fmt.Appendf(nil, "%d|%s", count, strings.Join(sorted, ","))) + return "ping:" + hex.EncodeToString(h[:8]), nil +} + // resolveAddresses extracts target IP addresses from the options. func resolveAddresses(opts sdk.CheckerOptions) ([]string, error) { // Direct addresses (from HTTP server). diff --git a/checker/collect_test.go b/checker/collect_test.go index 6a33bc5..e91cf9f 100644 --- a/checker/collect_test.go +++ b/checker/collect_test.go @@ -119,3 +119,59 @@ func TestIpsFromServiceEmpty(t *testing.T) { t.Errorf("expected 0 ips, got %v", ips) } } + +func TestShareKeyStableRegardlessOfOrder(t *testing.T) { + p := &pingProvider{} + a, err := p.ShareKey(sdk.CheckerOptions{"addresses": []string{"1.1.1.1", "2.2.2.2"}}) + if err != nil { + t.Fatal(err) + } + b, err := p.ShareKey(sdk.CheckerOptions{"addresses": []string{"2.2.2.2", "1.1.1.1"}}) + if err != nil { + t.Fatal(err) + } + if a == "" { + t.Fatal("expected a non-empty share key") + } + if a != b { + t.Errorf("share key must not depend on address order: %q != %q", a, b) + } +} + +func TestShareKeyDiffersByAddress(t *testing.T) { + p := &pingProvider{} + a, _ := p.ShareKey(sdk.CheckerOptions{"address": "1.1.1.1"}) + b, _ := p.ShareKey(sdk.CheckerOptions{"address": "2.2.2.2"}) + if a == b { + t.Errorf("different addresses must yield different share keys, both %q", a) + } +} + +func TestShareKeyDiffersByCount(t *testing.T) { + p := &pingProvider{} + a, _ := p.ShareKey(sdk.CheckerOptions{"address": "1.1.1.1", "count": float64(5)}) + b, _ := p.ShareKey(sdk.CheckerOptions{"address": "1.1.1.1", "count": float64(10)}) + if a == b { + t.Errorf("different probe counts must yield different share keys, both %q", a) + } +} + +func TestShareKeyIgnoresEvaluationThresholds(t *testing.T) { + p := &pingProvider{} + a, _ := p.ShareKey(sdk.CheckerOptions{"address": "1.1.1.1", "warningRTT": float64(100)}) + b, _ := p.ShareKey(sdk.CheckerOptions{"address": "1.1.1.1", "warningRTT": float64(250)}) + if a != b { + t.Errorf("evaluation thresholds must not affect the share key: %q != %q", a, b) + } +} + +func TestShareKeyEmptyWhenUnresolvable(t *testing.T) { + p := &pingProvider{} + sk, err := p.ShareKey(sdk.CheckerOptions{}) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + if sk != "" { + t.Errorf("expected empty share key (per-target fallback), got %q", sk) + } +} diff --git a/go.mod b/go.mod index 5714a0b..49e4d9c 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module git.happydns.org/checker-ping go 1.25.0 require ( - git.happydns.org/checker-sdk-go v1.5.0 + git.happydns.org/checker-sdk-go v1.11.0 git.happydns.org/happyDomain v0.7.0 github.com/miekg/dns v1.1.72 github.com/prometheus-community/pro-bing v0.8.0 diff --git a/go.sum b/go.sum index c1ed874..7ffcf8c 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -git.happydns.org/checker-sdk-go v1.5.0 h1:5uD5Cm6xJ+lwnhbJ09iCXGHbYS9zRh+Yh0NeBHkAPBY= -git.happydns.org/checker-sdk-go v1.5.0/go.mod h1:aNAcfYFfbhvH9kJhE0Njp5GX0dQbxdRB0rJ0KvSC5nI= +git.happydns.org/checker-sdk-go v1.11.0 h1:+hs8OpcgvRMAoWyfqxih/Q0KYYUI1R8oFxO4RKioMLk= +git.happydns.org/checker-sdk-go v1.11.0/go.mod h1:aNAcfYFfbhvH9kJhE0Njp5GX0dQbxdRB0rJ0KvSC5nI= git.happydns.org/happyDomain v0.7.0 h1:NV82/NbcSeRm0+IUZqaK3Vu9Ovl5+vv4AigUJZMdwws= git.happydns.org/happyDomain v0.7.0/go.mod h1:5tgkmqFE65kK359rY49V++49wgZ0gco+Gh9X6tbL+bY= github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=