mirror of https://github.com/docker/cli.git
add unit tests to stack package
Signed-off-by: Arash Deshmeh <adeshmeh@ca.ibm.com>
This commit is contained in:
parent
5dd30732a2
commit
535af2d868
|
@ -103,11 +103,11 @@ func TestNodePs(t *testing.T) {
|
||||||
},
|
},
|
||||||
taskListFunc: func(options types.TaskListOptions) ([]swarm.Task, error) {
|
taskListFunc: func(options types.TaskListOptions) ([]swarm.Task, error) {
|
||||||
return []swarm.Task{
|
return []swarm.Task{
|
||||||
*Task(TaskID("taskID1"), ServiceID("failure"),
|
*Task(TaskID("taskID1"), TaskServiceID("failure"),
|
||||||
WithStatus(Timestamp(time.Now().Add(-2*time.Hour)), StatusErr("a task error"))),
|
WithStatus(Timestamp(time.Now().Add(-2*time.Hour)), StatusErr("a task error"))),
|
||||||
*Task(TaskID("taskID2"), ServiceID("failure"),
|
*Task(TaskID("taskID2"), TaskServiceID("failure"),
|
||||||
WithStatus(Timestamp(time.Now().Add(-3*time.Hour)), StatusErr("a task error"))),
|
WithStatus(Timestamp(time.Now().Add(-3*time.Hour)), StatusErr("a task error"))),
|
||||||
*Task(TaskID("taskID3"), ServiceID("failure"),
|
*Task(TaskID("taskID3"), TaskServiceID("failure"),
|
||||||
WithStatus(Timestamp(time.Now().Add(-4*time.Hour)), StatusErr("a task error"))),
|
WithStatus(Timestamp(time.Now().Add(-4*time.Hour)), StatusErr("a task error"))),
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
|
|
|
@ -29,6 +29,9 @@ type fakeClient struct {
|
||||||
networkListFunc func(options types.NetworkListOptions) ([]types.NetworkResource, error)
|
networkListFunc func(options types.NetworkListOptions) ([]types.NetworkResource, error)
|
||||||
secretListFunc func(options types.SecretListOptions) ([]swarm.Secret, error)
|
secretListFunc func(options types.SecretListOptions) ([]swarm.Secret, error)
|
||||||
configListFunc func(options types.ConfigListOptions) ([]swarm.Config, error)
|
configListFunc func(options types.ConfigListOptions) ([]swarm.Config, error)
|
||||||
|
nodeListFunc func(options types.NodeListOptions) ([]swarm.Node, error)
|
||||||
|
taskListFunc func(options types.TaskListOptions) ([]swarm.Task, error)
|
||||||
|
nodeInspectWithRaw func(ref string) (swarm.Node, []byte, error)
|
||||||
serviceRemoveFunc func(serviceID string) error
|
serviceRemoveFunc func(serviceID string) error
|
||||||
networkRemoveFunc func(networkID string) error
|
networkRemoveFunc func(networkID string) error
|
||||||
secretRemoveFunc func(secretID string) error
|
secretRemoveFunc func(secretID string) error
|
||||||
|
@ -102,6 +105,27 @@ func (cli *fakeClient) ConfigList(ctx context.Context, options types.ConfigListO
|
||||||
return configsList, nil
|
return configsList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cli *fakeClient) TaskList(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error) {
|
||||||
|
if cli.taskListFunc != nil {
|
||||||
|
return cli.taskListFunc(options)
|
||||||
|
}
|
||||||
|
return []swarm.Task{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli *fakeClient) NodeList(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error) {
|
||||||
|
if cli.nodeListFunc != nil {
|
||||||
|
return cli.nodeListFunc(options)
|
||||||
|
}
|
||||||
|
return []swarm.Node{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli *fakeClient) NodeInspectWithRaw(ctx context.Context, ref string) (swarm.Node, []byte, error) {
|
||||||
|
if cli.nodeInspectWithRaw != nil {
|
||||||
|
return cli.nodeInspectWithRaw(ref)
|
||||||
|
}
|
||||||
|
return swarm.Node{}, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (cli *fakeClient) ServiceRemove(ctx context.Context, serviceID string) error {
|
func (cli *fakeClient) ServiceRemove(ctx context.Context, serviceID string) error {
|
||||||
if cli.serviceRemoveFunc != nil {
|
if cli.serviceRemoveFunc != nil {
|
||||||
return cli.serviceRemoveFunc(serviceID)
|
return cli.serviceRemoveFunc(serviceID)
|
||||||
|
|
|
@ -18,7 +18,7 @@ type listOptions struct {
|
||||||
format string
|
format string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
|
func newListCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
opts := listOptions{}
|
opts := listOptions{}
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
|
@ -36,7 +36,7 @@ func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func runList(dockerCli *command.DockerCli, opts listOptions) error {
|
func runList(dockerCli command.Cli, opts listOptions) error {
|
||||||
client := dockerCli.Client()
|
client := dockerCli.Client()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
package stack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/cli/cli/internal/test"
|
||||||
|
// Import builders to get the builder function as package function
|
||||||
|
. "github.com/docker/cli/cli/internal/test/builders"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/api/types/swarm"
|
||||||
|
"github.com/docker/docker/pkg/testutil"
|
||||||
|
"github.com/docker/docker/pkg/testutil/golden"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestListErrors(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
args []string
|
||||||
|
flags map[string]string
|
||||||
|
serviceListFunc func(options types.ServiceListOptions) ([]swarm.Service, error)
|
||||||
|
expectedError string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
args: []string{"foo"},
|
||||||
|
expectedError: "accepts no argument",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
flags: map[string]string{
|
||||||
|
"format": "{{invalid format}}",
|
||||||
|
},
|
||||||
|
expectedError: "Template parsing error",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serviceListFunc: func(options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||||
|
return []swarm.Service{}, errors.Errorf("error getting services")
|
||||||
|
},
|
||||||
|
expectedError: "error getting services",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serviceListFunc: func(options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||||
|
return []swarm.Service{*Service()}, nil
|
||||||
|
},
|
||||||
|
expectedError: "cannot get label",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
cmd.Flags().Set(key, value)
|
||||||
|
}
|
||||||
|
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListWithFormat(t *testing.T) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
cmd := newListCommand(test.NewFakeCli(&fakeClient{
|
||||||
|
serviceListFunc: func(options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||||
|
return []swarm.Service{
|
||||||
|
*Service(
|
||||||
|
ServiceLabels(map[string]string{
|
||||||
|
"com.docker.stack.namespace": "service-name-foo",
|
||||||
|
}),
|
||||||
|
)}, nil
|
||||||
|
},
|
||||||
|
}, buf))
|
||||||
|
cmd.Flags().Set("format", "{{ .Name }}")
|
||||||
|
assert.NoError(t, cmd.Execute())
|
||||||
|
actual := buf.String()
|
||||||
|
expected := golden.Get(t, []byte(actual), "stack-list-with-format.golden")
|
||||||
|
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListWithoutFormat(t *testing.T) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
cmd := newListCommand(test.NewFakeCli(&fakeClient{
|
||||||
|
serviceListFunc: func(options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||||
|
return []swarm.Service{
|
||||||
|
*Service(
|
||||||
|
ServiceLabels(map[string]string{
|
||||||
|
"com.docker.stack.namespace": "service-name-foo",
|
||||||
|
}),
|
||||||
|
)}, nil
|
||||||
|
},
|
||||||
|
}, buf))
|
||||||
|
assert.NoError(t, cmd.Execute())
|
||||||
|
actual := buf.String()
|
||||||
|
expected := golden.Get(t, []byte(actual), "stack-list-without-format.golden")
|
||||||
|
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListOrder(t *testing.T) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
cmd := newListCommand(test.NewFakeCli(&fakeClient{
|
||||||
|
serviceListFunc: func(options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||||
|
return []swarm.Service{
|
||||||
|
*Service(
|
||||||
|
ServiceLabels(map[string]string{
|
||||||
|
"com.docker.stack.namespace": "service-name-foo",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
*Service(
|
||||||
|
ServiceLabels(map[string]string{
|
||||||
|
"com.docker.stack.namespace": "service-name-bar",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
}, buf))
|
||||||
|
assert.NoError(t, cmd.Execute())
|
||||||
|
actual := buf.String()
|
||||||
|
expected := golden.Get(t, []byte(actual), "stack-list-sort.golden")
|
||||||
|
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package stack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLoadBundlefileErrors(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
namespace string
|
||||||
|
path string
|
||||||
|
expectedError error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
namespace: "namespace_foo",
|
||||||
|
expectedError: fmt.Errorf("Bundle %s.dab not found", "namespace_foo"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "namespace_foo",
|
||||||
|
path: "invalid_path",
|
||||||
|
expectedError: fmt.Errorf("Bundle %s not found", "invalid_path"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "namespace_foo",
|
||||||
|
path: filepath.Join("testdata", "bundlefile_with_invalid_syntax"),
|
||||||
|
expectedError: fmt.Errorf("Error reading"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
_, err := loadBundlefile(&bytes.Buffer{}, tc.namespace, tc.path)
|
||||||
|
assert.Error(t, err, tc.expectedError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadBundlefile(t *testing.T) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
namespace := ""
|
||||||
|
path := filepath.Join("testdata", "bundlefile_with_two_services.dab")
|
||||||
|
bundleFile, err := loadBundlefile(buf, namespace, path)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, len(bundleFile.Services), 2)
|
||||||
|
}
|
|
@ -0,0 +1,185 @@
|
||||||
|
package stack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/cli/cli/config/configfile"
|
||||||
|
"github.com/docker/cli/cli/internal/test"
|
||||||
|
// Import builders to get the builder function as package function
|
||||||
|
. "github.com/docker/cli/cli/internal/test/builders"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/api/types/swarm"
|
||||||
|
"github.com/docker/docker/pkg/testutil"
|
||||||
|
"github.com/docker/docker/pkg/testutil/golden"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStackPsErrors(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
args []string
|
||||||
|
taskListFunc func(options types.TaskListOptions) ([]swarm.Task, error)
|
||||||
|
expectedError string
|
||||||
|
}{
|
||||||
|
|
||||||
|
{
|
||||||
|
args: []string{},
|
||||||
|
expectedError: "requires exactly 1 argument",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
args: []string{"foo", "bar"},
|
||||||
|
expectedError: "requires exactly 1 argument",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
args: []string{"foo"},
|
||||||
|
taskListFunc: func(options types.TaskListOptions) ([]swarm.Task, error) {
|
||||||
|
return nil, errors.Errorf("error getting tasks")
|
||||||
|
},
|
||||||
|
expectedError: "error getting tasks",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStackPsEmptyStack(t *testing.T) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
cmd := newPsCommand(test.NewFakeCli(&fakeClient{
|
||||||
|
taskListFunc: func(options types.TaskListOptions) ([]swarm.Task, error) {
|
||||||
|
return []swarm.Task{}, nil
|
||||||
|
},
|
||||||
|
}, buf))
|
||||||
|
cmd.SetArgs([]string{"foo"})
|
||||||
|
assert.NoError(t, cmd.Execute())
|
||||||
|
testutil.EqualNormalizedString(t, testutil.RemoveSpace, buf.String(), "Nothing found in stack: foo")
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
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()
|
||||||
|
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(
|
||||||
|
TaskNodeID("id-node-foo"),
|
||||||
|
)}, nil
|
||||||
|
},
|
||||||
|
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()
|
||||||
|
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()
|
||||||
|
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{
|
||||||
|
TasksFormat: "{{ .Name }}",
|
||||||
|
})
|
||||||
|
cmd := newPsCommand(cli)
|
||||||
|
cmd.SetArgs([]string{"foo"})
|
||||||
|
assert.NoError(t, cmd.Execute())
|
||||||
|
actual := buf.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(
|
||||||
|
TaskID("id-foo"),
|
||||||
|
TaskServiceID("service-id-foo"),
|
||||||
|
TaskNodeID("id-node"),
|
||||||
|
WithTaskSpec(TaskImage("myimage:mytag")),
|
||||||
|
TaskDesiredState(swarm.TaskStateReady),
|
||||||
|
WithStatus(TaskState(swarm.TaskStateFailed), Timestamp(time.Now().Add(-2*time.Hour))),
|
||||||
|
)}, nil
|
||||||
|
},
|
||||||
|
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()
|
||||||
|
expected := golden.Get(t, []byte(actual), "stack-ps-without-format.golden")
|
||||||
|
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||||
|
}
|
|
@ -87,7 +87,7 @@ func TestSkipEmptyStack(t *testing.T) {
|
||||||
assert.Equal(t, allConfigIDs, cli.removedConfigs)
|
assert.Equal(t, allConfigIDs, cli.removedConfigs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContinueAfterError(t *testing.T) {
|
func TestRemoveContinueAfterError(t *testing.T) {
|
||||||
allServices := []string{objectName("foo", "service1"), objectName("bar", "service1")}
|
allServices := []string{objectName("foo", "service1"), objectName("bar", "service1")}
|
||||||
allServiceIDs := buildObjectIDs(allServices)
|
allServiceIDs := buildObjectIDs(allServices)
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ type servicesOptions struct {
|
||||||
namespace string
|
namespace string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newServicesCommand(dockerCli *command.DockerCli) *cobra.Command {
|
func newServicesCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
options := servicesOptions{filter: opts.NewFilterOpt()}
|
options := servicesOptions{filter: opts.NewFilterOpt()}
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
|
@ -41,7 +41,7 @@ func newServicesCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func runServices(dockerCli *command.DockerCli, options servicesOptions) error {
|
func runServices(dockerCli command.Cli, options servicesOptions) error {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
client := dockerCli.Client()
|
client := dockerCli.Client()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,180 @@
|
||||||
|
package stack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/cli/cli/config/configfile"
|
||||||
|
"github.com/docker/cli/cli/internal/test"
|
||||||
|
// Import builders to get the builder function as package function
|
||||||
|
. "github.com/docker/cli/cli/internal/test/builders"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/api/types/swarm"
|
||||||
|
"github.com/docker/docker/pkg/testutil"
|
||||||
|
"github.com/docker/docker/pkg/testutil/golden"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStackServicesErrors(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
args []string
|
||||||
|
flags map[string]string
|
||||||
|
serviceListFunc func(options types.ServiceListOptions) ([]swarm.Service, error)
|
||||||
|
nodeListFunc func(options types.NodeListOptions) ([]swarm.Node, error)
|
||||||
|
taskListFunc func(options types.TaskListOptions) ([]swarm.Task, error)
|
||||||
|
expectedError string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
args: []string{"foo"},
|
||||||
|
serviceListFunc: func(options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||||
|
return nil, errors.Errorf("error getting services")
|
||||||
|
},
|
||||||
|
expectedError: "error getting services",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
args: []string{"foo"},
|
||||||
|
serviceListFunc: func(options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||||
|
return []swarm.Service{*Service()}, nil
|
||||||
|
},
|
||||||
|
nodeListFunc: func(options types.NodeListOptions) ([]swarm.Node, error) {
|
||||||
|
return nil, errors.Errorf("error getting nodes")
|
||||||
|
},
|
||||||
|
expectedError: "error getting nodes",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
args: []string{"foo"},
|
||||||
|
serviceListFunc: func(options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||||
|
return []swarm.Service{*Service()}, nil
|
||||||
|
},
|
||||||
|
taskListFunc: func(options types.TaskListOptions) ([]swarm.Task, error) {
|
||||||
|
return nil, errors.Errorf("error getting tasks")
|
||||||
|
},
|
||||||
|
expectedError: "error getting tasks",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
args: []string{"foo"},
|
||||||
|
flags: map[string]string{
|
||||||
|
"format": "{{invalid format}}",
|
||||||
|
},
|
||||||
|
serviceListFunc: func(options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||||
|
return []swarm.Service{*Service()}, nil
|
||||||
|
},
|
||||||
|
expectedError: "Template parsing error",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
cli := test.NewFakeCli(&fakeClient{
|
||||||
|
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 {
|
||||||
|
cmd.Flags().Set(key, value)
|
||||||
|
}
|
||||||
|
cmd.SetOutput(ioutil.Discard)
|
||||||
|
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
)
|
||||||
|
cmd.SetArgs([]string{"foo"})
|
||||||
|
assert.NoError(t, cmd.Execute())
|
||||||
|
testutil.EqualNormalizedString(t, testutil.RemoveSpace, buf.String(), "Nothing found in stack: foo")
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
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()
|
||||||
|
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{
|
||||||
|
ServicesFormat: "{{ .Name }}",
|
||||||
|
})
|
||||||
|
cmd := newServicesCommand(cli)
|
||||||
|
cmd.SetArgs([]string{"foo"})
|
||||||
|
assert.NoError(t, cmd.Execute())
|
||||||
|
actual := buf.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(
|
||||||
|
ServiceName("name-foo"),
|
||||||
|
ServiceID("id-foo"),
|
||||||
|
ReplicatedService(2),
|
||||||
|
ServiceImage("busybox:latest"),
|
||||||
|
ServicePort(swarm.PortConfig{
|
||||||
|
PublishMode: swarm.PortConfigPublishModeIngress,
|
||||||
|
PublishedPort: 0,
|
||||||
|
TargetPort: 3232,
|
||||||
|
Protocol: swarm.PortConfigProtocolTCP,
|
||||||
|
}),
|
||||||
|
)}, nil
|
||||||
|
},
|
||||||
|
}, buf)
|
||||||
|
cli.SetConfigfile(&configfile.ConfigFile{})
|
||||||
|
cmd := newServicesCommand(cli)
|
||||||
|
cmd.SetArgs([]string{"foo"})
|
||||||
|
assert.NoError(t, cmd.Execute())
|
||||||
|
actual := buf.String()
|
||||||
|
expected := golden.Get(t, []byte(actual), "stack-services-without-format.golden")
|
||||||
|
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"Services": {
|
||||||
|
"visualizer": {
|
||||||
|
"Image": "busybox@sha256:32f093055929dbc23dec4d03e09dfe971f5973a9ca5cf059cbfb644c206aa83f",
|
||||||
|
"Networks": [
|
||||||
|
"webnet"
|
||||||
|
],
|
||||||
|
"Ports": [
|
||||||
|
{
|
||||||
|
"Port": 8080,
|
||||||
|
"Protocol": "tcp"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"web": {
|
||||||
|
"Image": "busybox@sha256:32f093055929dbc23dec4d03e09dfe971f5973a9ca5cf059cbfb644c206aa83f",
|
||||||
|
"Networks": [
|
||||||
|
"webnet"
|
||||||
|
],
|
||||||
|
"Ports": [
|
||||||
|
{
|
||||||
|
"Port": 80,
|
||||||
|
"Protocol": "tcp"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Version": "0.1"
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
NAME SERVICES
|
||||||
|
service-name-bar 1
|
||||||
|
service-name-foo 1
|
|
@ -0,0 +1 @@
|
||||||
|
service-name-foo
|
|
@ -0,0 +1,2 @@
|
||||||
|
NAME SERVICES
|
||||||
|
service-name-foo 1
|
|
@ -0,0 +1 @@
|
||||||
|
service-id-foo.1
|
|
@ -0,0 +1 @@
|
||||||
|
service-id-foo.1
|
|
@ -0,0 +1 @@
|
||||||
|
id-node-foo
|
|
@ -0,0 +1 @@
|
||||||
|
xn4cypcov06f2w8gsbaf2lst3
|
|
@ -0,0 +1 @@
|
||||||
|
id-foo
|
|
@ -0,0 +1,2 @@
|
||||||
|
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
|
||||||
|
id-foo service-id-foo.1 myimage:mytag node-name-bar Ready Failed 2 hours ago
|
|
@ -0,0 +1 @@
|
||||||
|
service-name-foo
|
|
@ -0,0 +1 @@
|
||||||
|
service-name-foo
|
|
@ -0,0 +1 @@
|
||||||
|
id-foo
|
|
@ -0,0 +1,2 @@
|
||||||
|
ID NAME MODE REPLICAS IMAGE PORTS
|
||||||
|
id-foo name-foo replicated 0/2 busybox:latest *:0->3232/tcp
|
|
@ -14,6 +14,7 @@ func Service(builders ...func(*swarm.Service)) *swarm.Service {
|
||||||
Annotations: swarm.Annotations{
|
Annotations: swarm.Annotations{
|
||||||
Name: "defaultServiceName",
|
Name: "defaultServiceName",
|
||||||
},
|
},
|
||||||
|
EndpointSpec: &swarm.EndpointSpec{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,9 +25,44 @@ func Service(builders ...func(*swarm.Service)) *swarm.Service {
|
||||||
return service
|
return service
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServiceID sets the service ID
|
||||||
|
func ServiceID(ID string) func(*swarm.Service) {
|
||||||
|
return func(service *swarm.Service) {
|
||||||
|
service.ID = ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ServiceName sets the service name
|
// ServiceName sets the service name
|
||||||
func ServiceName(name string) func(*swarm.Service) {
|
func ServiceName(name string) func(*swarm.Service) {
|
||||||
return func(service *swarm.Service) {
|
return func(service *swarm.Service) {
|
||||||
service.Spec.Annotations.Name = name
|
service.Spec.Annotations.Name = name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServiceLabels sets the service's labels
|
||||||
|
func ServiceLabels(labels map[string]string) func(*swarm.Service) {
|
||||||
|
return func(service *swarm.Service) {
|
||||||
|
service.Spec.Annotations.Labels = labels
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplicatedService sets the number of replicas for the service
|
||||||
|
func ReplicatedService(replicas uint64) func(*swarm.Service) {
|
||||||
|
return func(service *swarm.Service) {
|
||||||
|
service.Spec.Mode = swarm.ServiceMode{Replicated: &swarm.ReplicatedService{Replicas: &replicas}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceImage sets the service's image
|
||||||
|
func ServiceImage(image string) func(*swarm.Service) {
|
||||||
|
return func(service *swarm.Service) {
|
||||||
|
service.Spec.TaskTemplate = swarm.TaskSpec{ContainerSpec: swarm.ContainerSpec{Image: image}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServicePort sets the service's port
|
||||||
|
func ServicePort(port swarm.PortConfig) func(*swarm.Service) {
|
||||||
|
return func(service *swarm.Service) {
|
||||||
|
service.Spec.EndpointSpec.Ports = append(service.Spec.EndpointSpec.Ports, port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -42,13 +42,34 @@ func TaskID(id string) func(*swarm.Task) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServiceID sets the task service's ID
|
// TaskName sets the task name
|
||||||
func ServiceID(id string) func(*swarm.Task) {
|
func TaskName(name string) func(*swarm.Task) {
|
||||||
|
return func(task *swarm.Task) {
|
||||||
|
task.Annotations.Name = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskServiceID sets the task service's ID
|
||||||
|
func TaskServiceID(id string) func(*swarm.Task) {
|
||||||
return func(task *swarm.Task) {
|
return func(task *swarm.Task) {
|
||||||
task.ServiceID = id
|
task.ServiceID = id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TaskNodeID sets the task's node id
|
||||||
|
func TaskNodeID(id string) func(*swarm.Task) {
|
||||||
|
return func(task *swarm.Task) {
|
||||||
|
task.NodeID = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskDesiredState sets the task's desired state
|
||||||
|
func TaskDesiredState(state swarm.TaskState) func(*swarm.Task) {
|
||||||
|
return func(task *swarm.Task) {
|
||||||
|
task.DesiredState = state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithStatus sets the task status
|
// WithStatus sets the task status
|
||||||
func WithStatus(statusBuilders ...func(*swarm.TaskStatus)) func(*swarm.Task) {
|
func WithStatus(statusBuilders ...func(*swarm.TaskStatus)) func(*swarm.Task) {
|
||||||
return func(task *swarm.Task) {
|
return func(task *swarm.Task) {
|
||||||
|
@ -86,6 +107,13 @@ func StatusErr(err string) func(*swarm.TaskStatus) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TaskState sets the task's current state
|
||||||
|
func TaskState(state swarm.TaskState) func(*swarm.TaskStatus) {
|
||||||
|
return func(taskStatus *swarm.TaskStatus) {
|
||||||
|
taskStatus.State = state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// PortStatus sets the tasks port config status
|
// PortStatus sets the tasks port config status
|
||||||
// FIXME(vdemeester) should be a sub builder 👼
|
// FIXME(vdemeester) should be a sub builder 👼
|
||||||
func PortStatus(portConfigs []swarm.PortConfig) func(*swarm.TaskStatus) {
|
func PortStatus(portConfigs []swarm.PortConfig) func(*swarm.TaskStatus) {
|
||||||
|
@ -94,6 +122,13 @@ func PortStatus(portConfigs []swarm.PortConfig) func(*swarm.TaskStatus) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithTaskSpec sets the task spec
|
||||||
|
func WithTaskSpec(specBuilders ...func(*swarm.TaskSpec)) func(*swarm.Task) {
|
||||||
|
return func(task *swarm.Task) {
|
||||||
|
task.Spec = *TaskSpec(specBuilders...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TaskSpec creates a task spec with default values .
|
// TaskSpec creates a task spec with default values .
|
||||||
// Any number of taskSpec function builder can be pass to augment it.
|
// Any number of taskSpec function builder can be pass to augment it.
|
||||||
func TaskSpec(specBuilders ...func(*swarm.TaskSpec)) *swarm.TaskSpec {
|
func TaskSpec(specBuilders ...func(*swarm.TaskSpec)) *swarm.TaskSpec {
|
||||||
|
@ -109,3 +144,10 @@ func TaskSpec(specBuilders ...func(*swarm.TaskSpec)) *swarm.TaskSpec {
|
||||||
|
|
||||||
return taskSpec
|
return taskSpec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TaskImage sets the task's image
|
||||||
|
func TaskImage(image string) func(*swarm.TaskSpec) {
|
||||||
|
return func(taskSpec *swarm.TaskSpec) {
|
||||||
|
taskSpec.ContainerSpec.Image = image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue