forked from TrueCloudLab/restic
repair snapshots: partially synchronize code with rewrite command
Simplify CLI options: * Rename "DeleteSnapshots" to "Forget" * Replace "AddTag" and "Append" with hardcoded values Change output and snapshot modifications to be more in line with the "rewrite" command.
This commit is contained in:
parent
118d599d0a
commit
903651c719
1 changed files with 31 additions and 31 deletions
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/restic/restic/internal/backend"
|
||||||
"github.com/restic/restic/internal/debug"
|
"github.com/restic/restic/internal/debug"
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
|
@ -50,12 +51,10 @@ Exit status is 0 if the command was successful, and non-zero if there was any er
|
||||||
|
|
||||||
// RepairOptions collects all options for the repair command.
|
// RepairOptions collects all options for the repair command.
|
||||||
type RepairOptions struct {
|
type RepairOptions struct {
|
||||||
restic.SnapshotFilter
|
DryRun bool
|
||||||
|
Forget bool
|
||||||
|
|
||||||
AddTag string
|
restic.SnapshotFilter
|
||||||
Append string
|
|
||||||
DryRun bool
|
|
||||||
DeleteSnapshots bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var repairSnapshotOptions RepairOptions
|
var repairSnapshotOptions RepairOptions
|
||||||
|
@ -64,28 +63,31 @@ func init() {
|
||||||
cmdRepair.AddCommand(cmdRepairSnapshots)
|
cmdRepair.AddCommand(cmdRepairSnapshots)
|
||||||
flags := cmdRepairSnapshots.Flags()
|
flags := cmdRepairSnapshots.Flags()
|
||||||
|
|
||||||
|
flags.BoolVarP(&repairSnapshotOptions.DryRun, "dry-run", "n", false, "do not do anything, just print what would be done")
|
||||||
|
flags.BoolVarP(&repairSnapshotOptions.Forget, "forget", "", false, "remove original snapshots after creating new ones")
|
||||||
|
|
||||||
initMultiSnapshotFilter(flags, &repairSnapshotOptions.SnapshotFilter, true)
|
initMultiSnapshotFilter(flags, &repairSnapshotOptions.SnapshotFilter, true)
|
||||||
flags.StringVar(&repairSnapshotOptions.AddTag, "add-tag", "repaired", "tag to add to repaired snapshots")
|
|
||||||
flags.StringVar(&repairSnapshotOptions.Append, "append", ".repaired", "string to append to repaired dirs/files; remove files if empty or impossible to repair")
|
|
||||||
flags.BoolVarP(&repairSnapshotOptions.DryRun, "dry-run", "n", true, "don't do anything, only show what would be done")
|
|
||||||
flags.BoolVar(&repairSnapshotOptions.DeleteSnapshots, "delete-snapshots", false, "delete original snapshots")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOptions, args []string) error {
|
func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOptions, args []string) error {
|
||||||
switch {
|
|
||||||
case opts.DryRun:
|
|
||||||
Printf("\n note: --dry-run is set\n-> repair will only show what it would do.\n\n")
|
|
||||||
case opts.DeleteSnapshots:
|
|
||||||
Printf("\n note: --dry-run is not set and --delete-snapshots is set\n-> this may result in data loss!\n\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
repo, err := OpenRepository(ctx, globalOptions)
|
repo, err := OpenRepository(ctx, globalOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
lock, ctx, err := lockRepoExclusive(ctx, repo, gopts.RetryLock, gopts.JSON)
|
if !opts.DryRun {
|
||||||
defer unlockRepo(lock)
|
var lock *restic.Lock
|
||||||
|
var err error
|
||||||
|
lock, ctx, err = lockRepoExclusive(ctx, repo, gopts.RetryLock, gopts.JSON)
|
||||||
|
defer unlockRepo(lock)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
repo.SetDryRun()
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshotLister, err := backend.MemorizeList(ctx, repo.Backend(), restic.SnapshotFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -96,7 +98,7 @@ func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOpt
|
||||||
|
|
||||||
// get snapshots to check & repair
|
// get snapshots to check & repair
|
||||||
var snapshots []*restic.Snapshot
|
var snapshots []*restic.Snapshot
|
||||||
for sn := range FindFilteredSnapshots(ctx, repo.Backend(), repo, &opts.SnapshotFilter, args) {
|
for sn := range FindFilteredSnapshots(ctx, snapshotLister, repo, &opts.SnapshotFilter, args) {
|
||||||
snapshots = append(snapshots, sn)
|
snapshots = append(snapshots, sn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +126,7 @@ func repairSnapshots(ctx context.Context, opts RepairOptions, repo restic.Reposi
|
||||||
Printf("the root tree is damaged -> delete snapshot.\n")
|
Printf("the root tree is damaged -> delete snapshot.\n")
|
||||||
deleteSn.Insert(*sn.ID())
|
deleteSn.Insert(*sn.ID())
|
||||||
case changed:
|
case changed:
|
||||||
err = changeSnapshot(ctx, opts, repo, sn, newID)
|
err = changeSnapshot(ctx, opts.DryRun, repo, sn, newID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -144,7 +146,7 @@ func repairSnapshots(ctx context.Context, opts RepairOptions, repo restic.Reposi
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(deleteSn) > 0 && opts.DeleteSnapshots {
|
if len(deleteSn) > 0 && opts.Forget {
|
||||||
Verbosef("delete %d snapshots...\n", len(deleteSn))
|
Verbosef("delete %d snapshots...\n", len(deleteSn))
|
||||||
if !opts.DryRun {
|
if !opts.DryRun {
|
||||||
DeleteFiles(ctx, globalOptions, repo, deleteSn, restic.SnapshotFile)
|
DeleteFiles(ctx, globalOptions, repo, deleteSn, restic.SnapshotFile)
|
||||||
|
@ -158,14 +160,12 @@ func repairSnapshots(ctx context.Context, opts RepairOptions, repo restic.Reposi
|
||||||
// - add the rag opts.AddTag
|
// - add the rag opts.AddTag
|
||||||
// - preserve original ID
|
// - preserve original ID
|
||||||
// if opts.DryRun is set, it doesn't change anything but only
|
// if opts.DryRun is set, it doesn't change anything but only
|
||||||
func changeSnapshot(ctx context.Context, opts RepairOptions, repo restic.Repository, sn *restic.Snapshot, newID *restic.ID) error {
|
func changeSnapshot(ctx context.Context, dryRun bool, repo restic.Repository, sn *restic.Snapshot, newID *restic.ID) error {
|
||||||
sn.AddTags([]string{opts.AddTag})
|
sn.AddTags([]string{"repaired"})
|
||||||
// Retain the original snapshot id over all tag changes.
|
// Always set the original snapshot id as this essentially a new snapshot.
|
||||||
if sn.Original == nil {
|
sn.Original = sn.ID()
|
||||||
sn.Original = sn.ID()
|
|
||||||
}
|
|
||||||
sn.Tree = newID
|
sn.Tree = newID
|
||||||
if !opts.DryRun {
|
if !dryRun {
|
||||||
newID, err := restic.SaveSnapshot(ctx, repo, sn)
|
newID, err := restic.SaveSnapshot(ctx, repo, sn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -236,12 +236,12 @@ func repairTree(ctx context.Context, opts RepairOptions, repo restic.Repository,
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
changed = true
|
changed = true
|
||||||
if opts.Append == "" || newSize == 0 {
|
if newSize == 0 {
|
||||||
Printf("removed defective file '%v'\n", path+node.Name)
|
Printf("removed defective file '%v'\n", path+node.Name)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
Printf("repaired defective file '%v'", path+node.Name)
|
Printf("repaired defective file '%v'", path+node.Name)
|
||||||
node.Name = node.Name + opts.Append
|
node.Name = node.Name + ".repaired"
|
||||||
Printf(" to '%v'\n", node.Name)
|
Printf(" to '%v'\n", node.Name)
|
||||||
node.Content = newContent
|
node.Content = newContent
|
||||||
node.Size = newSize
|
node.Size = newSize
|
||||||
|
@ -256,7 +256,7 @@ func repairTree(ctx context.Context, opts RepairOptions, repo restic.Repository,
|
||||||
// If we get an error, we remove this subtree
|
// If we get an error, we remove this subtree
|
||||||
changed = true
|
changed = true
|
||||||
Printf("removed defective dir '%v'", path+node.Name)
|
Printf("removed defective dir '%v'", path+node.Name)
|
||||||
node.Name = node.Name + opts.Append
|
node.Name = node.Name + ".repaired"
|
||||||
Printf("(now empty '%v')\n", node.Name)
|
Printf("(now empty '%v')\n", node.Name)
|
||||||
empty, err := emptyTree(ctx, repo, opts.DryRun)
|
empty, err := emptyTree(ctx, repo, opts.DryRun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in a new issue