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 })