Skip to content

Commit

Permalink
Merge pull request #21659 from rhatdan/retry
Browse files Browse the repository at this point in the history
Allow podman pull to specify --retry and --retry-delay
  • Loading branch information
openshift-merge-bot[bot] authored Feb 19, 2024
2 parents 6abf785 + 7ba23cd commit 80b1e95
Show file tree
Hide file tree
Showing 13 changed files with 184 additions and 5 deletions.
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)

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
}

0 comments on commit 80b1e95

Please sign in to comment.