feat: Add the vantagectl backup and inspect subcommands
Progress output goes to stderr so --out - stays a clean pipe into restic, age or aws s3 cp. Archive names carry no colon, because these get copied onto Windows shares.
This commit is contained in:
+73
-2
@@ -1,7 +1,78 @@
|
||||
package cmd
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"gitea.hostxtra.co.uk/mrhid6/vantage/shared/backup"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newInspectCmd() *cobra.Command {
|
||||
return &cobra.Command{Use: "inspect ARCHIVE", Short: "Print an archive's manifest"}
|
||||
return &cobra.Command{
|
||||
Use: "inspect ARCHIVE",
|
||||
Short: "Print an archive's manifest",
|
||||
Long: "inspect reads an archive and prints what it holds. It contacts no\n" +
|
||||
"database, so it is safe to run against an archive of unknown origin\n" +
|
||||
"and is the fastest way to find out whether one is worth anything.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(c *cobra.Command, args []string) error {
|
||||
archive, err := backup.Open(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer archive.Close()
|
||||
renderManifest(c.OutOrStdout(), archive.Manifest())
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// renderManifest prints a manifest for a human.
|
||||
func renderManifest(w io.Writer, m backup.Manifest) {
|
||||
fmt.Fprintf(w, "Created %s\n", m.CreatedAt.UTC().Format("2006-01-02 15:04:05 MST"))
|
||||
fmt.Fprintf(w, "Database %s\n", m.MongoDB)
|
||||
fmt.Fprintf(w, "MongoDB %s\n", m.MongoServerVersion)
|
||||
fmt.Fprintf(w, "Written by vantagectl %s on %s\n", m.VantageVersion, m.Hostname)
|
||||
fmt.Fprintf(w, "Format version %d\n", m.FormatVersion)
|
||||
|
||||
if m.KeyFingerprint == nil {
|
||||
fmt.Fprintf(w, "Key none recorded — this archive cannot be checked "+
|
||||
"against any KEY_ENCRYPTION_KEY\n")
|
||||
} else {
|
||||
fmt.Fprintf(w, "Key %s\n", *m.KeyFingerprint)
|
||||
}
|
||||
if len(m.Excluded) > 0 {
|
||||
fmt.Fprintf(w, "Excluded %s\n", strings.Join(m.Excluded, ", "))
|
||||
}
|
||||
|
||||
var docs, bytes int64
|
||||
for _, c := range m.Collections {
|
||||
docs += c.Documents
|
||||
bytes += c.Bytes
|
||||
}
|
||||
fmt.Fprintf(w, "\n%d collections, %d documents, %s\n\n",
|
||||
len(m.Collections), docs, humanBytes(bytes))
|
||||
|
||||
tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0)
|
||||
fmt.Fprintln(tw, "COLLECTION\tDOCUMENTS\tSIZE")
|
||||
for _, c := range m.Collections {
|
||||
fmt.Fprintf(tw, "%s\t%d\t%s\n", c.Name, c.Documents, humanBytes(c.Bytes))
|
||||
}
|
||||
tw.Flush()
|
||||
}
|
||||
|
||||
func humanBytes(n int64) string {
|
||||
const unit = 1024
|
||||
if n < unit {
|
||||
return fmt.Sprintf("%d B", n)
|
||||
}
|
||||
div, exp := int64(unit), 0
|
||||
for v := n / unit; v >= unit; v /= unit {
|
||||
div *= unit
|
||||
exp++
|
||||
}
|
||||
return fmt.Sprintf("%.1f %cB", float64(n)/float64(div), "KMGTP"[exp])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user