Merge fix/restore-fail-fast
This commit is contained in:
@@ -95,7 +95,7 @@ vaultik [--config <path>] config init
|
|||||||
vaultik [--config <path>] config edit
|
vaultik [--config <path>] config edit
|
||||||
vaultik [--config <path>] config get <key>
|
vaultik [--config <path>] config get <key>
|
||||||
vaultik [--config <path>] config set <key> <value>
|
vaultik [--config <path>] config set <key> <value>
|
||||||
vaultik [--config <path>] snapshot create [snapshot-names...] [--cron] [--prune] [--keep-newer-than <duration>] [--skip-errors]
|
vaultik [--config <path>] snapshot create [snapshot-names...] [--cron] [--prune] [--keep-newer-than <duration>]
|
||||||
vaultik [--config <path>] snapshot list [--json]
|
vaultik [--config <path>] snapshot list [--json]
|
||||||
vaultik [--config <path>] snapshot verify <snapshot-id> [--deep] [--json]
|
vaultik [--config <path>] snapshot verify <snapshot-id> [--deep] [--json]
|
||||||
vaultik [--config <path>] snapshot purge [--keep-latest | --older-than <duration>] [--snapshot <name>...] [--force]
|
vaultik [--config <path>] snapshot purge [--keep-latest | --older-than <duration>] [--snapshot <name>...] [--force]
|
||||||
@@ -117,7 +117,8 @@ vaultik version
|
|||||||
* `--config <path>`: Path to config file (default: `$VAULTIK_CONFIG`, then platform config dir, then `/etc/vaultik/config.yml`)
|
* `--config <path>`: Path to config file (default: `$VAULTIK_CONFIG`, then platform config dir, then `/etc/vaultik/config.yml`)
|
||||||
* `--verbose`, `-v`: Enable verbose output
|
* `--verbose`, `-v`: Enable verbose output
|
||||||
* `--debug`: Enable debug output
|
* `--debug`: Enable debug output
|
||||||
* `--quiet`, `-q`: Suppress non-error output
|
* `--quiet`, `-q`: Suppress non-error output (also suppresses startup banner)
|
||||||
|
* `--skip-errors`: Continue past per-file errors instead of aborting (applies to `snapshot create` and `restore`)
|
||||||
|
|
||||||
### environment variables
|
### environment variables
|
||||||
|
|
||||||
@@ -173,7 +174,6 @@ in the file are preserved; intermediate maps are created as needed.
|
|||||||
snapshot per name; use `--keep-newer-than` for a rolling window.
|
snapshot per name; use `--keep-newer-than` for a rolling window.
|
||||||
* `--keep-newer-than <duration>`: With `--prune`, keep snapshots newer than
|
* `--keep-newer-than <duration>`: With `--prune`, keep snapshots newer than
|
||||||
this duration instead of only the latest (e.g. `4w`, `30d`, `6mo`, `1y`)
|
this duration instead of only the latest (e.g. `4w`, `30d`, `6mo`, `1y`)
|
||||||
* `--skip-errors`: Skip file read errors (log them loudly but continue)
|
|
||||||
|
|
||||||
**`snapshot list`**: List all snapshots with their timestamps and sizes.
|
**`snapshot list`**: List all snapshots with their timestamps and sizes.
|
||||||
* `--json`: Output in JSON format
|
* `--json`: Output in JSON format
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ func buildRestoreInvokes(snapshotID string, opts *RestoreOptions) []fx.Option {
|
|||||||
TargetDir: opts.TargetDir,
|
TargetDir: opts.TargetDir,
|
||||||
Paths: opts.Paths,
|
Paths: opts.Paths,
|
||||||
Verify: opts.Verify,
|
Verify: opts.Verify,
|
||||||
|
SkipErrors: GetRootFlags().SkipErrors,
|
||||||
}
|
}
|
||||||
if err := app.Vaultik.Restore(restoreOpts); err != nil {
|
if err := app.Vaultik.Restore(restoreOpts); err != nil {
|
||||||
if err != context.Canceled {
|
if err != context.Canceled {
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ type RootFlags struct {
|
|||||||
Verbose bool
|
Verbose bool
|
||||||
Debug bool
|
Debug bool
|
||||||
Quiet bool
|
Quiet bool
|
||||||
|
SkipErrors bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var rootFlags RootFlags
|
var rootFlags RootFlags
|
||||||
@@ -84,6 +85,7 @@ on the source system.`,
|
|||||||
cmd.PersistentFlags().BoolVarP(&rootFlags.Verbose, "verbose", "v", false, "Enable verbose output")
|
cmd.PersistentFlags().BoolVarP(&rootFlags.Verbose, "verbose", "v", false, "Enable verbose output")
|
||||||
cmd.PersistentFlags().BoolVar(&rootFlags.Debug, "debug", false, "Enable debug output")
|
cmd.PersistentFlags().BoolVar(&rootFlags.Debug, "debug", false, "Enable debug output")
|
||||||
cmd.PersistentFlags().BoolVarP(&rootFlags.Quiet, "quiet", "q", false, "Suppress non-error output")
|
cmd.PersistentFlags().BoolVarP(&rootFlags.Quiet, "quiet", "q", false, "Suppress non-error output")
|
||||||
|
cmd.PersistentFlags().BoolVar(&rootFlags.SkipErrors, "skip-errors", false, "Continue past per-file errors instead of aborting (applies to snapshot create and restore)")
|
||||||
|
|
||||||
// Add subcommands
|
// Add subcommands
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ specifying a path using --config or by setting VAULTIK_CONFIG to a path.`,
|
|||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
// Pass snapshot names from args
|
// Pass snapshot names from args
|
||||||
opts.Snapshots = args
|
opts.Snapshots = args
|
||||||
|
// --skip-errors is a global flag on the root command.
|
||||||
|
opts.SkipErrors = rootFlags.SkipErrors
|
||||||
// Use unified config resolution
|
// Use unified config resolution
|
||||||
configPath, err := ResolveConfigPath()
|
configPath, err := ResolveConfigPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -103,7 +105,6 @@ specifying a path using --config or by setting VAULTIK_CONFIG to a path.`,
|
|||||||
cmd.Flags().BoolVar(&opts.Cron, "cron", false, "Run in cron mode (silent unless error)")
|
cmd.Flags().BoolVar(&opts.Cron, "cron", false, "Run in cron mode (silent unless error)")
|
||||||
cmd.Flags().BoolVar(&opts.Prune, "prune", false, "After backup, drop older snapshots of the same name and remove orphaned blobs")
|
cmd.Flags().BoolVar(&opts.Prune, "prune", false, "After backup, drop older snapshots of the same name and remove orphaned blobs")
|
||||||
cmd.Flags().StringVar(&opts.KeepNewerThan, "keep-newer-than", "", "With --prune: keep snapshots newer than this duration (e.g. 4w, 30d, 6mo) instead of only the latest")
|
cmd.Flags().StringVar(&opts.KeepNewerThan, "keep-newer-than", "", "With --prune: keep snapshots newer than this duration (e.g. 4w, 30d, 6mo) instead of only the latest")
|
||||||
cmd.Flags().BoolVar(&opts.SkipErrors, "skip-errors", false, "Skip file read errors (log them loudly but continue)")
|
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ type RestoreOptions struct {
|
|||||||
TargetDir string
|
TargetDir string
|
||||||
Paths []string // Optional paths to restore (empty = all)
|
Paths []string // Optional paths to restore (empty = all)
|
||||||
Verify bool // Verify restored files by checking chunk hashes
|
Verify bool // Verify restored files by checking chunk hashes
|
||||||
|
SkipErrors bool // Continue past file-restore errors instead of aborting
|
||||||
}
|
}
|
||||||
|
|
||||||
// RestoreResult contains statistics from a restore operation
|
// RestoreResult contains statistics from a restore operation
|
||||||
@@ -195,6 +196,10 @@ func (v *Vaultik) restoreAllFiles(
|
|||||||
|
|
||||||
if err := v.restoreFile(v.ctx, repos, file, opts.TargetDir, identity, chunkToBlobMap, blobCache, result); err != nil {
|
if err := v.restoreFile(v.ctx, repos, file, opts.TargetDir, identity, chunkToBlobMap, blobCache, result); err != nil {
|
||||||
log.Error("Failed to restore file", "path", file.Path, "error", err)
|
log.Error("Failed to restore file", "path", file.Path, "error", err)
|
||||||
|
if !opts.SkipErrors {
|
||||||
|
return nil, fmt.Errorf("restoring %s: %w (pass --skip-errors to continue past restore failures)", file.Path, err)
|
||||||
|
}
|
||||||
|
v.UI.Error("Failed to restore %s: %v. Skipping (--skip-errors).", v.UI.Path(file.Path.String()), err)
|
||||||
result.FilesFailed++
|
result.FilesFailed++
|
||||||
result.FailedFiles = append(result.FailedFiles, file.Path.String())
|
result.FailedFiles = append(result.FailedFiles, file.Path.String())
|
||||||
// Update progress bar even on failure
|
// Update progress bar even on failure
|
||||||
|
|||||||
Reference in New Issue
Block a user