Merge component 'cli' from git@github.com:docker/cli master
This commit is contained in:
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
# Github code owners
|
||||
# GitHub code owners
|
||||
# See https://github.com/blog/2392-introducing-code-owners
|
||||
|
||||
cli/command/stack/** @dnephin @vdemeester
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
[people.cpuguy83]
|
||||
Name = "Brian Goff"
|
||||
Email = "cpuguy83@gmail.com"
|
||||
Github = "cpuguy83"
|
||||
GitHub = "cpuguy83"
|
||||
|
||||
[people.crosbymichael]
|
||||
Name = "Michael Crosby"
|
||||
|
||||
@@ -1 +1 @@
|
||||
17.10.0-dev
|
||||
17.11.0-dev
|
||||
|
||||
@@ -17,6 +17,7 @@ func SetupRootCommand(rootCmd *cobra.Command) {
|
||||
cobra.AddTemplateFunc("operationSubCommands", operationSubCommands)
|
||||
cobra.AddTemplateFunc("managementSubCommands", managementSubCommands)
|
||||
cobra.AddTemplateFunc("wrappedFlagUsages", wrappedFlagUsages)
|
||||
cobra.AddTemplateFunc("useLine", UseLine)
|
||||
|
||||
rootCmd.SetUsageTemplate(usageTemplate)
|
||||
rootCmd.SetHelpTemplate(helpTemplate)
|
||||
@@ -25,6 +26,7 @@ func SetupRootCommand(rootCmd *cobra.Command) {
|
||||
|
||||
rootCmd.PersistentFlags().BoolP("help", "h", false, "Print usage")
|
||||
rootCmd.PersistentFlags().MarkShorthandDeprecated("help", "please use --help")
|
||||
rootCmd.PersistentFlags().Lookup("help").Hidden = true
|
||||
}
|
||||
|
||||
// FlagErrorFunc prints an error message which matches the format of the
|
||||
@@ -97,9 +99,19 @@ func managementSubCommands(cmd *cobra.Command) []*cobra.Command {
|
||||
return cmds
|
||||
}
|
||||
|
||||
// UseLine returns the usage line for a command. This implementation is different
|
||||
// from the default Command.UseLine in that it does not add a `[flags]` to the
|
||||
// end of the line.
|
||||
func UseLine(cmd *cobra.Command) string {
|
||||
if cmd.HasParent() {
|
||||
return cmd.Parent().CommandPath() + " " + cmd.Use
|
||||
}
|
||||
return cmd.Use
|
||||
}
|
||||
|
||||
var usageTemplate = `Usage:
|
||||
|
||||
{{- if not .HasSubCommands}} {{.UseLine}}{{end}}
|
||||
{{- if not .HasSubCommands}} {{ useLine . }}{{end}}
|
||||
{{- if .HasSubCommands}} {{ .CommandPath}} COMMAND{{end}}
|
||||
|
||||
{{ .Short | trim }}
|
||||
|
||||
@@ -9,11 +9,11 @@ import (
|
||||
// NewCheckpointCommand returns the `checkpoint` subcommand (only in experimental)
|
||||
func NewCheckpointCommand(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "checkpoint",
|
||||
Short: "Manage checkpoints",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Tags: map[string]string{"experimental": "", "version": "1.25"},
|
||||
Use: "checkpoint",
|
||||
Short: "Manage checkpoints",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Annotations: map[string]string{"experimental": "", "version": "1.25"},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newCreateCommand(dockerCli),
|
||||
|
||||
@@ -207,7 +207,8 @@ func NewAPIClientFromFlags(opts *cliflags.CommonOptions, configFile *configfile.
|
||||
return client.NewClient(host, verStr, httpClient, customHeaders)
|
||||
}
|
||||
|
||||
func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (host string, err error) {
|
||||
func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (string, error) {
|
||||
var host string
|
||||
switch len(hosts) {
|
||||
case 0:
|
||||
host = os.Getenv("DOCKER_HOST")
|
||||
@@ -217,8 +218,7 @@ func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (host string,
|
||||
return "", errors.New("Please specify only one -H")
|
||||
}
|
||||
|
||||
host, err = dopts.ParseHost(tlsOptions != nil, host)
|
||||
return
|
||||
return dopts.ParseHost(tlsOptions != nil, host)
|
||||
}
|
||||
|
||||
func newHTTPClient(host string, tlsOptions *tlsconfig.Options) (*http.Client, error) {
|
||||
|
||||
@@ -8,14 +8,13 @@ import (
|
||||
)
|
||||
|
||||
// NewConfigCommand returns a cobra command for `config` subcommands
|
||||
// nolint: interfacer
|
||||
func NewConfigCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewConfigCommand(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "config",
|
||||
Short: "Manage Docker configs",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Tags: map[string]string{"version": "1.30"},
|
||||
Use: "config",
|
||||
Short: "Manage Docker configs",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Annotations: map[string]string{"version": "1.30"},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newConfigListCommand(dockerCli),
|
||||
|
||||
@@ -7,8 +7,7 @@ import (
|
||||
)
|
||||
|
||||
// NewContainerCommand returns a cobra command for `container` subcommands
|
||||
// nolint: interfacer
|
||||
func NewContainerCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewContainerCommand(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "container",
|
||||
Short: "Manage containers",
|
||||
|
||||
@@ -22,7 +22,7 @@ type commitOptions struct {
|
||||
}
|
||||
|
||||
// NewCommitCommand creates a new cobra.Command for `docker commit`
|
||||
func NewCommitCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewCommitCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var options commitOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -51,7 +51,7 @@ func NewCommitCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runCommit(dockerCli *command.DockerCli, options *commitOptions) error {
|
||||
func runCommit(dockerCli command.Cli, options *commitOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
name := options.container
|
||||
|
||||
@@ -36,7 +36,7 @@ type cpConfig struct {
|
||||
}
|
||||
|
||||
// NewCopyCommand creates a new `docker cp` command
|
||||
func NewCopyCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewCopyCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts copyOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -72,7 +72,7 @@ func NewCopyCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runCopy(dockerCli *command.DockerCli, opts copyOptions) error {
|
||||
func runCopy(dockerCli command.Cli, opts copyOptions) error {
|
||||
srcContainer, srcPath := splitCpArg(opts.source)
|
||||
dstContainer, dstPath := splitCpArg(opts.destination)
|
||||
|
||||
@@ -104,7 +104,7 @@ func runCopy(dockerCli *command.DockerCli, opts copyOptions) error {
|
||||
}
|
||||
}
|
||||
|
||||
func statContainerPath(ctx context.Context, dockerCli *command.DockerCli, containerName, path string) (types.ContainerPathStat, error) {
|
||||
func statContainerPath(ctx context.Context, dockerCli command.Cli, containerName, path string) (types.ContainerPathStat, error) {
|
||||
return dockerCli.Client().ContainerStatPath(ctx, containerName, path)
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ func resolveLocalPath(localPath string) (absPath string, err error) {
|
||||
return archive.PreserveTrailingDotOrSeparator(absPath, localPath, filepath.Separator), nil
|
||||
}
|
||||
|
||||
func copyFromContainer(ctx context.Context, dockerCli *command.DockerCli, srcContainer, srcPath, dstPath string, cpParam *cpConfig) (err error) {
|
||||
func copyFromContainer(ctx context.Context, dockerCli command.Cli, srcContainer, srcPath, dstPath string, cpParam *cpConfig) (err error) {
|
||||
if dstPath != "-" {
|
||||
// Get an absolute destination path.
|
||||
dstPath, err = resolveLocalPath(dstPath)
|
||||
@@ -177,7 +177,7 @@ func copyFromContainer(ctx context.Context, dockerCli *command.DockerCli, srcCon
|
||||
return archive.CopyTo(preArchive, srcInfo, dstPath)
|
||||
}
|
||||
|
||||
func copyToContainer(ctx context.Context, dockerCli *command.DockerCli, srcPath, dstContainer, dstPath string, cpParam *cpConfig, copyUIDGID bool) (err error) {
|
||||
func copyToContainer(ctx context.Context, dockerCli command.Cli, srcPath, dstContainer, dstPath string, cpParam *cpConfig, copyUIDGID bool) (err error) {
|
||||
if srcPath != "-" {
|
||||
// Get an absolute source path.
|
||||
srcPath, err = resolveLocalPath(srcPath)
|
||||
|
||||
@@ -194,7 +194,7 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerConfig
|
||||
|
||||
//if image not found try to pull it
|
||||
if err != nil {
|
||||
if apiclient.IsErrImageNotFound(err) && namedRef != nil {
|
||||
if apiclient.IsErrNotFound(err) && namedRef != nil {
|
||||
fmt.Fprintf(stderr, "Unable to find image '%s' locally\n", reference.FamiliarString(namedRef))
|
||||
|
||||
// we don't want to write to stdout anything apart from container.ID
|
||||
|
||||
@@ -14,7 +14,7 @@ type diffOptions struct {
|
||||
}
|
||||
|
||||
// NewDiffCommand creates a new cobra.Command for `docker diff`
|
||||
func NewDiffCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewDiffCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts diffOptions
|
||||
|
||||
return &cobra.Command{
|
||||
@@ -28,7 +28,7 @@ func NewDiffCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func runDiff(dockerCli *command.DockerCli, opts *diffOptions) error {
|
||||
func runDiff(dockerCli command.Cli, opts *diffOptions) error {
|
||||
if opts.container == "" {
|
||||
return errors.New("Container name cannot be empty")
|
||||
}
|
||||
|
||||
@@ -122,7 +122,10 @@ func interactiveExec(ctx context.Context, dockerCli command.Cli, execConfig *typ
|
||||
}
|
||||
|
||||
client := dockerCli.Client()
|
||||
resp, err := client.ContainerExecAttach(ctx, execID, *execConfig)
|
||||
execStartCheck := types.ExecStartCheck{
|
||||
Tty: execConfig.Tty,
|
||||
}
|
||||
resp, err := client.ContainerExecAttach(ctx, execID, execStartCheck)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ type exportOptions struct {
|
||||
}
|
||||
|
||||
// NewExportCommand creates a new `docker export` command
|
||||
func NewExportCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewExportCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts exportOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -36,7 +36,7 @@ func NewExportCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runExport(dockerCli *command.DockerCli, opts exportOptions) error {
|
||||
func runExport(dockerCli command.Cli, opts exportOptions) error {
|
||||
if opts.output == "" && dockerCli.Out().IsTerminal() {
|
||||
return errors.New("cowardly refusing to save to a terminal. Use the -o flag or redirect")
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ type inspectOptions struct {
|
||||
}
|
||||
|
||||
// newInspectCommand creates a new cobra.Command for `docker container inspect`
|
||||
func newInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newInspectCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts inspectOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -35,7 +35,7 @@ func newInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runInspect(dockerCli *command.DockerCli, opts inspectOptions) error {
|
||||
func runInspect(dockerCli command.Cli, opts inspectOptions) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ type killOptions struct {
|
||||
}
|
||||
|
||||
// NewKillCommand creates a new cobra.Command for `docker kill`
|
||||
func NewKillCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewKillCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts killOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -36,7 +36,7 @@ func NewKillCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runKill(dockerCli *command.DockerCli, opts *killOptions) error {
|
||||
func runKill(dockerCli command.Cli, opts *killOptions) error {
|
||||
var errs []string
|
||||
ctx := context.Background()
|
||||
errChan := parallelOperation(ctx, opts.containers, func(ctx context.Context, container string) error {
|
||||
|
||||
@@ -25,7 +25,7 @@ type psOptions struct {
|
||||
}
|
||||
|
||||
// NewPsCommand creates a new cobra.Command for `docker ps`
|
||||
func NewPsCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewPsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
options := psOptions{filter: opts.NewFilterOpt()}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -51,7 +51,7 @@ func NewPsCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newListCommand(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := *NewPsCommand(dockerCli)
|
||||
cmd.Aliases = []string{"ps", "list"}
|
||||
cmd.Use = "ls [OPTIONS]"
|
||||
@@ -109,7 +109,7 @@ func buildContainerListOptions(opts *psOptions) (*types.ContainerListOptions, er
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func runPs(dockerCli *command.DockerCli, options *psOptions) error {
|
||||
func runPs(dockerCli command.Cli, options *psOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
listOptions, err := buildContainerListOptions(options)
|
||||
|
||||
@@ -22,7 +22,7 @@ type logsOptions struct {
|
||||
}
|
||||
|
||||
// NewLogsCommand creates a new cobra.Command for `docker logs`
|
||||
func NewLogsCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewLogsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts logsOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -44,7 +44,7 @@ func NewLogsCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runLogs(dockerCli *command.DockerCli, opts *logsOptions) error {
|
||||
func runLogs(dockerCli command.Cli, opts *logsOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
options := types.ContainerLogsOptions{
|
||||
|
||||
@@ -45,10 +45,7 @@ func TestValidateAttach(t *testing.T) {
|
||||
|
||||
// nolint: unparam
|
||||
func parseRun(args []string) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) {
|
||||
flags := pflag.NewFlagSet("run", pflag.ContinueOnError)
|
||||
flags.SetOutput(ioutil.Discard)
|
||||
flags.Usage = nil
|
||||
copts := addFlags(flags)
|
||||
flags, copts := setupRunFlags()
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
@@ -60,6 +57,14 @@ func parseRun(args []string) (*container.Config, *container.HostConfig, *network
|
||||
return containerConfig.Config, containerConfig.HostConfig, containerConfig.NetworkingConfig, err
|
||||
}
|
||||
|
||||
func setupRunFlags() (*pflag.FlagSet, *containerOptions) {
|
||||
flags := pflag.NewFlagSet("run", pflag.ContinueOnError)
|
||||
flags.SetOutput(ioutil.Discard)
|
||||
flags.Usage = nil
|
||||
copts := addFlags(flags)
|
||||
return flags, copts
|
||||
}
|
||||
|
||||
func parseMustError(t *testing.T, args string) {
|
||||
_, _, _, err := parseRun(strings.Split(args+" ubuntu bash", " "))
|
||||
assert.Error(t, err, args)
|
||||
@@ -227,20 +232,21 @@ func TestParseWithMacAddress(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseWithMemory(t *testing.T) {
|
||||
invalidMemory := "--memory=invalid"
|
||||
_, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"})
|
||||
testutil.ErrorContains(t, err, invalidMemory)
|
||||
func TestRunFlagsParseWithMemory(t *testing.T) {
|
||||
flags, _ := setupRunFlags()
|
||||
args := []string{"--memory=invalid", "img", "cmd"}
|
||||
err := flags.Parse(args)
|
||||
testutil.ErrorContains(t, err, `invalid argument "invalid" for "-m, --memory" flag`)
|
||||
|
||||
_, hostconfig := mustParse(t, "--memory=1G")
|
||||
assert.Equal(t, int64(1073741824), hostconfig.Memory)
|
||||
}
|
||||
|
||||
func TestParseWithMemorySwap(t *testing.T) {
|
||||
invalidMemory := "--memory-swap=invalid"
|
||||
|
||||
_, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"})
|
||||
testutil.ErrorContains(t, err, invalidMemory)
|
||||
flags, _ := setupRunFlags()
|
||||
args := []string{"--memory-swap=invalid", "img", "cmd"}
|
||||
err := flags.Parse(args)
|
||||
testutil.ErrorContains(t, err, `invalid argument "invalid" for "--memory-swap" flag`)
|
||||
|
||||
_, hostconfig := mustParse(t, "--memory-swap=1G")
|
||||
assert.Equal(t, int64(1073741824), hostconfig.MemorySwap)
|
||||
@@ -365,7 +371,10 @@ func TestParseDevice(t *testing.T) {
|
||||
|
||||
func TestParseModes(t *testing.T) {
|
||||
// pid ko
|
||||
_, _, _, err := parseRun([]string{"--pid=container:", "img", "cmd"})
|
||||
flags, copts := setupRunFlags()
|
||||
args := []string{"--pid=container:", "img", "cmd"}
|
||||
require.NoError(t, flags.Parse(args))
|
||||
_, err := parse(flags, copts)
|
||||
testutil.ErrorContains(t, err, "--pid: invalid PID mode")
|
||||
|
||||
// pid ok
|
||||
@@ -385,14 +394,18 @@ func TestParseModes(t *testing.T) {
|
||||
if !hostconfig.UTSMode.Valid() {
|
||||
t.Fatalf("Expected a valid UTSMode, got %v", hostconfig.UTSMode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunFlagsParseShmSize(t *testing.T) {
|
||||
// shm-size ko
|
||||
expectedErr := `invalid argument "a128m" for --shm-size=a128m: invalid size: 'a128m'`
|
||||
_, _, _, err = parseRun([]string{"--shm-size=a128m", "img", "cmd"})
|
||||
flags, _ := setupRunFlags()
|
||||
args := []string{"--shm-size=a128m", "img", "cmd"}
|
||||
expectedErr := `invalid argument "a128m" for "--shm-size" flag: invalid size: 'a128m'`
|
||||
err := flags.Parse(args)
|
||||
testutil.ErrorContains(t, err, expectedErr)
|
||||
|
||||
// shm-size ok
|
||||
_, hostconfig, _, err = parseRun([]string{"--shm-size=128m", "img", "cmd"})
|
||||
_, hostconfig, _, err := parseRun([]string{"--shm-size=128m", "img", "cmd"})
|
||||
require.NoError(t, err)
|
||||
if hostconfig.ShmSize != 134217728 {
|
||||
t.Fatalf("Expected a valid ShmSize, got %d", hostconfig.ShmSize)
|
||||
|
||||
@@ -16,7 +16,7 @@ type pauseOptions struct {
|
||||
}
|
||||
|
||||
// NewPauseCommand creates a new cobra.Command for `docker pause`
|
||||
func NewPauseCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewPauseCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts pauseOptions
|
||||
|
||||
return &cobra.Command{
|
||||
@@ -30,7 +30,7 @@ func NewPauseCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func runPause(dockerCli *command.DockerCli, opts *pauseOptions) error {
|
||||
func runPause(dockerCli command.Cli, opts *pauseOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
var errs []string
|
||||
|
||||
@@ -19,7 +19,7 @@ type portOptions struct {
|
||||
}
|
||||
|
||||
// NewPortCommand creates a new cobra.Command for `docker port`
|
||||
func NewPortCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewPortCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts portOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -37,7 +37,7 @@ func NewPortCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runPort(dockerCli *command.DockerCli, opts *portOptions) error {
|
||||
func runPort(dockerCli command.Cli, opts *portOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
c, err := dockerCli.Client().ContainerInspect(ctx, opts.container)
|
||||
|
||||
@@ -35,7 +35,7 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
fmt.Fprintln(dockerCli.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed)))
|
||||
return nil
|
||||
},
|
||||
Tags: map[string]string{"version": "1.25"},
|
||||
Annotations: map[string]string{"version": "1.25"},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
@@ -52,12 +52,12 @@ func runPrune(dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint6
|
||||
pruneFilters := command.PruneFilters(dockerCli, options.filter.Value())
|
||||
|
||||
if !options.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) {
|
||||
return
|
||||
return 0, "", nil
|
||||
}
|
||||
|
||||
report, err := dockerCli.Client().ContainersPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
if len(report.ContainersDeleted) > 0 {
|
||||
@@ -68,7 +68,7 @@ func runPrune(dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint6
|
||||
spaceReclaimed = report.SpaceReclaimed
|
||||
}
|
||||
|
||||
return
|
||||
return spaceReclaimed, output, nil
|
||||
}
|
||||
|
||||
// RunPrune calls the Container Prune API
|
||||
|
||||
@@ -17,7 +17,7 @@ type renameOptions struct {
|
||||
}
|
||||
|
||||
// NewRenameCommand creates a new cobra.Command for `docker rename`
|
||||
func NewRenameCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewRenameCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts renameOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -33,7 +33,7 @@ func NewRenameCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRename(dockerCli *command.DockerCli, opts *renameOptions) error {
|
||||
func runRename(dockerCli command.Cli, opts *renameOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
oldName := strings.TrimSpace(opts.oldName)
|
||||
|
||||
@@ -20,7 +20,7 @@ type restartOptions struct {
|
||||
}
|
||||
|
||||
// NewRestartCommand creates a new cobra.Command for `docker restart`
|
||||
func NewRestartCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewRestartCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts restartOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -39,7 +39,7 @@ func NewRestartCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRestart(dockerCli *command.DockerCli, opts *restartOptions) error {
|
||||
func runRestart(dockerCli command.Cli, opts *restartOptions) error {
|
||||
ctx := context.Background()
|
||||
var errs []string
|
||||
var timeout *time.Duration
|
||||
|
||||
@@ -21,7 +21,7 @@ type rmOptions struct {
|
||||
}
|
||||
|
||||
// NewRmCommand creates a new cobra.Command for `docker rm`
|
||||
func NewRmCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewRmCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts rmOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -41,7 +41,7 @@ func NewRmCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRm(dockerCli *command.DockerCli, opts *rmOptions) error {
|
||||
func runRm(dockerCli command.Cli, opts *rmOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
var errs []string
|
||||
|
||||
@@ -32,7 +32,7 @@ type runOptions struct {
|
||||
}
|
||||
|
||||
// NewRunCommand create a new `docker run` command
|
||||
func NewRunCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewRunCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts runOptions
|
||||
var copts *containerOptions
|
||||
|
||||
@@ -96,7 +96,7 @@ func isLocalhost(ip string) bool {
|
||||
return localhostIPRegexp.MatchString(ip)
|
||||
}
|
||||
|
||||
func runRun(dockerCli *command.DockerCli, flags *pflag.FlagSet, ropts *runOptions, copts *containerOptions) error {
|
||||
func runRun(dockerCli command.Cli, flags *pflag.FlagSet, ropts *runOptions, copts *containerOptions) error {
|
||||
proxyConfig := dockerCli.ConfigFile().ParseProxyConfig(dockerCli.Client().DaemonHost(), copts.env.GetAll())
|
||||
newEnv := []string{}
|
||||
for k, v := range proxyConfig {
|
||||
@@ -117,7 +117,7 @@ func runRun(dockerCli *command.DockerCli, flags *pflag.FlagSet, ropts *runOption
|
||||
}
|
||||
|
||||
// nolint: gocyclo
|
||||
func runContainer(dockerCli *command.DockerCli, opts *runOptions, copts *containerOptions, containerConfig *containerConfig) error {
|
||||
func runContainer(dockerCli command.Cli, opts *runOptions, copts *containerOptions, containerConfig *containerConfig) error {
|
||||
config := containerConfig.Config
|
||||
hostConfig := containerConfig.HostConfig
|
||||
stdout, stderr := dockerCli.Out(), dockerCli.Err()
|
||||
@@ -290,8 +290,11 @@ func attachContainer(
|
||||
return nil, errAttach
|
||||
}
|
||||
|
||||
ch := make(chan error, 1)
|
||||
*errCh = ch
|
||||
|
||||
go func() {
|
||||
*errCh <- func() error {
|
||||
ch <- func() error {
|
||||
streamer := hijackedIOStreamer{
|
||||
streams: dockerCli,
|
||||
inputStream: in,
|
||||
|
||||
@@ -27,7 +27,7 @@ type startOptions struct {
|
||||
}
|
||||
|
||||
// NewStartCommand creates a new cobra.Command for `docker start`
|
||||
func NewStartCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewStartCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts startOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -53,7 +53,7 @@ func NewStartCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
}
|
||||
|
||||
// nolint: gocyclo
|
||||
func runStart(dockerCli *command.DockerCli, opts *startOptions) error {
|
||||
func runStart(dockerCli command.Cli, opts *startOptions) error {
|
||||
ctx, cancelFun := context.WithCancel(context.Background())
|
||||
|
||||
if opts.attach || opts.openStdin {
|
||||
@@ -181,7 +181,7 @@ func runStart(dockerCli *command.DockerCli, opts *startOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func startContainersWithoutAttachments(ctx context.Context, dockerCli *command.DockerCli, containers []string) error {
|
||||
func startContainersWithoutAttachments(ctx context.Context, dockerCli command.Cli, containers []string) error {
|
||||
var failedContainers []string
|
||||
for _, container := range containers {
|
||||
if err := dockerCli.Client().ContainerStart(ctx, container, types.ContainerStartOptions{}); err != nil {
|
||||
|
||||
@@ -27,7 +27,7 @@ type statsOptions struct {
|
||||
}
|
||||
|
||||
// NewStatsCommand creates a new cobra.Command for `docker stats`
|
||||
func NewStatsCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewStatsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts statsOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -51,7 +51,7 @@ func NewStatsCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
// runStats displays a live stream of resource usage statistics for one or more containers.
|
||||
// This shows real-time information on CPU usage, memory usage, and network I/O.
|
||||
// nolint: gocyclo
|
||||
func runStats(dockerCli *command.DockerCli, opts *statsOptions) error {
|
||||
func runStats(dockerCli command.Cli, opts *statsOptions) error {
|
||||
showAll := len(opts.containers) == 0
|
||||
closeChan := make(chan error)
|
||||
|
||||
|
||||
@@ -200,7 +200,8 @@ func calculateCPUPercentWindows(v *types.StatsJSON) float64 {
|
||||
return 0.00
|
||||
}
|
||||
|
||||
func calculateBlockIO(blkio types.BlkioStats) (blkRead uint64, blkWrite uint64) {
|
||||
func calculateBlockIO(blkio types.BlkioStats) (uint64, uint64) {
|
||||
var blkRead, blkWrite uint64
|
||||
for _, bioEntry := range blkio.IoServiceBytesRecursive {
|
||||
switch strings.ToLower(bioEntry.Op) {
|
||||
case "read":
|
||||
@@ -209,7 +210,7 @@ func calculateBlockIO(blkio types.BlkioStats) (blkRead uint64, blkWrite uint64)
|
||||
blkWrite = blkWrite + bioEntry.Value
|
||||
}
|
||||
}
|
||||
return
|
||||
return blkRead, blkWrite
|
||||
}
|
||||
|
||||
func calculateNetwork(network map[string]types.NetworkStats) (float64, float64) {
|
||||
|
||||
@@ -20,7 +20,7 @@ type stopOptions struct {
|
||||
}
|
||||
|
||||
// NewStopCommand creates a new cobra.Command for `docker stop`
|
||||
func NewStopCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewStopCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts stopOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -39,7 +39,7 @@ func NewStopCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runStop(dockerCli *command.DockerCli, opts *stopOptions) error {
|
||||
func runStop(dockerCli command.Cli, opts *stopOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
var timeout *time.Duration
|
||||
|
||||
@@ -18,7 +18,7 @@ type topOptions struct {
|
||||
}
|
||||
|
||||
// NewTopCommand creates a new cobra.Command for `docker top`
|
||||
func NewTopCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewTopCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts topOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -38,7 +38,7 @@ func NewTopCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runTop(dockerCli *command.DockerCli, opts *topOptions) error {
|
||||
func runTop(dockerCli command.Cli, opts *topOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
procList, err := dockerCli.Client().ContainerTop(ctx, opts.container, opts.args)
|
||||
|
||||
@@ -16,7 +16,7 @@ type unpauseOptions struct {
|
||||
}
|
||||
|
||||
// NewUnpauseCommand creates a new cobra.Command for `docker unpause`
|
||||
func NewUnpauseCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewUnpauseCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts unpauseOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -31,7 +31,7 @@ func NewUnpauseCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runUnpause(dockerCli *command.DockerCli, opts *unpauseOptions) error {
|
||||
func runUnpause(dockerCli command.Cli, opts *unpauseOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
var errs []string
|
||||
|
||||
@@ -35,7 +35,7 @@ type updateOptions struct {
|
||||
}
|
||||
|
||||
// NewUpdateCommand creates a new cobra.Command for `docker update`
|
||||
func NewUpdateCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewUpdateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var options updateOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -72,7 +72,7 @@ func NewUpdateCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runUpdate(dockerCli *command.DockerCli, options *updateOptions) error {
|
||||
func runUpdate(dockerCli command.Cli, options *updateOptions) error {
|
||||
var err error
|
||||
|
||||
if options.nFlag == 0 {
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func waitExitOrRemoved(ctx context.Context, dockerCli *command.DockerCli, containerID string, waitRemove bool) <-chan int {
|
||||
func waitExitOrRemoved(ctx context.Context, dockerCli command.Cli, containerID string, waitRemove bool) <-chan int {
|
||||
if len(containerID) == 0 {
|
||||
// containerID can never be empty
|
||||
panic("Internal Error: waitExitOrRemoved needs a containerID as parameter")
|
||||
@@ -47,7 +47,7 @@ func waitExitOrRemoved(ctx context.Context, dockerCli *command.DockerCli, contai
|
||||
return statusC
|
||||
}
|
||||
|
||||
func legacyWaitExitOrRemoved(ctx context.Context, dockerCli *command.DockerCli, containerID string, waitRemove bool) <-chan int {
|
||||
func legacyWaitExitOrRemoved(ctx context.Context, dockerCli command.Cli, containerID string, waitRemove bool) <-chan int {
|
||||
var removeErr error
|
||||
statusChan := make(chan int)
|
||||
exitCode := 125
|
||||
|
||||
@@ -16,7 +16,7 @@ type waitOptions struct {
|
||||
}
|
||||
|
||||
// NewWaitCommand creates a new cobra.Command for `docker wait`
|
||||
func NewWaitCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewWaitCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts waitOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -32,7 +32,7 @@ func NewWaitCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runWait(dockerCli *command.DockerCli, opts *waitOptions) error {
|
||||
func runWait(dockerCli command.Cli, opts *waitOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
var errs []string
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
"github.com/docker/go-units"
|
||||
)
|
||||
|
||||
@@ -165,7 +164,7 @@ func (c *containerContext) Image() string {
|
||||
func (c *containerContext) Command() string {
|
||||
command := c.c.Command
|
||||
if c.trunc {
|
||||
command = stringutils.Ellipsis(command, 20)
|
||||
command = Ellipsis(command, 20)
|
||||
}
|
||||
return strconv.Quote(command)
|
||||
}
|
||||
@@ -227,7 +226,7 @@ func (c *containerContext) Mounts() string {
|
||||
name = m.Name
|
||||
}
|
||||
if c.trunc {
|
||||
name = stringutils.Ellipsis(name, 15)
|
||||
name = Ellipsis(name, 15)
|
||||
}
|
||||
mounts = append(mounts, name)
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ func TestContainerPsContext(t *testing.T) {
|
||||
Source: "/a/path",
|
||||
},
|
||||
},
|
||||
}, true, "this-is-a-lo...", ctx.Mounts},
|
||||
}, true, "this-is-a-long…", ctx.Mounts},
|
||||
{types.Container{
|
||||
Mounts: []types.MountPoint{
|
||||
{
|
||||
|
||||
@@ -118,11 +118,11 @@ func (ctx *DiskUsageContext) Write() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
func (ctx *DiskUsageContext) verboseWrite() (err error) {
|
||||
func (ctx *DiskUsageContext) verboseWrite() error {
|
||||
// First images
|
||||
tmpl, err := ctx.startSubsection(defaultDiskUsageImageTableFormat)
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
ctx.Output.Write([]byte("Images space usage:\n\n"))
|
||||
@@ -141,14 +141,14 @@ func (ctx *DiskUsageContext) verboseWrite() (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
err = ctx.contextFormat(tmpl, &imageContext{
|
||||
err := ctx.contextFormat(tmpl, &imageContext{
|
||||
repo: repo,
|
||||
tag: tag,
|
||||
trunc: true,
|
||||
i: *i,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
}
|
||||
ctx.postFormat(tmpl, newImageContext())
|
||||
@@ -157,17 +157,14 @@ func (ctx *DiskUsageContext) verboseWrite() (err error) {
|
||||
ctx.Output.Write([]byte("\nContainers space usage:\n\n"))
|
||||
tmpl, err = ctx.startSubsection(defaultDiskUsageContainerTableFormat)
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
for _, c := range ctx.Containers {
|
||||
// Don't display the virtual size
|
||||
c.SizeRootFs = 0
|
||||
err = ctx.contextFormat(tmpl, &containerContext{
|
||||
trunc: true,
|
||||
c: *c,
|
||||
})
|
||||
err := ctx.contextFormat(tmpl, &containerContext{trunc: true, c: *c})
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
}
|
||||
ctx.postFormat(tmpl, newContainerContext())
|
||||
@@ -176,21 +173,18 @@ func (ctx *DiskUsageContext) verboseWrite() (err error) {
|
||||
ctx.Output.Write([]byte("\nLocal Volumes space usage:\n\n"))
|
||||
tmpl, err = ctx.startSubsection(defaultDiskUsageVolumeTableFormat)
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
for _, v := range ctx.Volumes {
|
||||
err = ctx.contextFormat(tmpl, &volumeContext{
|
||||
v: *v,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
if err := ctx.contextFormat(tmpl, &volumeContext{v: *v}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
ctx.postFormat(tmpl, newVolumeContext())
|
||||
|
||||
// And build cache
|
||||
fmt.Fprintf(ctx.Output, "\nBuild cache usage: %s\n\n", units.HumanSize(float64(ctx.BuilderSize)))
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
type diskUsageImagesContext struct {
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/width"
|
||||
)
|
||||
|
||||
// charWidth returns the number of horizontal positions a character occupies,
|
||||
// and is used to account for wide characters when displaying strings.
|
||||
//
|
||||
// In a broad sense, wide characters include East Asian Wide, East Asian Full-width,
|
||||
// (when not in East Asian context) see http://unicode.org/reports/tr11/.
|
||||
func charWidth(r rune) int {
|
||||
switch width.LookupRune(r).Kind() {
|
||||
case width.EastAsianWide, width.EastAsianFullwidth:
|
||||
return 2
|
||||
default:
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
// Ellipsis truncates a string to fit within maxDisplayWidth, and appends ellipsis (…).
|
||||
// For maxDisplayWidth of 1 and lower, no ellipsis is appended.
|
||||
// For maxDisplayWidth of 1, first char of string will return even if its width > 1.
|
||||
func Ellipsis(s string, maxDisplayWidth int) string {
|
||||
if maxDisplayWidth <= 0 {
|
||||
return ""
|
||||
}
|
||||
rs := []rune(s)
|
||||
if maxDisplayWidth == 1 {
|
||||
return string(rs[0])
|
||||
}
|
||||
|
||||
byteLen := len(s)
|
||||
if byteLen == utf8.RuneCountInString(s) {
|
||||
if byteLen <= maxDisplayWidth {
|
||||
return s
|
||||
}
|
||||
return string(rs[:maxDisplayWidth-1]) + "…"
|
||||
}
|
||||
|
||||
var (
|
||||
display []int
|
||||
displayWidth int
|
||||
)
|
||||
for _, r := range rs {
|
||||
cw := charWidth(r)
|
||||
displayWidth += cw
|
||||
display = append(display, displayWidth)
|
||||
}
|
||||
if displayWidth <= maxDisplayWidth {
|
||||
return s
|
||||
}
|
||||
for i := range display {
|
||||
if display[i] <= maxDisplayWidth-1 && display[i+1] > maxDisplayWidth-1 {
|
||||
return string(rs[:i+1]) + "…"
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestEllipsis(t *testing.T) {
|
||||
var testcases = []struct {
|
||||
source string
|
||||
width int
|
||||
expected string
|
||||
}{
|
||||
{source: "t🐳ststring", width: 0, expected: ""},
|
||||
{source: "t🐳ststring", width: 1, expected: "t"},
|
||||
{source: "t🐳ststring", width: 2, expected: "t…"},
|
||||
{source: "t🐳ststring", width: 6, expected: "t🐳st…"},
|
||||
{source: "t🐳ststring", width: 20, expected: "t🐳ststring"},
|
||||
{source: "你好世界teststring", width: 0, expected: ""},
|
||||
{source: "你好世界teststring", width: 1, expected: "你"},
|
||||
{source: "你好世界teststring", width: 3, expected: "你…"},
|
||||
{source: "你好世界teststring", width: 6, expected: "你好…"},
|
||||
{source: "你好世界teststring", width: 20, expected: "你好世界teststring"},
|
||||
}
|
||||
|
||||
for _, testcase := range testcases {
|
||||
assert.Equal(t, testcase.expected, Ellipsis(testcase.source, testcase.width))
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
units "github.com/docker/go-units"
|
||||
)
|
||||
|
||||
@@ -93,7 +92,7 @@ func (c *historyContext) CreatedSince() string {
|
||||
func (c *historyContext) CreatedBy() string {
|
||||
createdBy := strings.Replace(c.h.CreatedBy, "\t", " ", -1)
|
||||
if c.trunc {
|
||||
return stringutils.Ellipsis(createdBy, 45)
|
||||
return Ellipsis(createdBy, 45)
|
||||
}
|
||||
return createdBy
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -96,7 +95,7 @@ func TestHistoryContext_CreatedBy(t *testing.T) {
|
||||
historyContext{
|
||||
h: image.HistoryResponseItem{CreatedBy: withTabs},
|
||||
trunc: true,
|
||||
}, stringutils.Ellipsis(expected, 45), ctx.CreatedBy,
|
||||
}, Ellipsis(expected, 45), ctx.CreatedBy,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -191,7 +190,7 @@ imageID3 24 hours ago /bin/bash ls
|
||||
imageID4 24 hours ago /bin/bash grep 183MB Hi
|
||||
`
|
||||
expectedTrunc := `IMAGE CREATED CREATED BY SIZE COMMENT
|
||||
imageID1 24 hours ago /bin/bash ls && npm i && npm run test && k... 183MB Hi
|
||||
imageID1 24 hours ago /bin/bash ls && npm i && npm run test && kar… 183MB Hi
|
||||
imageID2 24 hours ago /bin/bash echo 183MB Hi
|
||||
imageID3 24 hours ago /bin/bash ls 183MB Hi
|
||||
imageID4 24 hours ago /bin/bash grep 183MB Hi
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -80,7 +79,7 @@ func (c *pluginContext) Description() string {
|
||||
desc := strings.Replace(c.p.Config.Description, "\n", "", -1)
|
||||
desc = strings.Replace(desc, "\r", "", -1)
|
||||
if c.trunc {
|
||||
desc = stringutils.Ellipsis(desc, 45)
|
||||
desc = Ellipsis(desc, 45)
|
||||
}
|
||||
|
||||
return desc
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"strings"
|
||||
|
||||
registry "github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -73,7 +72,7 @@ func (c *searchContext) Description() string {
|
||||
desc := strings.Replace(c.s.Description, "\n", " ", -1)
|
||||
desc = strings.Replace(desc, "\r", " ", -1)
|
||||
if c.trunc {
|
||||
desc = stringutils.Ellipsis(desc, 45)
|
||||
desc = Ellipsis(desc, 45)
|
||||
}
|
||||
return desc
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"testing"
|
||||
|
||||
registrytypes "github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
"github.com/gotestyourself/gotestyourself/golden"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -79,7 +78,7 @@ func TestSearchContextDescription(t *testing.T) {
|
||||
{searchContext{
|
||||
s: registrytypes.SearchResult{Description: longDescription},
|
||||
trunc: true,
|
||||
}, stringutils.Ellipsis(longDescription, 45), ctx.Description},
|
||||
}, Ellipsis(longDescription, 45), ctx.Description},
|
||||
{searchContext{
|
||||
s: registrytypes.SearchResult{Description: descriptionWReturns},
|
||||
trunc: false,
|
||||
@@ -87,7 +86,7 @@ func TestSearchContextDescription(t *testing.T) {
|
||||
{searchContext{
|
||||
s: registrytypes.SearchResult{Description: descriptionWReturns},
|
||||
trunc: true,
|
||||
}, stringutils.Ellipsis(longDescription, 45), ctx.Description},
|
||||
}, Ellipsis(longDescription, 45), ctx.Description},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
|
||||
@@ -128,18 +128,18 @@ func getBuildSharedKey(dir string) (string, error) {
|
||||
return hex.EncodeToString(s[:]), nil
|
||||
}
|
||||
|
||||
func tryNodeIdentifier() (out string) {
|
||||
out = cliconfig.Dir() // return config dir as default on permission error
|
||||
func tryNodeIdentifier() string {
|
||||
out := cliconfig.Dir() // return config dir as default on permission error
|
||||
if err := os.MkdirAll(cliconfig.Dir(), 0700); err == nil {
|
||||
sessionFile := filepath.Join(cliconfig.Dir(), ".buildNodeID")
|
||||
if _, err := os.Lstat(sessionFile); err != nil {
|
||||
if os.IsNotExist(err) { // create a new file with stored randomness
|
||||
b := make([]byte, 32)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return
|
||||
return out
|
||||
}
|
||||
if err := ioutil.WriteFile(sessionFile, []byte(hex.EncodeToString(b)), 0600); err != nil {
|
||||
return
|
||||
return out
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,5 +149,5 @@ func tryNodeIdentifier() (out string) {
|
||||
return string(dt)
|
||||
}
|
||||
}
|
||||
return
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
fmt.Fprintln(dockerCli.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed)))
|
||||
return nil
|
||||
},
|
||||
Tags: map[string]string{"version": "1.25"},
|
||||
Annotations: map[string]string{"version": "1.25"},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
@@ -65,12 +65,12 @@ func runPrune(dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint6
|
||||
warning = allImageWarning
|
||||
}
|
||||
if !options.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) {
|
||||
return
|
||||
return 0, "", nil
|
||||
}
|
||||
|
||||
report, err := dockerCli.Client().ImagesPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
if len(report.ImagesDeleted) > 0 {
|
||||
@@ -85,7 +85,7 @@ func runPrune(dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint6
|
||||
spaceReclaimed = report.SpaceReclaimed
|
||||
}
|
||||
|
||||
return
|
||||
return spaceReclaimed, output, nil
|
||||
}
|
||||
|
||||
// RunPrune calls the Image Prune API
|
||||
|
||||
@@ -41,24 +41,25 @@ func NewPullCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
func runPull(cli command.Cli, opts pullOptions) error {
|
||||
ctx := context.Background()
|
||||
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, AuthResolver(cli), opts.remote)
|
||||
if err != nil {
|
||||
distributionRef, err := reference.ParseNormalizedNamed(opts.remote)
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
}
|
||||
|
||||
distributionRef := imgRefAndAuth.Reference()
|
||||
if opts.all && !reference.IsNameOnly(distributionRef) {
|
||||
case opts.all && !reference.IsNameOnly(distributionRef):
|
||||
return errors.New("tag can't be used with --all-tags/-a")
|
||||
}
|
||||
|
||||
if !opts.all && reference.IsNameOnly(distributionRef) {
|
||||
case !opts.all && reference.IsNameOnly(distributionRef):
|
||||
distributionRef = reference.TagNameOnly(distributionRef)
|
||||
if tagged, ok := distributionRef.(reference.Tagged); ok {
|
||||
fmt.Fprintf(cli.Out(), "Using default tag: %s\n", tagged.Tag())
|
||||
}
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, AuthResolver(cli), distributionRef.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if reference has a digest
|
||||
_, isCanonical := distributionRef.(reference.Canonical)
|
||||
if command.IsTrusted() && !isCanonical {
|
||||
|
||||
@@ -2,11 +2,14 @@ package image
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/internal/test"
|
||||
"github.com/docker/cli/internal/test/testutil"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/gotestyourself/gotestyourself/golden"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -44,20 +47,28 @@ func TestNewPullCommandErrors(t *testing.T) {
|
||||
|
||||
func TestNewPullCommandSuccess(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
name string
|
||||
args []string
|
||||
expectedTag string
|
||||
}{
|
||||
{
|
||||
name: "simple",
|
||||
args: []string{"image:tag"},
|
||||
name: "simple",
|
||||
args: []string{"image:tag"},
|
||||
expectedTag: "image:tag",
|
||||
},
|
||||
{
|
||||
name: "simple-no-tag",
|
||||
args: []string{"image"},
|
||||
name: "simple-no-tag",
|
||||
args: []string{"image"},
|
||||
expectedTag: "image:latest",
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
cli := test.NewFakeCli(&fakeClient{})
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
imagePullFunc: func(ref string, options types.ImagePullOptions) (io.ReadCloser, error) {
|
||||
assert.Equal(t, tc.expectedTag, ref, tc.name)
|
||||
return ioutil.NopCloser(strings.NewReader("")), nil
|
||||
},
|
||||
})
|
||||
cmd := NewPullCommand(cli)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
|
||||
@@ -84,7 +84,7 @@ func PushTrustedReference(streams command.Streams, repoInfo *registry.Repository
|
||||
if err := jsonmessage.DisplayJSONMessagesToStream(in, streams.Out(), nil); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(streams.Out(), "No tag specified, skipping trust metadata push")
|
||||
fmt.Fprintln(streams.Err(), "No tag specified, skipping trust metadata push")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -97,16 +97,14 @@ func PushTrustedReference(streams command.Streams, repoInfo *registry.Repository
|
||||
}
|
||||
|
||||
if target == nil {
|
||||
fmt.Fprintln(streams.Out(), "No targets found, please provide a specific tag in order to sign it")
|
||||
return nil
|
||||
return errors.Errorf("no targets found, please provide a specific tag in order to sign it")
|
||||
}
|
||||
|
||||
fmt.Fprintln(streams.Out(), "Signing and pushing trust metadata")
|
||||
|
||||
repo, err := trust.GetNotaryRepository(streams.In(), streams.Out(), command.UserAgent(), repoInfo, &authConfig, "push", "pull")
|
||||
if err != nil {
|
||||
fmt.Fprintf(streams.Out(), "Error establishing connection to notary repository: %s\n", err)
|
||||
return err
|
||||
return errors.Wrap(err, "error establishing connection to trust repository")
|
||||
}
|
||||
|
||||
// get the latest repository metadata so we can figure out which roles to sign
|
||||
@@ -146,7 +144,7 @@ func PushTrustedReference(streams command.Streams, repoInfo *registry.Repository
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Fprintf(streams.Out(), "Failed to sign %q:%s - %s\n", repoInfo.Name.Name(), tag, err.Error())
|
||||
err = errors.Wrapf(err, "failed to sign %s:%s", repoInfo.Name.Name(), tag)
|
||||
return trust.NotaryError(repoInfo.Name.Name(), err)
|
||||
}
|
||||
|
||||
@@ -200,7 +198,11 @@ func trustedPull(ctx context.Context, cli command.Cli, imgRefAndAuth trust.Image
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := imagePullPrivileged(ctx, cli, imgRefAndAuth, false); err != nil {
|
||||
updatedImgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, AuthResolver(cli), trustedRef.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := imagePullPrivileged(ctx, cli, updatedImgRefAndAuth, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -219,8 +221,7 @@ func trustedPull(ctx context.Context, cli command.Cli, imgRefAndAuth trust.Image
|
||||
func getTrustedPullTargets(cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth) ([]target, error) {
|
||||
notaryRepo, err := cli.NotaryClient(imgRefAndAuth, trust.ActionsPullOnly)
|
||||
if err != nil {
|
||||
fmt.Fprintf(cli.Out(), "Error establishing connection to trust repository: %s\n", err)
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "error establishing connection to trust repository")
|
||||
}
|
||||
|
||||
ref := imgRefAndAuth.Reference()
|
||||
@@ -235,7 +236,7 @@ func getTrustedPullTargets(cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth)
|
||||
for _, tgt := range targets {
|
||||
t, err := convertTarget(tgt.Target)
|
||||
if err != nil {
|
||||
fmt.Fprintf(cli.Out(), "Skipping target for %q\n", reference.FamiliarName(ref))
|
||||
fmt.Fprintf(cli.Err(), "Skipping target for %q\n", reference.FamiliarName(ref))
|
||||
continue
|
||||
}
|
||||
// Only list tags in the top level targets role or the releases delegation role - ignore
|
||||
@@ -261,7 +262,7 @@ func getTrustedPullTargets(cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth)
|
||||
return nil, trust.NotaryError(ref.Name(), errors.Errorf("No trust data for %s", tagged.Tag()))
|
||||
}
|
||||
|
||||
logrus.Debugf("retrieving target for %s role\n", t.Role)
|
||||
logrus.Debugf("retrieving target for %s role", t.Role)
|
||||
r, err := convertTarget(t.Target)
|
||||
return []target{r}, err
|
||||
}
|
||||
@@ -310,8 +311,7 @@ func TrustedReference(ctx context.Context, cli command.Cli, ref reference.NamedT
|
||||
|
||||
notaryRepo, err := trust.GetNotaryRepository(cli.In(), cli.Out(), command.UserAgent(), repoInfo, &authConfig, "pull")
|
||||
if err != nil {
|
||||
fmt.Fprintf(cli.Out(), "Error establishing connection to trust repository: %s\n", err)
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "error establishing connection to trust repository")
|
||||
}
|
||||
|
||||
t, err := notaryRepo.GetTargetByName(ref.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole)
|
||||
@@ -328,7 +328,6 @@ func TrustedReference(ctx context.Context, cli command.Cli, ref reference.NamedT
|
||||
return nil, err
|
||||
|
||||
}
|
||||
|
||||
return reference.WithDigest(reference.TrimNamed(ref), r.digest)
|
||||
}
|
||||
|
||||
@@ -351,7 +350,7 @@ func TagTrusted(ctx context.Context, cli command.Cli, trustedRef reference.Canon
|
||||
familiarRef := reference.FamiliarString(ref)
|
||||
trustedFamiliarRef := reference.FamiliarString(trustedRef)
|
||||
|
||||
fmt.Fprintf(cli.Out(), "Tagging %s as %s\n", trustedFamiliarRef, familiarRef)
|
||||
fmt.Fprintf(cli.Err(), "Tagging %s as %s\n", trustedFamiliarRef, familiarRef)
|
||||
|
||||
return cli.Client().ImageTag(ctx, trustedFamiliarRef, familiarRef)
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Tags: map[string]string{"version": "1.25"},
|
||||
Annotations: map[string]string{"version": "1.25"},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
@@ -50,12 +50,12 @@ func runPrune(dockerCli command.Cli, options pruneOptions) (output string, err e
|
||||
pruneFilters := command.PruneFilters(dockerCli, options.filter.Value())
|
||||
|
||||
if !options.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) {
|
||||
return
|
||||
return "", nil
|
||||
}
|
||||
|
||||
report, err := dockerCli.Client().NetworksPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(report.NetworksDeleted) > 0 {
|
||||
@@ -65,7 +65,7 @@ func runPrune(dockerCli command.Cli, options pruneOptions) (output string, err e
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return output, nil
|
||||
}
|
||||
|
||||
// RunPrune calls the Network Prune API
|
||||
|
||||
@@ -14,11 +14,11 @@ import (
|
||||
// NewNodeCommand returns a cobra command for `node` subcommands
|
||||
func NewNodeCommand(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "node",
|
||||
Short: "Manage Swarm nodes",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Tags: map[string]string{"version": "1.24"},
|
||||
Use: "node",
|
||||
Short: "Manage Swarm nodes",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Annotations: map[string]string{"version": "1.24"},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newDemoteCommand(dockerCli),
|
||||
|
||||
@@ -7,14 +7,13 @@ import (
|
||||
)
|
||||
|
||||
// NewPluginCommand returns a cobra command for `plugin` subcommands
|
||||
// nolint: interfacer
|
||||
func NewPluginCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewPluginCommand(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "plugin",
|
||||
Short: "Manage plugins",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Tags: map[string]string{"version": "1.25"},
|
||||
Use: "plugin",
|
||||
Short: "Manage plugins",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Annotations: map[string]string{"version": "1.25"},
|
||||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
|
||||
@@ -63,7 +63,7 @@ type pluginCreateOptions struct {
|
||||
compress bool
|
||||
}
|
||||
|
||||
func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
options := pluginCreateOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -84,7 +84,7 @@ func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runCreate(dockerCli *command.DockerCli, options pluginCreateOptions) error {
|
||||
func runCreate(dockerCli command.Cli, options pluginCreateOptions) error {
|
||||
var (
|
||||
createCtx io.ReadCloser
|
||||
err error
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func newDisableCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newDisableCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var force bool
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -27,7 +27,7 @@ func newDisableCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runDisable(dockerCli *command.DockerCli, name string, force bool) error {
|
||||
func runDisable(dockerCli command.Cli, name string, force bool) error {
|
||||
if err := dockerCli.Client().PluginDisable(context.Background(), name, types.PluginDisableOptions{Force: force}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ type enableOpts struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func newEnableCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newEnableCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts enableOpts
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -34,7 +34,7 @@ func newEnableCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runEnable(dockerCli *command.DockerCli, opts *enableOpts) error {
|
||||
func runEnable(dockerCli command.Cli, opts *enableOpts) error {
|
||||
name := opts.name
|
||||
if opts.timeout < 0 {
|
||||
return errors.Errorf("negative timeout %d is invalid", opts.timeout)
|
||||
|
||||
@@ -13,7 +13,7 @@ type inspectOptions struct {
|
||||
format string
|
||||
}
|
||||
|
||||
func newInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newInspectCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts inspectOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -31,7 +31,7 @@ func newInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runInspect(dockerCli *command.DockerCli, opts inspectOptions) error {
|
||||
func runInspect(dockerCli command.Cli, opts inspectOptions) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
getRef := func(ref string) (interface{}, []byte, error) {
|
||||
|
||||
@@ -31,7 +31,7 @@ func loadPullFlags(opts *pluginOptions, flags *pflag.FlagSet) {
|
||||
command.AddTrustVerificationFlags(flags)
|
||||
}
|
||||
|
||||
func newInstallCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newInstallCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var options pluginOptions
|
||||
cmd := &cobra.Command{
|
||||
Use: "install [OPTIONS] PLUGIN [KEY=VALUE...]",
|
||||
@@ -57,12 +57,12 @@ type pluginRegistryService struct {
|
||||
registry.Service
|
||||
}
|
||||
|
||||
func (s pluginRegistryService) ResolveRepository(name reference.Named) (repoInfo *registry.RepositoryInfo, err error) {
|
||||
repoInfo, err = s.Service.ResolveRepository(name)
|
||||
func (s pluginRegistryService) ResolveRepository(name reference.Named) (*registry.RepositoryInfo, error) {
|
||||
repoInfo, err := s.Service.ResolveRepository(name)
|
||||
if repoInfo != nil {
|
||||
repoInfo.Class = "plugin"
|
||||
}
|
||||
return
|
||||
return repoInfo, err
|
||||
}
|
||||
|
||||
func newRegistryService() (registry.Service, error) {
|
||||
@@ -73,7 +73,7 @@ func newRegistryService() (registry.Service, error) {
|
||||
return pluginRegistryService{Service: svc}, nil
|
||||
}
|
||||
|
||||
func buildPullConfig(ctx context.Context, dockerCli *command.DockerCli, opts pluginOptions, cmdName string) (types.PluginInstallOptions, error) {
|
||||
func buildPullConfig(ctx context.Context, dockerCli command.Cli, opts pluginOptions, cmdName string) (types.PluginInstallOptions, error) {
|
||||
// Names with both tag and digest will be treated by the daemon
|
||||
// as a pull by digest with a local name for the tag
|
||||
// (if no local name is provided).
|
||||
@@ -130,7 +130,7 @@ func buildPullConfig(ctx context.Context, dockerCli *command.DockerCli, opts plu
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func runInstall(dockerCli *command.DockerCli, opts pluginOptions) error {
|
||||
func runInstall(dockerCli command.Cli, opts pluginOptions) error {
|
||||
var localName string
|
||||
if opts.localName != "" {
|
||||
aref, err := reference.ParseNormalizedNamed(opts.localName)
|
||||
@@ -163,7 +163,7 @@ func runInstall(dockerCli *command.DockerCli, opts pluginOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func acceptPrivileges(dockerCli *command.DockerCli, name string) func(privileges types.PluginPrivileges) (bool, error) {
|
||||
func acceptPrivileges(dockerCli command.Cli, name string) func(privileges types.PluginPrivileges) (bool, error) {
|
||||
return func(privileges types.PluginPrivileges) (bool, error) {
|
||||
fmt.Fprintf(dockerCli.Out(), "Plugin %q is requesting the following privileges:\n", name)
|
||||
for _, privilege := range privileges {
|
||||
|
||||
@@ -16,7 +16,7 @@ type listOptions struct {
|
||||
filter opts.FilterOpt
|
||||
}
|
||||
|
||||
func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newListCommand(dockerCli command.Cli) *cobra.Command {
|
||||
options := listOptions{filter: opts.NewFilterOpt()}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -39,7 +39,7 @@ func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runList(dockerCli *command.DockerCli, options listOptions) error {
|
||||
func runList(dockerCli command.Cli, options listOptions) error {
|
||||
plugins, err := dockerCli.Client().PluginList(context.Background(), options.filter.Value())
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -16,7 +16,7 @@ type rmOptions struct {
|
||||
plugins []string
|
||||
}
|
||||
|
||||
func newRemoveCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newRemoveCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts rmOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -35,7 +35,7 @@ func newRemoveCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRemove(dockerCli *command.DockerCli, opts *rmOptions) error {
|
||||
func runRemove(dockerCli command.Cli, opts *rmOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
var errs cli.Errors
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newSetCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newSetCommand(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "set PLUGIN KEY=VALUE [KEY=VALUE...]",
|
||||
Short: "Change settings for a plugin",
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newUpgradeCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newUpgradeCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var options pluginOptions
|
||||
cmd := &cobra.Command{
|
||||
Use: "upgrade [OPTIONS] PLUGIN [REMOTE]",
|
||||
@@ -26,7 +26,7 @@ func newUpgradeCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
}
|
||||
return runUpgrade(dockerCli, options)
|
||||
},
|
||||
Tags: map[string]string{"version": "1.26"},
|
||||
Annotations: map[string]string{"version": "1.26"},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
@@ -35,7 +35,7 @@ func newUpgradeCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runUpgrade(dockerCli *command.DockerCli, opts pluginOptions) error {
|
||||
func runUpgrade(dockerCli command.Cli, opts pluginOptions) error {
|
||||
ctx := context.Background()
|
||||
p, _, err := dockerCli.Client().PluginInspectWithRaw(ctx, opts.localName)
|
||||
if err != nil {
|
||||
|
||||
@@ -8,14 +8,13 @@ import (
|
||||
)
|
||||
|
||||
// NewSecretCommand returns a cobra command for `secret` subcommands
|
||||
// nolint: interfacer
|
||||
func NewSecretCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewSecretCommand(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "secret",
|
||||
Short: "Manage Docker secrets",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Tags: map[string]string{"version": "1.25"},
|
||||
Use: "secret",
|
||||
Short: "Manage Docker secrets",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Annotations: map[string]string{"version": "1.25"},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newSecretListCommand(dockerCli),
|
||||
|
||||
@@ -14,6 +14,7 @@ type fakeClient struct {
|
||||
serviceInspectWithRawFunc func(ctx context.Context, serviceID string, options types.ServiceInspectOptions) (swarm.Service, []byte, error)
|
||||
serviceUpdateFunc func(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error)
|
||||
serviceListFunc func(context.Context, types.ServiceListOptions) ([]swarm.Service, error)
|
||||
taskListFunc func(context.Context, types.TaskListOptions) ([]swarm.Task, error)
|
||||
infoFunc func(ctx context.Context) (types.Info, error)
|
||||
}
|
||||
|
||||
@@ -22,6 +23,9 @@ func (f *fakeClient) NodeList(ctx context.Context, options types.NodeListOptions
|
||||
}
|
||||
|
||||
func (f *fakeClient) TaskList(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error) {
|
||||
if f.taskListFunc != nil {
|
||||
return f.taskListFunc(ctx, options)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -8,14 +8,13 @@ import (
|
||||
)
|
||||
|
||||
// NewServiceCommand returns a cobra command for `service` subcommands
|
||||
// nolint: interfacer
|
||||
func NewServiceCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewServiceCommand(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "service",
|
||||
Short: "Manage services",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Tags: map[string]string{"version": "1.24"},
|
||||
Use: "service",
|
||||
Short: "Manage services",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Annotations: map[string]string{"version": "1.24"},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newCreateCommand(dockerCli),
|
||||
|
||||
@@ -54,7 +54,7 @@ func runInspect(dockerCli command.Cli, opts inspectOptions) error {
|
||||
getRef := func(ref string) (interface{}, []byte, error) {
|
||||
// Service inspect shows defaults values in empty fields.
|
||||
service, _, err := client.ServiceInspectWithRaw(ctx, ref, types.ServiceInspectOptions{InsertDefaults: true})
|
||||
if err == nil || !apiclient.IsErrServiceNotFound(err) {
|
||||
if err == nil || !apiclient.IsErrNotFound(err) {
|
||||
return service, nil, err
|
||||
}
|
||||
return nil, nil, errors.Errorf("Error: no such service: %s", ref)
|
||||
@@ -62,7 +62,7 @@ func runInspect(dockerCli command.Cli, opts inspectOptions) error {
|
||||
|
||||
getNetwork := func(ref string) (interface{}, []byte, error) {
|
||||
network, _, err := client.NetworkInspectWithRaw(ctx, ref, types.NetworkInspectOptions{Scope: "swarm"})
|
||||
if err == nil || !apiclient.IsErrNetworkNotFound(err) {
|
||||
if err == nil || !apiclient.IsErrNotFound(err) {
|
||||
return network, nil, err
|
||||
}
|
||||
return nil, nil, errors.Errorf("Error: no such network: %s", ref)
|
||||
|
||||
@@ -37,7 +37,7 @@ type logsOptions struct {
|
||||
target string
|
||||
}
|
||||
|
||||
func newLogsCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newLogsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts logsOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -48,7 +48,7 @@ func newLogsCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
opts.target = args[0]
|
||||
return runLogs(dockerCli, &opts)
|
||||
},
|
||||
Tags: map[string]string{"version": "1.29"},
|
||||
Annotations: map[string]string{"version": "1.29"},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
@@ -68,7 +68,7 @@ func newLogsCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runLogs(dockerCli *command.DockerCli, opts *logsOptions) error {
|
||||
func runLogs(dockerCli command.Cli, opts *logsOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
options := types.ContainerLogsOptions{
|
||||
@@ -97,12 +97,12 @@ func runLogs(dockerCli *command.DockerCli, opts *logsOptions) error {
|
||||
service, _, err := cli.ServiceInspectWithRaw(ctx, opts.target, types.ServiceInspectOptions{})
|
||||
if err != nil {
|
||||
// if it's any error other than service not found, it's Real
|
||||
if !client.IsErrServiceNotFound(err) {
|
||||
if !client.IsErrNotFound(err) {
|
||||
return err
|
||||
}
|
||||
task, _, err := cli.TaskInspectWithRaw(ctx, opts.target)
|
||||
if err != nil {
|
||||
if client.IsErrTaskNotFound(err) {
|
||||
if client.IsErrNotFound(err) {
|
||||
// if the task isn't found, rewrite the error to be clear
|
||||
// that we looked for services AND tasks and found none
|
||||
err = fmt.Errorf("no such task or service: %v", opts.target)
|
||||
|
||||
@@ -354,7 +354,7 @@ func convertNetworks(ctx context.Context, apiClient client.NetworkAPIClient, net
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
netAttach = append(netAttach, swarm.NetworkAttachmentConfig{ // nolint: gosimple
|
||||
netAttach = append(netAttach, swarm.NetworkAttachmentConfig{
|
||||
Target: net.Target,
|
||||
Aliases: net.Aliases,
|
||||
DriverOpts: net.DriverOpts,
|
||||
|
||||
@@ -69,6 +69,9 @@ func runPS(dockerCli command.Cli, options psOptions) error {
|
||||
if len(format) == 0 {
|
||||
format = task.DefaultFormat(dockerCli.ConfigFile(), options.quiet)
|
||||
}
|
||||
if options.quiet {
|
||||
options.noTrunc = true
|
||||
}
|
||||
if err := task.Print(ctx, dockerCli, tasks, idresolver.New(client, options.noResolve), !options.noTrunc, options.quiet, format); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -90,6 +90,22 @@ func TestRunPSWarnsOnNotFound(t *testing.T) {
|
||||
assert.EqualError(t, err, "no such service: bar")
|
||||
}
|
||||
|
||||
func TestRunPSQuiet(t *testing.T) {
|
||||
client := &fakeClient{
|
||||
serviceListFunc: func(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||
return []swarm.Service{{ID: "foo"}}, nil
|
||||
},
|
||||
taskListFunc: func(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error) {
|
||||
return []swarm.Task{{ID: "sxabyp0obqokwekpun4rjo0b3"}}, nil
|
||||
},
|
||||
}
|
||||
|
||||
cli := test.NewFakeCli(client)
|
||||
err := runPS(cli, psOptions{services: []string{"foo"}, quiet: true, filter: opts.NewFilterOpt()})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "sxabyp0obqokwekpun4rjo0b3\n", cli.OutBuffer().String())
|
||||
}
|
||||
|
||||
func TestUpdateNodeFilter(t *testing.T) {
|
||||
selfNodeID := "foofoo"
|
||||
filter := filters.NewArgs()
|
||||
|
||||
@@ -21,7 +21,7 @@ func newRollbackCommand(dockerCli command.Cli) *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runRollback(dockerCli, options, args[0])
|
||||
},
|
||||
Tags: map[string]string{"version": "1.31"},
|
||||
Annotations: map[string]string{"version": "1.31"},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/internal/test/testutil"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
mounttypes "github.com/docker/docker/api/types/mount"
|
||||
@@ -165,7 +166,7 @@ func TestUpdateDNSConfig(t *testing.T) {
|
||||
// IPv6
|
||||
flags.Set("dns-add", "2001:db8:abc8::1")
|
||||
// Invalid dns record
|
||||
assert.EqualError(t, flags.Set("dns-add", "x.y.z.w"), "x.y.z.w is not an ip address")
|
||||
testutil.ErrorContains(t, flags.Set("dns-add", "x.y.z.w"), "x.y.z.w is not an ip address")
|
||||
|
||||
// domains with duplicates
|
||||
flags.Set("dns-search-add", "example.com")
|
||||
@@ -173,7 +174,7 @@ func TestUpdateDNSConfig(t *testing.T) {
|
||||
flags.Set("dns-search-add", "example.org")
|
||||
flags.Set("dns-search-rm", "example.org")
|
||||
// Invalid dns search domain
|
||||
assert.EqualError(t, flags.Set("dns-search-add", "example$com"), "example$com is not a valid domain")
|
||||
testutil.ErrorContains(t, flags.Set("dns-search-add", "example$com"), "example$com is not a valid domain")
|
||||
|
||||
flags.Set("dns-option-add", "ndots:9")
|
||||
flags.Set("dns-option-rm", "timeout:3")
|
||||
@@ -362,7 +363,7 @@ func TestUpdateHosts(t *testing.T) {
|
||||
// just hostname should work as well
|
||||
flags.Set("host-rm", "example.net")
|
||||
// bad format error
|
||||
assert.EqualError(t, flags.Set("host-add", "$example.com$"), `bad format for add-host: "$example.com$"`)
|
||||
testutil.ErrorContains(t, flags.Set("host-add", "$example.com$"), `bad format for add-host: "$example.com$"`)
|
||||
|
||||
hosts := []string{"1.2.3.4 example.com", "4.3.2.1 example.org", "2001:db8:abc8::1 example.net"}
|
||||
|
||||
|
||||
@@ -7,14 +7,13 @@ import (
|
||||
)
|
||||
|
||||
// NewStackCommand returns a cobra command for `stack` subcommands
|
||||
// nolint: interfacer
|
||||
func NewStackCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewStackCommand(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "stack",
|
||||
Short: "Manage Docker stacks",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Tags: map[string]string{"version": "1.25"},
|
||||
Use: "stack",
|
||||
Short: "Manage Docker stacks",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Annotations: map[string]string{"version": "1.25"},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newDeployCommand(dockerCli),
|
||||
@@ -31,6 +30,6 @@ func NewTopLevelDeployCommand(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := newDeployCommand(dockerCli)
|
||||
// Remove the aliases at the top level
|
||||
cmd.Aliases = []string{}
|
||||
cmd.Tags = map[string]string{"experimental": "", "version": "1.25"}
|
||||
cmd.Annotations = map[string]string{"experimental": "", "version": "1.25"}
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -222,8 +222,9 @@ func createSecrets(
|
||||
if err := client.SecretUpdate(ctx, secret.ID, secret.Meta.Version, secretSpec); err != nil {
|
||||
return errors.Wrapf(err, "failed to update secret %s", secretSpec.Name)
|
||||
}
|
||||
case apiclient.IsErrSecretNotFound(err):
|
||||
case apiclient.IsErrNotFound(err):
|
||||
// secret does not exist, then we create a new one.
|
||||
fmt.Fprintf(dockerCli.Out(), "Creating secret %s\n", secretSpec.Name)
|
||||
if _, err := client.SecretCreate(ctx, secretSpec); err != nil {
|
||||
return errors.Wrapf(err, "failed to create secret %s", secretSpec.Name)
|
||||
}
|
||||
@@ -249,8 +250,9 @@ func createConfigs(
|
||||
if err := client.ConfigUpdate(ctx, config.ID, config.Meta.Version, configSpec); err != nil {
|
||||
errors.Wrapf(err, "failed to update config %s", configSpec.Name)
|
||||
}
|
||||
case apiclient.IsErrConfigNotFound(err):
|
||||
case apiclient.IsErrNotFound(err):
|
||||
// config does not exist, then we create a new one.
|
||||
fmt.Fprintf(dockerCli.Out(), "Creating config %s\n", configSpec.Name)
|
||||
if _, err := client.ConfigCreate(ctx, configSpec); err != nil {
|
||||
errors.Wrapf(err, "failed to create config %s", configSpec.Name)
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ func newCACommand(dockerCli command.Cli) *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runCA(dockerCli, cmd.Flags(), opts)
|
||||
},
|
||||
Tags: map[string]string{"version": "1.30"},
|
||||
Annotations: map[string]string{"version": "1.30"},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
@@ -8,14 +8,13 @@ import (
|
||||
)
|
||||
|
||||
// NewSwarmCommand returns a cobra command for `swarm` subcommands
|
||||
// nolint: interfacer
|
||||
func NewSwarmCommand(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "swarm",
|
||||
Short: "Manage Swarm",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Tags: map[string]string{"version": "1.24"},
|
||||
Use: "swarm",
|
||||
Short: "Manage Swarm",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Annotations: map[string]string{"version": "1.24"},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newInitCommand(dockerCli),
|
||||
|
||||
@@ -8,6 +8,7 @@ Flags:
|
||||
--cert-expiry duration Validity period for node certificates (ns|us|ms|s|m|h) (default 2160h0m0s)
|
||||
--dispatcher-heartbeat duration Dispatcher heartbeat period (ns|us|ms|s|m|h) (default 5s)
|
||||
--external-ca external-ca Specifications of one or more certificate signing endpoints
|
||||
-h, --help help for update
|
||||
--max-snapshots uint Number of additional Raft snapshots to retain
|
||||
--snapshot-interval uint Number of log entries between Raft snapshots (default 10000)
|
||||
--task-history-limit int Task history retention limit (default 5)
|
||||
|
||||
@@ -7,8 +7,7 @@ import (
|
||||
)
|
||||
|
||||
// NewSystemCommand returns a cobra command for `system` subcommands
|
||||
// nolint: interfacer
|
||||
func NewSystemCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewSystemCommand(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "system",
|
||||
Short: "Manage Docker",
|
||||
|
||||
@@ -16,7 +16,7 @@ type diskUsageOptions struct {
|
||||
}
|
||||
|
||||
// newDiskUsageCommand creates a new cobra.Command for `docker df`
|
||||
func newDiskUsageCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newDiskUsageCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts diskUsageOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -26,7 +26,7 @@ func newDiskUsageCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runDiskUsage(dockerCli, opts)
|
||||
},
|
||||
Tags: map[string]string{"version": "1.25"},
|
||||
Annotations: map[string]string{"version": "1.25"},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
@@ -37,7 +37,7 @@ func newDiskUsageCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runDiskUsage(dockerCli *command.DockerCli, opts diskUsageOptions) error {
|
||||
func runDiskUsage(dockerCli command.Cli, opts diskUsageOptions) error {
|
||||
if opts.verbose && len(opts.format) != 0 {
|
||||
return errors.New("the verbose and the format options conflict")
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ type eventsOptions struct {
|
||||
}
|
||||
|
||||
// NewEventsCommand creates a new cobra.Command for `docker events`
|
||||
func NewEventsCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewEventsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
options := eventsOptions{filter: opts.NewFilterOpt()}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -48,7 +48,7 @@ func NewEventsCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runEvents(dockerCli *command.DockerCli, options *eventsOptions) error {
|
||||
func runEvents(dockerCli command.Cli, options *eventsOptions) error {
|
||||
tmpl, err := makeTemplate(options.format)
|
||||
if err != nil {
|
||||
return cli.StatusError{
|
||||
|
||||
@@ -22,7 +22,7 @@ type infoOptions struct {
|
||||
}
|
||||
|
||||
// NewInfoCommand creates a new cobra.Command for `docker info`
|
||||
func NewInfoCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewInfoCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts infoOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -41,7 +41,7 @@ func NewInfoCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runInfo(dockerCli *command.DockerCli, opts *infoOptions) error {
|
||||
func runInfo(dockerCli command.Cli, opts *infoOptions) error {
|
||||
ctx := context.Background()
|
||||
info, err := dockerCli.Client().Info(ctx)
|
||||
if err != nil {
|
||||
@@ -357,7 +357,7 @@ func getBackingFs(info types.Info) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func formatInfo(dockerCli *command.DockerCli, info types.Info, format string) error {
|
||||
func formatInfo(dockerCli command.Cli, info types.Info, format string) error {
|
||||
tmpl, err := templates.Parse(format)
|
||||
if err != nil {
|
||||
return cli.StatusError{StatusCode: 64,
|
||||
|
||||
@@ -37,7 +37,7 @@ func newPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runPrune(dockerCli, options)
|
||||
},
|
||||
Tags: map[string]string{"version": "1.25"},
|
||||
Annotations: map[string]string{"version": "1.25"},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
@@ -9,11 +9,11 @@ import (
|
||||
// NewVolumeCommand returns a cobra command for `volume` subcommands
|
||||
func NewVolumeCommand(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "volume COMMAND",
|
||||
Short: "Manage volumes",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Tags: map[string]string{"version": "1.21"},
|
||||
Use: "volume COMMAND",
|
||||
Short: "Manage volumes",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Annotations: map[string]string{"version": "1.21"},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newCreateCommand(dockerCli),
|
||||
|
||||
@@ -35,7 +35,7 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
fmt.Fprintln(dockerCli.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed)))
|
||||
return nil
|
||||
},
|
||||
Tags: map[string]string{"version": "1.25"},
|
||||
Annotations: map[string]string{"version": "1.25"},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
@@ -52,12 +52,12 @@ func runPrune(dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint6
|
||||
pruneFilters := command.PruneFilters(dockerCli, options.filter.Value())
|
||||
|
||||
if !options.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) {
|
||||
return
|
||||
return 0, "", nil
|
||||
}
|
||||
|
||||
report, err := dockerCli.Client().VolumesPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
if len(report.VolumesDeleted) > 0 {
|
||||
@@ -68,7 +68,7 @@ func runPrune(dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint6
|
||||
spaceReclaimed = report.SpaceReclaimed
|
||||
}
|
||||
|
||||
return
|
||||
return spaceReclaimed, output, nil
|
||||
}
|
||||
|
||||
// RunPrune calls the Volume Prune API
|
||||
|
||||
@@ -1,75 +1,71 @@
|
||||
package interpolation
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli/compose/template"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Interpolate replaces variables in a string with the values from a mapping
|
||||
func Interpolate(config map[string]interface{}, section string, mapping template.Mapping) (map[string]interface{}, error) {
|
||||
out := map[string]interface{}{}
|
||||
|
||||
for name, item := range config {
|
||||
if item == nil {
|
||||
out[name] = nil
|
||||
continue
|
||||
}
|
||||
mapItem, ok := item.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, errors.Errorf("Invalid type for %s : %T instead of %T", name, item, out)
|
||||
}
|
||||
interpolatedItem, err := interpolateSectionItem(name, mapItem, section, mapping)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out[name] = interpolatedItem
|
||||
}
|
||||
|
||||
return out, nil
|
||||
// Options supported by Interpolate
|
||||
type Options struct {
|
||||
// LookupValue from a key
|
||||
LookupValue LookupValue
|
||||
// TypeCastMapping maps key paths to functions to cast to a type
|
||||
TypeCastMapping map[Path]Cast
|
||||
}
|
||||
|
||||
func interpolateSectionItem(
|
||||
name string,
|
||||
item map[string]interface{},
|
||||
section string,
|
||||
mapping template.Mapping,
|
||||
) (map[string]interface{}, error) {
|
||||
// LookupValue is a function which maps from variable names to values.
|
||||
// Returns the value as a string and a bool indicating whether
|
||||
// the value is present, to distinguish between an empty string
|
||||
// and the absence of a value.
|
||||
type LookupValue func(key string) (string, bool)
|
||||
|
||||
// Cast a value to a new type, or return an error if the value can't be cast
|
||||
type Cast func(value string) (interface{}, error)
|
||||
|
||||
// Interpolate replaces variables in a string with the values from a mapping
|
||||
func Interpolate(config map[string]interface{}, opts Options) (map[string]interface{}, error) {
|
||||
if opts.LookupValue == nil {
|
||||
opts.LookupValue = os.LookupEnv
|
||||
}
|
||||
if opts.TypeCastMapping == nil {
|
||||
opts.TypeCastMapping = make(map[Path]Cast)
|
||||
}
|
||||
|
||||
out := map[string]interface{}{}
|
||||
|
||||
for key, value := range item {
|
||||
interpolatedValue, err := recursiveInterpolate(value, mapping)
|
||||
switch err := err.(type) {
|
||||
case nil:
|
||||
case *template.InvalidTemplateError:
|
||||
return nil, errors.Errorf(
|
||||
"Invalid interpolation format for %#v option in %s %#v: %#v. You may need to escape any $ with another $.",
|
||||
key, section, name, err.Template,
|
||||
)
|
||||
default:
|
||||
return nil, errors.Wrapf(err, "error while interpolating %s in %s %s", key, section, name)
|
||||
for key, value := range config {
|
||||
interpolatedValue, err := recursiveInterpolate(value, NewPath(key), opts)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
out[key] = interpolatedValue
|
||||
}
|
||||
|
||||
return out, nil
|
||||
|
||||
}
|
||||
|
||||
func recursiveInterpolate(
|
||||
value interface{},
|
||||
mapping template.Mapping,
|
||||
) (interface{}, error) {
|
||||
|
||||
func recursiveInterpolate(value interface{}, path Path, opts Options) (interface{}, error) {
|
||||
switch value := value.(type) {
|
||||
|
||||
case string:
|
||||
return template.Substitute(value, mapping)
|
||||
newValue, err := template.Substitute(value, template.Mapping(opts.LookupValue))
|
||||
if err != nil || newValue == value {
|
||||
return value, newPathError(path, err)
|
||||
}
|
||||
caster, ok := opts.getCasterForPath(path)
|
||||
if !ok {
|
||||
return newValue, nil
|
||||
}
|
||||
casted, err := caster(newValue)
|
||||
return casted, newPathError(path, errors.Wrap(err, "failed to cast to expected type"))
|
||||
|
||||
case map[string]interface{}:
|
||||
out := map[string]interface{}{}
|
||||
for key, elem := range value {
|
||||
interpolatedElem, err := recursiveInterpolate(elem, mapping)
|
||||
interpolatedElem, err := recursiveInterpolate(elem, path.Next(key), opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -80,7 +76,7 @@ func recursiveInterpolate(
|
||||
case []interface{}:
|
||||
out := make([]interface{}, len(value))
|
||||
for i, elem := range value {
|
||||
interpolatedElem, err := recursiveInterpolate(elem, mapping)
|
||||
interpolatedElem, err := recursiveInterpolate(elem, path.Next(PathMatchList), opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -92,5 +88,71 @@ func recursiveInterpolate(
|
||||
return value, nil
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func newPathError(path Path, err error) error {
|
||||
switch err := err.(type) {
|
||||
case nil:
|
||||
return nil
|
||||
case *template.InvalidTemplateError:
|
||||
return errors.Errorf(
|
||||
"invalid interpolation format for %s: %#v. You may need to escape any $ with another $.",
|
||||
path, err.Template)
|
||||
default:
|
||||
return errors.Wrapf(err, "error while interpolating %s", path)
|
||||
}
|
||||
}
|
||||
|
||||
const pathSeparator = "."
|
||||
|
||||
// PathMatchAll is a token used as part of a Path to match any key at that level
|
||||
// in the nested structure
|
||||
const PathMatchAll = "*"
|
||||
|
||||
// PathMatchList is a token used as part of a Path to match items in a list
|
||||
const PathMatchList = "[]"
|
||||
|
||||
// Path is a dotted path of keys to a value in a nested mapping structure. A *
|
||||
// section in a path will match any key in the mapping structure.
|
||||
type Path string
|
||||
|
||||
// NewPath returns a new Path
|
||||
func NewPath(items ...string) Path {
|
||||
return Path(strings.Join(items, pathSeparator))
|
||||
}
|
||||
|
||||
// Next returns a new path by append part to the current path
|
||||
func (p Path) Next(part string) Path {
|
||||
return Path(string(p) + pathSeparator + part)
|
||||
}
|
||||
|
||||
func (p Path) parts() []string {
|
||||
return strings.Split(string(p), pathSeparator)
|
||||
}
|
||||
|
||||
func (p Path) matches(pattern Path) bool {
|
||||
patternParts := pattern.parts()
|
||||
parts := p.parts()
|
||||
|
||||
if len(patternParts) != len(parts) {
|
||||
return false
|
||||
}
|
||||
for index, part := range parts {
|
||||
switch patternParts[index] {
|
||||
case PathMatchAll, part:
|
||||
continue
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (o Options) getCasterForPath(path Path) (Cast, bool) {
|
||||
for pattern, caster := range o.TypeCastMapping {
|
||||
if path.matches(pattern) {
|
||||
return caster, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
@@ -3,12 +3,16 @@ package interpolation
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"strconv"
|
||||
|
||||
"github.com/gotestyourself/gotestyourself/env"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var defaults = map[string]string{
|
||||
"USER": "jenny",
|
||||
"FOO": "bar",
|
||||
"USER": "jenny",
|
||||
"FOO": "bar",
|
||||
"count": "5",
|
||||
}
|
||||
|
||||
func defaultMapping(name string) (string, bool) {
|
||||
@@ -41,7 +45,7 @@ func TestInterpolate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
result, err := Interpolate(services, "service", defaultMapping)
|
||||
result, err := Interpolate(services, Options{LookupValue: defaultMapping})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, result)
|
||||
}
|
||||
@@ -52,6 +56,91 @@ func TestInvalidInterpolation(t *testing.T) {
|
||||
"image": "${",
|
||||
},
|
||||
}
|
||||
_, err := Interpolate(services, "service", defaultMapping)
|
||||
assert.EqualError(t, err, `Invalid interpolation format for "image" option in service "servicea": "${". You may need to escape any $ with another $.`)
|
||||
_, err := Interpolate(services, Options{LookupValue: defaultMapping})
|
||||
assert.EqualError(t, err, `invalid interpolation format for servicea.image: "${". You may need to escape any $ with another $.`)
|
||||
}
|
||||
|
||||
func TestInterpolateWithDefaults(t *testing.T) {
|
||||
defer env.Patch(t, "FOO", "BARZ")()
|
||||
|
||||
config := map[string]interface{}{
|
||||
"networks": map[string]interface{}{
|
||||
"foo": "thing_${FOO}",
|
||||
},
|
||||
}
|
||||
expected := map[string]interface{}{
|
||||
"networks": map[string]interface{}{
|
||||
"foo": "thing_BARZ",
|
||||
},
|
||||
}
|
||||
result, err := Interpolate(config, Options{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, result)
|
||||
}
|
||||
|
||||
func TestInterpolateWithCast(t *testing.T) {
|
||||
config := map[string]interface{}{
|
||||
"foo": map[string]interface{}{
|
||||
"replicas": "$count",
|
||||
},
|
||||
}
|
||||
toInt := func(value string) (interface{}, error) {
|
||||
return strconv.Atoi(value)
|
||||
}
|
||||
result, err := Interpolate(config, Options{
|
||||
LookupValue: defaultMapping,
|
||||
TypeCastMapping: map[Path]Cast{NewPath(PathMatchAll, "replicas"): toInt},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
expected := map[string]interface{}{
|
||||
"foo": map[string]interface{}{
|
||||
"replicas": 5,
|
||||
},
|
||||
}
|
||||
assert.Equal(t, expected, result)
|
||||
}
|
||||
|
||||
func TestPathMatches(t *testing.T) {
|
||||
var testcases = []struct {
|
||||
doc string
|
||||
path Path
|
||||
pattern Path
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
doc: "pattern too short",
|
||||
path: NewPath("one", "two", "three"),
|
||||
pattern: NewPath("one", "two"),
|
||||
},
|
||||
{
|
||||
doc: "pattern too long",
|
||||
path: NewPath("one", "two"),
|
||||
pattern: NewPath("one", "two", "three"),
|
||||
},
|
||||
{
|
||||
doc: "pattern mismatch",
|
||||
path: NewPath("one", "three", "two"),
|
||||
pattern: NewPath("one", "two", "three"),
|
||||
},
|
||||
{
|
||||
doc: "pattern mismatch with match-all part",
|
||||
path: NewPath("one", "three", "two"),
|
||||
pattern: NewPath(PathMatchAll, "two", "three"),
|
||||
},
|
||||
{
|
||||
doc: "pattern match with match-all part",
|
||||
path: NewPath("one", "two", "three"),
|
||||
pattern: NewPath("one", "*", "three"),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
doc: "pattern match",
|
||||
path: NewPath("one", "two", "three"),
|
||||
pattern: NewPath("one", "two", "three"),
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
for _, testcase := range testcases {
|
||||
assert.Equal(t, testcase.expected, testcase.path.matches(testcase.pattern))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package loader
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
interp "github.com/docker/cli/cli/compose/interpolation"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var interpolateTypeCastMapping = map[interp.Path]interp.Cast{
|
||||
servicePath("configs", interp.PathMatchList, "mode"): toInt,
|
||||
servicePath("secrets", interp.PathMatchList, "mode"): toInt,
|
||||
servicePath("healthcheck", "retries"): toInt,
|
||||
servicePath("healthcheck", "disable"): toBoolean,
|
||||
servicePath("deploy", "replicas"): toInt,
|
||||
servicePath("deploy", "update_config", "parallelism"): toInt,
|
||||
servicePath("deploy", "update_config", "max_failure_ratio"): toFloat,
|
||||
servicePath("deploy", "restart_policy", "max_attempts"): toInt,
|
||||
servicePath("ports", interp.PathMatchList, "target"): toInt,
|
||||
servicePath("ports", interp.PathMatchList, "published"): toInt,
|
||||
servicePath("ulimits", interp.PathMatchAll): toInt,
|
||||
servicePath("ulimits", interp.PathMatchAll, "hard"): toInt,
|
||||
servicePath("ulimits", interp.PathMatchAll, "soft"): toInt,
|
||||
servicePath("privileged"): toBoolean,
|
||||
servicePath("read_only"): toBoolean,
|
||||
servicePath("stdin_open"): toBoolean,
|
||||
servicePath("tty"): toBoolean,
|
||||
servicePath("volumes", interp.PathMatchList, "read_only"): toBoolean,
|
||||
servicePath("volumes", interp.PathMatchList, "volume", "nocopy"): toBoolean,
|
||||
iPath("networks", interp.PathMatchAll, "external"): toBoolean,
|
||||
iPath("networks", interp.PathMatchAll, "internal"): toBoolean,
|
||||
iPath("networks", interp.PathMatchAll, "attachable"): toBoolean,
|
||||
iPath("volumes", interp.PathMatchAll, "external"): toBoolean,
|
||||
iPath("secrets", interp.PathMatchAll, "external"): toBoolean,
|
||||
iPath("configs", interp.PathMatchAll, "external"): toBoolean,
|
||||
}
|
||||
|
||||
func iPath(parts ...string) interp.Path {
|
||||
return interp.NewPath(parts...)
|
||||
}
|
||||
|
||||
func servicePath(parts ...string) interp.Path {
|
||||
return iPath(append([]string{"services", interp.PathMatchAll}, parts...)...)
|
||||
}
|
||||
|
||||
func toInt(value string) (interface{}, error) {
|
||||
return strconv.Atoi(value)
|
||||
}
|
||||
|
||||
func toFloat(value string) (interface{}, error) {
|
||||
return strconv.ParseFloat(value, 64)
|
||||
}
|
||||
|
||||
// should match http://yaml.org/type/bool.html
|
||||
func toBoolean(value string) (interface{}, error) {
|
||||
switch strings.ToLower(value) {
|
||||
case "y", "yes", "true", "on":
|
||||
return true, nil
|
||||
case "n", "no", "false", "off":
|
||||
return false, nil
|
||||
default:
|
||||
return nil, errors.Errorf("invalid boolean: %s", value)
|
||||
}
|
||||
}
|
||||
|
||||
func interpolateConfig(configDict map[string]interface{}, lookupEnv interp.LookupValue) (map[string]interface{}, error) {
|
||||
return interp.Interpolate(
|
||||
configDict,
|
||||
interp.Options{
|
||||
LookupValue: lookupEnv,
|
||||
TypeCastMapping: interpolateTypeCastMapping,
|
||||
})
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli/compose/interpolation"
|
||||
"github.com/docker/cli/cli/compose/schema"
|
||||
"github.com/docker/cli/cli/compose/template"
|
||||
"github.com/docker/cli/cli/compose/types"
|
||||
@@ -51,67 +50,92 @@ func Load(configDetails types.ConfigDetails) (*types.Config, error) {
|
||||
|
||||
configDict := getConfigDict(configDetails)
|
||||
|
||||
if services, ok := configDict["services"]; ok {
|
||||
if servicesDict, ok := services.(map[string]interface{}); ok {
|
||||
forbidden := getProperties(servicesDict, types.ForbiddenProperties)
|
||||
if err := validateForbidden(configDict); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(forbidden) > 0 {
|
||||
return nil, &ForbiddenPropertiesError{Properties: forbidden}
|
||||
}
|
||||
}
|
||||
var err error
|
||||
configDict, err = interpolateConfig(configDict, configDetails.LookupEnv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := schema.Validate(configDict, schema.Version(configDict)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg := types.Config{}
|
||||
|
||||
config, err := interpolateConfig(configDict, configDetails.LookupEnv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg.Services, err = LoadServices(config["services"], configDetails.WorkingDir, configDetails.LookupEnv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg.Networks, err = LoadNetworks(config["networks"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg.Volumes, err = LoadVolumes(config["volumes"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg.Secrets, err = LoadSecrets(config["secrets"], configDetails.WorkingDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg.Configs, err = LoadConfigObjs(config["configs"], configDetails.WorkingDir)
|
||||
return &cfg, err
|
||||
return loadSections(configDict, configDetails)
|
||||
}
|
||||
|
||||
func interpolateConfig(configDict map[string]interface{}, lookupEnv template.Mapping) (map[string]map[string]interface{}, error) {
|
||||
config := make(map[string]map[string]interface{})
|
||||
func validateForbidden(configDict map[string]interface{}) error {
|
||||
servicesDict, ok := configDict["services"].(map[string]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
forbidden := getProperties(servicesDict, types.ForbiddenProperties)
|
||||
if len(forbidden) > 0 {
|
||||
return &ForbiddenPropertiesError{Properties: forbidden}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, key := range []string{"services", "networks", "volumes", "secrets", "configs"} {
|
||||
section, ok := configDict[key]
|
||||
if !ok {
|
||||
config[key] = make(map[string]interface{})
|
||||
continue
|
||||
}
|
||||
var err error
|
||||
config[key], err = interpolation.Interpolate(section.(map[string]interface{}), key, lookupEnv)
|
||||
if err != nil {
|
||||
func loadSections(config map[string]interface{}, configDetails types.ConfigDetails) (*types.Config, error) {
|
||||
var err error
|
||||
cfg := types.Config{}
|
||||
|
||||
var loaders = []struct {
|
||||
key string
|
||||
fnc func(config map[string]interface{}) error
|
||||
}{
|
||||
{
|
||||
key: "services",
|
||||
fnc: func(config map[string]interface{}) error {
|
||||
cfg.Services, err = LoadServices(config, configDetails.WorkingDir, configDetails.LookupEnv)
|
||||
return err
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "networks",
|
||||
fnc: func(config map[string]interface{}) error {
|
||||
cfg.Networks, err = LoadNetworks(config)
|
||||
return err
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "volumes",
|
||||
fnc: func(config map[string]interface{}) error {
|
||||
cfg.Volumes, err = LoadVolumes(config)
|
||||
return err
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "secrets",
|
||||
fnc: func(config map[string]interface{}) error {
|
||||
cfg.Secrets, err = LoadSecrets(config, configDetails.WorkingDir)
|
||||
return err
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "configs",
|
||||
fnc: func(config map[string]interface{}) error {
|
||||
cfg.Configs, err = LoadConfigObjs(config, configDetails.WorkingDir)
|
||||
return err
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, loader := range loaders {
|
||||
if err := loader.fnc(getSection(config, loader.key)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return config, nil
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
func getSection(config map[string]interface{}, key string) map[string]interface{} {
|
||||
section, ok := config[key]
|
||||
if !ok {
|
||||
return make(map[string]interface{})
|
||||
}
|
||||
return section.(map[string]interface{})
|
||||
}
|
||||
|
||||
// GetUnsupportedProperties returns the list of any unsupported properties that are
|
||||
|
||||
@@ -465,7 +465,7 @@ services:
|
||||
assert.Contains(t, err.Error(), "services.dict-env.environment must be a mapping")
|
||||
}
|
||||
|
||||
func TestEnvironmentInterpolation(t *testing.T) {
|
||||
func TestLoadWithEnvironmentInterpolation(t *testing.T) {
|
||||
home := "/home/foo"
|
||||
config, err := loadYAMLWithEnv(`
|
||||
version: "3"
|
||||
@@ -502,19 +502,161 @@ volumes:
|
||||
assert.Equal(t, home, config.Volumes["test"].Driver)
|
||||
}
|
||||
|
||||
func TestLoadWithInterpolationCastFull(t *testing.T) {
|
||||
dict, err := ParseYAML([]byte(`
|
||||
version: "3.4"
|
||||
services:
|
||||
web:
|
||||
configs:
|
||||
- source: appconfig
|
||||
mode: $theint
|
||||
secrets:
|
||||
- source: super
|
||||
mode: $theint
|
||||
healthcheck:
|
||||
retries: ${theint}
|
||||
disable: $thebool
|
||||
deploy:
|
||||
replicas: $theint
|
||||
update_config:
|
||||
parallelism: $theint
|
||||
max_failure_ratio: $thefloat
|
||||
restart_policy:
|
||||
max_attempts: $theint
|
||||
ports:
|
||||
- $theint
|
||||
- "34567"
|
||||
- target: $theint
|
||||
published: $theint
|
||||
ulimits:
|
||||
nproc: $theint
|
||||
nofile:
|
||||
hard: $theint
|
||||
soft: $theint
|
||||
privileged: $thebool
|
||||
read_only: $thebool
|
||||
stdin_open: ${thebool}
|
||||
tty: $thebool
|
||||
volumes:
|
||||
- source: data
|
||||
type: volume
|
||||
read_only: $thebool
|
||||
volume:
|
||||
nocopy: $thebool
|
||||
|
||||
configs:
|
||||
appconfig:
|
||||
external: $thebool
|
||||
secrets:
|
||||
super:
|
||||
external: $thebool
|
||||
volumes:
|
||||
data:
|
||||
external: $thebool
|
||||
networks:
|
||||
front:
|
||||
external: $thebool
|
||||
internal: $thebool
|
||||
attachable: $thebool
|
||||
|
||||
`))
|
||||
require.NoError(t, err)
|
||||
env := map[string]string{
|
||||
"theint": "555",
|
||||
"thefloat": "3.14",
|
||||
"thebool": "true",
|
||||
}
|
||||
|
||||
config, err := Load(buildConfigDetails(dict, env))
|
||||
require.NoError(t, err)
|
||||
expected := &types.Config{
|
||||
Services: []types.ServiceConfig{
|
||||
{
|
||||
Name: "web",
|
||||
Configs: []types.ServiceConfigObjConfig{
|
||||
{
|
||||
Source: "appconfig",
|
||||
Mode: uint32Ptr(555),
|
||||
},
|
||||
},
|
||||
Secrets: []types.ServiceSecretConfig{
|
||||
{
|
||||
Source: "super",
|
||||
Mode: uint32Ptr(555),
|
||||
},
|
||||
},
|
||||
HealthCheck: &types.HealthCheckConfig{
|
||||
Retries: uint64Ptr(555),
|
||||
Disable: true,
|
||||
},
|
||||
Deploy: types.DeployConfig{
|
||||
Replicas: uint64Ptr(555),
|
||||
UpdateConfig: &types.UpdateConfig{
|
||||
Parallelism: uint64Ptr(555),
|
||||
MaxFailureRatio: 3.14,
|
||||
},
|
||||
RestartPolicy: &types.RestartPolicy{
|
||||
MaxAttempts: uint64Ptr(555),
|
||||
},
|
||||
},
|
||||
Ports: []types.ServicePortConfig{
|
||||
{Target: 555, Mode: "ingress", Protocol: "tcp"},
|
||||
{Target: 34567, Mode: "ingress", Protocol: "tcp"},
|
||||
{Target: 555, Published: 555},
|
||||
},
|
||||
Ulimits: map[string]*types.UlimitsConfig{
|
||||
"nproc": {Single: 555},
|
||||
"nofile": {Hard: 555, Soft: 555},
|
||||
},
|
||||
Privileged: true,
|
||||
ReadOnly: true,
|
||||
StdinOpen: true,
|
||||
Tty: true,
|
||||
Volumes: []types.ServiceVolumeConfig{
|
||||
{
|
||||
Source: "data",
|
||||
Type: "volume",
|
||||
ReadOnly: true,
|
||||
Volume: &types.ServiceVolumeVolume{NoCopy: true},
|
||||
},
|
||||
},
|
||||
Environment: types.MappingWithEquals{},
|
||||
},
|
||||
},
|
||||
Configs: map[string]types.ConfigObjConfig{
|
||||
"appconfig": {External: types.External{External: true, Name: "appconfig"}},
|
||||
},
|
||||
Secrets: map[string]types.SecretConfig{
|
||||
"super": {External: types.External{External: true, Name: "super"}},
|
||||
},
|
||||
Volumes: map[string]types.VolumeConfig{
|
||||
"data": {External: types.External{External: true, Name: "data"}},
|
||||
},
|
||||
Networks: map[string]types.NetworkConfig{
|
||||
"front": {
|
||||
External: types.External{External: true, Name: "front"},
|
||||
Internal: true,
|
||||
Attachable: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.Equal(t, expected, config)
|
||||
}
|
||||
|
||||
func TestUnsupportedProperties(t *testing.T) {
|
||||
dict, err := ParseYAML([]byte(`
|
||||
version: "3"
|
||||
services:
|
||||
web:
|
||||
image: web
|
||||
build:
|
||||
build:
|
||||
context: ./web
|
||||
links:
|
||||
- bar
|
||||
db:
|
||||
image: db
|
||||
build:
|
||||
build:
|
||||
context: ./db
|
||||
`))
|
||||
require.NoError(t, err)
|
||||
@@ -679,6 +821,10 @@ func uint64Ptr(value uint64) *uint64 {
|
||||
return &value
|
||||
}
|
||||
|
||||
func uint32Ptr(value uint32) *uint32 {
|
||||
return &value
|
||||
}
|
||||
|
||||
func TestFullExample(t *testing.T) {
|
||||
bytes, err := ioutil.ReadFile("full-example.yml")
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -112,6 +112,11 @@ func isFilePath(source string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// windows named pipes
|
||||
if strings.HasPrefix(source, `\\`) {
|
||||
return true
|
||||
}
|
||||
|
||||
first, nextIndex := utf8.DecodeRuneInString(source)
|
||||
return isWindowsDrive([]rune{first}, rune(source[nextIndex]))
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/docker/cli/cli/compose/types"
|
||||
"github.com/docker/cli/internal/test/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParseVolumeAnonymousVolume(t *testing.T) {
|
||||
@@ -149,6 +150,17 @@ func TestParseVolumeWithRW(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseVolumeWindowsNamedPipe(t *testing.T) {
|
||||
volume, err := ParseVolume(`\\.\pipe\docker_engine:\\.\pipe\inside`)
|
||||
require.NoError(t, err)
|
||||
expected := types.ServiceVolumeConfig{
|
||||
Type: "bind",
|
||||
Source: `\\.\pipe\docker_engine`,
|
||||
Target: `\\.\pipe\inside`,
|
||||
}
|
||||
assert.Equal(t, expected, volume)
|
||||
}
|
||||
|
||||
func TestIsFilePath(t *testing.T) {
|
||||
assert.False(t, isFilePath("a界"))
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ func setFlagErrorFunc(dockerCli *command.DockerCli, cmd *cobra.Command, flags *p
|
||||
}
|
||||
|
||||
func setHelpFunc(dockerCli *command.DockerCli, cmd *cobra.Command, flags *pflag.FlagSet, opts *cliflags.ClientOptions) {
|
||||
defaultHelpFunc := cmd.HelpFunc()
|
||||
cmd.SetHelpFunc(func(ccmd *cobra.Command, args []string) {
|
||||
initializeDockerCli(dockerCli, flags, opts)
|
||||
if err := isSupported(ccmd, dockerCli); err != nil {
|
||||
@@ -96,10 +97,7 @@ func setHelpFunc(dockerCli *command.DockerCli, cmd *cobra.Command, flags *pflag.
|
||||
}
|
||||
|
||||
hideUnsupportedFeatures(ccmd, dockerCli)
|
||||
|
||||
if err := ccmd.Help(); err != nil {
|
||||
ccmd.Println(err)
|
||||
}
|
||||
defaultHelpFunc(ccmd, args)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -225,13 +223,13 @@ func hideUnsupportedFeatures(cmd *cobra.Command, details versionDetails) {
|
||||
for _, subcmd := range cmd.Commands() {
|
||||
// hide experimental subcommands
|
||||
if !hasExperimental {
|
||||
if _, ok := subcmd.Tags["experimental"]; ok {
|
||||
if _, ok := subcmd.Annotations["experimental"]; ok {
|
||||
subcmd.Hidden = true
|
||||
}
|
||||
}
|
||||
|
||||
// hide subcommands not supported by the server
|
||||
if subcmdVersion, ok := subcmd.Tags["version"]; ok && versions.LessThan(clientVersion, subcmdVersion) {
|
||||
if subcmdVersion, ok := subcmd.Annotations["version"]; ok && versions.LessThan(clientVersion, subcmdVersion) {
|
||||
subcmd.Hidden = true
|
||||
}
|
||||
}
|
||||
@@ -244,10 +242,10 @@ func isSupported(cmd *cobra.Command, details versionDetails) error {
|
||||
|
||||
// Check recursively so that, e.g., `docker stack ls` returns the same output as `docker stack`
|
||||
for curr := cmd; curr != nil; curr = curr.Parent() {
|
||||
if cmdVersion, ok := curr.Tags["version"]; ok && versions.LessThan(clientVersion, cmdVersion) {
|
||||
if cmdVersion, ok := curr.Annotations["version"]; ok && versions.LessThan(clientVersion, cmdVersion) {
|
||||
return fmt.Errorf("%s requires API version %s, but the Docker daemon API version is %s", cmd.CommandPath(), cmdVersion, clientVersion)
|
||||
}
|
||||
if _, ok := curr.Tags["experimental"]; ok && !hasExperimental {
|
||||
if _, ok := curr.Annotations["experimental"]; ok && !hasExperimental {
|
||||
return fmt.Errorf("%s is only supported on a Docker daemon with experimental features enabled", cmd.CommandPath())
|
||||
}
|
||||
}
|
||||
@@ -300,7 +298,7 @@ func isOSTypeSupported(f *pflag.Flag, osType string) bool {
|
||||
// hasTags return true if any of the command's parents has tags
|
||||
func hasTags(cmd *cobra.Command) bool {
|
||||
for curr := cmd; curr != nil; curr = curr.Parent() {
|
||||
if len(curr.Tags) > 0 {
|
||||
if len(curr.Annotations) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2201,6 +2201,7 @@ _docker_daemon() {
|
||||
--max-concurrent-uploads
|
||||
--metrics-addr
|
||||
--mtu
|
||||
--network-control-plane-mtu
|
||||
--oom-score-adjust
|
||||
--pidfile -p
|
||||
--registry-mirror
|
||||
@@ -3224,7 +3225,7 @@ _docker_service_rm() {
|
||||
_docker_service_rollback() {
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "--detach=false -d --help --quit -q" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "--detach -d --help --quit -q" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
local counter=$( __docker_pos_first_nonflag )
|
||||
@@ -3238,7 +3239,7 @@ _docker_service_rollback() {
|
||||
_docker_service_scale() {
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "--detach=false -d=false --help" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "--detach -d --help" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
__docker_complete_services
|
||||
@@ -3339,7 +3340,7 @@ _docker_service_update_and_create() {
|
||||
"
|
||||
|
||||
local boolean_options="
|
||||
--detach=false -d=false
|
||||
--detach -d
|
||||
--help
|
||||
--no-healthcheck
|
||||
--read-only
|
||||
@@ -4188,17 +4189,17 @@ _docker_secret() {
|
||||
|
||||
_docker_secret_create() {
|
||||
case "$prev" in
|
||||
--label|-l)
|
||||
--driver|-d|--label|-l)
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "--help --label -l" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "--driver -d --help --label -l" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag '--label|-l')
|
||||
local counter=$(__docker_pos_first_nonflag '--driver|-d|--label|-l')
|
||||
if [ "$cword" -eq "$((counter + 1))" ]; then
|
||||
_filedir
|
||||
fi
|
||||
@@ -4345,16 +4346,20 @@ _docker_stack_deploy() {
|
||||
_filedir yml
|
||||
return
|
||||
;;
|
||||
--resolve-image)
|
||||
COMPREPLY=( $( compgen -W "always changed never" -- "$cur" ) )
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
local options="--compose-file -c --help --prune --with-registry-auth"
|
||||
local options="--compose-file -c --help --prune --resolve-image --with-registry-auth"
|
||||
__docker_daemon_is_experimental && options+=" --bundle-file"
|
||||
COMPREPLY=( $( compgen -W "$options" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag '--compose-file|-c|--bundle-file')
|
||||
local counter=$(__docker_pos_first_nonflag '--bundle-file|--compose-file|-c|--resolve-image')
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_stacks
|
||||
fi
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
See https://github.com/samneirinck/posh-docker
|
||||
See https://github.com/matt9ucci/DockerCompletion
|
||||
or https://github.com/samneirinck/posh-docker
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
FROM dockercore/golang-cross@sha256:d24e7affa3a85d460d2303c2549f03fc866f2b97d771ccf07b0e6e2b411dd207
|
||||
FROM dockercore/golang-cross@sha256:8a347b3692ba925dcef753f2de289e11774410c1bc752b9d525cb05477a7697b
|
||||
ENV DISABLE_WARN_OUTSIDE_CONTAINER=1
|
||||
WORKDIR /go/src/github.com/docker/cli
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
FROM golang:1.8.3-alpine
|
||||
FROM golang:1.8.4-alpine3.6
|
||||
|
||||
RUN apk add -U git make bash coreutils
|
||||
RUN apk add -U git make bash coreutils ca-certificates
|
||||
|
||||
ARG VNDR_SHA=a6e196d8b4b0cbbdc29aebdb20c59ac6926bb384
|
||||
RUN go get -d github.com/LK4D4/vndr && \
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
FROM golang:1.8.3-alpine
|
||||
FROM golang:1.8.4-alpine3.6
|
||||
|
||||
RUN apk add -U git
|
||||
|
||||
ARG GOMETALINTER_SHA=8eca55135021737bbc65ed68b548b3336853274c
|
||||
ARG GOMETALINTER_SHA=7f9672e7ea538b8682e83395d50b12f09bb17b91
|
||||
RUN go get -d github.com/alecthomas/gometalinter && \
|
||||
cd /go/src/github.com/alecthomas/gometalinter && \
|
||||
git checkout -q "$GOMETALINTER_SHA" && \
|
||||
@@ -10,6 +10,13 @@ RUN go get -d github.com/alecthomas/gometalinter && \
|
||||
gometalinter --install && \
|
||||
rm -rf /go/src/* /go/pkg/*
|
||||
|
||||
ARG NAKEDRET_SHA=3ddb495a6d63bc9041ba843e7d651cf92639d8cb
|
||||
RUN go get -d github.com/alexkohler/nakedret && \
|
||||
cd /go/src/github.com/alexkohler/nakedret && \
|
||||
git checkout -q "$NAKEDRET_SHA" && \
|
||||
go build -v -o /usr/local/bin/nakedret . && \
|
||||
rm -rf /go/src/* /go/pkg/*
|
||||
|
||||
WORKDIR /go/src/github.com/docker/cli
|
||||
ENV CGO_ENABLED=0
|
||||
ENV DISABLE_WARN_OUTSIDE_CONTAINER=1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# The non-reference docs have been moved!
|
||||
|
||||
<!-- This file is maintained within the docker/cli Github
|
||||
<!-- This file is maintained within the docker/cli GitHub
|
||||
repository at https://github.com/docker/cli/. Make all
|
||||
pull requests against that repo. If you see this file in
|
||||
another repository, consider it read-only there, as it will
|
||||
|
||||
@@ -5,7 +5,7 @@ description: "Deprecated Features."
|
||||
keywords: "docker, documentation, about, technology, deprecate"
|
||||
---
|
||||
|
||||
<!-- This file is maintained within the docker/cli Github
|
||||
<!-- This file is maintained within the docker/cli GitHub
|
||||
repository at https://github.com/docker/cli/. Make all
|
||||
pull requests against that repo. If you see this file in
|
||||
another repository, consider it read-only there, as it will
|
||||
@@ -24,13 +24,16 @@ see [Feature Deprecation Policy](https://docs.docker.com/engine/#feature-depreca
|
||||
|
||||
**Deprecated In Release: v17.05.0**
|
||||
|
||||
**Disabled by default in release: [v17.09](https://github.com/docker/docker-ce/releases/tag/v17.09.0-ce)**
|
||||
**Disabled by default in release: [v17.10](https://github.com/docker/docker-ce/releases/tag/v17.10.0-ce)**
|
||||
|
||||
Docker 17.05.0 added an optional `--detach=false` option to make the
|
||||
`docker service create` and `docker service update` work synchronously. This
|
||||
option will be enable by default in Docker 17.09, at which point the `--detach`
|
||||
option will be enabled by default in Docker 17.10, at which point the `--detach`
|
||||
flag can be used to use the previous (asynchronous) behavior.
|
||||
|
||||
The default for this option will also be changed accordingly for `docker service rollback`
|
||||
and `docker service scale` in Docker 17.10.
|
||||
|
||||
### `-g` and `--graph` flags on `dockerd`
|
||||
|
||||
**Deprecated In Release: v17.05.0**
|
||||
|
||||
@@ -4,7 +4,7 @@ keywords: "API, Usage, plugins, documentation, developer, amazon, ebs, rexray, v
|
||||
title: Volume plugin for Amazon EBS
|
||||
---
|
||||
|
||||
<!-- This file is maintained within the docker/cli Github
|
||||
<!-- This file is maintained within the docker/cli GitHub
|
||||
repository at https://github.com/docker/cli/. Make all
|
||||
pull requests against that repo. If you see this file in
|
||||
another repository, consider it read-only there, as it will
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user