github.com/containers/podman/v5@v5.1.0-rc1/test/e2e/search_test.go (about) 1 package integration 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "os" 8 "strconv" 9 "text/template" 10 11 "github.com/containers/podman/v5/pkg/domain/entities" 12 . "github.com/containers/podman/v5/test/utils" 13 . "github.com/onsi/ginkgo/v2" 14 . "github.com/onsi/gomega" 15 ) 16 17 type endpoint struct { 18 Host string 19 Port string 20 } 21 22 func (e *endpoint) Address() string { 23 return fmt.Sprintf("%s:%s", e.Host, e.Port) 24 } 25 26 var _ = Describe("Podman search", func() { 27 28 const regFileContents = ` 29 [registries.search] 30 registries = ['{{.Host}}:{{.Port}}'] 31 32 [registries.insecure] 33 registries = ['{{.Host}}:{{.Port}}']` 34 registryFileTmpl := template.Must(template.New("registryFile").Parse(regFileContents)) 35 36 const badRegFileContents = ` 37 [registries.search] 38 registries = ['{{.Host}}:{{.Port}}'] 39 # empty 40 [registries.insecure] 41 registries = []` 42 registryFileBadTmpl := template.Must(template.New("registryFileBad").Parse(badRegFileContents)) 43 44 It("podman search", func() { 45 search := podmanTest.Podman([]string{"search", "alpine"}) 46 search.WaitWithDefaultTimeout() 47 Expect(search).Should(ExitCleanly()) 48 Expect(len(search.OutputToStringArray())).To(BeNumerically(">", 1)) 49 Expect(search.OutputToString()).To(ContainSubstring("alpine")) 50 }) 51 52 It("podman search single registry flag", func() { 53 search := podmanTest.Podman([]string{"search", "quay.io/skopeo/stable:latest"}) 54 search.WaitWithDefaultTimeout() 55 Expect(search).Should(ExitCleanly()) 56 Expect(search.OutputToString()).To(ContainSubstring("quay.io/skopeo/stable")) 57 }) 58 59 It("podman search image with description", func() { 60 search := podmanTest.Podman([]string{"search", "quay.io/podman/stable"}) 61 search.WaitWithDefaultTimeout() 62 Expect(search).Should(ExitCleanly()) 63 output := string(search.Out.Contents()) 64 Expect(output).To(MatchRegexp(`(?m)NAME\s+DESCRIPTION$`)) 65 Expect(output).To(MatchRegexp(`(?m)quay.io/podman/stable\s+.*PODMAN logo`)) 66 }) 67 68 It("podman search image with --compatible", func() { 69 search := podmanTest.Podman([]string{"search", "--compatible", "quay.io/podman/stable"}) 70 search.WaitWithDefaultTimeout() 71 Expect(search).Should(ExitCleanly()) 72 output := string(search.Out.Contents()) 73 Expect(output).To(MatchRegexp(`(?m)NAME\s+DESCRIPTION\s+STARS\s+OFFICIAL\s+AUTOMATED$`)) 74 }) 75 76 It("podman search format flag", func() { 77 search := podmanTest.Podman([]string{"search", "--format", "table {{.Index}} {{.Name}}", "testdigest_v2s2"}) 78 search.WaitWithDefaultTimeout() 79 Expect(search).Should(ExitCleanly()) 80 Expect(len(search.OutputToStringArray())).To(BeNumerically(">", 1)) 81 Expect(search.OutputToString()).To(ContainSubstring("quay.io/libpod/testdigest_v2s2")) 82 }) 83 84 It("podman search format json", func() { 85 search := podmanTest.Podman([]string{"search", "--format", "json", "testdigest_v2s1"}) 86 search.WaitWithDefaultTimeout() 87 Expect(search).Should(ExitCleanly()) 88 Expect(search.OutputToString()).To(BeValidJSON()) 89 Expect(search.OutputToString()).To(ContainSubstring("quay.io/libpod/testdigest_v2s1")) 90 Expect(search.OutputToString()).To(ContainSubstring("Test image used by buildah regression tests")) 91 92 // Test for https://github.com/containers/podman/issues/11894 93 contents := make([]entities.ImageSearchReport, 0) 94 err := json.Unmarshal(search.Out.Contents(), &contents) 95 Expect(err).ToNot(HaveOccurred()) 96 Expect(contents).ToNot(BeEmpty(), "No results from image search") 97 for _, element := range contents { 98 Expect(element.Description).ToNot(HaveSuffix("...")) 99 } 100 }) 101 102 It("podman search format json list tags", func() { 103 search := podmanTest.Podman([]string{"search", "--list-tags", "--format", "json", ALPINE}) 104 search.WaitWithDefaultTimeout() 105 Expect(search).Should(ExitCleanly()) 106 Expect(search.OutputToString()).To(BeValidJSON()) 107 Expect(search.OutputToString()).To(ContainSubstring("quay.io/libpod/alpine")) 108 Expect(search.OutputToString()).To(ContainSubstring("3.10.2")) 109 Expect(search.OutputToString()).To(ContainSubstring("3.2")) 110 }) 111 112 // Test for https://github.com/containers/podman/issues/11894 113 It("podman search no-trunc=false flag", func() { 114 search := podmanTest.Podman([]string{"search", "--no-trunc=false", "alpine", "--format={{.Description}}"}) 115 search.WaitWithDefaultTimeout() 116 Expect(search).Should(ExitCleanly()) 117 118 for _, line := range search.OutputToStringArray() { 119 if len(line) > 44 { 120 Expect(line).To(HaveSuffix("..."), line+" should have been truncated") 121 } 122 } 123 }) 124 125 It("podman search limit flag", func() { 126 search := podmanTest.Podman([]string{"search", "quay.io/alpine"}) 127 search.WaitWithDefaultTimeout() 128 Expect(search).Should(ExitCleanly()) 129 Expect(len(search.OutputToStringArray())).To(BeNumerically(">", 10)) 130 131 search = podmanTest.Podman([]string{"search", "--limit", "3", "quay.io/alpine"}) 132 search.WaitWithDefaultTimeout() 133 Expect(search).Should(ExitCleanly()) 134 Expect(search.OutputToStringArray()).To(HaveLen(4)) 135 136 search = podmanTest.Podman([]string{"search", "--limit", "30", "quay.io/alpine"}) 137 search.WaitWithDefaultTimeout() 138 Expect(search).Should(ExitCleanly()) 139 Expect(search.OutputToStringArray()).To(HaveLen(31)) 140 }) 141 142 It("podman search with filter stars", func() { 143 search := podmanTest.Podman([]string{"search", "--filter", "stars=10", "--format", "{{.Stars}}", "alpine"}) 144 search.WaitWithDefaultTimeout() 145 Expect(search).Should(ExitCleanly()) 146 output := search.OutputToStringArray() 147 for i := 0; i < len(output); i++ { 148 Expect(strconv.Atoi(output[i])).To(BeNumerically(">=", 10)) 149 } 150 }) 151 152 It("podman search with filter is-official", func() { 153 search := podmanTest.Podman([]string{"search", "--filter", "is-official", "--format", "{{.Official}}", "alpine"}) 154 search.WaitWithDefaultTimeout() 155 Expect(search).Should(ExitCleanly()) 156 output := search.OutputToStringArray() 157 for i := 0; i < len(output); i++ { 158 Expect(output[i]).To(Equal("[OK]")) 159 } 160 }) 161 162 It("podman search with filter is-automated", func() { 163 search := podmanTest.Podman([]string{"search", "--filter", "is-automated=false", "--format", "{{.Automated}}", "alpine"}) 164 search.WaitWithDefaultTimeout() 165 Expect(search).Should(ExitCleanly()) 166 output := search.OutputToStringArray() 167 for i := 0; i < len(output); i++ { 168 Expect(output[i]).To(Equal("")) 169 } 170 }) 171 172 It("podman search format list tags with custom", func() { 173 search := podmanTest.Podman([]string{"search", "--list-tags", "--format", "{{.Name}}", "--limit", "1", ALPINE}) 174 search.WaitWithDefaultTimeout() 175 Expect(search).Should(ExitCleanly()) 176 Expect(search.OutputToString()).To(Equal("quay.io/libpod/alpine")) 177 }) 178 179 It("podman search attempts HTTP if tls-verify flag is set false", func() { 180 if podmanTest.Host.Arch == "ppc64le" { 181 Skip("No registry image for ppc64le") 182 } 183 port := GetPort() 184 fakereg := podmanTest.Podman([]string{"run", "-d", "--name", "registry", 185 "-p", fmt.Sprintf("%d:5000", port), 186 REGISTRY_IMAGE, "/entrypoint.sh", "/etc/docker/registry/config.yml"}) 187 fakereg.WaitWithDefaultTimeout() 188 Expect(fakereg).Should(ExitCleanly()) 189 190 if !WaitContainerReady(podmanTest, "registry", "listening on", 20, 1) { 191 Fail("Cannot start docker registry on port %s", port) 192 } 193 ep := endpoint{Port: strconv.Itoa(port), Host: "localhost"} 194 search := podmanTest.Podman([]string{"search", 195 fmt.Sprintf("%s/fake/image:andtag", ep.Address()), "--tls-verify=false"}) 196 search.WaitWithDefaultTimeout() 197 198 // if this test succeeded, there will be no output (there is no entry named fake/image:andtag in an empty registry) 199 // and the exit code will be 0 200 Expect(search).Should(ExitCleanly()) 201 Expect(search.OutputToString()).Should(BeEmpty()) 202 }) 203 204 It("podman search in local registry", func() { 205 if podmanTest.Host.Arch == "ppc64le" { 206 Skip("No registry image for ppc64le") 207 } 208 port := GetPort() 209 registry := podmanTest.Podman([]string{"run", "-d", "--name", "registry3", 210 "-p", fmt.Sprintf("%d:5000", port), REGISTRY_IMAGE, 211 "/entrypoint.sh", "/etc/docker/registry/config.yml"}) 212 registry.WaitWithDefaultTimeout() 213 Expect(registry).Should(ExitCleanly()) 214 215 if !WaitContainerReady(podmanTest, "registry3", "listening on", 20, 1) { 216 Fail("Cannot start docker registry on port %s", port) 217 } 218 ep := endpoint{Port: strconv.Itoa(port), Host: "localhost"} 219 err = podmanTest.RestoreArtifact(ALPINE) 220 Expect(err).ToNot(HaveOccurred()) 221 image := fmt.Sprintf("%s/my-alpine", ep.Address()) 222 push := podmanTest.Podman([]string{"push", "-q", "--tls-verify=false", "--remove-signatures", ALPINE, image}) 223 push.WaitWithDefaultTimeout() 224 Expect(push).Should(ExitCleanly()) 225 search := podmanTest.Podman([]string{"search", image, "--tls-verify=false"}) 226 search.WaitWithDefaultTimeout() 227 228 Expect(search).Should(ExitCleanly()) 229 Expect(search.OutputToString()).ShouldNot(BeEmpty()) 230 231 // podman search v2 registry with empty query 232 searchEmpty := podmanTest.Podman([]string{"search", fmt.Sprintf("%s/", ep.Address()), "--tls-verify=false"}) 233 searchEmpty.WaitWithDefaultTimeout() 234 Expect(searchEmpty).Should(ExitCleanly()) 235 Expect(searchEmpty.OutputToStringArray()).ToNot(BeEmpty()) 236 Expect(search.OutputToString()).To(ContainSubstring("my-alpine")) 237 }) 238 239 It("podman search attempts HTTP if registry is in registries.insecure and force secure is false", func() { 240 if podmanTest.Host.Arch == "ppc64le" { 241 Skip("No registry image for ppc64le") 242 } 243 244 port := GetPort() 245 ep := endpoint{Port: strconv.Itoa(port), Host: "localhost"} 246 registry := podmanTest.Podman([]string{"run", "-d", "-p", fmt.Sprintf("%d:5000", port), 247 "--name", "registry4", REGISTRY_IMAGE, "/entrypoint.sh", "/etc/docker/registry/config.yml"}) 248 registry.WaitWithDefaultTimeout() 249 Expect(registry).Should(ExitCleanly()) 250 251 if !WaitContainerReady(podmanTest, "registry4", "listening on", 20, 1) { 252 Fail("unable to start registry on port %s", port) 253 } 254 255 err = podmanTest.RestoreArtifact(ALPINE) 256 Expect(err).ToNot(HaveOccurred()) 257 image := fmt.Sprintf("%s/my-alpine", ep.Address()) 258 push := podmanTest.Podman([]string{"push", "-q", "--tls-verify=false", "--remove-signatures", ALPINE, image}) 259 push.WaitWithDefaultTimeout() 260 Expect(push).Should(ExitCleanly()) 261 262 // registries.conf set up 263 var buffer bytes.Buffer 264 err = registryFileTmpl.Execute(&buffer, ep) 265 Expect(err).ToNot(HaveOccurred()) 266 podmanTest.setRegistriesConfigEnv(buffer.Bytes()) 267 err = os.WriteFile(fmt.Sprintf("%s/registry4.conf", tempdir), buffer.Bytes(), 0644) 268 Expect(err).ToNot(HaveOccurred()) 269 if IsRemote() { 270 podmanTest.RestartRemoteService() 271 defer podmanTest.RestartRemoteService() 272 } 273 274 search := podmanTest.Podman([]string{"search", image}) 275 search.WaitWithDefaultTimeout() 276 277 Expect(search).Should(ExitCleanly()) 278 Expect(search.OutputToString()).To(ContainSubstring("my-alpine")) 279 280 // cleanup 281 resetRegistriesConfigEnv() 282 }) 283 284 It("podman search doesn't attempt HTTP if force secure is true", func() { 285 if podmanTest.Host.Arch == "ppc64le" { 286 Skip("No registry image for ppc64le") 287 } 288 port := GetPort() 289 ep := endpoint{Port: strconv.Itoa(port), Host: "localhost"} 290 registry := podmanTest.Podman([]string{"run", "-d", "-p", fmt.Sprintf("%d:5000", port), 291 "--name", "registry5", REGISTRY_IMAGE}) 292 registry.WaitWithDefaultTimeout() 293 Expect(registry).Should(ExitCleanly()) 294 295 if !WaitContainerReady(podmanTest, "registry5", "listening on", 20, 1) { 296 Fail("Cannot start docker registry on port %s", port) 297 } 298 299 err = podmanTest.RestoreArtifact(ALPINE) 300 Expect(err).ToNot(HaveOccurred()) 301 image := fmt.Sprintf("%s/my-alpine", ep.Address()) 302 push := podmanTest.Podman([]string{"push", "-q", "--tls-verify=false", "--remove-signatures", ALPINE, image}) 303 push.WaitWithDefaultTimeout() 304 Expect(push).Should(ExitCleanly()) 305 306 var buffer bytes.Buffer 307 err = registryFileTmpl.Execute(&buffer, ep) 308 Expect(err).ToNot(HaveOccurred()) 309 podmanTest.setRegistriesConfigEnv(buffer.Bytes()) 310 err = os.WriteFile(fmt.Sprintf("%s/registry5.conf", tempdir), buffer.Bytes(), 0644) 311 Expect(err).ToNot(HaveOccurred()) 312 313 search := podmanTest.Podman([]string{"search", image, "--tls-verify=true"}) 314 search.WaitWithDefaultTimeout() 315 316 Expect(search).Should(ExitWithError(125, fmt.Sprintf(`couldn't search registry "localhost:%d": pinging container registry localhost:%d: Get "https://localhost:%d/v2/": http: server gave HTTP response to HTTPS client`, port, port, port))) 317 Expect(search.OutputToString()).Should(BeEmpty()) 318 319 // cleanup 320 resetRegistriesConfigEnv() 321 }) 322 323 It("podman search doesn't attempt HTTP if registry is not listed as insecure", func() { 324 if podmanTest.Host.Arch == "ppc64le" { 325 Skip("No registry image for ppc64le") 326 } 327 port := GetPort() 328 ep := endpoint{Port: strconv.Itoa(port), Host: "localhost"} 329 registry := podmanTest.Podman([]string{"run", "-d", "-p", fmt.Sprintf("%d:5000", port), 330 "--name", "registry6", REGISTRY_IMAGE}) 331 registry.WaitWithDefaultTimeout() 332 Expect(registry).Should(ExitCleanly()) 333 334 if !WaitContainerReady(podmanTest, "registry6", "listening on", 20, 1) { 335 Fail("Cannot start docker registry on port %s", port) 336 } 337 338 err = podmanTest.RestoreArtifact(ALPINE) 339 Expect(err).ToNot(HaveOccurred()) 340 image := fmt.Sprintf("%s/my-alpine", ep.Address()) 341 push := podmanTest.Podman([]string{"push", "-q", "--tls-verify=false", "--remove-signatures", ALPINE, image}) 342 push.WaitWithDefaultTimeout() 343 Expect(push).Should(ExitCleanly()) 344 345 var buffer bytes.Buffer 346 err = registryFileBadTmpl.Execute(&buffer, ep) 347 Expect(err).ToNot(HaveOccurred()) 348 podmanTest.setRegistriesConfigEnv(buffer.Bytes()) 349 err = os.WriteFile(fmt.Sprintf("%s/registry6.conf", tempdir), buffer.Bytes(), 0644) 350 Expect(err).ToNot(HaveOccurred()) 351 352 if IsRemote() { 353 podmanTest.RestartRemoteService() 354 defer podmanTest.RestartRemoteService() 355 } 356 357 search := podmanTest.Podman([]string{"search", image}) 358 search.WaitWithDefaultTimeout() 359 360 Expect(search).Should(ExitWithError(125, fmt.Sprintf(`couldn't search registry "localhost:%d": pinging container registry localhost:%d: Get "https://localhost:%d/v2/": http: server gave HTTP response to HTTPS client`, port, port, port))) 361 Expect(search.OutputToString()).Should(BeEmpty()) 362 363 // cleanup 364 resetRegistriesConfigEnv() 365 }) 366 367 // search should fail with nonexistent authfile 368 It("podman search fail with nonexistent --authfile", func() { 369 search := podmanTest.Podman([]string{"search", "--authfile", "/tmp/nonexistent", ALPINE}) 370 search.WaitWithDefaultTimeout() 371 Expect(search).To(ExitWithError(125, "credential file is not accessible: faccessat /tmp/nonexistent: no such file or directory")) 372 }) 373 374 // Registry is unreliable (#18484), this is another super-common flake 375 It("podman search with wildcards", FlakeAttempts(3), func() { 376 search := podmanTest.Podman([]string{"search", "registry.access.redhat.com/*openshift*"}) 377 search.WaitWithDefaultTimeout() 378 Expect(search).Should(ExitCleanly()) 379 Expect(len(search.OutputToStringArray())).To(BeNumerically(">", 1)) 380 }) 381 382 It("podman search repository tags", func() { 383 search := podmanTest.Podman([]string{"search", "--list-tags", "--limit", "30", "quay.io/podman/stable"}) 384 search.WaitWithDefaultTimeout() 385 Expect(search).Should(ExitCleanly()) 386 Expect(search.OutputToStringArray()).To(HaveLen(31)) 387 388 search = podmanTest.Podman([]string{"search", "--list-tags", "quay.io/podman/stable"}) 389 search.WaitWithDefaultTimeout() 390 Expect(search).Should(ExitCleanly()) 391 Expect(len(search.OutputToStringArray())).To(BeNumerically(">", 2)) 392 393 search = podmanTest.Podman([]string{"search", "--filter=is-official", "--list-tags", "quay.io/podman/stable"}) 394 search.WaitWithDefaultTimeout() 395 Expect(search).To(ExitWithError(125, "filters are not applicable to list tags result")) 396 397 // With trailing slash 398 search = podmanTest.Podman([]string{"search", "--list-tags", "quay.io/podman/"}) 399 search.WaitWithDefaultTimeout() 400 Expect(search).To(ExitWithError(125, `reference "podman/" must be a docker reference`)) 401 Expect(search.OutputToStringArray()).To(BeEmpty()) 402 403 // No trailing slash 404 search = podmanTest.Podman([]string{"search", "--list-tags", "quay.io/podman"}) 405 search.WaitWithDefaultTimeout() 406 Expect(search).To(ExitWithError(125, "getting repository tags: fetching tags list: StatusCode: 404")) 407 Expect(search.OutputToStringArray()).To(BeEmpty()) 408 }) 409 410 It("podman search with limit over 100", func() { 411 search := podmanTest.Podman([]string{"search", "--limit", "100", "quay.io/podman"}) 412 search.WaitWithDefaultTimeout() 413 Expect(search).Should(ExitCleanly()) 414 Expect(len(search.OutputToStringArray())).To(BeNumerically("<=", 101)) 415 }) 416 })