mirror of https://github.com/restic/restic.git
Add check summary
This commit is contained in:
parent
a58a8f2ce0
commit
7cc1aa0cd4
|
@ -212,6 +212,7 @@ func prepareCheckCache(opts CheckOptions, gopts *GlobalOptions, printer progress
|
||||||
}
|
}
|
||||||
|
|
||||||
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) error {
|
||||||
|
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 errors.Fatal("the check command expects no arguments, only options - please see `restic help check` for usage and flags")
|
||||||
}
|
}
|
||||||
|
@ -249,26 +250,24 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args
|
||||||
}
|
}
|
||||||
|
|
||||||
errorsFound := false
|
errorsFound := false
|
||||||
suggestIndexRebuild := false
|
|
||||||
mixedFound := false
|
|
||||||
for _, hint := range hints {
|
for _, hint := range hints {
|
||||||
switch hint.(type) {
|
switch hint.(type) {
|
||||||
case *checker.ErrDuplicatePacks:
|
case *checker.ErrDuplicatePacks:
|
||||||
term.Print(hint.Error())
|
term.Print(hint.Error())
|
||||||
suggestIndexRebuild = true
|
summary.HintRepairIndex = true
|
||||||
case *checker.ErrMixedPack:
|
case *checker.ErrMixedPack:
|
||||||
term.Print(hint.Error())
|
term.Print(hint.Error())
|
||||||
mixedFound = true
|
summary.HintPrune = true
|
||||||
default:
|
default:
|
||||||
printer.E("error: %v\n", hint)
|
printer.E("error: %v\n", hint)
|
||||||
errorsFound = true
|
errorsFound = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if suggestIndexRebuild {
|
if summary.HintRepairIndex {
|
||||||
term.Print("Duplicate packs are non-critical, you can run `restic repair index' to correct this.\n")
|
term.Print("Duplicate packs are non-critical, you can run `restic repair index' to correct this.\n")
|
||||||
}
|
}
|
||||||
if mixedFound {
|
if summary.HintPrune {
|
||||||
term.Print("Mixed packs with tree and data blobs are non-critical, you can run `restic prune` to correct this.\n")
|
term.Print("Mixed packs with tree and data blobs are non-critical, you can run `restic prune` to correct this.\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,6 +276,8 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args
|
||||||
printer.E("error: %v\n", err)
|
printer.E("error: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
summary.NumErrors += len(errs)
|
||||||
|
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 errors.Fatal("repository contains errors")
|
||||||
}
|
}
|
||||||
|
@ -299,6 +300,7 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args
|
||||||
salvagePacks.Insert(packErr.ID)
|
salvagePacks.Insert(packErr.ID)
|
||||||
}
|
}
|
||||||
errorsFound = true
|
errorsFound = true
|
||||||
|
summary.NumErrors++
|
||||||
printer.E("%v\n", err)
|
printer.E("%v\n", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -307,9 +309,12 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if orphanedPacks > 0 && !errorsFound {
|
if orphanedPacks > 0 {
|
||||||
// hide notice if repository is damaged
|
summary.HintPrune = true
|
||||||
printer.P("%d additional files were found in the repo, which likely contain duplicate data.\nThis is non-critical, you can run `restic prune` to correct this.\n", orphanedPacks)
|
if !errorsFound {
|
||||||
|
// hide notice if repository is damaged
|
||||||
|
printer.P("%d additional files were found in the repo, which likely contain duplicate data.\nThis is non-critical, you can run `restic prune` to correct this.\n", orphanedPacks)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
|
@ -332,9 +337,11 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args
|
||||||
if e, ok := err.(*checker.TreeError); ok {
|
if e, ok := err.(*checker.TreeError); ok {
|
||||||
printer.E("error for tree %v:\n", e.ID.Str())
|
printer.E("error for tree %v:\n", e.ID.Str())
|
||||||
for _, treeErr := range e.Errors {
|
for _, treeErr := range e.Errors {
|
||||||
|
summary.NumErrors++
|
||||||
printer.E(" %v\n", treeErr)
|
printer.E(" %v\n", treeErr)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
summary.NumErrors++
|
||||||
printer.E("error: %v\n", err)
|
printer.E("error: %v\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -367,6 +374,7 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args
|
||||||
|
|
||||||
for err := range errChan {
|
for err := range errChan {
|
||||||
errorsFound = true
|
errorsFound = true
|
||||||
|
summary.NumErrors++
|
||||||
printer.E("%v\n", err)
|
printer.E("%v\n", err)
|
||||||
if err, ok := err.(*repository.ErrPackData); ok {
|
if err, ok := err.(*repository.ErrPackData); ok {
|
||||||
salvagePacks.Insert(err.PackID)
|
salvagePacks.Insert(err.PackID)
|
||||||
|
@ -418,11 +426,10 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args
|
||||||
|
|
||||||
if len(salvagePacks) > 0 {
|
if len(salvagePacks) > 0 {
|
||||||
printer.E("\nThe repository contains damaged pack files. These damaged files must be removed to repair the repository. This can be done using the following commands. Please read the troubleshooting guide at https://restic.readthedocs.io/en/stable/077_troubleshooting.html first.\n\n")
|
printer.E("\nThe repository contains damaged pack files. These damaged files must be removed to repair the repository. This can be done using the following commands. Please read the troubleshooting guide at https://restic.readthedocs.io/en/stable/077_troubleshooting.html first.\n\n")
|
||||||
var strIDs []string
|
|
||||||
for id := range salvagePacks {
|
for id := range salvagePacks {
|
||||||
strIDs = append(strIDs, id.String())
|
summary.BrokenPacks = append(summary.BrokenPacks, id.String())
|
||||||
}
|
}
|
||||||
printer.E("restic repair packs %v\nrestic repair snapshots --forget\n\n", strings.Join(strIDs, " "))
|
printer.E("restic repair packs %v\nrestic repair snapshots --forget\n\n", strings.Join(summary.BrokenPacks, " "))
|
||||||
printer.E("Damaged pack files can be caused by backend problems, hardware problems or bugs in restic. Please open an issue at https://github.com/restic/restic/issues/new/choose for further troubleshooting!\n")
|
printer.E("Damaged pack files can be caused by backend problems, hardware problems or bugs in restic. Please open an issue at https://github.com/restic/restic/issues/new/choose for further troubleshooting!\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,6 +437,9 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args
|
||||||
return ctx.Err()
|
return 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")
|
||||||
|
@ -437,14 +447,6 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args
|
||||||
return errors.Fatal("repository contains errors")
|
return errors.Fatal("repository contains errors")
|
||||||
}
|
}
|
||||||
printer.P("no errors were found\n")
|
printer.P("no errors were found\n")
|
||||||
if gopts.JSON {
|
|
||||||
status := checkSuccess{
|
|
||||||
MessageType: "checked",
|
|
||||||
Message: "no errors were found",
|
|
||||||
}
|
|
||||||
term.Print(ui.ToJSONString(status))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,9 +494,12 @@ func selectRandomPacksByFileSize(allPacks map[restic.ID]int64, subsetSize int64,
|
||||||
return packs
|
return packs
|
||||||
}
|
}
|
||||||
|
|
||||||
type checkSuccess struct {
|
type checkSummary struct {
|
||||||
MessageType string `json:"message_type"` // "checked"
|
MessageType string `json:"message_type"` // "summary"
|
||||||
Message string `json:"message"`
|
NumErrors int `json:"num_errors"`
|
||||||
|
BrokenPacks []string `json:"broken_packs"` // run "restic repair packs ID..." and "restic repair snapshots --forget" to remove damaged files
|
||||||
|
HintRepairIndex bool `json:"suggest_repair_index"` // run "restic repair index"
|
||||||
|
HintPrune bool `json:"suggest_prune"` // run "restic prune"
|
||||||
}
|
}
|
||||||
|
|
||||||
type checkError struct {
|
type checkError struct {
|
||||||
|
@ -521,7 +526,7 @@ func (p *jsonErrorPrinter) E(msg string, args ...interface{}) {
|
||||||
MessageType: "error",
|
MessageType: "error",
|
||||||
Message: fmt.Sprintf(msg, args...),
|
Message: fmt.Sprintf(msg, args...),
|
||||||
}
|
}
|
||||||
p.term.Print(ui.ToJSONString(status))
|
p.term.Error(ui.ToJSONString(status))
|
||||||
}
|
}
|
||||||
func (*jsonErrorPrinter) P(_ string, _ ...interface{}) {}
|
func (*jsonErrorPrinter) P(_ string, _ ...interface{}) {}
|
||||||
func (*jsonErrorPrinter) V(_ string, _ ...interface{}) {}
|
func (*jsonErrorPrinter) V(_ string, _ ...interface{}) {}
|
||||||
|
|
|
@ -248,13 +248,33 @@ non-JSON messages the command generates.
|
||||||
check
|
check
|
||||||
-----
|
-----
|
||||||
|
|
||||||
The ``check`` command outputs JSON messages with the following format:
|
The ``check`` command uses the JSON lines format with the following message types.
|
||||||
|
|
||||||
+------------------+--------------------------------+
|
Status
|
||||||
| ``message_type`` | Either "checked" or "error" |
|
^^^^^^
|
||||||
+------------------+--------------------------------+
|
|
||||||
| ``message`` | Descriptive message |
|
+--------------------------+------------------------------------------------------------------------------------------------+
|
||||||
+------------------+--------------------------------+
|
| ``message_type`` | Always "summary" |
|
||||||
|
+--------------------------+------------------------------------------------------------------------------------------------+
|
||||||
|
| ``num_errors`` | Number of errors |
|
||||||
|
+--------------------------+------------------------------------------------------------------------------------------------+
|
||||||
|
| ``broken_packs`` | Run "restic repair packs ID..." and "restic repair snapshots --forget" to remove damaged files |
|
||||||
|
+--------------------------+------------------------------------------------------------------------------------------------+
|
||||||
|
| ``suggest_repair_index`` | Run "restic repair index" |
|
||||||
|
+--------------------------+------------------------------------------------------------------------------------------------+
|
||||||
|
| ``suggest_prune`` | Run "restic prune" |
|
||||||
|
+--------------------------+------------------------------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
Error
|
||||||
|
^^^^^
|
||||||
|
|
||||||
|
These errors are printed on ``stderr``.
|
||||||
|
|
||||||
|
+----------------------+---------------------------------------------------------------------+
|
||||||
|
| ``message_type`` | Always "error" |
|
||||||
|
+----------------------+---------------------------------------------------------------------+
|
||||||
|
| ``message`` | Error message. May change in arbitrary ways across restic versions. |
|
||||||
|
+----------------------+---------------------------------------------------------------------+
|
||||||
|
|
||||||
|
|
||||||
diff
|
diff
|
||||||
|
|
Loading…
Reference in New Issue