github.com/mckael/restic@v0.8.3/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  
    20  var cmdDebug = &cobra.Command{
    21  	Use:   "debug",
    22  	Short: "Debug commands",
    23  }
    24  
    25  var cmdDebugDump = &cobra.Command{
    26  	Use:   "dump [indexes|snapshots|all|packs]",
    27  	Short: "Dump data structures",
    28  	Long: `
    29  The "dump" command dumps data structures from the repository as JSON objects. It
    30  is used for debugging purposes only.`,
    31  	DisableAutoGenTag: true,
    32  	RunE: func(cmd *cobra.Command, args []string) error {
    33  		return runDebugDump(globalOptions, args)
    34  	},
    35  }
    36  
    37  func init() {
    38  	cmdRoot.AddCommand(cmdDebug)
    39  	cmdDebug.AddCommand(cmdDebugDump)
    40  }
    41  
    42  func prettyPrintJSON(wr io.Writer, item interface{}) error {
    43  	buf, err := json.MarshalIndent(item, "", "  ")
    44  	if err != nil {
    45  		return err
    46  	}
    47  
    48  	_, err = wr.Write(append(buf, '\n'))
    49  	return err
    50  }
    51  
    52  func debugPrintSnapshots(repo *repository.Repository, wr io.Writer) error {
    53  	return repo.List(context.TODO(), restic.SnapshotFile, func(id restic.ID, size int64) error {
    54  		snapshot, err := restic.LoadSnapshot(context.TODO(), repo, id)
    55  		if err != nil {
    56  			return err
    57  		}
    58  
    59  		fmt.Fprintf(wr, "snapshot_id: %v\n", id)
    60  
    61  		return prettyPrintJSON(wr, snapshot)
    62  	})
    63  }
    64  
    65  // Pack is the struct used in printPacks.
    66  type Pack struct {
    67  	Name string `json:"name"`
    68  
    69  	Blobs []Blob `json:"blobs"`
    70  }
    71  
    72  // Blob is the struct used in printPacks.
    73  type Blob struct {
    74  	Type   restic.BlobType `json:"type"`
    75  	Length uint            `json:"length"`
    76  	ID     restic.ID       `json:"id"`
    77  	Offset uint            `json:"offset"`
    78  }
    79  
    80  func printPacks(repo *repository.Repository, wr io.Writer) error {
    81  
    82  	return repo.List(context.TODO(), restic.DataFile, func(id restic.ID, size int64) error {
    83  		h := restic.Handle{Type: restic.DataFile, Name: id.String()}
    84  
    85  		blobs, err := pack.List(repo.Key(), restic.ReaderAt(repo.Backend(), h), size)
    86  		if err != nil {
    87  			fmt.Fprintf(os.Stderr, "error for pack %v: %v\n", id.Str(), err)
    88  			return nil
    89  		}
    90  
    91  		p := Pack{
    92  			Name:  id.String(),
    93  			Blobs: make([]Blob, len(blobs)),
    94  		}
    95  		for i, blob := range blobs {
    96  			p.Blobs[i] = Blob{
    97  				Type:   blob.Type,
    98  				Length: blob.Length,
    99  				ID:     blob.ID,
   100  				Offset: blob.Offset,
   101  			}
   102  		}
   103  
   104  		return prettyPrintJSON(os.Stdout, p)
   105  	})
   106  
   107  	return nil
   108  }
   109  
   110  func dumpIndexes(repo restic.Repository) error {
   111  	return repo.List(context.TODO(), restic.IndexFile, func(id restic.ID, size int64) error {
   112  		fmt.Printf("index_id: %v\n", id)
   113  
   114  		idx, err := repository.LoadIndex(context.TODO(), repo, id)
   115  		if err != nil {
   116  			return err
   117  		}
   118  
   119  		return idx.Dump(os.Stdout)
   120  	})
   121  }
   122  
   123  func runDebugDump(gopts GlobalOptions, args []string) error {
   124  	if len(args) != 1 {
   125  		return errors.Fatal("type not specified")
   126  	}
   127  
   128  	repo, err := OpenRepository(gopts)
   129  	if err != nil {
   130  		return err
   131  	}
   132  
   133  	if !gopts.NoLock {
   134  		lock, err := lockRepo(repo)
   135  		defer unlockRepo(lock)
   136  		if err != nil {
   137  			return err
   138  		}
   139  	}
   140  
   141  	err = repo.LoadIndex(gopts.ctx)
   142  	if err != nil {
   143  		return err
   144  	}
   145  
   146  	tpe := args[0]
   147  
   148  	switch tpe {
   149  	case "indexes":
   150  		return dumpIndexes(repo)
   151  	case "snapshots":
   152  		return debugPrintSnapshots(repo, os.Stdout)
   153  	case "packs":
   154  		return printPacks(repo, os.Stdout)
   155  	case "all":
   156  		fmt.Printf("snapshots:\n")
   157  		err := debugPrintSnapshots(repo, os.Stdout)
   158  		if err != nil {
   159  			return err
   160  		}
   161  
   162  		fmt.Printf("\nindexes:\n")
   163  		err = dumpIndexes(repo)
   164  		if err != nil {
   165  			return err
   166  		}
   167  
   168  		return nil
   169  	default:
   170  		return errors.Fatalf("no such type %q", tpe)
   171  	}
   172  }