fix: include error messages in restic logs

This commit is contained in:
garethgeorge
2024-03-30 15:10:41 -07:00
parent 270cd01a6b
commit 7f962130ed
3 changed files with 36 additions and 28 deletions
+11 -4
View File
@@ -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
}
+5 -4
View File
@@ -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
View File
@@ -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