github.com/containers/podman/v4@v4.9.4/test/e2e/save_test.go (about) 1 package integration 2 3 import ( 4 "os" 5 "os/exec" 6 "path/filepath" 7 "strconv" 8 "strings" 9 10 . "github.com/containers/podman/v4/test/utils" 11 . "github.com/onsi/ginkgo/v2" 12 . "github.com/onsi/gomega" 13 ) 14 15 var _ = Describe("Podman save", func() { 16 17 It("podman save output flag", func() { 18 outfile := filepath.Join(podmanTest.TempDir, "alpine.tar") 19 20 save := podmanTest.Podman([]string{"save", "-q", "-o", outfile, ALPINE}) 21 save.WaitWithDefaultTimeout() 22 Expect(save).Should(ExitCleanly()) 23 }) 24 25 It("podman save signature-policy flag", func() { 26 SkipIfRemote("--signature-policy N/A for remote") 27 outfile := filepath.Join(podmanTest.TempDir, "alpine.tar") 28 29 save := podmanTest.Podman([]string{"save", "-q", "--signature-policy", "/etc/containers/policy.json", "-o", outfile, ALPINE}) 30 save.WaitWithDefaultTimeout() 31 Expect(save).Should(ExitCleanly()) 32 }) 33 34 It("podman save oci flag", func() { 35 outfile := filepath.Join(podmanTest.TempDir, "alpine.tar") 36 37 save := podmanTest.Podman([]string{"save", "-q", "-o", outfile, "--format", "oci-archive", ALPINE}) 38 save.WaitWithDefaultTimeout() 39 Expect(save).Should(ExitCleanly()) 40 }) 41 42 It("podman save with stdout", func() { 43 Skip("Pipe redirection in ginkgo probably won't work") 44 outfile := filepath.Join(podmanTest.TempDir, "alpine.tar") 45 46 save := podmanTest.Podman([]string{"save", ALPINE, ">", outfile}) 47 save.WaitWithDefaultTimeout() 48 Expect(save).Should(ExitCleanly()) 49 }) 50 51 It("podman save quiet flag", func() { 52 outfile := filepath.Join(podmanTest.TempDir, "alpine.tar") 53 54 save := podmanTest.Podman([]string{"save", "-q", "-o", outfile, ALPINE}) 55 save.WaitWithDefaultTimeout() 56 Expect(save).Should(ExitCleanly()) 57 }) 58 59 It("podman save bogus image", func() { 60 outfile := filepath.Join(podmanTest.TempDir, "alpine.tar") 61 62 save := podmanTest.Podman([]string{"save", "-q", "-o", outfile, "FOOBAR"}) 63 save.WaitWithDefaultTimeout() 64 Expect(save).To(ExitWithError()) 65 }) 66 67 It("podman save to directory with oci format", func() { 68 outdir := filepath.Join(podmanTest.TempDir, "save") 69 70 save := podmanTest.Podman([]string{"save", "-q", "--format", "oci-dir", "-o", outdir, ALPINE}) 71 save.WaitWithDefaultTimeout() 72 Expect(save).Should(ExitCleanly()) 73 74 // Smoke test if it looks like an OCI dir 75 Expect(filepath.Join(outdir, "oci-layout")).Should(BeAnExistingFile()) 76 Expect(filepath.Join(outdir, "index.json")).Should(BeAnExistingFile()) 77 Expect(filepath.Join(outdir, "blobs")).Should(BeAnExistingFile()) 78 }) 79 80 It("podman save to directory with v2s2 docker format", func() { 81 outdir := filepath.Join(podmanTest.TempDir, "save") 82 83 save := podmanTest.Podman([]string{"save", "-q", "--format", "docker-dir", "-o", outdir, ALPINE}) 84 save.WaitWithDefaultTimeout() 85 Expect(save).Should(ExitCleanly()) 86 87 // Smoke test if it looks like a docker dir 88 Expect(filepath.Join(outdir, "version")).Should(BeAnExistingFile()) 89 }) 90 91 It("podman save to directory with docker format and compression", func() { 92 outdir := filepath.Join(podmanTest.TempDir, "save") 93 94 save := podmanTest.Podman([]string{"save", "-q", "--compress", "--format", "docker-dir", "-o", outdir, ALPINE}) 95 save.WaitWithDefaultTimeout() 96 Expect(save).Should(ExitCleanly()) 97 }) 98 99 It("podman save to directory with --compress but not use docker-dir and oci-dir", func() { 100 outdir := filepath.Join(podmanTest.TempDir, "save") 101 102 save := podmanTest.Podman([]string{"save", "-q", "--compress", "--format", "docker-archive", "-o", outdir, ALPINE}) 103 save.WaitWithDefaultTimeout() 104 // should not be 0 105 Expect(save).To(ExitWithError()) 106 107 save = podmanTest.Podman([]string{"save", "-q", "--compress", "--format", "oci-archive", "-o", outdir, ALPINE}) 108 save.WaitWithDefaultTimeout() 109 // should not be 0 110 Expect(save).To(ExitWithError()) 111 112 }) 113 114 It("podman save bad filename", func() { 115 outdir := filepath.Join(podmanTest.TempDir, "save:colon") 116 117 save := podmanTest.Podman([]string{"save", "-q", "--compress", "--format", "docker-dir", "-o", outdir, ALPINE}) 118 save.WaitWithDefaultTimeout() 119 Expect(save).To(ExitWithError()) 120 }) 121 122 It("podman save remove signature", func() { 123 podmanTest.AddImageToRWStore(ALPINE) 124 SkipIfRootless("FIXME: Need get in rootless push sign") 125 if podmanTest.Host.Arch == "ppc64le" { 126 Skip("No registry image for ppc64le") 127 } 128 tempGNUPGHOME := filepath.Join(podmanTest.TempDir, "tmpGPG") 129 err := os.Mkdir(tempGNUPGHOME, os.ModePerm) 130 Expect(err).ToNot(HaveOccurred()) 131 origGNUPGHOME := os.Getenv("GNUPGHOME") 132 err = os.Setenv("GNUPGHOME", tempGNUPGHOME) 133 Expect(err).ToNot(HaveOccurred()) 134 defer os.Setenv("GNUPGHOME", origGNUPGHOME) 135 136 port := 5000 137 portlock := GetPortLock(strconv.Itoa(port)) 138 defer portlock.Unlock() 139 140 session := podmanTest.Podman([]string{"run", "-d", "--name", "registry", "-p", strings.Join([]string{strconv.Itoa(port), strconv.Itoa(port)}, ":"), REGISTRY_IMAGE}) 141 session.WaitWithDefaultTimeout() 142 Expect(session).Should(ExitCleanly()) 143 if !WaitContainerReady(podmanTest, "registry", "listening on", 20, 1) { 144 Skip("Cannot start docker registry.") 145 } 146 147 cmd := exec.Command("gpg", "--import", "sign/secret-key.asc") 148 cmd.Stdout = GinkgoWriter 149 cmd.Stderr = GinkgoWriter 150 err = cmd.Run() 151 Expect(err).ToNot(HaveOccurred()) 152 153 defaultYaml := filepath.Join(podmanTest.TempDir, "default.yaml") 154 cmd = exec.Command("cp", "/etc/containers/registries.d/default.yaml", defaultYaml) 155 if err = cmd.Run(); err != nil { 156 Skip("no signature store to verify") 157 } 158 defer func() { 159 cmd = exec.Command("cp", defaultYaml, "/etc/containers/registries.d/default.yaml") 160 err := cmd.Run() 161 Expect(err).ToNot(HaveOccurred()) 162 }() 163 164 keyPath := filepath.Join(podmanTest.TempDir, "key.gpg") 165 cmd = exec.Command("cp", "sign/key.gpg", keyPath) 166 Expect(cmd.Run()).To(Succeed()) 167 defer os.Remove(keyPath) 168 169 sigstore := ` 170 default-docker: 171 sigstore: file:///var/lib/containers/sigstore 172 sigstore-staging: file:///var/lib/containers/sigstore 173 ` 174 Expect(os.WriteFile("/etc/containers/registries.d/default.yaml", []byte(sigstore), 0755)).To(Succeed()) 175 176 session = podmanTest.Podman([]string{"tag", ALPINE, "localhost:5000/alpine"}) 177 session.WaitWithDefaultTimeout() 178 Expect(session).Should(ExitCleanly()) 179 180 session = podmanTest.Podman([]string{"push", "-q", "--tls-verify=false", "--sign-by", "foo@bar.com", "localhost:5000/alpine"}) 181 session.WaitWithDefaultTimeout() 182 Expect(session).Should(ExitCleanly()) 183 184 session = podmanTest.Podman([]string{"rmi", ALPINE, "localhost:5000/alpine"}) 185 session.WaitWithDefaultTimeout() 186 Expect(session).Should(ExitCleanly()) 187 188 if !IsRemote() { 189 // Generate a signature verification policy file 190 policyPath := generatePolicyFile(podmanTest.TempDir) 191 defer os.Remove(policyPath) 192 193 session = podmanTest.Podman([]string{"pull", "-q", "--tls-verify=false", "--signature-policy", policyPath, "localhost:5000/alpine"}) 194 session.WaitWithDefaultTimeout() 195 Expect(session).Should(ExitCleanly()) 196 197 outfile := filepath.Join(podmanTest.TempDir, "temp.tar") 198 save := podmanTest.Podman([]string{"save", "-q", "remove-signatures=true", "-o", outfile, "localhost:5000/alpine"}) 199 save.WaitWithDefaultTimeout() 200 Expect(save).To(ExitWithError()) 201 } 202 }) 203 204 It("podman save image with digest reference", func() { 205 // pull a digest reference 206 session := podmanTest.Podman([]string{"pull", "-q", ALPINELISTDIGEST}) 207 session.WaitWithDefaultTimeout() 208 Expect(session).Should(ExitCleanly()) 209 210 // save a digest reference should exit without error. 211 outfile := filepath.Join(podmanTest.TempDir, "temp.tar") 212 save := podmanTest.Podman([]string{"save", "-q", "-o", outfile, ALPINELISTDIGEST}) 213 save.WaitWithDefaultTimeout() 214 Expect(save).Should(ExitCleanly()) 215 }) 216 217 It("podman save --multi-image-archive (tagged images)", func() { 218 multiImageSave(podmanTest, RESTORE_IMAGES) 219 }) 220 221 It("podman save --multi-image-archive (untagged images)", func() { 222 // #14468: to make execution time more predictable, save at 223 // most three images and sort them by size. 224 session := podmanTest.Podman([]string{"images", "--sort", "size", "--format", "{{.ID}}"}) 225 session.WaitWithDefaultTimeout() 226 Expect(session).Should(ExitCleanly()) 227 ids := session.OutputToStringArray() 228 229 Expect(len(ids)).To(BeNumerically(">", 1), "We need to have *some* images to save") 230 if len(ids) > 3 { 231 ids = ids[:3] 232 } 233 multiImageSave(podmanTest, ids) 234 }) 235 }) 236 237 // Create a multi-image archive, remove all images, load it and 238 // make sure that all images are (again) present. 239 func multiImageSave(podmanTest *PodmanTestIntegration, images []string) { 240 // Create the archive. 241 outfile := filepath.Join(podmanTest.TempDir, "temp.tar") 242 session := podmanTest.Podman(append([]string{"save", "-q", "-o", outfile, "--multi-image-archive"}, images...)) 243 session.WaitWithDefaultTimeout() 244 Expect(session).Should(ExitCleanly()) 245 246 // Remove all images. 247 session = podmanTest.Podman([]string{"rmi", "-af"}) 248 session.WaitWithDefaultTimeout() 249 Expect(session).Should(ExitCleanly()) 250 251 // Now load the archive. 252 session = podmanTest.Podman([]string{"load", "-q", "-i", outfile}) 253 session.WaitWithDefaultTimeout() 254 Expect(session).Should(ExitCleanly()) 255 // Grep for each image in the `podman load` output. 256 for _, image := range images { 257 Expect(session.OutputToString()).To(ContainSubstring(image)) 258 } 259 260 // Make sure that each image has really been loaded. 261 for _, image := range images { 262 session = podmanTest.Podman([]string{"image", "exists", image}) 263 session.WaitWithDefaultTimeout() 264 Expect(session).Should(ExitCleanly()) 265 } 266 }