Commit [cli@27b2797] forked the AuthConfig type from the API, and changed existing code to do a direct cast / convert of the forked type to the API type. This can cause issues if the API types diverges, such as the removal of the Email field. This patch explicitly maps each field to the corresponding API type, but adds some TODOs, because various code-paths only included a subset of the fields, which may be intentional for fields that were meant to be handled on the daemon / registry-client only. We should evaluate these conversions to make sure these fields should be sent from the client or not (and possibly even removed from the API type). [cli@27b2797]:27b2797f7dSigned-off-by: Sebastiaan van Stijn <github@gone.nl> (cherry picked from commit9f02d9643d) Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
127 lines
4.1 KiB
Go
127 lines
4.1 KiB
Go
package registry
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/docker/cli/cli"
|
|
"github.com/docker/cli/cli/command"
|
|
"github.com/docker/cli/cli/command/formatter"
|
|
"github.com/docker/cli/opts"
|
|
registrytypes "github.com/docker/docker/api/types/registry"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
type searchOptions struct {
|
|
format string
|
|
term string
|
|
noTrunc bool
|
|
limit int
|
|
filter opts.FilterOpt
|
|
}
|
|
|
|
// NewSearchCommand creates a new `docker search` command
|
|
//
|
|
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
|
func NewSearchCommand(dockerCLI command.Cli) *cobra.Command {
|
|
return newSearchCommand(dockerCLI)
|
|
}
|
|
|
|
// newSearchCommand creates a new `docker search` command
|
|
func newSearchCommand(dockerCLI command.Cli) *cobra.Command {
|
|
options := searchOptions{filter: opts.NewFilterOpt()}
|
|
|
|
cmd := &cobra.Command{
|
|
Use: "search [OPTIONS] TERM",
|
|
Short: "Search Docker Hub for images",
|
|
Args: cli.ExactArgs(1),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
options.term = args[0]
|
|
return runSearch(cmd.Context(), dockerCLI, options)
|
|
},
|
|
Annotations: map[string]string{
|
|
"category-top": "10",
|
|
},
|
|
}
|
|
|
|
flags := cmd.Flags()
|
|
|
|
flags.BoolVar(&options.noTrunc, "no-trunc", false, "Don't truncate output")
|
|
flags.VarP(&options.filter, "filter", "f", "Filter output based on conditions provided")
|
|
flags.IntVar(&options.limit, "limit", 0, "Max number of search results")
|
|
flags.StringVar(&options.format, "format", "", "Pretty-print search using a Go template")
|
|
|
|
return cmd
|
|
}
|
|
|
|
func runSearch(ctx context.Context, dockerCli command.Cli, options searchOptions) error {
|
|
if options.filter.Value().Contains("is-automated") {
|
|
_, _ = fmt.Fprintln(dockerCli.Err(), `WARNING: the "is-automated" filter is deprecated, and searching for "is-automated=true" will not yield any results in future.`)
|
|
}
|
|
encodedAuth, err := getAuth(dockerCli, options.term)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
results, err := dockerCli.Client().ImageSearch(ctx, options.term, registrytypes.SearchOptions{
|
|
RegistryAuth: encodedAuth,
|
|
PrivilegeFunc: nil,
|
|
Filters: options.filter.Value(),
|
|
Limit: options.limit,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
searchCtx := formatter.Context{
|
|
Output: dockerCli.Out(),
|
|
Format: newFormat(options.format),
|
|
Trunc: !options.noTrunc,
|
|
}
|
|
return formatWrite(searchCtx, results)
|
|
}
|
|
|
|
// authConfigKey is the key used to store credentials for Docker Hub. It is
|
|
// a copy of [registry.IndexServer].
|
|
//
|
|
// [registry.IndexServer]: https://pkg.go.dev/github.com/docker/docker/registry#IndexServer
|
|
const authConfigKey = "https://index.docker.io/v1/"
|
|
|
|
// getAuth will use fetch auth based on the given search-term. If the search
|
|
// does not contain a hostname for the registry, it assumes Docker Hub is used,
|
|
// and resolves authentication for Docker Hub, otherwise it resolves authentication
|
|
// for the given registry.
|
|
func getAuth(dockerCLI command.Cli, reposName string) (encodedAuth string, err error) {
|
|
authCfgKey := splitReposSearchTerm(reposName)
|
|
if authCfgKey == "docker.io" || authCfgKey == "index.docker.io" {
|
|
authCfgKey = authConfigKey
|
|
}
|
|
|
|
// Ignoring errors here, which was the existing behavior (likely
|
|
// "no credentials found"). We'll get an error when search failed,
|
|
// so fine to ignore in most situations.
|
|
authConfig, _ := dockerCLI.ConfigFile().GetAuthConfig(authCfgKey)
|
|
return registrytypes.EncodeAuthConfig(registrytypes.AuthConfig{
|
|
Username: authConfig.Username,
|
|
Password: authConfig.Password,
|
|
ServerAddress: authConfig.ServerAddress,
|
|
|
|
// TODO(thaJeztah): Are these expected to be included?
|
|
Auth: authConfig.Auth,
|
|
IdentityToken: authConfig.IdentityToken,
|
|
RegistryToken: authConfig.RegistryToken,
|
|
})
|
|
}
|
|
|
|
// splitReposSearchTerm breaks a search term into an index name and remote name
|
|
func splitReposSearchTerm(reposName string) string {
|
|
nameParts := strings.SplitN(reposName, "/", 2)
|
|
if len(nameParts) == 1 || (!strings.Contains(nameParts[0], ".") && !strings.Contains(nameParts[0], ":") && nameParts[0] != "localhost") {
|
|
// This is a Docker Hub repository (ex: samalba/hipache or ubuntu),
|
|
// use the default Docker Hub registry (docker.io)
|
|
return "docker.io"
|
|
}
|
|
return nameParts[0]
|
|
}
|