diff --git a/test/e2e/search_mock_registry.go b/test/e2e/search_mock_registry.go new file mode 100644 index 0000000000..1254e69cc1 --- /dev/null +++ b/test/e2e/search_mock_registry.go @@ -0,0 +1,562 @@ +//go:build linux || freebsd + +package integration + +import ( + "encoding/json" + "fmt" + "io" + "log" + "net" + "net/http" + "net/url" + "regexp" + "strconv" + "strings" + + . "github.com/onsi/ginkgo/v2" //nolint:staticcheck + . "github.com/onsi/gomega" //nolint:staticcheck +) + +const ( + contentTypeJSON = "application/json" +) + +var searchResults = map[string]any{ + "alpine": map[string]any{ + "query": "alpine", + "num_results": 25, + "num_pages": 2, + "page": 1, + "page_size": 25, + "results": []map[string]any{ + { + "name": "cilium/alpine-curl", + "description": "", + "is_public": true, + "href": "/repository/cilium/alpine-curl", + }, + { + "name": "libpod/alpine", + "description": "This image is used for testing purposes only. Do NOT use it in production!", + "is_public": true, + "href": "/repository/libpod/alpine", + "stars": 11, + "official": true, + }, + { + "name": "openshifttest/alpine", + "description": nil, + "is_public": true, + "href": "/repository/openshifttest/alpine", + "stars": 5, + "official": false, + "is_automated": true, + }, + { + "name": "openshifttest/base-alpine", + "description": nil, + "is_public": true, + "href": "/repository/openshifttest/base-alpine", + }, + { + "name": "astronomer/ap-alpine", + "description": "", + "is_public": true, + "href": "/repository/astronomer/ap-alpine", + }, + { + "name": "almworks/alpine-curl", + "description": "", + "is_public": true, + "href": "/repository/almworks/alpine-curl", + }, + { + "name": "jitesoft/alpine", + "description": "# Alpine linux", + "is_public": true, + "href": "/repository/jitesoft/alpine", + }, + { + "name": "dougbtv/alpine", + "description": nil, + "is_public": true, + "href": "/repository/dougbtv/alpine", + }, + { + "name": "tccr/alpine", + "description": nil, + "is_public": true, + "href": "/repository/tccr/alpine", + }, + { + "name": "aptible/alpine", + "description": "Alpine base image, borrowed from gliderlabs/alpine", + "is_public": true, + "href": "/repository/aptible/alpine", + }, + { + "name": "openshifttest/nginx-alpine", + "description": nil, + "is_public": true, + "href": "/repository/openshifttest/nginx-alpine", + }, + { + "name": "wire/alpine-git", + "description": "", + "is_public": true, + "href": "/repository/wire/alpine-git", + }, + { + "name": "ditto/alpine-non-root", + "description": "", + "is_public": true, + "href": "/repository/ditto/alpine-non-root", + }, + { + "name": "kubevirt/alpine-ext-kernel-boot-demo", + "description": "", + "is_public": true, + "href": "/repository/kubevirt/alpine-ext-kernel-boot-demo", + }, + { + "name": "ansible/alpine321-test-container", + "description": "", + "is_public": true, + "href": "/repository/ansible/alpine321-test-container", + }, + { + "name": "crio/alpine", + "description": nil, + "is_public": true, + "href": "/repository/crio/alpine", + }, + { + "name": "ansible/alpine-test-container", + "description": "", + "is_public": true, + "href": "/repository/ansible/alpine-test-container", + }, + { + "name": "ansible/alpine322-test-container", + "description": "", + "is_public": true, + "href": "/repository/ansible/alpine322-test-container", + }, + { + "name": "bedrock/alpine", + "description": "", + "is_public": true, + "href": "/repository/bedrock/alpine", + }, + { + "name": "ansible/alpine3-test-container", + "description": "", + "is_public": true, + "href": "/repository/ansible/alpine3-test-container", + }, + { + "name": "openshift-psap-qe/nginx-alpine", + "description": nil, + "is_public": true, + "href": "/repository/openshift-psap-qe/nginx-alpine", + }, + { + "name": "startx/alpine", + "description": "", + "is_public": true, + "href": "/repository/startx/alpine", + }, + { + "name": "pcc3202/alpine_multi", + "description": "", + "is_public": true, + "href": "/repository/pcc3202/alpine_multi", + }, + { + "name": "nvlab/alpine", + "description": nil, + "is_public": true, + "href": "/repository/nvlab/alpine", + }, + { + "name": "kubevirt/alpine-container-disk-demo", + "description": "Part of kubevirt/kubevirt artifacts", + "is_public": true, + "href": "/repository/kubevirt/alpine-container-disk-demo", + }, + }, + }, + "busybox": map[string]any{ + "num_results": 2, + "query": "busybox", + "results": []map[string]any{ + { + "name": "busybox", + "description": "Busybox base image", + "star_count": 80, + "is_official": true, + "is_automated": false, + }, + { + "name": "progrium/busybox", + "description": "Custom busybox build", + "star_count": 15, + "is_official": false, + "is_automated": true, + }, + }, + }, + "skopeo/stable:latest": map[string]any{ + "query": "skopeo/stable:latest", + "num_results": 3, + "num_pages": 1, + "page": 1, + "page_size": 25, + "results": []map[string]any{ + { + "name": "skopeo/stable", + "description": "Stable Skopeo Image", + "is_public": true, + "href": "/repository/skopeo/stable", + }, + { + "name": "skopeo/testing", + "description": "Testing Skopeo Image", + "is_public": true, + "href": "/repository/skopeo/testing", + }, + { + "name": "skopeo/upstream", + "description": "Upstream Skopeo Image", + "is_public": true, + "href": "/repository/skopeo/upstream", + }, + }, + }, + "podman/stable": map[string]any{ + "query": "podman/stable", + "num_results": 3, + "num_pages": 1, + "page": 1, + "page_size": 25, + "results": []map[string]any{ + { + "name": "podman/stable", + "description": "Stable Podman Image", + "is_public": true, + "href": "/repository/podman/stable", + }, + { + "name": "podman/testing", + "description": "Testing Podman Image", + "is_public": true, + "href": "/repository/podman/testing", + }, + { + "name": "podman/upstream", + "description": "Upstream Podman Image", + "is_public": true, + "href": "/repository/podman/upstream", + }, + }, + }, + "testdigest_v2s1": map[string]any{ + "query": "testdigest_v2s1", + "num_results": 2, + "num_pages": 1, + "page": 1, + "page_size": 25, + "results": []map[string]any{ + { + "name": "libpod/testdigest_v2s1", + "description": "Test image used by buildah regression tests", + "is_public": true, + "href": "/repository/libpod/testdigest_v2s1", + }, + { + "name": "libpod/testdigest_v2s1_with_dups", + "description": "This is a specially crafted test-only image used in buildah CI and gating tests.", + "is_public": true, + "href": "/repository/libpod/testdigest_v2s1_with_dups", + }, + }, + }, + "testdigest_v2s2": map[string]any{ + "query": "testdigest_v2s2", + "num_results": 1, + "num_pages": 1, + "page": 1, + "page_size": 25, + "results": []map[string]any{ + { + "name": "libpod/testdigest_v2s2", + "description": "This is a specially crafted test-only image used in buildah CI and gating tests.", + "is_public": true, + "href": "/repository/libpod/testdigest_v2s2", + }, + }, + }, +} + +// Mock repository tag data - simplified to just store tag lists +var mockRepoTags = map[string][]string{ + "libpod/alpine": {"3.10.2", "3.2", "latest", "withbogusseccomp", "withseccomp"}, + "podman/stable": { + "latest", "v1.4.2", "v1.4.4", "v1.5.0", "v1.5.1", "v1.6", "v1.6.2", + "v1.9.0", "v1.9.1", "v2.0.2", "v2.0.6", "v2.1.1", "v2.2.1", "v3", + "v3.1.2", "v3.2.0", "v3.2.1", "v3.2.2", "v3.2.3", "v3.3.0", "v3.3.1", + "v3.4", "v3.4.0", "v3.4.1", "v3.4.2", "v3.4.4", "v3.4.7", "v4", + "v4.1", "v4.1.0", "v4.1.1", "v4.2", "v4.2.0", "v4.2.1", "v4.3", + "v4.3.0", "v4.3.1", "v4.4", "v4.4.1", "v4.4.2", "v4.4.4", "v4.5", + "v4.5.0", "v4.5.1", "v4.6", "v4.6.1", "v4.6.2", "v4.7", "v4.7.0", + "v4.7.2", "v4.8", "v4.8.0", "v4.8.1", "v4.8.2", "v4.8.3", "v4.9", + "v4.9.0", "v4.9.3", "v4.9.4", "v4.9.4-immutable", "v4.9-immutable", + "v4-immutable", "v5", "v5.0", "v5.0.1", "v5.0.1-immutable", "v5.0.2", + "v5.0.2-immutable", "v5.0.3", "v5.0.3-immutable", "v5.0-immutable", + "v5.1", "v5.1.0", "v5.1.0-immutable", "v5.1.1", "v5.1.1-immutable", + "v5.1.2", "v5.1.2-immutable", "v5.1-immutable", "v5.2", "v5.2.0", + "v5.2.0-immutable", "v5.2.1", "v5.2.1-immutable", "v5.2.2", + "v5.2.2-immutable", "v5.2.3", "v5.2.3-immutable", "v5.2.5", + "v5.2.5-immutable", "v5.2-immutable", "v5.3", "v5.3.0", + "v5.3.0-immutable", "v5.3.1", "v5.3.1-immutable", "v5.3.2", + "v5.3.2-immutable", "v5.3-immutable", "v5.4", + }, +} + +// Pagination tags for podman/stable (returned after v5.4 in pagination requests) +// This simulates the specific test case where limit=100 and last=v5.4 +var podmanStablePaginatedTags = []string{ + "v5.4.0", "v5.4.0-immutable", "v5.4.1", "v5.4.1-immutable", "v5.4.2", + "v5.4.2-immutable", "v5.4-immutable", "v5.5", "v5.5.0", "v5.5.0-immutable", + "v5.5.1", "v5.5.1-immutable", "v5.5.2", "v5.5.2-immutable", "v5.5-immutable", + "v5.6", "v5.6.0", "v5.6.0-immutable", "v5.6.1", "v5.6.1-immutable", "v5.6.2", + "v5.6.2-immutable", "v5.6-immutable", "v5-immutable", +} + +func writeJSONResponse(w http.ResponseWriter, data any) { + w.Header().Set("Content-Type", contentTypeJSON) + w.WriteHeader(http.StatusOK) + if err := json.NewEncoder(w).Encode(data); err != nil { + http.Error(w, "Internal server error", http.StatusInternalServerError) + } +} + +func handleV1Search(w http.ResponseWriter, r *http.Request) { + query := r.URL.Query().Get("q") + if decodedQuery, err := url.QueryUnescape(query); err == nil { + query = decodedQuery + } + + limitStr := r.URL.Query().Get("n") + limitNum := -1 + if limit, err := strconv.Atoi(limitStr); err == nil && limit > 0 { + limitNum = limit + } else if err != nil { + http.Error(w, "Invalid limit parameter", http.StatusBadRequest) + return + } + + results := searchForResults(query) + + if results != nil { + response := applyLimitToResults(results, limitNum) + writeJSONResponse(w, response) + } else { + defaultResponse := map[string]any{ + "num_results": 0, + "query": query, + "results": []any{}, + } + writeJSONResponse(w, defaultResponse) + } +} + +func searchForResults(query string) map[string]any { + regexPattern := query + if strings.Contains(query, "*") { + regexPattern = strings.ReplaceAll(query, "*", ".*") + } + + for key, value := range searchResults { + match, _ := regexp.MatchString(regexPattern, key) + if match { + return value.(map[string]any) + } + } + return nil +} + +func applyLimitToResults(results map[string]any, limitNum int) map[string]any { + originalBytes, err := json.Marshal(results) + if err != nil { + return results + } + var resultsCopy map[string]any + if err := json.Unmarshal(originalBytes, &resultsCopy); err != nil { + return results + } + + if limitNum > 0 { + if resultsArray, ok := resultsCopy["results"].([]any); ok { + actualLimit := limitNum + if len(resultsArray) < limitNum { + actualLimit = len(resultsArray) + } + resultsCopy["results"] = resultsArray[:actualLimit] + resultsCopy["num_results"] = actualLimit + } + } + return resultsCopy +} + +func handleV2(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/v2/_catalog" { + handleCatalog(w, r) + return + } + + if strings.HasSuffix(r.URL.Path, "/tags/list") { + handleTagsList(w, r) + return + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{}`)) +} + +func parseRepositoryPath(path string) (string, bool) { + pathParts := strings.Split(strings.TrimPrefix(path, "/v2/"), "/") + if len(pathParts) < 2 { + return "", false + } + + if pathParts[len(pathParts)-1] == "list" && pathParts[len(pathParts)-2] == "tags" { + repoName := strings.Join(pathParts[:len(pathParts)-2], "/") + return repoName, true + } + return "", false +} + +func handleTagsList(w http.ResponseWriter, r *http.Request) { + repoName, isValidPath := parseRepositoryPath(r.URL.Path) + if !isValidPath { + http.Error(w, "Invalid tags list path", http.StatusBadRequest) + return + } + + allTags, exists := mockRepoTags[repoName] + if !exists { + http.Error(w, fmt.Sprintf("repository %s not found", repoName), http.StatusNotFound) + return + } + query := r.URL.Query() + limit := -1 + last := query.Get("last") + + if limitStr := query.Get("n"); limitStr != "" { + if l, err := strconv.Atoi(limitStr); err == nil && l > 0 { + limit = l + } + } + + paginatedTags := applyPagination(allTags, limit, last, repoName) + + response := map[string]any{ + "name": repoName, + "tags": paginatedTags, + } + writeJSONResponse(w, response) +} + +func applyPagination(allTags []string, limit int, last string, repoName string) []string { + if repoName == "podman/stable" && limit == 100 && last == "v5.4" { + return podmanStablePaginatedTags + } + + if limit <= 0 && last == "" { + return allTags + } + + startIndex := 0 + + if last != "" { + for i, tag := range allTags { + if tag == last { + startIndex = i + 1 + break + } + } + } + + if limit > 0 { + endIndex := startIndex + limit + if endIndex > len(allTags) { + endIndex = len(allTags) + } + return allTags[startIndex:endIndex] + } + + return allTags[startIndex:] +} + +func handleCatalog(w http.ResponseWriter, _ *http.Request) { + repositories := make([]string, 0, len(mockRepoTags)) + for repoName := range mockRepoTags { + repositories = append(repositories, repoName) + } + + response := map[string]any{ + "repositories": repositories, + } + writeJSONResponse(w, response) +} + +// CreateMockRegistryServer creates and starts a mock Docker registry server +// Returns: server address, server instance, error channel, and logged requests slice +func CreateMockRegistryServer() (string, *http.Server, chan error) { + listener, err := net.Listen("tcp4", "127.0.0.1:0") + Expect(err).ToNot(HaveOccurred()) + serverAddr := listener.Addr().String() + + mux := http.NewServeMux() + + mux.HandleFunc("/v1/search", func(w http.ResponseWriter, r *http.Request) { + handleV1Search(w, r) + }) + + mux.HandleFunc("/v2/", func(w http.ResponseWriter, r *http.Request) { + handleV2(w, r) + }) + + srv := &http.Server{ + Handler: mux, + ErrorLog: log.New(io.Discard, "", 0), + } + + serverErr := make(chan error, 1) + go func() { + defer GinkgoRecover() + serverErr <- srv.Serve(listener) + }() + + Eventually(func() error { + resp, err := http.Get("http://" + serverAddr + "/v2/") + if err != nil { + return err + } + resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("server not ready, status: %d", resp.StatusCode) + } + return nil + }, "5s", "100ms").Should(Succeed()) + + return serverAddr, srv, serverErr +} + +func CloseMockRegistryServer(srv *http.Server, serverErr chan error) { + srv.Close() + Expect(<-serverErr).To(Equal(http.ErrServerClosed)) +} diff --git a/test/e2e/search_test.go b/test/e2e/search_test.go index 6e03ad4045..08052d972b 100644 --- a/test/e2e/search_test.go +++ b/test/e2e/search_test.go @@ -44,7 +44,10 @@ registries = []` registryFileBadTmpl := template.Must(template.New("registryFileBad").Parse(badRegFileContents)) It("podman search", func() { - search := podmanTest.Podman([]string{"search", "alpine"}) + registryAddress, srv, serverErr := CreateMockRegistryServer() + defer CloseMockRegistryServer(srv, serverErr) + + search := podmanTest.Podman([]string{"search", "--tls-verify=false", registryAddress + "/alpine"}) search.WaitWithDefaultTimeout() Expect(search).Should(ExitCleanly()) Expect(len(search.OutputToStringArray())).To(BeNumerically(">", 1)) @@ -52,23 +55,32 @@ registries = []` }) It("podman search single registry flag", func() { - search := podmanTest.Podman([]string{"search", "quay.io/skopeo/stable:latest"}) + registryAddress, srv, serverErr := CreateMockRegistryServer() + defer CloseMockRegistryServer(srv, serverErr) + + search := podmanTest.Podman([]string{"search", "--tls-verify=false", registryAddress + "/skopeo/stable:latest"}) search.WaitWithDefaultTimeout() Expect(search).Should(ExitCleanly()) - Expect(search.OutputToString()).To(ContainSubstring("quay.io/skopeo/stable")) + Expect(search.OutputToString()).To(ContainSubstring(registryAddress + "/skopeo/stable")) }) It("podman search image with description", func() { - search := podmanTest.Podman([]string{"search", "quay.io/podman/stable"}) + registryAddress, srv, serverErr := CreateMockRegistryServer() + defer CloseMockRegistryServer(srv, serverErr) + + search := podmanTest.Podman([]string{"search", "--tls-verify=false", registryAddress + "/podman/stable"}) search.WaitWithDefaultTimeout() Expect(search).Should(ExitCleanly()) output := string(search.Out.Contents()) Expect(output).To(MatchRegexp(`(?m)NAME\s+DESCRIPTION$`)) - Expect(output).To(MatchRegexp(`(?m)quay.io/podman/stable\s+.*PODMAN logo`)) + Expect(output).To(MatchRegexp(`(?m)/podman/stable\s+.*Podman Image`)) }) It("podman search image with --compatible", func() { - search := podmanTest.Podman([]string{"search", "--compatible", "quay.io/podman/stable"}) + registryAddress, srv, serverErr := CreateMockRegistryServer() + defer CloseMockRegistryServer(srv, serverErr) + + search := podmanTest.Podman([]string{"search", "--compatible", "--tls-verify=false", registryAddress + "/podman/stable"}) search.WaitWithDefaultTimeout() Expect(search).Should(ExitCleanly()) output := string(search.Out.Contents()) @@ -76,19 +88,25 @@ registries = []` }) It("podman search format flag", func() { - search := podmanTest.Podman([]string{"search", "--format", "table {{.Index}} {{.Name}}", "testdigest_v2s2"}) + registryAddress, srv, serverErr := CreateMockRegistryServer() + defer CloseMockRegistryServer(srv, serverErr) + + search := podmanTest.Podman([]string{"search", "--format", "table {{.Index}} {{.Name}}", "--tls-verify=false", registryAddress + "/testdigest_v2s2"}) search.WaitWithDefaultTimeout() Expect(search).Should(ExitCleanly()) Expect(len(search.OutputToStringArray())).To(BeNumerically(">", 1)) - Expect(search.OutputToString()).To(ContainSubstring("quay.io/libpod/testdigest_v2s2")) + Expect(search.OutputToString()).To(ContainSubstring(registryAddress + "/libpod/testdigest_v2s2")) }) It("podman search format json", func() { - search := podmanTest.Podman([]string{"search", "--format", "json", "testdigest_v2s1"}) + registryAddress, srv, serverErr := CreateMockRegistryServer() + defer CloseMockRegistryServer(srv, serverErr) + + search := podmanTest.Podman([]string{"search", "--format", "json", "--tls-verify=false", registryAddress + "/testdigest_v2s1"}) search.WaitWithDefaultTimeout() Expect(search).Should(ExitCleanly()) Expect(search.OutputToString()).To(BeValidJSON()) - Expect(search.OutputToString()).To(ContainSubstring("quay.io/libpod/testdigest_v2s1")) + Expect(search.OutputToString()).To(ContainSubstring(registryAddress + "/libpod/testdigest_v2s1")) Expect(search.OutputToString()).To(ContainSubstring("Test image used by buildah regression tests")) // Test for https://github.com/containers/podman/issues/11894 @@ -102,18 +120,24 @@ registries = []` }) It("podman search format json list tags", func() { - search := podmanTest.Podman([]string{"search", "--list-tags", "--format", "json", ALPINE}) + registryAddress, srv, serverErr := CreateMockRegistryServer() + defer CloseMockRegistryServer(srv, serverErr) + + search := podmanTest.Podman([]string{"search", "--list-tags", "--format", "json", "--tls-verify=false", registryAddress + "/libpod/alpine:latest"}) search.WaitWithDefaultTimeout() Expect(search).Should(ExitCleanly()) Expect(search.OutputToString()).To(BeValidJSON()) - Expect(search.OutputToString()).To(ContainSubstring("quay.io/libpod/alpine")) + Expect(search.OutputToString()).To(ContainSubstring(registryAddress + "/libpod/alpine")) Expect(search.OutputToString()).To(ContainSubstring("3.10.2")) Expect(search.OutputToString()).To(ContainSubstring("3.2")) }) // Test for https://github.com/containers/podman/issues/11894 It("podman search no-trunc=false flag", func() { - search := podmanTest.Podman([]string{"search", "--no-trunc=false", "alpine", "--format={{.Description}}"}) + registryAddress, srv, serverErr := CreateMockRegistryServer() + defer CloseMockRegistryServer(srv, serverErr) + + search := podmanTest.Podman([]string{"search", "--no-trunc=false", "--tls-verify=false", registryAddress + "/alpine", "--format={{.Description}}"}) search.WaitWithDefaultTimeout() Expect(search).Should(ExitCleanly()) @@ -125,24 +149,30 @@ registries = []` }) It("podman search limit flag", func() { - search := podmanTest.Podman([]string{"search", "quay.io/alpine"}) + registryAddress, srv, serverErr := CreateMockRegistryServer() + defer CloseMockRegistryServer(srv, serverErr) + + search := podmanTest.Podman([]string{"search", "--tls-verify=false", registryAddress + "/alpine"}) search.WaitWithDefaultTimeout() Expect(search).Should(ExitCleanly()) Expect(len(search.OutputToStringArray())).To(BeNumerically(">", 10)) - search = podmanTest.Podman([]string{"search", "--limit", "3", "quay.io/alpine"}) + search = podmanTest.Podman([]string{"search", "--limit", "3", "--tls-verify=false", registryAddress + "/alpine"}) search.WaitWithDefaultTimeout() Expect(search).Should(ExitCleanly()) Expect(search.OutputToStringArray()).To(HaveLen(4)) - search = podmanTest.Podman([]string{"search", "--limit", "30", "quay.io/alpine"}) + search = podmanTest.Podman([]string{"search", "--limit", "10", "--tls-verify=false", registryAddress + "/alpine"}) search.WaitWithDefaultTimeout() Expect(search).Should(ExitCleanly()) - Expect(search.OutputToStringArray()).To(HaveLen(31)) + Expect(search.OutputToStringArray()).To(HaveLen(11)) }) It("podman search with filter stars", func() { - search := podmanTest.Podman([]string{"search", "--filter", "stars=10", "--format", "{{.Stars}}", "alpine"}) + registryAddress, srv, serverErr := CreateMockRegistryServer() + defer CloseMockRegistryServer(srv, serverErr) + + search := podmanTest.Podman([]string{"search", "--filter", "stars=10", "--format", "{{.Stars}}", "--tls-verify=false", registryAddress + "/alpine"}) search.WaitWithDefaultTimeout() Expect(search).Should(ExitCleanly()) output := search.OutputToStringArray() @@ -152,7 +182,10 @@ registries = []` }) It("podman search with filter is-official", func() { - search := podmanTest.Podman([]string{"search", "--filter", "is-official", "--format", "{{.Official}}", "alpine"}) + registryAddress, srv, serverErr := CreateMockRegistryServer() + defer CloseMockRegistryServer(srv, serverErr) + + search := podmanTest.Podman([]string{"search", "--filter", "is-official", "--format", "{{.Official}}", "--tls-verify=false", registryAddress + "/alpine"}) search.WaitWithDefaultTimeout() Expect(search).Should(ExitCleanly()) output := search.OutputToStringArray() @@ -162,7 +195,10 @@ registries = []` }) It("podman search with filter is-automated", func() { - search := podmanTest.Podman([]string{"search", "--filter", "is-automated=false", "--format", "{{.Automated}}", "alpine"}) + registryAddress, srv, serverErr := CreateMockRegistryServer() + defer CloseMockRegistryServer(srv, serverErr) + + search := podmanTest.Podman([]string{"search", "--filter", "is-automated=false", "--format", "{{.Automated}}", "--tls-verify=false", registryAddress + "/alpine"}) search.WaitWithDefaultTimeout() Expect(search).Should(ExitCleanly()) output := search.OutputToStringArray() @@ -172,10 +208,13 @@ registries = []` }) It("podman search format list tags with custom", func() { - search := podmanTest.Podman([]string{"search", "--list-tags", "--format", "{{.Name}}", "--limit", "1", ALPINE}) + registryAddress, srv, serverErr := CreateMockRegistryServer() + defer CloseMockRegistryServer(srv, serverErr) + + search := podmanTest.Podman([]string{"search", "--list-tags", "--format", "{{.Name}}", "--limit", "1", "--tls-verify=false", registryAddress + "/libpod/alpine"}) search.WaitWithDefaultTimeout() Expect(search).Should(ExitCleanly()) - Expect(search.OutputToString()).To(Equal("quay.io/libpod/alpine")) + Expect(search.OutputToString()).To(Equal(registryAddress + "/libpod/alpine")) }) It("podman search attempts HTTP if tls-verify flag is set false", func() { @@ -373,44 +412,53 @@ registries = []` Expect(search).To(ExitWithError(125, "credential file is not accessible: faccessat /tmp/nonexistent: no such file or directory")) }) - // Registry is unreliable (#18484), this is another super-common flake - It("podman search with wildcards", FlakeAttempts(3), func() { - search := podmanTest.Podman([]string{"search", "registry.access.redhat.com/*openshift*"}) + It("podman search with wildcards", func() { + registryAddress, srv, serverErr := CreateMockRegistryServer() + defer CloseMockRegistryServer(srv, serverErr) + + search := podmanTest.Podman([]string{"search", "--tls-verify=false", registryAddress + "/*alpine*"}) search.WaitWithDefaultTimeout() Expect(search).Should(ExitCleanly()) Expect(len(search.OutputToStringArray())).To(BeNumerically(">", 1)) + Expect(search.OutputToString()).To(ContainSubstring("alpine")) }) It("podman search repository tags", func() { - search := podmanTest.Podman([]string{"search", "--list-tags", "--limit", "30", "quay.io/podman/stable"}) + registryAddress, srv, serverErr := CreateMockRegistryServer() + defer CloseMockRegistryServer(srv, serverErr) + + search := podmanTest.Podman([]string{"search", "--list-tags", "--limit", "30", "--tls-verify=false", registryAddress + "/podman/stable"}) search.WaitWithDefaultTimeout() Expect(search).Should(ExitCleanly()) Expect(search.OutputToStringArray()).To(HaveLen(31)) - search = podmanTest.Podman([]string{"search", "--list-tags", "quay.io/podman/stable"}) + search = podmanTest.Podman([]string{"search", "--list-tags", "--tls-verify=false", registryAddress + "/podman/stable"}) search.WaitWithDefaultTimeout() Expect(search).Should(ExitCleanly()) Expect(len(search.OutputToStringArray())).To(BeNumerically(">", 2)) - search = podmanTest.Podman([]string{"search", "--filter=is-official", "--list-tags", "quay.io/podman/stable"}) + search = podmanTest.Podman([]string{"search", "--filter=is-official", "--list-tags", "--tls-verify=false", registryAddress + "/podman/stable"}) search.WaitWithDefaultTimeout() Expect(search).To(ExitWithError(125, "filters are not applicable to list tags result")) // With trailing slash - search = podmanTest.Podman([]string{"search", "--list-tags", "quay.io/podman/"}) + search = podmanTest.Podman([]string{"search", "--list-tags", "--tls-verify=false", registryAddress + "/podman/"}) search.WaitWithDefaultTimeout() Expect(search).To(ExitWithError(125, `reference "podman/" must be a docker reference`)) Expect(search.OutputToStringArray()).To(BeEmpty()) // No trailing slash - search = podmanTest.Podman([]string{"search", "--list-tags", "quay.io/podman"}) + search = podmanTest.Podman([]string{"search", "--list-tags", "--tls-verify=false", registryAddress + "/podman"}) search.WaitWithDefaultTimeout() Expect(search).To(ExitWithError(125, "getting repository tags: fetching tags list: StatusCode: 404")) Expect(search.OutputToStringArray()).To(BeEmpty()) }) It("podman search with limit over 100", func() { - search := podmanTest.Podman([]string{"search", "--limit", "100", "quay.io/podman"}) + registryAddress, srv, serverErr := CreateMockRegistryServer() + defer CloseMockRegistryServer(srv, serverErr) + + search := podmanTest.Podman([]string{"search", "--limit", "100", "--tls-verify=false", registryAddress + "/podman"}) search.WaitWithDefaultTimeout() Expect(search).Should(ExitCleanly()) Expect(len(search.OutputToStringArray())).To(BeNumerically("<=", 101))