Merge component 'cli' from git@github.com:docker/cli master
This commit is contained in:
8
components/cli/.github/CODEOWNERS
vendored
Normal file
8
components/cli/.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# Github code owners
|
||||
# See https://github.com/blog/2392-introducing-code-owners
|
||||
|
||||
cli/compose/* @dnephin @vdemeester
|
||||
contrib/completion/bash/* @albers
|
||||
contrib/completion/zsh/* @sdurrheimer
|
||||
docs/* @mstanleyjones @vdemeester @thaJeztah
|
||||
scripts/* @dnephin
|
||||
@ -7,7 +7,9 @@ jobs:
|
||||
docker: [{image: 'docker:17.05-git'}]
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker
|
||||
- setup_remote_docker:
|
||||
reusable: true
|
||||
exclusive: false
|
||||
- run:
|
||||
command: docker version
|
||||
- run:
|
||||
@ -15,23 +17,32 @@ jobs:
|
||||
command: |
|
||||
dockerfile=dockerfiles/Dockerfile.lint
|
||||
echo "COPY . ." >> $dockerfile
|
||||
docker build -f $dockerfile --tag cli-linter .
|
||||
docker run cli-linter
|
||||
docker build -f $dockerfile --tag cli-linter:$CIRCLE_BUILD_NUM .
|
||||
docker run --rm cli-linter:$CIRCLE_BUILD_NUM
|
||||
|
||||
cross:
|
||||
working_directory: /work
|
||||
docker: [{image: 'docker:17.05-git'}]
|
||||
parallelism: 3
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker
|
||||
- setup_remote_docker:
|
||||
reusable: true
|
||||
exclusive: false
|
||||
- run:
|
||||
name: "Cross"
|
||||
command: |
|
||||
dockerfile=dockerfiles/Dockerfile.cross
|
||||
echo "COPY . ." >> $dockerfile
|
||||
docker build -f $dockerfile --tag cli-builder .
|
||||
docker run --name cross cli-builder make cross
|
||||
docker cp cross:/go/src/github.com/docker/cli/build /work/build
|
||||
docker build -f $dockerfile --tag cli-builder:$CIRCLE_BUILD_NUM .
|
||||
name=cross-$CIRCLE_BUILD_NUM-$CIRCLE_NODE_INDEX
|
||||
docker run \
|
||||
-e CROSS_GROUP=$CIRCLE_NODE_INDEX \
|
||||
--name $name cli-builder:$CIRCLE_BUILD_NUM \
|
||||
make cross
|
||||
docker cp \
|
||||
$name:/go/src/github.com/docker/cli/build \
|
||||
/work/build
|
||||
- store_artifacts:
|
||||
path: /work/build
|
||||
|
||||
@ -40,19 +51,25 @@ jobs:
|
||||
docker: [{image: 'docker:17.05-git'}]
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker
|
||||
- setup_remote_docker:
|
||||
reusable: true
|
||||
exclusive: false
|
||||
- run:
|
||||
name: "Unit Test with Coverage"
|
||||
command: |
|
||||
dockerfile=dockerfiles/Dockerfile.dev
|
||||
echo "COPY . ." >> $dockerfile
|
||||
docker build -f $dockerfile --tag cli-builder .
|
||||
docker run --name test cli-builder make test-coverage
|
||||
docker build -f $dockerfile --tag cli-builder:$CIRCLE_BUILD_NUM .
|
||||
docker run --name \
|
||||
test-$CIRCLE_BUILD_NUM cli-builder:$CIRCLE_BUILD_NUM \
|
||||
make test-coverage
|
||||
|
||||
- run:
|
||||
name: "Upload to Codecov"
|
||||
command: |
|
||||
docker cp test:/go/src/github.com/docker/cli/coverage.txt coverage.txt
|
||||
docker cp \
|
||||
test-$CIRCLE_BUILD_NUM:/go/src/github.com/docker/cli/coverage.txt \
|
||||
coverage.txt
|
||||
apk add -U bash curl
|
||||
curl -s https://codecov.io/bash | bash
|
||||
|
||||
@ -60,16 +77,20 @@ jobs:
|
||||
working_directory: /work
|
||||
docker: [{image: 'docker:17.05-git'}]
|
||||
steps:
|
||||
- run: apk add -U git openssh
|
||||
- checkout
|
||||
- setup_remote_docker
|
||||
- setup_remote_docker:
|
||||
reusable: true
|
||||
exclusive: false
|
||||
- run:
|
||||
name: "Validate Vendor, Docs, and Code Generation"
|
||||
command: |
|
||||
dockerfile=dockerfiles/Dockerfile.dev
|
||||
echo "COPY . ." >> $dockerfile
|
||||
rm -f .dockerignore # include .git
|
||||
docker build -f $dockerfile --tag cli-builder .
|
||||
docker run cli-builder make -B vendor compose-jsonschema manpages yamldocs
|
||||
docker build -f $dockerfile --tag cli-builder-with-git:$CIRCLE_BUILD_NUM .
|
||||
docker run --rm cli-builder-with-git:$CIRCLE_BUILD_NUM \
|
||||
make -B vendor compose-jsonschema manpages yamldocs
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
|
||||
35
components/cli/cli/command/checkpoint/client_test.go
Normal file
35
components/cli/cli/command/checkpoint/client_test.go
Normal file
@ -0,0 +1,35 @@
|
||||
package checkpoint
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type fakeClient struct {
|
||||
client.Client
|
||||
checkpointCreateFunc func(container string, options types.CheckpointCreateOptions) error
|
||||
checkpointDeleteFunc func(container string, options types.CheckpointDeleteOptions) error
|
||||
checkpointListFunc func(container string, options types.CheckpointListOptions) ([]types.Checkpoint, error)
|
||||
}
|
||||
|
||||
func (cli *fakeClient) CheckpointCreate(ctx context.Context, container string, options types.CheckpointCreateOptions) error {
|
||||
if cli.checkpointCreateFunc != nil {
|
||||
return cli.checkpointCreateFunc(container, options)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) CheckpointDelete(ctx context.Context, container string, options types.CheckpointDeleteOptions) error {
|
||||
if cli.checkpointDeleteFunc != nil {
|
||||
return cli.checkpointDeleteFunc(container, options)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) CheckpointList(ctx context.Context, container string, options types.CheckpointListOptions) ([]types.Checkpoint, error) {
|
||||
if cli.checkpointListFunc != nil {
|
||||
return cli.checkpointListFunc(container, options)
|
||||
}
|
||||
return []types.Checkpoint{}, nil
|
||||
}
|
||||
72
components/cli/cli/command/checkpoint/create_test.go
Normal file
72
components/cli/cli/command/checkpoint/create_test.go
Normal file
@ -0,0 +1,72 @@
|
||||
package checkpoint
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/internal/test"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/pkg/testutil"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCheckpointCreateErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
args []string
|
||||
checkpointCreateFunc func(container string, options types.CheckpointCreateOptions) error
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
args: []string{"too-few-arguments"},
|
||||
expectedError: "requires exactly 2 argument(s)",
|
||||
},
|
||||
{
|
||||
args: []string{"too", "many", "arguments"},
|
||||
expectedError: "requires exactly 2 argument(s)",
|
||||
},
|
||||
{
|
||||
args: []string{"foo", "bar"},
|
||||
checkpointCreateFunc: func(container string, options types.CheckpointCreateOptions) error {
|
||||
return errors.Errorf("error creating checkpoint for container foo")
|
||||
},
|
||||
expectedError: "error creating checkpoint for container foo",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
checkpointCreateFunc: tc.checkpointCreateFunc,
|
||||
})
|
||||
cmd := newCreateCommand(cli)
|
||||
cmd.SetArgs(tc.args)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckpointCreateWithOptions(t *testing.T) {
|
||||
var containerID, checkpointID, checkpointDir string
|
||||
var exit bool
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
checkpointCreateFunc: func(container string, options types.CheckpointCreateOptions) error {
|
||||
containerID = container
|
||||
checkpointID = options.CheckpointID
|
||||
checkpointDir = options.CheckpointDir
|
||||
exit = options.Exit
|
||||
return nil
|
||||
},
|
||||
})
|
||||
cmd := newCreateCommand(cli)
|
||||
checkpoint := "checkpoint-bar"
|
||||
cmd.SetArgs([]string{"container-foo", checkpoint})
|
||||
cmd.Flags().Set("leave-running", "true")
|
||||
cmd.Flags().Set("checkpoint-dir", "/dir/foo")
|
||||
assert.NoError(t, cmd.Execute())
|
||||
assert.Equal(t, "container-foo", containerID)
|
||||
assert.Equal(t, checkpoint, checkpointID)
|
||||
assert.Equal(t, "/dir/foo", checkpointDir)
|
||||
assert.Equal(t, false, exit)
|
||||
assert.Equal(t, checkpoint, strings.TrimSpace(cli.OutBuffer().String()))
|
||||
}
|
||||
71
components/cli/cli/command/checkpoint/list_test.go
Normal file
71
components/cli/cli/command/checkpoint/list_test.go
Normal file
@ -0,0 +1,71 @@
|
||||
package checkpoint
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/internal/test"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/pkg/testutil"
|
||||
"github.com/docker/docker/pkg/testutil/golden"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCheckpointListErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
args []string
|
||||
checkpointListFunc func(container string, options types.CheckpointListOptions) ([]types.Checkpoint, error)
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
args: []string{},
|
||||
expectedError: "requires exactly 1 argument",
|
||||
},
|
||||
{
|
||||
args: []string{"too", "many", "arguments"},
|
||||
expectedError: "requires exactly 1 argument",
|
||||
},
|
||||
{
|
||||
args: []string{"foo"},
|
||||
checkpointListFunc: func(container string, options types.CheckpointListOptions) ([]types.Checkpoint, error) {
|
||||
return []types.Checkpoint{}, errors.Errorf("error getting checkpoints for container foo")
|
||||
},
|
||||
expectedError: "error getting checkpoints for container foo",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
checkpointListFunc: tc.checkpointListFunc,
|
||||
}, &bytes.Buffer{})
|
||||
cmd := newListCommand(cli)
|
||||
cmd.SetArgs(tc.args)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckpointListWithOptions(t *testing.T) {
|
||||
var containerID, checkpointDir string
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
checkpointListFunc: func(container string, options types.CheckpointListOptions) ([]types.Checkpoint, error) {
|
||||
containerID = container
|
||||
checkpointDir = options.CheckpointDir
|
||||
return []types.Checkpoint{
|
||||
{Name: "checkpoint-foo"},
|
||||
}, nil
|
||||
},
|
||||
}, buf)
|
||||
cmd := newListCommand(cli)
|
||||
cmd.SetArgs([]string{"container-foo"})
|
||||
cmd.Flags().Set("checkpoint-dir", "/dir/foo")
|
||||
assert.NoError(t, cmd.Execute())
|
||||
assert.Equal(t, "container-foo", containerID)
|
||||
assert.Equal(t, "/dir/foo", checkpointDir)
|
||||
actual := buf.String()
|
||||
expected := golden.Get(t, []byte(actual), "checkpoint-list-with-options.golden")
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
66
components/cli/cli/command/checkpoint/remove_test.go
Normal file
66
components/cli/cli/command/checkpoint/remove_test.go
Normal file
@ -0,0 +1,66 @@
|
||||
package checkpoint
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/internal/test"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/pkg/testutil"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCheckpointRemoveErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
args []string
|
||||
checkpointDeleteFunc func(container string, options types.CheckpointDeleteOptions) error
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
args: []string{"too-few-arguments"},
|
||||
expectedError: "requires exactly 2 argument(s)",
|
||||
},
|
||||
{
|
||||
args: []string{"too", "many", "arguments"},
|
||||
expectedError: "requires exactly 2 argument(s)",
|
||||
},
|
||||
{
|
||||
args: []string{"foo", "bar"},
|
||||
checkpointDeleteFunc: func(container string, options types.CheckpointDeleteOptions) error {
|
||||
return errors.Errorf("error deleting checkpoint")
|
||||
},
|
||||
expectedError: "error deleting checkpoint",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
checkpointDeleteFunc: tc.checkpointDeleteFunc,
|
||||
}, &bytes.Buffer{})
|
||||
cmd := newRemoveCommand(cli)
|
||||
cmd.SetArgs(tc.args)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckpointRemoveWithOptions(t *testing.T) {
|
||||
var containerID, checkpointID, checkpointDir string
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
checkpointDeleteFunc: func(container string, options types.CheckpointDeleteOptions) error {
|
||||
containerID = container
|
||||
checkpointID = options.CheckpointID
|
||||
checkpointDir = options.CheckpointDir
|
||||
return nil
|
||||
},
|
||||
}, &bytes.Buffer{})
|
||||
cmd := newRemoveCommand(cli)
|
||||
cmd.SetArgs([]string{"container-foo", "checkpoint-bar"})
|
||||
cmd.Flags().Set("checkpoint-dir", "/dir/foo")
|
||||
assert.NoError(t, cmd.Execute())
|
||||
assert.Equal(t, "container-foo", containerID)
|
||||
assert.Equal(t, "checkpoint-bar", checkpointID)
|
||||
assert.Equal(t, "/dir/foo", checkpointDir)
|
||||
}
|
||||
2
components/cli/cli/command/checkpoint/testdata/checkpoint-list-with-options.golden
vendored
Normal file
2
components/cli/cli/command/checkpoint/testdata/checkpoint-list-with-options.golden
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
CHECKPOINT NAME
|
||||
checkpoint-foo
|
||||
@ -1,7 +1,6 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
@ -41,11 +40,10 @@ func TestConfigCreateErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newConfigCreateCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
configCreateFunc: tc.configCreateFunc,
|
||||
}, buf),
|
||||
}),
|
||||
)
|
||||
cmd.SetArgs(tc.args)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
@ -55,7 +53,6 @@ func TestConfigCreateErrors(t *testing.T) {
|
||||
|
||||
func TestConfigCreateWithName(t *testing.T) {
|
||||
name := "foo"
|
||||
buf := new(bytes.Buffer)
|
||||
var actual []byte
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
configCreateFunc: func(spec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
|
||||
@ -69,14 +66,14 @@ func TestConfigCreateWithName(t *testing.T) {
|
||||
ID: "ID-" + spec.Name,
|
||||
}, nil
|
||||
},
|
||||
}, buf)
|
||||
})
|
||||
|
||||
cmd := newConfigCreateCommand(cli)
|
||||
cmd.SetArgs([]string{name, filepath.Join("testdata", configDataFile)})
|
||||
assert.NoError(t, cmd.Execute())
|
||||
expected := golden.Get(t, actual, configDataFile)
|
||||
assert.Equal(t, string(expected), string(actual))
|
||||
assert.Equal(t, "ID-"+name, strings.TrimSpace(buf.String()))
|
||||
assert.Equal(t, "ID-"+name, strings.TrimSpace(cli.OutBuffer().String()))
|
||||
}
|
||||
|
||||
func TestConfigCreateWithLabels(t *testing.T) {
|
||||
@ -86,7 +83,6 @@ func TestConfigCreateWithLabels(t *testing.T) {
|
||||
}
|
||||
name := "foo"
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
configCreateFunc: func(spec swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
|
||||
if spec.Name != name {
|
||||
@ -101,12 +97,12 @@ func TestConfigCreateWithLabels(t *testing.T) {
|
||||
ID: "ID-" + spec.Name,
|
||||
}, nil
|
||||
},
|
||||
}, buf)
|
||||
})
|
||||
|
||||
cmd := newConfigCreateCommand(cli)
|
||||
cmd.SetArgs([]string{name, filepath.Join("testdata", configDataFile)})
|
||||
cmd.Flags().Set("label", "lbl1=Label-foo")
|
||||
cmd.Flags().Set("label", "lbl2=Label-bar")
|
||||
assert.NoError(t, cmd.Execute())
|
||||
assert.Equal(t, "ID-"+name, strings.TrimSpace(buf.String()))
|
||||
assert.Equal(t, "ID-"+name, strings.TrimSpace(cli.OutBuffer().String()))
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ func TestConfigInspectErrors(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newConfigInspectCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
configInspectFunc: tc.configInspectFunc,
|
||||
}, buf),
|
||||
)
|
||||
@ -97,7 +97,7 @@ func TestConfigInspectWithoutFormat(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newConfigInspectCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
configInspectFunc: tc.configInspectFunc,
|
||||
}, buf),
|
||||
)
|
||||
@ -137,7 +137,7 @@ func TestConfigInspectWithFormat(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newConfigInspectCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
configInspectFunc: tc.configInspectFunc,
|
||||
}, buf),
|
||||
)
|
||||
@ -174,7 +174,7 @@ func TestConfigInspectPretty(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newConfigInspectCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
configInspectFunc: tc.configInspectFunc,
|
||||
}, buf))
|
||||
cmd.SetArgs([]string{"configID"})
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
"time"
|
||||
@ -36,11 +35,10 @@ func TestConfigListErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newConfigListCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
configListFunc: tc.configListFunc,
|
||||
}, buf),
|
||||
}),
|
||||
)
|
||||
cmd.SetArgs(tc.args)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
@ -49,7 +47,6 @@ func TestConfigListErrors(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestConfigList(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
configListFunc: func(options types.ConfigListOptions) ([]swarm.Config, error) {
|
||||
return []swarm.Config{
|
||||
@ -67,18 +64,16 @@ func TestConfigList(t *testing.T) {
|
||||
),
|
||||
}, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{})
|
||||
})
|
||||
cmd := newConfigListCommand(cli)
|
||||
cmd.SetOutput(buf)
|
||||
cmd.SetOutput(cli.OutBuffer())
|
||||
assert.NoError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
actual := cli.OutBuffer().String()
|
||||
expected := golden.Get(t, []byte(actual), "config-list.golden")
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
|
||||
func TestConfigListWithQuietOption(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
configListFunc: func(options types.ConfigListOptions) ([]swarm.Config, error) {
|
||||
return []swarm.Config{
|
||||
@ -88,18 +83,16 @@ func TestConfigListWithQuietOption(t *testing.T) {
|
||||
})),
|
||||
}, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{})
|
||||
})
|
||||
cmd := newConfigListCommand(cli)
|
||||
cmd.Flags().Set("quiet", "true")
|
||||
assert.NoError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
actual := cli.OutBuffer().String()
|
||||
expected := golden.Get(t, []byte(actual), "config-list-with-quiet-option.golden")
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
|
||||
func TestConfigListWithConfigFormat(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
configListFunc: func(options types.ConfigListOptions) ([]swarm.Config, error) {
|
||||
return []swarm.Config{
|
||||
@ -109,19 +102,18 @@ func TestConfigListWithConfigFormat(t *testing.T) {
|
||||
})),
|
||||
}, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{
|
||||
})
|
||||
cli.SetConfigFile(&configfile.ConfigFile{
|
||||
ConfigFormat: "{{ .Name }} {{ .Labels }}",
|
||||
})
|
||||
cmd := newConfigListCommand(cli)
|
||||
assert.NoError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
actual := cli.OutBuffer().String()
|
||||
expected := golden.Get(t, []byte(actual), "config-list-with-config-format.golden")
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
|
||||
func TestConfigListWithFormat(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
configListFunc: func(options types.ConfigListOptions) ([]swarm.Config, error) {
|
||||
return []swarm.Config{
|
||||
@ -131,17 +123,16 @@ func TestConfigListWithFormat(t *testing.T) {
|
||||
})),
|
||||
}, nil
|
||||
},
|
||||
}, buf)
|
||||
})
|
||||
cmd := newConfigListCommand(cli)
|
||||
cmd.Flags().Set("format", "{{ .Name }} {{ .Labels }}")
|
||||
assert.NoError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
actual := cli.OutBuffer().String()
|
||||
expected := golden.Get(t, []byte(actual), "config-list-with-format.golden")
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
|
||||
func TestConfigListWithFilter(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
configListFunc: func(options types.ConfigListOptions) ([]swarm.Config, error) {
|
||||
assert.Equal(t, "foo", options.Filters.Get("name")[0])
|
||||
@ -161,13 +152,12 @@ func TestConfigListWithFilter(t *testing.T) {
|
||||
),
|
||||
}, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{})
|
||||
})
|
||||
cmd := newConfigListCommand(cli)
|
||||
cmd.Flags().Set("filter", "name=foo")
|
||||
cmd.Flags().Set("filter", "label=lbl1=Label-bar")
|
||||
assert.NoError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
actual := cli.OutBuffer().String()
|
||||
expected := golden.Get(t, []byte(actual), "config-list-with-filter.golden")
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ func TestConfigRemoveErrors(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newConfigRemoveCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
configRemoveFunc: tc.configRemoveFunc,
|
||||
}, buf),
|
||||
)
|
||||
@ -47,7 +47,7 @@ func TestConfigRemoveWithName(t *testing.T) {
|
||||
names := []string{"foo", "bar"}
|
||||
buf := new(bytes.Buffer)
|
||||
var removedConfigs []string
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
configRemoveFunc: func(name string) error {
|
||||
removedConfigs = append(removedConfigs, name)
|
||||
return nil
|
||||
@ -65,7 +65,7 @@ func TestConfigRemoveContinueAfterError(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
var removedConfigs []string
|
||||
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
configRemoveFunc: func(name string) error {
|
||||
removedConfigs = append(removedConfigs, name)
|
||||
if name == "foo" {
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
@ -68,8 +67,7 @@ func TestNewAttachCommandErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := NewAttachCommand(test.NewFakeCli(&fakeClient{containerInspectFunc: tc.containerInspectFunc}, buf))
|
||||
cmd := NewAttachCommand(test.NewFakeCli(&fakeClient{containerInspectFunc: tc.containerInspectFunc}))
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
|
||||
@ -1,15 +1,14 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/cli/internal/test"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/pkg/testutil"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type arguments struct {
|
||||
@ -79,9 +78,7 @@ func TestParseExec(t *testing.T) {
|
||||
|
||||
for valid, expectedExecConfig := range valids {
|
||||
execConfig, err := parseExec(&valid.options, valid.execCmd)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
if !compareExecConfig(expectedExecConfig, execConfig) {
|
||||
t.Fatalf("Expected [%v] for %v, got [%v]", expectedExecConfig, valid, execConfig)
|
||||
}
|
||||
@ -138,10 +135,7 @@ func TestNewExecCommandErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
conf := configfile.ConfigFile{}
|
||||
cli := test.NewFakeCli(&fakeClient{containerInspectFunc: tc.containerInspectFunc}, buf)
|
||||
cli.SetConfigfile(&conf)
|
||||
cli := test.NewFakeCli(&fakeClient{containerInspectFunc: tc.containerInspectFunc})
|
||||
cmd := NewExecCommand(cli)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
|
||||
@ -190,10 +190,11 @@ func runContainer(dockerCli *command.DockerCli, opts *runOptions, copts *contain
|
||||
}
|
||||
|
||||
close, err := attachContainer(ctx, dockerCli, &errCh, config, createResponse.ID)
|
||||
defer close()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer close()
|
||||
}
|
||||
|
||||
statusChan := waitExitOrRemoved(ctx, dockerCli, createResponse.ID, copts.autoRemove)
|
||||
|
||||
@ -243,7 +243,7 @@ func (c *diskUsageImagesContext) Reclaimable() string {
|
||||
if c.totalSize > 0 {
|
||||
return fmt.Sprintf("%s (%v%%)", units.HumanSize(float64(reclaimable)), (reclaimable*100)/c.totalSize)
|
||||
}
|
||||
return fmt.Sprintf("%s", units.HumanSize(float64(reclaimable)))
|
||||
return units.HumanSize(float64(reclaimable))
|
||||
}
|
||||
|
||||
type diskUsageContainersContext struct {
|
||||
@ -305,7 +305,7 @@ func (c *diskUsageContainersContext) Reclaimable() string {
|
||||
return fmt.Sprintf("%s (%v%%)", units.HumanSize(float64(reclaimable)), (reclaimable*100)/totalSize)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s", units.HumanSize(float64(reclaimable)))
|
||||
return units.HumanSize(float64(reclaimable))
|
||||
}
|
||||
|
||||
type diskUsageVolumesContext struct {
|
||||
@ -366,7 +366,7 @@ func (c *diskUsageVolumesContext) Reclaimable() string {
|
||||
return fmt.Sprintf("%s (%v%%)", units.HumanSize(float64(reclaimable)), (reclaimable*100)/totalSize)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s", units.HumanSize(float64(reclaimable)))
|
||||
return units.HumanSize(float64(reclaimable))
|
||||
}
|
||||
|
||||
type diskUsageBuilderContext struct {
|
||||
|
||||
@ -184,7 +184,7 @@ func (c *containerStatsContext) MemUsage() string {
|
||||
return fmt.Sprintf("-- / --")
|
||||
}
|
||||
if c.os == winOSType {
|
||||
return fmt.Sprintf("%s", units.BytesSize(c.s.Memory))
|
||||
return units.BytesSize(c.s.Memory)
|
||||
}
|
||||
return fmt.Sprintf("%s / %s", units.BytesSize(c.s.Memory), units.BytesSize(c.s.MemoryLimit))
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ func TestRunBuildDockerfileFromStdinWithCompress(t *testing.T) {
|
||||
return types.ImageBuildResponse{Body: ioutil.NopCloser(body)}, nil
|
||||
}
|
||||
|
||||
cli := test.NewFakeCli(&fakeClient{imageBuildFunc: fakeImageBuild}, ioutil.Discard)
|
||||
cli := test.NewFakeCli(&fakeClient{imageBuildFunc: fakeImageBuild})
|
||||
dockerfile := bytes.NewBufferString(`
|
||||
FROM alpine:3.6
|
||||
COPY foo /
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
@ -38,8 +37,7 @@ func TestNewHistoryCommandErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := NewHistoryCommand(test.NewFakeCli(&fakeClient{imageHistoryFunc: tc.imageHistoryFunc}, buf))
|
||||
cmd := NewHistoryCommand(test.NewFakeCli(&fakeClient{imageHistoryFunc: tc.imageHistoryFunc}))
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
@ -90,13 +88,13 @@ func TestNewHistoryCommandSuccess(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := NewHistoryCommand(test.NewFakeCli(&fakeClient{imageHistoryFunc: tc.imageHistoryFunc}, buf))
|
||||
cli := test.NewFakeCli(&fakeClient{imageHistoryFunc: tc.imageHistoryFunc})
|
||||
cmd := NewHistoryCommand(cli)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
err := cmd.Execute()
|
||||
assert.NoError(t, err)
|
||||
actual := buf.String()
|
||||
actual := cli.OutBuffer().String()
|
||||
if tc.outputRegex == "" {
|
||||
expected := string(golden.Get(t, []byte(actual), fmt.Sprintf("history-command-success.%s.golden", tc.name))[:])
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, expected)
|
||||
|
||||
@ -37,7 +37,7 @@ func TestNewImportCommandErrors(t *testing.T) {
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := NewImportCommand(test.NewFakeCli(&fakeClient{imageImportFunc: tc.imageImportFunc}, buf))
|
||||
cmd := NewImportCommand(test.NewFakeCliWithOutput(&fakeClient{imageImportFunc: tc.imageImportFunc}, buf))
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
@ -45,7 +45,7 @@ func TestNewImportCommandErrors(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewImportCommandInvalidFile(t *testing.T) {
|
||||
cmd := NewImportCommand(test.NewFakeCli(&fakeClient{}, new(bytes.Buffer)))
|
||||
cmd := NewImportCommand(test.NewFakeCli(&fakeClient{}))
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.SetArgs([]string{"testdata/import-command-success.unexistent-file"})
|
||||
testutil.ErrorContains(t, cmd.Execute(), "testdata/import-command-success.unexistent-file")
|
||||
@ -92,7 +92,7 @@ func TestNewImportCommandSuccess(t *testing.T) {
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := NewImportCommand(test.NewFakeCli(&fakeClient{imageImportFunc: tc.imageImportFunc}, buf))
|
||||
cmd := NewImportCommand(test.NewFakeCliWithOutput(&fakeClient{imageImportFunc: tc.imageImportFunc}, buf))
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
assert.NoError(t, cmd.Execute())
|
||||
|
||||
@ -27,7 +27,7 @@ func TestNewInspectCommandErrors(t *testing.T) {
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newInspectCommand(test.NewFakeCli(&fakeClient{}, buf))
|
||||
cmd := newInspectCommand(test.NewFakeCliWithOutput(&fakeClient{}, buf))
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
@ -79,7 +79,7 @@ func TestNewInspectCommandSuccess(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
imageInspectInvocationCount = 0
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newInspectCommand(test.NewFakeCli(&fakeClient{imageInspectFunc: tc.imageInspectFunc}, buf))
|
||||
cmd := newInspectCommand(test.NewFakeCliWithOutput(&fakeClient{imageInspectFunc: tc.imageInspectFunc}, buf))
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
err := cmd.Execute()
|
||||
|
||||
@ -36,7 +36,7 @@ func TestNewImagesCommandErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
cmd := NewImagesCommand(test.NewFakeCli(&fakeClient{imageListFunc: tc.imageListFunc}, new(bytes.Buffer)))
|
||||
cmd := NewImagesCommand(test.NewFakeCli(&fakeClient{imageListFunc: tc.imageListFunc}))
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
@ -81,8 +81,8 @@ func TestNewImagesCommandSuccess(t *testing.T) {
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{imageListFunc: tc.imageListFunc}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{ImagesFormat: tc.imageFormat})
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{imageListFunc: tc.imageListFunc}, buf)
|
||||
cli.SetConfigFile(&configfile.ConfigFile{ImagesFormat: tc.imageFormat})
|
||||
cmd := NewImagesCommand(cli)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
@ -95,7 +95,7 @@ func TestNewImagesCommandSuccess(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewListCommandAlias(t *testing.T) {
|
||||
cmd := newListCommand(test.NewFakeCli(&fakeClient{}, new(bytes.Buffer)))
|
||||
cmd := newListCommand(test.NewFakeCli(&fakeClient{}))
|
||||
assert.True(t, cmd.HasAlias("images"))
|
||||
assert.True(t, cmd.HasAlias("list"))
|
||||
assert.False(t, cmd.HasAlias("other"))
|
||||
|
||||
@ -43,7 +43,7 @@ func TestNewLoadCommandErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
cli := test.NewFakeCli(&fakeClient{imageLoadFunc: tc.imageLoadFunc}, new(bytes.Buffer))
|
||||
cli := test.NewFakeCli(&fakeClient{imageLoadFunc: tc.imageLoadFunc})
|
||||
cli.In().SetIsTerminal(tc.isTerminalIn)
|
||||
cmd := NewLoadCommand(cli)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
@ -54,7 +54,7 @@ func TestNewLoadCommandErrors(t *testing.T) {
|
||||
|
||||
func TestNewLoadCommandInvalidInput(t *testing.T) {
|
||||
expectedError := "open *"
|
||||
cmd := NewLoadCommand(test.NewFakeCli(&fakeClient{}, new(bytes.Buffer)))
|
||||
cmd := NewLoadCommand(test.NewFakeCli(&fakeClient{}))
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.SetArgs([]string{"--input", "*"})
|
||||
err := cmd.Execute()
|
||||
@ -93,7 +93,7 @@ func TestNewLoadCommandSuccess(t *testing.T) {
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := NewLoadCommand(test.NewFakeCli(&fakeClient{imageLoadFunc: tc.imageLoadFunc}, buf))
|
||||
cmd := NewLoadCommand(test.NewFakeCliWithOutput(&fakeClient{imageLoadFunc: tc.imageLoadFunc}, buf))
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
err := cmd.Execute()
|
||||
|
||||
@ -38,7 +38,7 @@ func TestNewPruneCommandErrors(t *testing.T) {
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := NewPruneCommand(test.NewFakeCli(&fakeClient{
|
||||
cmd := NewPruneCommand(test.NewFakeCliWithOutput(&fakeClient{
|
||||
imagesPruneFunc: tc.imagesPruneFunc,
|
||||
}, buf))
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
@ -86,7 +86,7 @@ func TestNewPruneCommandSuccess(t *testing.T) {
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := NewPruneCommand(test.NewFakeCli(&fakeClient{
|
||||
cmd := NewPruneCommand(test.NewFakeCliWithOutput(&fakeClient{
|
||||
imagesPruneFunc: tc.imagesPruneFunc,
|
||||
}, buf))
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/cli/internal/test"
|
||||
"github.com/docker/docker/pkg/testutil"
|
||||
"github.com/docker/docker/pkg/testutil/golden"
|
||||
@ -41,9 +39,7 @@ func TestNewPullCommandErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{}, buf)
|
||||
cli.SetConfigfile(configfile.New("filename"))
|
||||
cli := test.NewFakeCli(&fakeClient{})
|
||||
cmd := NewPullCommand(cli)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
@ -66,15 +62,13 @@ func TestNewPullCommandSuccess(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{}, buf)
|
||||
cli.SetConfigfile(configfile.New("filename"))
|
||||
cli := test.NewFakeCli(&fakeClient{})
|
||||
cmd := NewPullCommand(cli)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
err := cmd.Execute()
|
||||
assert.NoError(t, err)
|
||||
actual := buf.String()
|
||||
actual := cli.OutBuffer().String()
|
||||
expected := string(golden.Get(t, []byte(actual), fmt.Sprintf("pull-command-success.%s.golden", tc.name))[:])
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, expected)
|
||||
}
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/cli/internal/test"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/pkg/testutil"
|
||||
@ -47,9 +45,7 @@ func TestNewPushCommandErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{imagePushFunc: tc.imagePushFunc}, buf)
|
||||
cli.SetConfigfile(configfile.New("filename"))
|
||||
cli := test.NewFakeCli(&fakeClient{imagePushFunc: tc.imagePushFunc})
|
||||
cmd := NewPushCommand(cli)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
@ -68,13 +64,11 @@ func TestNewPushCommandSuccess(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
imagePushFunc: func(ref string, options types.ImagePushOptions) (io.ReadCloser, error) {
|
||||
return ioutil.NopCloser(strings.NewReader("")), nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(configfile.New("filename"))
|
||||
})
|
||||
cmd := NewPushCommand(cli)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
@ -15,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
func TestNewRemoveCommandAlias(t *testing.T) {
|
||||
cmd := newRemoveCommand(test.NewFakeCli(&fakeClient{}, new(bytes.Buffer)))
|
||||
cmd := newRemoveCommand(test.NewFakeCli(&fakeClient{}))
|
||||
assert.True(t, cmd.HasAlias("rmi"))
|
||||
assert.True(t, cmd.HasAlias("remove"))
|
||||
assert.False(t, cmd.HasAlias("other"))
|
||||
@ -46,7 +45,7 @@ func TestNewRemoveCommandErrors(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
cmd := NewRemoveCommand(test.NewFakeCli(&fakeClient{
|
||||
imageRemoveFunc: tc.imageRemoveFunc,
|
||||
}, new(bytes.Buffer)))
|
||||
}))
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
@ -97,18 +96,15 @@ func TestNewRemoveCommandSuccess(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
errBuf := new(bytes.Buffer)
|
||||
fakeCli := test.NewFakeCli(&fakeClient{imageRemoveFunc: tc.imageRemoveFunc}, buf)
|
||||
fakeCli.SetErr(errBuf)
|
||||
fakeCli := test.NewFakeCli(&fakeClient{imageRemoveFunc: tc.imageRemoveFunc})
|
||||
cmd := NewRemoveCommand(fakeCli)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
assert.NoError(t, cmd.Execute())
|
||||
if tc.expectedErrMsg != "" {
|
||||
assert.Equal(t, tc.expectedErrMsg, errBuf.String())
|
||||
assert.Equal(t, tc.expectedErrMsg, fakeCli.ErrBuffer().String())
|
||||
}
|
||||
actual := buf.String()
|
||||
actual := fakeCli.OutBuffer().String()
|
||||
expected := string(golden.Get(t, []byte(actual), fmt.Sprintf("remove-command-success.%s.golden", tc.name))[:])
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, expected)
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ func TestNewSaveCommandErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
cli := test.NewFakeCli(&fakeClient{imageSaveFunc: tc.imageSaveFunc}, new(bytes.Buffer))
|
||||
cli := test.NewFakeCli(&fakeClient{imageSaveFunc: tc.imageSaveFunc})
|
||||
cli.Out().SetIsTerminal(tc.isTerminal)
|
||||
cmd := NewSaveCommand(cli)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
@ -85,7 +85,7 @@ func TestNewSaveCommandSuccess(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
cmd := NewSaveCommand(test.NewFakeCli(&fakeClient{
|
||||
cmd := NewSaveCommand(test.NewFakeCliWithOutput(&fakeClient{
|
||||
imageSaveFunc: func(images []string) (io.ReadCloser, error) {
|
||||
return ioutil.NopCloser(strings.NewReader("")), nil
|
||||
},
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
@ -17,9 +16,8 @@ func TestCliNewTagCommandErrors(t *testing.T) {
|
||||
{"image1", "image2", "image3"},
|
||||
}
|
||||
expectedError := "\"tag\" requires exactly 2 argument(s)."
|
||||
buf := new(bytes.Buffer)
|
||||
for _, args := range testCases {
|
||||
cmd := NewTagCommand(test.NewFakeCli(&fakeClient{}, buf))
|
||||
cmd := NewTagCommand(test.NewFakeCli(&fakeClient{}))
|
||||
cmd.SetArgs(args)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
testutil.ErrorContains(t, cmd.Execute(), expectedError)
|
||||
@ -27,7 +25,6 @@ func TestCliNewTagCommandErrors(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCliNewTagCommand(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := NewTagCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
imageTagFunc: func(image string, ref string) error {
|
||||
@ -35,7 +32,7 @@ func TestCliNewTagCommand(t *testing.T) {
|
||||
assert.Equal(t, "image2", ref)
|
||||
return nil
|
||||
},
|
||||
}, buf))
|
||||
}))
|
||||
cmd.SetArgs([]string{"image1", "image2"})
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
assert.NoError(t, cmd.Execute())
|
||||
|
||||
@ -21,6 +21,9 @@
|
||||
},
|
||||
"RootFS": {
|
||||
"Type": ""
|
||||
},
|
||||
"Metadata": {
|
||||
"LastTagTime": "0001-01-01T00:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -45,6 +48,9 @@
|
||||
},
|
||||
"RootFS": {
|
||||
"Type": ""
|
||||
},
|
||||
"Metadata": {
|
||||
"LastTagTime": "0001-01-01T00:00:00Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@ -21,6 +21,9 @@
|
||||
},
|
||||
"RootFS": {
|
||||
"Type": ""
|
||||
},
|
||||
"Metadata": {
|
||||
"LastTagTime": "0001-01-01T00:00:00Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
19
components/cli/cli/command/network/client_test.go
Normal file
19
components/cli/cli/command/network/client_test.go
Normal file
@ -0,0 +1,19 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type fakeClient struct {
|
||||
client.Client
|
||||
networkCreateFunc func(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error)
|
||||
}
|
||||
|
||||
func (c *fakeClient) NetworkCreate(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error) {
|
||||
if c.networkCreateFunc != nil {
|
||||
return c.networkCreateFunc(ctx, name, options)
|
||||
}
|
||||
return types.NetworkCreateResponse{}, nil
|
||||
}
|
||||
@ -8,8 +8,7 @@ import (
|
||||
)
|
||||
|
||||
// NewNetworkCommand returns a cobra command for `network` subcommands
|
||||
// nolint: interfacer
|
||||
func NewNetworkCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func NewNetworkCommand(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "network",
|
||||
Short: "Manage networks",
|
||||
|
||||
@ -19,7 +19,7 @@ type connectOptions struct {
|
||||
linklocalips []string
|
||||
}
|
||||
|
||||
func newConnectCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newConnectCommand(dockerCli command.Cli) *cobra.Command {
|
||||
options := connectOptions{
|
||||
links: opts.NewListOpts(opts.ValidateLink),
|
||||
}
|
||||
@ -45,7 +45,7 @@ func newConnectCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runConnect(dockerCli *command.DockerCli, options connectOptions) error {
|
||||
func runConnect(dockerCli command.Cli, options connectOptions) error {
|
||||
client := dockerCli.Client()
|
||||
|
||||
epConfig := &network.EndpointSettings{
|
||||
|
||||
@ -36,7 +36,7 @@ type createOptions struct {
|
||||
ipamOpt opts.MapOpts
|
||||
}
|
||||
|
||||
func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
options := createOptions{
|
||||
driverOpts: *opts.NewMapOpts(nil, nil),
|
||||
labels: opts.NewListOpts(opts.ValidateEnv),
|
||||
@ -82,7 +82,7 @@ func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runCreate(dockerCli *command.DockerCli, options createOptions) error {
|
||||
func runCreate(dockerCli command.Cli, options createOptions) error {
|
||||
client := dockerCli.Client()
|
||||
|
||||
ipamCfg, err := consolidateIpam(options.ipamSubnet, options.ipamIPRange, options.ipamGateway, options.ipamAux.GetAll())
|
||||
@ -232,13 +232,13 @@ func subnetMatches(subnet, data string) (bool, error) {
|
||||
|
||||
_, s, err := net.ParseCIDR(subnet)
|
||||
if err != nil {
|
||||
return false, errors.Errorf("Invalid subnet %s : %v", s, err)
|
||||
return false, errors.Wrap(err, "invalid subnet")
|
||||
}
|
||||
|
||||
if strings.Contains(data, "/") {
|
||||
ip, _, err = net.ParseCIDR(data)
|
||||
if err != nil {
|
||||
return false, errors.Errorf("Invalid cidr %s : %v", data, err)
|
||||
return false, err
|
||||
}
|
||||
} else {
|
||||
ip = net.ParseIP(data)
|
||||
|
||||
175
components/cli/cli/command/network/create_test.go
Normal file
175
components/cli/cli/command/network/create_test.go
Normal file
@ -0,0 +1,175 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/internal/test"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/pkg/testutil"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func TestNetworkCreateErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
args []string
|
||||
flags map[string]string
|
||||
networkCreateFunc func(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error)
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
expectedError: "exactly 1 argument",
|
||||
},
|
||||
{
|
||||
args: []string{"toto"},
|
||||
networkCreateFunc: func(ctx context.Context, name string, createBody types.NetworkCreate) (types.NetworkCreateResponse, error) {
|
||||
return types.NetworkCreateResponse{}, errors.Errorf("error creating network")
|
||||
},
|
||||
expectedError: "error creating network",
|
||||
},
|
||||
{
|
||||
args: []string{"toto"},
|
||||
flags: map[string]string{
|
||||
"ip-range": "255.255.0.0/24",
|
||||
"gateway": "255.0.255.0/24",
|
||||
"subnet": "10.1.2.0.30.50",
|
||||
},
|
||||
expectedError: "invalid CIDR address: 10.1.2.0.30.50",
|
||||
},
|
||||
{
|
||||
args: []string{"toto"},
|
||||
flags: map[string]string{
|
||||
"ip-range": "255.255.0.0.30/24",
|
||||
"gateway": "255.0.255.0/24",
|
||||
"subnet": "255.0.0.0/24",
|
||||
},
|
||||
expectedError: "invalid CIDR address: 255.255.0.0.30/24",
|
||||
},
|
||||
{
|
||||
args: []string{"toto"},
|
||||
flags: map[string]string{
|
||||
"gateway": "255.0.0.0/24",
|
||||
},
|
||||
expectedError: "every ip-range or gateway must have a corresponding subnet",
|
||||
},
|
||||
{
|
||||
args: []string{"toto"},
|
||||
flags: map[string]string{
|
||||
"ip-range": "255.0.0.0/24",
|
||||
},
|
||||
expectedError: "every ip-range or gateway must have a corresponding subnet",
|
||||
},
|
||||
{
|
||||
args: []string{"toto"},
|
||||
flags: map[string]string{
|
||||
"ip-range": "255.0.0.0/24",
|
||||
"gateway": "255.0.0.0/24",
|
||||
},
|
||||
expectedError: "every ip-range or gateway must have a corresponding subnet",
|
||||
},
|
||||
{
|
||||
args: []string{"toto"},
|
||||
flags: map[string]string{
|
||||
"ip-range": "255.255.0.0/24",
|
||||
"gateway": "255.0.255.0/24",
|
||||
"subnet": "10.1.2.0/23,10.1.3.248/30",
|
||||
},
|
||||
expectedError: "multiple overlapping subnet configuration is not supported",
|
||||
},
|
||||
{
|
||||
args: []string{"toto"},
|
||||
flags: map[string]string{
|
||||
"ip-range": "192.168.1.0/24,192.168.1.200/24",
|
||||
"gateway": "192.168.1.1,192.168.1.4",
|
||||
"subnet": "192.168.2.0/24,192.168.1.250/24",
|
||||
},
|
||||
expectedError: "cannot configure multiple ranges (192.168.1.200/24, 192.168.1.0/24) on the same subnet (192.168.1.250/24)",
|
||||
},
|
||||
{
|
||||
args: []string{"toto"},
|
||||
flags: map[string]string{
|
||||
"ip-range": "255.255.200.0/24,255.255.120.0/24",
|
||||
"gateway": "255.0.255.0/24",
|
||||
"subnet": "255.255.255.0/24,255.255.0.255/24",
|
||||
},
|
||||
expectedError: "no matching subnet for range 255.255.200.0/24",
|
||||
},
|
||||
{
|
||||
args: []string{"toto"},
|
||||
flags: map[string]string{
|
||||
"ip-range": "192.168.1.0/24",
|
||||
"gateway": "192.168.1.1,192.168.1.4",
|
||||
"subnet": "192.168.2.0/24,192.168.1.250/24",
|
||||
},
|
||||
expectedError: "cannot configure multiple gateways (192.168.1.4, 192.168.1.1) for the same subnet (192.168.1.250/24)",
|
||||
},
|
||||
{
|
||||
args: []string{"toto"},
|
||||
flags: map[string]string{
|
||||
"ip-range": "192.168.1.0/24",
|
||||
"gateway": "192.168.4.1,192.168.5.4",
|
||||
"subnet": "192.168.2.0/24,192.168.1.250/24",
|
||||
},
|
||||
expectedError: "no matching subnet for gateway 192.168.4.1",
|
||||
},
|
||||
{
|
||||
args: []string{"toto"},
|
||||
flags: map[string]string{
|
||||
"gateway": "255.255.0.0/24",
|
||||
"subnet": "255.255.0.0/24",
|
||||
"aux-address": "255.255.0.30/24",
|
||||
},
|
||||
expectedError: "no matching subnet for aux-address",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
cmd := newCreateCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
networkCreateFunc: tc.networkCreateFunc,
|
||||
}),
|
||||
)
|
||||
cmd.SetArgs(tc.args)
|
||||
for key, value := range tc.flags {
|
||||
require.NoError(t, cmd.Flags().Set(key, value))
|
||||
}
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
|
||||
}
|
||||
}
|
||||
func TestNetworkCreateWithFlags(t *testing.T) {
|
||||
expectedDriver := "foo"
|
||||
expectedOpts := []network.IPAMConfig{
|
||||
{
|
||||
"192.168.4.0/24",
|
||||
"192.168.4.0/24",
|
||||
"192.168.4.1/24",
|
||||
map[string]string{},
|
||||
},
|
||||
}
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
networkCreateFunc: func(ctx context.Context, name string, createBody types.NetworkCreate) (types.NetworkCreateResponse, error) {
|
||||
assert.Equal(t, expectedDriver, createBody.Driver, "not expected driver error")
|
||||
assert.Equal(t, expectedOpts, createBody.IPAM.Config, "not expected driver error")
|
||||
return types.NetworkCreateResponse{
|
||||
ID: name,
|
||||
}, nil
|
||||
},
|
||||
})
|
||||
args := []string{"banana"}
|
||||
cmd := newCreateCommand(cli)
|
||||
|
||||
cmd.SetArgs(args)
|
||||
cmd.Flags().Set("driver", "foo")
|
||||
cmd.Flags().Set("ip-range", "192.168.4.0/24")
|
||||
cmd.Flags().Set("gateway", "192.168.4.1/24")
|
||||
cmd.Flags().Set("subnet", "192.168.4.0/24")
|
||||
assert.NoError(t, cmd.Execute())
|
||||
assert.Equal(t, "banana", strings.TrimSpace(cli.OutBuffer().String()))
|
||||
}
|
||||
@ -14,7 +14,7 @@ type disconnectOptions struct {
|
||||
force bool
|
||||
}
|
||||
|
||||
func newDisconnectCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newDisconnectCommand(dockerCli command.Cli) *cobra.Command {
|
||||
opts := disconnectOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -34,7 +34,7 @@ func newDisconnectCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runDisconnect(dockerCli *command.DockerCli, opts disconnectOptions) error {
|
||||
func runDisconnect(dockerCli command.Cli, opts disconnectOptions) error {
|
||||
client := dockerCli.Client()
|
||||
|
||||
return client.NetworkDisconnect(context.Background(), opts.network, opts.container, opts.force)
|
||||
|
||||
@ -16,7 +16,7 @@ type inspectOptions struct {
|
||||
verbose bool
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
@ -25,7 +25,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{
|
||||
@ -47,7 +47,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 {
|
||||
client := dockerCli.Client()
|
||||
listOptions := types.NetworkListOptions{Filters: options.filter.Value()}
|
||||
networkResources, err := client.NetworkList(context.Background(), listOptions)
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newRemoveCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newRemoveCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "rm NETWORK [NETWORK...]",
|
||||
Aliases: []string{"remove"},
|
||||
@ -28,7 +28,7 @@ const ingressWarning = "WARNING! Before removing the routing-mesh network, " +
|
||||
"Otherwise, removal may not be effective and functionality of newly create " +
|
||||
"ingress networks will be impaired.\nAre you sure you want to continue?"
|
||||
|
||||
func runRemove(dockerCli *command.DockerCli, networks []string) error {
|
||||
func runRemove(dockerCli command.Cli, networks []string) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
status := 0
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
@ -40,12 +39,11 @@ func TestNodeDemoteErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newDemoteCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
nodeInspectFunc: tc.nodeInspectFunc,
|
||||
nodeUpdateFunc: tc.nodeUpdateFunc,
|
||||
}, buf))
|
||||
}))
|
||||
cmd.SetArgs(tc.args)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
@ -53,7 +51,6 @@ func TestNodeDemoteErrors(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNodeDemoteNoChange(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newDemoteCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
@ -65,13 +62,12 @@ func TestNodeDemoteNoChange(t *testing.T) {
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}, buf))
|
||||
}))
|
||||
cmd.SetArgs([]string{"nodeID"})
|
||||
assert.NoError(t, cmd.Execute())
|
||||
}
|
||||
|
||||
func TestNodeDemoteMultipleNode(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newDemoteCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
@ -83,7 +79,7 @@ func TestNodeDemoteMultipleNode(t *testing.T) {
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}, buf))
|
||||
}))
|
||||
cmd.SetArgs([]string{"nodeID1", "nodeID2"})
|
||||
assert.NoError(t, cmd.Execute())
|
||||
}
|
||||
|
||||
@ -69,7 +69,7 @@ func TestNodeInspectErrors(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newInspectCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
nodeInspectFunc: tc.nodeInspectFunc,
|
||||
infoFunc: tc.infoFunc,
|
||||
}, buf))
|
||||
@ -111,7 +111,7 @@ func TestNodeInspectPretty(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newInspectCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
nodeInspectFunc: tc.nodeInspectFunc,
|
||||
}, buf))
|
||||
cmd.SetArgs([]string{"nodeID"})
|
||||
|
||||
@ -42,12 +42,10 @@ func TestNodeListErrorOnAPIFailure(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
nodeListFunc: tc.nodeListFunc,
|
||||
infoFunc: tc.infoFunc,
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{})
|
||||
})
|
||||
cmd := newListCommand(cli)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
assert.EqualError(t, cmd.Execute(), tc.expectedError)
|
||||
@ -55,7 +53,6 @@ func TestNodeListErrorOnAPIFailure(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNodeList(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
nodeListFunc: func() ([]swarm.Node, error) {
|
||||
return []swarm.Node{
|
||||
@ -71,25 +68,25 @@ func TestNodeList(t *testing.T) {
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{})
|
||||
})
|
||||
|
||||
cmd := newListCommand(cli)
|
||||
assert.NoError(t, cmd.Execute())
|
||||
assert.Contains(t, buf.String(), `nodeID1 * nodeHostname1 Ready Active Leader`)
|
||||
assert.Contains(t, buf.String(), `nodeID2 nodeHostname2 Ready Active Reachable`)
|
||||
assert.Contains(t, buf.String(), `nodeID3 nodeHostname3 Ready Active`)
|
||||
out := cli.OutBuffer().String()
|
||||
assert.Contains(t, out, `nodeID1 * nodeHostname1 Ready Active Leader`)
|
||||
assert.Contains(t, out, `nodeID2 nodeHostname2 Ready Active Reachable`)
|
||||
assert.Contains(t, out, `nodeID3 nodeHostname3 Ready Active`)
|
||||
}
|
||||
|
||||
func TestNodeListQuietShouldOnlyPrintIDs(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
nodeListFunc: func() ([]swarm.Node, error) {
|
||||
return []swarm.Node{
|
||||
*Node(),
|
||||
}, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{})
|
||||
cmd := newListCommand(cli)
|
||||
cmd.Flags().Set("quiet", "true")
|
||||
assert.NoError(t, cmd.Execute())
|
||||
@ -99,8 +96,7 @@ func TestNodeListQuietShouldOnlyPrintIDs(t *testing.T) {
|
||||
// Test case for #24090
|
||||
func TestNodeListContainsHostname(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{})
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{}, buf)
|
||||
cmd := newListCommand(cli)
|
||||
assert.NoError(t, cmd.Execute())
|
||||
assert.Contains(t, buf.String(), "HOSTNAME")
|
||||
@ -108,7 +104,7 @@ func TestNodeListContainsHostname(t *testing.T) {
|
||||
|
||||
func TestNodeListDefaultFormat(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
nodeListFunc: func() ([]swarm.Node, error) {
|
||||
return []swarm.Node{
|
||||
*Node(NodeID("nodeID1"), Hostname("nodeHostname1"), Manager(Leader())),
|
||||
@ -124,7 +120,7 @@ func TestNodeListDefaultFormat(t *testing.T) {
|
||||
}, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{
|
||||
cli.SetConfigFile(&configfile.ConfigFile{
|
||||
NodesFormat: "{{.ID}}: {{.Hostname}} {{.Status}}/{{.ManagerStatus}}",
|
||||
})
|
||||
cmd := newListCommand(cli)
|
||||
@ -136,7 +132,7 @@ func TestNodeListDefaultFormat(t *testing.T) {
|
||||
|
||||
func TestNodeListFormat(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
nodeListFunc: func() ([]swarm.Node, error) {
|
||||
return []swarm.Node{
|
||||
*Node(NodeID("nodeID1"), Hostname("nodeHostname1"), Manager(Leader())),
|
||||
@ -151,7 +147,7 @@ func TestNodeListFormat(t *testing.T) {
|
||||
}, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{
|
||||
cli.SetConfigFile(&configfile.ConfigFile{
|
||||
NodesFormat: "{{.ID}}: {{.Hostname}} {{.Status}}/{{.ManagerStatus}}",
|
||||
})
|
||||
cmd := newListCommand(cli)
|
||||
|
||||
@ -42,7 +42,7 @@ func TestNodePromoteErrors(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newPromoteCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
nodeInspectFunc: tc.nodeInspectFunc,
|
||||
nodeUpdateFunc: tc.nodeUpdateFunc,
|
||||
}, buf))
|
||||
@ -55,7 +55,7 @@ func TestNodePromoteErrors(t *testing.T) {
|
||||
func TestNodePromoteNoChange(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newPromoteCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return *Node(Manager()), []byte{}, nil
|
||||
},
|
||||
@ -73,7 +73,7 @@ func TestNodePromoteNoChange(t *testing.T) {
|
||||
func TestNodePromoteMultipleNode(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newPromoteCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return *Node(), []byte{}, nil
|
||||
},
|
||||
|
||||
@ -5,7 +5,6 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
"github.com/docker/cli/cli/command/idresolver"
|
||||
"github.com/docker/cli/cli/command/task"
|
||||
"github.com/docker/cli/opts"
|
||||
@ -88,11 +87,7 @@ func runPs(dockerCli command.Cli, options psOptions) error {
|
||||
|
||||
format := options.format
|
||||
if len(format) == 0 {
|
||||
if dockerCli.ConfigFile() != nil && len(dockerCli.ConfigFile().TasksFormat) > 0 && !options.quiet {
|
||||
format = dockerCli.ConfigFile().TasksFormat
|
||||
} else {
|
||||
format = formatter.TableFormatKey
|
||||
}
|
||||
format = task.DefaultFormat(dockerCli.ConfigFile(), options.quiet)
|
||||
}
|
||||
|
||||
if len(errs) == 0 || len(tasks) != 0 {
|
||||
|
||||
@ -52,7 +52,7 @@ func TestNodePsErrors(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newPsCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
infoFunc: tc.infoFunc,
|
||||
nodeInspectFunc: tc.nodeInspectFunc,
|
||||
taskInspectFunc: tc.taskInspectFunc,
|
||||
@ -116,7 +116,7 @@ func TestNodePs(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newPsCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
infoFunc: tc.infoFunc,
|
||||
nodeInspectFunc: tc.nodeInspectFunc,
|
||||
taskInspectFunc: tc.taskInspectFunc,
|
||||
|
||||
@ -31,7 +31,7 @@ func TestNodeRemoveErrors(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newRemoveCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
nodeRemoveFunc: tc.nodeRemoveFunc,
|
||||
}, buf))
|
||||
cmd.SetArgs(tc.args)
|
||||
@ -42,7 +42,7 @@ func TestNodeRemoveErrors(t *testing.T) {
|
||||
|
||||
func TestNodeRemoveMultiple(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newRemoveCommand(test.NewFakeCli(&fakeClient{}, buf))
|
||||
cmd := newRemoveCommand(test.NewFakeCliWithOutput(&fakeClient{}, buf))
|
||||
cmd.SetArgs([]string{"nodeID1", "nodeID2"})
|
||||
assert.NoError(t, cmd.Execute())
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
@ -57,12 +56,11 @@ func TestNodeUpdateErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newUpdateCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
nodeInspectFunc: tc.nodeInspectFunc,
|
||||
nodeUpdateFunc: tc.nodeUpdateFunc,
|
||||
}, buf))
|
||||
}))
|
||||
cmd.SetArgs(tc.args)
|
||||
for key, value := range tc.flags {
|
||||
cmd.Flags().Set(key, value)
|
||||
@ -158,12 +156,11 @@ func TestNodeUpdate(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newUpdateCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
nodeInspectFunc: tc.nodeInspectFunc,
|
||||
nodeUpdateFunc: tc.nodeUpdateFunc,
|
||||
}, buf))
|
||||
}))
|
||||
cmd.SetArgs(tc.args)
|
||||
for key, value := range tc.flags {
|
||||
cmd.Flags().Set(key, value)
|
||||
|
||||
@ -7,29 +7,8 @@ import (
|
||||
"github.com/docker/cli/cli/command/network"
|
||||
"github.com/docker/cli/cli/command/volume"
|
||||
"github.com/docker/cli/opts"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewContainerPruneCommand returns a cobra prune command for containers
|
||||
func NewContainerPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return container.NewPruneCommand(dockerCli)
|
||||
}
|
||||
|
||||
// NewVolumePruneCommand returns a cobra prune command for volumes
|
||||
func NewVolumePruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return volume.NewPruneCommand(dockerCli)
|
||||
}
|
||||
|
||||
// NewImagePruneCommand returns a cobra prune command for images
|
||||
func NewImagePruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return image.NewPruneCommand(dockerCli)
|
||||
}
|
||||
|
||||
// NewNetworkPruneCommand returns a cobra prune command for Networks
|
||||
func NewNetworkPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return network.NewPruneCommand(dockerCli)
|
||||
}
|
||||
|
||||
// RunContainerPrune executes a prune command for containers
|
||||
func RunContainerPrune(dockerCli command.Cli, filter opts.FilterOpt) (uint64, string, error) {
|
||||
return container.RunPrune(dockerCli, filter)
|
||||
|
||||
@ -2,6 +2,8 @@ package registry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
@ -16,6 +18,7 @@ type loginOptions struct {
|
||||
serverAddress string
|
||||
user string
|
||||
password string
|
||||
passwordStdin bool
|
||||
}
|
||||
|
||||
// NewLoginCommand creates a new `docker login` command
|
||||
@ -39,6 +42,7 @@ func NewLoginCommand(dockerCli command.Cli) *cobra.Command {
|
||||
|
||||
flags.StringVarP(&opts.user, "username", "u", "", "Username")
|
||||
flags.StringVarP(&opts.password, "password", "p", "", "Password")
|
||||
flags.BoolVarP(&opts.passwordStdin, "password-stdin", "", false, "Take the password from stdin")
|
||||
|
||||
return cmd
|
||||
}
|
||||
@ -47,6 +51,27 @@ func runLogin(dockerCli command.Cli, opts loginOptions) error {
|
||||
ctx := context.Background()
|
||||
clnt := dockerCli.Client()
|
||||
|
||||
if opts.password != "" {
|
||||
fmt.Fprintln(dockerCli.Err(), "WARNING! Using --password via the CLI is insecure. Use --password-stdin.")
|
||||
if opts.passwordStdin {
|
||||
return errors.New("--password and --password-stdin are mutually exclusive")
|
||||
}
|
||||
}
|
||||
|
||||
if opts.passwordStdin {
|
||||
if opts.user == "" {
|
||||
return errors.New("Must provide --username with --password-stdin")
|
||||
}
|
||||
|
||||
contents, err := ioutil.ReadAll(dockerCli.In())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
opts.password = strings.TrimSuffix(string(contents), "\n")
|
||||
opts.password = strings.TrimSuffix(opts.password, "\r")
|
||||
}
|
||||
|
||||
var (
|
||||
serverAddress string
|
||||
authServer = command.ElectAuthServer(ctx, dockerCli)
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package command_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@ -63,13 +62,10 @@ func TestElectAuthServer(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{infoFunc: tc.infoFunc}, buf)
|
||||
errBuf := new(bytes.Buffer)
|
||||
cli.SetErr(errBuf)
|
||||
cli := test.NewFakeCli(&fakeClient{infoFunc: tc.infoFunc})
|
||||
server := ElectAuthServer(context.Background(), cli)
|
||||
assert.Equal(t, tc.expectedAuthServer, server)
|
||||
actual := errBuf.String()
|
||||
actual := cli.ErrBuffer().String()
|
||||
if tc.expectedWarning == "" {
|
||||
assert.Empty(t, actual)
|
||||
} else {
|
||||
|
||||
@ -41,11 +41,10 @@ func TestSecretCreateErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newSecretCreateCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
secretCreateFunc: tc.secretCreateFunc,
|
||||
}, buf),
|
||||
}),
|
||||
)
|
||||
cmd.SetArgs(tc.args)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
@ -57,7 +56,7 @@ func TestSecretCreateWithName(t *testing.T) {
|
||||
name := "foo"
|
||||
buf := new(bytes.Buffer)
|
||||
var actual []byte
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
secretCreateFunc: func(spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
|
||||
if spec.Name != name {
|
||||
return types.SecretCreateResponse{}, errors.Errorf("expected name %q, got %q", name, spec.Name)
|
||||
@ -87,7 +86,7 @@ func TestSecretCreateWithLabels(t *testing.T) {
|
||||
name := "foo"
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
secretCreateFunc: func(spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
|
||||
if spec.Name != name {
|
||||
return types.SecretCreateResponse{}, errors.Errorf("expected name %q, got %q", name, spec.Name)
|
||||
|
||||
@ -55,7 +55,7 @@ func TestSecretInspectErrors(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newSecretInspectCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
secretInspectFunc: tc.secretInspectFunc,
|
||||
}, buf),
|
||||
)
|
||||
@ -97,7 +97,7 @@ func TestSecretInspectWithoutFormat(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newSecretInspectCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
secretInspectFunc: tc.secretInspectFunc,
|
||||
}, buf),
|
||||
)
|
||||
@ -137,7 +137,7 @@ func TestSecretInspectWithFormat(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newSecretInspectCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
secretInspectFunc: tc.secretInspectFunc,
|
||||
}, buf),
|
||||
)
|
||||
@ -173,7 +173,7 @@ func TestSecretInspectPretty(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newSecretInspectCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
secretInspectFunc: tc.secretInspectFunc,
|
||||
}, buf))
|
||||
cmd.SetArgs([]string{"secretID"})
|
||||
|
||||
@ -38,7 +38,7 @@ func TestSecretListErrors(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newSecretListCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
secretListFunc: tc.secretListFunc,
|
||||
}, buf),
|
||||
)
|
||||
@ -50,7 +50,7 @@ func TestSecretListErrors(t *testing.T) {
|
||||
|
||||
func TestSecretList(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) {
|
||||
return []swarm.Secret{
|
||||
*Secret(SecretID("ID-foo"),
|
||||
@ -68,7 +68,6 @@ func TestSecretList(t *testing.T) {
|
||||
}, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{})
|
||||
cmd := newSecretListCommand(cli)
|
||||
cmd.SetOutput(buf)
|
||||
assert.NoError(t, cmd.Execute())
|
||||
@ -79,7 +78,7 @@ func TestSecretList(t *testing.T) {
|
||||
|
||||
func TestSecretListWithQuietOption(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) {
|
||||
return []swarm.Secret{
|
||||
*Secret(SecretID("ID-foo"), SecretName("foo")),
|
||||
@ -89,7 +88,6 @@ func TestSecretListWithQuietOption(t *testing.T) {
|
||||
}, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{})
|
||||
cmd := newSecretListCommand(cli)
|
||||
cmd.Flags().Set("quiet", "true")
|
||||
assert.NoError(t, cmd.Execute())
|
||||
@ -100,7 +98,7 @@ func TestSecretListWithQuietOption(t *testing.T) {
|
||||
|
||||
func TestSecretListWithConfigFormat(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) {
|
||||
return []swarm.Secret{
|
||||
*Secret(SecretID("ID-foo"), SecretName("foo")),
|
||||
@ -110,7 +108,7 @@ func TestSecretListWithConfigFormat(t *testing.T) {
|
||||
}, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{
|
||||
cli.SetConfigFile(&configfile.ConfigFile{
|
||||
SecretFormat: "{{ .Name }} {{ .Labels }}",
|
||||
})
|
||||
cmd := newSecretListCommand(cli)
|
||||
@ -122,7 +120,7 @@ func TestSecretListWithConfigFormat(t *testing.T) {
|
||||
|
||||
func TestSecretListWithFormat(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) {
|
||||
return []swarm.Secret{
|
||||
*Secret(SecretID("ID-foo"), SecretName("foo")),
|
||||
@ -142,7 +140,7 @@ func TestSecretListWithFormat(t *testing.T) {
|
||||
|
||||
func TestSecretListWithFilter(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) {
|
||||
assert.Equal(t, "foo", options.Filters.Get("name")[0], "foo")
|
||||
assert.Equal(t, "lbl1=Label-bar", options.Filters.Get("label")[0])
|
||||
@ -162,7 +160,6 @@ func TestSecretListWithFilter(t *testing.T) {
|
||||
}, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{})
|
||||
cmd := newSecretListCommand(cli)
|
||||
cmd.Flags().Set("filter", "name=foo")
|
||||
cmd.Flags().Set("filter", "label=lbl1=Label-bar")
|
||||
|
||||
@ -33,7 +33,7 @@ func TestSecretRemoveErrors(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newSecretRemoveCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
secretRemoveFunc: tc.secretRemoveFunc,
|
||||
}, buf),
|
||||
)
|
||||
@ -47,7 +47,7 @@ func TestSecretRemoveWithName(t *testing.T) {
|
||||
names := []string{"foo", "bar"}
|
||||
buf := new(bytes.Buffer)
|
||||
var removedSecrets []string
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
secretRemoveFunc: func(name string) error {
|
||||
removedSecrets = append(removedSecrets, name)
|
||||
return nil
|
||||
@ -65,7 +65,7 @@ func TestSecretRemoveContinueAfterError(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
var removedSecrets []string
|
||||
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
secretRemoveFunc: func(name string) error {
|
||||
removedSecrets = append(removedSecrets, name)
|
||||
if name == "foo" {
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
@ -29,6 +30,13 @@ var (
|
||||
swarm.TaskStateReady: 7,
|
||||
swarm.TaskStateStarting: 8,
|
||||
swarm.TaskStateRunning: 9,
|
||||
|
||||
// The following states are not actually shown in progress
|
||||
// output, but are used internally for ordering.
|
||||
swarm.TaskStateComplete: 10,
|
||||
swarm.TaskStateShutdown: 11,
|
||||
swarm.TaskStateFailed: 12,
|
||||
swarm.TaskStateRejected: 13,
|
||||
}
|
||||
|
||||
longestState int
|
||||
@ -40,22 +48,26 @@ const (
|
||||
)
|
||||
|
||||
type progressUpdater interface {
|
||||
update(service swarm.Service, tasks []swarm.Task, activeNodes map[string]swarm.Node, rollback bool) (bool, error)
|
||||
update(service swarm.Service, tasks []swarm.Task, activeNodes map[string]struct{}, rollback bool) (bool, error)
|
||||
}
|
||||
|
||||
func init() {
|
||||
for state := range numberedStates {
|
||||
if len(state) > longestState {
|
||||
if !terminalState(state) && len(state) > longestState {
|
||||
longestState = len(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func terminalState(state swarm.TaskState) bool {
|
||||
return numberedStates[state] > numberedStates[swarm.TaskStateRunning]
|
||||
}
|
||||
|
||||
func stateToProgress(state swarm.TaskState, rollback bool) int64 {
|
||||
if !rollback {
|
||||
return numberedStates[state]
|
||||
}
|
||||
return int64(len(numberedStates)) - numberedStates[state]
|
||||
return numberedStates[swarm.TaskStateRunning] - numberedStates[state]
|
||||
}
|
||||
|
||||
// ServiceProgress outputs progress information for convergence of a service.
|
||||
@ -192,16 +204,16 @@ func ServiceProgress(ctx context.Context, client client.APIClient, serviceID str
|
||||
}
|
||||
}
|
||||
|
||||
func getActiveNodes(ctx context.Context, client client.APIClient) (map[string]swarm.Node, error) {
|
||||
func getActiveNodes(ctx context.Context, client client.APIClient) (map[string]struct{}, error) {
|
||||
nodes, err := client.NodeList(ctx, types.NodeListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
activeNodes := make(map[string]swarm.Node)
|
||||
activeNodes := make(map[string]struct{})
|
||||
for _, n := range nodes {
|
||||
if n.Status.State != swarm.NodeStateDown {
|
||||
activeNodes[n.ID] = n
|
||||
activeNodes[n.ID] = struct{}{}
|
||||
}
|
||||
}
|
||||
return activeNodes, nil
|
||||
@ -235,6 +247,18 @@ func writeOverallProgress(progressOut progress.Output, numerator, denominator in
|
||||
})
|
||||
}
|
||||
|
||||
func truncError(errMsg string) string {
|
||||
// Remove newlines from the error, which corrupt the output.
|
||||
errMsg = strings.Replace(errMsg, "\n", " ", -1)
|
||||
|
||||
// Limit the length to 75 characters, so that even on narrow terminals
|
||||
// this will not overflow to the next line.
|
||||
if len(errMsg) > 75 {
|
||||
errMsg = errMsg[:74] + "…"
|
||||
}
|
||||
return errMsg
|
||||
}
|
||||
|
||||
type replicatedProgressUpdater struct {
|
||||
progressOut progress.Output
|
||||
|
||||
@ -246,8 +270,7 @@ type replicatedProgressUpdater struct {
|
||||
done bool
|
||||
}
|
||||
|
||||
// nolint: gocyclo
|
||||
func (u *replicatedProgressUpdater) update(service swarm.Service, tasks []swarm.Task, activeNodes map[string]swarm.Node, rollback bool) (bool, error) {
|
||||
func (u *replicatedProgressUpdater) update(service swarm.Service, tasks []swarm.Task, activeNodes map[string]struct{}, rollback bool) (bool, error) {
|
||||
if service.Spec.Mode.Replicated == nil || service.Spec.Mode.Replicated.Replicas == nil {
|
||||
return false, errors.New("no replica count")
|
||||
}
|
||||
@ -267,27 +290,7 @@ func (u *replicatedProgressUpdater) update(service swarm.Service, tasks []swarm.
|
||||
u.initialized = true
|
||||
}
|
||||
|
||||
// If there are multiple tasks with the same slot number, favor the one
|
||||
// with the *lowest* desired state. This can happen in restart
|
||||
// scenarios.
|
||||
tasksBySlot := make(map[int]swarm.Task)
|
||||
for _, task := range tasks {
|
||||
if numberedStates[task.DesiredState] == 0 {
|
||||
continue
|
||||
}
|
||||
if existingTask, ok := tasksBySlot[task.Slot]; ok {
|
||||
if numberedStates[existingTask.DesiredState] <= numberedStates[task.DesiredState] {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if task.NodeID != "" {
|
||||
if _, nodeActive := activeNodes[task.NodeID]; nodeActive {
|
||||
tasksBySlot[task.Slot] = task
|
||||
}
|
||||
} else {
|
||||
tasksBySlot[task.Slot] = task
|
||||
}
|
||||
}
|
||||
tasksBySlot := u.tasksBySlot(tasks, activeNodes)
|
||||
|
||||
// If we had reached a converged state, check if we are still converged.
|
||||
if u.done {
|
||||
@ -308,18 +311,11 @@ func (u *replicatedProgressUpdater) update(service swarm.Service, tasks []swarm.
|
||||
u.slotMap[task.Slot] = mappedSlot
|
||||
}
|
||||
|
||||
if !u.done && replicas <= maxProgressBars && uint64(mappedSlot) <= replicas {
|
||||
u.progressOut.WriteProgress(progress.Progress{
|
||||
ID: fmt.Sprintf("%d/%d", mappedSlot, replicas),
|
||||
Action: fmt.Sprintf("%-[1]*s", longestState, task.Status.State),
|
||||
Current: stateToProgress(task.Status.State, rollback),
|
||||
Total: maxProgress,
|
||||
HideCounts: true,
|
||||
})
|
||||
}
|
||||
if task.Status.State == swarm.TaskStateRunning {
|
||||
if !terminalState(task.DesiredState) && task.Status.State == swarm.TaskStateRunning {
|
||||
running++
|
||||
}
|
||||
|
||||
u.writeTaskProgress(task, mappedSlot, replicas, rollback)
|
||||
}
|
||||
|
||||
if !u.done {
|
||||
@ -333,6 +329,62 @@ func (u *replicatedProgressUpdater) update(service swarm.Service, tasks []swarm.
|
||||
return running == replicas, nil
|
||||
}
|
||||
|
||||
func (u *replicatedProgressUpdater) tasksBySlot(tasks []swarm.Task, activeNodes map[string]struct{}) map[int]swarm.Task {
|
||||
// If there are multiple tasks with the same slot number, favor the one
|
||||
// with the *lowest* desired state. This can happen in restart
|
||||
// scenarios.
|
||||
tasksBySlot := make(map[int]swarm.Task)
|
||||
for _, task := range tasks {
|
||||
if numberedStates[task.DesiredState] == 0 || numberedStates[task.Status.State] == 0 {
|
||||
continue
|
||||
}
|
||||
if existingTask, ok := tasksBySlot[task.Slot]; ok {
|
||||
if numberedStates[existingTask.DesiredState] < numberedStates[task.DesiredState] {
|
||||
continue
|
||||
}
|
||||
// If the desired states match, observed state breaks
|
||||
// ties. This can happen with the "start first" service
|
||||
// update mode.
|
||||
if numberedStates[existingTask.DesiredState] == numberedStates[task.DesiredState] &&
|
||||
numberedStates[existingTask.Status.State] <= numberedStates[task.Status.State] {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if task.NodeID != "" {
|
||||
if _, nodeActive := activeNodes[task.NodeID]; !nodeActive {
|
||||
continue
|
||||
}
|
||||
}
|
||||
tasksBySlot[task.Slot] = task
|
||||
}
|
||||
|
||||
return tasksBySlot
|
||||
}
|
||||
|
||||
func (u *replicatedProgressUpdater) writeTaskProgress(task swarm.Task, mappedSlot int, replicas uint64, rollback bool) {
|
||||
if u.done || replicas > maxProgressBars || uint64(mappedSlot) > replicas {
|
||||
return
|
||||
}
|
||||
|
||||
if task.Status.Err != "" {
|
||||
u.progressOut.WriteProgress(progress.Progress{
|
||||
ID: fmt.Sprintf("%d/%d", mappedSlot, replicas),
|
||||
Action: truncError(task.Status.Err),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if !terminalState(task.DesiredState) && !terminalState(task.Status.State) {
|
||||
u.progressOut.WriteProgress(progress.Progress{
|
||||
ID: fmt.Sprintf("%d/%d", mappedSlot, replicas),
|
||||
Action: fmt.Sprintf("%-[1]*s", longestState, task.Status.State),
|
||||
Current: stateToProgress(task.Status.State, rollback),
|
||||
Total: maxProgress,
|
||||
HideCounts: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type globalProgressUpdater struct {
|
||||
progressOut progress.Output
|
||||
|
||||
@ -340,22 +392,8 @@ type globalProgressUpdater struct {
|
||||
done bool
|
||||
}
|
||||
|
||||
func (u *globalProgressUpdater) update(service swarm.Service, tasks []swarm.Task, activeNodes map[string]swarm.Node, rollback bool) (bool, error) {
|
||||
// If there are multiple tasks with the same node ID, favor the one
|
||||
// with the *lowest* desired state. This can happen in restart
|
||||
// scenarios.
|
||||
tasksByNode := make(map[string]swarm.Task)
|
||||
for _, task := range tasks {
|
||||
if numberedStates[task.DesiredState] == 0 {
|
||||
continue
|
||||
}
|
||||
if existingTask, ok := tasksByNode[task.NodeID]; ok {
|
||||
if numberedStates[existingTask.DesiredState] <= numberedStates[task.DesiredState] {
|
||||
continue
|
||||
}
|
||||
}
|
||||
tasksByNode[task.NodeID] = task
|
||||
}
|
||||
func (u *globalProgressUpdater) update(service swarm.Service, tasks []swarm.Task, activeNodes map[string]struct{}, rollback bool) (bool, error) {
|
||||
tasksByNode := u.tasksByNode(tasks)
|
||||
|
||||
// We don't have perfect knowledge of how many nodes meet the
|
||||
// constraints for this service. But the orchestrator creates tasks
|
||||
@ -392,19 +430,12 @@ func (u *globalProgressUpdater) update(service swarm.Service, tasks []swarm.Task
|
||||
running := 0
|
||||
|
||||
for _, task := range tasksByNode {
|
||||
if node, nodeActive := activeNodes[task.NodeID]; nodeActive {
|
||||
if !u.done && nodeCount <= maxProgressBars {
|
||||
u.progressOut.WriteProgress(progress.Progress{
|
||||
ID: stringid.TruncateID(node.ID),
|
||||
Action: fmt.Sprintf("%-[1]*s", longestState, task.Status.State),
|
||||
Current: stateToProgress(task.Status.State, rollback),
|
||||
Total: maxProgress,
|
||||
HideCounts: true,
|
||||
})
|
||||
}
|
||||
if task.Status.State == swarm.TaskStateRunning {
|
||||
if _, nodeActive := activeNodes[task.NodeID]; nodeActive {
|
||||
if !terminalState(task.DesiredState) && task.Status.State == swarm.TaskStateRunning {
|
||||
running++
|
||||
}
|
||||
|
||||
u.writeTaskProgress(task, nodeCount, rollback)
|
||||
}
|
||||
}
|
||||
|
||||
@ -418,3 +449,56 @@ func (u *globalProgressUpdater) update(service swarm.Service, tasks []swarm.Task
|
||||
|
||||
return running == nodeCount, nil
|
||||
}
|
||||
|
||||
func (u *globalProgressUpdater) tasksByNode(tasks []swarm.Task) map[string]swarm.Task {
|
||||
// If there are multiple tasks with the same node ID, favor the one
|
||||
// with the *lowest* desired state. This can happen in restart
|
||||
// scenarios.
|
||||
tasksByNode := make(map[string]swarm.Task)
|
||||
for _, task := range tasks {
|
||||
if numberedStates[task.DesiredState] == 0 || numberedStates[task.Status.State] == 0 {
|
||||
continue
|
||||
}
|
||||
if existingTask, ok := tasksByNode[task.NodeID]; ok {
|
||||
if numberedStates[existingTask.DesiredState] < numberedStates[task.DesiredState] {
|
||||
continue
|
||||
}
|
||||
|
||||
// If the desired states match, observed state breaks
|
||||
// ties. This can happen with the "start first" service
|
||||
// update mode.
|
||||
if numberedStates[existingTask.DesiredState] == numberedStates[task.DesiredState] &&
|
||||
numberedStates[existingTask.Status.State] <= numberedStates[task.Status.State] {
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
tasksByNode[task.NodeID] = task
|
||||
}
|
||||
|
||||
return tasksByNode
|
||||
}
|
||||
|
||||
func (u *globalProgressUpdater) writeTaskProgress(task swarm.Task, nodeCount int, rollback bool) {
|
||||
if u.done || nodeCount > maxProgressBars {
|
||||
return
|
||||
}
|
||||
|
||||
if task.Status.Err != "" {
|
||||
u.progressOut.WriteProgress(progress.Progress{
|
||||
ID: stringid.TruncateID(task.NodeID),
|
||||
Action: truncError(task.Status.Err),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if !terminalState(task.DesiredState) && !terminalState(task.Status.State) {
|
||||
u.progressOut.WriteProgress(progress.Progress{
|
||||
ID: stringid.TruncateID(task.NodeID),
|
||||
Action: fmt.Sprintf("%-[1]*s", longestState, task.Status.State),
|
||||
Current: stateToProgress(task.Status.State, rollback),
|
||||
Total: maxProgress,
|
||||
HideCounts: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
374
components/cli/cli/command/service/progress/progress_test.go
Normal file
374
components/cli/cli/command/service/progress/progress_test.go
Normal file
@ -0,0 +1,374 @@
|
||||
package progress
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/pkg/progress"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type mockProgress struct {
|
||||
p []progress.Progress
|
||||
}
|
||||
|
||||
func (mp *mockProgress) WriteProgress(p progress.Progress) error {
|
||||
mp.p = append(mp.p, p)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mp *mockProgress) clear() {
|
||||
mp.p = nil
|
||||
}
|
||||
|
||||
type updaterTester struct {
|
||||
t *testing.T
|
||||
updater progressUpdater
|
||||
p *mockProgress
|
||||
service swarm.Service
|
||||
activeNodes map[string]struct{}
|
||||
rollback bool
|
||||
}
|
||||
|
||||
func (u updaterTester) testUpdater(tasks []swarm.Task, expectedConvergence bool, expectedProgress []progress.Progress) {
|
||||
u.p.clear()
|
||||
|
||||
converged, err := u.updater.update(u.service, tasks, u.activeNodes, u.rollback)
|
||||
assert.NoError(u.t, err)
|
||||
assert.Equal(u.t, expectedConvergence, converged)
|
||||
assert.Equal(u.t, expectedProgress, u.p.p)
|
||||
}
|
||||
|
||||
func TestReplicatedProgressUpdaterOneReplica(t *testing.T) {
|
||||
replicas := uint64(1)
|
||||
|
||||
service := swarm.Service{
|
||||
Spec: swarm.ServiceSpec{
|
||||
Mode: swarm.ServiceMode{
|
||||
Replicated: &swarm.ReplicatedService{
|
||||
Replicas: &replicas,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
p := &mockProgress{}
|
||||
updaterTester := updaterTester{
|
||||
t: t,
|
||||
updater: &replicatedProgressUpdater{
|
||||
progressOut: p,
|
||||
},
|
||||
p: p,
|
||||
activeNodes: map[string]struct{}{"a": {}, "b": {}},
|
||||
service: service,
|
||||
}
|
||||
|
||||
tasks := []swarm.Task{}
|
||||
|
||||
updaterTester.testUpdater(tasks, false,
|
||||
[]progress.Progress{
|
||||
{ID: "overall progress", Action: "0 out of 1 tasks"},
|
||||
{ID: "1/1", Action: " "},
|
||||
{ID: "overall progress", Action: "0 out of 1 tasks"},
|
||||
})
|
||||
|
||||
// Task with DesiredState beyond Running is ignored
|
||||
tasks = append(tasks,
|
||||
swarm.Task{ID: "1",
|
||||
NodeID: "a",
|
||||
DesiredState: swarm.TaskStateShutdown,
|
||||
Status: swarm.TaskStatus{State: swarm.TaskStateNew},
|
||||
})
|
||||
updaterTester.testUpdater(tasks, false,
|
||||
[]progress.Progress{
|
||||
{ID: "overall progress", Action: "0 out of 1 tasks"},
|
||||
})
|
||||
|
||||
// Task with valid DesiredState and State updates progress bar
|
||||
tasks[0].DesiredState = swarm.TaskStateRunning
|
||||
updaterTester.testUpdater(tasks, false,
|
||||
[]progress.Progress{
|
||||
{ID: "1/1", Action: "new ", Current: 1, Total: 9, HideCounts: true},
|
||||
{ID: "overall progress", Action: "0 out of 1 tasks"},
|
||||
})
|
||||
|
||||
// If the task exposes an error, we should show that instead of the
|
||||
// progress bar.
|
||||
tasks[0].Status.Err = "something is wrong"
|
||||
updaterTester.testUpdater(tasks, false,
|
||||
[]progress.Progress{
|
||||
{ID: "1/1", Action: "something is wrong"},
|
||||
{ID: "overall progress", Action: "0 out of 1 tasks"},
|
||||
})
|
||||
|
||||
// When the task reaches running, update should return true
|
||||
tasks[0].Status.Err = ""
|
||||
tasks[0].Status.State = swarm.TaskStateRunning
|
||||
updaterTester.testUpdater(tasks, true,
|
||||
[]progress.Progress{
|
||||
{ID: "1/1", Action: "running ", Current: 9, Total: 9, HideCounts: true},
|
||||
{ID: "overall progress", Action: "1 out of 1 tasks"},
|
||||
})
|
||||
|
||||
// If the task fails, update should return false again
|
||||
tasks[0].Status.Err = "task failed"
|
||||
tasks[0].Status.State = swarm.TaskStateFailed
|
||||
updaterTester.testUpdater(tasks, false,
|
||||
[]progress.Progress{
|
||||
{ID: "1/1", Action: "task failed"},
|
||||
{ID: "overall progress", Action: "0 out of 1 tasks"},
|
||||
})
|
||||
|
||||
// If the task is restarted, progress output should be shown for the
|
||||
// replacement task, not the old task.
|
||||
tasks[0].DesiredState = swarm.TaskStateShutdown
|
||||
tasks = append(tasks,
|
||||
swarm.Task{ID: "2",
|
||||
NodeID: "b",
|
||||
DesiredState: swarm.TaskStateRunning,
|
||||
Status: swarm.TaskStatus{State: swarm.TaskStateRunning},
|
||||
})
|
||||
updaterTester.testUpdater(tasks, true,
|
||||
[]progress.Progress{
|
||||
{ID: "1/1", Action: "running ", Current: 9, Total: 9, HideCounts: true},
|
||||
{ID: "overall progress", Action: "1 out of 1 tasks"},
|
||||
})
|
||||
|
||||
// Add a new task while the current one is still running, to simulate
|
||||
// "start-then-stop" updates.
|
||||
tasks = append(tasks,
|
||||
swarm.Task{ID: "3",
|
||||
NodeID: "b",
|
||||
DesiredState: swarm.TaskStateRunning,
|
||||
Status: swarm.TaskStatus{State: swarm.TaskStatePreparing},
|
||||
})
|
||||
updaterTester.testUpdater(tasks, false,
|
||||
[]progress.Progress{
|
||||
{ID: "1/1", Action: "preparing", Current: 6, Total: 9, HideCounts: true},
|
||||
{ID: "overall progress", Action: "0 out of 1 tasks"},
|
||||
})
|
||||
}
|
||||
|
||||
func TestReplicatedProgressUpdaterManyReplicas(t *testing.T) {
|
||||
replicas := uint64(50)
|
||||
|
||||
service := swarm.Service{
|
||||
Spec: swarm.ServiceSpec{
|
||||
Mode: swarm.ServiceMode{
|
||||
Replicated: &swarm.ReplicatedService{
|
||||
Replicas: &replicas,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
p := &mockProgress{}
|
||||
updaterTester := updaterTester{
|
||||
t: t,
|
||||
updater: &replicatedProgressUpdater{
|
||||
progressOut: p,
|
||||
},
|
||||
p: p,
|
||||
activeNodes: map[string]struct{}{"a": {}, "b": {}},
|
||||
service: service,
|
||||
}
|
||||
|
||||
tasks := []swarm.Task{}
|
||||
|
||||
// No per-task progress bars because there are too many replicas
|
||||
updaterTester.testUpdater(tasks, false,
|
||||
[]progress.Progress{
|
||||
{ID: "overall progress", Action: fmt.Sprintf("0 out of %d tasks", replicas)},
|
||||
{ID: "overall progress", Action: fmt.Sprintf("0 out of %d tasks", replicas)},
|
||||
})
|
||||
|
||||
for i := 0; i != int(replicas); i++ {
|
||||
tasks = append(tasks,
|
||||
swarm.Task{
|
||||
ID: strconv.Itoa(i),
|
||||
Slot: i + 1,
|
||||
NodeID: "a",
|
||||
DesiredState: swarm.TaskStateRunning,
|
||||
Status: swarm.TaskStatus{State: swarm.TaskStateNew},
|
||||
})
|
||||
|
||||
if i%2 == 1 {
|
||||
tasks[i].NodeID = "b"
|
||||
}
|
||||
updaterTester.testUpdater(tasks, false,
|
||||
[]progress.Progress{
|
||||
{ID: "overall progress", Action: fmt.Sprintf("%d out of %d tasks", i, replicas)},
|
||||
})
|
||||
|
||||
tasks[i].Status.State = swarm.TaskStateRunning
|
||||
updaterTester.testUpdater(tasks, uint64(i) == replicas-1,
|
||||
[]progress.Progress{
|
||||
{ID: "overall progress", Action: fmt.Sprintf("%d out of %d tasks", i+1, replicas)},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGlobalProgressUpdaterOneNode(t *testing.T) {
|
||||
service := swarm.Service{
|
||||
Spec: swarm.ServiceSpec{
|
||||
Mode: swarm.ServiceMode{
|
||||
Global: &swarm.GlobalService{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
p := &mockProgress{}
|
||||
updaterTester := updaterTester{
|
||||
t: t,
|
||||
updater: &globalProgressUpdater{
|
||||
progressOut: p,
|
||||
},
|
||||
p: p,
|
||||
activeNodes: map[string]struct{}{"a": {}, "b": {}},
|
||||
service: service,
|
||||
}
|
||||
|
||||
tasks := []swarm.Task{}
|
||||
|
||||
updaterTester.testUpdater(tasks, false,
|
||||
[]progress.Progress{
|
||||
{ID: "overall progress", Action: "waiting for new tasks"},
|
||||
})
|
||||
|
||||
// Task with DesiredState beyond Running is ignored
|
||||
tasks = append(tasks,
|
||||
swarm.Task{ID: "1",
|
||||
NodeID: "a",
|
||||
DesiredState: swarm.TaskStateShutdown,
|
||||
Status: swarm.TaskStatus{State: swarm.TaskStateNew},
|
||||
})
|
||||
updaterTester.testUpdater(tasks, false,
|
||||
[]progress.Progress{
|
||||
{ID: "overall progress", Action: "0 out of 1 tasks"},
|
||||
{ID: "overall progress", Action: "0 out of 1 tasks"},
|
||||
})
|
||||
|
||||
// Task with valid DesiredState and State updates progress bar
|
||||
tasks[0].DesiredState = swarm.TaskStateRunning
|
||||
updaterTester.testUpdater(tasks, false,
|
||||
[]progress.Progress{
|
||||
{ID: "a", Action: "new ", Current: 1, Total: 9, HideCounts: true},
|
||||
{ID: "overall progress", Action: "0 out of 1 tasks"},
|
||||
})
|
||||
|
||||
// If the task exposes an error, we should show that instead of the
|
||||
// progress bar.
|
||||
tasks[0].Status.Err = "something is wrong"
|
||||
updaterTester.testUpdater(tasks, false,
|
||||
[]progress.Progress{
|
||||
{ID: "a", Action: "something is wrong"},
|
||||
{ID: "overall progress", Action: "0 out of 1 tasks"},
|
||||
})
|
||||
|
||||
// When the task reaches running, update should return true
|
||||
tasks[0].Status.Err = ""
|
||||
tasks[0].Status.State = swarm.TaskStateRunning
|
||||
updaterTester.testUpdater(tasks, true,
|
||||
[]progress.Progress{
|
||||
{ID: "a", Action: "running ", Current: 9, Total: 9, HideCounts: true},
|
||||
{ID: "overall progress", Action: "1 out of 1 tasks"},
|
||||
})
|
||||
|
||||
// If the task fails, update should return false again
|
||||
tasks[0].Status.Err = "task failed"
|
||||
tasks[0].Status.State = swarm.TaskStateFailed
|
||||
updaterTester.testUpdater(tasks, false,
|
||||
[]progress.Progress{
|
||||
{ID: "a", Action: "task failed"},
|
||||
{ID: "overall progress", Action: "0 out of 1 tasks"},
|
||||
})
|
||||
|
||||
// If the task is restarted, progress output should be shown for the
|
||||
// replacement task, not the old task.
|
||||
tasks[0].DesiredState = swarm.TaskStateShutdown
|
||||
tasks = append(tasks,
|
||||
swarm.Task{ID: "2",
|
||||
NodeID: "a",
|
||||
DesiredState: swarm.TaskStateRunning,
|
||||
Status: swarm.TaskStatus{State: swarm.TaskStateRunning},
|
||||
})
|
||||
updaterTester.testUpdater(tasks, true,
|
||||
[]progress.Progress{
|
||||
{ID: "a", Action: "running ", Current: 9, Total: 9, HideCounts: true},
|
||||
{ID: "overall progress", Action: "1 out of 1 tasks"},
|
||||
})
|
||||
|
||||
// Add a new task while the current one is still running, to simulate
|
||||
// "start-then-stop" updates.
|
||||
tasks = append(tasks,
|
||||
swarm.Task{ID: "3",
|
||||
NodeID: "a",
|
||||
DesiredState: swarm.TaskStateRunning,
|
||||
Status: swarm.TaskStatus{State: swarm.TaskStatePreparing},
|
||||
})
|
||||
updaterTester.testUpdater(tasks, false,
|
||||
[]progress.Progress{
|
||||
{ID: "a", Action: "preparing", Current: 6, Total: 9, HideCounts: true},
|
||||
{ID: "overall progress", Action: "0 out of 1 tasks"},
|
||||
})
|
||||
}
|
||||
|
||||
func TestGlobalProgressUpdaterManyNodes(t *testing.T) {
|
||||
nodes := 50
|
||||
|
||||
service := swarm.Service{
|
||||
Spec: swarm.ServiceSpec{
|
||||
Mode: swarm.ServiceMode{
|
||||
Global: &swarm.GlobalService{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
p := &mockProgress{}
|
||||
updaterTester := updaterTester{
|
||||
t: t,
|
||||
updater: &globalProgressUpdater{
|
||||
progressOut: p,
|
||||
},
|
||||
p: p,
|
||||
activeNodes: map[string]struct{}{},
|
||||
service: service,
|
||||
}
|
||||
|
||||
for i := 0; i != nodes; i++ {
|
||||
updaterTester.activeNodes[strconv.Itoa(i)] = struct{}{}
|
||||
}
|
||||
|
||||
tasks := []swarm.Task{}
|
||||
|
||||
updaterTester.testUpdater(tasks, false,
|
||||
[]progress.Progress{
|
||||
{ID: "overall progress", Action: "waiting for new tasks"},
|
||||
})
|
||||
|
||||
for i := 0; i != nodes; i++ {
|
||||
tasks = append(tasks,
|
||||
swarm.Task{
|
||||
ID: "task" + strconv.Itoa(i),
|
||||
NodeID: strconv.Itoa(i),
|
||||
DesiredState: swarm.TaskStateRunning,
|
||||
Status: swarm.TaskStatus{State: swarm.TaskStateNew},
|
||||
})
|
||||
}
|
||||
|
||||
updaterTester.testUpdater(tasks, false,
|
||||
[]progress.Progress{
|
||||
{ID: "overall progress", Action: fmt.Sprintf("0 out of %d tasks", nodes)},
|
||||
{ID: "overall progress", Action: fmt.Sprintf("0 out of %d tasks", nodes)},
|
||||
})
|
||||
|
||||
for i := 0; i != nodes; i++ {
|
||||
tasks[i].Status.State = swarm.TaskStateRunning
|
||||
updaterTester.testUpdater(tasks, i == nodes-1,
|
||||
[]progress.Progress{
|
||||
{ID: "overall progress", Action: fmt.Sprintf("%d out of %d tasks", i+1, nodes)},
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,6 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
"github.com/docker/cli/cli/command/idresolver"
|
||||
"github.com/docker/cli/cli/command/node"
|
||||
"github.com/docker/cli/cli/command/task"
|
||||
@ -65,11 +64,7 @@ func runPS(dockerCli command.Cli, options psOptions) error {
|
||||
|
||||
format := options.format
|
||||
if len(format) == 0 {
|
||||
if len(dockerCli.ConfigFile().TasksFormat) > 0 && !options.quiet {
|
||||
format = dockerCli.ConfigFile().TasksFormat
|
||||
} else {
|
||||
format = formatter.TableFormatKey
|
||||
}
|
||||
format = task.DefaultFormat(dockerCli.ConfigFile(), options.quiet)
|
||||
}
|
||||
if err := task.Print(ctx, dockerCli, tasks, idresolver.New(client, options.noResolve), !options.noTrunc, options.quiet, format); err != nil {
|
||||
return err
|
||||
|
||||
@ -107,7 +107,7 @@ func TestRunPSWarnsOnNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
out := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(client, out)
|
||||
cli := test.NewFakeCliWithOutput(client, out)
|
||||
options := psOptions{
|
||||
services: []string{"foo", "bar"},
|
||||
filter: opts.NewFilterOpt(),
|
||||
|
||||
@ -12,17 +12,28 @@ import (
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
type scaleOptions struct {
|
||||
detach bool
|
||||
}
|
||||
|
||||
func newScaleCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
options := &scaleOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "scale SERVICE=REPLICAS [SERVICE=REPLICAS...]",
|
||||
Short: "Scale one or multiple replicated services",
|
||||
Args: scaleArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runScale(dockerCli, args)
|
||||
return runScale(dockerCli, cmd.Flags(), options, args)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
addDetachFlag(flags, &options.detach)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func scaleArgs(cmd *cobra.Command, args []string) error {
|
||||
@ -43,8 +54,11 @@ func scaleArgs(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func runScale(dockerCli *command.DockerCli, args []string) error {
|
||||
func runScale(dockerCli *command.DockerCli, flags *pflag.FlagSet, options *scaleOptions, args []string) error {
|
||||
var errs []string
|
||||
var serviceIDs []string
|
||||
ctx := context.Background()
|
||||
|
||||
for _, arg := range args {
|
||||
parts := strings.SplitN(arg, "=", 2)
|
||||
serviceID, scaleStr := parts[0], parts[1]
|
||||
@ -56,8 +70,23 @@ func runScale(dockerCli *command.DockerCli, args []string) error {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := runServiceScale(dockerCli, serviceID, scale); err != nil {
|
||||
if err := runServiceScale(ctx, dockerCli, serviceID, scale); err != nil {
|
||||
errs = append(errs, fmt.Sprintf("%s: %v", serviceID, err))
|
||||
} else {
|
||||
serviceIDs = append(serviceIDs, serviceID)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(serviceIDs) > 0 {
|
||||
if options.detach {
|
||||
warnDetachDefault(dockerCli.Err(), dockerCli.Client().ClientVersion(), flags, "scaled")
|
||||
} else {
|
||||
for _, serviceID := range serviceIDs {
|
||||
if err := waitOnService(ctx, dockerCli, serviceID, false); err != nil {
|
||||
errs = append(errs, fmt.Sprintf("%s: %v", serviceID, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,9 +96,8 @@ func runScale(dockerCli *command.DockerCli, args []string) error {
|
||||
return errors.Errorf(strings.Join(errs, "\n"))
|
||||
}
|
||||
|
||||
func runServiceScale(dockerCli *command.DockerCli, serviceID string, scale uint64) error {
|
||||
func runServiceScale(ctx context.Context, dockerCli *command.DockerCli, serviceID string, scale uint64) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
|
||||
service, _, err := client.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
|
||||
if err != nil {
|
||||
|
||||
@ -15,6 +15,8 @@ import (
|
||||
type fakeClient struct {
|
||||
client.Client
|
||||
|
||||
version string
|
||||
|
||||
services []string
|
||||
networks []string
|
||||
secrets []string
|
||||
@ -45,6 +47,10 @@ func (cli *fakeClient) ServerVersion(ctx context.Context) (types.Version, error)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) ClientVersion() string {
|
||||
return cli.version
|
||||
}
|
||||
|
||||
func (cli *fakeClient) ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||
if cli.serviceListFunc != nil {
|
||||
return cli.serviceListFunc(options)
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package stack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/compose/convert"
|
||||
@ -18,10 +17,8 @@ func TestPruneServices(t *testing.T) {
|
||||
"keep": {},
|
||||
}
|
||||
client := &fakeClient{services: []string{objectName("foo", "keep"), objectName("foo", "remove")}}
|
||||
dockerCli := test.NewFakeCli(client, &bytes.Buffer{})
|
||||
dockerCli.SetErr(&bytes.Buffer{})
|
||||
dockerCli := test.NewFakeCli(client)
|
||||
|
||||
pruneServices(ctx, dockerCli, namespace, services)
|
||||
|
||||
assert.Equal(t, buildObjectIDs([]string{objectName("foo", "remove")}), client.removedServices)
|
||||
}
|
||||
|
||||
@ -50,7 +50,7 @@ func TestListErrors(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
cmd := newListCommand(test.NewFakeCli(&fakeClient{
|
||||
serviceListFunc: tc.serviceListFunc,
|
||||
}, &bytes.Buffer{}))
|
||||
}))
|
||||
cmd.SetArgs(tc.args)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
for key, value := range tc.flags {
|
||||
@ -62,7 +62,7 @@ func TestListErrors(t *testing.T) {
|
||||
|
||||
func TestListWithFormat(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newListCommand(test.NewFakeCli(&fakeClient{
|
||||
cmd := newListCommand(test.NewFakeCliWithOutput(&fakeClient{
|
||||
serviceListFunc: func(options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||
return []swarm.Service{
|
||||
*Service(
|
||||
@ -81,7 +81,7 @@ func TestListWithFormat(t *testing.T) {
|
||||
|
||||
func TestListWithoutFormat(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newListCommand(test.NewFakeCli(&fakeClient{
|
||||
cmd := newListCommand(test.NewFakeCliWithOutput(&fakeClient{
|
||||
serviceListFunc: func(options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||
return []swarm.Service{
|
||||
*Service(
|
||||
@ -99,7 +99,7 @@ func TestListWithoutFormat(t *testing.T) {
|
||||
|
||||
func TestListOrder(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newListCommand(test.NewFakeCli(&fakeClient{
|
||||
cmd := newListCommand(test.NewFakeCliWithOutput(&fakeClient{
|
||||
serviceListFunc: func(options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||
return []swarm.Service{
|
||||
*Service(
|
||||
|
||||
@ -5,7 +5,6 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
"github.com/docker/cli/cli/command/idresolver"
|
||||
"github.com/docker/cli/cli/command/task"
|
||||
"github.com/docker/cli/opts"
|
||||
@ -58,17 +57,13 @@ func runPS(dockerCli command.Cli, options psOptions) error {
|
||||
}
|
||||
|
||||
if len(tasks) == 0 {
|
||||
fmt.Fprintf(dockerCli.Out(), "Nothing found in stack: %s\n", namespace)
|
||||
fmt.Fprintf(dockerCli.Err(), "Nothing found in stack: %s\n", namespace)
|
||||
return nil
|
||||
}
|
||||
|
||||
format := options.format
|
||||
if len(format) == 0 {
|
||||
if len(dockerCli.ConfigFile().TasksFormat) > 0 && !options.quiet {
|
||||
format = dockerCli.ConfigFile().TasksFormat
|
||||
} else {
|
||||
format = formatter.TableFormatKey
|
||||
}
|
||||
format = task.DefaultFormat(dockerCli.ConfigFile(), options.quiet)
|
||||
}
|
||||
|
||||
return task.Print(ctx, dockerCli, tasks, idresolver.New(client, options.noResolve), !options.noTrunc, options.quiet, format)
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package stack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
"time"
|
||||
@ -45,7 +44,7 @@ func TestStackPsErrors(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
cmd := newPsCommand(test.NewFakeCli(&fakeClient{
|
||||
taskListFunc: tc.taskListFunc,
|
||||
}, &bytes.Buffer{}))
|
||||
}))
|
||||
cmd.SetArgs(tc.args)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
@ -53,55 +52,52 @@ func TestStackPsErrors(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStackPsEmptyStack(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newPsCommand(test.NewFakeCli(&fakeClient{
|
||||
fakeCli := test.NewFakeCli(&fakeClient{
|
||||
taskListFunc: func(options types.TaskListOptions) ([]swarm.Task, error) {
|
||||
return []swarm.Task{}, nil
|
||||
},
|
||||
}, buf))
|
||||
})
|
||||
cmd := newPsCommand(fakeCli)
|
||||
cmd.SetArgs([]string{"foo"})
|
||||
|
||||
assert.NoError(t, cmd.Execute())
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, buf.String(), "Nothing found in stack: foo")
|
||||
assert.Equal(t, "", fakeCli.OutBuffer().String())
|
||||
assert.Equal(t, "Nothing found in stack: foo\n", fakeCli.ErrBuffer().String())
|
||||
}
|
||||
|
||||
func TestStackPsWithQuietOption(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
taskListFunc: func(options types.TaskListOptions) ([]swarm.Task, error) {
|
||||
return []swarm.Task{*Task(TaskID("id-foo"))}, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{})
|
||||
})
|
||||
cmd := newPsCommand(cli)
|
||||
cmd.SetArgs([]string{"foo"})
|
||||
cmd.Flags().Set("quiet", "true")
|
||||
assert.NoError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
actual := cli.OutBuffer().String()
|
||||
expected := golden.Get(t, []byte(actual), "stack-ps-with-quiet-option.golden")
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||
|
||||
}
|
||||
|
||||
func TestStackPsWithNoTruncOption(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
taskListFunc: func(options types.TaskListOptions) ([]swarm.Task, error) {
|
||||
return []swarm.Task{*Task(TaskID("xn4cypcov06f2w8gsbaf2lst3"))}, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{})
|
||||
})
|
||||
cmd := newPsCommand(cli)
|
||||
cmd.SetArgs([]string{"foo"})
|
||||
cmd.Flags().Set("no-trunc", "true")
|
||||
cmd.Flags().Set("format", "{{ .ID }}")
|
||||
assert.NoError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
actual := cli.OutBuffer().String()
|
||||
expected := golden.Get(t, []byte(actual), "stack-ps-with-no-trunc-option.golden")
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
|
||||
func TestStackPsWithNoResolveOption(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
taskListFunc: func(options types.TaskListOptions) ([]swarm.Task, error) {
|
||||
return []swarm.Task{*Task(
|
||||
@ -111,55 +107,50 @@ func TestStackPsWithNoResolveOption(t *testing.T) {
|
||||
nodeInspectWithRaw: func(ref string) (swarm.Node, []byte, error) {
|
||||
return *Node(NodeName("node-name-bar")), nil, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{})
|
||||
})
|
||||
cmd := newPsCommand(cli)
|
||||
cmd.SetArgs([]string{"foo"})
|
||||
cmd.Flags().Set("no-resolve", "true")
|
||||
cmd.Flags().Set("format", "{{ .Node }}")
|
||||
assert.NoError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
actual := cli.OutBuffer().String()
|
||||
expected := golden.Get(t, []byte(actual), "stack-ps-with-no-resolve-option.golden")
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
|
||||
func TestStackPsWithFormat(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
taskListFunc: func(options types.TaskListOptions) ([]swarm.Task, error) {
|
||||
return []swarm.Task{*Task(TaskServiceID("service-id-foo"))}, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{})
|
||||
})
|
||||
cmd := newPsCommand(cli)
|
||||
cmd.SetArgs([]string{"foo"})
|
||||
cmd.Flags().Set("format", "{{ .Name }}")
|
||||
assert.NoError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
actual := cli.OutBuffer().String()
|
||||
expected := golden.Get(t, []byte(actual), "stack-ps-with-format.golden")
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
|
||||
func TestStackPsWithConfigFormat(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
taskListFunc: func(options types.TaskListOptions) ([]swarm.Task, error) {
|
||||
return []swarm.Task{*Task(TaskServiceID("service-id-foo"))}, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{
|
||||
})
|
||||
cli.SetConfigFile(&configfile.ConfigFile{
|
||||
TasksFormat: "{{ .Name }}",
|
||||
})
|
||||
cmd := newPsCommand(cli)
|
||||
cmd.SetArgs([]string{"foo"})
|
||||
assert.NoError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
actual := cli.OutBuffer().String()
|
||||
expected := golden.Get(t, []byte(actual), "stack-ps-with-config-format.golden")
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
|
||||
func TestStackPsWithoutFormat(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
taskListFunc: func(options types.TaskListOptions) ([]swarm.Task, error) {
|
||||
return []swarm.Task{*Task(
|
||||
@ -174,12 +165,11 @@ func TestStackPsWithoutFormat(t *testing.T) {
|
||||
nodeInspectWithRaw: func(ref string) (swarm.Node, []byte, error) {
|
||||
return *Node(NodeName("node-name-bar")), nil, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{})
|
||||
})
|
||||
cmd := newPsCommand(cli)
|
||||
cmd.SetArgs([]string{"foo"})
|
||||
assert.NoError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
actual := cli.OutBuffer().String()
|
||||
expected := golden.Get(t, []byte(actual), "stack-ps-without-format.golden")
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
|
||||
@ -51,20 +51,16 @@ func runRemove(dockerCli command.Cli, opts removeOptions) error {
|
||||
return err
|
||||
}
|
||||
|
||||
secrets, err := getStackSecrets(ctx, client, namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
var secrets []swarm.Secret
|
||||
if versions.GreaterThanOrEqualTo(client.ClientVersion(), "1.25") {
|
||||
secrets, err = getStackSecrets(ctx, client, namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var configs []swarm.Config
|
||||
|
||||
version, err := client.ServerVersion(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if versions.LessThan(version.APIVersion, "1.30") {
|
||||
fmt.Fprintf(dockerCli.Err(), "WARNING: ignoring \"configs\" (requires API version 1.30, but the Docker daemon API version is %s)\n", version.APIVersion)
|
||||
} else {
|
||||
if versions.GreaterThanOrEqualTo(client.ClientVersion(), "1.30") {
|
||||
configs, err = getStackConfigs(ctx, client, namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -72,7 +68,7 @@ func runRemove(dockerCli command.Cli, opts removeOptions) error {
|
||||
}
|
||||
|
||||
if len(services)+len(networks)+len(secrets)+len(configs) == 0 {
|
||||
fmt.Fprintf(dockerCli.Out(), "Nothing found in stack: %s\n", namespace)
|
||||
fmt.Fprintf(dockerCli.Err(), "Nothing found in stack: %s\n", namespace)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -97,14 +93,15 @@ func removeServices(
|
||||
dockerCli command.Cli,
|
||||
services []swarm.Service,
|
||||
) bool {
|
||||
var err error
|
||||
var hasError bool
|
||||
for _, service := range services {
|
||||
fmt.Fprintf(dockerCli.Err(), "Removing service %s\n", service.Spec.Name)
|
||||
if err = dockerCli.Client().ServiceRemove(ctx, service.ID); err != nil {
|
||||
if err := dockerCli.Client().ServiceRemove(ctx, service.ID); err != nil {
|
||||
hasError = true
|
||||
fmt.Fprintf(dockerCli.Err(), "Failed to remove service %s: %s", service.ID, err)
|
||||
}
|
||||
}
|
||||
return err != nil
|
||||
return hasError
|
||||
}
|
||||
|
||||
func removeNetworks(
|
||||
@ -112,14 +109,15 @@ func removeNetworks(
|
||||
dockerCli command.Cli,
|
||||
networks []types.NetworkResource,
|
||||
) bool {
|
||||
var err error
|
||||
var hasError bool
|
||||
for _, network := range networks {
|
||||
fmt.Fprintf(dockerCli.Err(), "Removing network %s\n", network.Name)
|
||||
if err = dockerCli.Client().NetworkRemove(ctx, network.ID); err != nil {
|
||||
if err := dockerCli.Client().NetworkRemove(ctx, network.ID); err != nil {
|
||||
hasError = true
|
||||
fmt.Fprintf(dockerCli.Err(), "Failed to remove network %s: %s", network.ID, err)
|
||||
}
|
||||
}
|
||||
return err != nil
|
||||
return hasError
|
||||
}
|
||||
|
||||
func removeSecrets(
|
||||
@ -127,14 +125,15 @@ func removeSecrets(
|
||||
dockerCli command.Cli,
|
||||
secrets []swarm.Secret,
|
||||
) bool {
|
||||
var err error
|
||||
var hasError bool
|
||||
for _, secret := range secrets {
|
||||
fmt.Fprintf(dockerCli.Err(), "Removing secret %s\n", secret.Spec.Name)
|
||||
if err = dockerCli.Client().SecretRemove(ctx, secret.ID); err != nil {
|
||||
if err := dockerCli.Client().SecretRemove(ctx, secret.ID); err != nil {
|
||||
hasError = true
|
||||
fmt.Fprintf(dockerCli.Err(), "Failed to remove secret %s: %s", secret.ID, err)
|
||||
}
|
||||
}
|
||||
return err != nil
|
||||
return hasError
|
||||
}
|
||||
|
||||
func removeConfigs(
|
||||
@ -142,12 +141,13 @@ func removeConfigs(
|
||||
dockerCli command.Cli,
|
||||
configs []swarm.Config,
|
||||
) bool {
|
||||
var err error
|
||||
var hasError bool
|
||||
for _, config := range configs {
|
||||
fmt.Fprintf(dockerCli.Err(), "Removing config %s\n", config.Spec.Name)
|
||||
if err = dockerCli.Client().ConfigRemove(ctx, config.ID); err != nil {
|
||||
if err := dockerCli.Client().ConfigRemove(ctx, config.ID); err != nil {
|
||||
hasError = true
|
||||
fmt.Fprintf(dockerCli.Err(), "Failed to remove config %s: %s", config.ID, err)
|
||||
}
|
||||
}
|
||||
return err != nil
|
||||
return hasError
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package stack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
@ -11,53 +10,73 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRemoveStack(t *testing.T) {
|
||||
func fakeClientForRemoveStackTest(version string) *fakeClient {
|
||||
allServices := []string{
|
||||
objectName("foo", "service1"),
|
||||
objectName("foo", "service2"),
|
||||
objectName("bar", "service1"),
|
||||
objectName("bar", "service2"),
|
||||
}
|
||||
allServiceIDs := buildObjectIDs(allServices)
|
||||
|
||||
allNetworks := []string{
|
||||
objectName("foo", "network1"),
|
||||
objectName("bar", "network1"),
|
||||
}
|
||||
allNetworkIDs := buildObjectIDs(allNetworks)
|
||||
|
||||
allSecrets := []string{
|
||||
objectName("foo", "secret1"),
|
||||
objectName("foo", "secret2"),
|
||||
objectName("bar", "secret1"),
|
||||
}
|
||||
allSecretIDs := buildObjectIDs(allSecrets)
|
||||
|
||||
allConfigs := []string{
|
||||
objectName("foo", "config1"),
|
||||
objectName("foo", "config2"),
|
||||
objectName("bar", "config1"),
|
||||
}
|
||||
allConfigIDs := buildObjectIDs(allConfigs)
|
||||
|
||||
cli := &fakeClient{
|
||||
return &fakeClient{
|
||||
version: version,
|
||||
services: allServices,
|
||||
networks: allNetworks,
|
||||
secrets: allSecrets,
|
||||
configs: allConfigs,
|
||||
}
|
||||
cmd := newRemoveCommand(test.NewFakeCli(cli, &bytes.Buffer{}))
|
||||
}
|
||||
|
||||
func TestRemoveStackVersion124DoesNotRemoveConfigsOrSecrets(t *testing.T) {
|
||||
client := fakeClientForRemoveStackTest("1.24")
|
||||
cmd := newRemoveCommand(test.NewFakeCli(client))
|
||||
cmd.SetArgs([]string{"foo", "bar"})
|
||||
|
||||
assert.NoError(t, cmd.Execute())
|
||||
assert.Equal(t, allServiceIDs, cli.removedServices)
|
||||
assert.Equal(t, allNetworkIDs, cli.removedNetworks)
|
||||
assert.Equal(t, allSecretIDs, cli.removedSecrets)
|
||||
assert.Equal(t, allConfigIDs, cli.removedConfigs)
|
||||
assert.Equal(t, buildObjectIDs(client.services), client.removedServices)
|
||||
assert.Equal(t, buildObjectIDs(client.networks), client.removedNetworks)
|
||||
assert.Nil(t, client.removedSecrets)
|
||||
assert.Nil(t, client.removedConfigs)
|
||||
}
|
||||
|
||||
func TestSkipEmptyStack(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
func TestRemoveStackVersion125DoesNotRemoveConfigs(t *testing.T) {
|
||||
client := fakeClientForRemoveStackTest("1.25")
|
||||
cmd := newRemoveCommand(test.NewFakeCli(client))
|
||||
cmd.SetArgs([]string{"foo", "bar"})
|
||||
|
||||
assert.NoError(t, cmd.Execute())
|
||||
assert.Equal(t, buildObjectIDs(client.services), client.removedServices)
|
||||
assert.Equal(t, buildObjectIDs(client.networks), client.removedNetworks)
|
||||
assert.Equal(t, buildObjectIDs(client.secrets), client.removedSecrets)
|
||||
assert.Nil(t, client.removedConfigs)
|
||||
}
|
||||
|
||||
func TestRemoveStackVersion130RemovesEverything(t *testing.T) {
|
||||
client := fakeClientForRemoveStackTest("1.30")
|
||||
cmd := newRemoveCommand(test.NewFakeCli(client))
|
||||
cmd.SetArgs([]string{"foo", "bar"})
|
||||
|
||||
assert.NoError(t, cmd.Execute())
|
||||
assert.Equal(t, buildObjectIDs(client.services), client.removedServices)
|
||||
assert.Equal(t, buildObjectIDs(client.networks), client.removedNetworks)
|
||||
assert.Equal(t, buildObjectIDs(client.secrets), client.removedSecrets)
|
||||
assert.Equal(t, buildObjectIDs(client.configs), client.removedConfigs)
|
||||
}
|
||||
|
||||
func TestRemoveStackSkipEmpty(t *testing.T) {
|
||||
allServices := []string{objectName("bar", "service1"), objectName("bar", "service2")}
|
||||
allServiceIDs := buildObjectIDs(allServices)
|
||||
|
||||
@ -70,21 +89,24 @@ func TestSkipEmptyStack(t *testing.T) {
|
||||
allConfigs := []string{objectName("bar", "config1")}
|
||||
allConfigIDs := buildObjectIDs(allConfigs)
|
||||
|
||||
cli := &fakeClient{
|
||||
fakeClient := &fakeClient{
|
||||
version: "1.30",
|
||||
services: allServices,
|
||||
networks: allNetworks,
|
||||
secrets: allSecrets,
|
||||
configs: allConfigs,
|
||||
}
|
||||
cmd := newRemoveCommand(test.NewFakeCli(cli, buf))
|
||||
fakeCli := test.NewFakeCli(fakeClient)
|
||||
cmd := newRemoveCommand(fakeCli)
|
||||
cmd.SetArgs([]string{"foo", "bar"})
|
||||
|
||||
assert.NoError(t, cmd.Execute())
|
||||
assert.Contains(t, buf.String(), "Nothing found in stack: foo")
|
||||
assert.Equal(t, allServiceIDs, cli.removedServices)
|
||||
assert.Equal(t, allNetworkIDs, cli.removedNetworks)
|
||||
assert.Equal(t, allSecretIDs, cli.removedSecrets)
|
||||
assert.Equal(t, allConfigIDs, cli.removedConfigs)
|
||||
assert.Equal(t, "", fakeCli.OutBuffer().String())
|
||||
assert.Contains(t, fakeCli.ErrBuffer().String(), "Nothing found in stack: foo\n")
|
||||
assert.Equal(t, allServiceIDs, fakeClient.removedServices)
|
||||
assert.Equal(t, allNetworkIDs, fakeClient.removedNetworks)
|
||||
assert.Equal(t, allSecretIDs, fakeClient.removedSecrets)
|
||||
assert.Equal(t, allConfigIDs, fakeClient.removedConfigs)
|
||||
}
|
||||
|
||||
func TestRemoveContinueAfterError(t *testing.T) {
|
||||
@ -102,6 +124,7 @@ func TestRemoveContinueAfterError(t *testing.T) {
|
||||
|
||||
removedServices := []string{}
|
||||
cli := &fakeClient{
|
||||
version: "1.30",
|
||||
services: allServices,
|
||||
networks: allNetworks,
|
||||
secrets: allSecrets,
|
||||
@ -116,7 +139,7 @@ func TestRemoveContinueAfterError(t *testing.T) {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
cmd := newRemoveCommand(test.NewFakeCli(cli, &bytes.Buffer{}))
|
||||
cmd := newRemoveCommand(test.NewFakeCli(cli))
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.SetArgs([]string{"foo", "bar"})
|
||||
|
||||
|
||||
@ -51,11 +51,9 @@ func runServices(dockerCli command.Cli, options servicesOptions) error {
|
||||
return err
|
||||
}
|
||||
|
||||
out := dockerCli.Out()
|
||||
|
||||
// if no services in this stack, print message and exit 0
|
||||
if len(services) == 0 {
|
||||
fmt.Fprintf(out, "Nothing found in stack: %s\n", options.namespace)
|
||||
fmt.Fprintf(dockerCli.Err(), "Nothing found in stack: %s\n", options.namespace)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package stack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
@ -70,8 +69,7 @@ func TestStackServicesErrors(t *testing.T) {
|
||||
serviceListFunc: tc.serviceListFunc,
|
||||
nodeListFunc: tc.nodeListFunc,
|
||||
taskListFunc: tc.taskListFunc,
|
||||
}, &bytes.Buffer{})
|
||||
cli.SetConfigfile(&configfile.ConfigFile{})
|
||||
})
|
||||
cmd := newServicesCommand(cli)
|
||||
cmd.SetArgs(tc.args)
|
||||
for key, value := range tc.flags {
|
||||
@ -83,77 +81,70 @@ func TestStackServicesErrors(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStackServicesEmptyServiceList(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newServicesCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
serviceListFunc: func(options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||
return []swarm.Service{}, nil
|
||||
},
|
||||
}, buf),
|
||||
)
|
||||
fakeCli := test.NewFakeCli(&fakeClient{
|
||||
serviceListFunc: func(options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||
return []swarm.Service{}, nil
|
||||
},
|
||||
})
|
||||
cmd := newServicesCommand(fakeCli)
|
||||
cmd.SetArgs([]string{"foo"})
|
||||
assert.NoError(t, cmd.Execute())
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, buf.String(), "Nothing found in stack: foo")
|
||||
assert.Equal(t, "", fakeCli.OutBuffer().String())
|
||||
assert.Equal(t, "Nothing found in stack: foo\n", fakeCli.ErrBuffer().String())
|
||||
}
|
||||
|
||||
func TestStackServicesWithQuietOption(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
serviceListFunc: func(options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||
return []swarm.Service{*Service(ServiceID("id-foo"))}, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{})
|
||||
})
|
||||
cmd := newServicesCommand(cli)
|
||||
cmd.Flags().Set("quiet", "true")
|
||||
cmd.SetArgs([]string{"foo"})
|
||||
assert.NoError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
actual := cli.OutBuffer().String()
|
||||
expected := golden.Get(t, []byte(actual), "stack-services-with-quiet-option.golden")
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
|
||||
func TestStackServicesWithFormat(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
serviceListFunc: func(options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||
return []swarm.Service{
|
||||
*Service(ServiceName("service-name-foo")),
|
||||
}, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{})
|
||||
})
|
||||
cmd := newServicesCommand(cli)
|
||||
cmd.SetArgs([]string{"foo"})
|
||||
cmd.Flags().Set("format", "{{ .Name }}")
|
||||
assert.NoError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
actual := cli.OutBuffer().String()
|
||||
expected := golden.Get(t, []byte(actual), "stack-services-with-format.golden")
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
|
||||
func TestStackServicesWithConfigFormat(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
serviceListFunc: func(options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||
return []swarm.Service{
|
||||
*Service(ServiceName("service-name-foo")),
|
||||
}, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{
|
||||
})
|
||||
cli.SetConfigFile(&configfile.ConfigFile{
|
||||
ServicesFormat: "{{ .Name }}",
|
||||
})
|
||||
cmd := newServicesCommand(cli)
|
||||
cmd.SetArgs([]string{"foo"})
|
||||
assert.NoError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
actual := cli.OutBuffer().String()
|
||||
expected := golden.Get(t, []byte(actual), "stack-services-with-config-format.golden")
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
|
||||
func TestStackServicesWithoutFormat(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
serviceListFunc: func(options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||
return []swarm.Service{*Service(
|
||||
@ -169,12 +160,11 @@ func TestStackServicesWithoutFormat(t *testing.T) {
|
||||
}),
|
||||
)}, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{})
|
||||
})
|
||||
cmd := newServicesCommand(cli)
|
||||
cmd.SetArgs([]string{"foo"})
|
||||
assert.NoError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
actual := cli.OutBuffer().String()
|
||||
expected := golden.Get(t, []byte(actual), "stack-services-without-format.golden")
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ func runCA(dockerCli command.Cli, flags *pflag.FlagSet, opts caOptions) error {
|
||||
}
|
||||
|
||||
if !opts.rotate {
|
||||
for _, f := range []string{flagCACert, flagCAKey, flagCACert, flagExternalCA} {
|
||||
for _, f := range []string{flagCACert, flagCAKey, flagCertExpiry, flagExternalCA} {
|
||||
if flags.Changed(f) {
|
||||
return fmt.Errorf("`--%s` flag requires the `--rotate` flag to update the CA", f)
|
||||
}
|
||||
|
||||
@ -2,10 +2,14 @@ package swarm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/cli/internal/test"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/pkg/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@ -34,6 +38,69 @@ func TestDisplayTrustRootNoRoot(t *testing.T) {
|
||||
assert.EqualError(t, err, "No CA information available")
|
||||
}
|
||||
|
||||
func TestDisplayTrustRootInvalidFlags(t *testing.T) {
|
||||
// we need an actual PEMfile to test
|
||||
tmpfile, err := ioutil.TempFile("", "pemfile")
|
||||
assert.NoError(t, err)
|
||||
defer os.Remove(tmpfile.Name())
|
||||
tmpfile.Write([]byte(`
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBajCCARCgAwIBAgIUe0+jYWhxN8fFOByC7yveIYgvx1kwCgYIKoZIzj0EAwIw
|
||||
EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNjI3MTUxNDAwWhcNMzcwNjIyMTUx
|
||||
NDAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH
|
||||
A0IABGgbOZLd7b4b262+6m4ignIecbAZKim6djNiIS1Kl5IHciXYn7gnSpsayjn7
|
||||
GQABpgkdPeM9TEQowmtR1qSnORujQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB
|
||||
Af8EBTADAQH/MB0GA1UdDgQWBBQ6Rtcn823/fxRZyheRDFpDzuBMpTAKBggqhkjO
|
||||
PQQDAgNIADBFAiEAqD3Kb2rgsy6NoTk+zEgcUi/aGBCsvQDG3vML1PXN8j0CIBjj
|
||||
4nDj+GmHXcnKa8wXx70Z8OZEpRQIiKDDLmcXuslp
|
||||
-----END CERTIFICATE-----
|
||||
`))
|
||||
tmpfile.Close()
|
||||
|
||||
errorTestCases := [][]string{
|
||||
{
|
||||
"--ca-cert=" + tmpfile.Name(),
|
||||
},
|
||||
{
|
||||
"--ca-key=" + tmpfile.Name(),
|
||||
},
|
||||
{ // to make sure we're not erroring because we didn't provide a CA key along with the CA cert
|
||||
|
||||
"--ca-cert=" + tmpfile.Name(),
|
||||
"--ca-key=" + tmpfile.Name(),
|
||||
},
|
||||
{
|
||||
"--cert-expiry=2160h0m0s",
|
||||
},
|
||||
{
|
||||
"--external-ca=protocol=cfssl,url=https://some.com/https/url",
|
||||
},
|
||||
{ // to make sure we're not erroring because we didn't provide a CA cert and external CA
|
||||
|
||||
"--ca-cert=" + tmpfile.Name(),
|
||||
"--external-ca=protocol=cfssl,url=https://some.com/https/url",
|
||||
},
|
||||
}
|
||||
|
||||
for _, args := range errorTestCases {
|
||||
cmd := newCACommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
swarmInspectFunc: func() (swarm.Swarm, error) {
|
||||
return swarm.Swarm{
|
||||
ClusterInfo: swarm.ClusterInfo{
|
||||
TLSInfo: swarm.TLSInfo{
|
||||
TrustRoot: "root",
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
}))
|
||||
assert.NoError(t, cmd.Flags().Parse(args))
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
testutil.ErrorContains(t, cmd.Execute(), "flag requires the `--rotate` flag to update the CA")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisplayTrustRoot(t *testing.T) {
|
||||
buffer := new(bytes.Buffer)
|
||||
trustRoot := "trustme"
|
||||
|
||||
@ -67,7 +67,7 @@ func TestSwarmInitErrorOnAPIFailure(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newInitCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
swarmInitFunc: tc.swarmInitFunc,
|
||||
swarmInspectFunc: tc.swarmInspectFunc,
|
||||
swarmGetUnlockKeyFunc: tc.swarmGetUnlockKeyFunc,
|
||||
@ -114,7 +114,7 @@ func TestSwarmInit(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newInitCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
swarmInitFunc: tc.swarmInitFunc,
|
||||
swarmInspectFunc: tc.swarmInspectFunc,
|
||||
swarmGetUnlockKeyFunc: tc.swarmGetUnlockKeyFunc,
|
||||
|
||||
@ -51,7 +51,7 @@ func TestSwarmJoinErrors(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newJoinCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
swarmJoinFunc: tc.swarmJoinFunc,
|
||||
infoFunc: tc.infoFunc,
|
||||
}, buf))
|
||||
@ -93,7 +93,7 @@ func TestSwarmJoin(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newJoinCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
infoFunc: tc.infoFunc,
|
||||
}, buf))
|
||||
cmd.SetArgs([]string{"remote"})
|
||||
|
||||
@ -92,7 +92,7 @@ func TestSwarmJoinTokenErrors(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newJoinTokenCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
swarmInspectFunc: tc.swarmInspectFunc,
|
||||
swarmUpdateFunc: tc.swarmUpdateFunc,
|
||||
infoFunc: tc.infoFunc,
|
||||
@ -200,7 +200,7 @@ func TestSwarmJoinToken(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newJoinTokenCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
swarmInspectFunc: tc.swarmInspectFunc,
|
||||
infoFunc: tc.infoFunc,
|
||||
nodeInspectFunc: tc.nodeInspectFunc,
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package swarm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
@ -33,11 +32,10 @@ func TestSwarmLeaveErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newLeaveCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
swarmLeaveFunc: tc.swarmLeaveFunc,
|
||||
}, buf))
|
||||
}))
|
||||
cmd.SetArgs(tc.args)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
@ -45,9 +43,8 @@ func TestSwarmLeaveErrors(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSwarmLeave(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newLeaveCommand(
|
||||
test.NewFakeCli(&fakeClient{}, buf))
|
||||
cli := test.NewFakeCli(&fakeClient{})
|
||||
cmd := newLeaveCommand(cli)
|
||||
assert.NoError(t, cmd.Execute())
|
||||
assert.Equal(t, "Node left the swarm.", strings.TrimSpace(buf.String()))
|
||||
assert.Equal(t, "Node left the swarm.", strings.TrimSpace(cli.OutBuffer().String()))
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package swarm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
@ -83,13 +82,12 @@ func TestSwarmUnlockKeyErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newUnlockKeyCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
swarmInspectFunc: tc.swarmInspectFunc,
|
||||
swarmUpdateFunc: tc.swarmUpdateFunc,
|
||||
swarmGetUnlockKeyFunc: tc.swarmGetUnlockKeyFunc,
|
||||
}, buf))
|
||||
}))
|
||||
cmd.SetArgs(tc.args)
|
||||
for key, value := range tc.flags {
|
||||
cmd.Flags().Set(key, value)
|
||||
@ -158,19 +156,18 @@ func TestSwarmUnlockKey(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newUnlockKeyCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
swarmInspectFunc: tc.swarmInspectFunc,
|
||||
swarmUpdateFunc: tc.swarmUpdateFunc,
|
||||
swarmGetUnlockKeyFunc: tc.swarmGetUnlockKeyFunc,
|
||||
}, buf))
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
swarmInspectFunc: tc.swarmInspectFunc,
|
||||
swarmUpdateFunc: tc.swarmUpdateFunc,
|
||||
swarmGetUnlockKeyFunc: tc.swarmGetUnlockKeyFunc,
|
||||
})
|
||||
cmd := newUnlockKeyCommand(cli)
|
||||
cmd.SetArgs(tc.args)
|
||||
for key, value := range tc.flags {
|
||||
cmd.Flags().Set(key, value)
|
||||
}
|
||||
assert.NoError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
actual := cli.OutBuffer().String()
|
||||
expected := golden.Get(t, []byte(actual), fmt.Sprintf("unlockkeys-%s.golden", tc.name))
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package swarm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
@ -66,12 +65,11 @@ func TestSwarmUnlockErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newUnlockCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
infoFunc: tc.infoFunc,
|
||||
swarmUnlockFunc: tc.swarmUnlockFunc,
|
||||
}, buf))
|
||||
}))
|
||||
cmd.SetArgs(tc.args)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
@ -80,7 +78,6 @@ func TestSwarmUnlockErrors(t *testing.T) {
|
||||
|
||||
func TestSwarmUnlock(t *testing.T) {
|
||||
input := "unlockKey"
|
||||
buf := new(bytes.Buffer)
|
||||
dockerCli := test.NewFakeCli(&fakeClient{
|
||||
infoFunc: func() (types.Info, error) {
|
||||
return types.Info{
|
||||
@ -95,7 +92,7 @@ func TestSwarmUnlock(t *testing.T) {
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}, buf)
|
||||
})
|
||||
dockerCli.SetIn(command.NewInStream(ioutil.NopCloser(strings.NewReader(input))))
|
||||
cmd := newUnlockCommand(dockerCli)
|
||||
assert.NoError(t, cmd.Execute())
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package swarm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
@ -68,13 +67,12 @@ func TestSwarmUpdateErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newUpdateCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
swarmInspectFunc: tc.swarmInspectFunc,
|
||||
swarmUpdateFunc: tc.swarmUpdateFunc,
|
||||
swarmGetUnlockKeyFunc: tc.swarmGetUnlockKeyFunc,
|
||||
}, buf))
|
||||
}))
|
||||
cmd.SetArgs(tc.args)
|
||||
for key, value := range tc.flags {
|
||||
cmd.Flags().Set(key, value)
|
||||
@ -164,20 +162,19 @@ func TestSwarmUpdate(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newUpdateCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
swarmInspectFunc: tc.swarmInspectFunc,
|
||||
swarmUpdateFunc: tc.swarmUpdateFunc,
|
||||
swarmGetUnlockKeyFunc: tc.swarmGetUnlockKeyFunc,
|
||||
}, buf))
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
swarmInspectFunc: tc.swarmInspectFunc,
|
||||
swarmUpdateFunc: tc.swarmUpdateFunc,
|
||||
swarmGetUnlockKeyFunc: tc.swarmGetUnlockKeyFunc,
|
||||
})
|
||||
cmd := newUpdateCommand(cli)
|
||||
cmd.SetArgs(tc.args)
|
||||
for key, value := range tc.flags {
|
||||
cmd.Flags().Set(key, value)
|
||||
}
|
||||
cmd.SetOutput(buf)
|
||||
cmd.SetOutput(cli.OutBuffer())
|
||||
assert.NoError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
actual := cli.OutBuffer().String()
|
||||
expected := golden.Get(t, []byte(actual), fmt.Sprintf("update-%s.golden", tc.name))
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
|
||||
@ -4,12 +4,12 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
"github.com/docker/cli/cli/command/idresolver"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type tasksBySlot []swarm.Task
|
||||
@ -82,3 +82,12 @@ func Print(ctx context.Context, dockerCli command.Cli, tasks []swarm.Task, resol
|
||||
|
||||
return formatter.TaskWrite(tasksCtx, tasks, names, nodes)
|
||||
}
|
||||
|
||||
// DefaultFormat returns the default format from the config file, or table
|
||||
// format if nothing is set in the config.
|
||||
func DefaultFormat(configFile *configfile.ConfigFile, quiet bool) string {
|
||||
if len(configFile.TasksFormat) > 0 && !quiet {
|
||||
return configFile.TasksFormat
|
||||
}
|
||||
return formatter.TableFormatKey
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ func TestTaskPrintWithQuietOption(t *testing.T) {
|
||||
noResolve := true
|
||||
buf := new(bytes.Buffer)
|
||||
apiClient := &fakeClient{}
|
||||
cli := test.NewFakeCli(apiClient, buf)
|
||||
cli := test.NewFakeCliWithOutput(apiClient, buf)
|
||||
tasks := []swarm.Task{
|
||||
*Task(TaskID("id-foo")),
|
||||
}
|
||||
@ -41,7 +41,7 @@ func TestTaskPrintWithNoTruncOption(t *testing.T) {
|
||||
noResolve := true
|
||||
buf := new(bytes.Buffer)
|
||||
apiClient := &fakeClient{}
|
||||
cli := test.NewFakeCli(apiClient, buf)
|
||||
cli := test.NewFakeCliWithOutput(apiClient, buf)
|
||||
tasks := []swarm.Task{
|
||||
*Task(TaskID("id-foo-yov6omdek8fg3k5stosyp2m50")),
|
||||
}
|
||||
@ -58,7 +58,7 @@ func TestTaskPrintWithGlobalService(t *testing.T) {
|
||||
noResolve := true
|
||||
buf := new(bytes.Buffer)
|
||||
apiClient := &fakeClient{}
|
||||
cli := test.NewFakeCli(apiClient, buf)
|
||||
cli := test.NewFakeCliWithOutput(apiClient, buf)
|
||||
tasks := []swarm.Task{
|
||||
*Task(TaskServiceID("service-id-foo"), TaskNodeID("node-id-bar"), TaskSlot(0)),
|
||||
}
|
||||
@ -75,7 +75,7 @@ func TestTaskPrintWithReplicatedService(t *testing.T) {
|
||||
noResolve := true
|
||||
buf := new(bytes.Buffer)
|
||||
apiClient := &fakeClient{}
|
||||
cli := test.NewFakeCli(apiClient, buf)
|
||||
cli := test.NewFakeCliWithOutput(apiClient, buf)
|
||||
tasks := []swarm.Task{
|
||||
*Task(TaskServiceID("service-id-foo"), TaskSlot(1)),
|
||||
}
|
||||
@ -99,7 +99,7 @@ func TestTaskPrintWithIndentation(t *testing.T) {
|
||||
return *Node(NodeName("node-name-bar")), nil, nil
|
||||
},
|
||||
}
|
||||
cli := test.NewFakeCli(apiClient, buf)
|
||||
cli := test.NewFakeCliWithOutput(apiClient, buf)
|
||||
tasks := []swarm.Task{
|
||||
*Task(
|
||||
TaskID("id-foo"),
|
||||
@ -138,7 +138,7 @@ func TestTaskPrintWithResolution(t *testing.T) {
|
||||
return *Node(NodeName("node-name-bar")), nil, nil
|
||||
},
|
||||
}
|
||||
cli := test.NewFakeCli(apiClient, buf)
|
||||
cli := test.NewFakeCliWithOutput(apiClient, buf)
|
||||
tasks := []swarm.Task{
|
||||
*Task(TaskServiceID("service-id-foo"), TaskSlot(1)),
|
||||
}
|
||||
|
||||
@ -41,11 +41,10 @@ func TestVolumeCreateErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newCreateCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
volumeCreateFunc: tc.volumeCreateFunc,
|
||||
}, buf),
|
||||
}),
|
||||
)
|
||||
cmd.SetArgs(tc.args)
|
||||
for key, value := range tc.flags {
|
||||
@ -59,7 +58,7 @@ func TestVolumeCreateErrors(t *testing.T) {
|
||||
func TestVolumeCreateWithName(t *testing.T) {
|
||||
name := "foo"
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
volumeCreateFunc: func(body volumetypes.VolumesCreateBody) (types.Volume, error) {
|
||||
if body.Name != name {
|
||||
return types.Volume{}, errors.Errorf("expected name %q, got %q", name, body.Name)
|
||||
@ -97,7 +96,7 @@ func TestVolumeCreateWithFlags(t *testing.T) {
|
||||
name := "banana"
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
volumeCreateFunc: func(body volumetypes.VolumesCreateBody) (types.Volume, error) {
|
||||
if body.Name != "" {
|
||||
return types.Volume{}, errors.Errorf("expected empty name, got %q", body.Name)
|
||||
|
||||
@ -56,7 +56,7 @@ func TestVolumeInspectErrors(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newInspectCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
volumeInspectFunc: tc.volumeInspectFunc,
|
||||
}, buf),
|
||||
)
|
||||
@ -98,7 +98,7 @@ func TestVolumeInspectWithoutFormat(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newInspectCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
volumeInspectFunc: tc.volumeInspectFunc,
|
||||
}, buf),
|
||||
)
|
||||
@ -138,7 +138,7 @@ func TestVolumeInspectWithFormat(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newInspectCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
volumeInspectFunc: tc.volumeInspectFunc,
|
||||
}, buf),
|
||||
)
|
||||
|
||||
@ -39,7 +39,7 @@ func TestVolumeListErrors(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newListCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
volumeListFunc: tc.volumeListFunc,
|
||||
}, buf),
|
||||
)
|
||||
@ -54,7 +54,7 @@ func TestVolumeListErrors(t *testing.T) {
|
||||
|
||||
func TestVolumeListWithoutFormat(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
volumeListFunc: func(filter filters.Args) (volumetypes.VolumesListOKBody, error) {
|
||||
return volumetypes.VolumesListOKBody{
|
||||
Volumes: []*types.Volume{
|
||||
@ -67,7 +67,6 @@ func TestVolumeListWithoutFormat(t *testing.T) {
|
||||
}, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{})
|
||||
cmd := newListCommand(cli)
|
||||
assert.NoError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
@ -77,7 +76,7 @@ func TestVolumeListWithoutFormat(t *testing.T) {
|
||||
|
||||
func TestVolumeListWithConfigFormat(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
volumeListFunc: func(filter filters.Args) (volumetypes.VolumesListOKBody, error) {
|
||||
return volumetypes.VolumesListOKBody{
|
||||
Volumes: []*types.Volume{
|
||||
@ -90,7 +89,7 @@ func TestVolumeListWithConfigFormat(t *testing.T) {
|
||||
}, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{
|
||||
cli.SetConfigFile(&configfile.ConfigFile{
|
||||
VolumesFormat: "{{ .Name }} {{ .Driver }} {{ .Labels }}",
|
||||
})
|
||||
cmd := newListCommand(cli)
|
||||
@ -102,7 +101,7 @@ func TestVolumeListWithConfigFormat(t *testing.T) {
|
||||
|
||||
func TestVolumeListWithFormat(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
volumeListFunc: func(filter filters.Args) (volumetypes.VolumesListOKBody, error) {
|
||||
return volumetypes.VolumesListOKBody{
|
||||
Volumes: []*types.Volume{
|
||||
@ -115,7 +114,6 @@ func TestVolumeListWithFormat(t *testing.T) {
|
||||
}, nil
|
||||
},
|
||||
}, buf)
|
||||
cli.SetConfigfile(&configfile.ConfigFile{})
|
||||
cmd := newListCommand(cli)
|
||||
cmd.Flags().Set("format", "{{ .Name }} {{ .Driver }} {{ .Labels }}")
|
||||
assert.NoError(t, cmd.Execute())
|
||||
|
||||
@ -41,7 +41,7 @@ func TestVolumePruneErrors(t *testing.T) {
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
cmd := NewPruneCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
volumePruneFunc: tc.volumePruneFunc,
|
||||
}, ioutil.Discard),
|
||||
)
|
||||
@ -70,7 +70,7 @@ func TestVolumePruneForce(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := NewPruneCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
volumePruneFunc: tc.volumePruneFunc,
|
||||
}, buf),
|
||||
)
|
||||
@ -88,7 +88,7 @@ func TestVolumePrunePromptYes(t *testing.T) {
|
||||
}
|
||||
for _, input := range []string{"y", "Y"} {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
volumePruneFunc: simplePruneFunc,
|
||||
}, buf)
|
||||
|
||||
@ -110,7 +110,7 @@ func TestVolumePrunePromptNo(t *testing.T) {
|
||||
}
|
||||
for _, input := range []string{"n", "N", "no", "anything", "really"} {
|
||||
buf := new(bytes.Buffer)
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
cli := test.NewFakeCliWithOutput(&fakeClient{
|
||||
volumePruneFunc: simplePruneFunc,
|
||||
}, buf)
|
||||
|
||||
|
||||
@ -31,7 +31,7 @@ func TestVolumeRemoveErrors(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newRemoveCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
test.NewFakeCliWithOutput(&fakeClient{
|
||||
volumeRemoveFunc: tc.volumeRemoveFunc,
|
||||
}, buf))
|
||||
cmd.SetArgs(tc.args)
|
||||
@ -42,7 +42,7 @@ func TestVolumeRemoveErrors(t *testing.T) {
|
||||
|
||||
func TestNodeRemoveMultiple(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newRemoveCommand(test.NewFakeCli(&fakeClient{}, buf))
|
||||
cmd := newRemoveCommand(test.NewFakeCliWithOutput(&fakeClient{}, buf))
|
||||
cmd.SetArgs([]string{"volume1", "volume2"})
|
||||
assert.NoError(t, cmd.Execute())
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
@ -16,17 +17,29 @@ type FakeCli struct {
|
||||
client client.APIClient
|
||||
configfile *configfile.ConfigFile
|
||||
out *command.OutStream
|
||||
err io.Writer
|
||||
outBuffer *bytes.Buffer
|
||||
err *bytes.Buffer
|
||||
in *command.InStream
|
||||
server command.ServerInfo
|
||||
}
|
||||
|
||||
// NewFakeCli returns a Cli backed by the fakeCli
|
||||
func NewFakeCli(client client.APIClient, out io.Writer) *FakeCli {
|
||||
// NewFakeCliWithOutput returns a Cli backed by the fakeCli
|
||||
// Deprecated: Use NewFakeCli
|
||||
func NewFakeCliWithOutput(client client.APIClient, out io.Writer) *FakeCli {
|
||||
cli := NewFakeCli(client)
|
||||
cli.out = command.NewOutStream(out)
|
||||
return cli
|
||||
}
|
||||
|
||||
// NewFakeCli returns a fake for the command.Cli interface
|
||||
func NewFakeCli(client client.APIClient) *FakeCli {
|
||||
outBuffer := new(bytes.Buffer)
|
||||
errBuffer := new(bytes.Buffer)
|
||||
return &FakeCli{
|
||||
client: client,
|
||||
out: command.NewOutStream(out),
|
||||
err: ioutil.Discard,
|
||||
out: command.NewOutStream(outBuffer),
|
||||
outBuffer: outBuffer,
|
||||
err: errBuffer,
|
||||
in: command.NewInStream(ioutil.NopCloser(strings.NewReader(""))),
|
||||
configfile: configfile.New("configfile"),
|
||||
}
|
||||
@ -38,12 +51,12 @@ func (c *FakeCli) SetIn(in *command.InStream) {
|
||||
}
|
||||
|
||||
// SetErr sets the stderr stream for the cli to the specified io.Writer
|
||||
func (c *FakeCli) SetErr(err io.Writer) {
|
||||
func (c *FakeCli) SetErr(err *bytes.Buffer) {
|
||||
c.err = err
|
||||
}
|
||||
|
||||
// SetConfigfile sets the "fake" config file
|
||||
func (c *FakeCli) SetConfigfile(configfile *configfile.ConfigFile) {
|
||||
// SetConfigFile sets the "fake" config file
|
||||
func (c *FakeCli) SetConfigFile(configfile *configfile.ConfigFile) {
|
||||
c.configfile = configfile
|
||||
}
|
||||
|
||||
@ -76,3 +89,13 @@ func (c *FakeCli) ConfigFile() *configfile.ConfigFile {
|
||||
func (c *FakeCli) ServerInfo() command.ServerInfo {
|
||||
return c.server
|
||||
}
|
||||
|
||||
// OutBuffer returns the stdout buffer
|
||||
func (c *FakeCli) OutBuffer() *bytes.Buffer {
|
||||
return c.outBuffer
|
||||
}
|
||||
|
||||
// ErrBuffer Buffer returns the stderr buffer
|
||||
func (c *FakeCli) ErrBuffer() *bytes.Buffer {
|
||||
return c.err
|
||||
}
|
||||
|
||||
@ -636,18 +636,40 @@ __docker_complete_resolved_hostname() {
|
||||
COMPREPLY=( $(host 2>/dev/null "${cur%:}" | awk '/has address/ {print $4}') )
|
||||
}
|
||||
|
||||
# __docker_local_interfaces returns a list of the names and addresses of all
|
||||
# local network interfaces.
|
||||
# If `--ip-only` is passed as a first argument, only addresses are returned.
|
||||
__docker_local_interfaces() {
|
||||
command -v ip >/dev/null 2>&1 || return
|
||||
ip addr show scope global 2>/dev/null | sed -n 's| \+inet \([0-9.]\+\).* \([^ ]\+\)|\1 \2|p'
|
||||
|
||||
local format
|
||||
if [ "$1" = "--ip-only" ] ; then
|
||||
format='\1'
|
||||
shift
|
||||
else
|
||||
format='\1 \2'
|
||||
fi
|
||||
|
||||
ip addr show scope global 2>/dev/null | sed -n "s| \+inet \([0-9.]\+\).* \([^ ]\+\)|$format|p"
|
||||
}
|
||||
|
||||
# __docker_complete_local_interfaces applies completion of the names and addresses of all
|
||||
# local network interfaces based on the current value of `$cur`.
|
||||
# An additional value can be added to the possible completions with an `--add` argument.
|
||||
__docker_complete_local_interfaces() {
|
||||
local additional_interface
|
||||
if [ "$1" = "--add" ] ; then
|
||||
additional_interface="$2"
|
||||
shift 2
|
||||
fi
|
||||
|
||||
COMPREPLY=( $( compgen -W "$(__docker_local_interfaces) $additional_interface" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "$(__docker_local_interfaces "$@") $additional_interface" -- "$cur" ) )
|
||||
}
|
||||
|
||||
# __docker_complete_local_ips applies completion of the addresses of all local network
|
||||
# interfaces based on the current value of `$cur`.
|
||||
__docker_complete_local_ips() {
|
||||
__docker_complete_local_interfaces --ip-only
|
||||
}
|
||||
|
||||
# __docker_complete_capabilities_addable completes Linux capabilities which are
|
||||
@ -1413,7 +1435,7 @@ _docker_container_port() {
|
||||
_docker_container_prune() {
|
||||
case "$prev" in
|
||||
--filter)
|
||||
COMPREPLY=( $( compgen -W "until" -S = -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "label label! until" -S = -- "$cur" ) )
|
||||
__docker_nospace
|
||||
return
|
||||
;;
|
||||
@ -1962,9 +1984,11 @@ _docker_daemon() {
|
||||
--iptables=false
|
||||
--ipv6
|
||||
--live-restore
|
||||
--no-new-privileges
|
||||
--raw-logs
|
||||
--selinux-enabled
|
||||
--userland-proxy=false
|
||||
--version -v
|
||||
"
|
||||
local options_with_args="
|
||||
$global_options_with_args
|
||||
@ -1980,9 +2004,12 @@ _docker_daemon() {
|
||||
--cluster-store-opt
|
||||
--config-file
|
||||
--containerd
|
||||
--cpu-rt-period
|
||||
--cpu-rt-runtime
|
||||
--data-root
|
||||
--default-gateway
|
||||
--default-gateway-v6
|
||||
--default-runtime
|
||||
--default-shm-size
|
||||
--default-ulimit
|
||||
--dns
|
||||
@ -2001,6 +2028,7 @@ _docker_daemon() {
|
||||
--log-opt
|
||||
--max-concurrent-downloads
|
||||
--max-concurrent-uploads
|
||||
--metrics-addr
|
||||
--mtu
|
||||
--oom-score-adjust
|
||||
--pidfile -p
|
||||
@ -2009,6 +2037,7 @@ _docker_daemon() {
|
||||
--shutdown-timeout
|
||||
--storage-driver -s
|
||||
--storage-opt
|
||||
--swarm-default-advertise-addr
|
||||
--userland-proxy-path
|
||||
--userns-remap
|
||||
"
|
||||
@ -2069,7 +2098,7 @@ _docker_daemon() {
|
||||
return
|
||||
;;
|
||||
--storage-driver|-s)
|
||||
COMPREPLY=( $( compgen -W "aufs btrfs devicemapper overlay overlay2 vfs zfs" -- "$(echo $cur | tr '[:upper:]' '[:lower:]')" ) )
|
||||
COMPREPLY=( $( compgen -W "aufs btrfs devicemapper overlay overlay2 vfs zfs" -- "$(echo $cur | tr '[:upper:]' '[:lower:]')" ) )
|
||||
return
|
||||
;;
|
||||
--storage-opt)
|
||||
@ -2094,11 +2123,14 @@ _docker_daemon() {
|
||||
dm.use_deferred_deletion
|
||||
dm.use_deferred_removal
|
||||
"
|
||||
local overlay2_options="overlay2.size"
|
||||
local zfs_options="zfs.fsname"
|
||||
|
||||
local all_options="$btrfs_options $devicemapper_options $overlay2_options $zfs_options"
|
||||
|
||||
case $(__docker_value_of_option '--storage-driver|-s') in
|
||||
'')
|
||||
COMPREPLY=( $( compgen -W "$btrfs_options $devicemapper_options $zfs_options" -S = -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "$all_options" -S = -- "$cur" ) )
|
||||
;;
|
||||
btrfs)
|
||||
COMPREPLY=( $( compgen -W "$btrfs_options" -S = -- "$cur" ) )
|
||||
@ -2106,6 +2138,9 @@ _docker_daemon() {
|
||||
devicemapper)
|
||||
COMPREPLY=( $( compgen -W "$devicemapper_options" -S = -- "$cur" ) )
|
||||
;;
|
||||
overlay2)
|
||||
COMPREPLY=( $( compgen -W "$overlay2_options" -S = -- "$cur" ) )
|
||||
;;
|
||||
zfs)
|
||||
COMPREPLY=( $( compgen -W "$zfs_options" -S = -- "$cur" ) )
|
||||
;;
|
||||
@ -2124,10 +2159,20 @@ _docker_daemon() {
|
||||
__docker_complete_log_options
|
||||
return
|
||||
;;
|
||||
--metrics-addr)
|
||||
__docker_complete_local_ips
|
||||
__docker_append_to_completions ":"
|
||||
__docker_nospace
|
||||
return
|
||||
;;
|
||||
--seccomp-profile)
|
||||
_filedir json
|
||||
return
|
||||
;;
|
||||
--swarm-default-advertise-addr)
|
||||
__docker_complete_local_interfaces
|
||||
return
|
||||
;;
|
||||
--userns-remap)
|
||||
__docker_complete_user_group
|
||||
return
|
||||
@ -2428,7 +2473,7 @@ _docker_image_ls() {
|
||||
_docker_image_prune() {
|
||||
case "$prev" in
|
||||
--filter)
|
||||
COMPREPLY=( $( compgen -W "until" -S = -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "label label! until" -S = -- "$cur" ) )
|
||||
__docker_nospace
|
||||
return
|
||||
;;
|
||||
@ -2707,8 +2752,8 @@ _docker_network_create() {
|
||||
--aux-address|--gateway|--ip-range|--ipam-opt|--ipv6|--opt|-o|--subnet)
|
||||
return
|
||||
;;
|
||||
--ipam-driver)
|
||||
COMPREPLY=( $( compgen -W "default" -- "$cur" ) )
|
||||
--config-from)
|
||||
__docker_complete_networks
|
||||
return
|
||||
;;
|
||||
--driver|-d)
|
||||
@ -2716,14 +2761,22 @@ _docker_network_create() {
|
||||
__docker_complete_plugins_bundled --type Network --remove host --remove null --add macvlan
|
||||
return
|
||||
;;
|
||||
--ipam-driver)
|
||||
COMPREPLY=( $( compgen -W "default" -- "$cur" ) )
|
||||
return
|
||||
;;
|
||||
--label)
|
||||
return
|
||||
;;
|
||||
--scope)
|
||||
COMPREPLY=( $( compgen -W "local swarm" -- "$cur" ) )
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "--attachable --aux-address --driver -d --gateway --help --ingress --internal --ip-range --ipam-driver --ipam-opt --ipv6 --label --opt -o --subnet" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "--attachable --aux-address --config-from --config-only --driver -d --gateway --help --ingress --internal --ip-range --ipam-driver --ipam-opt --ipv6 --label --opt -o --scope --subnet" -- "$cur" ) )
|
||||
;;
|
||||
esac
|
||||
}
|
||||
@ -2806,7 +2859,7 @@ _docker_network_ls() {
|
||||
_docker_network_prune() {
|
||||
case "$prev" in
|
||||
--filter)
|
||||
COMPREPLY=( $( compgen -W "until" -S = -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "label label! until" -S = -- "$cur" ) )
|
||||
__docker_nospace
|
||||
return
|
||||
;;
|
||||
@ -3054,7 +3107,6 @@ _docker_service_update_and_create() {
|
||||
--log-driver
|
||||
--log-opt
|
||||
--mount
|
||||
--network
|
||||
--replicas
|
||||
--reserve-cpu
|
||||
--reserve-memory
|
||||
@ -3103,6 +3155,7 @@ _docker_service_update_and_create() {
|
||||
--host
|
||||
--mode
|
||||
--name
|
||||
--network
|
||||
--placement-pref
|
||||
--publish -p
|
||||
--secret
|
||||
@ -4359,7 +4412,7 @@ _docker_system_info() {
|
||||
_docker_system_prune() {
|
||||
case "$prev" in
|
||||
--filter)
|
||||
COMPREPLY=( $( compgen -W "until" -S = -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "label label! until" -S = -- "$cur" ) )
|
||||
__docker_nospace
|
||||
return
|
||||
;;
|
||||
@ -4478,9 +4531,17 @@ _docker_volume_ls() {
|
||||
}
|
||||
|
||||
_docker_volume_prune() {
|
||||
case "$prev" in
|
||||
--filter)
|
||||
COMPREPLY=( $( compgen -W "label label!" -S = -- "$cur" ) )
|
||||
__docker_nospace
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "--force -f --help" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "--filter --force -f --help" -- "$cur" ) )
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
@ -14,16 +14,16 @@ ENVVARS = -e VERSION=$(VERSION) -e GITCOMMIT
|
||||
# build docker image (dockerfiles/Dockerfile.build)
|
||||
.PHONY: build_docker_image
|
||||
build_docker_image:
|
||||
docker build -t $(DEV_DOCKER_IMAGE_NAME) -f ./dockerfiles/Dockerfile.dev .
|
||||
docker build ${DOCKER_BUILD_ARGS} -t $(DEV_DOCKER_IMAGE_NAME) -f ./dockerfiles/Dockerfile.dev .
|
||||
|
||||
# build docker image having the linting tools (dockerfiles/Dockerfile.lint)
|
||||
.PHONY: build_linter_image
|
||||
build_linter_image:
|
||||
docker build -t $(LINTER_IMAGE_NAME) -f ./dockerfiles/Dockerfile.lint .
|
||||
docker build ${DOCKER_BUILD_ARGS} -t $(LINTER_IMAGE_NAME) -f ./dockerfiles/Dockerfile.lint .
|
||||
|
||||
.PHONY: build_cross_image
|
||||
build_cross_image:
|
||||
docker build -t $(CROSS_IMAGE_NAME) -f ./dockerfiles/Dockerfile.cross .
|
||||
docker build ${DOCKER_BUILD_ARGS} -t $(CROSS_IMAGE_NAME) -f ./dockerfiles/Dockerfile.cross .
|
||||
|
||||
|
||||
# build executable using a container
|
||||
|
||||
@ -1,18 +1,2 @@
|
||||
|
||||
FROM golang:1.8.3
|
||||
|
||||
# allow replacing httpredir or deb mirror
|
||||
ARG APT_MIRROR=deb.debian.org
|
||||
RUN sed -ri "s/(httpredir|deb).debian.org/$APT_MIRROR/g" /etc/apt/sources.list
|
||||
|
||||
RUN apt-get update -qq && apt-get install -y -q \
|
||||
libltdl-dev \
|
||||
gcc-mingw-w64 \
|
||||
parallel \
|
||||
;
|
||||
|
||||
COPY dockerfiles/osx-cross.sh /tmp/
|
||||
RUN /tmp/osx-cross.sh
|
||||
ENV PATH /osxcross/target/bin:$PATH
|
||||
|
||||
FROM dockercore/golang-cross@sha256:d24e7affa3a85d460d2303c2549f03fc866f2b97d771ccf07b0e6e2b411dd207
|
||||
WORKDIR /go/src/github.com/docker/cli
|
||||
|
||||
@ -3,16 +3,25 @@ FROM golang:1.8.3-alpine
|
||||
|
||||
RUN apk add -U git make bash coreutils
|
||||
|
||||
ARG VNDR_SHA=9909bb2b8a0b7ea464527b376dc50389c90df587
|
||||
RUN go get github.com/LK4D4/vndr && \
|
||||
cp /go/bin/vndr /usr/bin && \
|
||||
cd /go/src/github.com/LK4D4/vndr && \
|
||||
git checkout -q "$VNDR_SHA" && \
|
||||
go build -v -o /usr/bin/vndr . && \
|
||||
rm -rf /go/src/* /go/pkg/* /go/bin/*
|
||||
|
||||
ARG BINDATA_SHA=a0ff2567cfb70903282db057e799fd826784d41d
|
||||
RUN go get github.com/jteeuwen/go-bindata/go-bindata && \
|
||||
cp /go/bin/go-bindata /usr/bin && \
|
||||
cd /go/src/github.com/jteeuwen/go-bindata/go-bindata && \
|
||||
git checkout -q "$BINDATA_SHA" && \
|
||||
go build -v -o /usr/bin/go-bindata . && \
|
||||
rm -rf /go/src/* /go/pkg/* /go/bin/*
|
||||
|
||||
ARG FILEWATCHER_SHA=2e12ea42f6c8c089b19e992145bb94e8adaecedb
|
||||
RUN go get github.com/dnephin/filewatcher && \
|
||||
cp /go/bin/filewatcher /usr/bin/ && \
|
||||
cd /go/src/github.com/dnephin/filewatcher && \
|
||||
git checkout -q "$FILEWATCHER_SHA" && \
|
||||
go build -v -o /usr/bin/filewatcher . && \
|
||||
rm -rf /go/src/* /go/pkg/* /go/bin/*
|
||||
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
@ -2,9 +2,13 @@ FROM golang:1.8.3-alpine
|
||||
|
||||
RUN apk add -U git
|
||||
|
||||
RUN go get -u gopkg.in/dnephin/gometalinter.v1 && \
|
||||
mv /go/bin/gometalinter.v1 /usr/local/bin/gometalinter && \
|
||||
gometalinter --install
|
||||
ARG GOMETALINTER_SHA=4306381615a2ba2a207f8fcea02c08c6b2b0803f
|
||||
RUN go get github.com/alecthomas/gometalinter && \
|
||||
cd /go/src/github.com/alecthomas/gometalinter && \
|
||||
git checkout -q "$GOMETALINTER_SHA" && \
|
||||
go build -v -o /usr/local/bin/gometalinter . && \
|
||||
gometalinter --install && \
|
||||
rm -rf /go/src/* /go/pkg/*
|
||||
|
||||
WORKDIR /go/src/github.com/docker/cli
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Install dependencies required to cross compile osx, then cleanup
|
||||
#
|
||||
# TODO: this should be a separate build stage when CI supports it
|
||||
|
||||
|
||||
set -eu -o pipefail
|
||||
|
||||
PKG_DEPS="patch xz-utils clang"
|
||||
|
||||
apt-get update -qq
|
||||
apt-get install -y -q $PKG_DEPS
|
||||
|
||||
OSX_SDK=MacOSX10.11.sdk
|
||||
OSX_CROSS_COMMIT=a9317c18a3a457ca0a657f08cc4d0d43c6cf8953
|
||||
OSXCROSS_PATH="/osxcross"
|
||||
|
||||
echo "Cloning osxcross"
|
||||
time git clone https://github.com/tpoechtrager/osxcross.git $OSXCROSS_PATH
|
||||
cd $OSXCROSS_PATH
|
||||
git checkout -q $OSX_CROSS_COMMIT
|
||||
|
||||
echo "Downloading OSX SDK"
|
||||
time curl -sSL https://s3.dockerproject.org/darwin/v2/${OSX_SDK}.tar.xz \
|
||||
-o "${OSXCROSS_PATH}/tarballs/${OSX_SDK}.tar.xz"
|
||||
|
||||
echo "Building osxcross"
|
||||
UNATTENDED=yes OSX_VERSION_MIN=10.6 ${OSXCROSS_PATH}/build.sh > /dev/null
|
||||
@ -138,7 +138,7 @@ be UPPERCASE to distinguish them from arguments more easily.
|
||||
Docker runs instructions in a `Dockerfile` in order. A `Dockerfile` **must
|
||||
start with a \`FROM\` instruction**. The `FROM` instruction specifies the [*Base
|
||||
Image*](glossary.md#base-image) from which you are building. `FROM` may only be
|
||||
proceeded by one or more `ARG` instructions, which declare arguments that are used
|
||||
preceded by one or more `ARG` instructions, which declare arguments that are used
|
||||
in `FROM` lines in the `Dockerfile`.
|
||||
|
||||
Docker treats lines that *begin* with `#` as a comment, unless the line is
|
||||
@ -499,7 +499,7 @@ valid `Dockerfile` must start with a `FROM` instruction. The image can be
|
||||
any valid image – it is especially easy to start by **pulling an image** from
|
||||
the [*Public Repositories*](https://docs.docker.com/engine/tutorials/dockerrepos/).
|
||||
|
||||
- `ARG` is the only instruction that may proceed `FROM` in the `Dockerfile`.
|
||||
- `ARG` is the only instruction that may precede `FROM` in the `Dockerfile`.
|
||||
See [Understand how ARG and FROM interact](#understand-how-arg-and-from-interact).
|
||||
|
||||
- `FROM` can appear multiple times within a single `Dockerfile` to
|
||||
|
||||
@ -700,6 +700,34 @@ ENOSPC and will shutdown filesystem.
|
||||
$ sudo dockerd --storage-opt dm.xfs_nospace_max_retries=0
|
||||
```
|
||||
|
||||
##### `dm.libdm_log_level`
|
||||
|
||||
Specifies the maxmimum `libdm` log level that will be forwarded to the
|
||||
`dockerd` log (as specified by `--log-level`). This option is primarily
|
||||
intended for debugging problems involving `libdm`. Using values other than the
|
||||
defaults may cause false-positive warnings to be logged.
|
||||
|
||||
Values specified must fall within the range of valid `libdm` log levels. At the
|
||||
time of writing, the following is the list of `libdm` log levels as well as
|
||||
their corresponding levels when output by `dockerd`.
|
||||
|
||||
| `libdm` Level | Value | `--log-level` |
|
||||
| ------------- | -----:| ------------- |
|
||||
| `_LOG_FATAL` | 2 | error |
|
||||
| `_LOG_ERR` | 3 | error |
|
||||
| `_LOG_WARN` | 4 | warn |
|
||||
| `_LOG_NOTICE` | 5 | info |
|
||||
| `_LOG_INFO` | 6 | info |
|
||||
| `_LOG_DEBUG` | 7 | debug |
|
||||
|
||||
###### Example
|
||||
|
||||
```bash
|
||||
$ sudo dockerd \
|
||||
--log-level debug \
|
||||
--storage-opt dm.libdm_log_level=7
|
||||
```
|
||||
|
||||
#### ZFS options
|
||||
|
||||
##### `zfs.fsname`
|
||||
|
||||
@ -167,6 +167,8 @@ $ docker service create --name redis \
|
||||
4cdgfyky7ozwh3htjfw0d12qv
|
||||
```
|
||||
|
||||
To grant a service access to multiple secrets, use multiple `--secret` flags.
|
||||
|
||||
Secrets are located in `/run/secrets` in the container. If no target is
|
||||
specified, the name of the secret will be used as the in memory file in the
|
||||
container. If a target is specified, that will be the filename. In the
|
||||
@ -191,10 +193,26 @@ tutorial](https://docs.docker.com/engine/swarm/swarm-tutorial/rolling-update/).
|
||||
|
||||
### Set environment variables (-e, --env)
|
||||
|
||||
This sets environmental variables for all tasks in a service. For example:
|
||||
This sets an environmental variable for all tasks in a service. For example:
|
||||
|
||||
```bash
|
||||
$ docker service create --name redis_2 --replicas 5 --env MYVAR=foo redis:3.0.6
|
||||
$ docker service create \
|
||||
--name redis_2 \
|
||||
--replicas 5 \
|
||||
--env MYVAR=foo \
|
||||
redis:3.0.6
|
||||
```
|
||||
|
||||
To specify multiple environment variables, specify multiple `--env` flags, each
|
||||
with a separate key-value pair.
|
||||
|
||||
```bash
|
||||
$ docker service create \
|
||||
--name redis_2 \
|
||||
--replicas 5 \
|
||||
--env MYVAR=foo \
|
||||
--env MYVAR2=bar \
|
||||
redis:3.0.6
|
||||
```
|
||||
|
||||
### Create a service with specific hostname (--hostname)
|
||||
|
||||
@ -16,11 +16,12 @@ keywords: "service, scale"
|
||||
# service scale
|
||||
|
||||
```markdown
|
||||
Usage: docker service scale SERVICE=REPLICAS [SERVICE=REPLICAS...]
|
||||
Usage: docker service scale [OPTIONS] SERVICE=REPLICAS [SERVICE=REPLICAS...]
|
||||
|
||||
Scale one or multiple replicated services
|
||||
|
||||
Options:
|
||||
-d, --detach Exit immediately instead of waiting for the service to converge (default true)
|
||||
--help Print usage
|
||||
```
|
||||
|
||||
|
||||
@ -87,8 +87,9 @@ default foreground mode:
|
||||
|
||||
To start a container in detached mode, you use `-d=true` or just `-d` option. By
|
||||
design, containers started in detached mode exit when the root process used to
|
||||
run the container exits. A container in detached mode cannot be automatically
|
||||
removed when it stops, this means you cannot use the `--rm` option with `-d` option.
|
||||
run the container exits, unless you also specify the `--rm` option. If you use
|
||||
`-d` with `--rm`, the container is removed when it exits **or** when the daemon
|
||||
exits, whichever happens first.
|
||||
|
||||
Do not pass a `service x start` command to a detached container. For example, this
|
||||
command attempts to start the `nginx` service.
|
||||
@ -149,7 +150,7 @@ is receiving its standard input from a pipe, as in:
|
||||
The operator can identify a container in three ways:
|
||||
|
||||
| Identifier type | Example value |
|
||||
| --------------------- | ------------------------------------------------------------------ |
|
||||
|:----------------------|:-------------------------------------------------------------------|
|
||||
| UUID long identifier | "f78375b1c487e03c9438c729345e54db9d20cfa2ac1fc3494b6eb60872e74778" |
|
||||
| UUID short identifier | "f78375b1c487" |
|
||||
| Name | "evil_ptolemy" |
|
||||
@ -686,29 +687,29 @@ parent group.
|
||||
The operator can also adjust the performance parameters of the
|
||||
container:
|
||||
|
||||
| Option | Description |
|
||||
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `-m`, `--memory=""` | Memory limit (format: `<number>[<unit>]`). Number is a positive integer. Unit can be one of `b`, `k`, `m`, or `g`. Minimum is 4M. |
|
||||
| `--memory-swap=""` | Total memory limit (memory + swap, format: `<number>[<unit>]`). Number is a positive integer. Unit can be one of `b`, `k`, `m`, or `g`. |
|
||||
| `--memory-reservation=""` | Memory soft limit (format: `<number>[<unit>]`). Number is a positive integer. Unit can be one of `b`, `k`, `m`, or `g`. |
|
||||
| `--kernel-memory=""` | Kernel memory limit (format: `<number>[<unit>]`). Number is a positive integer. Unit can be one of `b`, `k`, `m`, or `g`. Minimum is 4M. |
|
||||
| `-c`, `--cpu-shares=0` | CPU shares (relative weight) |
|
||||
| `--cpus=0.000` | Number of CPUs. Number is a fractional number. 0.000 means no limit. |
|
||||
| `--cpu-period=0` | Limit the CPU CFS (Completely Fair Scheduler) period |
|
||||
| `--cpuset-cpus=""` | CPUs in which to allow execution (0-3, 0,1) |
|
||||
| `--cpuset-mems=""` | Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems. |
|
||||
| `--cpu-quota=0` | Limit the CPU CFS (Completely Fair Scheduler) quota |
|
||||
| `--cpu-rt-period=0` | Limit the CPU real-time period. In microseconds. Requires parent cgroups be set and cannot be higher than parent. Also check rtprio ulimits. |
|
||||
| `--cpu-rt-runtime=0` | Limit the CPU real-time runtime. In microseconds. Requires parent cgroups be set and cannot be higher than parent. Also check rtprio ulimits. |
|
||||
| `--blkio-weight=0` | Block IO weight (relative weight) accepts a weight value between 10 and 1000. |
|
||||
| `--blkio-weight-device=""` | Block IO weight (relative device weight, format: `DEVICE_NAME:WEIGHT`) |
|
||||
| `--device-read-bps=""` | Limit read rate from a device (format: `<device-path>:<number>[<unit>]`). Number is a positive integer. Unit can be one of `kb`, `mb`, or `gb`. |
|
||||
| `--device-write-bps=""` | Limit write rate to a device (format: `<device-path>:<number>[<unit>]`). Number is a positive integer. Unit can be one of `kb`, `mb`, or `gb`. |
|
||||
| `--device-read-iops="" ` | Limit read rate (IO per second) from a device (format: `<device-path>:<number>`). Number is a positive integer. |
|
||||
| `--device-write-iops="" ` | Limit write rate (IO per second) to a device (format: `<device-path>:<number>`). Number is a positive integer. |
|
||||
| `--oom-kill-disable=false` | Whether to disable OOM Killer for the container or not. |
|
||||
| `--oom-score-adj=0` | Tune container's OOM preferences (-1000 to 1000) |
|
||||
| `--memory-swappiness=""` | Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100. |
|
||||
| Option | Description |
|
||||
|:---------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `-m`, `--memory=""` | Memory limit (format: `<number>[<unit>]`). Number is a positive integer. Unit can be one of `b`, `k`, `m`, or `g`. Minimum is 4M. |
|
||||
| `--memory-swap=""` | Total memory limit (memory + swap, format: `<number>[<unit>]`). Number is a positive integer. Unit can be one of `b`, `k`, `m`, or `g`. |
|
||||
| `--memory-reservation=""` | Memory soft limit (format: `<number>[<unit>]`). Number is a positive integer. Unit can be one of `b`, `k`, `m`, or `g`. |
|
||||
| `--kernel-memory=""` | Kernel memory limit (format: `<number>[<unit>]`). Number is a positive integer. Unit can be one of `b`, `k`, `m`, or `g`. Minimum is 4M. |
|
||||
| `-c`, `--cpu-shares=0` | CPU shares (relative weight) |
|
||||
| `--cpus=0.000` | Number of CPUs. Number is a fractional number. 0.000 means no limit. |
|
||||
| `--cpu-period=0` | Limit the CPU CFS (Completely Fair Scheduler) period |
|
||||
| `--cpuset-cpus=""` | CPUs in which to allow execution (0-3, 0,1) |
|
||||
| `--cpuset-mems=""` | Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems. |
|
||||
| `--cpu-quota=0` | Limit the CPU CFS (Completely Fair Scheduler) quota |
|
||||
| `--cpu-rt-period=0` | Limit the CPU real-time period. In microseconds. Requires parent cgroups be set and cannot be higher than parent. Also check rtprio ulimits. |
|
||||
| `--cpu-rt-runtime=0` | Limit the CPU real-time runtime. In microseconds. Requires parent cgroups be set and cannot be higher than parent. Also check rtprio ulimits. |
|
||||
| `--blkio-weight=0` | Block IO weight (relative weight) accepts a weight value between 10 and 1000. |
|
||||
| `--blkio-weight-device=""` | Block IO weight (relative device weight, format: `DEVICE_NAME:WEIGHT`) |
|
||||
| `--device-read-bps=""` | Limit read rate from a device (format: `<device-path>:<number>[<unit>]`). Number is a positive integer. Unit can be one of `kb`, `mb`, or `gb`. |
|
||||
| `--device-write-bps=""` | Limit write rate to a device (format: `<device-path>:<number>[<unit>]`). Number is a positive integer. Unit can be one of `kb`, `mb`, or `gb`. |
|
||||
| `--device-read-iops="" ` | Limit read rate (IO per second) from a device (format: `<device-path>:<number>`). Number is a positive integer. |
|
||||
| `--device-write-iops="" ` | Limit write rate (IO per second) to a device (format: `<device-path>:<number>`). Number is a positive integer. |
|
||||
| `--oom-kill-disable=false` | Whether to disable OOM Killer for the container or not. |
|
||||
| `--oom-score-adj=0` | Tune container's OOM preferences (-1000 to 1000) |
|
||||
| `--memory-swappiness=""` | Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100. |
|
||||
| `--shm-size=""` | Size of `/dev/shm`. The format is `<number><unit>`. `number` must be greater than `0`. Unit is optional and can be `b` (bytes), `k` (kilobytes), `m` (megabytes), or `g` (gigabytes). If you omit the unit, the system uses bytes. If you omit the size entirely, the system uses `64m`. |
|
||||
|
||||
### User memory constraints
|
||||
@ -1158,7 +1159,7 @@ list of capabilities that are kept. The following table lists the Linux capabili
|
||||
options which are allowed by default and can be dropped.
|
||||
|
||||
| Capability Key | Capability Description |
|
||||
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
|:-----------------|:------------------------------------------------------------------------------------------------------------------------------|
|
||||
| SETPCAP | Modify process capabilities. |
|
||||
| MKNOD | Create special files using mknod(2). |
|
||||
| AUDIT_WRITE | Write records to kernel auditing log. |
|
||||
@ -1176,31 +1177,31 @@ options which are allowed by default and can be dropped.
|
||||
|
||||
The next table shows the capabilities which are not granted by default and may be added.
|
||||
|
||||
| Capability Key | Capability Description |
|
||||
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| SYS_MODULE | Load and unload kernel modules. |
|
||||
| SYS_RAWIO | Perform I/O port operations (iopl(2) and ioperm(2)). |
|
||||
| SYS_PACCT | Use acct(2), switch process accounting on or off. |
|
||||
| SYS_ADMIN | Perform a range of system administration operations. |
|
||||
| SYS_NICE | Raise process nice value (nice(2), setpriority(2)) and change the nice value for arbitrary processes. |
|
||||
| SYS_RESOURCE | Override resource Limits. |
|
||||
| SYS_TIME | Set system clock (settimeofday(2), stime(2), adjtimex(2)); set real-time (hardware) clock. |
|
||||
| SYS_TTY_CONFIG | Use vhangup(2); employ various privileged ioctl(2) operations on virtual terminals. |
|
||||
| AUDIT_CONTROL | Enable and disable kernel auditing; change auditing filter rules; retrieve auditing status and filtering rules. |
|
||||
| MAC_OVERRIDE | Allow MAC configuration or state changes. Implemented for the Smack LSM. |
|
||||
| MAC_ADMIN | Override Mandatory Access Control (MAC). Implemented for the Smack Linux Security Module (LSM). |
|
||||
| NET_ADMIN | Perform various network-related operations. |
|
||||
| SYSLOG | Perform privileged syslog(2) operations. |
|
||||
| DAC_READ_SEARCH | Bypass file read permission checks and directory read and execute permission checks. |
|
||||
| LINUX_IMMUTABLE | Set the FS_APPEND_FL and FS_IMMUTABLE_FL i-node flags. |
|
||||
| NET_BROADCAST | Make socket broadcasts, and listen to multicasts. |
|
||||
| IPC_LOCK | Lock memory (mlock(2), mlockall(2), mmap(2), shmctl(2)). |
|
||||
| IPC_OWNER | Bypass permission checks for operations on System V IPC objects. |
|
||||
| SYS_PTRACE | Trace arbitrary processes using ptrace(2). |
|
||||
| SYS_BOOT | Use reboot(2) and kexec_load(2), reboot and load a new kernel for later execution. |
|
||||
| LEASE | Establish leases on arbitrary files (see fcntl(2)). |
|
||||
| WAKE_ALARM | Trigger something that will wake up the system. |
|
||||
| BLOCK_SUSPEND | Employ features that can block system suspend. |
|
||||
| Capability Key | Capability Description |
|
||||
|:----------------|:----------------------------------------------------------------------------------------------------------------|
|
||||
| SYS_MODULE | Load and unload kernel modules. |
|
||||
| SYS_RAWIO | Perform I/O port operations (iopl(2) and ioperm(2)). |
|
||||
| SYS_PACCT | Use acct(2), switch process accounting on or off. |
|
||||
| SYS_ADMIN | Perform a range of system administration operations. |
|
||||
| SYS_NICE | Raise process nice value (nice(2), setpriority(2)) and change the nice value for arbitrary processes. |
|
||||
| SYS_RESOURCE | Override resource Limits. |
|
||||
| SYS_TIME | Set system clock (settimeofday(2), stime(2), adjtimex(2)); set real-time (hardware) clock. |
|
||||
| SYS_TTY_CONFIG | Use vhangup(2); employ various privileged ioctl(2) operations on virtual terminals. |
|
||||
| AUDIT_CONTROL | Enable and disable kernel auditing; change auditing filter rules; retrieve auditing status and filtering rules. |
|
||||
| MAC_OVERRIDE | Allow MAC configuration or state changes. Implemented for the Smack LSM. |
|
||||
| MAC_ADMIN | Override Mandatory Access Control (MAC). Implemented for the Smack Linux Security Module (LSM). |
|
||||
| NET_ADMIN | Perform various network-related operations. |
|
||||
| SYSLOG | Perform privileged syslog(2) operations. |
|
||||
| DAC_READ_SEARCH | Bypass file read permission checks and directory read and execute permission checks. |
|
||||
| LINUX_IMMUTABLE | Set the FS_APPEND_FL and FS_IMMUTABLE_FL i-node flags. |
|
||||
| NET_BROADCAST | Make socket broadcasts, and listen to multicasts. |
|
||||
| IPC_LOCK | Lock memory (mlock(2), mlockall(2), mmap(2), shmctl(2)). |
|
||||
| IPC_OWNER | Bypass permission checks for operations on System V IPC objects. |
|
||||
| SYS_PTRACE | Trace arbitrary processes using ptrace(2). |
|
||||
| SYS_BOOT | Use reboot(2) and kexec_load(2), reboot and load a new kernel for later execution. |
|
||||
| LEASE | Establish leases on arbitrary files (see fcntl(2)). |
|
||||
| WAKE_ALARM | Trigger something that will wake up the system. |
|
||||
| BLOCK_SUSPEND | Employ features that can block system suspend. |
|
||||
|
||||
Further reference information is available on the [capabilities(7) - Linux man page](http://man7.org/linux/man-pages/man7/capabilities.7.html)
|
||||
|
||||
@ -1252,7 +1253,7 @@ the `--log-driver=VALUE` with the `docker run` command to configure the
|
||||
container's logging driver. The following options are supported:
|
||||
|
||||
| Driver | Description |
|
||||
| ----------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
|:------------|:------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `none` | Disables any logging for the container. `docker logs` won't be available with this driver. |
|
||||
| `json-file` | Default logging driver for Docker. Writes JSON messages to file. No logging options are supported for this driver. |
|
||||
| `syslog` | Syslog logging driver for Docker. Writes log messages to syslog. |
|
||||
@ -1398,12 +1399,12 @@ container.
|
||||
|
||||
The following environment variables are set for Linux containers:
|
||||
|
||||
| Variable | Value |
|
||||
| -------- | ----- |
|
||||
| `HOME` | Set based on the value of `USER` |
|
||||
| `HOSTNAME` | The hostname associated with the container |
|
||||
| `PATH` | Includes popular directories, such as `/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin` |
|
||||
| `TERM` | `xterm` if the container is allocated a pseudo-TTY |
|
||||
| Variable | Value |
|
||||
|:-----------|:-----------------------------------------------------------------------------------------------------|
|
||||
| `HOME` | Set based on the value of `USER` |
|
||||
| `HOSTNAME` | The hostname associated with the container |
|
||||
| `PATH` | Includes popular directories, such as `/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin` |
|
||||
| `TERM` | `xterm` if the container is allocated a pseudo-TTY |
|
||||
|
||||
|
||||
Additionally, the operator can **set any environment variable** in the
|
||||
|
||||
@ -703,6 +703,31 @@ Example use:
|
||||
|
||||
$ sudo dockerd --storage-opt dm.xfs_nospace_max_retries=0
|
||||
|
||||
##### dm.libdm_log_level
|
||||
|
||||
Specifies the maxmimum libdm log level that will be forwarded to the dockerd
|
||||
log (as specified by --log-level). This option is primarily intended for
|
||||
debugging problems involving libdm. Using values other than the defaults may
|
||||
cause false-positive warnings to be logged.
|
||||
|
||||
Values specified must fall within the range of valid libdm log levels. At the
|
||||
time of writing, the following is the list of libdm log levels as well as their
|
||||
corresponding levels when output by dockerd.
|
||||
|
||||
| libdm Level | Value | --log-level |
|
||||
| ----------- | -----:| ----------- |
|
||||
| _LOG_FATAL | 2 | error |
|
||||
| _LOG_ERR | 3 | error |
|
||||
| _LOG_WARN | 4 | warn |
|
||||
| _LOG_NOTICE | 5 | info |
|
||||
| _LOG_INFO | 6 | info |
|
||||
| _LOG_DEBUG | 7 | debug |
|
||||
|
||||
Example use:
|
||||
|
||||
$ sudo dockerd \
|
||||
--log-level debug \
|
||||
--storage-opt dm.libdm_log_level=7
|
||||
|
||||
## ZFS options
|
||||
|
||||
|
||||
@ -6,13 +6,28 @@
|
||||
set -eu -o pipefail
|
||||
|
||||
BUILDDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
export SHELL=bash
|
||||
|
||||
echo "Building binaries for all platforms"
|
||||
SHELL=/bin/bash parallel ::: \
|
||||
jobs=(
|
||||
"$BUILDDIR/windows" \
|
||||
"$BUILDDIR/osx" \
|
||||
"GOOS=linux GOARCH=amd64 $BUILDDIR/binary" \
|
||||
"GOOS=linux GOARCH=arm $BUILDDIR/binary" \
|
||||
"GOOS=linux GOARCH=ppc64le $BUILDDIR/binary" \
|
||||
"GOOS=linux GOARCH=s390x $BUILDDIR/binary" \
|
||||
;
|
||||
)
|
||||
|
||||
# Outside of circleCI run all at once. On circleCI run two at a time because
|
||||
# each continer has access to two cores.
|
||||
group=${CROSS_GROUP-"all"}
|
||||
|
||||
if [ "$group" == "all" ]; then
|
||||
|
||||
echo "Building binaries for all platforms"
|
||||
parallel ::: "${jobs[@]}"
|
||||
exit 0
|
||||
|
||||
fi
|
||||
|
||||
declare -i start=$group*2
|
||||
parallel ::: "${jobs[@]:$start:2}"
|
||||
|
||||
@ -1,12 +1,2 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
filewatcher \
|
||||
-L 6 \
|
||||
-x '**/*.swp' \
|
||||
-x .git \
|
||||
-x build \
|
||||
-x .idea \
|
||||
-- \
|
||||
sh -c 'go test -timeout 10s -v ./${dir} || ( echo; echo; exit 1 )'
|
||||
exec filewatcher -L 6 -x build -x script go test -timeout 10s -v './${dir}'
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
github.com/Azure/go-ansiterm 388960b655244e76e24c75f48631564eaefade62
|
||||
github.com/Microsoft/go-winio v0.4.1
|
||||
github.com/Microsoft/go-winio v0.4.2
|
||||
github.com/Nvveen/Gotty a8b993ba6abdb0e0c12b0125c603323a71c7790c https://github.com/ijc25/Gotty
|
||||
github.com/Sirupsen/logrus v0.11.0
|
||||
github.com/agl/ed25519 d2b94fd789ea21d12fac1a4443dd3a3f79cda72c
|
||||
@ -7,9 +7,12 @@ github.com/coreos/etcd 824277cb3a577a0e8c829ca9ec557b973fe06d20
|
||||
github.com/cpuguy83/go-md2man a65d4d2de4d5f7c74868dfa9b202a3c8be315aaa
|
||||
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
|
||||
github.com/docker/distribution b38e5838b7b2f2ad48e06ec4b500011976080621
|
||||
github.com/docker/docker 050c1bb17bd033e909cb653f5449b683608293d6
|
||||
github.com/docker/docker 87df0e533b619c088091fd1e2310e92bb9a24822
|
||||
github.com/docker/docker-credential-helpers v0.5.1
|
||||
github.com/docker/go d30aec9fd63c35133f8f79c3412ad91a3b08be06 #?
|
||||
|
||||
# the docker/go package contains a customized version of canonical/json
|
||||
# and is used by Notary. The package is periodically rebased on current Go versions.
|
||||
github.com/docker/go d30aec9fd63c35133f8f79c3412ad91a3b08be06
|
||||
github.com/docker/go-connections 3ede32e2033de7505e6500d6c868c2b9ed9f169d
|
||||
github.com/docker/go-events 18b43f1bc85d9cdd42c05a6cd2d444c7a200a894
|
||||
github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1
|
||||
@ -30,7 +33,7 @@ github.com/opencontainers/image-spec f03dbe35d449c54915d235f1a3cf8f585a24babe
|
||||
github.com/opencontainers/runc 9c2d8d184e5da67c95d601382adf14862e4f2228 https://github.com/docker/runc.git
|
||||
github.com/opencontainers/selinux v1.0.0-rc1
|
||||
github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9
|
||||
github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2
|
||||
github.com/pmezard/go-difflib v1.0.0
|
||||
github.com/russross/blackfriday 1d6b8e9301e720b08a8938b8c25c018285885438
|
||||
github.com/shurcooL/sanitized_anchor_name 10ef21a441db47d8b13ebcc5fd2310f636973c77
|
||||
github.com/spf13/cobra v1.5.1 https://github.com/dnephin/cobra.git
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user