commit a2ebf17774fcbbb8b7a08b7548e69a689d131338 Author: Pierre-Olivier Mercier Date: Wed Apr 8 01:46:37 2026 +0700 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..94b48b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +checker-ping +*.so diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c306172 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +FROM golang:1.25-alpine AS builder + +ARG CHECKER_VERSION=custom-build + +WORKDIR /src +COPY go.mod go.sum ./ +RUN go mod download +COPY . . +RUN CGO_ENABLED=0 go build -ldflags "-X main.Version=${CHECKER_VERSION}" -o /checker-ping . + +FROM scratch +COPY --from=builder /checker-ping /checker-ping +EXPOSE 8080 +ENTRYPOINT ["/checker-ping"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..70487bb --- /dev/null +++ b/Makefile @@ -0,0 +1,25 @@ +CHECKER_NAME := checker-ping +CHECKER_IMAGE := happydomain/$(CHECKER_NAME) +CHECKER_VERSION ?= custom-build + +CHECKER_SOURCES := main.go $(wildcard checker/*.go) + +GO_LDFLAGS := -X main.Version=$(CHECKER_VERSION) + +.PHONY: all plugin docker clean + +all: $(CHECKER_NAME) + +$(CHECKER_NAME): $(CHECKER_SOURCES) + go build -ldflags "$(GO_LDFLAGS)" -o $@ . + +plugin: $(CHECKER_NAME).so + +$(CHECKER_NAME).so: $(CHECKER_SOURCES) $(wildcard plugin/*.go) + go build -buildmode=plugin -ldflags "$(GO_LDFLAGS)" -o $@ ./plugin/ + +docker: + docker build --build-arg CHECKER_VERSION=$(CHECKER_VERSION) -t $(CHECKER_IMAGE) . + +clean: + rm -f $(CHECKER_NAME) $(CHECKER_NAME).so diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..faf6ef4 --- /dev/null +++ b/NOTICE @@ -0,0 +1,32 @@ +checker-ping +Copyright (c) 2020-2026 happyDomain +Authors: Pierre-Olivier Mercier, et al. + +This product is currently licensed under the GNU Affero General Public +License v3.0 (see LICENSE), because it imports types from the happyDomain +server module which is itself licensed under AGPL-3.0. + +A relicensing to the MIT License is planned once that dependency has been +removed. See README.md for the licensing roadmap. + +------------------------------------------------------------------------------- +Third-party notices +------------------------------------------------------------------------------- + +This product includes software developed as part of the checker-sdk-go +project (https://git.happydns.org/happyDomain/checker-sdk-go), licensed +under the Apache License, Version 2.0: + + checker-sdk-go + Copyright 2020-2026 The happyDomain Authors + + This product includes software developed as part of the happyDomain + project (https://happydomain.org). + + Portions of this code were originally written for the happyDomain + server (licensed under AGPL-3.0 and a commercial license) and are + made available there under the Apache License, Version 2.0 to enable + a permissively licensed ecosystem of checker plugins. + +You may obtain a copy of the Apache License 2.0 at: + http://www.apache.org/licenses/LICENSE-2.0 diff --git a/README.md b/README.md new file mode 100644 index 0000000..8572c87 --- /dev/null +++ b/README.md @@ -0,0 +1,124 @@ +# checker-ping + +ICMP ping checker for [happyDomain](https://www.happydomain.org/). + +Checks reachability, round-trip time, and packet loss for IP addresses associated with a domain's services. + +## Usage + +### Standalone HTTP server + +```bash +# Build and run +make +./checker-ping -listen :8080 + +# With privileged ICMP (requires CAP_NET_RAW or root) +./checker-ping -listen :8080 -privileged +``` + +The server exposes: + +- `GET /health` — health check +- `POST /collect` — collect ping observations (happyDomain external checker protocol) + +### Docker + +```bash +make docker +docker run -p 8080:8080 happydomain/checker-ping + +# With privileged ICMP +docker run --cap-add NET_RAW -p 8080:8080 happydomain/checker-ping -privileged +``` + +### happyDomain plugin + +```bash +make plugin +# produces checker-ping.so, loadable by happyDomain as a Go plugin +``` + +The plugin exposes a `NewCheckerPlugin` symbol returning the checker +definition and observation provider, which happyDomain registers in its +global registries at load time. + +### Versioning + +The binary, plugin, and Docker image embed a version string overridable +at build time: + +```bash +make CHECKER_VERSION=1.2.3 +make plugin CHECKER_VERSION=1.2.3 +make docker CHECKER_VERSION=1.2.3 +``` + +### happyDomain remote endpoint + +Set the `endpoint` admin option for the ping checker to the URL of the running checker-ping server (e.g., `http://checker-ping:8080`). happyDomain will delegate observation collection to this endpoint. + +## Protocol + +### POST /collect + +Request: +```json +{ + "key": "ping", + "target": {"userId": "...", "domainId": "..."}, + "options": { + "addresses": ["1.2.3.4", "2001:db8::1"], + "count": 5 + } +} +``` + +Response: +```json +{ + "data": { + "targets": [ + { + "address": "1.2.3.4", + "rtt_min": 1.2, + "rtt_avg": 3.4, + "rtt_max": 5.6, + "packet_loss": 0, + "sent": 5, + "received": 5 + } + ] + } +} +``` + +## License & licensing roadmap + +This project is currently licensed under the **GNU Affero General Public +License v3.0** (see `LICENSE`), because it still imports +`happydns.ServiceMessage` and `abstract.Server` from the happyDomain +server module (`git.happydns.org/happyDomain/model` and +`git.happydns.org/happyDomain/services/abstract`), which are themselves +distributed under AGPL-3.0 and a commercial license. + +The core checker types (`CheckerOptions`, `CheckerDefinition`, +`ObservationProvider`, `CheckRule`, …) have already been migrated to +[`checker-sdk-go`](https://git.happydns.org/checker-sdk-go); only the +service-message types remain on the AGPL side. + +**Planned relicensing:** as soon as the remaining `ServiceMessage` / +`abstract.Server` dependency has been removed (moved into a dedicated +permissively licensed module), this project will be relicensed under the +**MIT License**, in line with the rest of the happyDomain checker +ecosystem (see `checker-dummy` for the target shape). + +**Contributors notice:** by submitting a contribution to this repository, +you accept that your contribution will be relicensed from AGPL-3.0 to MIT +at the time of the relicensing described above. If you do not agree with +this, please do not submit contributions until the relicensing has taken +place. + +The third-party Apache-2.0 attributions for `checker-sdk-go` are recorded +in `NOTICE` and must accompany any binary or source redistribution of this +project. diff --git a/checker/collect.go b/checker/collect.go new file mode 100644 index 0000000..f6bff1e --- /dev/null +++ b/checker/collect.go @@ -0,0 +1,159 @@ +// This file is part of the happyDomain (R) project. +// Copyright (c) 2020-2026 happyDomain +// Authors: Pierre-Olivier Mercier, et al. +// +// This program is offered under a commercial and under the AGPL license. +// For commercial licensing, contact us at . +// +// For AGPL licensing: +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package checker + +import ( + "context" + "encoding/json" + "fmt" + "net" + "strings" + "time" + + probing "github.com/prometheus-community/pro-bing" + + sdk "git.happydns.org/checker-sdk-go/checker" + happydns "git.happydns.org/happyDomain/model" + "git.happydns.org/happyDomain/services/abstract" +) + +// Collect performs ICMP ping and returns PingData. +// Addresses are resolved from opts: "addresses" ([]string), "address" (string), +// or "service" (*ServiceMessage of type abstract.Server). +func (p *pingProvider) Collect(ctx context.Context, opts sdk.CheckerOptions) (any, error) { + addresses, err := resolveAddresses(opts) + if err != nil { + return nil, err + } + + count := sdk.GetIntOption(opts, "count", 5) + if count < 1 { + count = 1 + } + if count > 20 { + count = 20 + } + + data := &PingData{} + var errs []string + + for _, addr := range addresses { + pinger, err := probing.NewPinger(addr) + if err != nil { + errs = append(errs, fmt.Sprintf("failed to create pinger for %s: %v", addr, err)) + continue + } + + pinger.Count = count + pinger.Timeout = time.Duration(count)*time.Second + 5*time.Second + + if p.Privileged { + pinger.SetPrivileged(true) + } + + if err = pinger.RunWithContext(ctx); err != nil { + errs = append(errs, fmt.Sprintf("ping failed for %s: %v", addr, err)) + continue + } + + stats := pinger.Statistics() + data.Targets = append(data.Targets, PingTargetResult{ + Address: addr, + RTTMin: float64(stats.MinRtt.Microseconds()) / 1000.0, + RTTAvg: float64(stats.AvgRtt.Microseconds()) / 1000.0, + RTTMax: float64(stats.MaxRtt.Microseconds()) / 1000.0, + PacketLoss: stats.PacketLoss, + Sent: stats.PacketsSent, + Received: stats.PacketsRecv, + }) + } + + if len(data.Targets) == 0 { + return nil, fmt.Errorf("all pings failed: %s", strings.Join(errs, "; ")) + } + + return data, nil +} + +// resolveAddresses extracts target IP addresses from the options. +func resolveAddresses(opts sdk.CheckerOptions) ([]string, error) { + // Direct addresses (from HTTP server). + if v, ok := opts["addresses"]; ok { + switch addrs := v.(type) { + case []any: + var result []string + for _, a := range addrs { + if s, ok := a.(string); ok && s != "" { + result = append(result, s) + } + } + if len(result) > 0 { + return result, nil + } + case []string: + if len(addrs) > 0 { + return addrs, nil + } + } + } + + // Single address. + if v, ok := opts["address"]; ok { + if s, ok := v.(string); ok && s != "" { + return []string{s}, nil + } + } + + // From auto-filled service (plugin provider path or HTTP JSON). + if svc, ok := sdk.GetOption[happydns.ServiceMessage](opts, "service"); ok { + if svc.Type != "abstract.Server" { + return nil, fmt.Errorf("service is %s, expected abstract.Server", svc.Type) + } + ips := ipsFromService(&svc) + if len(ips) > 0 { + addrs := make([]string, len(ips)) + for i, ip := range ips { + addrs[i] = ip.String() + } + return addrs, nil + } + return nil, fmt.Errorf("no IP addresses found in the service") + } + + return nil, fmt.Errorf("no addresses provided: set 'addresses', 'address', or 'service' in options") +} + +func ipsFromService(svc *happydns.ServiceMessage) []net.IP { + var server abstract.Server + if err := json.Unmarshal(svc.Service, &server); err != nil { + return nil + } + + var ips []net.IP + if server.A != nil && len(server.A.A) > 0 { + ips = append(ips, server.A.A) + } + if server.AAAA != nil && len(server.AAAA.AAAA) > 0 { + ips = append(ips, server.AAAA.AAAA) + } + return ips +} diff --git a/checker/definition.go b/checker/definition.go new file mode 100644 index 0000000..e7e44fe --- /dev/null +++ b/checker/definition.go @@ -0,0 +1,102 @@ +// This file is part of the happyDomain (R) project. +// Copyright (c) 2020-2026 happyDomain +// Authors: Pierre-Olivier Mercier, et al. +// +// This program is offered under a commercial and under the AGPL license. +// For commercial licensing, contact us at . +// +// For AGPL licensing: +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package checker + +import ( + "time" + + happydns "git.happydns.org/checker-sdk-go/checker" +) + +// Version is the checker version reported in CheckerDefinition.Version. +// +// It defaults to "built-in", which is appropriate when the checker package is +// imported directly (built-in or plugin mode). Standalone binaries (like +// main.go) should override this from their own Version variable at the start +// of main(), which makes it easy for CI to inject a version with a single +// -ldflags "-X main.Version=..." flag instead of targeting the nested +// package path. +var Version = "built-in" + +// Definition returns the CheckerDefinition for the ping checker. +func Definition() *happydns.CheckerDefinition { + return &happydns.CheckerDefinition{ + ID: "ping", + Name: "Ping (ICMP)", + Version: Version, + Availability: happydns.CheckerAvailability{ + ApplyToService: true, + LimitToServices: []string{"abstract.Server"}, + }, + ObservationKeys: []happydns.ObservationKey{ObservationKeyPing}, + Options: happydns.CheckerOptionsDocumentation{ + UserOpts: []happydns.CheckerOptionDocumentation{ + { + Id: "warningRTT", + Type: "number", + Label: "Warning RTT threshold (ms)", + Default: float64(100), + }, + { + Id: "criticalRTT", + Type: "number", + Label: "Critical RTT threshold (ms)", + Default: float64(500), + }, + { + Id: "warningPacketLoss", + Type: "number", + Label: "Warning packet loss threshold (%)", + Default: float64(10), + }, + { + Id: "criticalPacketLoss", + Type: "number", + Label: "Critical packet loss threshold (%)", + Default: float64(50), + }, + { + Id: "count", + Type: "uint", + Label: "Number of pings to send", + Default: float64(5), + }, + }, + ServiceOpts: []happydns.CheckerOptionDocumentation{ + { + Id: "service", + Label: "Service", + AutoFill: happydns.AutoFillService, + }, + }, + }, + Rules: []happydns.CheckRule{ + Rule(), + }, + Interval: &happydns.CheckIntervalSpec{ + Min: 1 * time.Minute, + Max: 1 * time.Hour, + Default: 5 * time.Minute, + }, + HasMetrics: true, + } +} diff --git a/checker/evaluate.go b/checker/evaluate.go new file mode 100644 index 0000000..f07c543 --- /dev/null +++ b/checker/evaluate.go @@ -0,0 +1,89 @@ +// This file is part of the happyDomain (R) project. +// Copyright (c) 2020-2026 happyDomain +// Authors: Pierre-Olivier Mercier, et al. +// +// This program is offered under a commercial and under the AGPL license. +// For commercial licensing, contact us at . +// +// For AGPL licensing: +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package checker + +import ( + "fmt" + "strings" + "time" +) + +// Metrics extracts time-series metrics from ping data. +func Metrics(data *PingData, collectedAt time.Time) []Metric { + var metrics []Metric + for _, t := range data.Targets { + labels := map[string]string{"address": t.Address} + metrics = append(metrics, + Metric{Name: "ping_rtt_avg", Value: t.RTTAvg, Unit: "ms", Labels: labels, Timestamp: collectedAt}, + Metric{Name: "ping_rtt_min", Value: t.RTTMin, Unit: "ms", Labels: labels, Timestamp: collectedAt}, + Metric{Name: "ping_rtt_max", Value: t.RTTMax, Unit: "ms", Labels: labels, Timestamp: collectedAt}, + Metric{Name: "ping_packet_loss", Value: t.PacketLoss, Unit: "%", Labels: labels, Timestamp: collectedAt}, + Metric{Name: "ping_packets_sent", Value: float64(t.Sent), Unit: "count", Labels: labels, Timestamp: collectedAt}, + Metric{Name: "ping_packets_received", Value: float64(t.Received), Unit: "count", Labels: labels, Timestamp: collectedAt}, + ) + } + return metrics +} + +// Metric represents a single time-series metric. +type Metric struct { + Name string `json:"name"` + Value float64 `json:"value"` + Unit string `json:"unit,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + Timestamp time.Time `json:"timestamp"` +} + +// EvaluateResult holds the evaluation outcome. +type EvaluateResult struct { + Status int `json:"status"` + Message string `json:"message"` + Code string `json:"code"` +} + +const ( + StatusOK = 1 + StatusWarn = 3 + StatusCrit = 4 +) + +// Evaluate checks the ping data against the given thresholds. +func Evaluate(data *PingData, warningRTT, criticalRTT, warningPacketLoss, criticalPacketLoss float64) EvaluateResult { + overallStatus := StatusOK + var summaryParts []string + + for _, target := range data.Targets { + if target.PacketLoss >= criticalPacketLoss || target.RTTAvg >= criticalRTT { + overallStatus = StatusCrit + } else if (target.PacketLoss >= warningPacketLoss || target.RTTAvg >= warningRTT) && overallStatus < StatusWarn { + overallStatus = StatusWarn + } + + summaryParts = append(summaryParts, fmt.Sprintf("%s: %.1fms avg, %.0f%% loss", target.Address, target.RTTAvg, target.PacketLoss)) + } + + return EvaluateResult{ + Status: overallStatus, + Message: strings.Join(summaryParts, " | "), + Code: "ping_result", + } +} diff --git a/checker/provider.go b/checker/provider.go new file mode 100644 index 0000000..ab6b7e8 --- /dev/null +++ b/checker/provider.go @@ -0,0 +1,74 @@ +// This file is part of the happyDomain (R) project. +// Copyright (c) 2020-2026 happyDomain +// Authors: Pierre-Olivier Mercier, et al. +// +// This program is offered under a commercial and under the AGPL license. +// For commercial licensing, contact us at . +// +// For AGPL licensing: +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package checker + +import ( + "encoding/json" + "time" + + happydns "git.happydns.org/checker-sdk-go/checker" +) + +// Provider returns a new ping observation provider for local execution. +func Provider() happydns.ObservationProvider { + return &pingProvider{} +} + +// ProviderWithPrivileged returns a provider with privileged ICMP mode enabled. +func ProviderWithPrivileged(privileged bool) happydns.ObservationProvider { + return &pingProvider{Privileged: privileged} +} + +type pingProvider struct { + // Privileged controls whether raw ICMP sockets are used (requires CAP_NET_RAW or root). + Privileged bool +} + +func (p *pingProvider) Key() happydns.ObservationKey { + return ObservationKeyPing +} + +// Definition implements happydns.CheckerDefinitionProvider. +func (p *pingProvider) Definition() *happydns.CheckerDefinition { + return Definition() +} + +// ExtractMetrics implements happydns.CheckerMetricsReporter. +func (p *pingProvider) ExtractMetrics(raw json.RawMessage, collectedAt time.Time) ([]happydns.CheckMetric, error) { + var data PingData + if err := json.Unmarshal(raw, &data); err != nil { + return nil, err + } + + metrics := Metrics(&data, collectedAt) + result := make([]happydns.CheckMetric, len(metrics)) + for i, m := range metrics { + result[i] = happydns.CheckMetric{ + Name: m.Name, + Value: m.Value, + Unit: m.Unit, + Labels: m.Labels, + Timestamp: m.Timestamp, + } + } + return result, nil +} diff --git a/checker/rule.go b/checker/rule.go new file mode 100644 index 0000000..5932cb1 --- /dev/null +++ b/checker/rule.go @@ -0,0 +1,146 @@ +// This file is part of the happyDomain (R) project. +// Copyright (c) 2020-2026 happyDomain +// Authors: Pierre-Olivier Mercier, et al. +// +// This program is offered under a commercial and under the AGPL license. +// For commercial licensing, contact us at . +// +// For AGPL licensing: +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package checker + +import ( + "context" + "fmt" + + sdk "git.happydns.org/checker-sdk-go/checker" +) + +// Rule returns a new ping check rule for local evaluation. +func Rule() sdk.CheckRule { + return &pingRule{} +} + +type pingRule struct{} + +func (r *pingRule) Name() string { return "ping_check" } +func (r *pingRule) Description() string { + return "Checks ICMP ping reachability, round-trip time, and packet loss" +} + +func (r *pingRule) ValidateOptions(opts sdk.CheckerOptions) error { + warningRTT := float64(100) + criticalRTT := float64(500) + warningPacketLoss := float64(10) + criticalPacketLoss := float64(50) + + if v, ok := opts["warningRTT"]; ok { + d, ok := v.(float64) + if !ok { + return fmt.Errorf("warningRTT must be a number") + } + if d <= 0 { + return fmt.Errorf("warningRTT must be positive") + } + warningRTT = d + } + if v, ok := opts["criticalRTT"]; ok { + d, ok := v.(float64) + if !ok { + return fmt.Errorf("criticalRTT must be a number") + } + if d <= 0 { + return fmt.Errorf("criticalRTT must be positive") + } + criticalRTT = d + } + if v, ok := opts["warningPacketLoss"]; ok { + d, ok := v.(float64) + if !ok { + return fmt.Errorf("warningPacketLoss must be a number") + } + if d < 0 || d > 100 { + return fmt.Errorf("warningPacketLoss must be between 0 and 100") + } + warningPacketLoss = d + } + if v, ok := opts["criticalPacketLoss"]; ok { + d, ok := v.(float64) + if !ok { + return fmt.Errorf("criticalPacketLoss must be a number") + } + if d < 0 || d > 100 { + return fmt.Errorf("criticalPacketLoss must be between 0 and 100") + } + criticalPacketLoss = d + } + if v, ok := opts["count"]; ok { + d, ok := v.(float64) + if !ok { + return fmt.Errorf("count must be a number") + } + if d < 1 || d > 20 { + return fmt.Errorf("count must be between 1 and 20") + } + } + + if criticalRTT <= warningRTT { + return fmt.Errorf("criticalRTT (%v) must be greater than warningRTT (%v)", criticalRTT, warningRTT) + } + if criticalPacketLoss <= warningPacketLoss { + return fmt.Errorf("criticalPacketLoss (%v) must be greater than warningPacketLoss (%v)", criticalPacketLoss, warningPacketLoss) + } + + return nil +} + +func (r *pingRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) sdk.CheckState { + var data PingData + if err := obs.Get(ctx, ObservationKeyPing, &data); err != nil { + return sdk.CheckState{ + Status: sdk.StatusError, + Message: fmt.Sprintf("Failed to get ping data: %v", err), + Code: "ping_error", + } + } + + warningRTT := sdk.GetFloatOption(opts, "warningRTT", 100) + criticalRTT := sdk.GetFloatOption(opts, "criticalRTT", 500) + warningPacketLoss := sdk.GetFloatOption(opts, "warningPacketLoss", 10) + criticalPacketLoss := sdk.GetFloatOption(opts, "criticalPacketLoss", 50) + + result := Evaluate(&data, warningRTT, criticalRTT, warningPacketLoss, criticalPacketLoss) + + var status sdk.Status + switch result.Status { + case StatusOK: + status = sdk.StatusOK + case StatusWarn: + status = sdk.StatusWarn + case StatusCrit: + status = sdk.StatusCrit + default: + status = sdk.StatusUnknown + } + + return sdk.CheckState{ + Status: status, + Message: result.Message, + Code: result.Code, + Meta: map[string]any{ + "targets": data.Targets, + }, + } +} diff --git a/checker/types.go b/checker/types.go new file mode 100644 index 0000000..a641976 --- /dev/null +++ b/checker/types.go @@ -0,0 +1,41 @@ +// This file is part of the happyDomain (R) project. +// Copyright (c) 2020-2026 happyDomain +// Authors: Pierre-Olivier Mercier, et al. +// +// This program is offered under a commercial and under the AGPL license. +// For commercial licensing, contact us at . +// +// For AGPL licensing: +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package checker + +// ObservationKeyPing is the observation key for ICMP ping data. +const ObservationKeyPing = "ping" + +// PingData holds the collected ping results for all targets. +type PingData struct { + Targets []PingTargetResult `json:"targets"` +} + +// PingTargetResult contains the ping statistics for a single IP address. +type PingTargetResult struct { + Address string `json:"address"` + RTTMin float64 `json:"rtt_min"` + RTTAvg float64 `json:"rtt_avg"` + RTTMax float64 `json:"rtt_max"` + PacketLoss float64 `json:"packet_loss"` + Sent int `json:"sent"` + Received int `json:"received"` +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..02a7c39 --- /dev/null +++ b/go.mod @@ -0,0 +1,47 @@ +module git.happydns.org/checker-ping + +go 1.25.0 + +require ( + git.happydns.org/happyDomain v0.7.0 + github.com/prometheus-community/pro-bing v0.8.0 +) + +require ( + git.happydns.org/checker-sdk-go v0.0.1 + github.com/bytedance/gopkg v0.1.3 // indirect + github.com/bytedance/sonic v1.15.0 // indirect + github.com/bytedance/sonic/loader v0.5.0 // indirect + github.com/cloudwego/base64x v0.1.6 // indirect + github.com/gabriel-vasile/mimetype v1.4.13 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect + github.com/gin-gonic/gin v1.12.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.30.1 // indirect + github.com/goccy/go-json v0.10.5 // indirect + github.com/goccy/go-yaml v1.19.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/miekg/dns v1.1.72 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/quic-go/qpack v0.6.0 // indirect + github.com/quic-go/quic-go v0.59.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.3.1 // indirect + go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect + golang.org/x/arch v0.24.0 // indirect + golang.org/x/crypto v0.49.0 // indirect + golang.org/x/mod v0.33.0 // indirect + golang.org/x/net v0.51.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/text v0.35.0 // indirect + golang.org/x/tools v0.42.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..4a5626b --- /dev/null +++ b/go.sum @@ -0,0 +1,106 @@ +git.happydns.org/checker-sdk-go v0.0.1 h1:4RxCJr73HWKxjOyU/6NJMO8lXJmH0gMLA68EzTqLbQI= +git.happydns.org/checker-sdk-go v0.0.1/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= +github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= +github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE= +github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k= +github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= +github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= +github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= +github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= +github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8= +github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w= +github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= +github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 h1:9Nu54bhS/H/Kgo2/7xNSUuC5G28VR8ljfrLKU2G4IjU= +github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12/go.mod h1:TBzl5BIHNXfS9+C35ZyJaklL7mLDbgUkcgXzSLa8Tk0= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= +github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus-community/pro-bing v0.8.0 h1:CEY/g1/AgERRDjxw5P32ikcOgmrSuXs7xon7ovx6mNc= +github.com/prometheus-community/pro-bing v0.8.0/go.mod h1:Idyxz8raDO6TgkUN6ByiEGvWJNyQd40kN9ZUeho3lN0= +github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= +github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= +github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= +github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= +github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= +go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE= +go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= +golang.org/x/arch v0.24.0 h1:qlJ3M9upxvFfwRM51tTg3Yl+8CP9vCC1E7vlFpgv99Y= +golang.org/x/arch v0.24.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= +golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= +golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= +golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= +golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= +golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..a28e774 --- /dev/null +++ b/main.go @@ -0,0 +1,54 @@ +// This file is part of the happyDomain (R) project. +// Copyright (c) 2020-2026 happyDomain +// Authors: Pierre-Olivier Mercier, et al. +// +// This program is offered under a commercial and under the AGPL license. +// For commercial licensing, contact us at . +// +// For AGPL licensing: +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package main + +import ( + "flag" + "log" + + ping "git.happydns.org/checker-ping/checker" + sdk "git.happydns.org/checker-sdk-go/checker" +) + +var ( + listenAddr = flag.String("listen", ":8080", "HTTP listen address") + privileged = flag.Bool("privileged", false, "Use privileged ICMP (requires CAP_NET_RAW or root)") +) + +// Version is the standalone binary's version. It defaults to "custom-build" +// and is meant to be overridden by the CI at link time: +// +// go build -ldflags "-X main.Version=1.2.3" . +var Version = "custom-build" + +func main() { + flag.Parse() + + // Propagate the binary version to the checker package so it shows up in + // CheckerDefinition.Version. + ping.Version = Version + + server := sdk.NewServer(ping.ProviderWithPrivileged(*privileged)) + if err := server.ListenAndServe(*listenAddr); err != nil { + log.Fatalf("server error: %v", err) + } +} diff --git a/plugin/plugin.go b/plugin/plugin.go new file mode 100644 index 0000000..4655463 --- /dev/null +++ b/plugin/plugin.go @@ -0,0 +1,26 @@ +// Command plugin is the happyDomain plugin entrypoint for the ping checker. +// +// It is built as a Go plugin (`go build -buildmode=plugin`) and loaded at +// runtime by happyDomain. +package main + +import ( + ping "git.happydns.org/checker-ping/checker" + sdk "git.happydns.org/checker-sdk-go/checker" +) + +// Version is the plugin's version. It defaults to "custom-build" and is +// meant to be overridden by the CI at link time: +// +// go build -buildmode=plugin -ldflags "-X main.Version=1.2.3" -o checker-ping.so ./plugin +var Version = "custom-build" + +// NewCheckerPlugin is the symbol resolved by happyDomain when loading the +// .so file. It returns the checker definition and the observation provider +// that the host will register in its global registries. +func NewCheckerPlugin() (*sdk.CheckerDefinition, sdk.ObservationProvider, error) { + // Propagate the plugin's version to the checker package so it shows up + // in CheckerDefinition.Version. + ping.Version = Version + return ping.Definition(), ping.Provider(), nil +}