2
2
mirror of https://github.com/octoleo/restic.git synced 2024-11-25 14:17:42 +00:00
restic/cmd/restic/cmd_cat.go
jo ea59896bd6
Add a global option --retry-lock
Fixes restic#719

If the option is passed, restic will wait the specified duration of time
and retry locking the repo every 10 seconds (or more often if the total
timeout is relatively small).

- Play nice with json output
- Reduce wait time in lock tests
- Rework timeout last attempt
- Reduce test wait time to 0.1s
- Use exponential back off for the retry lock
- Don't pass gopts to lockRepo functions
- Use global variable for retry sleep setup
- Exit retry lock on cancel
- Better wording for flag help
- Reorder debug statement
- Refactor tests
- Lower max sleep time to 1m
- Test that we cancel/timeout in time
- Use non blocking sleep function
- Refactor into minDuration func

Co-authored-by: Julian Brost <julian@0x4a42.net>
2023-04-07 16:24:14 +02:00

172 lines
3.5 KiB
Go

package main
import (
"context"
"encoding/json"
"github.com/spf13/cobra"
"github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
)
var cmdCat = &cobra.Command{
Use: "cat [flags] [pack|blob|snapshot|index|key|masterkey|config|lock] ID",
Short: "Print internal objects to stdout",
Long: `
The "cat" command is used to print internal objects to stdout.
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runCat(cmd.Context(), globalOptions, args)
},
}
func init() {
cmdRoot.AddCommand(cmdCat)
}
func runCat(ctx context.Context, gopts GlobalOptions, args []string) error {
if len(args) < 1 || (args[0] != "masterkey" && args[0] != "config" && len(args) != 2) {
return errors.Fatal("type or ID not specified")
}
repo, err := OpenRepository(ctx, gopts)
if err != nil {
return err
}
if !gopts.NoLock {
var lock *restic.Lock
lock, ctx, err = lockRepo(ctx, repo, gopts.RetryLock, gopts.JSON)
defer unlockRepo(lock)
if err != nil {
return err
}
}
tpe := args[0]
var id restic.ID
if tpe != "masterkey" && tpe != "config" && tpe != "snapshot" {
id, err = restic.ParseID(args[1])
if err != nil {
return errors.Fatalf("unable to parse ID: %v\n", err)
}
}
switch tpe {
case "config":
buf, err := json.MarshalIndent(repo.Config(), "", " ")
if err != nil {
return err
}
Println(string(buf))
return nil
case "index":
buf, err := repo.LoadUnpacked(ctx, restic.IndexFile, id)
if err != nil {
return err
}
Println(string(buf))
return nil
case "snapshot":
sn, err := restic.FindSnapshot(ctx, repo.Backend(), repo, args[1])
if err != nil {
return errors.Fatalf("could not find snapshot: %v\n", err)
}
buf, err := json.MarshalIndent(sn, "", " ")
if err != nil {
return err
}
Println(string(buf))
return nil
case "key":
key, err := repository.LoadKey(ctx, repo, id)
if err != nil {
return err
}
buf, err := json.MarshalIndent(&key, "", " ")
if err != nil {
return err
}
Println(string(buf))
return nil
case "masterkey":
buf, err := json.MarshalIndent(repo.Key(), "", " ")
if err != nil {
return err
}
Println(string(buf))
return nil
case "lock":
lock, err := restic.LoadLock(ctx, repo, id)
if err != nil {
return err
}
buf, err := json.MarshalIndent(&lock, "", " ")
if err != nil {
return err
}
Println(string(buf))
return nil
case "pack":
h := restic.Handle{Type: restic.PackFile, Name: id.String()}
buf, err := backend.LoadAll(ctx, nil, repo.Backend(), h)
if err != nil {
return err
}
hash := restic.Hash(buf)
if !hash.Equal(id) {
Warnf("Warning: hash of data does not match ID, want\n %v\ngot:\n %v\n", id.String(), hash.String())
}
_, err = globalOptions.stdout.Write(buf)
return err
case "blob":
err = repo.LoadIndex(ctx)
if err != nil {
return err
}
for _, t := range []restic.BlobType{restic.DataBlob, restic.TreeBlob} {
bh := restic.BlobHandle{ID: id, Type: t}
if !repo.Index().Has(bh) {
continue
}
buf, err := repo.LoadBlob(ctx, t, id, nil)
if err != nil {
return err
}
_, err = globalOptions.stdout.Write(buf)
return err
}
return errors.Fatal("blob not found")
default:
return errors.Fatal("invalid type")
}
}