From edeffd05dc62c4c3d747cc41067bf4e1814f300a Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Thu, 16 Apr 2026 08:43:55 +0300 Subject: Add excluded_hosts feature: store in SQLite, expose CLI subcommands Adds an excluded_host table to the SQLite schema and three new CLI subcommands (exclude, unexclude, list-excluded) so operators can mark hosts that are no longer expected to send updates. The IsExcludedHost and LoadExcludedHosts storage helpers are ready for the Prometheus alerting endpoint (task d4). Co-Authored-By: Claude Sonnet 4.6 --- internal/cli/cli.go | 6 +++ internal/cli/cmd_exclude.go | 102 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 internal/cli/cmd_exclude.go (limited to 'internal/cli') diff --git a/internal/cli/cli.go b/internal/cli/cli.go index e791512..8c22d6e 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -30,6 +30,12 @@ func Execute(args []string) error { return runImport(args[1:]) case "query": return runQuery(args[1:]) + case "exclude": + return runExclude(args[1:]) + case "unexclude": + return runUnexclude(args[1:]) + case "list-excluded": + return runListExcluded(args[1:]) case "test": return runTests() default: diff --git a/internal/cli/cmd_exclude.go b/internal/cli/cmd_exclude.go new file mode 100644 index 0000000..92e1b84 --- /dev/null +++ b/internal/cli/cmd_exclude.go @@ -0,0 +1,102 @@ +package cli + +import ( + "context" + "flag" + "fmt" + "os" + "time" + + "codeberg.org/snonux/goprecords/internal/storage" +) + +func runExclude(args []string) error { + fs := flag.NewFlagSet("exclude", flag.ExitOnError) + dbPath := fs.String("db", "goprecords.db", "SQLite database path") + reason := fs.String("reason", "", "Reason for exclusion") + if err := fs.Parse(args); err != nil { + return err + } + if fs.NArg() < 1 { + fmt.Fprintln(os.Stderr, "exclude: hostname required") + fs.Usage() + return fmt.Errorf("missing hostname") + } + host := fs.Arg(0) + ctx := context.Background() + db, err := storage.Open(ctx, *dbPath) + if err != nil { + return fmt.Errorf("open db: %w", err) + } + defer db.Close() + if err := storage.CreateSchema(ctx, db); err != nil { + return fmt.Errorf("schema: %w", err) + } + if err := storage.AddExcludedHost(ctx, db, host, *reason); err != nil { + return err + } + fmt.Fprintf(os.Stderr, "excluded host %q from alerts\n", host) + return nil +} + +func runUnexclude(args []string) error { + fs := flag.NewFlagSet("unexclude", flag.ExitOnError) + dbPath := fs.String("db", "goprecords.db", "SQLite database path") + if err := fs.Parse(args); err != nil { + return err + } + if fs.NArg() < 1 { + fmt.Fprintln(os.Stderr, "unexclude: hostname required") + fs.Usage() + return fmt.Errorf("missing hostname") + } + host := fs.Arg(0) + ctx := context.Background() + db, err := storage.Open(ctx, *dbPath) + if err != nil { + return fmt.Errorf("open db: %w", err) + } + defer db.Close() + if err := storage.CreateSchema(ctx, db); err != nil { + return fmt.Errorf("schema: %w", err) + } + if err := storage.RemoveExcludedHost(ctx, db, host); err != nil { + return err + } + fmt.Fprintf(os.Stderr, "removed host %q from exclusion list\n", host) + return nil +} + +func runListExcluded(args []string) error { + fs := flag.NewFlagSet("list-excluded", flag.ExitOnError) + dbPath := fs.String("db", "goprecords.db", "SQLite database path") + if err := fs.Parse(args); err != nil { + return err + } + ctx := context.Background() + db, err := storage.Open(ctx, *dbPath) + if err != nil { + return fmt.Errorf("open db: %w", err) + } + defer db.Close() + if err := storage.CreateSchema(ctx, db); err != nil { + return fmt.Errorf("schema: %w", err) + } + hosts, err := storage.LoadExcludedHosts(ctx, db) + if err != nil { + return err + } + if len(hosts) == 0 { + fmt.Println("no excluded hosts") + return nil + } + for _, h := range hosts { + t := time.Unix(h.ExcludedAt, 0).UTC().Format("2006-01-02") + if h.Reason != "" { + fmt.Printf("%-40s excluded=%s reason=%s\n", h.Host, t, h.Reason) + } else { + fmt.Printf("%-40s excluded=%s\n", h.Host, t) + } + } + return nil +} -- cgit v1.2.3