mirror of
https://github.com/restic/restic.git
synced 2024-12-22 15:57:07 +00:00
restic-from-command: abort snapshot on non-zero exit codes
This commit is contained in:
parent
6990b0122e
commit
81f8d473df
2 changed files with 43 additions and 4 deletions
|
@ -634,6 +634,8 @@ func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, ter
|
||||||
wg.Go(func() error { return sc.Scan(cancelCtx, targets) })
|
wg.Go(func() error { return sc.Scan(cancelCtx, targets) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
snapshotCtx, cancelSnapshot := context.WithCancel(ctx)
|
||||||
|
|
||||||
arch := archiver.New(repo, targetFS, archiver.Options{ReadConcurrency: backupOptions.ReadConcurrency})
|
arch := archiver.New(repo, targetFS, archiver.Options{ReadConcurrency: backupOptions.ReadConcurrency})
|
||||||
arch.SelectByName = selectByNameFilter
|
arch.SelectByName = selectByNameFilter
|
||||||
arch.Select = selectFilter
|
arch.Select = selectFilter
|
||||||
|
@ -641,6 +643,11 @@ func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, ter
|
||||||
success := true
|
success := true
|
||||||
arch.Error = func(item string, err error) error {
|
arch.Error = func(item string, err error) error {
|
||||||
success = false
|
success = false
|
||||||
|
// If we receive a fatal error during the execution of the snapshot,
|
||||||
|
// we abort the snapshot.
|
||||||
|
if errors.IsFatal(err) {
|
||||||
|
cancelSnapshot()
|
||||||
|
}
|
||||||
return progressReporter.Error(item, err)
|
return progressReporter.Error(item, err)
|
||||||
}
|
}
|
||||||
arch.CompleteItem = progressReporter.CompleteItem
|
arch.CompleteItem = progressReporter.CompleteItem
|
||||||
|
@ -668,7 +675,8 @@ func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, ter
|
||||||
if !gopts.JSON {
|
if !gopts.JSON {
|
||||||
progressPrinter.V("start backup on %v", targets)
|
progressPrinter.V("start backup on %v", targets)
|
||||||
}
|
}
|
||||||
_, id, err := arch.Snapshot(ctx, targets, snapshotOpts)
|
_, id, err := arch.Snapshot(snapshotCtx, targets, snapshotOpts)
|
||||||
|
cancelSnapshot()
|
||||||
|
|
||||||
// cleanly shutdown all running goroutines
|
// cleanly shutdown all running goroutines
|
||||||
cancel()
|
cancel()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/restic/restic/internal/errors"
|
||||||
"io"
|
"io"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
)
|
)
|
||||||
|
@ -11,13 +12,43 @@ import (
|
||||||
type ReadCloserCommand struct {
|
type ReadCloserCommand struct {
|
||||||
Cmd *exec.Cmd
|
Cmd *exec.Cmd
|
||||||
Stdout io.ReadCloser
|
Stdout io.ReadCloser
|
||||||
|
|
||||||
|
bytesRead bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fp *ReadCloserCommand) Read(p []byte) (n int, err error) {
|
// Read populate the array with data from the process stdout.
|
||||||
return fp.Stdout.Read(p)
|
func (fp *ReadCloserCommand) Read(p []byte) (int, error) {
|
||||||
|
// We may encounter two different error conditions here:
|
||||||
|
// - EOF with no bytes read: the program terminated prematurely, so we send
|
||||||
|
// a fatal error to cancel the snapshot;
|
||||||
|
// - an error that is not EOF: something bad happened, we need to abort the
|
||||||
|
// snapshot.
|
||||||
|
b, err := fp.Stdout.Read(p)
|
||||||
|
if b == 0 && errors.Is(err, io.EOF) && !fp.bytesRead {
|
||||||
|
// The command terminated with no output at all. Raise a fatal error.
|
||||||
|
return 0, errors.Fatalf("command terminated with no output")
|
||||||
|
} else if err != nil && !errors.Is(err, io.EOF) {
|
||||||
|
// The command terminated with an error that is not EOF. Raise a fatal
|
||||||
|
// error.
|
||||||
|
return 0, errors.Fatal(err.Error())
|
||||||
|
} else if b > 0 {
|
||||||
|
fp.bytesRead = true
|
||||||
|
}
|
||||||
|
return b, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fp *ReadCloserCommand) Close() error {
|
func (fp *ReadCloserCommand) Close() error {
|
||||||
// No need to close fp.Stdout as Wait() closes all pipes.
|
// No need to close fp.Stdout as Wait() closes all pipes.
|
||||||
return fp.Cmd.Wait()
|
err := fp.Cmd.Wait()
|
||||||
|
if err != nil {
|
||||||
|
// If we have information about the exit code, let's use it in the
|
||||||
|
// error message. Otherwise, send the error message along.
|
||||||
|
// In any case, use a fatal error to abort the snapshot.
|
||||||
|
var err2 *exec.ExitError
|
||||||
|
if errors.As(err, &err2) {
|
||||||
|
return errors.Fatalf("command terminated with exit code %d", err2.ExitCode())
|
||||||
|
}
|
||||||
|
return errors.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue