diff --git a/cmd/tabacco/query.go b/cmd/tabacco/query.go new file mode 100644 index 0000000000000000000000000000000000000000..30a1f290ca2aed5834b75b61900e8238940bf651 --- /dev/null +++ b/cmd/tabacco/query.go @@ -0,0 +1,89 @@ +package main + +import ( + "context" + "encoding/json" + "errors" + "flag" + "log" + "os" + "time" + + "github.com/google/subcommands" + + "git.autistici.org/ai3/tools/tabacco" + mdbc "git.autistici.org/ai3/tools/tabacco/metadb/client" +) + +var rpcTimeout = 120 * time.Second + +type queryCommand struct { + configPath string + host string + numVersions int +} + +func (c *queryCommand) Name() string { return "query" } +func (c *queryCommand) Synopsis() string { return "query the backup metadata database" } +func (c *queryCommand) Usage() string { + return `query [] + Query the backup metadata database. + +` +} + +func (c *queryCommand) SetFlags(f *flag.FlagSet) { + f.StringVar(&c.configPath, "config", "/etc/tabacco/agent.yml", "configuration `file`") + f.StringVar(&c.host, "host", "", "filter by host") + f.IntVar(&c.numVersions, "num-versions", 1, "return the most recent `N` versions") +} + +func (c *queryCommand) buildRequest(f *flag.FlagSet) (*tabacco.FindRequest, error) { + if f.NArg() != 1 { + return nil, errors.New("error: wrong number of arguments") + } + return &tabacco.FindRequest{ + Pattern: f.Arg(0), + Host: c.host, + NumVersions: c.numVersions, + }, nil +} + +func (c *queryCommand) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { + req, err := c.buildRequest(f) + if err != nil { + log.Printf("error in request: %v", err) + return subcommands.ExitUsageError + } + + // Parse configuration and connect to the metadata store. + config, err := tabacco.ReadConfig(c.configPath) + if err != nil { + log.Printf("error reading config: %v", err) + return subcommands.ExitFailure + } + store, err := mdbc.New(config.MetadataStoreBackend) + if err != nil { + log.Printf("error in metadata client config: %v", err) + return subcommands.ExitFailure + } + + // Make the RPC. + rctx, cancel := context.WithTimeout(ctx, rpcTimeout) + defer cancel() + result, err := store.FindAtoms(rctx, req) + + if err != nil { + log.Printf("FindAtoms() error: %v", err) + return subcommands.ExitFailure + } + + data, _ := json.MarshalIndent(result, "", " ") + os.Stdout.Write(data) + + return subcommands.ExitSuccess +} + +func init() { + subcommands.Register(&queryCommand{}, "") +}