Merge pull request #29143 from vdemeester/node-cli-unit-tests
Add some unit tests to the node and swarm cli code
This commit is contained in:
@ -32,7 +32,15 @@ type Streams interface {
|
||||
Err() io.Writer
|
||||
}
|
||||
|
||||
// DockerCli represents the docker command line client.
|
||||
// Cli represents the docker command line client.
|
||||
type Cli interface {
|
||||
Client() client.APIClient
|
||||
Out() *OutStream
|
||||
Err() io.Writer
|
||||
In() *InStream
|
||||
}
|
||||
|
||||
// DockerCli is an instance the docker command line client.
|
||||
// Instances of the client can be returned from NewDockerCli.
|
||||
type DockerCli struct {
|
||||
configFile *configfile.ConfigFile
|
||||
|
||||
68
command/node/client_test.go
Normal file
68
command/node/client_test.go
Normal file
@ -0,0 +1,68 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/client"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type fakeClient struct {
|
||||
client.Client
|
||||
infoFunc func() (types.Info, error)
|
||||
nodeInspectFunc func() (swarm.Node, []byte, error)
|
||||
nodeListFunc func() ([]swarm.Node, error)
|
||||
nodeRemoveFunc func() error
|
||||
nodeUpdateFunc func(nodeID string, version swarm.Version, node swarm.NodeSpec) error
|
||||
taskInspectFunc func(taskID string) (swarm.Task, []byte, error)
|
||||
taskListFunc func(options types.TaskListOptions) ([]swarm.Task, error)
|
||||
}
|
||||
|
||||
func (cli *fakeClient) NodeInspectWithRaw(ctx context.Context, ref string) (swarm.Node, []byte, error) {
|
||||
if cli.nodeInspectFunc != nil {
|
||||
return cli.nodeInspectFunc()
|
||||
}
|
||||
return swarm.Node{}, []byte{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) NodeList(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error) {
|
||||
if cli.nodeListFunc != nil {
|
||||
return cli.nodeListFunc()
|
||||
}
|
||||
return []swarm.Node{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) NodeRemove(ctx context.Context, nodeID string, options types.NodeRemoveOptions) error {
|
||||
if cli.nodeRemoveFunc != nil {
|
||||
return cli.nodeRemoveFunc()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) NodeUpdate(ctx context.Context, nodeID string, version swarm.Version, node swarm.NodeSpec) error {
|
||||
if cli.nodeUpdateFunc != nil {
|
||||
return cli.nodeUpdateFunc(nodeID, version, node)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) Info(ctx context.Context) (types.Info, error) {
|
||||
if cli.infoFunc != nil {
|
||||
return cli.infoFunc()
|
||||
}
|
||||
return types.Info{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error) {
|
||||
if cli.taskInspectFunc != nil {
|
||||
return cli.taskInspectFunc(taskID)
|
||||
}
|
||||
return swarm.Task{}, []byte{}, 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
|
||||
}
|
||||
@ -9,7 +9,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newDemoteCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newDemoteCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "demote NODE [NODE...]",
|
||||
Short: "Demote one or more nodes from manager in the swarm",
|
||||
@ -20,7 +20,7 @@ func newDemoteCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func runDemote(dockerCli *command.DockerCli, nodes []string) error {
|
||||
func runDemote(dockerCli command.Cli, nodes []string) error {
|
||||
demote := func(node *swarm.Node) error {
|
||||
if node.Spec.Role == swarm.NodeRoleWorker {
|
||||
fmt.Fprintf(dockerCli.Out(), "Node %s is already a worker.\n", node.ID)
|
||||
|
||||
88
command/node/demote_test.go
Normal file
88
command/node/demote_test.go
Normal file
@ -0,0 +1,88 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/cli/internal/test"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/docker/cli/internal/test/builders"
|
||||
"github.com/docker/docker/pkg/testutil/assert"
|
||||
)
|
||||
|
||||
func TestNodeDemoteErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
args []string
|
||||
nodeInspectFunc func() (swarm.Node, []byte, error)
|
||||
nodeUpdateFunc func(nodeID string, version swarm.Version, node swarm.NodeSpec) error
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
expectedError: "requires at least 1 argument",
|
||||
},
|
||||
{
|
||||
args: []string{"nodeID"},
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return swarm.Node{}, []byte{}, fmt.Errorf("error inspecting the node")
|
||||
},
|
||||
expectedError: "error inspecting the node",
|
||||
},
|
||||
{
|
||||
args: []string{"nodeID"},
|
||||
nodeUpdateFunc: func(nodeID string, version swarm.Version, node swarm.NodeSpec) error {
|
||||
return fmt.Errorf("error updating the node")
|
||||
},
|
||||
expectedError: "error updating the node",
|
||||
},
|
||||
}
|
||||
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)
|
||||
assert.Error(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeDemoteNoChange(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newDemoteCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return *Node(), []byte{}, nil
|
||||
},
|
||||
nodeUpdateFunc: func(nodeID string, version swarm.Version, node swarm.NodeSpec) error {
|
||||
if node.Role != swarm.NodeRoleWorker {
|
||||
return fmt.Errorf("expected role worker, got %s", node.Role)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}, buf))
|
||||
cmd.SetArgs([]string{"nodeID"})
|
||||
assert.NilError(t, cmd.Execute())
|
||||
}
|
||||
|
||||
func TestNodeDemoteMultipleNode(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newDemoteCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return *Node(Manager()), []byte{}, nil
|
||||
},
|
||||
nodeUpdateFunc: func(nodeID string, version swarm.Version, node swarm.NodeSpec) error {
|
||||
if node.Role != swarm.NodeRoleWorker {
|
||||
return fmt.Errorf("expected role worker, got %s", node.Role)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}, buf))
|
||||
cmd.SetArgs([]string{"nodeID1", "nodeID2"})
|
||||
assert.NilError(t, cmd.Execute())
|
||||
}
|
||||
@ -22,7 +22,7 @@ type inspectOptions struct {
|
||||
pretty bool
|
||||
}
|
||||
|
||||
func newInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newInspectCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts inspectOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -41,7 +41,7 @@ func newInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runInspect(dockerCli *command.DockerCli, opts inspectOptions) error {
|
||||
func runInspect(dockerCli command.Cli, opts inspectOptions) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
getRef := func(ref string) (interface{}, []byte, error) {
|
||||
|
||||
122
command/node/inspect_test.go
Normal file
122
command/node/inspect_test.go
Normal file
@ -0,0 +1,122 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/cli/internal/test"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/docker/cli/internal/test/builders"
|
||||
"github.com/docker/docker/pkg/testutil/assert"
|
||||
"github.com/docker/docker/pkg/testutil/golden"
|
||||
)
|
||||
|
||||
func TestNodeInspectErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
args []string
|
||||
flags map[string]string
|
||||
nodeInspectFunc func() (swarm.Node, []byte, error)
|
||||
infoFunc func() (types.Info, error)
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
expectedError: "requires at least 1 argument",
|
||||
},
|
||||
{
|
||||
args: []string{"self"},
|
||||
infoFunc: func() (types.Info, error) {
|
||||
return types.Info{}, fmt.Errorf("error asking for node info")
|
||||
},
|
||||
expectedError: "error asking for node info",
|
||||
},
|
||||
{
|
||||
args: []string{"nodeID"},
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return swarm.Node{}, []byte{}, fmt.Errorf("error inspecting the node")
|
||||
},
|
||||
infoFunc: func() (types.Info, error) {
|
||||
return types.Info{}, fmt.Errorf("error asking for node info")
|
||||
},
|
||||
expectedError: "error inspecting the node",
|
||||
},
|
||||
{
|
||||
args: []string{"self"},
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return swarm.Node{}, []byte{}, fmt.Errorf("error inspecting the node")
|
||||
},
|
||||
infoFunc: func() (types.Info, error) {
|
||||
return types.Info{}, nil
|
||||
},
|
||||
expectedError: "error inspecting the node",
|
||||
},
|
||||
{
|
||||
args: []string{"self"},
|
||||
flags: map[string]string{
|
||||
"pretty": "true",
|
||||
},
|
||||
infoFunc: func() (types.Info, error) {
|
||||
return types.Info{}, fmt.Errorf("error asking for node info")
|
||||
},
|
||||
expectedError: "error asking for node info",
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newInspectCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
nodeInspectFunc: tc.nodeInspectFunc,
|
||||
infoFunc: tc.infoFunc,
|
||||
}, buf))
|
||||
cmd.SetArgs(tc.args)
|
||||
for key, value := range tc.flags {
|
||||
cmd.Flags().Set(key, value)
|
||||
}
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
assert.Error(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeInspectPretty(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
nodeInspectFunc func() (swarm.Node, []byte, error)
|
||||
}{
|
||||
{
|
||||
name: "simple",
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return *Node(NodeLabels(map[string]string{
|
||||
"lbl1": "value1",
|
||||
})), []byte{}, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "manager",
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return *Node(Manager()), []byte{}, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "manager-leader",
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return *Node(Manager(Leader())), []byte{}, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newInspectCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
nodeInspectFunc: tc.nodeInspectFunc,
|
||||
}, buf))
|
||||
cmd.SetArgs([]string{"nodeID"})
|
||||
cmd.Flags().Set("pretty", "true")
|
||||
assert.NilError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
expected := golden.Get(t, []byte(actual), fmt.Sprintf("node-inspect-pretty.%s.golden", tc.name))
|
||||
assert.EqualNormalizedString(t, assert.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
}
|
||||
@ -24,7 +24,7 @@ type listOptions struct {
|
||||
filter opts.FilterOpt
|
||||
}
|
||||
|
||||
func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newListCommand(dockerCli command.Cli) *cobra.Command {
|
||||
opts := listOptions{filter: opts.NewFilterOpt()}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -43,7 +43,7 @@ func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runList(dockerCli *command.DockerCli, opts listOptions) error {
|
||||
func runList(dockerCli command.Cli, opts listOptions) error {
|
||||
client := dockerCli.Client()
|
||||
out := dockerCli.Out()
|
||||
ctx := context.Background()
|
||||
|
||||
101
command/node/list_test.go
Normal file
101
command/node/list_test.go
Normal file
@ -0,0 +1,101 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/cli/internal/test"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/docker/cli/internal/test/builders"
|
||||
"github.com/docker/docker/pkg/testutil/assert"
|
||||
)
|
||||
|
||||
func TestNodeListErrorOnAPIFailure(t *testing.T) {
|
||||
testCases := []struct {
|
||||
nodeListFunc func() ([]swarm.Node, error)
|
||||
infoFunc func() (types.Info, error)
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
nodeListFunc: func() ([]swarm.Node, error) {
|
||||
return []swarm.Node{}, fmt.Errorf("error listing nodes")
|
||||
},
|
||||
expectedError: "error listing nodes",
|
||||
},
|
||||
{
|
||||
nodeListFunc: func() ([]swarm.Node, error) {
|
||||
return []swarm.Node{
|
||||
{
|
||||
ID: "nodeID",
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
infoFunc: func() (types.Info, error) {
|
||||
return types.Info{}, fmt.Errorf("error asking for node info")
|
||||
},
|
||||
expectedError: "error asking for node info",
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newListCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
nodeListFunc: tc.nodeListFunc,
|
||||
infoFunc: tc.infoFunc,
|
||||
}, buf))
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
assert.Error(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeList(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newListCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
nodeListFunc: func() ([]swarm.Node, error) {
|
||||
return []swarm.Node{
|
||||
*Node(NodeID("nodeID1"), Hostname("nodeHostname1"), Manager(Leader())),
|
||||
*Node(NodeID("nodeID2"), Hostname("nodeHostname2"), Manager()),
|
||||
*Node(NodeID("nodeID3"), Hostname("nodeHostname3")),
|
||||
}, nil
|
||||
},
|
||||
infoFunc: func() (types.Info, error) {
|
||||
return types.Info{
|
||||
Swarm: swarm.Info{
|
||||
NodeID: "nodeID1",
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
}, buf))
|
||||
assert.NilError(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`)
|
||||
}
|
||||
|
||||
func TestNodeListQuietShouldOnlyPrintIDs(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newListCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
nodeListFunc: func() ([]swarm.Node, error) {
|
||||
return []swarm.Node{
|
||||
*Node(),
|
||||
}, nil
|
||||
},
|
||||
}, buf))
|
||||
cmd.Flags().Set("quiet", "true")
|
||||
assert.NilError(t, cmd.Execute())
|
||||
assert.Contains(t, buf.String(), "nodeID")
|
||||
}
|
||||
|
||||
// Test case for #24090
|
||||
func TestNodeListContainsHostname(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newListCommand(test.NewFakeCli(&fakeClient{}, buf))
|
||||
assert.NilError(t, cmd.Execute())
|
||||
assert.Contains(t, buf.String(), "HOSTNAME")
|
||||
}
|
||||
@ -1,12 +1,7 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/opts"
|
||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
||||
)
|
||||
|
||||
type nodeOptions struct {
|
||||
@ -27,34 +22,3 @@ func newNodeOptions() *nodeOptions {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (opts *nodeOptions) ToNodeSpec() (swarm.NodeSpec, error) {
|
||||
var spec swarm.NodeSpec
|
||||
|
||||
spec.Annotations.Name = opts.annotations.name
|
||||
spec.Annotations.Labels = runconfigopts.ConvertKVStringsToMap(opts.annotations.labels.GetAll())
|
||||
|
||||
switch swarm.NodeRole(strings.ToLower(opts.role)) {
|
||||
case swarm.NodeRoleWorker:
|
||||
spec.Role = swarm.NodeRoleWorker
|
||||
case swarm.NodeRoleManager:
|
||||
spec.Role = swarm.NodeRoleManager
|
||||
case "":
|
||||
default:
|
||||
return swarm.NodeSpec{}, fmt.Errorf("invalid role %q, only worker and manager are supported", opts.role)
|
||||
}
|
||||
|
||||
switch swarm.NodeAvailability(strings.ToLower(opts.availability)) {
|
||||
case swarm.NodeAvailabilityActive:
|
||||
spec.Availability = swarm.NodeAvailabilityActive
|
||||
case swarm.NodeAvailabilityPause:
|
||||
spec.Availability = swarm.NodeAvailabilityPause
|
||||
case swarm.NodeAvailabilityDrain:
|
||||
spec.Availability = swarm.NodeAvailabilityDrain
|
||||
case "":
|
||||
default:
|
||||
return swarm.NodeSpec{}, fmt.Errorf("invalid availability %q, only active, pause and drain are supported", opts.availability)
|
||||
}
|
||||
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newPromoteCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newPromoteCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "promote NODE [NODE...]",
|
||||
Short: "Promote one or more nodes to manager in the swarm",
|
||||
@ -20,7 +20,7 @@ func newPromoteCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func runPromote(dockerCli *command.DockerCli, nodes []string) error {
|
||||
func runPromote(dockerCli command.Cli, nodes []string) error {
|
||||
promote := func(node *swarm.Node) error {
|
||||
if node.Spec.Role == swarm.NodeRoleManager {
|
||||
fmt.Fprintf(dockerCli.Out(), "Node %s is already a manager.\n", node.ID)
|
||||
|
||||
88
command/node/promote_test.go
Normal file
88
command/node/promote_test.go
Normal file
@ -0,0 +1,88 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/cli/internal/test"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/docker/cli/internal/test/builders"
|
||||
"github.com/docker/docker/pkg/testutil/assert"
|
||||
)
|
||||
|
||||
func TestNodePromoteErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
args []string
|
||||
nodeInspectFunc func() (swarm.Node, []byte, error)
|
||||
nodeUpdateFunc func(nodeID string, version swarm.Version, node swarm.NodeSpec) error
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
expectedError: "requires at least 1 argument",
|
||||
},
|
||||
{
|
||||
args: []string{"nodeID"},
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return swarm.Node{}, []byte{}, fmt.Errorf("error inspecting the node")
|
||||
},
|
||||
expectedError: "error inspecting the node",
|
||||
},
|
||||
{
|
||||
args: []string{"nodeID"},
|
||||
nodeUpdateFunc: func(nodeID string, version swarm.Version, node swarm.NodeSpec) error {
|
||||
return fmt.Errorf("error updating the node")
|
||||
},
|
||||
expectedError: "error updating the node",
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newPromoteCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
nodeInspectFunc: tc.nodeInspectFunc,
|
||||
nodeUpdateFunc: tc.nodeUpdateFunc,
|
||||
}, buf))
|
||||
cmd.SetArgs(tc.args)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
assert.Error(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodePromoteNoChange(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newPromoteCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return *Node(Manager()), []byte{}, nil
|
||||
},
|
||||
nodeUpdateFunc: func(nodeID string, version swarm.Version, node swarm.NodeSpec) error {
|
||||
if node.Role != swarm.NodeRoleManager {
|
||||
return fmt.Errorf("expected role manager, got %s", node.Role)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}, buf))
|
||||
cmd.SetArgs([]string{"nodeID"})
|
||||
assert.NilError(t, cmd.Execute())
|
||||
}
|
||||
|
||||
func TestNodePromoteMultipleNode(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newPromoteCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return *Node(), []byte{}, nil
|
||||
},
|
||||
nodeUpdateFunc: func(nodeID string, version swarm.Version, node swarm.NodeSpec) error {
|
||||
if node.Role != swarm.NodeRoleManager {
|
||||
return fmt.Errorf("expected role manager, got %s", node.Role)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}, buf))
|
||||
cmd.SetArgs([]string{"nodeID1", "nodeID2"})
|
||||
assert.NilError(t, cmd.Execute())
|
||||
}
|
||||
@ -22,7 +22,7 @@ type psOptions struct {
|
||||
filter opts.FilterOpt
|
||||
}
|
||||
|
||||
func newPsCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newPsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
opts := psOptions{filter: opts.NewFilterOpt()}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -47,7 +47,7 @@ func newPsCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runPs(dockerCli *command.DockerCli, opts psOptions) error {
|
||||
func runPs(dockerCli command.Cli, opts psOptions) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
|
||||
|
||||
132
command/node/ps_test.go
Normal file
132
command/node/ps_test.go
Normal file
@ -0,0 +1,132 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/cli/internal/test"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/docker/cli/internal/test/builders"
|
||||
"github.com/docker/docker/pkg/testutil/assert"
|
||||
"github.com/docker/docker/pkg/testutil/golden"
|
||||
)
|
||||
|
||||
func TestNodePsErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
args []string
|
||||
flags map[string]string
|
||||
infoFunc func() (types.Info, error)
|
||||
nodeInspectFunc func() (swarm.Node, []byte, error)
|
||||
taskListFunc func(options types.TaskListOptions) ([]swarm.Task, error)
|
||||
taskInspectFunc func(taskID string) (swarm.Task, []byte, error)
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
infoFunc: func() (types.Info, error) {
|
||||
return types.Info{}, fmt.Errorf("error asking for node info")
|
||||
},
|
||||
expectedError: "error asking for node info",
|
||||
},
|
||||
{
|
||||
args: []string{"nodeID"},
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return swarm.Node{}, []byte{}, fmt.Errorf("error inspecting the node")
|
||||
},
|
||||
expectedError: "error inspecting the node",
|
||||
},
|
||||
{
|
||||
args: []string{"nodeID"},
|
||||
taskListFunc: func(options types.TaskListOptions) ([]swarm.Task, error) {
|
||||
return []swarm.Task{}, fmt.Errorf("error returning the task list")
|
||||
},
|
||||
expectedError: "error returning the task list",
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newPsCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
infoFunc: tc.infoFunc,
|
||||
nodeInspectFunc: tc.nodeInspectFunc,
|
||||
taskInspectFunc: tc.taskInspectFunc,
|
||||
taskListFunc: tc.taskListFunc,
|
||||
}, buf))
|
||||
cmd.SetArgs(tc.args)
|
||||
for key, value := range tc.flags {
|
||||
cmd.Flags().Set(key, value)
|
||||
}
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
assert.Error(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodePs(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
flags map[string]string
|
||||
infoFunc func() (types.Info, error)
|
||||
nodeInspectFunc func() (swarm.Node, []byte, error)
|
||||
taskListFunc func(options types.TaskListOptions) ([]swarm.Task, error)
|
||||
taskInspectFunc func(taskID string) (swarm.Task, []byte, error)
|
||||
}{
|
||||
{
|
||||
name: "simple",
|
||||
args: []string{"nodeID"},
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return *Node(), []byte{}, nil
|
||||
},
|
||||
taskListFunc: func(options types.TaskListOptions) ([]swarm.Task, error) {
|
||||
return []swarm.Task{
|
||||
*Task(WithStatus(Timestamp(time.Now().Add(-2*time.Hour)), PortStatus([]swarm.PortConfig{
|
||||
{
|
||||
TargetPort: 80,
|
||||
PublishedPort: 80,
|
||||
Protocol: "tcp",
|
||||
},
|
||||
}))),
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with-errors",
|
||||
args: []string{"nodeID"},
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return *Node(), []byte{}, nil
|
||||
},
|
||||
taskListFunc: func(options types.TaskListOptions) ([]swarm.Task, error) {
|
||||
return []swarm.Task{
|
||||
*Task(TaskID("taskID1"), ServiceID("failure"),
|
||||
WithStatus(Timestamp(time.Now().Add(-2*time.Hour)), StatusErr("a task error"))),
|
||||
*Task(TaskID("taskID2"), ServiceID("failure"),
|
||||
WithStatus(Timestamp(time.Now().Add(-3*time.Hour)), StatusErr("a task error"))),
|
||||
*Task(TaskID("taskID3"), ServiceID("failure"),
|
||||
WithStatus(Timestamp(time.Now().Add(-4*time.Hour)), StatusErr("a task error"))),
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newPsCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
infoFunc: tc.infoFunc,
|
||||
nodeInspectFunc: tc.nodeInspectFunc,
|
||||
taskInspectFunc: tc.taskInspectFunc,
|
||||
taskListFunc: tc.taskListFunc,
|
||||
}, buf))
|
||||
cmd.SetArgs(tc.args)
|
||||
for key, value := range tc.flags {
|
||||
cmd.Flags().Set(key, value)
|
||||
}
|
||||
assert.NilError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
expected := golden.Get(t, []byte(actual), fmt.Sprintf("node-ps.%s.golden", tc.name))
|
||||
assert.EqualNormalizedString(t, assert.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
}
|
||||
@ -16,7 +16,7 @@ type removeOptions struct {
|
||||
force bool
|
||||
}
|
||||
|
||||
func newRemoveCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newRemoveCommand(dockerCli command.Cli) *cobra.Command {
|
||||
opts := removeOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -33,7 +33,7 @@ func newRemoveCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRemove(dockerCli *command.DockerCli, args []string, opts removeOptions) error {
|
||||
func runRemove(dockerCli command.Cli, args []string, opts removeOptions) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
|
||||
|
||||
47
command/node/remove_test.go
Normal file
47
command/node/remove_test.go
Normal file
@ -0,0 +1,47 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/cli/internal/test"
|
||||
"github.com/docker/docker/pkg/testutil/assert"
|
||||
)
|
||||
|
||||
func TestNodeRemoveErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
args []string
|
||||
nodeRemoveFunc func() error
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
expectedError: "requires at least 1 argument",
|
||||
},
|
||||
{
|
||||
args: []string{"nodeID"},
|
||||
nodeRemoveFunc: func() error {
|
||||
return fmt.Errorf("error removing the node")
|
||||
},
|
||||
expectedError: "error removing the node",
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newRemoveCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
nodeRemoveFunc: tc.nodeRemoveFunc,
|
||||
}, buf))
|
||||
cmd.SetArgs(tc.args)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
assert.Error(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeRemoveMultiple(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newRemoveCommand(test.NewFakeCli(&fakeClient{}, buf))
|
||||
cmd.SetArgs([]string{"nodeID1", "nodeID2"})
|
||||
assert.NilError(t, cmd.Execute())
|
||||
}
|
||||
25
command/node/testdata/node-inspect-pretty.manager-leader.golden
vendored
Normal file
25
command/node/testdata/node-inspect-pretty.manager-leader.golden
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
ID: nodeID
|
||||
Name: defaultNodeName
|
||||
Hostname: defaultNodeHostname
|
||||
Joined at: 2009-11-10 23:00:00 +0000 utc
|
||||
Status:
|
||||
State: Ready
|
||||
Availability: Active
|
||||
Address: 127.0.0.1
|
||||
Manager Status:
|
||||
Address: 127.0.0.1
|
||||
Raft Status: Reachable
|
||||
Leader: Yes
|
||||
Platform:
|
||||
Operating System: linux
|
||||
Architecture: x86_64
|
||||
Resources:
|
||||
CPUs: 0
|
||||
Memory: 20 MiB
|
||||
Plugins:
|
||||
Network: bridge, overlay
|
||||
Volume: local
|
||||
Engine Version: 1.13.0
|
||||
Engine Labels:
|
||||
- engine = label
|
||||
|
||||
25
command/node/testdata/node-inspect-pretty.manager.golden
vendored
Normal file
25
command/node/testdata/node-inspect-pretty.manager.golden
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
ID: nodeID
|
||||
Name: defaultNodeName
|
||||
Hostname: defaultNodeHostname
|
||||
Joined at: 2009-11-10 23:00:00 +0000 utc
|
||||
Status:
|
||||
State: Ready
|
||||
Availability: Active
|
||||
Address: 127.0.0.1
|
||||
Manager Status:
|
||||
Address: 127.0.0.1
|
||||
Raft Status: Reachable
|
||||
Leader: No
|
||||
Platform:
|
||||
Operating System: linux
|
||||
Architecture: x86_64
|
||||
Resources:
|
||||
CPUs: 0
|
||||
Memory: 20 MiB
|
||||
Plugins:
|
||||
Network: bridge, overlay
|
||||
Volume: local
|
||||
Engine Version: 1.13.0
|
||||
Engine Labels:
|
||||
- engine = label
|
||||
|
||||
23
command/node/testdata/node-inspect-pretty.simple.golden
vendored
Normal file
23
command/node/testdata/node-inspect-pretty.simple.golden
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
ID: nodeID
|
||||
Name: defaultNodeName
|
||||
Labels:
|
||||
- lbl1 = value1
|
||||
Hostname: defaultNodeHostname
|
||||
Joined at: 2009-11-10 23:00:00 +0000 utc
|
||||
Status:
|
||||
State: Ready
|
||||
Availability: Active
|
||||
Address: 127.0.0.1
|
||||
Platform:
|
||||
Operating System: linux
|
||||
Architecture: x86_64
|
||||
Resources:
|
||||
CPUs: 0
|
||||
Memory: 20 MiB
|
||||
Plugins:
|
||||
Network: bridge, overlay
|
||||
Volume: local
|
||||
Engine Version: 1.13.0
|
||||
Engine Labels:
|
||||
- engine = label
|
||||
|
||||
2
command/node/testdata/node-ps.simple.golden
vendored
Normal file
2
command/node/testdata/node-ps.simple.golden
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
|
||||
taskID rl02d5gwz6chzu7il5fhtb8be.1 myimage:mytag defaultNodeName Ready Ready 2 hours ago *:80->80/tcp
|
||||
4
command/node/testdata/node-ps.with-errors.golden
vendored
Normal file
4
command/node/testdata/node-ps.with-errors.golden
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
|
||||
taskID1 failure.1 myimage:mytag defaultNodeName Ready Ready 2 hours ago "a task error"
|
||||
taskID2 \_ failure.1 myimage:mytag defaultNodeName Ready Ready 3 hours ago "a task error"
|
||||
taskID3 \_ failure.1 myimage:mytag defaultNodeName Ready Ready 4 hours ago "a task error"
|
||||
@ -18,7 +18,7 @@ var (
|
||||
errNoRoleChange = errors.New("role was already set to the requested value")
|
||||
)
|
||||
|
||||
func newUpdateCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newUpdateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
nodeOpts := newNodeOptions()
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -39,14 +39,14 @@ func newUpdateCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runUpdate(dockerCli *command.DockerCli, flags *pflag.FlagSet, nodeID string) error {
|
||||
func runUpdate(dockerCli command.Cli, flags *pflag.FlagSet, nodeID string) error {
|
||||
success := func(_ string) {
|
||||
fmt.Fprintln(dockerCli.Out(), nodeID)
|
||||
}
|
||||
return updateNodes(dockerCli, []string{nodeID}, mergeNodeUpdate(flags), success)
|
||||
}
|
||||
|
||||
func updateNodes(dockerCli *command.DockerCli, nodes []string, mergeNode func(node *swarm.Node) error, success func(nodeID string)) error {
|
||||
func updateNodes(dockerCli command.Cli, nodes []string, mergeNode func(node *swarm.Node) error, success func(nodeID string)) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
|
||||
|
||||
172
command/node/update_test.go
Normal file
172
command/node/update_test.go
Normal file
@ -0,0 +1,172 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/cli/internal/test"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/docker/cli/internal/test/builders"
|
||||
"github.com/docker/docker/pkg/testutil/assert"
|
||||
)
|
||||
|
||||
func TestNodeUpdateErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
args []string
|
||||
flags map[string]string
|
||||
nodeInspectFunc func() (swarm.Node, []byte, error)
|
||||
nodeUpdateFunc func(nodeID string, version swarm.Version, node swarm.NodeSpec) error
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
expectedError: "requires exactly 1 argument",
|
||||
},
|
||||
{
|
||||
args: []string{"node1", "node2"},
|
||||
expectedError: "requires exactly 1 argument",
|
||||
},
|
||||
{
|
||||
args: []string{"nodeID"},
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return swarm.Node{}, []byte{}, fmt.Errorf("error inspecting the node")
|
||||
},
|
||||
expectedError: "error inspecting the node",
|
||||
},
|
||||
{
|
||||
args: []string{"nodeID"},
|
||||
nodeUpdateFunc: func(nodeID string, version swarm.Version, node swarm.NodeSpec) error {
|
||||
return fmt.Errorf("error updating the node")
|
||||
},
|
||||
expectedError: "error updating the node",
|
||||
},
|
||||
{
|
||||
args: []string{"nodeID"},
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return *Node(NodeLabels(map[string]string{
|
||||
"key": "value",
|
||||
})), []byte{}, nil
|
||||
},
|
||||
flags: map[string]string{
|
||||
"label-rm": "notpresent",
|
||||
},
|
||||
expectedError: "key notpresent doesn't exist in node's labels",
|
||||
},
|
||||
}
|
||||
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)
|
||||
}
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
assert.Error(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeUpdate(t *testing.T) {
|
||||
testCases := []struct {
|
||||
args []string
|
||||
flags map[string]string
|
||||
nodeInspectFunc func() (swarm.Node, []byte, error)
|
||||
nodeUpdateFunc func(nodeID string, version swarm.Version, node swarm.NodeSpec) error
|
||||
}{
|
||||
{
|
||||
args: []string{"nodeID"},
|
||||
flags: map[string]string{
|
||||
"role": "manager",
|
||||
},
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return *Node(), []byte{}, nil
|
||||
},
|
||||
nodeUpdateFunc: func(nodeID string, version swarm.Version, node swarm.NodeSpec) error {
|
||||
if node.Role != swarm.NodeRoleManager {
|
||||
return fmt.Errorf("expected role manager, got %s", node.Role)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
args: []string{"nodeID"},
|
||||
flags: map[string]string{
|
||||
"availability": "drain",
|
||||
},
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return *Node(), []byte{}, nil
|
||||
},
|
||||
nodeUpdateFunc: func(nodeID string, version swarm.Version, node swarm.NodeSpec) error {
|
||||
if node.Availability != swarm.NodeAvailabilityDrain {
|
||||
return fmt.Errorf("expected drain availability, got %s", node.Availability)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
args: []string{"nodeID"},
|
||||
flags: map[string]string{
|
||||
"label-add": "lbl",
|
||||
},
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return *Node(), []byte{}, nil
|
||||
},
|
||||
nodeUpdateFunc: func(nodeID string, version swarm.Version, node swarm.NodeSpec) error {
|
||||
if _, present := node.Annotations.Labels["lbl"]; !present {
|
||||
return fmt.Errorf("expected 'lbl' label, got %v", node.Annotations.Labels)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
args: []string{"nodeID"},
|
||||
flags: map[string]string{
|
||||
"label-add": "key=value",
|
||||
},
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return *Node(), []byte{}, nil
|
||||
},
|
||||
nodeUpdateFunc: func(nodeID string, version swarm.Version, node swarm.NodeSpec) error {
|
||||
if value, present := node.Annotations.Labels["key"]; !present || value != "value" {
|
||||
return fmt.Errorf("expected 'key' label to be 'value', got %v", node.Annotations.Labels)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
args: []string{"nodeID"},
|
||||
flags: map[string]string{
|
||||
"label-rm": "key",
|
||||
},
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return *Node(NodeLabels(map[string]string{
|
||||
"key": "value",
|
||||
})), []byte{}, nil
|
||||
},
|
||||
nodeUpdateFunc: func(nodeID string, version swarm.Version, node swarm.NodeSpec) error {
|
||||
if len(node.Annotations.Labels) > 0 {
|
||||
return fmt.Errorf("expected no labels, got %v", node.Annotations.Labels)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
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)
|
||||
}
|
||||
assert.NilError(t, cmd.Execute())
|
||||
}
|
||||
}
|
||||
84
command/swarm/client_test.go
Normal file
84
command/swarm/client_test.go
Normal file
@ -0,0 +1,84 @@
|
||||
package swarm
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/client"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type fakeClient struct {
|
||||
client.Client
|
||||
infoFunc func() (types.Info, error)
|
||||
swarmInitFunc func() (string, error)
|
||||
swarmInspectFunc func() (swarm.Swarm, error)
|
||||
nodeInspectFunc func() (swarm.Node, []byte, error)
|
||||
swarmGetUnlockKeyFunc func() (types.SwarmUnlockKeyResponse, error)
|
||||
swarmJoinFunc func() error
|
||||
swarmLeaveFunc func() error
|
||||
swarmUpdateFunc func(swarm swarm.Spec, flags swarm.UpdateFlags) error
|
||||
swarmUnlockFunc func(req swarm.UnlockRequest) error
|
||||
}
|
||||
|
||||
func (cli *fakeClient) Info(ctx context.Context) (types.Info, error) {
|
||||
if cli.infoFunc != nil {
|
||||
return cli.infoFunc()
|
||||
}
|
||||
return types.Info{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) NodeInspectWithRaw(ctx context.Context, ref string) (swarm.Node, []byte, error) {
|
||||
if cli.nodeInspectFunc != nil {
|
||||
return cli.nodeInspectFunc()
|
||||
}
|
||||
return swarm.Node{}, []byte{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) SwarmInit(ctx context.Context, req swarm.InitRequest) (string, error) {
|
||||
if cli.swarmInitFunc != nil {
|
||||
return cli.swarmInitFunc()
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) SwarmInspect(ctx context.Context) (swarm.Swarm, error) {
|
||||
if cli.swarmInspectFunc != nil {
|
||||
return cli.swarmInspectFunc()
|
||||
}
|
||||
return swarm.Swarm{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) SwarmGetUnlockKey(ctx context.Context) (types.SwarmUnlockKeyResponse, error) {
|
||||
if cli.swarmGetUnlockKeyFunc != nil {
|
||||
return cli.swarmGetUnlockKeyFunc()
|
||||
}
|
||||
return types.SwarmUnlockKeyResponse{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) SwarmJoin(ctx context.Context, req swarm.JoinRequest) error {
|
||||
if cli.swarmJoinFunc != nil {
|
||||
return cli.swarmJoinFunc()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) SwarmLeave(ctx context.Context, force bool) error {
|
||||
if cli.swarmLeaveFunc != nil {
|
||||
return cli.swarmLeaveFunc()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) SwarmUpdate(ctx context.Context, version swarm.Version, swarm swarm.Spec, flags swarm.UpdateFlags) error {
|
||||
if cli.swarmUpdateFunc != nil {
|
||||
return cli.swarmUpdateFunc(swarm, flags)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) SwarmUnlock(ctx context.Context, req swarm.UnlockRequest) error {
|
||||
if cli.swarmUnlockFunc != nil {
|
||||
return cli.swarmUnlockFunc(req)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -22,7 +22,7 @@ type initOptions struct {
|
||||
forceNewCluster bool
|
||||
}
|
||||
|
||||
func newInitCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newInitCommand(dockerCli command.Cli) *cobra.Command {
|
||||
opts := initOptions{
|
||||
listenAddr: NewListenAddrOption(),
|
||||
}
|
||||
@ -45,7 +45,7 @@ func newInitCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runInit(dockerCli *command.DockerCli, flags *pflag.FlagSet, opts initOptions) error {
|
||||
func runInit(dockerCli command.Cli, flags *pflag.FlagSet, opts initOptions) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
|
||||
@ -67,7 +67,7 @@ func runInit(dockerCli *command.DockerCli, flags *pflag.FlagSet, opts initOption
|
||||
|
||||
fmt.Fprintf(dockerCli.Out(), "Swarm initialized: current node (%s) is now a manager.\n\n", nodeID)
|
||||
|
||||
if err := printJoinCommand(ctx, dockerCli, nodeID, true, false); err != nil {
|
||||
if err := printJoinCommand(ctx, dockerCli, nodeID, false, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
129
command/swarm/init_test.go
Normal file
129
command/swarm/init_test.go
Normal file
@ -0,0 +1,129 @@
|
||||
package swarm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/cli/internal/test"
|
||||
"github.com/docker/docker/pkg/testutil/assert"
|
||||
"github.com/docker/docker/pkg/testutil/golden"
|
||||
)
|
||||
|
||||
func TestSwarmInitErrorOnAPIFailure(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
flags map[string]string
|
||||
swarmInitFunc func() (string, error)
|
||||
swarmInspectFunc func() (swarm.Swarm, error)
|
||||
swarmGetUnlockKeyFunc func() (types.SwarmUnlockKeyResponse, error)
|
||||
nodeInspectFunc func() (swarm.Node, []byte, error)
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "init-failed",
|
||||
swarmInitFunc: func() (string, error) {
|
||||
return "", fmt.Errorf("error initializing the swarm")
|
||||
},
|
||||
expectedError: "error initializing the swarm",
|
||||
},
|
||||
{
|
||||
name: "init-faild-with-ip-choice",
|
||||
swarmInitFunc: func() (string, error) {
|
||||
return "", fmt.Errorf("could not choose an IP address to advertise")
|
||||
},
|
||||
expectedError: "could not choose an IP address to advertise - specify one with --advertise-addr",
|
||||
},
|
||||
{
|
||||
name: "swarm-inspect-after-init-failed",
|
||||
swarmInspectFunc: func() (swarm.Swarm, error) {
|
||||
return swarm.Swarm{}, fmt.Errorf("error inspecting the swarm")
|
||||
},
|
||||
expectedError: "error inspecting the swarm",
|
||||
},
|
||||
{
|
||||
name: "node-inspect-after-init-failed",
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return swarm.Node{}, []byte{}, fmt.Errorf("error inspecting the node")
|
||||
},
|
||||
expectedError: "error inspecting the node",
|
||||
},
|
||||
{
|
||||
name: "swarm-get-unlock-key-after-init-failed",
|
||||
flags: map[string]string{
|
||||
flagAutolock: "true",
|
||||
},
|
||||
swarmGetUnlockKeyFunc: func() (types.SwarmUnlockKeyResponse, error) {
|
||||
return types.SwarmUnlockKeyResponse{}, fmt.Errorf("error getting swarm unlock key")
|
||||
},
|
||||
expectedError: "could not fetch unlock key: error getting swarm unlock key",
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newInitCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
swarmInitFunc: tc.swarmInitFunc,
|
||||
swarmInspectFunc: tc.swarmInspectFunc,
|
||||
swarmGetUnlockKeyFunc: tc.swarmGetUnlockKeyFunc,
|
||||
nodeInspectFunc: tc.nodeInspectFunc,
|
||||
}, buf))
|
||||
for key, value := range tc.flags {
|
||||
cmd.Flags().Set(key, value)
|
||||
}
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
assert.Error(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSwarmInit(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
flags map[string]string
|
||||
swarmInitFunc func() (string, error)
|
||||
swarmInspectFunc func() (swarm.Swarm, error)
|
||||
swarmGetUnlockKeyFunc func() (types.SwarmUnlockKeyResponse, error)
|
||||
nodeInspectFunc func() (swarm.Node, []byte, error)
|
||||
}{
|
||||
{
|
||||
name: "init",
|
||||
swarmInitFunc: func() (string, error) {
|
||||
return "nodeID", nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "init-autolock",
|
||||
flags: map[string]string{
|
||||
flagAutolock: "true",
|
||||
},
|
||||
swarmInitFunc: func() (string, error) {
|
||||
return "nodeID", nil
|
||||
},
|
||||
swarmGetUnlockKeyFunc: func() (types.SwarmUnlockKeyResponse, error) {
|
||||
return types.SwarmUnlockKeyResponse{
|
||||
UnlockKey: "unlock-key",
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newInitCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
swarmInitFunc: tc.swarmInitFunc,
|
||||
swarmInspectFunc: tc.swarmInspectFunc,
|
||||
swarmGetUnlockKeyFunc: tc.swarmGetUnlockKeyFunc,
|
||||
nodeInspectFunc: tc.nodeInspectFunc,
|
||||
}, buf))
|
||||
for key, value := range tc.flags {
|
||||
cmd.Flags().Set(key, value)
|
||||
}
|
||||
assert.NilError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
expected := golden.Get(t, []byte(actual), fmt.Sprintf("init-%s.golden", tc.name))
|
||||
assert.EqualNormalizedString(t, assert.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
}
|
||||
@ -18,7 +18,7 @@ type joinOptions struct {
|
||||
token string
|
||||
}
|
||||
|
||||
func newJoinCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newJoinCommand(dockerCli command.Cli) *cobra.Command {
|
||||
opts := joinOptions{
|
||||
listenAddr: NewListenAddrOption(),
|
||||
}
|
||||
@ -40,7 +40,7 @@ func newJoinCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runJoin(dockerCli *command.DockerCli, opts joinOptions) error {
|
||||
func runJoin(dockerCli command.Cli, opts joinOptions) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
|
||||
|
||||
102
command/swarm/join_test.go
Normal file
102
command/swarm/join_test.go
Normal file
@ -0,0 +1,102 @@
|
||||
package swarm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/cli/internal/test"
|
||||
"github.com/docker/docker/pkg/testutil/assert"
|
||||
)
|
||||
|
||||
func TestSwarmJoinErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
swarmJoinFunc func() error
|
||||
infoFunc func() (types.Info, error)
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "not-enough-args",
|
||||
expectedError: "requires exactly 1 argument",
|
||||
},
|
||||
{
|
||||
name: "too-many-args",
|
||||
args: []string{"remote1", "remote2"},
|
||||
expectedError: "requires exactly 1 argument",
|
||||
},
|
||||
{
|
||||
name: "join-failed",
|
||||
args: []string{"remote"},
|
||||
swarmJoinFunc: func() error {
|
||||
return fmt.Errorf("error joining the swarm")
|
||||
},
|
||||
expectedError: "error joining the swarm",
|
||||
},
|
||||
{
|
||||
name: "join-failed-on-init",
|
||||
args: []string{"remote"},
|
||||
infoFunc: func() (types.Info, error) {
|
||||
return types.Info{}, fmt.Errorf("error asking for node info")
|
||||
},
|
||||
expectedError: "error asking for node info",
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newJoinCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
swarmJoinFunc: tc.swarmJoinFunc,
|
||||
infoFunc: tc.infoFunc,
|
||||
}, buf))
|
||||
cmd.SetArgs(tc.args)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
assert.Error(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSwarmJoin(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
infoFunc func() (types.Info, error)
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "join-as-manager",
|
||||
infoFunc: func() (types.Info, error) {
|
||||
return types.Info{
|
||||
Swarm: swarm.Info{
|
||||
ControlAvailable: true,
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
expected: "This node joined a swarm as a manager.",
|
||||
},
|
||||
{
|
||||
name: "join-as-worker",
|
||||
infoFunc: func() (types.Info, error) {
|
||||
return types.Info{
|
||||
Swarm: swarm.Info{
|
||||
ControlAvailable: false,
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
expected: "This node joined a swarm as a worker.",
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newJoinCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
infoFunc: tc.infoFunc,
|
||||
}, buf))
|
||||
cmd.SetArgs([]string{"remote"})
|
||||
assert.NilError(t, cmd.Execute())
|
||||
assert.Equal(t, strings.TrimSpace(buf.String()), tc.expected)
|
||||
}
|
||||
}
|
||||
@ -18,7 +18,7 @@ type joinTokenOptions struct {
|
||||
quiet bool
|
||||
}
|
||||
|
||||
func newJoinTokenCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newJoinTokenCommand(dockerCli command.Cli) *cobra.Command {
|
||||
opts := joinTokenOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -38,7 +38,7 @@ func newJoinTokenCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runJoinToken(dockerCli *command.DockerCli, opts joinTokenOptions) error {
|
||||
func runJoinToken(dockerCli command.Cli, opts joinTokenOptions) error {
|
||||
worker := opts.role == "worker"
|
||||
manager := opts.role == "manager"
|
||||
|
||||
@ -94,7 +94,7 @@ func runJoinToken(dockerCli *command.DockerCli, opts joinTokenOptions) error {
|
||||
return printJoinCommand(ctx, dockerCli, info.Swarm.NodeID, worker, manager)
|
||||
}
|
||||
|
||||
func printJoinCommand(ctx context.Context, dockerCli *command.DockerCli, nodeID string, worker bool, manager bool) error {
|
||||
func printJoinCommand(ctx context.Context, dockerCli command.Cli, nodeID string, worker bool, manager bool) error {
|
||||
client := dockerCli.Client()
|
||||
|
||||
node, _, err := client.NodeInspectWithRaw(ctx, nodeID)
|
||||
|
||||
215
command/swarm/join_token_test.go
Normal file
215
command/swarm/join_token_test.go
Normal file
@ -0,0 +1,215 @@
|
||||
package swarm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/cli/internal/test"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/docker/cli/internal/test/builders"
|
||||
"github.com/docker/docker/pkg/testutil/assert"
|
||||
"github.com/docker/docker/pkg/testutil/golden"
|
||||
)
|
||||
|
||||
func TestSwarmJoinTokenErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
flags map[string]string
|
||||
infoFunc func() (types.Info, error)
|
||||
swarmInspectFunc func() (swarm.Swarm, error)
|
||||
swarmUpdateFunc func(swarm swarm.Spec, flags swarm.UpdateFlags) error
|
||||
nodeInspectFunc func() (swarm.Node, []byte, error)
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "not-enough-args",
|
||||
expectedError: "requires exactly 1 argument",
|
||||
},
|
||||
{
|
||||
name: "too-many-args",
|
||||
args: []string{"worker", "manager"},
|
||||
expectedError: "requires exactly 1 argument",
|
||||
},
|
||||
{
|
||||
name: "invalid-args",
|
||||
args: []string{"foo"},
|
||||
expectedError: "unknown role foo",
|
||||
},
|
||||
{
|
||||
name: "swarm-inspect-failed",
|
||||
args: []string{"worker"},
|
||||
swarmInspectFunc: func() (swarm.Swarm, error) {
|
||||
return swarm.Swarm{}, fmt.Errorf("error inspecting the swarm")
|
||||
},
|
||||
expectedError: "error inspecting the swarm",
|
||||
},
|
||||
{
|
||||
name: "swarm-inspect-rotate-failed",
|
||||
args: []string{"worker"},
|
||||
flags: map[string]string{
|
||||
flagRotate: "true",
|
||||
},
|
||||
swarmInspectFunc: func() (swarm.Swarm, error) {
|
||||
return swarm.Swarm{}, fmt.Errorf("error inspecting the swarm")
|
||||
},
|
||||
expectedError: "error inspecting the swarm",
|
||||
},
|
||||
{
|
||||
name: "swarm-update-failed",
|
||||
args: []string{"worker"},
|
||||
flags: map[string]string{
|
||||
flagRotate: "true",
|
||||
},
|
||||
swarmUpdateFunc: func(swarm swarm.Spec, flags swarm.UpdateFlags) error {
|
||||
return fmt.Errorf("error updating the swarm")
|
||||
},
|
||||
expectedError: "error updating the swarm",
|
||||
},
|
||||
{
|
||||
name: "node-inspect-failed",
|
||||
args: []string{"worker"},
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return swarm.Node{}, []byte{}, fmt.Errorf("error inspecting node")
|
||||
},
|
||||
expectedError: "error inspecting node",
|
||||
},
|
||||
{
|
||||
name: "info-failed",
|
||||
args: []string{"worker"},
|
||||
infoFunc: func() (types.Info, error) {
|
||||
return types.Info{}, fmt.Errorf("error asking for node info")
|
||||
},
|
||||
expectedError: "error asking for node info",
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newJoinTokenCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
swarmInspectFunc: tc.swarmInspectFunc,
|
||||
swarmUpdateFunc: tc.swarmUpdateFunc,
|
||||
infoFunc: tc.infoFunc,
|
||||
nodeInspectFunc: tc.nodeInspectFunc,
|
||||
}, buf))
|
||||
cmd.SetArgs(tc.args)
|
||||
for key, value := range tc.flags {
|
||||
cmd.Flags().Set(key, value)
|
||||
}
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
assert.Error(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSwarmJoinToken(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
flags map[string]string
|
||||
infoFunc func() (types.Info, error)
|
||||
swarmInspectFunc func() (swarm.Swarm, error)
|
||||
nodeInspectFunc func() (swarm.Node, []byte, error)
|
||||
}{
|
||||
{
|
||||
name: "worker",
|
||||
args: []string{"worker"},
|
||||
infoFunc: func() (types.Info, error) {
|
||||
return types.Info{
|
||||
Swarm: swarm.Info{
|
||||
NodeID: "nodeID",
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return *Node(Manager()), []byte{}, nil
|
||||
},
|
||||
swarmInspectFunc: func() (swarm.Swarm, error) {
|
||||
return *Swarm(), nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "manager",
|
||||
args: []string{"manager"},
|
||||
infoFunc: func() (types.Info, error) {
|
||||
return types.Info{
|
||||
Swarm: swarm.Info{
|
||||
NodeID: "nodeID",
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return *Node(Manager()), []byte{}, nil
|
||||
},
|
||||
swarmInspectFunc: func() (swarm.Swarm, error) {
|
||||
return *Swarm(), nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "manager-rotate",
|
||||
args: []string{"manager"},
|
||||
flags: map[string]string{
|
||||
flagRotate: "true",
|
||||
},
|
||||
infoFunc: func() (types.Info, error) {
|
||||
return types.Info{
|
||||
Swarm: swarm.Info{
|
||||
NodeID: "nodeID",
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return *Node(Manager()), []byte{}, nil
|
||||
},
|
||||
swarmInspectFunc: func() (swarm.Swarm, error) {
|
||||
return *Swarm(), nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "worker-quiet",
|
||||
args: []string{"worker"},
|
||||
flags: map[string]string{
|
||||
flagQuiet: "true",
|
||||
},
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return *Node(Manager()), []byte{}, nil
|
||||
},
|
||||
swarmInspectFunc: func() (swarm.Swarm, error) {
|
||||
return *Swarm(), nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "manager-quiet",
|
||||
args: []string{"manager"},
|
||||
flags: map[string]string{
|
||||
flagQuiet: "true",
|
||||
},
|
||||
nodeInspectFunc: func() (swarm.Node, []byte, error) {
|
||||
return *Node(Manager()), []byte{}, nil
|
||||
},
|
||||
swarmInspectFunc: func() (swarm.Swarm, error) {
|
||||
return *Swarm(), nil
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newJoinTokenCommand(
|
||||
test.NewFakeCli(&fakeClient{
|
||||
swarmInspectFunc: tc.swarmInspectFunc,
|
||||
infoFunc: tc.infoFunc,
|
||||
nodeInspectFunc: tc.nodeInspectFunc,
|
||||
}, buf))
|
||||
cmd.SetArgs(tc.args)
|
||||
for key, value := range tc.flags {
|
||||
cmd.Flags().Set(key, value)
|
||||
}
|
||||
assert.NilError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
expected := golden.Get(t, []byte(actual), fmt.Sprintf("jointoken-%s.golden", tc.name))
|
||||
assert.EqualNormalizedString(t, assert.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
}
|
||||
@ -14,7 +14,7 @@ type leaveOptions struct {
|
||||
force bool
|
||||
}
|
||||
|
||||
func newLeaveCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newLeaveCommand(dockerCli command.Cli) *cobra.Command {
|
||||
opts := leaveOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -31,7 +31,7 @@ func newLeaveCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runLeave(dockerCli *command.DockerCli, opts leaveOptions) error {
|
||||
func runLeave(dockerCli command.Cli, opts leaveOptions) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
|
||||
|
||||
52
command/swarm/leave_test.go
Normal file
52
command/swarm/leave_test.go
Normal file
@ -0,0 +1,52 @@
|
||||
package swarm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/cli/internal/test"
|
||||
"github.com/docker/docker/pkg/testutil/assert"
|
||||
)
|
||||
|
||||
func TestSwarmLeaveErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
swarmLeaveFunc func() error
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "too-many-args",
|
||||
args: []string{"foo"},
|
||||
expectedError: "accepts no argument(s)",
|
||||
},
|
||||
{
|
||||
name: "leave-failed",
|
||||
swarmLeaveFunc: func() error {
|
||||
return fmt.Errorf("error leaving the swarm")
|
||||
},
|
||||
expectedError: "error leaving the swarm",
|
||||
},
|
||||
}
|
||||
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)
|
||||
assert.Error(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSwarmLeave(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newLeaveCommand(
|
||||
test.NewFakeCli(&fakeClient{}, buf))
|
||||
assert.NilError(t, cmd.Execute())
|
||||
assert.Equal(t, strings.TrimSpace(buf.String()), "Node left the swarm.")
|
||||
}
|
||||
@ -35,3 +35,76 @@ func TestNodeAddrOptionSetInvalidFormat(t *testing.T) {
|
||||
opt := NewListenAddrOption()
|
||||
assert.Error(t, opt.Set("http://localhost:4545"), "Invalid")
|
||||
}
|
||||
|
||||
func TestExternalCAOptionErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
externalCA string
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
externalCA: "",
|
||||
expectedError: "EOF",
|
||||
},
|
||||
{
|
||||
externalCA: "anything",
|
||||
expectedError: "invalid field 'anything' must be a key=value pair",
|
||||
},
|
||||
{
|
||||
externalCA: "foo=bar",
|
||||
expectedError: "the external-ca option needs a protocol= parameter",
|
||||
},
|
||||
{
|
||||
externalCA: "protocol=baz",
|
||||
expectedError: "unrecognized external CA protocol baz",
|
||||
},
|
||||
{
|
||||
externalCA: "protocol=cfssl",
|
||||
expectedError: "the external-ca option needs a url= parameter",
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
opt := &ExternalCAOption{}
|
||||
assert.Error(t, opt.Set(tc.externalCA), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExternalCAOption(t *testing.T) {
|
||||
testCases := []struct {
|
||||
externalCA string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
externalCA: "protocol=cfssl,url=anything",
|
||||
expected: "cfssl: anything",
|
||||
},
|
||||
{
|
||||
externalCA: "protocol=CFSSL,url=anything",
|
||||
expected: "cfssl: anything",
|
||||
},
|
||||
{
|
||||
externalCA: "protocol=Cfssl,url=https://example.com",
|
||||
expected: "cfssl: https://example.com",
|
||||
},
|
||||
{
|
||||
externalCA: "protocol=Cfssl,url=https://example.com,foo=bar",
|
||||
expected: "cfssl: https://example.com",
|
||||
},
|
||||
{
|
||||
externalCA: "protocol=Cfssl,url=https://example.com,foo=bar,foo=baz",
|
||||
expected: "cfssl: https://example.com",
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
opt := &ExternalCAOption{}
|
||||
assert.NilError(t, opt.Set(tc.externalCA))
|
||||
assert.Equal(t, opt.String(), tc.expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExternalCAOptionMultiple(t *testing.T) {
|
||||
opt := &ExternalCAOption{}
|
||||
assert.NilError(t, opt.Set("protocol=cfssl,url=https://example.com"))
|
||||
assert.NilError(t, opt.Set("protocol=CFSSL,url=anything"))
|
||||
assert.Equal(t, len(opt.Value()), 2)
|
||||
assert.Equal(t, opt.String(), "cfssl: https://example.com, cfssl: anything")
|
||||
}
|
||||
|
||||
11
command/swarm/testdata/init-init-autolock.golden
vendored
Normal file
11
command/swarm/testdata/init-init-autolock.golden
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
Swarm initialized: current node (nodeID) is now a manager.
|
||||
|
||||
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
|
||||
|
||||
To unlock a swarm manager after it restarts, run the `docker swarm unlock`
|
||||
command and provide the following key:
|
||||
|
||||
unlock-key
|
||||
|
||||
Please remember to store this key in a password manager, since without it you
|
||||
will not be able to restart the manager.
|
||||
4
command/swarm/testdata/init-init.golden
vendored
Normal file
4
command/swarm/testdata/init-init.golden
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
Swarm initialized: current node (nodeID) is now a manager.
|
||||
|
||||
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
|
||||
|
||||
1
command/swarm/testdata/jointoken-manager-quiet.golden
vendored
Normal file
1
command/swarm/testdata/jointoken-manager-quiet.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
manager-join-token
|
||||
8
command/swarm/testdata/jointoken-manager-rotate.golden
vendored
Normal file
8
command/swarm/testdata/jointoken-manager-rotate.golden
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
Successfully rotated manager join token.
|
||||
|
||||
To add a manager to this swarm, run the following command:
|
||||
|
||||
docker swarm join \
|
||||
--token manager-join-token \
|
||||
127.0.0.1
|
||||
|
||||
6
command/swarm/testdata/jointoken-manager.golden
vendored
Normal file
6
command/swarm/testdata/jointoken-manager.golden
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
To add a manager to this swarm, run the following command:
|
||||
|
||||
docker swarm join \
|
||||
--token manager-join-token \
|
||||
127.0.0.1
|
||||
|
||||
1
command/swarm/testdata/jointoken-worker-quiet.golden
vendored
Normal file
1
command/swarm/testdata/jointoken-worker-quiet.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
worker-join-token
|
||||
6
command/swarm/testdata/jointoken-worker.golden
vendored
Normal file
6
command/swarm/testdata/jointoken-worker.golden
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
To add a worker to this swarm, run the following command:
|
||||
|
||||
docker swarm join \
|
||||
--token worker-join-token \
|
||||
127.0.0.1
|
||||
|
||||
1
command/swarm/testdata/unlockkeys-unlock-key-quiet.golden
vendored
Normal file
1
command/swarm/testdata/unlockkeys-unlock-key-quiet.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
unlock-key
|
||||
1
command/swarm/testdata/unlockkeys-unlock-key-rotate-quiet.golden
vendored
Normal file
1
command/swarm/testdata/unlockkeys-unlock-key-rotate-quiet.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
unlock-key
|
||||
9
command/swarm/testdata/unlockkeys-unlock-key-rotate.golden
vendored
Normal file
9
command/swarm/testdata/unlockkeys-unlock-key-rotate.golden
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
Successfully rotated manager unlock key.
|
||||
|
||||
To unlock a swarm manager after it restarts, run the `docker swarm unlock`
|
||||
command and provide the following key:
|
||||
|
||||
unlock-key
|
||||
|
||||
Please remember to store this key in a password manager, since without it you
|
||||
will not be able to restart the manager.
|
||||
7
command/swarm/testdata/unlockkeys-unlock-key.golden
vendored
Normal file
7
command/swarm/testdata/unlockkeys-unlock-key.golden
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
To unlock a swarm manager after it restarts, run the `docker swarm unlock`
|
||||
command and provide the following key:
|
||||
|
||||
unlock-key
|
||||
|
||||
Please remember to store this key in a password manager, since without it you
|
||||
will not be able to restart the manager.
|
||||
1
command/swarm/testdata/update-all-flags-quiet.golden
vendored
Normal file
1
command/swarm/testdata/update-all-flags-quiet.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
Swarm updated.
|
||||
8
command/swarm/testdata/update-autolock-unlock-key.golden
vendored
Normal file
8
command/swarm/testdata/update-autolock-unlock-key.golden
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
Swarm updated.
|
||||
To unlock a swarm manager after it restarts, run the `docker swarm unlock`
|
||||
command and provide the following key:
|
||||
|
||||
unlock-key
|
||||
|
||||
Please remember to store this key in a password manager, since without it you
|
||||
will not be able to restart the manager.
|
||||
13
command/swarm/testdata/update-noargs.golden
vendored
Normal file
13
command/swarm/testdata/update-noargs.golden
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
Update the swarm
|
||||
|
||||
Usage:
|
||||
update [OPTIONS] [flags]
|
||||
|
||||
Flags:
|
||||
--autolock Change manager autolocking setting (true|false)
|
||||
--cert-expiry duration Validity period for node certificates (ns|us|ms|s|m|h) (default 2160h0m0s)
|
||||
--dispatcher-heartbeat duration Dispatcher heartbeat period (ns|us|ms|s|m|h) (default 5s)
|
||||
--external-ca external-ca Specifications of one or more certificate signing endpoints
|
||||
--max-snapshots uint Number of additional Raft snapshots to retain
|
||||
--snapshot-interval uint Number of log entries between Raft snapshots (default 10000)
|
||||
--task-history-limit int Task history retention limit (default 5)
|
||||
@ -18,7 +18,7 @@ import (
|
||||
|
||||
type unlockOptions struct{}
|
||||
|
||||
func newUnlockCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newUnlockCommand(dockerCli command.Cli) *cobra.Command {
|
||||
opts := unlockOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -33,7 +33,7 @@ func newUnlockCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runUnlock(dockerCli *command.DockerCli, opts unlockOptions) error {
|
||||
func runUnlock(dockerCli command.Cli, opts unlockOptions) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
|
||||
|
||||
@ -3,12 +3,11 @@ package swarm
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/cli/command"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
@ -17,7 +16,7 @@ type unlockKeyOptions struct {
|
||||
quiet bool
|
||||
}
|
||||
|
||||
func newUnlockKeyCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newUnlockKeyCommand(dockerCli command.Cli) *cobra.Command {
|
||||
opts := unlockKeyOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -36,7 +35,7 @@ func newUnlockKeyCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runUnlockKey(dockerCli *command.DockerCli, opts unlockKeyOptions) error {
|
||||
func runUnlockKey(dockerCli command.Cli, opts unlockKeyOptions) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
|
||||
@ -79,7 +78,7 @@ func runUnlockKey(dockerCli *command.DockerCli, opts unlockKeyOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func printUnlockCommand(ctx context.Context, dockerCli *command.DockerCli, unlockKey string) {
|
||||
func printUnlockCommand(ctx context.Context, dockerCli command.Cli, unlockKey string) {
|
||||
if len(unlockKey) > 0 {
|
||||
fmt.Fprintf(dockerCli.Out(), "To unlock a swarm manager after it restarts, run the `docker swarm unlock`\ncommand and provide the following key:\n\n %s\n\nPlease remember to store this key in a password manager, since without it you\nwill not be able to restart the manager.\n", unlockKey)
|
||||
}
|
||||
|
||||
175
command/swarm/unlock_key_test.go
Normal file
175
command/swarm/unlock_key_test.go
Normal file
@ -0,0 +1,175 @@
|
||||
package swarm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/cli/internal/test"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/docker/cli/internal/test/builders"
|
||||
"github.com/docker/docker/pkg/testutil/assert"
|
||||
"github.com/docker/docker/pkg/testutil/golden"
|
||||
)
|
||||
|
||||
func TestSwarmUnlockKeyErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
flags map[string]string
|
||||
swarmInspectFunc func() (swarm.Swarm, error)
|
||||
swarmUpdateFunc func(swarm swarm.Spec, flags swarm.UpdateFlags) error
|
||||
swarmGetUnlockKeyFunc func() (types.SwarmUnlockKeyResponse, error)
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "too-many-args",
|
||||
args: []string{"foo"},
|
||||
expectedError: "accepts no argument(s)",
|
||||
},
|
||||
{
|
||||
name: "swarm-inspect-rotate-failed",
|
||||
flags: map[string]string{
|
||||
flagRotate: "true",
|
||||
},
|
||||
swarmInspectFunc: func() (swarm.Swarm, error) {
|
||||
return swarm.Swarm{}, fmt.Errorf("error inspecting the swarm")
|
||||
},
|
||||
expectedError: "error inspecting the swarm",
|
||||
},
|
||||
{
|
||||
name: "swarm-rotate-no-autolock-failed",
|
||||
flags: map[string]string{
|
||||
flagRotate: "true",
|
||||
},
|
||||
swarmInspectFunc: func() (swarm.Swarm, error) {
|
||||
return *Swarm(), nil
|
||||
},
|
||||
expectedError: "cannot rotate because autolock is not turned on",
|
||||
},
|
||||
{
|
||||
name: "swarm-update-failed",
|
||||
flags: map[string]string{
|
||||
flagRotate: "true",
|
||||
},
|
||||
swarmInspectFunc: func() (swarm.Swarm, error) {
|
||||
return *Swarm(Autolock()), nil
|
||||
},
|
||||
swarmUpdateFunc: func(swarm swarm.Spec, flags swarm.UpdateFlags) error {
|
||||
return fmt.Errorf("error updating the swarm")
|
||||
},
|
||||
expectedError: "error updating the swarm",
|
||||
},
|
||||
{
|
||||
name: "swarm-get-unlock-key-failed",
|
||||
swarmGetUnlockKeyFunc: func() (types.SwarmUnlockKeyResponse, error) {
|
||||
return types.SwarmUnlockKeyResponse{}, fmt.Errorf("error getting unlock key")
|
||||
},
|
||||
expectedError: "error getting unlock key",
|
||||
},
|
||||
{
|
||||
name: "swarm-no-unlock-key-failed",
|
||||
swarmGetUnlockKeyFunc: func() (types.SwarmUnlockKeyResponse, error) {
|
||||
return types.SwarmUnlockKeyResponse{
|
||||
UnlockKey: "",
|
||||
}, nil
|
||||
},
|
||||
expectedError: "no unlock key is set",
|
||||
},
|
||||
}
|
||||
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)
|
||||
}
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
assert.Error(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSwarmUnlockKey(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
flags map[string]string
|
||||
swarmInspectFunc func() (swarm.Swarm, error)
|
||||
swarmUpdateFunc func(swarm swarm.Spec, flags swarm.UpdateFlags) error
|
||||
swarmGetUnlockKeyFunc func() (types.SwarmUnlockKeyResponse, error)
|
||||
}{
|
||||
{
|
||||
name: "unlock-key",
|
||||
swarmGetUnlockKeyFunc: func() (types.SwarmUnlockKeyResponse, error) {
|
||||
return types.SwarmUnlockKeyResponse{
|
||||
UnlockKey: "unlock-key",
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unlock-key-quiet",
|
||||
flags: map[string]string{
|
||||
flagQuiet: "true",
|
||||
},
|
||||
swarmGetUnlockKeyFunc: func() (types.SwarmUnlockKeyResponse, error) {
|
||||
return types.SwarmUnlockKeyResponse{
|
||||
UnlockKey: "unlock-key",
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unlock-key-rotate",
|
||||
flags: map[string]string{
|
||||
flagRotate: "true",
|
||||
},
|
||||
swarmInspectFunc: func() (swarm.Swarm, error) {
|
||||
return *Swarm(Autolock()), nil
|
||||
},
|
||||
swarmGetUnlockKeyFunc: func() (types.SwarmUnlockKeyResponse, error) {
|
||||
return types.SwarmUnlockKeyResponse{
|
||||
UnlockKey: "unlock-key",
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unlock-key-rotate-quiet",
|
||||
flags: map[string]string{
|
||||
flagQuiet: "true",
|
||||
flagRotate: "true",
|
||||
},
|
||||
swarmInspectFunc: func() (swarm.Swarm, error) {
|
||||
return *Swarm(Autolock()), nil
|
||||
},
|
||||
swarmGetUnlockKeyFunc: func() (types.SwarmUnlockKeyResponse, error) {
|
||||
return types.SwarmUnlockKeyResponse{
|
||||
UnlockKey: "unlock-key",
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
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)
|
||||
}
|
||||
assert.NilError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
expected := golden.Get(t, []byte(actual), fmt.Sprintf("unlockkeys-%s.golden", tc.name))
|
||||
assert.EqualNormalizedString(t, assert.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
}
|
||||
101
command/swarm/unlock_test.go
Normal file
101
command/swarm/unlock_test.go
Normal file
@ -0,0 +1,101 @@
|
||||
package swarm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/cli/internal/test"
|
||||
"github.com/docker/docker/pkg/testutil/assert"
|
||||
)
|
||||
|
||||
func TestSwarmUnlockErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
input string
|
||||
swarmUnlockFunc func(req swarm.UnlockRequest) error
|
||||
infoFunc func() (types.Info, error)
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "too-many-args",
|
||||
args: []string{"foo"},
|
||||
expectedError: "accepts no argument(s)",
|
||||
},
|
||||
{
|
||||
name: "is-not-part-of-a-swarm",
|
||||
infoFunc: func() (types.Info, error) {
|
||||
return types.Info{
|
||||
Swarm: swarm.Info{
|
||||
LocalNodeState: swarm.LocalNodeStateInactive,
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
expectedError: "This node is not part of a swarm",
|
||||
},
|
||||
{
|
||||
name: "is-not-locked",
|
||||
infoFunc: func() (types.Info, error) {
|
||||
return types.Info{
|
||||
Swarm: swarm.Info{
|
||||
LocalNodeState: swarm.LocalNodeStateActive,
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
expectedError: "Error: swarm is not locked",
|
||||
},
|
||||
{
|
||||
name: "unlockrequest-failed",
|
||||
infoFunc: func() (types.Info, error) {
|
||||
return types.Info{
|
||||
Swarm: swarm.Info{
|
||||
LocalNodeState: swarm.LocalNodeStateLocked,
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
swarmUnlockFunc: func(req swarm.UnlockRequest) error {
|
||||
return fmt.Errorf("error unlocking the swarm")
|
||||
},
|
||||
expectedError: "error unlocking the swarm",
|
||||
},
|
||||
}
|
||||
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)
|
||||
assert.Error(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSwarmUnlock(t *testing.T) {
|
||||
input := "unlockKey"
|
||||
buf := new(bytes.Buffer)
|
||||
dockerCli := test.NewFakeCli(&fakeClient{
|
||||
infoFunc: func() (types.Info, error) {
|
||||
return types.Info{
|
||||
Swarm: swarm.Info{
|
||||
LocalNodeState: swarm.LocalNodeStateLocked,
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
swarmUnlockFunc: func(req swarm.UnlockRequest) error {
|
||||
if req.UnlockKey != input {
|
||||
return fmt.Errorf("Invalid unlock key")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}, buf)
|
||||
dockerCli.SetIn(ioutil.NopCloser(strings.NewReader(input)))
|
||||
cmd := newUnlockCommand(dockerCli)
|
||||
assert.NilError(t, cmd.Execute())
|
||||
}
|
||||
@ -13,7 +13,7 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func newUpdateCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
func newUpdateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
opts := swarmOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -36,24 +36,24 @@ func newUpdateCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runUpdate(dockerCli *command.DockerCli, flags *pflag.FlagSet, opts swarmOptions) error {
|
||||
func runUpdate(dockerCli command.Cli, flags *pflag.FlagSet, opts swarmOptions) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
|
||||
var updateFlags swarm.UpdateFlags
|
||||
|
||||
swarm, err := client.SwarmInspect(ctx)
|
||||
swarmInspect, err := client.SwarmInspect(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
prevAutoLock := swarm.Spec.EncryptionConfig.AutoLockManagers
|
||||
prevAutoLock := swarmInspect.Spec.EncryptionConfig.AutoLockManagers
|
||||
|
||||
opts.mergeSwarmSpec(&swarm.Spec, flags)
|
||||
opts.mergeSwarmSpec(&swarmInspect.Spec, flags)
|
||||
|
||||
curAutoLock := swarm.Spec.EncryptionConfig.AutoLockManagers
|
||||
curAutoLock := swarmInspect.Spec.EncryptionConfig.AutoLockManagers
|
||||
|
||||
err = client.SwarmUpdate(ctx, swarm.Version, swarm.Spec, updateFlags)
|
||||
err = client.SwarmUpdate(ctx, swarmInspect.Version, swarmInspect.Spec, updateFlags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
182
command/swarm/update_test.go
Normal file
182
command/swarm/update_test.go
Normal file
@ -0,0 +1,182 @@
|
||||
package swarm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/cli/internal/test"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/docker/cli/internal/test/builders"
|
||||
"github.com/docker/docker/pkg/testutil/assert"
|
||||
"github.com/docker/docker/pkg/testutil/golden"
|
||||
)
|
||||
|
||||
func TestSwarmUpdateErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
flags map[string]string
|
||||
swarmInspectFunc func() (swarm.Swarm, error)
|
||||
swarmUpdateFunc func(swarm swarm.Spec, flags swarm.UpdateFlags) error
|
||||
swarmGetUnlockKeyFunc func() (types.SwarmUnlockKeyResponse, error)
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "too-many-args",
|
||||
args: []string{"foo"},
|
||||
expectedError: "accepts no argument(s)",
|
||||
},
|
||||
{
|
||||
name: "swarm-inspect-error",
|
||||
flags: map[string]string{
|
||||
flagTaskHistoryLimit: "10",
|
||||
},
|
||||
swarmInspectFunc: func() (swarm.Swarm, error) {
|
||||
return swarm.Swarm{}, fmt.Errorf("error inspecting the swarm")
|
||||
},
|
||||
expectedError: "error inspecting the swarm",
|
||||
},
|
||||
{
|
||||
name: "swarm-update-error",
|
||||
flags: map[string]string{
|
||||
flagTaskHistoryLimit: "10",
|
||||
},
|
||||
swarmUpdateFunc: func(swarm swarm.Spec, flags swarm.UpdateFlags) error {
|
||||
return fmt.Errorf("error updating the swarm")
|
||||
},
|
||||
expectedError: "error updating the swarm",
|
||||
},
|
||||
{
|
||||
name: "swarm-unlockkey-error",
|
||||
flags: map[string]string{
|
||||
flagAutolock: "true",
|
||||
},
|
||||
swarmInspectFunc: func() (swarm.Swarm, error) {
|
||||
return *Swarm(), nil
|
||||
},
|
||||
swarmGetUnlockKeyFunc: func() (types.SwarmUnlockKeyResponse, error) {
|
||||
return types.SwarmUnlockKeyResponse{}, fmt.Errorf("error getting unlock key")
|
||||
},
|
||||
expectedError: "error getting unlock key",
|
||||
},
|
||||
}
|
||||
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)
|
||||
}
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
assert.Error(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSwarmUpdate(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
flags map[string]string
|
||||
swarmInspectFunc func() (swarm.Swarm, error)
|
||||
swarmUpdateFunc func(swarm swarm.Spec, flags swarm.UpdateFlags) error
|
||||
swarmGetUnlockKeyFunc func() (types.SwarmUnlockKeyResponse, error)
|
||||
}{
|
||||
{
|
||||
name: "noargs",
|
||||
},
|
||||
{
|
||||
name: "all-flags-quiet",
|
||||
flags: map[string]string{
|
||||
flagTaskHistoryLimit: "10",
|
||||
flagDispatcherHeartbeat: "10s",
|
||||
flagCertExpiry: "20s",
|
||||
flagExternalCA: "protocol=cfssl,url=https://example.com.",
|
||||
flagMaxSnapshots: "10",
|
||||
flagSnapshotInterval: "100",
|
||||
flagAutolock: "true",
|
||||
flagQuiet: "true",
|
||||
},
|
||||
swarmUpdateFunc: func(swarm swarm.Spec, flags swarm.UpdateFlags) error {
|
||||
if *swarm.Orchestration.TaskHistoryRetentionLimit != 10 {
|
||||
return fmt.Errorf("historyLimit not correctly set")
|
||||
}
|
||||
heartbeatDuration, err := time.ParseDuration("10s")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if swarm.Dispatcher.HeartbeatPeriod != heartbeatDuration {
|
||||
return fmt.Errorf("heartbeatPeriodLimit not correctly set")
|
||||
}
|
||||
certExpiryDuration, err := time.ParseDuration("20s")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if swarm.CAConfig.NodeCertExpiry != certExpiryDuration {
|
||||
return fmt.Errorf("certExpiry not correctly set")
|
||||
}
|
||||
if len(swarm.CAConfig.ExternalCAs) != 1 {
|
||||
return fmt.Errorf("externalCA not correctly set")
|
||||
}
|
||||
if *swarm.Raft.KeepOldSnapshots != 10 {
|
||||
return fmt.Errorf("keepOldSnapshots not correctly set")
|
||||
}
|
||||
if swarm.Raft.SnapshotInterval != 100 {
|
||||
return fmt.Errorf("snapshotInterval not correctly set")
|
||||
}
|
||||
if !swarm.EncryptionConfig.AutoLockManagers {
|
||||
return fmt.Errorf("autolock not correctly set")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "autolock-unlock-key",
|
||||
flags: map[string]string{
|
||||
flagTaskHistoryLimit: "10",
|
||||
flagAutolock: "true",
|
||||
},
|
||||
swarmUpdateFunc: func(swarm swarm.Spec, flags swarm.UpdateFlags) error {
|
||||
if *swarm.Orchestration.TaskHistoryRetentionLimit != 10 {
|
||||
return fmt.Errorf("historyLimit not correctly set")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
swarmInspectFunc: func() (swarm.Swarm, error) {
|
||||
return *Swarm(), nil
|
||||
},
|
||||
swarmGetUnlockKeyFunc: func() (types.SwarmUnlockKeyResponse, error) {
|
||||
return types.SwarmUnlockKeyResponse{
|
||||
UnlockKey: "unlock-key",
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
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)
|
||||
}
|
||||
cmd.SetOutput(buf)
|
||||
assert.NilError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
expected := golden.Get(t, []byte(actual), fmt.Sprintf("update-%s.golden", tc.name))
|
||||
assert.EqualNormalizedString(t, assert.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
}
|
||||
@ -61,7 +61,7 @@ func (t tasksBySlot) Less(i, j int) bool {
|
||||
// Print task information in a table format.
|
||||
// Besides this, command `docker node ps <node>`
|
||||
// and `docker stack ps` will call this, too.
|
||||
func Print(dockerCli *command.DockerCli, ctx context.Context, tasks []swarm.Task, resolver *idresolver.IDResolver, noTrunc bool) error {
|
||||
func Print(dockerCli command.Cli, ctx context.Context, tasks []swarm.Task, resolver *idresolver.IDResolver, noTrunc bool) error {
|
||||
sort.Stable(tasksBySlot(tasks))
|
||||
|
||||
writer := tabwriter.NewWriter(dockerCli.Out(), 0, 4, 2, ' ', 0)
|
||||
@ -74,7 +74,7 @@ func Print(dockerCli *command.DockerCli, ctx context.Context, tasks []swarm.Task
|
||||
}
|
||||
|
||||
// PrintQuiet shows task list in a quiet way.
|
||||
func PrintQuiet(dockerCli *command.DockerCli, tasks []swarm.Task) error {
|
||||
func PrintQuiet(dockerCli command.Cli, tasks []swarm.Task) error {
|
||||
sort.Stable(tasksBySlot(tasks))
|
||||
|
||||
out := dockerCli.Out()
|
||||
|
||||
Reference in New Issue
Block a user