Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow podman pull to specify --retry and --retry-delay #21659

Merged
merged 1 commit into from
Feb 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions cmd/podman/images/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@ func pullFlags(cmd *cobra.Command) {
flags.StringArrayVar(&pullOptions.DecryptionKeys, decryptionKeysFlagName, nil, "Key needed to decrypt the image (e.g. /path/to/key.pem)")
_ = cmd.RegisterFlagCompletionFunc(decryptionKeysFlagName, completion.AutocompleteDefault)

retryFlagName := "retry"
flags.Uint(retryFlagName, cli.MaxPullPushRetries, "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")
_ = cmd.RegisterFlagCompletionFunc(retryDelayFlagName, completion.AutocompleteNone)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IDK, does Docker have an equivalent?

if registry.IsRemote() {
_ = flags.MarkHidden(decryptionKeysFlagName)
}
Expand All @@ -136,6 +143,25 @@ func imagePull(cmd *cobra.Command, args []string) error {
if cmd.Flags().Changed("tls-verify") {
pullOptions.SkipTLSVerify = types.NewOptionalBool(!pullOptions.TLSVerifyCLI)
}

if cmd.Flags().Changed("retry") {
retry, err := cmd.Flags().GetUint("retry")
if err != nil {
return err
}

pullOptions.Retry = &retry
}

if cmd.Flags().Changed("retry-delay") {
val, err := cmd.Flags().GetString("retry-delay")
if err != nil {
return err
}

pullOptions.RetryDelay = val
}

if cmd.Flags().Changed("authfile") {
if err := auth.CheckAuthFile(pullOptions.Authfile); err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion docs/source/markdown/options/retry-delay.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
####> This option file is used in:
####> podman build, farm build
####> podman build, farm build, pull
####> If file is edited, make sure the changes
####> are applicable to all of those.
#### **--retry-delay**=*duration*
Expand Down
2 changes: 1 addition & 1 deletion docs/source/markdown/options/retry.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
####> This option file is used in:
####> podman build, farm build
####> podman build, farm build, pull
####> If file is edited, make sure the changes
####> are applicable to all of those.
#### **--retry**=*attempts*
Expand Down
8 changes: 8 additions & 0 deletions docs/source/markdown/podman-pull.1.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ Print the usage statement.

Suppress output information when pulling images

@@option retry

@@option retry-delay

@@option tls-verify

@@option variant.container
Expand Down Expand Up @@ -205,6 +209,10 @@ Storing signatures
3cba58dad5d9b35e755b48b634acb3fdd185ab1c996ac11510cc72c17780e13c
```

Pull an image with up to 6 retries, delaying 10 seconds between retries in quet mode.
$ podman --remote pull -q --retry 6 --retry-delay 10s ubi9
4d6addf62a90e392ff6d3f470259eb5667eab5b9a8e03d20b41d0ab910f92170

## SEE ALSO
**[podman(1)](podman.1.md)**, **[podman-push(1)](podman-push.1.md)**, **[podman-login(1)](podman-login.1.md)**, **[containers-certs.d(5)](https://github.com/containers/image/blob/main/docs/containers-certs.d.5.md)**, **[containers-registries.conf(5)](https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md)**, **[containers-transports(5)](https://github.com/containers/image/blob/main/docs/containers-transports.5.md)**

Expand Down
21 changes: 18 additions & 3 deletions pkg/api/handlers/compat/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,11 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)

query := struct {
FromImage string `schema:"fromImage"`
Tag string `schema:"tag"`
Platform string `schema:"platform"`
FromImage string `schema:"fromImage"`
Tag string `schema:"tag"`
Platform string `schema:"platform"`
Retry uint `schema:"retry"`
RetryDelay string `schema:"retryDelay"`
}{
// This is where you can override the golang default value for one of fields
}
Expand Down Expand Up @@ -290,6 +292,19 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) {
}
pullOptions.Writer = os.Stderr // allows for debugging on the server

if _, found := r.URL.Query()["retry"]; found {
pullOptions.MaxRetries = &query.Retry
}

if _, found := r.URL.Query()["retryDelay"]; found {
duration, err := time.ParseDuration(query.RetryDelay)
if err != nil {
utils.Error(w, http.StatusBadRequest, err)
return
}
pullOptions.RetryDelay = &duration
}

// Handle the platform.
platformSpecs := strings.Split(query.Platform, "/")
pullOptions.OS = platformSpecs[0] // may be empty
Expand Down
16 changes: 16 additions & 0 deletions pkg/api/handlers/libpod/images_pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"net/http"
"time"

"github.com/containers/common/libimage"
"github.com/containers/common/pkg/config"
Expand Down Expand Up @@ -33,6 +34,8 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
PullPolicy string `schema:"policy"`
Quiet bool `schema:"quiet"`
Reference string `schema:"reference"`
Retry uint `schema:"retry"`
RetryDelay string `schema:"retrydelay"`
TLSVerify bool `schema:"tlsVerify"`
// Platform fields below:
Arch string `schema:"Arch"`
Expand Down Expand Up @@ -95,6 +98,19 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
return
}

if _, found := r.URL.Query()["retry"]; found {
pullOptions.MaxRetries = &query.Retry
}

if _, found := r.URL.Query()["retrydelay"]; found {
duration, err := time.ParseDuration(query.RetryDelay)
if err != nil {
utils.Error(w, http.StatusBadRequest, err)
return
}
pullOptions.RetryDelay = &duration
}

// Let's keep thing simple when running in quiet mode and pull directly.
if query.Quiet {
images, err := runtime.LibimageRuntime().Pull(r.Context(), query.Reference, pullPolicy, pullOptions)
Expand Down
4 changes: 4 additions & 0 deletions pkg/bindings/images/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,10 @@ type PullOptions struct {
// Quiet can be specified to suppress pull progress when pulling. Ignored
// for remote calls.
Quiet *bool
// Retry number of times to retry pull in case of failure
Retry *uint
// RetryDelay between retries in case of pull failures
RetryDelay *string
// SkipTLSVerify to skip HTTPS and certificate verification.
SkipTLSVerify *bool `schema:"-"`
// Username for authenticating against the registry.
Expand Down
30 changes: 30 additions & 0 deletions pkg/bindings/images/types_pull_options.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions pkg/domain/entities/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ type ImagePullOptions struct {
// Quiet can be specified to suppress pull progress when pulling. Ignored
// for remote calls.
Quiet bool
// Retry number of times to retry pull in case of failure
Retry *uint
// RetryDelay between retries in case of pull failures
RetryDelay string
// SignaturePolicy to use when pulling. Ignored for remote calls.
SignaturePolicy string
// SkipTLSVerify to skip HTTPS and certificate verification.
Expand Down
10 changes: 10 additions & 0 deletions pkg/domain/infra/abi/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"strconv"
"strings"
"syscall"
"time"

bdefine "github.com/containers/buildah/define"
"github.com/containers/common/libimage"
Expand Down Expand Up @@ -254,6 +255,15 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entiti
pullOptions.Writer = options.Writer
pullOptions.OciDecryptConfig = options.OciDecryptConfig

pullOptions.MaxRetries = options.Retry
if options.RetryDelay != "" {
duration, err := time.ParseDuration(options.RetryDelay)
if err != nil {
return nil, err
}
pullOptions.RetryDelay = &duration
}

if !options.Quiet && pullOptions.Writer == nil {
pullOptions.Writer = os.Stderr
}
Expand Down
6 changes: 6 additions & 0 deletions pkg/domain/infra/tunnel/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,12 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, opts entities.
options.WithSkipTLSVerify(false)
}
}
if opts.Retry != nil {
options.WithRetry(*opts.Retry)
}
if opts.RetryDelay != "" {
options.WithRetryDelay(opts.RetryDelay)
}
pulledImages, err := images.Pull(ir.ClientCtx, rawImage, options)
if err != nil {
return nil, err
Expand Down
40 changes: 40 additions & 0 deletions test/system/150-login.bats
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,46 @@ function _test_skopeo_credential_sharing() {

}

@test "podman 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"

skip_if_remote "running a local registry doesn't work with podman-remote"
start_registry
authfile=${PODMAN_LOGIN_WORKDIR}/auth-$(random_string 10).json
run_podman login --tls-verify=false \
--username ${PODMAN_LOGIN_USER} \
--password-stdin \
--authfile=$authfile \
localhost:${PODMAN_LOGIN_REGISTRY_PORT} <<<"${PODMAN_LOGIN_PASS}"
is "$output" "Login Succeeded!" "output from podman login"

image1="localhost:${PODMAN_LOGIN_REGISTRY_PORT}/test:1.0"

run_podman tag $IMAGE $image1
run_podman push --authfile=$authfile \
--tls-verify=false $mid \
$image1
run_podman rmi $image1
run_podman pull -q --retry 4 --retry-delay "0s" --authfile=$authfile \
--tls-verify=false $image1
assert "${output:0:12}" = "$PODMAN_TEST_IMAGE_ID" "First pull (before stopping registry)"
run_podman rmi $image1

# This actually STOPs the registry, so the port is unbound...
pause_registry
# ...then, in eight seconds, we start it again
(sleep 8; unpause_registry) &
run_podman 0+w pull -q --retry 4 --retry-delay "5s" --authfile=$authfile \
--tls-verify=false $image1
assert "$output" =~ "Failed, retrying in 5s.*Error: initializing.* connection refused"
assert "${lines[-1]:0:12}" = "$PODMAN_TEST_IMAGE_ID" "push should succeed via retry"
unpause_registry

run_podman rmi $image1
}

# END cooperation with skopeo
# END actual tests
###############################################################################
Expand Down
20 changes: 20 additions & 0 deletions test/system/helpers.registry.bash
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,23 @@ function stop_registry() {
die "Socket still seems open"
fi
}

function pause_registry() {
if [[ ! -d "$PODMAN_LOGIN_WORKDIR/auth" ]]; then
# No registry running
return
fi

opts="--storage-driver vfs $(podman_isolation_opts ${PODMAN_LOGIN_WORKDIR})"
run_podman $opts stop registry
}

function unpause_registry() {
if [[ ! -d "$PODMAN_LOGIN_WORKDIR/auth" ]]; then
# No registry running
return
fi

opts="--storage-driver vfs $(podman_isolation_opts ${PODMAN_LOGIN_WORKDIR})"
run_podman $opts start registry
}
Loading