From 5667a952c5fbaaab6de161645e7f20fb90be2470 Mon Sep 17 00:00:00 2001 From: Alano Terblanche <18033717+Benehiko@users.noreply.github.com> Date: Fri, 18 Oct 2024 14:42:47 +0200 Subject: [PATCH] feat: use engine API instead of Hub directly Signed-off-by: Alano Terblanche <18033717+Benehiko@users.noreply.github.com> --- cli/command/completion/functions.go | 124 ---------------------------- cli/command/container/run.go | 45 ++++++++-- cli/command/image/pull.go | 46 ++++++++++- 3 files changed, 83 insertions(+), 132 deletions(-) diff --git a/cli/command/completion/functions.go b/cli/command/completion/functions.go index 978615bfb3..b217ec54b0 100644 --- a/cli/command/completion/functions.go +++ b/cli/command/completion/functions.go @@ -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 -} diff --git a/cli/command/container/run.go b/cli/command/container/run.go index aeaffe680e..9d67777142 100644 --- a/cli/command/container/run.go +++ b/cli/command/container/run.go @@ -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") } } } diff --git a/cli/command/image/pull.go b/cli/command/image/pull.go index 2d81bfa7bc..e051b9e6ed 100644 --- a/cli/command/image/pull.go +++ b/cli/command/image/pull.go @@ -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 }, }