docker/docker_client.go
func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, image string, limit int) ([]SearchResult, error) {
	type V2Results struct {
		// Repositories holds the results returned by the /v2/_catalog endpoint
		Repositories []string `json:"repositories"`
	}
	type V1Results struct {
		// Results holds the results returned by the /v1/search endpoint
		Results []SearchResult `json:"results"`
	}
	v2Res := &V2Results{}
	v1Res := &V1Results{}

	// Get credentials from authfile for the underlying hostname
	username, password, err := config.GetAuthentication(sys, registry)
	if err != nil {
		return nil, errors.Wrapf(err, "error getting username and password")
	}

	// The /v2/_catalog endpoint has been disabled for docker.io therefore
	// the call made to that endpoint will fail.  So using the v1 hostname
	// for docker.io for simplicity of implementation and the fact that it
	// returns search results.
	hostname := registry
	if registry == dockerHostname {
		hostname = dockerV1Hostname
	}

	client, err := newDockerClient(sys, hostname, registry)
	if err != nil {
		return nil, errors.Wrapf(err, "error creating new docker client")
	}
	client.username = username
	client.password = password

	// Only try the v1 search endpoint if the search query is not empty. If it is
	// empty skip to the v2 endpoint.
	if image != "" {
		// set up the query values for the v1 endpoint
		u := url.URL{
			Path: "/v1/search",
		}
		q := u.Query()
		q.Set("q", image)
		q.Set("n", strconv.Itoa(limit))
		u.RawQuery = q.Encode()

		logrus.Debugf("trying to talk to v1 search endpoint")
		resp, err := client.makeRequest(ctx, "GET", u.String(), nil, nil, noAuth, nil)
		if err != nil {
			logrus.Debugf("error getting search results from v1 endpoint %q: %v", registry, err)
		} else {
			defer resp.Body.Close()
			if resp.StatusCode != http.StatusOK {
				logrus.Debugf("error getting search results from v1 endpoint %q, status code %d (%s)", registry, resp.StatusCode, http.StatusText(resp.StatusCode))
			} else {
				if err := json.NewDecoder(resp.Body).Decode(v1Res); err != nil {
					return nil, err
				}
				return v1Res.Results, nil
			}
		}
	}

	logrus.Debugf("trying to talk to v2 search endpoint")
	resp, err := client.makeRequest(ctx, "GET", "/v2/_catalog", nil, nil, v2Auth, nil)
	if err != nil {
		logrus.Debugf("error getting search results from v2 endpoint %q: %v", registry, err)
	} else {
		defer resp.Body.Close()
		if resp.StatusCode != http.StatusOK {
			logrus.Errorf("error getting search results from v2 endpoint %q, status code %d (%s)", registry, resp.StatusCode, http.StatusText(resp.StatusCode))
		} else {
			if err := json.NewDecoder(resp.Body).Decode(v2Res); err != nil {
				return nil, err
			}
			searchRes := []SearchResult{}
			for _, repo := range v2Res.Repositories {
				if strings.Contains(repo, image) {
					res := SearchResult{
						Name: repo,
					}
					searchRes = append(searchRes, res)
				}
			}
			return searchRes, nil
		}
	}

	return nil, errors.Wrapf(err, "couldn't search registry %q", registry)
}
func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, image string, limit int) ([]SearchResult, error) {
	type V2Results struct {
		// Repositories holds the results returned by the /v2/_catalog endpoint
		Repositories []string `json:"repositories"`
	}
	type V1Results struct {
		// Results holds the results returned by the /v1/search endpoint
		Results []SearchResult `json:"results"`
	}
	v2Res := &V2Results{}
	v1Res := &V1Results{}

	// Get credentials from authfile for the underlying hostname
	username, password, err := config.GetAuthentication(sys, registry)
	if err != nil {
		return nil, errors.Wrapf(err, "error getting username and password")
	}

	// The /v2/_catalog endpoint has been disabled for docker.io therefore
	// the call made to that endpoint will fail.  So using the v1 hostname
	// for docker.io for simplicity of implementation and the fact that it
	// returns search results.
	hostname := registry
	if registry == dockerHostname {
		hostname = dockerV1Hostname
	}

	client, err := newDockerClient(sys, hostname, registry)
	if err != nil {
		return nil, errors.Wrapf(err, "error creating new docker client")
	}
	client.username = username
	client.password = password

	// Only try the v1 search endpoint if the search query is not empty. If it is
	// empty skip to the v2 endpoint.
	if image != "" {
		// set up the query values for the v1 endpoint
		u := url.URL{
			Path: "/v1/search",
		}
		q := u.Query()
		q.Set("q", image)
		q.Set("n", strconv.Itoa(limit))
		u.RawQuery = q.Encode()

		logrus.Debugf("trying to talk to v1 search endpoint")
		resp, err := client.makeRequest(ctx, "GET", u.String(), nil, nil, noAuth, nil)
		if err != nil {
			logrus.Debugf("error getting search results from v1 endpoint %q: %v", registry, err)
		} else {
			defer resp.Body.Close()
			if resp.StatusCode != http.StatusOK {
				logrus.Debugf("error getting search results from v1 endpoint %q, status code %d (%s)", registry, resp.StatusCode, http.StatusText(resp.StatusCode))
			} else {
				if err := json.NewDecoder(resp.Body).Decode(v1Res); err != nil {
					return nil, err
				}
				return v1Res.Results, nil
			}
		}
	}

	logrus.Debugf("trying to talk to v2 search endpoint")
	resp, err := client.makeRequest(ctx, "GET", "/v2/_catalog", nil, nil, v2Auth, nil)
	if err != nil {
		logrus.Debugf("error getting search results from v2 endpoint %q: %v", registry, err)
	} else {
		defer resp.Body.Close()
		if resp.StatusCode != http.StatusOK {
			logrus.Errorf("error getting search results from v2 endpoint %q, status code %d (%s)", registry, resp.StatusCode, http.StatusText(resp.StatusCode))
		} else {
			if err := json.NewDecoder(resp.Body).Decode(v2Res); err != nil {
				return nil, err
			}
			searchRes := []SearchResult{}
			for _, repo := range v2Res.Repositories {
				if strings.Contains(repo, image) {
					res := SearchResult{
						Name: repo,
					}
					searchRes = append(searchRes, res)
				}
			}
			return searchRes, nil
		}
	}

	return nil, errors.Wrapf(err, "couldn't search registry %q", registry)
}
// SearchRegistry queries a registry for images that contain "image" in their name
// The limit is the max number of results desired
// Note: The limit value doesn't work with all registries
// for example registry.access.redhat.com returns all the results without limiting it to the limit value