diff --git a/cmd/podman/images/pull.go b/cmd/podman/images/pull.go index 47ab528b6f..95f6687072 100644 --- a/cmd/podman/images/pull.go +++ b/cmd/podman/images/pull.go @@ -114,10 +114,10 @@ func pullFlags(cmd *cobra.Command) { _ = cmd.RegisterFlagCompletionFunc(decryptionKeysFlagName, completion.AutocompleteDefault) retryFlagName := "retry" - flags.Uint(retryFlagName, cli.MaxPullPushRetries, "number of times to retry in case of failure when performing pull") + flags.Uint(retryFlagName, registry.RetryDefault(), "number of times to retry in case of failure when performing pull") _ = cmd.RegisterFlagCompletionFunc(retryFlagName, completion.AutocompleteNone) retryDelayFlagName := "retry-delay" - flags.String(retryDelayFlagName, cli.PullPushRetryDelay.String(), "delay between retries in case of pull failures") + flags.String(retryDelayFlagName, registry.RetryDelayDefault(), "delay between retries in case of pull failures") _ = cmd.RegisterFlagCompletionFunc(retryDelayFlagName, completion.AutocompleteNone) if registry.IsRemote() { diff --git a/cmd/podman/images/push.go b/cmd/podman/images/push.go index f71e0fa7d5..2288ee7e8d 100644 --- a/cmd/podman/images/push.go +++ b/cmd/podman/images/push.go @@ -111,6 +111,13 @@ func pushFlags(cmd *cobra.Command) { flags.BoolVarP(&pushOptions.Quiet, "quiet", "q", false, "Suppress output information when pushing images") flags.BoolVar(&pushOptions.RemoveSignatures, "remove-signatures", false, "Discard any pre-existing signatures in the image") + retryFlagName := "retry" + flags.Uint(retryFlagName, registry.RetryDefault(), "number of times to retry in case of failure when performing push") + _ = cmd.RegisterFlagCompletionFunc(retryFlagName, completion.AutocompleteNone) + retryDelayFlagName := "retry-delay" + flags.String(retryDelayFlagName, registry.RetryDelayDefault(), "delay between retries in case of push failures") + _ = cmd.RegisterFlagCompletionFunc(retryDelayFlagName, completion.AutocompleteNone) + signByFlagName := "sign-by" flags.StringVar(&pushOptions.SignBy, signByFlagName, "", "Add a signature at the destination using the specified key") _ = cmd.RegisterFlagCompletionFunc(signByFlagName, completion.AutocompleteNone) @@ -208,6 +215,24 @@ func imagePush(cmd *cobra.Command, args []string) error { pushOptions.OciEncryptConfig = encConfig pushOptions.OciEncryptLayers = encLayers + if cmd.Flags().Changed("retry") { + retry, err := cmd.Flags().GetUint("retry") + if err != nil { + return err + } + + pushOptions.Retry = &retry + } + + if cmd.Flags().Changed("retry-delay") { + val, err := cmd.Flags().GetString("retry-delay") + if err != nil { + return err + } + + pushOptions.RetryDelay = val + } + if cmd.Flags().Changed("compression-level") { val, err := cmd.Flags().GetInt("compression-level") if err != nil { diff --git a/cmd/podman/registry/config.go b/cmd/podman/registry/config.go index d3d7ad8be7..9b52d65bc1 100644 --- a/cmd/podman/registry/config.go +++ b/cmd/podman/registry/config.go @@ -166,3 +166,19 @@ func setXdgDirs() error { } return nil } + +func RetryDefault() uint { + if IsRemote() { + return 0 + } + + return PodmanConfig().ContainersConfDefaultsRO.Engine.Retry +} + +func RetryDelayDefault() string { + if IsRemote() { + return "" + } + + return PodmanConfig().ContainersConfDefaultsRO.Engine.RetryDelay +} diff --git a/docs/source/markdown/options/retry-delay.md b/docs/source/markdown/options/retry-delay.md index bb20a06027..bd1b898e9e 100644 --- a/docs/source/markdown/options/retry-delay.md +++ b/docs/source/markdown/options/retry-delay.md @@ -1,7 +1,8 @@ ####> This option file is used in: -####> podman build, farm build, pull +####> podman build, farm build, pull, push ####> If file is edited, make sure the changes ####> are applicable to all of those. #### **--retry-delay**=*duration* -Duration of delay between retry attempts in case of failure when performing pull of images from registry. Default is **2s**. +Duration of delay between retry attempts when pulling or pushing images between +the registry and local storage in case of failure. The default is to start at two seconds and then exponentially back off. The delay is used when this value is set, and no exponential back off occurs. diff --git a/docs/source/markdown/options/retry.md b/docs/source/markdown/options/retry.md index e3c72b65d8..ae95152231 100644 --- a/docs/source/markdown/options/retry.md +++ b/docs/source/markdown/options/retry.md @@ -1,8 +1,8 @@ ####> This option file is used in: -####> podman build, farm build, pull +####> podman build, farm build, pull, push ####> If file is edited, make sure the changes ####> are applicable to all of those. #### **--retry**=*attempts* -Number of times to retry in case of failure when performing pull of -images from registry. Default is **3**. +Number of times to retry pulling or pushing images between the registry and +local storage in case of failure. Default is **3**. diff --git a/docs/source/markdown/podman-push.1.md.in b/docs/source/markdown/podman-push.1.md.in index 4076b29e74..990783bccb 100644 --- a/docs/source/markdown/podman-push.1.md.in +++ b/docs/source/markdown/podman-push.1.md.in @@ -84,6 +84,10 @@ When writing the output image, suppress progress output Discard any pre-existing signatures in the image. +@@option retry + +@@option retry-delay + #### **--sign-by**=*key* Add a “simple signing” signature at the destination using the specified key. (This option is not available with the remote Podman client, including Mac and Windows (excluding WSL2) machines) diff --git a/pkg/api/handlers/libpod/images_push.go b/pkg/api/handlers/libpod/images_push.go index 8caf23ef36..ce9d5d19e1 100644 --- a/pkg/api/handlers/libpod/images_push.go +++ b/pkg/api/handlers/libpod/images_push.go @@ -32,6 +32,8 @@ func PushImage(w http.ResponseWriter, r *http.Request) { Destination string `schema:"destination"` Format string `schema:"format"` RemoveSignatures bool `schema:"removeSignatures"` + Retry uint `schema:"retry"` + RetryDelay string `schema:"retryDelay"` TLSVerify bool `schema:"tlsVerify"` Quiet bool `schema:"quiet"` }{ @@ -83,9 +85,14 @@ func PushImage(w http.ResponseWriter, r *http.Request) { Password: password, Quiet: query.Quiet, RemoveSignatures: query.RemoveSignatures, + RetryDelay: query.RetryDelay, Username: username, } + if _, found := r.URL.Query()["retry"]; found { + options.Retry = &query.Retry + } + if _, found := r.URL.Query()["compressionFormat"]; found { if _, foundForceCompression := r.URL.Query()["forceCompressionFormat"]; !foundForceCompression { // If `compressionFormat` is set and no value for `forceCompressionFormat` diff --git a/pkg/bindings/images/types.go b/pkg/bindings/images/types.go index 6c4cb07dbd..7621f8cd7d 100644 --- a/pkg/bindings/images/types.go +++ b/pkg/bindings/images/types.go @@ -162,6 +162,10 @@ type PushOptions struct { SkipTLSVerify *bool `schema:"-"` // RemoveSignatures Discard any pre-existing signatures in the image. RemoveSignatures *bool + // Retry number of times to retry push in case of failure + Retry *uint + // RetryDelay between retries in case of push failures + RetryDelay *string // Username for authenticating against the registry. Username *string `schema:"-"` // Quiet can be specified to suppress progress when pushing. diff --git a/pkg/bindings/images/types_push_options.go b/pkg/bindings/images/types_push_options.go index 2dcd382912..686636cdb2 100644 --- a/pkg/bindings/images/types_push_options.go +++ b/pkg/bindings/images/types_push_options.go @@ -198,6 +198,36 @@ func (o *PushOptions) GetRemoveSignatures() bool { return *o.RemoveSignatures } +// WithRetry set field Retry to given value +func (o *PushOptions) WithRetry(value uint) *PushOptions { + o.Retry = &value + return o +} + +// GetRetry returns value of field Retry +func (o *PushOptions) GetRetry() uint { + if o.Retry == nil { + var z uint + return z + } + return *o.Retry +} + +// WithRetryDelay set field RetryDelay to given value +func (o *PushOptions) WithRetryDelay(value string) *PushOptions { + o.RetryDelay = &value + return o +} + +// GetRetryDelay returns value of field RetryDelay +func (o *PushOptions) GetRetryDelay() string { + if o.RetryDelay == nil { + var z string + return z + } + return *o.RetryDelay +} + // WithUsername set field Username to given value func (o *PushOptions) WithUsername(value string) *PushOptions { o.Username = &value diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index 96ed74617e..f70663d9f4 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -151,6 +151,10 @@ type ImagePushOptions struct { // RemoveSignatures, discard any pre-existing signatures in the image. // Ignored for remote calls. RemoveSignatures bool + // Retry number of times to retry push in case of failure + Retry *uint + // RetryDelay between retries in case of push failures + RetryDelay string // SignaturePolicy to use when pulling. Ignored for remote calls. SignaturePolicy string // Signers, if non-empty, asks for signatures to be added during the copy diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index be574f090c..f7d5356aed 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -254,8 +254,8 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entiti pullOptions.InsecureSkipTLSVerify = options.SkipTLSVerify pullOptions.Writer = options.Writer pullOptions.OciDecryptConfig = options.OciDecryptConfig - pullOptions.MaxRetries = options.Retry + if options.RetryDelay != "" { duration, err := time.ParseDuration(options.RetryDelay) if err != nil { @@ -343,6 +343,14 @@ func (ir *ImageEngine) Push(ctx context.Context, source string, destination stri pushOptions.OciEncryptLayers = options.OciEncryptLayers pushOptions.CompressionLevel = options.CompressionLevel pushOptions.ForceCompressionFormat = options.ForceCompressionFormat + pushOptions.MaxRetries = options.Retry + if options.RetryDelay != "" { + duration, err := time.ParseDuration(options.RetryDelay) + if err != nil { + return nil, err + } + pushOptions.RetryDelay = &duration + } compressionFormat := options.CompressionFormat if compressionFormat == "" { diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index 9090a2b6db..2b8daeba78 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -273,6 +273,12 @@ func (ir *ImageEngine) Push(ctx context.Context, source string, destination stri options.WithSkipTLSVerify(false) } } + if opts.Retry != nil { + options.WithRetry(*opts.Retry) + } + if opts.RetryDelay != "" { + options.WithRetryDelay(opts.RetryDelay) + } if err := images.Push(ir.ClientCtx, source, destination, options); err != nil { return nil, err } diff --git a/test/system/150-login.bats b/test/system/150-login.bats index 0a59371736..44651ff3ff 100644 --- a/test/system/150-login.bats +++ b/test/system/150-login.bats @@ -327,7 +327,7 @@ function _test_skopeo_credential_sharing() { } -@test "podman images with retry" { +@test "podman pull images with retry" { run_podman pull -q --retry 4 --retry-delay "10s" $IMAGE run_podman 125 pull -q --retry 4 --retry-delay "bogus" $IMAGE is "$output" 'Error: time: invalid duration "bogus"' "bad retry-delay" @@ -367,6 +367,30 @@ function _test_skopeo_credential_sharing() { run_podman rmi $image1 } +@test "podman containers.conf retry" { + skip_if_remote "containers.conf settings not set for remote connections" + run_podman pull --help + assert "$output" =~ "--retry .*performing pull \(default 3\)" + + run_podman push --help + assert "$output" =~ "--retry .*performing push \(default 3\)" + + containersConf=$PODMAN_TMPDIR/containers.conf + cat >$containersConf <