Add 'dump' command

'dump' writes internal data structes pretty-printed as JSON to stdout.
This was done to debug fsck error messages.

In contrast to the 'cat' command, this one prints the data structures as
there are interpreted by restic, not as they are stored in the
repository. This means that only the merged index from all the index
files is printed out.

This is meant for debugging only, it's compiled only when the "debug"
tag is active.
This commit is contained in:
Alexander Neumann 2015-05-16 14:24:24 +02:00
parent 65c3aead3e
commit df1e3e03e9
1 changed files with 149 additions and 0 deletions

149
cmd/restic/cmd_dump.go Normal file
View File

@ -0,0 +1,149 @@
// +build debug
package main
import (
"encoding/json"
"fmt"
"io"
"os"
"github.com/juju/errors"
"github.com/restic/restic"
"github.com/restic/restic/backend"
"github.com/restic/restic/pack"
"github.com/restic/restic/repository"
)
type CmdDump struct{}
func init() {
_, err := parser.AddCommand("dump",
"dump data structures",
"The dump command dumps data structures from a repository as JSON documents",
&CmdDump{})
if err != nil {
panic(err)
}
}
func dumpIndex(r *repository.Repository, wr io.Writer) error {
fmt.Fprintln(wr, "foo")
return nil
}
func (cmd CmdDump) Usage() string {
return "[index|snapshots|trees|all]"
}
func prettyPrintJSON(wr io.Writer, item interface{}) error {
buf, err := json.MarshalIndent(item, "", " ")
if err != nil {
return err
}
_, err = wr.Write(append(buf, '\n'))
return err
}
func printSnapshots(repo *repository.Repository, wr io.Writer) error {
done := make(chan struct{})
defer close(done)
for id := range repo.List(backend.Snapshot, done) {
snapshot, err := restic.LoadSnapshot(repo, id)
if err != nil {
fmt.Fprintf(os.Stderr, "LoadSnapshot(%v): %v", id.Str(), err)
continue
}
fmt.Fprintf(wr, "snapshot_id: %v\n", id)
err = prettyPrintJSON(wr, snapshot)
if err != nil {
return err
}
}
return nil
}
func printTrees(repo *repository.Repository, wr io.Writer) error {
done := make(chan struct{})
defer close(done)
trees := []backend.ID{}
for blob := range repo.Index().Each(done) {
if blob.Type != pack.Tree {
continue
}
trees = append(trees, blob.ID)
}
for _, id := range trees {
tree, err := restic.LoadTree(repo, id)
if err != nil {
fmt.Fprintf(os.Stderr, "LoadTree(%v): %v", id.Str(), err)
continue
}
fmt.Fprintf(wr, "tree_id: %v\n", id)
prettyPrintJSON(wr, tree)
}
return nil
}
func (cmd CmdDump) Execute(args []string) error {
if len(args) != 1 {
return fmt.Errorf("type not specified, Usage: %s", cmd.Usage())
}
repo, err := OpenRepo()
if err != nil {
return err
}
err = repo.LoadIndex()
if err != nil {
return err
}
tpe := args[0]
switch tpe {
case "index":
return repo.Index().Dump(os.Stdout)
case "snapshots":
return printSnapshots(repo, os.Stdout)
case "trees":
return printTrees(repo, os.Stdout)
case "all":
fmt.Printf("snapshots:\n")
err := printSnapshots(repo, os.Stdout)
if err != nil {
return err
}
fmt.Printf("\ntrees:\n")
err = printTrees(repo, os.Stdout)
if err != nil {
return err
}
fmt.Printf("\nindex:\n")
err = repo.Index().Dump(os.Stdout)
if err != nil {
return err
}
return nil
default:
return errors.Errorf("no such type %q", tpe)
}
}