-
Notifications
You must be signed in to change notification settings - Fork 0
fix: security & reliability hardening, agent/proxy config, and coverage gates #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,9 +4,11 @@ import ( | |
| "context" | ||
| "flag" | ||
| "fmt" | ||
| "io" | ||
| "log" | ||
| "os" | ||
| "os/signal" | ||
| "strings" | ||
| "syscall" | ||
| "time" | ||
|
|
||
|
|
@@ -15,35 +17,56 @@ import ( | |
| ) | ||
|
|
||
| func main() { | ||
| server := flag.String("server", "", "Pulse API base URL, e.g. https://status.example.com (required)") | ||
| token := flag.String("token", "", "Bearer token for ingest authentication (required)") | ||
| interval := flag.Int("interval", 30, "Push interval in seconds (default 30)") | ||
| flag.Parse() | ||
| ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) | ||
| defer stop() | ||
| os.Exit(parseAndRun(ctx, os.Args[1:], os.Stderr)) | ||
| } | ||
|
|
||
| if *server == "" || *token == "" { | ||
| fmt.Fprintln(os.Stderr, "pulse-agent: --server and --token are required") | ||
| flag.Usage() | ||
| os.Exit(1) | ||
| // parseAndRun parses flags, resolves the token, validates, and runs the agent. | ||
| // It returns a process exit code so the flag/validation branches are testable | ||
| // without os.Exit. run blocks until ctx is cancelled. | ||
| func parseAndRun(ctx context.Context, args []string, stderr io.Writer) int { | ||
| fs := flag.NewFlagSet("pulse-agent", flag.ContinueOnError) | ||
| fs.SetOutput(stderr) | ||
| server := fs.String("server", "", "Pulse API base URL, e.g. https://status.example.com (required)") | ||
| token := fs.String("token", "", "Bearer token (INSECURE: visible in ps/proc; prefer PULSE_AGENT_TOKEN or --token-file)") | ||
| tokenFile := fs.String("token-file", "", "File to read the bearer token from") | ||
| interval := fs.Int("interval", 30, "Push interval in seconds (default 30)") | ||
| if err := fs.Parse(args); err != nil { | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [major] |
||
| return 2 | ||
| } | ||
|
|
||
| tok, err := resolveToken(*token, *tokenFile) | ||
| if err != nil { | ||
| fmt.Fprintln(stderr, "pulse-agent:", err) | ||
| return 1 | ||
| } | ||
| if *server == "" || tok == "" { | ||
| fmt.Fprintln(stderr, "pulse-agent: --server and a token (PULSE_AGENT_TOKEN, --token-file, or --token) are required") | ||
| return 1 | ||
| } | ||
| if *interval < 1 { | ||
| fmt.Fprintln(os.Stderr, "pulse-agent: --interval must be >= 1") | ||
| os.Exit(1) | ||
| fmt.Fprintln(stderr, "pulse-agent: --interval must be >= 1") | ||
| return 1 | ||
| } | ||
|
|
||
| col := collector.New() | ||
| psh := pusher.New(*server, *token) | ||
| run(ctx, *server, tok, time.Duration(*interval)*time.Second) | ||
| return 0 | ||
| } | ||
|
|
||
| ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) | ||
| defer stop() | ||
| // run pushes a metrics snapshot immediately, then on every interval, until ctx | ||
| // is cancelled. | ||
| func run(ctx context.Context, serverURL, token string, interval time.Duration) { | ||
| col := collector.New() | ||
| psh := pusher.New(serverURL, token) | ||
|
|
||
| log.Printf("pulse-agent starting: server=%s interval=%ds", *server, *interval) | ||
| log.Printf("pulse-agent starting: server=%s interval=%s", serverURL, interval) | ||
|
|
||
| // Push immediately on startup, then on each tick. | ||
| if err := pushOnce(ctx, col, psh); err != nil { | ||
| log.Printf("push error: %v", err) | ||
| } | ||
|
|
||
| ticker := time.NewTicker(time.Duration(*interval) * time.Second) | ||
| ticker := time.NewTicker(interval) | ||
| defer ticker.Stop() | ||
|
|
||
| for { | ||
|
|
@@ -59,6 +82,24 @@ func main() { | |
| } | ||
| } | ||
|
|
||
| // resolveToken picks the bearer token from, in order of preference: | ||
| // PULSE_AGENT_TOKEN env var, --token-file contents, then --token. The env var | ||
| // and file are preferred because a --token flag is visible to any local user | ||
| // via ps(1) and /proc/<pid>/cmdline for the agent's whole lifetime. | ||
| func resolveToken(flagToken, tokenFile string) (string, error) { | ||
| if env := os.Getenv("PULSE_AGENT_TOKEN"); env != "" { | ||
| return strings.TrimSpace(env), nil | ||
| } | ||
| if tokenFile != "" { | ||
| b, err := os.ReadFile(tokenFile) | ||
| if err != nil { | ||
| return "", fmt.Errorf("reading --token-file: %w", err) | ||
| } | ||
| return strings.TrimSpace(string(b)), nil | ||
| } | ||
| return strings.TrimSpace(flagToken), nil | ||
| } | ||
|
|
||
| func pushOnce(ctx context.Context, col *collector.Collector, psh *pusher.Pusher) error { | ||
| m, err := col.Snapshot() | ||
| if err != nil { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[major] The new lint job covers only
api, unlike the Go test matrix.agentandclican accumulate unchecked errors and other lint regressions unnoticed; this PR already changes both. Matrix lint across all three modules (with per-module working directories/config if needed), pin the linter version for reproducibility, and make it blocking once the 38 current errcheck findings are resolved.