2
2
mirror of https://github.com/octoleo/restic.git synced 2024-11-29 16:23:59 +00:00

Merge pull request #4884 from MichaelEischer/lock-and-not-exist-error-codes

Return different exit code if repo is locked or does not exist
This commit is contained in:
Michael Eischer 2024-07-12 21:05:52 +02:00 committed by GitHub
commit 375c572c4f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
41 changed files with 195 additions and 42 deletions

View File

@ -0,0 +1,11 @@
Change: return exit code 10 or 11 if repository does not exist or is locked
If a repository does not exist or cannot be locked, then restic always returned
exit code 1. This made it difficult to distinguish these cases from other
errors.
Now, restic returns exit code 10 if the repository does not exist and exit code
11 if the repository could be not locked due to a conflicting lock.
https://github.com/restic/restic/issues/956
https://github.com/restic/restic/pull/4884

View File

@ -41,6 +41,8 @@ EXIT STATUS
Exit status is 0 if the command was successful.
Exit status is 1 if there was a fatal error (no snapshot created).
Exit status is 3 if some source data could not be read (incomplete snapshot created).
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
PreRun: func(_ *cobra.Command, _ []string) {
if backupOptions.Host == "" {

View File

@ -25,7 +25,8 @@ The "cache" command allows listing and cleaning local cache directories.
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
`,
DisableAutoGenTag: true,
RunE: func(_ *cobra.Command, args []string) error {

View File

@ -21,7 +21,10 @@ 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.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -35,7 +35,10 @@ repository and not use a local cache.
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -30,6 +30,14 @@ This means that copied files, which existed in both the source and destination
repository, /may occupy up to twice their space/ in the destination repository.
This can be mitigated by the "--copy-chunker-params" option when initializing a
new destination repository using the "init" command.
EXIT STATUS
===========
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
RunE: func(cmd *cobra.Command, args []string) error {
return runCopy(cmd.Context(), copyOptions, globalOptions, args)

View File

@ -43,7 +43,10 @@ is used for debugging purposes only.
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -39,7 +39,10 @@ snapshot.
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -34,7 +34,10 @@ snapshot.
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -28,7 +28,8 @@ A _deprecated_ feature is always disabled and cannot be enabled. The flag will b
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
`,
Hidden: true,
DisableAutoGenTag: true,

View File

@ -33,7 +33,10 @@ restic find --pack 025c1d06
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -35,7 +35,10 @@ security considerations.
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -18,7 +18,8 @@ and the auto-completion files for bash, fish and zsh).
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
`,
DisableAutoGenTag: true,
RunE: func(_ *cobra.Command, args []string) error {

View File

@ -23,7 +23,8 @@ The "init" command initializes a new repository.
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -19,7 +19,10 @@ The "add" sub-command creates a new key and validates the key. Returns the new k
EXIT STATUS
===========
Exit status is 0 if the command is successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
}

View File

@ -23,7 +23,10 @@ used to access the repository.
EXIT STATUS
===========
Exit status is 0 if the command is successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -19,7 +19,10 @@ Returns the new key ID.
EXIT STATUS
===========
Exit status is 0 if the command is successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
}

View File

@ -20,7 +20,10 @@ removing the current key being used to access the repository.
EXIT STATUS
===========
Exit status is 0 if the command is successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -19,7 +19,10 @@ The "list" command allows listing objects in the repository based on type.
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -39,7 +39,10 @@ a path separator); paths use the forward slash '/' as separator.
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -22,7 +22,10 @@ names are specified, these migrations are applied.
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -64,7 +64,10 @@ The default path templates are:
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -17,7 +17,8 @@ The "options" command prints a list of extended options.
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
`,
Hidden: true,
DisableAutoGenTag: true,

View File

@ -28,7 +28,10 @@ referenced and therefore not needed any more.
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, _ []string) error {

View File

@ -22,7 +22,10 @@ It can be used if, for example, a snapshot has been removed by accident with "fo
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, _ []string) error {

View File

@ -19,7 +19,10 @@ repository.
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, _ []string) error {

View File

@ -23,7 +23,10 @@ the index to remove the damaged pack files and removes the pack files from the r
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -37,7 +37,10 @@ snapshot!
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -32,7 +32,10 @@ syntax, where "subfolder" is a path within the snapshot.
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -38,7 +38,10 @@ use the "prune" command.
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -24,7 +24,10 @@ files.
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -23,7 +23,10 @@ The "snapshots" command lists all snapshots stored in the repository.
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -49,7 +49,10 @@ Refer to the online manual for more details about each mode.
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -25,7 +25,10 @@ When no snapshotID is given, all snapshots matching the host, tag and path filte
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -16,7 +16,8 @@ The "unlock" command removes stale locks that have been created by other restic
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, _ []string) error {

View File

@ -18,7 +18,8 @@ and the version of this software.
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
`,
DisableAutoGenTag: true,
Run: func(_ *cobra.Command, _ []string) {

View File

@ -43,6 +43,10 @@ import (
"golang.org/x/term"
)
// ErrNoRepository is used to report if opening a repsitory failed due
// to a missing backend storage location or config file
var ErrNoRepository = errors.New("repository does not exist")
var version = "0.16.5-dev (compiled manually)"
// TimeFormat is the format used for all timestamps printed by restic.
@ -607,6 +611,9 @@ func innerOpen(ctx context.Context, s string, gopts GlobalOptions, opts options.
be, err = factory.Open(ctx, cfg, rt, lim)
}
if errors.Is(err, backend.ErrNoRepository) {
return nil, fmt.Errorf("Fatal: %w at %v: %v", ErrNoRepository, location.StripPassword(gopts.backends, s), err)
}
if err != nil {
return nil, errors.Fatalf("unable to open repository at %v: %v", location.StripPassword(gopts.backends, s), err)
}
@ -635,6 +642,9 @@ func open(ctx context.Context, s string, gopts GlobalOptions, opts options.Optio
// check if config is there
fi, err := be.Stat(ctx, backend.Handle{Type: restic.ConfigFile})
if be.IsNotExist(err) {
return nil, fmt.Errorf("Fatal: %w: unable to open config file: %v\nIs there a repository at the following location?\n%v", ErrNoRepository, err, location.StripPassword(gopts.backends, s))
}
if err != nil {
return nil, errors.Fatalf("unable to open config file: %v\nIs there a repository at the following location?\n%v", err, location.StripPassword(gopts.backends, s))
}

View File

@ -156,6 +156,10 @@ func main() {
exitCode = 0
case err == ErrInvalidSourceData:
exitCode = 3
case errors.Is(err, ErrNoRepository):
exitCode = 10
case restic.IsAlreadyLocked(err):
exitCode = 11
case errors.Is(err, context.Canceled):
exitCode = 130
default:

View File

@ -21,23 +21,51 @@ Check if a repository is already initialized
********************************************
You may find a need to check if a repository is already initialized,
perhaps to prevent your script from initializing a repository multiple
times. The command ``cat config`` may be used for this purpose:
perhaps to prevent your script from trying to initialize a repository multiple
times (the ``init`` command contains a check to prevent overwriting existing
repositories). The command ``cat config`` may be used for this purpose:
.. code-block:: console
$ restic -r /srv/restic-repo cat config
Fatal: unable to open config file: stat /srv/restic-repo/config: no such file or directory
Fatal: repository does not exist: unable to open config file: stat /srv/restic-repo/config: no such file or directory
Is there a repository at the following location?
/srv/restic-repo
If a repository does not exist, restic will return a non-zero exit code
and print an error message. Note that restic will also return a non-zero
exit code if a different error is encountered (e.g.: incorrect password
to ``cat config``) and it may print a different error message. If there
are no errors, restic will return a zero exit code and print the repository
If a repository does not exist, restic (since 0.17.0) will return exit code ``10``
and print a corresponding error message. Older versions return exit code ``1``.
Note that restic will also return exit code ``1`` if a different error is encountered
(e.g.: incorrect password to ``cat config``) and it may print a different error message.
If there are no errors, restic will return a zero exit code and print the repository
metadata.
Exit codes
**********
Restic commands return an exit code that signals whether the command was successful.
The following table provides a general description, see the help of each command for
a more specific description.
.. warning::
New exit codes will be added over time. If an unknown exit code is returned, then it
MUST be treated as a command failure.
+-----+----------------------------------------------------+
| 0 | Command was successful |
+-----+----------------------------------------------------+
| 1 | Command failed, see command help for more details |
+-----+----------------------------------------------------+
| 2 | Go runtime error |
+-----+----------------------------------------------------+
| 3 | ``backup`` command could not read some source data |
+-----+----------------------------------------------------+
| 10 | Repository does not exist (since restic 0.17.0) |
+-----+----------------------------------------------------+
| 11 | Failed to lock repository (since restic 0.17.0) |
+-----+----------------------------------------------------+
| 130 | Restic was interrupted using SIGINT or SIGSTOP |
+-----+----------------------------------------------------+
JSON output
***********

View File

@ -100,7 +100,9 @@ func Open(ctx context.Context, cfg Config, rt http.RoundTripper) (backend.Backen
}
bucket, err := client.Bucket(ctx, cfg.Bucket)
if err != nil {
if b2.IsNotExist(err) {
return nil, backend.ErrNoRepository
} else if err != nil {
return nil, errors.Wrap(err, "Bucket")
}

View File

@ -2,10 +2,13 @@ package backend
import (
"context"
"fmt"
"hash"
"io"
)
var ErrNoRepository = fmt.Errorf("repository does not exist")
// Backend is used to store and access data.
//
// Backend operations that return an error will be retried when a Backend is