feat: use engine API instead of Hub directly

Signed-off-by: Alano Terblanche <18033717+Benehiko@users.noreply.github.com>
This commit is contained in:
Alano Terblanche 2024-10-18 14:42:47 +02:00
parent 5760a3d201
commit 5667a952c5
No known key found for this signature in database
GPG Key ID: 0E8FACD1BA98DE27
3 changed files with 83 additions and 132 deletions

View File

@ -1,12 +1,8 @@
package completion
import (
"encoding/json"
"net/http"
"net/url"
"os"
"strings"
"time"
"github.com/docker/cli/cli/command/formatter"
"github.com/docker/docker/api/types/container"
@ -14,7 +10,6 @@ import (
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/volume"
"github.com/docker/docker/client"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@ -194,122 +189,3 @@ var commonPlatforms = []string{
func Platforms(_ *cobra.Command, _ []string, _ string) (platforms []string, _ cobra.ShellCompDirective) {
return commonPlatforms, cobra.ShellCompDirectiveNoFileComp
}
type ImageSearchResult struct {
ID string `json:"id"`
Name string `json:"name"`
Slug string `json:"slug"`
Type string `json:"type"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
ShortDesc string `json:"short_description"`
Source string `json:"source"`
StarCount int `json:"star_count"`
}
type ImageSearch struct {
Totals int `json:"totals"`
Results []ImageSearchResult `json:"results"`
}
type Image struct {
ID int `json:"id"`
Name string `json:"name"`
TagStatus string `json:"tag_status"`
V2 bool `json:"v2"`
Digest string `json:"digest"`
LastUpdated time.Time `json:"last_updated"`
LastUpdater int `json:"last_updater"`
Creator int `json:"creator"`
Repository int `json:"repository"`
}
type ImageTags struct {
Count int `json:"count"`
Next string `json:"next"`
Prev string `json:"prev"`
Results []Image `json:"results"`
}
func RemoteImages(cmd *cobra.Command, arg []string, toComplete string) ([]string, cobra.ShellCompDirective) {
ctx := cmd.Context()
c := &http.Client{
Timeout: 2 * time.Second,
}
if imageName, imageTag, ok := strings.Cut(toComplete, ":"); ok {
u, err := url.Parse("https://hub.docker.com/v2/repositories/library/" + imageName + "/tags/")
if err != nil {
logrus.Errorf("Error parsing hub image tags URL: %v", err)
return nil, cobra.ShellCompDirectiveError
}
q := u.Query()
q.Set("ordering", "last_updated")
q.Set("page_size", "25")
q.Set("name", imageTag)
u.RawQuery = q.Encode()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
if err != nil {
logrus.Errorf("Error creating hub image tags request: %v", err)
return nil, cobra.ShellCompDirectiveError
}
resp, err := c.Do(req)
if err != nil {
logrus.Errorf("Error sending hub image tags request: %v", err)
return nil, cobra.ShellCompDirectiveError
}
defer resp.Body.Close()
var tags *ImageTags
if err := json.NewDecoder(resp.Body).Decode(&tags); err != nil {
logrus.Errorf("Error decoding hub image tags response: %v", err)
return nil, cobra.ShellCompDirectiveError
}
names := make([]string, 0, len(tags.Results))
for _, i := range tags.Results {
names = append(names, imageName+":"+i.Name)
}
return names, cobra.ShellCompDirectiveNoFileComp
}
u, err := url.Parse("https://hub.docker.com/api/search/v3/catalog/search")
if err != nil {
logrus.Errorf("Error parsing hub image search URL: %v", err)
return nil, cobra.ShellCompDirectiveError
}
q := u.Query()
q.Set("query", toComplete)
q.Set("extension_reviewed", "")
q.Set("from", "0")
q.Set("size", "25")
u.RawQuery = q.Encode()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
if err != nil {
logrus.Errorf("Error creating hub image search request: %v", err)
return nil, cobra.ShellCompDirectiveError
}
resp, err := c.Do(req)
if err != nil {
logrus.Errorf("Error sending hub image search request: %v", err)
return nil, cobra.ShellCompDirectiveError
}
defer resp.Body.Close()
var images *ImageSearch
if err := json.NewDecoder(resp.Body).Decode(&images); err != nil {
logrus.Errorf("Error decoding hub image search response: %v", err)
return nil, cobra.ShellCompDirectiveError
}
names := make([]string, 0, len(images.Results))
for _, i := range images.Results {
names = append(names, i.Name)
}
return names, cobra.ShellCompDirectiveNoFileComp
}

View File

@ -12,6 +12,7 @@ import (
"github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/opts"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/hub"
"github.com/moby/sys/signal"
"github.com/moby/term"
"github.com/pkg/errors"
@ -61,14 +62,44 @@ func NewRunCommand(dockerCli command.Cli) *cobra.Command {
}
}
remoteImages, shellCompRemote := completion.RemoteImages(cmd, args, toComplete)
if shellCompRemote != cobra.ShellCompDirectiveError {
if len(all) == 0 {
all = make([]string, 0, len(remoteImages))
if image, tag, found := strings.Cut(toComplete, ":"); found {
remoteTags, err := dockerCli.Client().ImageHubTags(cmd.Context(), image, hub.ImageOptions{
Name: tag,
Ordering: "last_updated",
Page: 0,
PageSize: 25,
})
if err == nil {
if len(all) == 0 {
all = make([]string, 0, len(remoteTags.Results))
}
for _, tag := range remoteTags.Results {
fullName := image + ":" + tag.Name
if _, ok := unique[fullName]; !ok {
all = append(all, fullName+"\tremote")
}
}
}
for _, img := range remoteImages {
if _, ok := unique[img]; !ok {
all = append(all, img+"\tremote")
return all, cobra.ShellCompDirectiveKeepOrder | cobra.ShellCompDirectiveNoFileComp
}
remoteImages, err := dockerCli.Client().ImageHubSearch(cmd.Context(), toComplete, hub.SearchOptions{
From: 0,
Size: 25,
Type: hub.SearchTypeImage,
Order: hub.SearchOrderDesc,
Official: true,
Source: hub.SearchSourceStore,
OpenSource: true,
ExtensionReviewed: true,
})
if err == nil {
if len(all) == 0 {
all = make([]string, 0, len(remoteImages.Results))
}
for _, img := range remoteImages.Results {
if _, ok := unique[img.Name]; !ok {
all = append(all, img.Name+"\tremote")
}
}
}

View File

@ -10,6 +10,7 @@ import (
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/cli/trust"
"github.com/docker/docker/api/types/hub"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@ -43,7 +44,50 @@ func NewPullCommand(dockerCli command.Cli) *cobra.Command {
if len(args) > 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return completion.RemoteImages(cmd, args, toComplete)
if image, tag, found := strings.Cut(toComplete, ":"); found {
remoteTags, err := dockerCli.Client().ImageHubTags(cmd.Context(), image, hub.ImageOptions{
Name: tag,
Ordering: "last_updated",
Page: 0,
PageSize: 25,
})
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
images := make([]string, 0, len(remoteTags.Results))
for _, tag := range remoteTags.Results {
fullName := image + ":" + tag.Name
images = append(images, fullName+"\t"+tag.LastUpdated.String())
}
return images, cobra.ShellCompDirectiveKeepOrder | cobra.ShellCompDirectiveNoFileComp
}
remoteImages, err := dockerCli.Client().ImageHubSearch(cmd.Context(), toComplete, hub.SearchOptions{
From: 0,
Size: 25,
Type: hub.SearchTypeImage,
Order: hub.SearchOrderDesc,
Official: true,
Source: hub.SearchSourceStore,
OpenSource: true,
ExtensionReviewed: true,
})
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
images := make([]string, 0, len(remoteImages.Results))
for _, img := range remoteImages.Results {
categories := make([]string, 0, len(img.Categories))
for _, cat := range img.Categories {
categories = append(categories, cat.Name)
}
images = append(images, img.Name+"\t"+strings.Join(categories, ", "))
}
return images, cobra.ShellCompDirectiveKeepOrder | cobra.ShellCompDirectiveNoFileComp
},
}