diff --git a/cmd/error.go b/cmd/error.go index 39189e31cd..82340560c8 100644 --- a/cmd/error.go +++ b/cmd/error.go @@ -45,6 +45,14 @@ type errorMessage struct { SysInfo map[string]string `json:"sysinfo,omitempty"` } +// errorOrFatal wrapper function to call errorIf or fatalIf based on the boolean value +func errorOrFatal(useFatal bool, err *probe.Error, msg string, data ...interface{}) { + if useFatal { + fatalIf(err, msg, data...) + } + errorIf(err, msg, data...) +} + // fatalIf wrapper function which takes error and selectively prints stack frames if available on debug func fatalIf(err *probe.Error, msg string, data ...interface{}) { if err == nil { diff --git a/cmd/mirror-main.go b/cmd/mirror-main.go index 21d69ab829..236a5998b9 100644 --- a/cmd/mirror-main.go +++ b/cmd/mirror-main.go @@ -136,6 +136,10 @@ var ( Name: "retry", Usage: "if specified, will enable retrying on a per object basis if errors occur", }, + cli.BoolFlag{ + Name: "fail-on-error", + Usage: "if specified, the application will exit if errors occur", + }, cli.BoolFlag{ Name: "summary", Usage: "print a summary of the mirror session", @@ -561,6 +565,10 @@ func (mj *mirrorJob) monitorMirrorStatus(cancel context.CancelFunc) (errDuringMi mj.status.Start() defer mj.status.Finish() + // if the operation is not retriable and fail-on-error is true, then + // we should exit on the first error. + useFatal := mj.opts.failOnError && !mj.opts.isRetriable + var cancelInProgress bool defer func() { @@ -595,22 +603,22 @@ func (mj *mirrorJob) monitorMirrorStatus(cancel context.CancelFunc) (errDuringMi ignoreErr = true } if !ignoreErr { - errorIf(sURLs.Error.Trace(sURLs.SourceContent.URL.String()), + errorOrFatal(useFatal, sURLs.Error.Trace(sURLs.SourceContent.URL.String()), "Failed to copy `%s`.", sURLs.SourceContent.URL) } } case sURLs.TargetContent != nil: // When sURLs.SourceContent is nil, we know that we have an error related to removing - errorIf(sURLs.Error.Trace(sURLs.TargetContent.URL.String()), + errorOrFatal(useFatal, sURLs.Error.Trace(sURLs.TargetContent.URL.String()), "Failed to remove `%s`.", sURLs.TargetContent.URL.String()) default: if strings.Contains(sURLs.Error.ToGoError().Error(), "Overwrite not allowed") { ignoreErr = true } if sURLs.ErrorCond == differInUnknown { - errorIf(sURLs.Error.Trace(), "Failed to perform mirroring") + errorOrFatal(useFatal, sURLs.Error.Trace(), "Failed to perform mirroring") } else { - errorIf(sURLs.Error.Trace(), + errorOrFatal(useFatal, sURLs.Error.Trace(), "Failed to perform mirroring, with error condition (%s)", sURLs.ErrorCond) } } @@ -1011,6 +1019,7 @@ func runMirror(ctx context.Context, srcURL, dstURL string, cli *cli.Context, enc isMetadata: isMetadata, isSummary: cli.Bool("summary"), isRetriable: cli.Bool("retry"), + failOnError: cli.Bool("fail-on-error"), md5: md5, checksum: checksum, disableMultipart: cli.Bool("disable-multipart"), diff --git a/cmd/mirror-url.go b/cmd/mirror-url.go index 1c71fb8917..d7c4d40707 100644 --- a/cmd/mirror-url.go +++ b/cmd/mirror-url.go @@ -268,6 +268,7 @@ type mirrorOptions struct { isFake, isOverwrite, activeActive bool isWatch, isRemove, isMetadata bool isRetriable bool + failOnError bool isSummary bool skipErrors bool excludeOptions, excludeStorageClasses, excludeBuckets []string