github.com/containers/podman/v5@v5.1.0-rc1/test/e2e/pull_test.go (about)

     1  package integration
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"runtime"
     8  
     9  	. "github.com/containers/podman/v5/test/utils"
    10  	. "github.com/onsi/ginkgo/v2"
    11  	. "github.com/onsi/gomega"
    12  	. "github.com/onsi/gomega/gexec"
    13  )
    14  
    15  var _ = Describe("Podman pull", func() {
    16  
    17  	It("podman pull multiple images with/without tag/digest", func() {
    18  		session := podmanTest.Podman([]string{"pull", "-q", "busybox:musl", "alpine", "alpine:latest", "quay.io/libpod/cirros", "quay.io/libpod/testdigest_v2s2@sha256:755f4d90b3716e2bf57060d249e2cd61c9ac089b1233465c5c2cb2d7ee550fdb"})
    19  		session.WaitWithDefaultTimeout()
    20  		Expect(session).Should(ExitCleanly())
    21  
    22  		session = podmanTest.Podman([]string{"pull", "busybox:latest", "docker.io/library/ibetthisdoesnotexistfr:random", "alpine"})
    23  		session.WaitWithDefaultTimeout()
    24  		Expect(session).Should(ExitWithError(125, "initializing source docker://ibetthisdoesnotexistfr:random: reading manifest random in quay.io/libpod/ibetthisdoesnotexistfr:"))
    25  
    26  		session = podmanTest.Podman([]string{"rmi", "busybox:musl", "alpine", "quay.io/libpod/cirros", "testdigest_v2s2@sha256:755f4d90b3716e2bf57060d249e2cd61c9ac089b1233465c5c2cb2d7ee550fdb"})
    27  		session.WaitWithDefaultTimeout()
    28  		Expect(session).Should(ExitCleanly())
    29  	})
    30  
    31  	It("podman pull bogus image", func() {
    32  		// This is a NOP in CI; but in a developer environment, if user
    33  		// has a valid login to quay.io, pull fails with "repository not found"
    34  		defer func() {
    35  			os.Unsetenv("REGISTRY_AUTH_FILE")
    36  		}()
    37  		os.Setenv("REGISTRY_AUTH_FILE", "/tmp/this/does/not/exist")
    38  
    39  		session := podmanTest.Podman([]string{"pull", "quay.io/libpod/ibetthisdoesntexist:there"})
    40  		session.WaitWithDefaultTimeout()
    41  		Expect(session).To(ExitWithError(125, "nitializing source docker://quay.io/libpod/ibetthisdoesntexist:there: reading manifest there in quay.io/libpod/ibetthisdoesntexist: unauthorized: access to the requested resource is not authorized"))
    42  	})
    43  
    44  	It("podman pull with tag --quiet", func() {
    45  		session := podmanTest.Podman([]string{"pull", "-q", "quay.io/libpod/testdigest_v2s2:20200210"})
    46  		session.WaitWithDefaultTimeout()
    47  		Expect(session).Should(ExitCleanly())
    48  		quietOutput := session.OutputToString()
    49  
    50  		session = podmanTest.Podman([]string{"inspect", "testdigest_v2s2:20200210", "--format", "{{.ID}}"})
    51  		session.WaitWithDefaultTimeout()
    52  		Expect(session).Should(ExitCleanly())
    53  		Expect(session.OutputToString()).To(Equal(quietOutput))
    54  
    55  		session = podmanTest.Podman([]string{"rmi", "testdigest_v2s2:20200210"})
    56  		session.WaitWithDefaultTimeout()
    57  		Expect(session).Should(ExitCleanly())
    58  	})
    59  
    60  	It("podman pull without tag", func() {
    61  		session := podmanTest.Podman([]string{"pull", "-q", "quay.io/libpod/testdigest_v2s2"})
    62  		session.WaitWithDefaultTimeout()
    63  		Expect(session).Should(ExitCleanly())
    64  
    65  		session = podmanTest.Podman([]string{"rmi", "testdigest_v2s2"})
    66  		session.WaitWithDefaultTimeout()
    67  		Expect(session).Should(ExitCleanly())
    68  	})
    69  
    70  	It("podman pull and run on split imagestore", func() {
    71  		SkipIfRemote("podman-remote does not support setting external imagestore")
    72  		imgName := "splitstoretest"
    73  
    74  		// Make alpine write-able
    75  		session := podmanTest.Podman([]string{"build", "--pull=never", "--tag", imgName, "build/basicalpine"})
    76  		session.WaitWithDefaultTimeout()
    77  		Expect(session).Should(ExitCleanly())
    78  
    79  		tmpDir := filepath.Join(podmanTest.TempDir, "splitstore")
    80  		outfile := filepath.Join(podmanTest.TempDir, "image.tar")
    81  
    82  		save := podmanTest.Podman([]string{"save", "-q", "-o", outfile, "--format", "oci-archive", imgName})
    83  		save.WaitWithDefaultTimeout()
    84  		Expect(save).Should(ExitCleanly())
    85  
    86  		rmi := podmanTest.Podman([]string{"rmi", imgName})
    87  		rmi.WaitWithDefaultTimeout()
    88  		Expect(rmi).Should(ExitCleanly())
    89  
    90  		// load to splitstore
    91  		result := podmanTest.Podman([]string{"load", "-q", "--imagestore", tmpDir, "-q", "-i", outfile})
    92  		result.WaitWithDefaultTimeout()
    93  		Expect(result).Should(ExitCleanly())
    94  
    95  		// tag busybox to busybox-test in graphroot since we can delete readonly busybox
    96  		session = podmanTest.Podman([]string{"tag", "quay.io/libpod/busybox:latest", "busybox-test"})
    97  		session.WaitWithDefaultTimeout()
    98  		Expect(session).Should(ExitCleanly())
    99  
   100  		session = podmanTest.Podman([]string{"images", "--imagestore", tmpDir})
   101  		session.WaitWithDefaultTimeout()
   102  		Expect(session).Should(ExitCleanly())
   103  		Expect(session.OutputToString()).To(ContainSubstring(imgName))
   104  		Expect(session.OutputToString()).To(ContainSubstring("busybox-test"))
   105  
   106  		// Test deleting image in graphroot even when `--imagestore` is set
   107  		session = podmanTest.Podman([]string{"rmi", "--imagestore", tmpDir, "busybox-test"})
   108  		session.WaitWithDefaultTimeout()
   109  		Expect(session).Should(ExitCleanly())
   110  
   111  		// Images without --imagestore should not contain alpine
   112  		session = podmanTest.Podman([]string{"images"})
   113  		session.WaitWithDefaultTimeout()
   114  		Expect(session).Should(ExitCleanly())
   115  		Expect(session.OutputToString()).To(Not(ContainSubstring(imgName)))
   116  
   117  		// Set `imagestore` in `storage.conf` and container should run.
   118  		configPath := filepath.Join(podmanTest.TempDir, ".config", "containers", "storage.conf")
   119  		os.Setenv("CONTAINERS_STORAGE_CONF", configPath)
   120  		defer func() {
   121  			os.Unsetenv("CONTAINERS_STORAGE_CONF")
   122  		}()
   123  
   124  		err = os.MkdirAll(filepath.Dir(configPath), os.ModePerm)
   125  		Expect(err).ToNot(HaveOccurred())
   126  		storageConf := []byte(fmt.Sprintf("[storage]\nimagestore=\"%s\"", tmpDir))
   127  		err = os.WriteFile(configPath, storageConf, os.ModePerm)
   128  		Expect(err).ToNot(HaveOccurred())
   129  
   130  		session = podmanTest.Podman([]string{"run", "--name", "test", "--rm",
   131  			imgName, "echo", "helloworld"})
   132  		session.WaitWithDefaultTimeout()
   133  		Expect(session).Should(Exit(0))
   134  		Expect(session.OutputToString()).To(ContainSubstring("helloworld"))
   135  		Expect(session.ErrorToString()).To(ContainSubstring("The storage 'driver' option should be set in "))
   136  		Expect(session.ErrorToString()).To(ContainSubstring("A driver was picked automatically."))
   137  	})
   138  
   139  	It("podman pull by digest", func() {
   140  		session := podmanTest.Podman([]string{"pull", "-q", "quay.io/libpod/testdigest_v2s2@sha256:755f4d90b3716e2bf57060d249e2cd61c9ac089b1233465c5c2cb2d7ee550fdb"})
   141  		session.WaitWithDefaultTimeout()
   142  		Expect(session).Should(ExitCleanly())
   143  
   144  		// Without a tag/digest the input is normalized with the "latest" tag, see #11964
   145  		session = podmanTest.Podman([]string{"rmi", "testdigest_v2s2"})
   146  		session.WaitWithDefaultTimeout()
   147  		Expect(session).Should(ExitWithError(1, "testdigest_v2s2: image not known"))
   148  
   149  		session = podmanTest.Podman([]string{"rmi", "testdigest_v2s2@sha256:755f4d90b3716e2bf57060d249e2cd61c9ac089b1233465c5c2cb2d7ee550fdb"})
   150  		session.WaitWithDefaultTimeout()
   151  		Expect(session).Should(ExitCleanly())
   152  	})
   153  
   154  	It("podman pull check all tags", func() {
   155  		session := podmanTest.Podman([]string{"pull", "-q", "--all-tags", "quay.io/libpod/testdigest_v2s2"})
   156  		session.WaitWithDefaultTimeout()
   157  		Expect(session).Should(ExitCleanly())
   158  
   159  		session = podmanTest.Podman([]string{"images"})
   160  		session.WaitWithDefaultTimeout()
   161  		Expect(session).Should(ExitCleanly())
   162  		Expect(len(session.OutputToStringArray())).To(BeNumerically(">=", 2), "Expected at least two images")
   163  
   164  		session = podmanTest.Podman([]string{"pull", "-q", "-a", "quay.io/libpod/testdigest_v2s2"})
   165  		session.WaitWithDefaultTimeout()
   166  		Expect(session).Should(ExitCleanly())
   167  
   168  		session = podmanTest.Podman([]string{"images"})
   169  		session.WaitWithDefaultTimeout()
   170  		Expect(session).Should(ExitCleanly())
   171  		Expect(len(session.OutputToStringArray())).To(BeNumerically(">=", 2), "Expected at least two images")
   172  	})
   173  
   174  	It("podman pull from docker with nonexistent --authfile", func() {
   175  		session := podmanTest.Podman([]string{"pull", "-q", "--authfile", "/tmp/nonexistent", ALPINE})
   176  		session.WaitWithDefaultTimeout()
   177  		Expect(session).To(ExitWithError(125, "credential file is not accessible: faccessat /tmp/nonexistent: no such file or directory"))
   178  	})
   179  
   180  	It("podman pull by digest (image list)", func() {
   181  		session := podmanTest.Podman([]string{"pull", "-q", "--arch=arm64", ALPINELISTDIGEST})
   182  		session.WaitWithDefaultTimeout()
   183  		Expect(session).Should(ExitCleanly())
   184  		// inspect using the digest of the list
   185  		session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoTags}}", ALPINELISTDIGEST})
   186  		session.WaitWithDefaultTimeout()
   187  		Expect(session).Should(ExitCleanly())
   188  		Expect(string(session.Out.Contents())).To(HavePrefix("[]"))
   189  		// inspect using the digest of the list
   190  		session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoDigests}}", ALPINELISTDIGEST})
   191  		session.WaitWithDefaultTimeout()
   192  		Expect(session).Should(ExitCleanly())
   193  		Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTDIGEST))
   194  		Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64DIGEST))
   195  		// inspect using the digest of the arch-specific image's manifest
   196  		session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoTags}}", ALPINEARM64DIGEST})
   197  		session.WaitWithDefaultTimeout()
   198  		Expect(session).Should(ExitCleanly())
   199  		Expect(string(session.Out.Contents())).To(HavePrefix("[]"))
   200  		// inspect using the digest of the arch-specific image's manifest
   201  		session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoDigests}}", ALPINEARM64DIGEST})
   202  		session.WaitWithDefaultTimeout()
   203  		Expect(session).Should(ExitCleanly())
   204  		Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTDIGEST))
   205  		Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64DIGEST))
   206  		// inspect using the image ID
   207  		session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoTags}}", ALPINEARM64ID})
   208  		session.WaitWithDefaultTimeout()
   209  		Expect(session).Should(ExitCleanly())
   210  		Expect(string(session.Out.Contents())).To(HavePrefix("[]"))
   211  		// inspect using the image ID
   212  		session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoDigests}}", ALPINEARM64ID})
   213  		session.WaitWithDefaultTimeout()
   214  		Expect(session).Should(ExitCleanly())
   215  		Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTDIGEST))
   216  		Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64DIGEST))
   217  		// remove using the digest of the list
   218  		session = podmanTest.Podman([]string{"rmi", ALPINELISTDIGEST})
   219  		session.WaitWithDefaultTimeout()
   220  		Expect(session).Should(ExitCleanly())
   221  	})
   222  
   223  	It("podman pull by instance digest (image list)", func() {
   224  		session := podmanTest.Podman([]string{"pull", "-q", "--arch=arm64", ALPINEARM64DIGEST})
   225  		session.WaitWithDefaultTimeout()
   226  		Expect(session).Should(ExitCleanly())
   227  
   228  		// inspect using the digest of the list
   229  		session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoTags}}", ALPINELISTDIGEST})
   230  		session.WaitWithDefaultTimeout()
   231  		Expect(session).To(ExitWithError(125, fmt.Sprintf(`no such object: "%s"`, ALPINELISTDIGEST)))
   232  		// inspect using the digest of the list
   233  		session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoDigests}}", ALPINELISTDIGEST})
   234  		session.WaitWithDefaultTimeout()
   235  		Expect(session).To(ExitWithError(125, fmt.Sprintf(`no such object: "%s"`, ALPINELISTDIGEST)))
   236  
   237  		// inspect using the digest of the arch-specific image's manifest
   238  		session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoTags}}", ALPINEARM64DIGEST})
   239  		session.WaitWithDefaultTimeout()
   240  		Expect(session).Should(ExitCleanly())
   241  		Expect(string(session.Out.Contents())).To(HavePrefix("[]"))
   242  		// inspect using the digest of the arch-specific image's manifest
   243  		session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoDigests}}", ALPINEARM64DIGEST})
   244  		session.WaitWithDefaultTimeout()
   245  		Expect(session).Should(ExitCleanly())
   246  		Expect(string(session.Out.Contents())).To(Not(ContainSubstring(ALPINELISTDIGEST)))
   247  		Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64DIGEST))
   248  		// inspect using the image ID
   249  		session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoTags}}", ALPINEARM64ID})
   250  		session.WaitWithDefaultTimeout()
   251  		Expect(session).Should(ExitCleanly())
   252  		Expect(string(session.Out.Contents())).To(HavePrefix("[]"))
   253  		// inspect using the image ID
   254  		session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoDigests}}", ALPINEARM64ID})
   255  		session.WaitWithDefaultTimeout()
   256  		Expect(session).Should(ExitCleanly())
   257  		Expect(string(session.Out.Contents())).To(Not(ContainSubstring(ALPINELISTDIGEST)))
   258  		Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64DIGEST))
   259  		// remove using the digest of the instance
   260  		session = podmanTest.Podman([]string{"rmi", ALPINEARM64DIGEST})
   261  		session.WaitWithDefaultTimeout()
   262  		Expect(session).Should(ExitCleanly())
   263  	})
   264  
   265  	It("podman pull by tag (image list)", func() {
   266  		session := podmanTest.Podman([]string{"pull", "-q", "--arch=arm64", ALPINELISTTAG})
   267  		session.WaitWithDefaultTimeout()
   268  		Expect(session).Should(ExitCleanly())
   269  		// inspect using the tag we used for pulling
   270  		session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoTags}}", ALPINELISTTAG})
   271  		session.WaitWithDefaultTimeout()
   272  		Expect(session).Should(ExitCleanly())
   273  		Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTTAG))
   274  		// inspect using the tag we used for pulling
   275  		session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoDigests}}", ALPINELISTTAG})
   276  		session.WaitWithDefaultTimeout()
   277  		Expect(session).Should(ExitCleanly())
   278  		Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTDIGEST))
   279  		Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64DIGEST))
   280  		// inspect using the digest of the list
   281  		session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoTags}}", ALPINELISTDIGEST})
   282  		session.WaitWithDefaultTimeout()
   283  		Expect(session).Should(ExitCleanly())
   284  		Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTTAG))
   285  		// inspect using the digest of the list
   286  		session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoDigests}}", ALPINELISTDIGEST})
   287  		session.WaitWithDefaultTimeout()
   288  		Expect(session).Should(ExitCleanly())
   289  		Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTDIGEST))
   290  		Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64DIGEST))
   291  		// inspect using the digest of the arch-specific image's manifest
   292  		session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoTags}}", ALPINEARM64DIGEST})
   293  		session.WaitWithDefaultTimeout()
   294  		Expect(session).Should(ExitCleanly())
   295  		Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTTAG))
   296  		// inspect using the digest of the arch-specific image's manifest
   297  		session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoDigests}}", ALPINEARM64DIGEST})
   298  		session.WaitWithDefaultTimeout()
   299  		Expect(session).Should(ExitCleanly())
   300  		Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTDIGEST))
   301  		Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64DIGEST))
   302  		// inspect using the image ID
   303  		session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoTags}}", ALPINEARM64ID})
   304  		session.WaitWithDefaultTimeout()
   305  		Expect(session).Should(ExitCleanly())
   306  		Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTTAG))
   307  		// inspect using the image ID
   308  		session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoDigests}}", ALPINEARM64ID})
   309  		session.WaitWithDefaultTimeout()
   310  		Expect(session).Should(ExitCleanly())
   311  		Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTDIGEST))
   312  		Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64DIGEST))
   313  		// remove using the tag
   314  		session = podmanTest.Podman([]string{"rmi", ALPINELISTTAG})
   315  		session.WaitWithDefaultTimeout()
   316  		Expect(session).Should(ExitCleanly())
   317  	})
   318  
   319  	It("podman pull from docker-archive", func() {
   320  		SkipIfRemote("podman-remote does not support pulling from docker-archive")
   321  
   322  		podmanTest.AddImageToRWStore(CIRROS_IMAGE)
   323  		tarfn := filepath.Join(podmanTest.TempDir, "cirros.tar")
   324  		session := podmanTest.Podman([]string{"save", "-q", "-o", tarfn, "cirros"})
   325  		session.WaitWithDefaultTimeout()
   326  
   327  		Expect(session).Should(ExitCleanly())
   328  		session = podmanTest.Podman([]string{"rmi", "cirros"})
   329  		session.WaitWithDefaultTimeout()
   330  		Expect(session).Should(ExitCleanly())
   331  		session = podmanTest.Podman([]string{"pull", "-q", fmt.Sprintf("docker-archive:%s", tarfn)})
   332  		session.WaitWithDefaultTimeout()
   333  		Expect(session).Should(ExitCleanly())
   334  		session = podmanTest.Podman([]string{"rmi", "cirros"})
   335  		session.WaitWithDefaultTimeout()
   336  		Expect(session).Should(ExitCleanly())
   337  
   338  		// Pulling a multi-image archive without further specifying
   339  		// which image _must_ error out. Pulling is restricted to one
   340  		// image.
   341  		session = podmanTest.Podman([]string{"pull", "-q", "docker-archive:./testdata/docker-two-images.tar.xz"})
   342  		session.WaitWithDefaultTimeout()
   343  		Expect(session).Should(ExitWithError(125, "Unexpected tar manifest.json: expected 1 item, got 2"))
   344  
   345  		// Now pull _one_ image from a multi-image archive via the name
   346  		// and index syntax.
   347  		session = podmanTest.Podman([]string{"pull", "-q", "docker-archive:./testdata/docker-two-images.tar.xz:@0"})
   348  		session.WaitWithDefaultTimeout()
   349  		Expect(session).Should(ExitCleanly())
   350  
   351  		session = podmanTest.Podman([]string{"pull", "-q", "docker-archive:./testdata/docker-two-images.tar.xz:example.com/empty:latest"})
   352  		session.WaitWithDefaultTimeout()
   353  		Expect(session).Should(ExitCleanly())
   354  
   355  		session = podmanTest.Podman([]string{"pull", "-q", "docker-archive:./testdata/docker-two-images.tar.xz:@1"})
   356  		session.WaitWithDefaultTimeout()
   357  		Expect(session).Should(ExitCleanly())
   358  
   359  		session = podmanTest.Podman([]string{"pull", "-q", "docker-archive:./testdata/docker-two-images.tar.xz:example.com/empty/but:different"})
   360  		session.WaitWithDefaultTimeout()
   361  		Expect(session).Should(ExitCleanly())
   362  
   363  		// Now check for some errors.
   364  		session = podmanTest.Podman([]string{"pull", "-q", "docker-archive:./testdata/docker-two-images.tar.xz:foo.com/does/not/exist:latest"})
   365  		session.WaitWithDefaultTimeout()
   366  		Expect(session).Should(ExitWithError(125, `Tag "foo.com/does/not/exist:latest" not found`))
   367  
   368  		session = podmanTest.Podman([]string{"pull", "-q", "docker-archive:./testdata/docker-two-images.tar.xz:@2"})
   369  		session.WaitWithDefaultTimeout()
   370  		Expect(session).Should(ExitWithError(125, "Invalid source index @2, only 2 manifest items available"))
   371  	})
   372  
   373  	It("podman pull from oci-archive", func() {
   374  		SkipIfRemote("podman-remote does not support pulling from oci-archive")
   375  
   376  		podmanTest.AddImageToRWStore(CIRROS_IMAGE)
   377  		tarfn := filepath.Join(podmanTest.TempDir, "oci-cirrus.tar")
   378  		session := podmanTest.Podman([]string{"save", "-q", "--format", "oci-archive", "-o", tarfn, "cirros"})
   379  		session.WaitWithDefaultTimeout()
   380  
   381  		Expect(session).Should(ExitCleanly())
   382  		session = podmanTest.Podman([]string{"rmi", "cirros"})
   383  		session.WaitWithDefaultTimeout()
   384  		Expect(session).Should(ExitCleanly())
   385  		session = podmanTest.Podman([]string{"pull", "-q", fmt.Sprintf("oci-archive:%s", tarfn)})
   386  		session.WaitWithDefaultTimeout()
   387  		Expect(session).Should(ExitCleanly())
   388  		session = podmanTest.Podman([]string{"rmi", "cirros"})
   389  		session.WaitWithDefaultTimeout()
   390  		Expect(session).Should(ExitCleanly())
   391  	})
   392  
   393  	It("podman pull from local directory", func() {
   394  		SkipIfRemote("podman-remote does not support pulling from local directory")
   395  
   396  		podmanTest.AddImageToRWStore(CIRROS_IMAGE)
   397  		dirpath := filepath.Join(podmanTest.TempDir, "cirros")
   398  		err = os.MkdirAll(dirpath, os.ModePerm)
   399  		Expect(err).ToNot(HaveOccurred())
   400  		imgPath := fmt.Sprintf("dir:%s", dirpath)
   401  
   402  		session := podmanTest.Podman([]string{"push", "-q", "cirros", imgPath})
   403  		session.WaitWithDefaultTimeout()
   404  		Expect(session).Should(ExitCleanly())
   405  		session = podmanTest.Podman([]string{"rmi", "cirros"})
   406  		session.WaitWithDefaultTimeout()
   407  		Expect(session).Should(ExitCleanly())
   408  		session = podmanTest.Podman([]string{"run", imgPath, "ls"})
   409  		session.WaitWithDefaultTimeout()
   410  		Expect(session).Should(Exit(0))
   411  		Expect(session.ErrorToString()).To(ContainSubstring("Copying blob"), "Image is pulled on run")
   412  
   413  		// Note that reference is not preserved in dir.
   414  		session = podmanTest.Podman([]string{"image", "exists", "cirros"})
   415  		session.WaitWithDefaultTimeout()
   416  		Expect(session).Should(Exit(1))
   417  	})
   418  
   419  	It("podman pull from local OCI directory", func() {
   420  		SkipIfRemote("podman-remote does not support pulling from OCI directory")
   421  
   422  		podmanTest.AddImageToRWStore(CIRROS_IMAGE)
   423  		dirpath := filepath.Join(podmanTest.TempDir, "cirros")
   424  		err = os.MkdirAll(dirpath, os.ModePerm)
   425  		Expect(err).ToNot(HaveOccurred())
   426  		imgName := "localhost/name:tag"
   427  		imgPath := fmt.Sprintf("oci:%s:%s", dirpath, imgName)
   428  
   429  		session := podmanTest.Podman([]string{"push", "-q", "cirros", imgPath})
   430  		session.WaitWithDefaultTimeout()
   431  		Expect(session).Should(ExitCleanly())
   432  		session = podmanTest.Podman([]string{"rmi", "cirros"})
   433  		session.WaitWithDefaultTimeout()
   434  		Expect(session).Should(ExitCleanly())
   435  		session = podmanTest.Podman([]string{"pull", "-q", imgPath})
   436  		session.WaitWithDefaultTimeout()
   437  		Expect(session).Should(ExitCleanly())
   438  		session = podmanTest.Podman([]string{"image", "exists", imgName})
   439  		session.WaitWithDefaultTimeout()
   440  		Expect(session).Should(ExitCleanly())
   441  	})
   442  
   443  	It("podman pull + inspect from unqualified-search registry", func() {
   444  		// Regression test for #6381:
   445  		// Make sure that `pull shortname` and `inspect shortname`
   446  		// refer to the same image.
   447  
   448  		// We already tested pulling, so we can save some energy and
   449  		// just restore local artifacts and tag them.
   450  		err := podmanTest.RestoreArtifact(ALPINE)
   451  		Expect(err).ToNot(HaveOccurred())
   452  		err = podmanTest.RestoreArtifact(BB)
   453  		Expect(err).ToNot(HaveOccurred())
   454  
   455  		// What we want is at least two images which have the same name
   456  		// and are prefixed with two different unqualified-search
   457  		// registries from ../registries.conf.
   458  		//
   459  		// A `podman inspect $name` must yield the one from the _first_
   460  		// matching registry in the registries.conf.
   461  		getID := func(image string) string {
   462  			setup := podmanTest.Podman([]string{"image", "inspect", image})
   463  			setup.WaitWithDefaultTimeout()
   464  			Expect(setup).Should(ExitCleanly())
   465  
   466  			data := setup.InspectImageJSON() // returns []inspect.ImageData
   467  			Expect(data).To(HaveLen(1))
   468  			return data[0].ID
   469  		}
   470  
   471  		untag := func(image string) {
   472  			setup := podmanTest.Podman([]string{"untag", image})
   473  			setup.WaitWithDefaultTimeout()
   474  			Expect(setup).Should(ExitCleanly())
   475  
   476  			setup = podmanTest.Podman([]string{"image", "inspect", image})
   477  			setup.WaitWithDefaultTimeout()
   478  			Expect(setup).Should(ExitCleanly())
   479  
   480  			data := setup.InspectImageJSON() // returns []inspect.ImageData
   481  			Expect(data).To(HaveLen(1))
   482  			Expect(data[0].RepoTags).To(BeEmpty())
   483  		}
   484  
   485  		tag := func(image, tag string) {
   486  			setup := podmanTest.Podman([]string{"tag", image, tag})
   487  			setup.WaitWithDefaultTimeout()
   488  			Expect(setup).Should(ExitCleanly())
   489  			setup = podmanTest.Podman([]string{"image", "exists", tag})
   490  			setup.WaitWithDefaultTimeout()
   491  			Expect(setup).Should(ExitCleanly())
   492  		}
   493  
   494  		image1 := getID(ALPINE)
   495  		image2 := getID(BB)
   496  
   497  		// $ head -n2 ../registries.conf
   498  		// [registries.search]
   499  		// registries = ['docker.io', 'quay.io', 'registry.fedoraproject.org']
   500  		registries := []string{"docker.io", "quay.io", "registry.fedoraproject.org"}
   501  		name := "foo/test:tag"
   502  		tests := []struct {
   503  			// tag1 has precedence (see list above) over tag2 when
   504  			// doing an inspect on "test:tag".
   505  			tag1, tag2 string
   506  		}{
   507  			{
   508  				fmt.Sprintf("%s/%s", registries[0], name),
   509  				fmt.Sprintf("%s/%s", registries[1], name),
   510  			},
   511  			{
   512  				fmt.Sprintf("%s/%s", registries[0], name),
   513  				fmt.Sprintf("%s/%s", registries[2], name),
   514  			},
   515  			{
   516  				fmt.Sprintf("%s/%s", registries[1], name),
   517  				fmt.Sprintf("%s/%s", registries[2], name),
   518  			},
   519  		}
   520  
   521  		for _, t := range tests {
   522  			// 1) untag both images
   523  			// 2) tag them according to `t`
   524  			// 3) make sure that an inspect of `name` returns `image1` with `tag1`
   525  			untag(image1)
   526  			untag(image2)
   527  			tag(image1, t.tag1)
   528  			tag(image2, t.tag2)
   529  
   530  			setup := podmanTest.Podman([]string{"image", "inspect", name})
   531  			setup.WaitWithDefaultTimeout()
   532  			Expect(setup).Should(ExitCleanly())
   533  
   534  			data := setup.InspectImageJSON() // returns []inspect.ImageData
   535  			Expect(data).To(HaveLen(1))
   536  			Expect(data[0].RepoTags).To(HaveLen(1))
   537  			Expect(data[0].RepoTags[0]).To(Equal(t.tag1))
   538  			Expect(data[0]).To(HaveField("ID", image1))
   539  		}
   540  	})
   541  
   542  	It("podman pull --platform", func() {
   543  		session := podmanTest.Podman([]string{"pull", "-q", "--platform=linux/bogus", ALPINE})
   544  		session.WaitWithDefaultTimeout()
   545  		Expect(session).Should(ExitWithError(125, "no image found in manifest list for architecture bogus"))
   546  
   547  		session = podmanTest.Podman([]string{"pull", "-q", "--platform=linux/arm64", "--os", "windows", ALPINE})
   548  		session.WaitWithDefaultTimeout()
   549  		Expect(session).Should(ExitWithError(125, "--platform option can not be specified with --arch or --os"))
   550  
   551  		session = podmanTest.Podman([]string{"pull", "-q", "--platform=linux/arm64", ALPINE})
   552  		session.WaitWithDefaultTimeout()
   553  		Expect(session).Should(ExitCleanly())
   554  
   555  		setup := podmanTest.Podman([]string{"image", "inspect", session.OutputToString()})
   556  		setup.WaitWithDefaultTimeout()
   557  		Expect(setup).Should(ExitCleanly())
   558  
   559  		data := setup.InspectImageJSON() // returns []inspect.ImageData
   560  		Expect(data).To(HaveLen(1))
   561  		Expect(data[0]).To(HaveField("Os", runtime.GOOS))
   562  		Expect(data[0]).To(HaveField("Architecture", "arm64"))
   563  	})
   564  
   565  	It("podman pull --arch", func() {
   566  		session := podmanTest.Podman([]string{"pull", "-q", "--arch=bogus", ALPINE})
   567  		session.WaitWithDefaultTimeout()
   568  		Expect(session).Should(ExitWithError(125, "no image found in manifest list for architecture bogus"))
   569  
   570  		session = podmanTest.Podman([]string{"pull", "-q", "--arch=arm64", "--os", "windows", ALPINE})
   571  		session.WaitWithDefaultTimeout()
   572  		Expect(session).Should(ExitWithError(125, "no image found in manifest list for architecture"))
   573  
   574  		session = podmanTest.Podman([]string{"pull", "-q", "--arch=arm64", ALPINE})
   575  		session.WaitWithDefaultTimeout()
   576  		Expect(session).Should(ExitCleanly())
   577  
   578  		setup := podmanTest.Podman([]string{"image", "inspect", session.OutputToString()})
   579  		setup.WaitWithDefaultTimeout()
   580  		Expect(setup).Should(ExitCleanly())
   581  
   582  		data := setup.InspectImageJSON() // returns []inspect.ImageData
   583  		Expect(data).To(HaveLen(1))
   584  		Expect(data[0]).To(HaveField("Os", runtime.GOOS))
   585  		Expect(data[0]).To(HaveField("Architecture", "arm64"))
   586  	})
   587  
   588  	It("podman pull progress", func() {
   589  		session := podmanTest.Podman([]string{"pull", ALPINE})
   590  		session.WaitWithDefaultTimeout()
   591  		Expect(session).Should(Exit(0))
   592  		output := session.ErrorToString()
   593  		Expect(output).To(ContainSubstring("Getting image source signatures"))
   594  		Expect(output).To(ContainSubstring("Copying blob "))
   595  
   596  		session = podmanTest.Podman([]string{"pull", "-q", ALPINE})
   597  		session.WaitWithDefaultTimeout()
   598  		Expect(session).Should(ExitCleanly())
   599  	})
   600  
   601  	Describe("podman pull and decrypt", func() {
   602  
   603  		decryptionTestHelper := func(imgPath string, expectedError1 string) *PodmanSessionIntegration {
   604  			bitSize := 1024
   605  			keyFileName := filepath.Join(podmanTest.TempDir, "key,withcomma")
   606  			publicKeyFileName, privateKeyFileName, err := WriteRSAKeyPair(keyFileName, bitSize)
   607  			Expect(err).ToNot(HaveOccurred())
   608  
   609  			wrongKeyFileName := filepath.Join(podmanTest.TempDir, "wrong_key")
   610  			_, wrongPrivateKeyFileName, err := WriteRSAKeyPair(wrongKeyFileName, bitSize)
   611  			Expect(err).ToNot(HaveOccurred())
   612  
   613  			session := podmanTest.Podman([]string{"push", "-q", "--encryption-key", "jwe:" + publicKeyFileName, "--tls-verify=false", "--remove-signatures", ALPINE, imgPath})
   614  			session.WaitWithDefaultTimeout()
   615  
   616  			session = podmanTest.Podman([]string{"rmi", ALPINE})
   617  			session.WaitWithDefaultTimeout()
   618  			Expect(session).Should(ExitCleanly())
   619  
   620  			// Pulling encrypted image without key should fail
   621  			session = podmanTest.Podman([]string{"pull", imgPath})
   622  			session.WaitWithDefaultTimeout()
   623  			Expect(session).Should(ExitWithError(125, expectedError1))
   624  
   625  			// Pulling encrypted image with wrong key should fail
   626  			session = podmanTest.Podman([]string{"pull", "-q", "--decryption-key", wrongPrivateKeyFileName, "--tls-verify=false", imgPath})
   627  			session.WaitWithDefaultTimeout()
   628  			Expect(session).Should(ExitWithError(125, "no suitable key unwrapper found or none of the private keys could be used for decryption"))
   629  
   630  			// Pulling encrypted image with correct key should pass
   631  			session = podmanTest.Podman([]string{"pull", "-q", "--decryption-key", privateKeyFileName, "--tls-verify=false", imgPath})
   632  			session.WaitWithDefaultTimeout()
   633  			Expect(session).Should(ExitCleanly())
   634  			session = podmanTest.Podman([]string{"images"})
   635  			session.WaitWithDefaultTimeout()
   636  			Expect(session).Should(ExitCleanly())
   637  
   638  			return session
   639  		}
   640  
   641  		It("From oci", func() {
   642  			SkipIfRemote("Remote pull neither supports oci transport, nor decryption")
   643  
   644  			podmanTest.AddImageToRWStore(ALPINE)
   645  
   646  			bbdir := filepath.Join(podmanTest.TempDir, "busybox-oci")
   647  			imgName := "localhost/name:tag"
   648  			imgPath := fmt.Sprintf("oci:%s:%s", bbdir, imgName)
   649  
   650  			session := decryptionTestHelper(imgPath, "invalid tar header")
   651  
   652  			Expect(session.LineInOutputContainsTag("localhost/name", "tag")).To(BeTrue())
   653  		})
   654  
   655  		It("From local registry", func() {
   656  			SkipIfRemote("Remote pull does not support decryption")
   657  
   658  			if podmanTest.Host.Arch == "ppc64le" {
   659  				Skip("No registry image for ppc64le")
   660  			}
   661  
   662  			podmanTest.AddImageToRWStore(ALPINE)
   663  
   664  			if isRootless() {
   665  				err := podmanTest.RestoreArtifact(REGISTRY_IMAGE)
   666  				Expect(err).ToNot(HaveOccurred())
   667  			}
   668  			lock := GetPortLock("5012")
   669  			defer lock.Unlock()
   670  			session := podmanTest.Podman([]string{"run", "-d", "--name", "registry", "-p", "5012:5000", REGISTRY_IMAGE, "/entrypoint.sh", "/etc/docker/registry/config.yml"})
   671  			session.WaitWithDefaultTimeout()
   672  			Expect(session).Should(ExitCleanly())
   673  
   674  			if !WaitContainerReady(podmanTest, "registry", "listening on", 20, 1) {
   675  				Skip("Cannot start docker registry.")
   676  			}
   677  
   678  			imgPath := "localhost:5012/my-alpine"
   679  
   680  			session = decryptionTestHelper(imgPath, `initializing source docker://localhost:5012/my-alpine:latest: pinging container registry localhost:5012: Get "https://localhost:5012/v2/": http: server gave HTTP response to HTTPS client`)
   681  
   682  			Expect(session.LineInOutputContainsTag(imgPath, "latest")).To(BeTrue())
   683  		})
   684  	})
   685  
   686  })