9f02d9643d
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]: https://github.com/docker/cli/commit/27b2797f7deb3ca5b7f80371d825113deb1faca1 Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
128 lines
4.1 KiB
Go
128 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/internal/commands"
|
|
"github.com/docker/cli/opts"
|
|
"github.com/moby/moby/api/pkg/authconfig"
|
|
registrytypes "github.com/moby/moby/api/types/registry"
|
|
"github.com/moby/moby/client"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
func init() {
|
|
commands.Register(newSearchCommand)
|
|
}
|
|
|
|
type searchOptions struct {
|
|
format string
|
|
term string
|
|
noTrunc bool
|
|
limit int
|
|
filter opts.FilterOpt
|
|
}
|
|
|
|
// 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",
|
|
},
|
|
DisableFlagsInUseLine: true,
|
|
}
|
|
|
|
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, client.ImageSearchOptions{
|
|
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 authconfig.Encode(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]
|
|
}
|