120 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			120 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package sysinfo
 | 
						|
 | 
						|
import (
 | 
						|
	"encoding/json"
 | 
						|
	"fmt"
 | 
						|
	"os"
 | 
						|
	"path/filepath"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/spf13/cobra"
 | 
						|
	"github.com/spf13/viper"
 | 
						|
)
 | 
						|
 | 
						|
type App struct {
 | 
						|
	force   bool
 | 
						|
	jsonOut bool
 | 
						|
	verbose bool
 | 
						|
	cmd     *cobra.Command
 | 
						|
}
 | 
						|
 | 
						|
func NewApp() (*App, error) {
 | 
						|
	a := &App{}
 | 
						|
	root := &cobra.Command{
 | 
						|
		Use:           "sysinfo",
 | 
						|
		Short:         "capture system snapshot",
 | 
						|
		Version:       Version,
 | 
						|
		SilenceUsage:  true,
 | 
						|
		SilenceErrors: true,
 | 
						|
		RunE:          a.run,
 | 
						|
	}
 | 
						|
	root.Flags().BoolVarP(&a.force, "force", "f", false,
 | 
						|
		"overwrite /etc/sysinfo")
 | 
						|
	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 JSON schema",
 | 
						|
		Run: func(*cobra.Command, []string) {
 | 
						|
			fmt.Println(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...)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (a *App) run(*cobra.Command, []string) error {
 | 
						|
	if err := ensureUbuntu(); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	pm := newPackageManager(a.logf)
 | 
						|
 | 
						|
	ctx := &Context{
 | 
						|
		Now:      time.Now().UTC(),
 | 
						|
		Logf:     a.logf,
 | 
						|
		Run:      runCmd,
 | 
						|
		SafeRun:  safeRun,
 | 
						|
		SafeRead: safeRead,
 | 
						|
	}
 | 
						|
 | 
						|
	snap := &Snapshot{
 | 
						|
		Timestamp: ctx.Now.Format(time.RFC3339),
 | 
						|
		Sections:  map[string]json.RawMessage{},
 | 
						|
	}
 | 
						|
 | 
						|
	collectors := []Collector{
 | 
						|
		SystemCollector{}, BlockCollector{},
 | 
						|
		NetworkCollector{}, SensorsCollector{},
 | 
						|
		PackagesCollector{}, ZFSCollector{},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, col := range collectors {
 | 
						|
		if err := col.EnsurePrerequisites(pm, ctx); err != nil {
 | 
						|
			ctx.Logf("%s: prereq error: %v", col.SectionKey(), err)
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		if !col.IsSupported(pm, ctx) {
 | 
						|
			ctx.Logf("%s: not supported on this host", col.SectionKey())
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		raw, err := col.CollectData(ctx)
 | 
						|
		if err != nil {
 | 
						|
			ctx.Logf("%s: %v", col.SectionKey(), err)
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		snap.Sections[col.SectionKey()] = raw
 | 
						|
	}
 | 
						|
 | 
						|
	if a.jsonOut {
 | 
						|
		return emitJSON(os.Stdout, snap)
 | 
						|
	}
 | 
						|
 | 
						|
	if _, err := os.Stat(rootDir); err == nil && !a.force {
 | 
						|
		return fmt.Errorf("%s exists; use --force", rootDir)
 | 
						|
	}
 | 
						|
	_ = os.RemoveAll(rootDir)
 | 
						|
	if err := os.MkdirAll(rootDir, 0755); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	f, err := os.Create(filepath.Join(rootDir, snapshotJSON))
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	defer f.Close()
 | 
						|
	return emitJSON(f, snap)
 | 
						|
}
 |