SPB Git forge
15commits 1branches 0releases
29.7 MBsize
maindefault branch
10 days agolast push
TypeScript 36.3% Python 31.8% Go 18% JavaScript 9.8% Shell 1.9% SQL 1.4% CSS 0.5%
4.2 KB · 158 lines go
Raw Blame History
1// Command ip-probe is the InternetPressure.io probe agent.2//3//	ip-probe run [--config FILE]            run the agent (default command)4//	ip-probe once --target HOST [...]       run every check once against an ad-hoc target, print JSON5//	ip-probe check-config [--config FILE]   validate the configuration and print it (key redacted)6//	ip-probe --version | version            print the version7//8// Protocol: docs/PROBE-PROTOCOL.md. Configuration: probe.example.yaml / README.md.9package main1011import (12	"context"13	"encoding/json"14	"errors"15	"flag"16	"fmt"17	"io"18	"log/slog"19	"os"20	"os/signal"21	"runtime"22	"strings"23	"syscall"2425	"internetpressure.io/probe-agent/internal/config"26)2728// version is injected at build time: -ldflags "-X main.version=$(VERSION)".29var version = "dev"3031func main() {32	os.Exit(run(os.Args[1:], os.Stdout, os.Stderr))33}3435func run(args []string, stdout, stderr io.Writer) int {36	cmd := "run"37	if len(args) > 0 && !strings.HasPrefix(args[0], "-") {38		cmd, args = args[0], args[1:]39	}40	for _, a := range args {41		if a == "--version" || a == "-version" || a == "-v" {42			cmd = "version"43		}44	}45	switch cmd {46	case "version", "--version":47		fmt.Fprintf(stdout, "ip-probe %s (%s/%s, %s)\n", version, runtime.GOOS, runtime.GOARCH, runtime.Version())48		return 049	case "check-config":50		return cmdCheckConfig(args, stdout, stderr)51	case "once":52		return cmdOnce(args, stdout, stderr)53	case "run":54		return cmdRun(args, stderr)55	case "help", "-h", "--help":56		usage(stdout)57		return 058	default:59		fmt.Fprintf(stderr, "ip-probe: unknown command %q\n\n", cmd)60		usage(stderr)61		return 262	}63}6465func usage(w io.Writer) {66	fmt.Fprint(w, `Usage:67  ip-probe run [--config FILE]                 run the agent (default)68  ip-probe once --target HOST [--url URL] [--ip IP] [--port N] [--no-traceroute] [--config FILE]69  ip-probe check-config [--config FILE]        validate configuration70  ip-probe --version7172Config file lookup: --config, $IP_PROBE_CONFIG, /etc/internetpressure/probe.yaml, ~/.internetpressure/probe.yaml.73Every key can be overridden with IP_PROBE_<KEY> (e.g. IP_PROBE_PROBE_ID, IP_PROBE_KEY, IP_PROBE_INGEST_URL).74`)75}7677func cmdCheckConfig(args []string, stdout, stderr io.Writer) int {78	fs := flag.NewFlagSet("check-config", flag.ContinueOnError)79	fs.SetOutput(stderr)80	path := fs.String("config", "", "config file")81	if err := fs.Parse(args); err != nil {82		return 283	}84	p := config.ResolvePath(*path)85	if p == "" && *path == "" {86		fmt.Fprintln(stderr, "no config file found (using defaults + IP_PROBE_* env)")87	}88	cfg, err := config.Load(p, true)89	if err != nil {90		fmt.Fprintf(stderr, "config invalid: %v\n", err)91		return 192	}93	enc := json.NewEncoder(stdout)94	enc.SetIndent("", "  ")95	enc.Encode(cfg.Redacted())96	fmt.Fprintf(stdout, "config OK (%s)\n", orDefault(cfg.Source, "defaults/env"))97	return 098}99100func cmdRun(args []string, stderr io.Writer) int {101	fs := flag.NewFlagSet("run", flag.ContinueOnError)102	fs.SetOutput(stderr)103	path := fs.String("config", "", "config file")104	if err := fs.Parse(args); err != nil {105		return 2106	}107	cfg, err := config.Load(config.ResolvePath(*path), true)108	if err != nil {109		fmt.Fprintf(stderr, "ip-probe: %v\n", err)110		return 1111	}112	log := newLogger(cfg.LogLevel, stderr)113	slog.SetDefault(log)114115	ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)116	defer stop()117118	a, err := newAgent(cfg, log)119	if err != nil {120		log.Error("start failed", "err", err)121		return 1122	}123	if err := a.Run(ctx); err != nil && !errors.Is(err, context.Canceled) {124		log.Error("agent stopped with error", "err", err)125		return 1126	}127	return 0128}129130// newLogger builds a slog logger: text on a terminal, JSON otherwise (launchd/systemd logs).131func newLogger(level string, w io.Writer) *slog.Logger {132	var lvl slog.Level133	switch level {134	case "debug":135		lvl = slog.LevelDebug136	case "warn":137		lvl = slog.LevelWarn138	case "error":139		lvl = slog.LevelError140	default:141		lvl = slog.LevelInfo142	}143	opts := &slog.HandlerOptions{Level: lvl}144	if f, ok := w.(*os.File); ok {145		if fi, err := f.Stat(); err == nil && fi.Mode()&os.ModeCharDevice != 0 {146			return slog.New(slog.NewTextHandler(w, opts))147		}148	}149	return slog.New(slog.NewJSONHandler(w, opts))150}151152func orDefault(s, d string) string {153	if s == "" {154		return d155	}156	return s157}158