mirror of https://github.com/restic/restic.git
Print JSON summary in all error cases
This commit is contained in:
parent
7cc1aa0cd4
commit
49a411f7ac
|
@ -46,7 +46,14 @@ Exit status is 12 if the password is incorrect.
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
term, cancel := setupTermstatus()
|
||||||
defer cancel()
|
defer cancel()
|
||||||
return runCheck(cmd.Context(), checkOptions, globalOptions, args, term)
|
summary, err := runCheck(cmd.Context(), checkOptions, globalOptions, args, term)
|
||||||
|
if globalOptions.JSON {
|
||||||
|
if err != nil && summary.NumErrors == 0 {
|
||||||
|
summary.NumErrors = 1
|
||||||
|
}
|
||||||
|
term.Print(ui.ToJSONString(summary))
|
||||||
|
}
|
||||||
|
return err
|
||||||
},
|
},
|
||||||
PreRunE: func(_ *cobra.Command, _ []string) error {
|
PreRunE: func(_ *cobra.Command, _ []string) error {
|
||||||
return checkFlags(checkOptions)
|
return checkFlags(checkOptions)
|
||||||
|
@ -211,10 +218,10 @@ func prepareCheckCache(opts CheckOptions, gopts *GlobalOptions, printer progress
|
||||||
return cleanup
|
return cleanup
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args []string, term *termstatus.Terminal) error {
|
func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args []string, term *termstatus.Terminal) (checkSummary, error) {
|
||||||
summary := checkSummary{MessageType: "summary"}
|
summary := checkSummary{MessageType: "summary"}
|
||||||
if len(args) != 0 {
|
if len(args) != 0 {
|
||||||
return errors.Fatal("the check command expects no arguments, only options - please see `restic help check` for usage and flags")
|
return summary, errors.Fatal("the check command expects no arguments, only options - please see `restic help check` for usage and flags")
|
||||||
}
|
}
|
||||||
|
|
||||||
var printer progress.Printer
|
var printer progress.Printer
|
||||||
|
@ -232,21 +239,21 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args
|
||||||
}
|
}
|
||||||
ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, gopts.NoLock)
|
ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, gopts.NoLock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return summary, err
|
||||||
}
|
}
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
chkr := checker.New(repo, opts.CheckUnused)
|
chkr := checker.New(repo, opts.CheckUnused)
|
||||||
err = chkr.LoadSnapshots(ctx)
|
err = chkr.LoadSnapshots(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return summary, err
|
||||||
}
|
}
|
||||||
|
|
||||||
printer.P("load indexes\n")
|
printer.P("load indexes\n")
|
||||||
bar := newIndexTerminalProgress(gopts.Quiet, gopts.JSON, term)
|
bar := newIndexTerminalProgress(gopts.Quiet, gopts.JSON, term)
|
||||||
hints, errs := chkr.LoadIndex(ctx, bar)
|
hints, errs := chkr.LoadIndex(ctx, bar)
|
||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
return ctx.Err()
|
return summary, ctx.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
errorsFound := false
|
errorsFound := false
|
||||||
|
@ -279,7 +286,7 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args
|
||||||
summary.NumErrors += len(errs)
|
summary.NumErrors += len(errs)
|
||||||
summary.HintRepairIndex = true
|
summary.HintRepairIndex = true
|
||||||
printer.E("\nThe repository index is damaged and must be repaired. You must run `restic repair index' to correct this.\n\n")
|
printer.E("\nThe repository index is damaged and must be repaired. You must run `restic repair index' to correct this.\n\n")
|
||||||
return errors.Fatal("repository contains errors")
|
return summary, errors.Fatal("repository contains errors")
|
||||||
}
|
}
|
||||||
|
|
||||||
orphanedPacks := 0
|
orphanedPacks := 0
|
||||||
|
@ -317,7 +324,7 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
return ctx.Err()
|
return summary, ctx.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
printer.P("check snapshots, trees and blobs\n")
|
printer.P("check snapshots, trees and blobs\n")
|
||||||
|
@ -351,13 +358,13 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args
|
||||||
// deadlocking in the case of errors.
|
// deadlocking in the case of errors.
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
return ctx.Err()
|
return summary, ctx.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.CheckUnused {
|
if opts.CheckUnused {
|
||||||
unused, err := chkr.UnusedBlobs(ctx)
|
unused, err := chkr.UnusedBlobs(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return summary, err
|
||||||
}
|
}
|
||||||
for _, id := range unused {
|
for _, id := range unused {
|
||||||
printer.P("unused blob %v\n", id)
|
printer.P("unused blob %v\n", id)
|
||||||
|
@ -409,7 +416,7 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args
|
||||||
repoSize += size
|
repoSize += size
|
||||||
}
|
}
|
||||||
if repoSize == 0 {
|
if repoSize == 0 {
|
||||||
return errors.Fatal("Cannot read from a repository having size 0")
|
return summary, errors.Fatal("Cannot read from a repository having size 0")
|
||||||
}
|
}
|
||||||
subsetSize, _ := ui.ParseBytes(opts.ReadDataSubset)
|
subsetSize, _ := ui.ParseBytes(opts.ReadDataSubset)
|
||||||
if subsetSize > repoSize {
|
if subsetSize > repoSize {
|
||||||
|
@ -419,7 +426,7 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args
|
||||||
printer.P("read %d bytes of data packs\n", subsetSize)
|
printer.P("read %d bytes of data packs\n", subsetSize)
|
||||||
}
|
}
|
||||||
if packs == nil {
|
if packs == nil {
|
||||||
return errors.Fatal("internal error: failed to select packs to check")
|
return summary, errors.Fatal("internal error: failed to select packs to check")
|
||||||
}
|
}
|
||||||
doReadData(packs)
|
doReadData(packs)
|
||||||
}
|
}
|
||||||
|
@ -434,20 +441,17 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
return ctx.Err()
|
return summary, ctx.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
if gopts.JSON {
|
|
||||||
term.Print(ui.ToJSONString(summary))
|
|
||||||
}
|
|
||||||
if errorsFound {
|
if errorsFound {
|
||||||
if len(salvagePacks) == 0 {
|
if len(salvagePacks) == 0 {
|
||||||
printer.E("\nThe repository is damaged and must be repaired. Please follow the troubleshooting guide at https://restic.readthedocs.io/en/stable/077_troubleshooting.html .\n\n")
|
printer.E("\nThe repository is damaged and must be repaired. Please follow the troubleshooting guide at https://restic.readthedocs.io/en/stable/077_troubleshooting.html .\n\n")
|
||||||
}
|
}
|
||||||
return errors.Fatal("repository contains errors")
|
return summary, errors.Fatal("repository contains errors")
|
||||||
}
|
}
|
||||||
printer.P("no errors were found\n")
|
printer.P("no errors were found\n")
|
||||||
return nil
|
return summary, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// selectPacksByBucket selects subsets of packs by ranges of buckets.
|
// selectPacksByBucket selects subsets of packs by ranges of buckets.
|
||||||
|
|
|
@ -32,7 +32,8 @@ func testRunCheckOutput(gopts GlobalOptions, checkUnused bool) (string, error) {
|
||||||
ReadData: true,
|
ReadData: true,
|
||||||
CheckUnused: checkUnused,
|
CheckUnused: checkUnused,
|
||||||
}
|
}
|
||||||
return runCheck(context.TODO(), opts, gopts, nil, term)
|
_, err := runCheck(context.TODO(), opts, gopts, nil, term)
|
||||||
|
return err
|
||||||
})
|
})
|
||||||
return buf.String(), err
|
return buf.String(), err
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,7 +105,7 @@ func applyMigrations(ctx context.Context, opts MigrateOptions, gopts GlobalOptio
|
||||||
// the repository is already locked
|
// the repository is already locked
|
||||||
checkGopts.NoLock = true
|
checkGopts.NoLock = true
|
||||||
|
|
||||||
err = runCheck(ctx, checkOptions, checkGopts, []string{}, term)
|
_, err = runCheck(ctx, checkOptions, checkGopts, []string{}, term)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,7 +112,8 @@ func testPrune(t *testing.T, pruneOpts PruneOptions, checkOpts CheckOptions) {
|
||||||
createPrunableRepo(t, env)
|
createPrunableRepo(t, env)
|
||||||
testRunPrune(t, env.gopts, pruneOpts)
|
testRunPrune(t, env.gopts, pruneOpts)
|
||||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, term *termstatus.Terminal) error {
|
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, term *termstatus.Terminal) error {
|
||||||
return runCheck(context.TODO(), checkOpts, env.gopts, nil, term)
|
_, err := runCheck(context.TODO(), checkOpts, env.gopts, nil, term)
|
||||||
|
return err
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,7 +221,8 @@ func testEdgeCaseRepo(t *testing.T, tarfile string, optionsCheck CheckOptions, o
|
||||||
testRunCheck(t, env.gopts)
|
testRunCheck(t, env.gopts)
|
||||||
} else {
|
} else {
|
||||||
rtest.Assert(t, withTermStatus(env.gopts, func(ctx context.Context, term *termstatus.Terminal) error {
|
rtest.Assert(t, withTermStatus(env.gopts, func(ctx context.Context, term *termstatus.Terminal) error {
|
||||||
return runCheck(context.TODO(), optionsCheck, env.gopts, nil, term)
|
_, err := runCheck(context.TODO(), optionsCheck, env.gopts, nil, term)
|
||||||
|
return err
|
||||||
}) != nil,
|
}) != nil,
|
||||||
"check should have reported an error")
|
"check should have reported an error")
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,8 @@ func TestListOnce(t *testing.T) {
|
||||||
createPrunableRepo(t, env)
|
createPrunableRepo(t, env)
|
||||||
testRunPrune(t, env.gopts, pruneOpts)
|
testRunPrune(t, env.gopts, pruneOpts)
|
||||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, term *termstatus.Terminal) error {
|
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, term *termstatus.Terminal) error {
|
||||||
return runCheck(context.TODO(), checkOpts, env.gopts, nil, term)
|
_, err := runCheck(context.TODO(), checkOpts, env.gopts, nil, term)
|
||||||
|
return err
|
||||||
}))
|
}))
|
||||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, term *termstatus.Terminal) error {
|
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, term *termstatus.Terminal) error {
|
||||||
return runRebuildIndex(context.TODO(), RepairIndexOptions{}, env.gopts, term)
|
return runRebuildIndex(context.TODO(), RepairIndexOptions{}, env.gopts, term)
|
||||||
|
|
Loading…
Reference in New Issue