mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-23 07:31:03 +00:00
96 lines
3.1 KiB
Go
96 lines
3.1 KiB
Go
package commands
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const dockerHubAPIBaseURL = "https://hub.docker.com"
|
|
|
|
var dockerHubHTTPClient = &http.Client{Timeout: 15 * time.Second}
|
|
|
|
// DockerHubTag contains the metadata Docker Hub returns for a repository tag.
|
|
type DockerHubTag struct {
|
|
Name string `json:"name"`
|
|
FullSize int64 `json:"full_size"`
|
|
LastUpdated string `json:"last_updated"`
|
|
TagLastPushed string `json:"tag_last_pushed"`
|
|
Digest string `json:"digest"`
|
|
MediaType string `json:"media_type"`
|
|
Images []DockerHubTagVariant `json:"images"`
|
|
}
|
|
|
|
// DockerHubTagVariant describes one platform-specific image behind a tag.
|
|
type DockerHubTagVariant struct {
|
|
Architecture string `json:"architecture"`
|
|
Variant string `json:"variant"`
|
|
Digest string `json:"digest"`
|
|
OS string `json:"os"`
|
|
Size int64 `json:"size"`
|
|
LastPushed string `json:"last_pushed"`
|
|
}
|
|
|
|
// SearchImageTags searches Docker Hub tags for an image repository.
|
|
func (c *DockerCommand) SearchImageTags(ctx context.Context, imageName, query string) ([]DockerHubTag, error) {
|
|
return searchImageTags(ctx, dockerHubHTTPClient, dockerHubAPIBaseURL, imageName, query)
|
|
}
|
|
|
|
func searchImageTags(ctx context.Context, httpClient *http.Client, baseURL, imageName, query string) ([]DockerHubTag, error) {
|
|
namespace, repository, err := dockerHubRepositoryParts(imageName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
endpoint, err := url.Parse(strings.TrimRight(baseURL, "/"))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("build Docker Hub URL: %w", err)
|
|
}
|
|
endpoint.Path = fmt.Sprintf("/v2/namespaces/%s/repositories/%s/tags", url.PathEscape(namespace), url.PathEscape(repository))
|
|
values := endpoint.Query()
|
|
values.Set("page_size", "50")
|
|
if query = strings.TrimSpace(query); query != "" {
|
|
values.Set("name", query)
|
|
}
|
|
endpoint.RawQuery = values.Encode()
|
|
|
|
request, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("build Docker Hub tag request: %w", err)
|
|
}
|
|
request.Header.Set("Accept", "application/json")
|
|
|
|
response, err := httpClient.Do(request)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("search Docker Hub tags: %w", err)
|
|
}
|
|
defer response.Body.Close()
|
|
|
|
if response.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("search Docker Hub tags: %s", response.Status)
|
|
}
|
|
|
|
var payload struct {
|
|
Results []DockerHubTag `json:"results"`
|
|
}
|
|
if err := json.NewDecoder(response.Body).Decode(&payload); err != nil {
|
|
return nil, fmt.Errorf("parse Docker Hub tags: %w", err)
|
|
}
|
|
|
|
return payload.Results, nil
|
|
}
|
|
|
|
func dockerHubRepositoryParts(imageName string) (string, string, error) {
|
|
parts := strings.SplitN(strings.TrimSpace(imageName), "/", 2)
|
|
if len(parts) == 1 && parts[0] != "" {
|
|
return "library", parts[0], nil
|
|
}
|
|
if len(parts) == 2 && parts[0] != "" && parts[1] != "" {
|
|
return parts[0], parts[1], nil
|
|
}
|
|
return "", "", fmt.Errorf("invalid Docker Hub image name %q", imageName)
|
|
}
|