From 12f57e0494115d5f3936d3774763fd4be0f84bf0 Mon Sep 17 00:00:00 2001 From: Ygal Blum Date: Thu, 19 Jun 2025 20:17:20 -0400 Subject: [PATCH] Podman pull - add policy flag Signed-off-by: Ygal Blum --- cmd/podman/images/pull.go | 13 +++++ docs/source/markdown/podman-pull.1.md.in | 31 ++++++++++++ test/system/156-pull-policy.bats | 60 ++++++++++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 test/system/156-pull-policy.bats diff --git a/cmd/podman/images/pull.go b/cmd/podman/images/pull.go index ee452a4bf1..6b769189ca 100644 --- a/cmd/podman/images/pull.go +++ b/cmd/podman/images/pull.go @@ -9,6 +9,7 @@ import ( "github.com/containers/buildah/pkg/cli" "github.com/containers/common/pkg/auth" "github.com/containers/common/pkg/completion" + "github.com/containers/common/pkg/config" "github.com/containers/image/v5/types" "github.com/containers/podman/v5/cmd/podman/common" "github.com/containers/podman/v5/cmd/podman/registry" @@ -25,6 +26,7 @@ type pullOptionsWrapper struct { TLSVerifyCLI bool // CLI only CredentialsCLI string DecryptionKeys []string + PolicyCLI string } var ( @@ -101,6 +103,11 @@ func pullFlags(cmd *cobra.Command) { flags.String(platformFlagName, "", "Specify the platform for selecting the image. (Conflicts with arch and os)") _ = cmd.RegisterFlagCompletionFunc(platformFlagName, completion.AutocompleteNone) + policyFlagName := "policy" + // Explicitly set the default to "always" to avoid the default being "missing" + flags.StringVar(&pullOptions.PolicyCLI, policyFlagName, "always", `Pull image policy ("always"|"missing"|"never"|"newer")`) + _ = cmd.RegisterFlagCompletionFunc(policyFlagName, common.AutocompletePullOption) + flags.Bool("disable-content-trust", false, "This is a Docker specific option and is a NOOP") flags.BoolVarP(&pullOptions.Quiet, "quiet", "q", false, "Suppress output information when pulling images") flags.BoolVar(&pullOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") @@ -143,6 +150,12 @@ func imagePull(cmd *cobra.Command, args []string) error { pullOptions.SkipTLSVerify = types.NewOptionalBool(!pullOptions.TLSVerifyCLI) } + pullPolicy, err := config.ParsePullPolicy(pullOptions.PolicyCLI) + if err != nil { + return err + } + pullOptions.PullPolicy = pullPolicy + if cmd.Flags().Changed("retry") { retry, err := cmd.Flags().GetUint("retry") if err != nil { diff --git a/docs/source/markdown/podman-pull.1.md.in b/docs/source/markdown/podman-pull.1.md.in index 73606f00cd..71da30f788 100644 --- a/docs/source/markdown/podman-pull.1.md.in +++ b/docs/source/markdown/podman-pull.1.md.in @@ -69,6 +69,15 @@ Print the usage statement. @@option platform +#### **--policy** + +Pull image policy. The default is **always**. + +- `always`: Always pull the image and throw an error if the pull fails. +- `missing`: Only pull the image if it could not be found in the local containers storage. Throw an error if no image could be found and the pull fails. +- `never`: Never pull the image; only use the local version. Throw an error if the image is not present locally. +- `newer`: Pull if the image on the registry is newer than the one in the local containers storage. An image is considered to be newer when the digests are different. Comparing the time stamps is prone to errors. Pull errors are suppressed if a local image was found. + #### **--quiet**, **-q** Suppress output information when pulling images @@ -215,6 +224,28 @@ $ podman --remote pull -q --retry 6 --retry-delay 10s ubi9 4d6addf62a90e392ff6d3f470259eb5667eab5b9a8e03d20b41d0ab910f92170 ``` +Pull an image only if not present locally. +``` +$ podman pull --policy missing alpine:latest +``` + +Never pull the image, only use local version. +``` +$ podman pull --policy never alpine:latest +``` + +Always pull the image even if present locally. +``` +$ podman pull --policy always alpine:latest +Trying to pull docker.io/library/alpine:latest... +Getting image source signatures +Copying blob 5843afab3874 done +Copying config d4ff818577 done +Writing manifest to image destination +Storing signatures +d4ff818577bc193b309b355b02ebc9220427090057b54a59e73b79bdfe139b83 +``` + ## 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)** diff --git a/test/system/156-pull-policy.bats b/test/system/156-pull-policy.bats new file mode 100644 index 0000000000..3edc0ac32c --- /dev/null +++ b/test/system/156-pull-policy.bats @@ -0,0 +1,60 @@ +load helpers +load helpers.network +load helpers.registry + +function setup() { + skip_if_remote "tests depend on start_registry which does not work with podman-remote" + + basic_setup + start_registry +} + +@test "podman pull with policy flag" { + local registry=localhost:${PODMAN_LOGIN_REGISTRY_PORT} + local image_for_test=$registry/i-$(safename):$(random_string) + local authfile=$PODMAN_TMPDIR/authfile.json + + run_podman login --authfile=$authfile \ + --tls-verify=false \ + --username ${PODMAN_LOGIN_USER} \ + --password ${PODMAN_LOGIN_PASS} \ + $registry + + # Generate a test image and push it to the registry. + # For safety in parallel runs, test image must be isolated + # from $IMAGE. A simple add-tag will not work. (#23756) + run_podman create -q $IMAGE true + local tmpcid=$output + run_podman commit -q $tmpcid $image_for_test + local image_id=$output + run_podman rm $tmpcid + run_podman image push --tls-verify=false --authfile=$authfile $image_for_test + # Remove the local image to make sure it will be pulled again + run_podman image rm --ignore $image_for_test + + # Test invalid policy + run_podman 125 pull --tls-verify=false --authfile $authfile --policy invalid $image_for_test + assert "$output" = "Error: unsupported pull policy \"invalid\"" + + # Test policy=never with image not present + run_podman 125 pull --tls-verify=false --authfile $authfile --policy never $image_for_test + assert "$output" = "Error: $image_for_test: image not known" + + # Test policy=missing with image not present (should succeed) + run_podman pull --tls-verify=false --authfile $authfile --policy missing $image_for_test + assert "$output" =~ "Writing manifest to image destination" + + # Test policy=missing with image present (should not pull again) + run_podman pull --tls-verify=false --authfile $authfile --policy missing $image_for_test + assert "$output" = $image_id + + # Test policy=always (should always pull) + run_podman pull --tls-verify=false --authfile $authfile --policy always $image_for_test + assert "$output" =~ "Writing manifest to image destination" + + # Test policy=newer with image present and no new image(should not pull again) + run_podman pull --tls-verify=false --authfile $authfile --policy newer $image_for_test + assert "$output" = $image_id + + run_podman image rm --ignore $image_for_test +}