github.com/advanderveer/restic@v0.8.1-0.20171209104529-42a8c19aaea6/cmd/restic/cmd_debug.go (about)

     1  // +build debug
     2  
     3  package main
     4  
     5  import (
     6  	"context"
     7  	"encoding/json"
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  
    12  	"github.com/spf13/cobra"
    13  
    14  	"github.com/restic/restic/internal/errors"
    15  	"github.com/restic/restic/internal/pack"
    16  	"github.com/restic/restic/internal/repository"
    17  	"github.com/restic/restic/internal/restic"
    18  
    19  	"github.com/restic/restic/internal/worker"
    20  )
    21  
    22  var cmdDebug = &cobra.Command{
    23  	Use:   "debug",
    24  	Short: "Debug commands",
    25  }
    26  
    27  var cmdDebugDump = &cobra.Command{
    28  	Use:   "dump [indexes|snapshots|all|packs]",
    29  	Short: "Dump data structures",
    30  	Long: `
    31  The "dump" command dumps data structures from the repository as JSON objects. It
    32  is used for debugging purposes only.`,
    33  	DisableAutoGenTag: true,
    34  	RunE: func(cmd *cobra.Command, args []string) error {
    35  		return runDebugDump(globalOptions, args)
    36  	},
    37  }
    38  
    39  func init() {
    40  	cmdRoot.AddCommand(cmdDebug)
    41  	cmdDebug.AddCommand(cmdDebugDump)
    42  }
    43  
    44  func prettyPrintJSON(wr io.Writer, item interface{}) error {
    45  	buf, err := json.MarshalIndent(item, "", "  ")
    46  	if err != nil {
    47  		return err
    48  	}
    49  
    50  	_, err = wr.Write(append(buf, '\n'))
    51  	return err
    52  }
    53  
    54  func debugPrintSnapshots(repo *repository.Repository, wr io.Writer) error {
    55  	for id := range repo.List(context.TODO(), restic.SnapshotFile) {
    56  		snapshot, err := restic.LoadSnapshot(context.TODO(), repo, id)
    57  		if err != nil {
    58  			fmt.Fprintf(os.Stderr, "LoadSnapshot(%v): %v", id.Str(), err)
    59  			continue
    60  		}
    61  
    62  		fmt.Fprintf(wr, "snapshot_id: %v\n", id)
    63  
    64  		err = prettyPrintJSON(wr, snapshot)
    65  		if err != nil {
    66  			return err
    67  		}
    68  	}
    69  
    70  	return nil
    71  }
    72  
    73  const dumpPackWorkers = 10
    74  
    75  // Pack is the struct used in printPacks.
    76  type Pack struct {
    77  	Name string `json:"name"`
    78  
    79  	Blobs []Blob `json:"blobs"`
    80  }
    81  
    82  // Blob is the struct used in printPacks.
    83  type Blob struct {
    84  	Type   restic.BlobType `json:"type"`
    85  	Length uint            `json:"length"`
    86  	ID     restic.ID       `json:"id"`
    87  	Offset uint            `json:"offset"`
    88  }
    89  
    90  func printPacks(repo *repository.Repository, wr io.Writer) error {
    91  	f := func(ctx context.Context, job worker.Job) (interface{}, error) {
    92  		name := job.Data.(string)
    93  
    94  		h := restic.Handle{Type: restic.DataFile, Name: name}
    95  
    96  		blobInfo, err := repo.Backend().Stat(ctx, h)
    97  		if err != nil {
    98  			return nil, err
    99  		}
   100  
   101  		blobs, err := pack.List(repo.Key(), restic.ReaderAt(repo.Backend(), h), blobInfo.Size)
   102  		if err != nil {
   103  			return nil, err
   104  		}
   105  
   106  		return blobs, nil
   107  	}
   108  
   109  	jobCh := make(chan worker.Job)
   110  	resCh := make(chan worker.Job)
   111  	wp := worker.New(context.TODO(), dumpPackWorkers, f, jobCh, resCh)
   112  
   113  	go func() {
   114  		for name := range repo.Backend().List(context.TODO(), restic.DataFile) {
   115  			jobCh <- worker.Job{Data: name}
   116  		}
   117  		close(jobCh)
   118  	}()
   119  
   120  	for job := range resCh {
   121  		name := job.Data.(string)
   122  
   123  		if job.Error != nil {
   124  			fmt.Fprintf(os.Stderr, "error for pack %v: %v\n", name, job.Error)
   125  			continue
   126  		}
   127  
   128  		entries := job.Result.([]restic.Blob)
   129  		p := Pack{
   130  			Name:  name,
   131  			Blobs: make([]Blob, len(entries)),
   132  		}
   133  		for i, blob := range entries {
   134  			p.Blobs[i] = Blob{
   135  				Type:   blob.Type,
   136  				Length: blob.Length,
   137  				ID:     blob.ID,
   138  				Offset: blob.Offset,
   139  			}
   140  		}
   141  
   142  		prettyPrintJSON(os.Stdout, p)
   143  	}
   144  
   145  	wp.Wait()
   146  
   147  	return nil
   148  }
   149  
   150  func dumpIndexes(repo restic.Repository) error {
   151  	for id := range repo.List(context.TODO(), restic.IndexFile) {
   152  		fmt.Printf("index_id: %v\n", id)
   153  
   154  		idx, err := repository.LoadIndex(context.TODO(), repo, id)
   155  		if err != nil {
   156  			return err
   157  		}
   158  
   159  		err = idx.Dump(os.Stdout)
   160  		if err != nil {
   161  			return err
   162  		}
   163  	}
   164  
   165  	return nil
   166  }
   167  
   168  func runDebugDump(gopts GlobalOptions, args []string) error {
   169  	if len(args) != 1 {
   170  		return errors.Fatal("type not specified")
   171  	}
   172  
   173  	repo, err := OpenRepository(gopts)
   174  	if err != nil {
   175  		return err
   176  	}
   177  
   178  	if !gopts.NoLock {
   179  		lock, err := lockRepo(repo)
   180  		defer unlockRepo(lock)
   181  		if err != nil {
   182  			return err
   183  		}
   184  	}
   185  
   186  	err = repo.LoadIndex(gopts.ctx)
   187  	if err != nil {
   188  		return err
   189  	}
   190  
   191  	tpe := args[0]
   192  
   193  	switch tpe {
   194  	case "indexes":
   195  		return dumpIndexes(repo)
   196  	case "snapshots":
   197  		return debugPrintSnapshots(repo, os.Stdout)
   198  	case "packs":
   199  		return printPacks(repo, os.Stdout)
   200  	case "all":
   201  		fmt.Printf("snapshots:\n")
   202  		err := debugPrintSnapshots(repo, os.Stdout)
   203  		if err != nil {
   204  			return err
   205  		}
   206  
   207  		fmt.Printf("\nindexes:\n")
   208  		err = dumpIndexes(repo)
   209  		if err != nil {
   210  			return err
   211  		}
   212  
   213  		return nil
   214  	default:
   215  		return errors.Fatalf("no such type %q", tpe)
   216  	}
   217  }