mirror of https://github.com/docker/cli.git
Compare commits
9 Commits
e22163db4d
...
95992cb0c8
Author | SHA1 | Date |
---|---|---|
Alano Terblanche | 95992cb0c8 | |
Sebastiaan van Stijn | abb8e9b78a | |
Laura Brehm | 7029147458 | |
Sebastiaan van Stijn | 6b9083776f | |
Sebastiaan van Stijn | fb61156b05 | |
Sebastiaan van Stijn | 87acf77aef | |
Alano Terblanche | 5667a952c5 | |
Alano Terblanche | 5760a3d201 | |
Alano Terblanche | f4c164d9b8 |
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/docker/cli/cli/command/completion"
|
"github.com/docker/cli/cli/command/completion"
|
||||||
"github.com/docker/cli/opts"
|
"github.com/docker/cli/opts"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"github.com/docker/docker/api/types/hub"
|
||||||
"github.com/moby/sys/signal"
|
"github.com/moby/sys/signal"
|
||||||
"github.com/moby/term"
|
"github.com/moby/term"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -37,13 +38,74 @@ func NewRunCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
Short: "Create and run a new container from an image",
|
Short: "Create and run a new container from an image",
|
||||||
Args: cli.RequiresMinArgs(1),
|
Args: cli.RequiresMinArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
copts.Image = args[0]
|
replacer := strings.NewReplacer("(local)", "", "(remote)", "")
|
||||||
|
copts.Image = replacer.Replace(args[0])
|
||||||
if len(args) > 1 {
|
if len(args) > 1 {
|
||||||
copts.Args = args[1:]
|
copts.Args = args[1:]
|
||||||
}
|
}
|
||||||
return runRun(cmd.Context(), dockerCli, cmd.Flags(), &options, copts)
|
return runRun(cmd.Context(), dockerCli, cmd.Flags(), &options, copts)
|
||||||
},
|
},
|
||||||
ValidArgsFunction: completion.ImageNames(dockerCli),
|
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||||
|
if len(args) > 0 {
|
||||||
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||||
|
}
|
||||||
|
|
||||||
|
unique := map[string]struct{}{}
|
||||||
|
localImages, shellComp := completion.ImageNames(dockerCli)(cmd, args, toComplete)
|
||||||
|
|
||||||
|
var all []string
|
||||||
|
if shellComp != cobra.ShellCompDirectiveError {
|
||||||
|
all = make([]string, 0, len(localImages))
|
||||||
|
for _, img := range localImages {
|
||||||
|
unique[img] = struct{}{}
|
||||||
|
all = append(all, img+"\tlocal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return all, cobra.ShellCompDirectiveKeepOrder | cobra.ShellCompDirectiveNoFileComp
|
||||||
|
},
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
"category-top": "1",
|
"category-top": "1",
|
||||||
"aliases": "docker container run, docker run",
|
"aliases": "docker container run, docker run",
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/cli/cli/command/completion"
|
"github.com/docker/cli/cli/command/completion"
|
||||||
"github.com/docker/cli/cli/trust"
|
"github.com/docker/cli/cli/trust"
|
||||||
|
"github.com/docker/docker/api/types/hub"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
@ -39,7 +40,55 @@ func NewPullCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
"category-top": "5",
|
"category-top": "5",
|
||||||
"aliases": "docker image pull, docker pull",
|
"aliases": "docker image pull, docker pull",
|
||||||
},
|
},
|
||||||
ValidArgsFunction: completion.NoComplete,
|
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||||
|
if len(args) > 0 {
|
||||||
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
flags := cmd.Flags()
|
flags := cmd.Flags()
|
||||||
|
|
|
@ -58,9 +58,9 @@ func NewLoginCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyloginOptions(dockerCli command.Cli, opts *loginOptions) error {
|
func verifyLoginOptions(dockerCli command.Cli, opts *loginOptions) error {
|
||||||
if opts.password != "" {
|
if opts.password != "" {
|
||||||
fmt.Fprintln(dockerCli.Err(), "WARNING! Using --password via the CLI is insecure. Use --password-stdin.")
|
_, _ = fmt.Fprintln(dockerCli.Err(), "WARNING! Using --password via the CLI is insecure. Use --password-stdin.")
|
||||||
if opts.passwordStdin {
|
if opts.passwordStdin {
|
||||||
return errors.New("--password and --password-stdin are mutually exclusive")
|
return errors.New("--password and --password-stdin are mutually exclusive")
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ func verifyloginOptions(dockerCli command.Cli, opts *loginOptions) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func runLogin(ctx context.Context, dockerCli command.Cli, opts loginOptions) error {
|
func runLogin(ctx context.Context, dockerCli command.Cli, opts loginOptions) error {
|
||||||
if err := verifyloginOptions(dockerCli, &opts); err != nil {
|
if err := verifyLoginOptions(dockerCli, &opts); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
|
@ -174,7 +174,7 @@ func loginUser(ctx context.Context, dockerCli command.Cli, opts loginOptions, de
|
||||||
if !errors.Is(err, manager.ErrDeviceLoginStartFail) {
|
if !errors.Is(err, manager.ErrDeviceLoginStartFail) {
|
||||||
return response, err
|
return response, err
|
||||||
}
|
}
|
||||||
fmt.Fprint(dockerCli.Err(), "Failed to start web-based login - falling back to command line login...\n\n")
|
_, _ = fmt.Fprint(dockerCli.Err(), "Failed to start web-based login - falling back to command line login...\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
return loginWithUsernameAndPassword(ctx, dockerCli, opts, defaultUsername, serverAddress)
|
return loginWithUsernameAndPassword(ctx, dockerCli, opts, defaultUsername, serverAddress)
|
||||||
|
|
|
@ -199,7 +199,7 @@ func PruneFilters(dockerCli Cli, pruneFilters filters.Args) filters.Args {
|
||||||
// AddPlatformFlag adds `platform` to a set of flags for API version 1.32 and later.
|
// AddPlatformFlag adds `platform` to a set of flags for API version 1.32 and later.
|
||||||
func AddPlatformFlag(flags *pflag.FlagSet, target *string) {
|
func AddPlatformFlag(flags *pflag.FlagSet, target *string) {
|
||||||
flags.StringVar(target, "platform", os.Getenv("DOCKER_DEFAULT_PLATFORM"), "Set platform if server is multi-platform capable")
|
flags.StringVar(target, "platform", os.Getenv("DOCKER_DEFAULT_PLATFORM"), "Set platform if server is multi-platform capable")
|
||||||
flags.SetAnnotation("platform", "version", []string{"1.32"})
|
_ = flags.SetAnnotation("platform", "version", []string{"1.32"})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateOutputPath validates the output paths of the `export` and `save` commands.
|
// ValidateOutputPath validates the output paths of the `export` and `save` commands.
|
||||||
|
|
|
@ -5,7 +5,9 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Enabled returns whether cli hints are enabled or not
|
// Enabled returns whether cli hints are enabled or not. Hints are enabled by
|
||||||
|
// default, but can be disabled through the "DOCKER_CLI_HINTS" environment
|
||||||
|
// variable.
|
||||||
func Enabled() bool {
|
func Enabled() bool {
|
||||||
if v := os.Getenv("DOCKER_CLI_HINTS"); v != "" {
|
if v := os.Getenv("DOCKER_CLI_HINTS"); v != "" {
|
||||||
enabled, err := strconv.ParseBool(v)
|
enabled, err := strconv.ParseBool(v)
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package hints
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gotest.tools/v3/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEnabled(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
doc string
|
||||||
|
env string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
doc: "default",
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc: "DOCKER_CLI_HINTS=1",
|
||||||
|
env: "1",
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc: "DOCKER_CLI_HINTS=true",
|
||||||
|
env: "true",
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc: "DOCKER_CLI_HINTS=0",
|
||||||
|
env: "0",
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc: "DOCKER_CLI_HINTS=false",
|
||||||
|
env: "false",
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc: "DOCKER_CLI_HINTS=not-a-bool",
|
||||||
|
env: "not-a-bool",
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.doc, func(t *testing.T) {
|
||||||
|
t.Setenv("DOCKER_CLI_HINTS", tc.env)
|
||||||
|
assert.Equal(t, Enabled(), tc.expected)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -92,7 +92,7 @@ func newDockerCommand(dockerCli *command.DockerCli) *cli.TopLevelCommand {
|
||||||
CompletionOptions: cobra.CompletionOptions{
|
CompletionOptions: cobra.CompletionOptions{
|
||||||
DisableDefaultCmd: false,
|
DisableDefaultCmd: false,
|
||||||
HiddenDefaultCmd: true,
|
HiddenDefaultCmd: true,
|
||||||
DisableDescriptions: true,
|
DisableDescriptions: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
cmd.SetIn(dockerCli.In())
|
cmd.SetIn(dockerCli.In())
|
||||||
|
|
Loading…
Reference in New Issue