repo init

This commit is contained in:
ayurishchev committed 2026-08-21 07:34:45 +03:00
commit 7e44db87b2
48 files changed
+5346

No files matched your search

+36
View File
@@ -0,0 +1,36 @@
package checkrunner
import (
"context"
"fmt"
"net/http"
"time"
)
// HTTPS performs a GET against target (expected to be a full URL, e.g.
// https://github.com) and reports success for any response with status
// < 400. It never follows the check to a different host on a check-type
// boundary — redirects within the same request are followed by the
// standard http.Client default policy, which is what we want for
// reachability checks.
func HTTPS(target string, timeout time.Duration) func(ctx context.Context) Result {
return run("https", target, func(ctx context.Context) error {
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, target, nil)
if err != nil {
return fmt.Errorf("build request: %w", err)
}
client := &http.Client{Timeout: timeout}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
return fmt.Errorf("unexpected status %d", resp.StatusCode)
}
return nil
})
}
+82
View File
@@ -0,0 +1,82 @@
package checkrunner
import (
"context"
"fmt"
"net"
"os"
"time"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
)
// ICMPEcho sends up to `count` ICMP echo requests to host and reports
// success if at least one echo reply is received before timeout. Uses a
// raw ICMP socket (ip4:icmp), which requires either running as root or
// (on Linux, as deployed here) the CAP_NET_RAW capability — see the
// validator-agent and prober systemd units.
func ICMPEcho(host string, count int, timeout time.Duration) func(ctx context.Context) Result {
return run("icmp", host, func(ctx context.Context) error {
conn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
if err != nil {
return fmt.Errorf("open icmp socket (needs CAP_NET_RAW or root): %w", err)
}
defer conn.Close()
dst, err := net.ResolveIPAddr("ip4", host)
if err != nil {
return fmt.Errorf("resolve %s: %w", host, err)
}
id := os.Getpid() & 0xffff
var lastErr error
for seq := 1; seq <= count; seq++ {
if err := ctx.Err(); err != nil {
return err
}
msg := icmp.Message{
Type: ipv4.ICMPTypeEcho,
Code: 0,
Body: &icmp.Echo{
ID: id,
Seq: seq,
Data: []byte("cloud-ip-validator"),
},
}
wb, err := msg.Marshal(nil)
if err != nil {
return fmt.Errorf("marshal echo request: %w", err)
}
if _, err := conn.WriteTo(wb, dst); err != nil {
lastErr = fmt.Errorf("write echo request: %w", err)
continue
}
perAttempt := timeout / time.Duration(count)
if perAttempt <= 0 {
perAttempt = timeout
}
conn.SetReadDeadline(time.Now().Add(perAttempt))
rb := make([]byte, 1500)
n, _, err := conn.ReadFrom(rb)
if err != nil {
lastErr = fmt.Errorf("read echo reply: %w", err)
continue
}
rm, err := icmp.ParseMessage(1 /* protocolICMP */, rb[:n])
if err != nil {
lastErr = fmt.Errorf("parse reply: %w", err)
continue
}
if rm.Type == ipv4.ICMPTypeEchoReply {
return nil
}
lastErr = fmt.Errorf("unexpected icmp type %v", rm.Type)
}
if lastErr == nil {
lastErr = fmt.Errorf("no reply received")
}
return lastErr
})
}
+51
View File
@@ -0,0 +1,51 @@
package checkrunner
import (
"context"
"fmt"
"net"
"strconv"
"time"
)
// TCPConnect reports success if a TCP handshake against host:port
// completes within timeout. This is the primitive behind the prober's
// per-port inbound reachability checks (22/80/443/8080).
func TCPConnect(host string, port int, timeout time.Duration) func(ctx context.Context) Result {
target := net.JoinHostPort(host, strconv.Itoa(port))
checkType := "tcp-" + strconv.Itoa(port)
return run(checkType, target, func(ctx context.Context) error {
d := net.Dialer{Timeout: timeout}
conn, err := d.DialContext(ctx, "tcp", target)
if err != nil {
return err
}
return conn.Close()
})
}
// SSHBanner performs a TCP connect to host:22 and additionally verifies the
// remote sends an "SSH-2.0-" banner, without performing any auth handshake.
// Used for the optional ssh check type.
func SSHBanner(host string, timeout time.Duration) func(ctx context.Context) Result {
target := net.JoinHostPort(host, "22")
return run("ssh", target, func(ctx context.Context) error {
d := net.Dialer{Timeout: timeout}
conn, err := d.DialContext(ctx, "tcp", target)
if err != nil {
return err
}
defer conn.Close()
conn.SetReadDeadline(time.Now().Add(timeout))
buf := make([]byte, 8)
n, err := conn.Read(buf)
if err != nil {
return fmt.Errorf("read banner: %w", err)
}
if n < 8 || string(buf[:8]) != "SSH-2.0-" {
return fmt.Errorf("unexpected banner prefix %q", string(buf[:n]))
}
return nil
})
}
+44
View File
@@ -0,0 +1,44 @@
// Package checkrunner implements the actual network probes (HTTPS, TCP
// connect, ICMP echo, SSH banner) shared by both the validator-agent
// (outbound/egress checks) and the prober (inbound/reachability checks).
// The two binaries use the same primitives against different targets and
// in different directions, but never share process state.
package checkrunner
import (
"context"
"time"
)
// Result is the outcome of a single check, in a form that maps directly
// onto a db.Check row (minus the fields the caller already knows: ip_id,
// attempt_number, validator_id, source).
type Result struct {
CheckType string
Target string
Success bool
LatencyMS int64
Detail string
CheckedAt time.Time
}
func run(checkType, target string, fn func(ctx context.Context) error) func(ctx context.Context) Result {
return func(ctx context.Context) Result {
start := time.Now()
err := fn(ctx)
latency := time.Since(start).Milliseconds()
res := Result{
CheckType: checkType,
Target: target,
Success: err == nil,
LatencyMS: latency,
CheckedAt: time.Now().UTC(),
}
if err != nil {
res.Detail = err.Error()
} else {
res.Detail = "ok"
}
return res
}
}