spb/internetpressure
Public
TypeScript 36.3%
Python 31.8%
Go 18%
JavaScript 9.8%
Shell 1.9%
SQL 1.4%
CSS 0.5%
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