sysinfo/internal/sysinfo/app.go
2025-05-01 03:07:12 -07:00

103 lines
2.1 KiB
Go

package sysinfo
import (
"fmt"
"os"
"time"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
// App holds CLI state.
type App struct {
force bool
jsonOut bool
verbose bool
aptUpdated bool
cmd *cobra.Command
}
// NewApp configures root command.
func NewApp() (*App, error) {
a := &App{}
root := &cobra.Command{
Use: "sysinfo",
Short: "capture block-device / system snapshot",
Version: Version, // <-- --version flag
SilenceUsage: true,
SilenceErrors: true,
RunE: a.run,
}
root.Flags().BoolVarP(&a.force, "force", "f", false,
"overwrite /etc/sysinfo if it exists")
root.Flags().BoolVar(&a.jsonOut, "json", false,
"emit JSON snapshot to stdout")
root.Flags().BoolVarP(&a.verbose, "verbose", "v", false,
"verbose progress")
_ = viper.BindPFlags(root.Flags())
root.AddCommand(&cobra.Command{
Use: "schema",
Short: "print the JSON schema",
Run: func(*cobra.Command, []string) {
fmt.Fprintln(os.Stdout, JSONSchema)
},
})
a.cmd = root
return a, nil
}
func (a *App) Execute() error { return a.cmd.Execute() }
func (a *App) logf(f string, v ...any) {
if a.verbose {
_, _ = fmt.Fprintf(os.Stderr, f+"\n", v...)
}
}
// run executes the snapshot workflow.
func (a *App) run(_ *cobra.Command, _ []string) error {
if err := a.ensureUbuntu(); err != nil {
return err
}
if err := a.ensureDeps(); err != nil {
return err
}
ctx := &Context{
Now: time.Now().UTC(),
Logf: a.logf,
Run: a.runCmd,
SafeRun: a.safeRun,
SafeRead: a.safeRead,
}
snap := &Snapshot{
Timestamp: ctx.Now.Format(time.RFC3339),
Sections: map[string]json.RawMessage{},
}
collectors := []Collector{
SystemCollector{}, BlockCollector{},
NetworkCollector{}, SensorsCollector{},
PackagesCollector{}, ZFSCollector{},
}
for _, c := range collectors {
raw, err := c.Collect(ctx)
if err != nil {
ctx.Logf("%s: %v", c.Key(), err)
continue
}
snap.Sections[c.Key()] = raw
}
if a.jsonOut {
return a.emitJSON(os.Stdout, snap)
}
return a.writeHierarchy(snap)
}