mirror of
https://github.com/garethgeorge/backrest.git
synced 2026-08-28 03:46:29 +00:00
fix: include error messages in restic logs
This commit is contained in:
+11
-4
@@ -1,6 +1,7 @@
|
||||
package restic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
)
|
||||
@@ -31,7 +32,7 @@ func (e *CmdError) Is(target error) bool {
|
||||
}
|
||||
|
||||
// newCmdError creates a new error indicating that running a command failed.
|
||||
func newCmdError(cmd *exec.Cmd, output string, err error) *CmdError {
|
||||
func newCmdError(ctx context.Context, cmd *exec.Cmd, output string, err error) *CmdError {
|
||||
cerr := &CmdError{
|
||||
Command: cmd.String(),
|
||||
Err: err,
|
||||
@@ -41,14 +42,20 @@ func newCmdError(cmd *exec.Cmd, output string, err error) *CmdError {
|
||||
if len(output) >= outputBufferLimit {
|
||||
cerr.Output = output[:outputBufferLimit] + "\n...[truncated]"
|
||||
}
|
||||
|
||||
if logger := LoggerFromContext(ctx); logger != nil {
|
||||
logger.Write([]byte(cerr.Error()))
|
||||
}
|
||||
return cerr
|
||||
}
|
||||
|
||||
func newCmdErrorPreformatted(cmd *exec.Cmd, output string, err error) *CmdError {
|
||||
return &CmdError{
|
||||
func newCmdErrorPreformatted(ctx context.Context, cmd *exec.Cmd, output string, err error) *CmdError {
|
||||
cerr := &CmdError{
|
||||
Command: cmd.String(),
|
||||
Err: err,
|
||||
Output: output,
|
||||
}
|
||||
if logger := LoggerFromContext(ctx); logger != nil {
|
||||
logger.Write([]byte(cerr.Error()))
|
||||
}
|
||||
return cerr
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package restic
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -94,7 +95,7 @@ func (b *BackupProgressEntry) Validate() error {
|
||||
}
|
||||
|
||||
// readBackupProgressEntries returns the summary event or an error if the command failed.
|
||||
func readBackupProgressEntries(cmd *exec.Cmd, output io.Reader, callback func(event *BackupProgressEntry)) (*BackupProgressEntry, error) {
|
||||
func readBackupProgressEntries(ctx context.Context, cmd *exec.Cmd, output io.Reader, callback func(event *BackupProgressEntry)) (*BackupProgressEntry, error) {
|
||||
scanner := bufio.NewScanner(output)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
|
||||
@@ -110,7 +111,7 @@ func readBackupProgressEntries(cmd *exec.Cmd, output io.Reader, callback func(ev
|
||||
bytes = append(bytes, scanner.Bytes()...)
|
||||
}
|
||||
|
||||
return nil, newCmdError(cmd, string(bytes), fmt.Errorf("command output was not JSON: %w", err))
|
||||
return nil, newCmdError(ctx, cmd, string(bytes), fmt.Errorf("command output was not JSON: %w", err))
|
||||
}
|
||||
if err := event.Validate(); err != nil {
|
||||
return nil, err
|
||||
@@ -243,7 +244,7 @@ func (e *RestoreProgressEntry) Validate() error {
|
||||
}
|
||||
|
||||
// readRestoreProgressEntries returns the summary event or an error if the command failed.
|
||||
func readRestoreProgressEntries(cmd *exec.Cmd, output io.Reader, callback func(event *RestoreProgressEntry)) (*RestoreProgressEntry, error) {
|
||||
func readRestoreProgressEntries(ctx context.Context, cmd *exec.Cmd, output io.Reader, callback func(event *RestoreProgressEntry)) (*RestoreProgressEntry, error) {
|
||||
scanner := bufio.NewScanner(output)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
|
||||
@@ -259,7 +260,7 @@ func readRestoreProgressEntries(cmd *exec.Cmd, output io.Reader, callback func(e
|
||||
bytes = append(bytes, scanner.Bytes()...)
|
||||
}
|
||||
|
||||
return nil, newCmdError(cmd, string(bytes), fmt.Errorf("command output was not JSON: %w", err))
|
||||
return nil, newCmdError(ctx, cmd, string(bytes), fmt.Errorf("command output was not JSON: %w", err))
|
||||
}
|
||||
if err := event.Validate(); err != nil {
|
||||
return nil, err
|
||||
|
||||
+20
-20
@@ -58,7 +58,7 @@ func (r *Repo) commandWithContext(ctx context.Context, args []string, opts ...Ge
|
||||
addLoggingToCommand(ctx, cmd)
|
||||
|
||||
if logger := LoggerFromContext(ctx); logger != nil {
|
||||
fmt.Fprintf(logger, "command: %v %v\n", r.cmd, strings.Join(args, " "))
|
||||
fmt.Fprintf(logger, "\ncommand: %v %v\n", r.cmd, strings.Join(args, " "))
|
||||
}
|
||||
|
||||
return cmd
|
||||
@@ -99,7 +99,7 @@ func (r *Repo) init(ctx context.Context, opts ...GenericOption) error {
|
||||
if strings.Contains(output.String(), "config file already exists") || strings.Contains(output.String(), "already initialized") {
|
||||
return errAlreadyInitialized
|
||||
}
|
||||
return newCmdError(cmd, output.String(), err)
|
||||
return newCmdError(ctx, cmd, output.String(), err)
|
||||
}
|
||||
|
||||
r.initialized = true
|
||||
@@ -129,7 +129,7 @@ func (r *Repo) Backup(ctx context.Context, paths []string, progressCallback func
|
||||
r.pipeCmdOutputToWriter(cmd, writer, capture)
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return nil, newCmdError(cmd, "", err)
|
||||
return nil, newCmdError(ctx, cmd, "", err)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
@@ -141,7 +141,7 @@ func (r *Repo) Backup(ctx context.Context, paths []string, progressCallback func
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
var err error
|
||||
summary, err = readBackupProgressEntries(cmd, reader, progressCallback)
|
||||
summary, err = readBackupProgressEntries(ctx, cmd, reader, progressCallback)
|
||||
if err != nil {
|
||||
readErr = fmt.Errorf("processing command output: %w", err)
|
||||
}
|
||||
@@ -173,7 +173,7 @@ func (r *Repo) Backup(ctx context.Context, paths []string, progressCallback func
|
||||
}
|
||||
|
||||
if cmdErr != nil || readErr != nil {
|
||||
return summary, newCmdErrorPreformatted(cmd, capture.String(), errors.Join(cmdErr, readErr))
|
||||
return summary, newCmdErrorPreformatted(ctx, cmd, capture.String(), errors.Join(cmdErr, readErr))
|
||||
}
|
||||
|
||||
return summary, nil
|
||||
@@ -186,12 +186,12 @@ func (r *Repo) Snapshots(ctx context.Context, opts ...GenericOption) ([]*Snapsho
|
||||
r.pipeCmdOutputToLogger(ctx, cmd)
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return nil, newCmdError(cmd, output.String(), err)
|
||||
return nil, newCmdError(ctx, cmd, output.String(), err)
|
||||
}
|
||||
|
||||
var snapshots []*Snapshot
|
||||
if err := json.Unmarshal(output.Bytes(), &snapshots); err != nil {
|
||||
return nil, newCmdError(cmd, output.String(), fmt.Errorf("command output is not valid JSON: %w", err))
|
||||
return nil, newCmdError(ctx, cmd, output.String(), fmt.Errorf("command output is not valid JSON: %w", err))
|
||||
}
|
||||
|
||||
for _, snapshot := range snapshots {
|
||||
@@ -211,18 +211,18 @@ func (r *Repo) Forget(ctx context.Context, policy *RetentionPolicy, opts ...Gene
|
||||
r.pipeCmdOutputToWriter(cmd, output)
|
||||
r.pipeCmdOutputToLogger(ctx, cmd)
|
||||
if err := cmd.Run(); err != nil {
|
||||
return nil, newCmdError(cmd, output.String(), err)
|
||||
return nil, newCmdError(ctx, cmd, output.String(), err)
|
||||
}
|
||||
|
||||
var result []ForgetResult
|
||||
if err := json.Unmarshal(output.Bytes(), &result); err != nil {
|
||||
return nil, newCmdError(cmd, output.String(), fmt.Errorf("command output is not valid JSON: %w", err))
|
||||
return nil, newCmdError(ctx, cmd, output.String(), fmt.Errorf("command output is not valid JSON: %w", err))
|
||||
}
|
||||
if len(result) != 1 {
|
||||
return nil, fmt.Errorf("expected 1 output from forget, got %v", len(result))
|
||||
}
|
||||
if err := result[0].Validate(); err != nil {
|
||||
return nil, newCmdError(cmd, output.String(), fmt.Errorf("invalid forget result: %w", err))
|
||||
return nil, newCmdError(ctx, cmd, output.String(), fmt.Errorf("invalid forget result: %w", err))
|
||||
}
|
||||
|
||||
return &result[0], nil
|
||||
@@ -236,7 +236,7 @@ func (r *Repo) ForgetSnapshot(ctx context.Context, snapshotId string, opts ...Ge
|
||||
r.pipeCmdOutputToWriter(cmd, output)
|
||||
r.pipeCmdOutputToLogger(ctx, cmd)
|
||||
if err := cmd.Run(); err != nil {
|
||||
return newCmdError(cmd, output.String(), err)
|
||||
return newCmdError(ctx, cmd, output.String(), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -252,7 +252,7 @@ func (r *Repo) Prune(ctx context.Context, pruneOutput io.Writer, opts ...Generic
|
||||
r.pipeCmdOutputToWriter(cmd, pruneOutput)
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return newCmdErrorPreformatted(cmd, output.String(), err)
|
||||
return newCmdErrorPreformatted(ctx, cmd, output.String(), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -265,7 +265,7 @@ func (r *Repo) Restore(ctx context.Context, snapshot string, callback func(*Rest
|
||||
r.pipeCmdOutputToLogger(ctx, cmd)
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return nil, newCmdError(cmd, "", err)
|
||||
return nil, newCmdError(ctx, cmd, "", err)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
@@ -277,7 +277,7 @@ func (r *Repo) Restore(ctx context.Context, snapshot string, callback func(*Rest
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
var err error
|
||||
summary, err = readRestoreProgressEntries(cmd, reader, callback)
|
||||
summary, err = readRestoreProgressEntries(ctx, cmd, reader, callback)
|
||||
if err != nil {
|
||||
readErr = fmt.Errorf("processing command output: %w", err)
|
||||
}
|
||||
@@ -295,7 +295,7 @@ func (r *Repo) Restore(ctx context.Context, snapshot string, callback func(*Rest
|
||||
wg.Wait()
|
||||
|
||||
if cmdErr != nil || readErr != nil {
|
||||
return nil, newCmdErrorPreformatted(cmd, output.String(), errors.Join(cmdErr, readErr))
|
||||
return nil, newCmdErrorPreformatted(ctx, cmd, output.String(), errors.Join(cmdErr, readErr))
|
||||
}
|
||||
|
||||
return summary, nil
|
||||
@@ -313,12 +313,12 @@ func (r *Repo) ListDirectory(ctx context.Context, snapshot string, path string,
|
||||
r.pipeCmdOutputToLogger(ctx, cmd)
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return nil, nil, newCmdError(cmd, output.String(), err)
|
||||
return nil, nil, newCmdError(ctx, cmd, output.String(), err)
|
||||
}
|
||||
|
||||
snapshots, entries, err := readLs(output)
|
||||
if err != nil {
|
||||
return nil, nil, newCmdError(cmd, output.String(), err)
|
||||
return nil, nil, newCmdError(ctx, cmd, output.String(), err)
|
||||
}
|
||||
|
||||
return snapshots, entries, nil
|
||||
@@ -330,7 +330,7 @@ func (r *Repo) Unlock(ctx context.Context, opts ...GenericOption) error {
|
||||
r.pipeCmdOutputToWriter(cmd, output)
|
||||
r.pipeCmdOutputToLogger(ctx, cmd)
|
||||
if err := cmd.Run(); err != nil {
|
||||
return newCmdError(cmd, output.String(), err)
|
||||
return newCmdError(ctx, cmd, output.String(), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -342,12 +342,12 @@ func (r *Repo) Stats(ctx context.Context, opts ...GenericOption) (*RepoStats, er
|
||||
r.pipeCmdOutputToLogger(ctx, cmd)
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return nil, newCmdError(cmd, output.String(), err)
|
||||
return nil, newCmdError(ctx, cmd, output.String(), err)
|
||||
}
|
||||
|
||||
var stats RepoStats
|
||||
if err := json.Unmarshal(output.Bytes(), &stats); err != nil {
|
||||
return nil, newCmdError(cmd, output.String(), fmt.Errorf("command output is not valid JSON: %w", err))
|
||||
return nil, newCmdError(ctx, cmd, output.String(), fmt.Errorf("command output is not valid JSON: %w", err))
|
||||
}
|
||||
|
||||
return &stats, nil
|
||||
|
||||
Reference in New Issue
Block a user