Browse code

fix docker swarm init/update --secret

Signed-off-by: Victor Vieux <vieux@docker.com>
(cherry picked from commit 085895342c7f797f0eef7ec956438e33489f211f)

Victor Vieux authored on 2016/06/16 06:30:54
Showing 15 changed files
... ...
@@ -41,7 +41,8 @@ func runRestart(dockerCli *client.DockerCli, opts *restartOptions) error {
41 41
 	ctx := context.Background()
42 42
 	var errs []string
43 43
 	for _, name := range opts.containers {
44
-		if err := dockerCli.Client().ContainerRestart(ctx, name, time.Duration(opts.nSeconds)*time.Second); err != nil {
44
+		timeout := time.Duration(opts.nSeconds) * time.Second
45
+		if err := dockerCli.Client().ContainerRestart(ctx, name, &timeout); err != nil {
45 46
 			errs = append(errs, err.Error())
46 47
 		} else {
47 48
 			fmt.Fprintf(dockerCli.Out(), "%s\n", name)
... ...
@@ -43,7 +43,8 @@ func runStop(dockerCli *client.DockerCli, opts *stopOptions) error {
43 43
 
44 44
 	var errs []string
45 45
 	for _, container := range opts.containers {
46
-		if err := dockerCli.Client().ContainerStop(ctx, container, time.Duration(opts.time)*time.Second); err != nil {
46
+		timeout := time.Duration(opts.time) * time.Second
47
+		if err := dockerCli.Client().ContainerStop(ctx, container, &timeout); err != nil {
47 48
 			errs = append(errs, err.Error())
48 49
 		} else {
49 50
 			fmt.Fprintf(dockerCli.Out(), "%s\n", container)
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"github.com/docker/docker/cli"
10 10
 	"github.com/docker/engine-api/types/swarm"
11 11
 	"github.com/spf13/cobra"
12
+	"github.com/spf13/pflag"
12 13
 )
13 14
 
14 15
 type initOptions struct {
... ...
@@ -19,6 +20,7 @@ type initOptions struct {
19 19
 }
20 20
 
21 21
 func newInitCommand(dockerCli *client.DockerCli) *cobra.Command {
22
+	var flags *pflag.FlagSet
22 23
 	opts := initOptions{
23 24
 		listenAddr: NewNodeAddrOption(),
24 25
 		autoAccept: NewAutoAcceptOption(),
... ...
@@ -29,11 +31,11 @@ func newInitCommand(dockerCli *client.DockerCli) *cobra.Command {
29 29
 		Short: "Initialize a Swarm.",
30 30
 		Args:  cli.NoArgs,
31 31
 		RunE: func(cmd *cobra.Command, args []string) error {
32
-			return runInit(dockerCli, opts)
32
+			return runInit(dockerCli, flags, opts)
33 33
 		},
34 34
 	}
35 35
 
36
-	flags := cmd.Flags()
36
+	flags = cmd.Flags()
37 37
 	flags.Var(&opts.listenAddr, "listen-addr", "Listen address")
38 38
 	flags.Var(&opts.autoAccept, "auto-accept", "Auto acceptance policy (worker, manager, or none)")
39 39
 	flags.StringVar(&opts.secret, "secret", "", "Set secret value needed to accept nodes into cluster")
... ...
@@ -41,7 +43,7 @@ func newInitCommand(dockerCli *client.DockerCli) *cobra.Command {
41 41
 	return cmd
42 42
 }
43 43
 
44
-func runInit(dockerCli *client.DockerCli, opts initOptions) error {
44
+func runInit(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts initOptions) error {
45 45
 	client := dockerCli.Client()
46 46
 	ctx := context.Background()
47 47
 
... ...
@@ -50,8 +52,11 @@ func runInit(dockerCli *client.DockerCli, opts initOptions) error {
50 50
 		ForceNewCluster: opts.forceNewCluster,
51 51
 	}
52 52
 
53
-	req.Spec.AcceptancePolicy.Policies = opts.autoAccept.Policies(opts.secret)
54
-
53
+	if flags.Changed("secret") {
54
+		req.Spec.AcceptancePolicy.Policies = opts.autoAccept.Policies(&opts.secret)
55
+	} else {
56
+		req.Spec.AcceptancePolicy.Policies = opts.autoAccept.Policies(nil)
57
+	}
55 58
 	nodeID, err := client.SwarmInit(ctx, req)
56 59
 	if err != nil {
57 60
 		return err
... ...
@@ -102,7 +102,7 @@ func (o *AutoAcceptOption) Type() string {
102 102
 }
103 103
 
104 104
 // Policies returns a representation of this option for the api
105
-func (o *AutoAcceptOption) Policies(secret string) []swarm.Policy {
105
+func (o *AutoAcceptOption) Policies(secret *string) []swarm.Policy {
106 106
 	policies := []swarm.Policy{}
107 107
 	for _, p := range defaultPolicies {
108 108
 		if len(o.values) != 0 {
... ...
@@ -54,6 +54,7 @@ func runUpdate(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts updateOpt
54 54
 	if err != nil {
55 55
 		return err
56 56
 	}
57
+
57 58
 	err = client.SwarmUpdate(ctx, swarm.Version, swarm.Spec)
58 59
 	if err != nil {
59 60
 		return err
... ...
@@ -68,18 +69,17 @@ func mergeSwarm(swarm *swarm.Swarm, flags *pflag.FlagSet) error {
68 68
 
69 69
 	if flags.Changed("auto-accept") {
70 70
 		value := flags.Lookup("auto-accept").Value.(*AutoAcceptOption)
71
-		if len(spec.AcceptancePolicy.Policies) > 0 {
72
-			spec.AcceptancePolicy.Policies = value.Policies(spec.AcceptancePolicy.Policies[0].Secret)
73
-		} else {
74
-			spec.AcceptancePolicy.Policies = value.Policies("")
75
-		}
71
+		spec.AcceptancePolicy.Policies = value.Policies(nil)
76 72
 	}
77 73
 
74
+	var psecret *string
78 75
 	if flags.Changed("secret") {
79 76
 		secret, _ := flags.GetString("secret")
80
-		for _, policy := range spec.AcceptancePolicy.Policies {
81
-			policy.Secret = secret
82
-		}
77
+		psecret = &secret
78
+	}
79
+
80
+	for i := range spec.AcceptancePolicy.Policies {
81
+		spec.AcceptancePolicy.Policies[i].Secret = psecret
83 82
 	}
84 83
 
85 84
 	if flags.Changed("task-history-limit") {
... ...
@@ -444,12 +444,12 @@ func (c *Cluster) Update(version uint64, spec types.Spec) error {
444 444
 		return ErrNoManager
445 445
 	}
446 446
 
447
-	swarmSpec, err := convert.SwarmSpecToGRPC(spec)
447
+	swarm, err := getSwarm(c.getRequestContext(), c.client)
448 448
 	if err != nil {
449 449
 		return err
450 450
 	}
451 451
 
452
-	swarm, err := getSwarm(c.getRequestContext(), c.client)
452
+	swarmSpec, err := convert.SwarmSpecToGRPCandMerge(spec, &swarm.Spec)
453 453
 	if err != nil {
454 454
 		return err
455 455
 	}
... ...
@@ -1030,7 +1030,7 @@ func initAcceptancePolicy(node *swarmagent.Node, acceptancePolicy types.Acceptan
1030 1030
 			}
1031 1031
 			spec := &cluster.Spec
1032 1032
 
1033
-			if err := convert.SwarmSpecUpdateAcceptancePolicy(spec, acceptancePolicy); err != nil {
1033
+			if err := convert.SwarmSpecUpdateAcceptancePolicy(spec, acceptancePolicy, nil); err != nil {
1034 1034
 				return fmt.Errorf("error updating cluster settings: %v", err)
1035 1035
 			}
1036 1036
 			_, err := client.UpdateCluster(ctx, &swarmapi.UpdateClusterRequest{
... ...
@@ -49,7 +49,8 @@ func SwarmFromGRPC(c swarmapi.Cluster) types.Swarm {
49 49
 			Autoaccept: policy.Autoaccept,
50 50
 		}
51 51
 		if policy.Secret != nil {
52
-			p.Secret = string(policy.Secret.Data)
52
+			secret := string(policy.Secret.Data)
53
+			p.Secret = &secret
53 54
 		}
54 55
 		swarm.Spec.AcceptancePolicy.Policies = append(swarm.Spec.AcceptancePolicy.Policies, p)
55 56
 	}
... ...
@@ -57,8 +58,8 @@ func SwarmFromGRPC(c swarmapi.Cluster) types.Swarm {
57 57
 	return swarm
58 58
 }
59 59
 
60
-// SwarmSpecToGRPC converts a Spec to a grpc ClusterSpec.
61
-func SwarmSpecToGRPC(s types.Spec) (swarmapi.ClusterSpec, error) {
60
+// SwarmSpecToGRPCandMerge converts a Spec to a grpc ClusterSpec and merge AcceptancePolicy from an existing grpc ClusterSpec if provided.
61
+func SwarmSpecToGRPCandMerge(s types.Spec, existingSpec *swarmapi.ClusterSpec) (swarmapi.ClusterSpec, error) {
62 62
 	spec := swarmapi.ClusterSpec{
63 63
 		Annotations: swarmapi.Annotations{
64 64
 			Name:   s.Name,
... ...
@@ -82,14 +83,15 @@ func SwarmSpecToGRPC(s types.Spec) (swarmapi.ClusterSpec, error) {
82 82
 		},
83 83
 	}
84 84
 
85
-	if err := SwarmSpecUpdateAcceptancePolicy(&spec, s.AcceptancePolicy); err != nil {
85
+	if err := SwarmSpecUpdateAcceptancePolicy(&spec, s.AcceptancePolicy, existingSpec); err != nil {
86 86
 		return swarmapi.ClusterSpec{}, err
87 87
 	}
88
+
88 89
 	return spec, nil
89 90
 }
90 91
 
91 92
 // SwarmSpecUpdateAcceptancePolicy updates a grpc ClusterSpec using AcceptancePolicy.
92
-func SwarmSpecUpdateAcceptancePolicy(spec *swarmapi.ClusterSpec, acceptancePolicy types.AcceptancePolicy) error {
93
+func SwarmSpecUpdateAcceptancePolicy(spec *swarmapi.ClusterSpec, acceptancePolicy types.AcceptancePolicy, oldSpec *swarmapi.ClusterSpec) error {
93 94
 	spec.AcceptancePolicy.Policies = nil
94 95
 	for _, p := range acceptancePolicy.Policies {
95 96
 		role, ok := swarmapi.NodeRole_value[strings.ToUpper(string(p.Role))]
... ...
@@ -102,11 +104,20 @@ func SwarmSpecUpdateAcceptancePolicy(spec *swarmapi.ClusterSpec, acceptancePolic
102 102
 			Autoaccept: p.Autoaccept,
103 103
 		}
104 104
 
105
-		if p.Secret != "" {
106
-			hashPwd, _ := bcrypt.GenerateFromPassword([]byte(p.Secret), 0)
105
+		if p.Secret != nil {
106
+			if *p.Secret == "" { // if provided secret is empty, it means erase previous secret.
107
+				policy.Secret = nil
108
+			} else { // if provided secret is not empty, we generate a new one.
109
+				hashPwd, _ := bcrypt.GenerateFromPassword([]byte(*p.Secret), 0)
110
+				policy.Secret = &swarmapi.AcceptancePolicy_RoleAdmissionPolicy_HashedSecret{
111
+					Data: hashPwd,
112
+					Alg:  "bcrypt",
113
+				}
114
+			}
115
+		} else if oldSecret := getOldSecret(oldSpec, policy.Role); oldSecret != nil { // else use the old one.
107 116
 			policy.Secret = &swarmapi.AcceptancePolicy_RoleAdmissionPolicy_HashedSecret{
108
-				Data: hashPwd,
109
-				Alg:  "bcrypt",
117
+				Data: oldSecret.Data,
118
+				Alg:  oldSecret.Alg,
110 119
 			}
111 120
 		}
112 121
 
... ...
@@ -114,3 +125,15 @@ func SwarmSpecUpdateAcceptancePolicy(spec *swarmapi.ClusterSpec, acceptancePolic
114 114
 	}
115 115
 	return nil
116 116
 }
117
+
118
+func getOldSecret(oldSpec *swarmapi.ClusterSpec, role swarmapi.NodeRole) *swarmapi.AcceptancePolicy_RoleAdmissionPolicy_HashedSecret {
119
+	if oldSpec == nil {
120
+		return nil
121
+	}
122
+	for _, p := range oldSpec.AcceptancePolicy.Policies {
123
+		if p.Role == role {
124
+			return p.Secret
125
+		}
126
+	}
127
+	return nil
128
+}
... ...
@@ -60,7 +60,7 @@ clone git golang.org/x/net 2beffdc2e92c8a3027590f898fe88f69af48a3f8 https://gith
60 60
 clone git golang.org/x/sys eb2c74142fd19a79b3f237334c7384d5167b1b46 https://github.com/golang/sys.git
61 61
 clone git github.com/docker/go-units 651fc226e7441360384da338d0fd37f2440ffbe3
62 62
 clone git github.com/docker/go-connections fa2850ff103453a9ad190da0df0af134f0314b3d
63
-clone git github.com/docker/engine-api f3b5ad20d4576de14c96603db522dec530d03f62
63
+clone git github.com/docker/engine-api f50fbe5f9c4c8eeed591549d2c8187f4076f3717
64 64
 clone git github.com/RackSec/srslog 259aed10dfa74ea2961eddd1d9847619f6e98837
65 65
 clone git github.com/imdario/mergo 0.2.1
66 66
 
... ...
@@ -26,11 +26,16 @@ func (d *SwarmDaemon) Init(autoAccept map[string]bool, secret string) error {
26 26
 		ListenAddr: d.listenAddr,
27 27
 	}
28 28
 	for _, role := range []swarm.NodeRole{swarm.NodeRoleManager, swarm.NodeRoleWorker} {
29
-		req.Spec.AcceptancePolicy.Policies = append(req.Spec.AcceptancePolicy.Policies, swarm.Policy{
29
+		policy := swarm.Policy{
30 30
 			Role:       role,
31 31
 			Autoaccept: autoAccept[strings.ToLower(string(role))],
32
-			Secret:     secret,
33
-		})
32
+		}
33
+
34
+		if secret != "" {
35
+			policy.Secret = &secret
36
+		}
37
+
38
+		req.Spec.AcceptancePolicy.Policies = append(req.Spec.AcceptancePolicy.Policies, policy)
34 39
 	}
35 40
 	status, out, err := d.SockRequest("POST", "/swarm/init", req)
36 41
 	if status != http.StatusOK {
... ...
@@ -49,13 +54,17 @@ func (d *SwarmDaemon) Init(autoAccept map[string]bool, secret string) error {
49 49
 
50 50
 // Join joins a current daemon with existing cluster.
51 51
 func (d *SwarmDaemon) Join(remoteAddr, secret, cahash string, manager bool) error {
52
-	status, out, err := d.SockRequest("POST", "/swarm/join", swarm.JoinRequest{
52
+	req := swarm.JoinRequest{
53 53
 		ListenAddr:  d.listenAddr,
54 54
 		RemoteAddrs: []string{remoteAddr},
55 55
 		Manager:     manager,
56
-		Secret:      secret,
57 56
 		CACertHash:  cahash,
58
-	})
57
+	}
58
+
59
+	if secret != "" {
60
+		req.Secret = secret
61
+	}
62
+	status, out, err := d.SockRequest("POST", "/swarm/join", req)
59 63
 	if status != http.StatusOK {
60 64
 		return fmt.Errorf("joining swarm: invalid statuscode %v, %q", status, out)
61 65
 	}
... ...
@@ -11,9 +11,11 @@ import (
11 11
 // ContainerRestart stops and starts a container again.
12 12
 // It makes the daemon to wait for the container to be up again for
13 13
 // a specific amount of time, given the timeout.
14
-func (cli *Client) ContainerRestart(ctx context.Context, containerID string, timeout time.Duration) error {
14
+func (cli *Client) ContainerRestart(ctx context.Context, containerID string, timeout *time.Duration) error {
15 15
 	query := url.Values{}
16
-	query.Set("t", timetypes.DurationToSecondsString(timeout))
16
+	if timeout != nil {
17
+		query.Set("t", timetypes.DurationToSecondsString(*timeout))
18
+	}
17 19
 	resp, err := cli.post(ctx, "/containers/"+containerID+"/restart", query, nil, nil)
18 20
 	ensureReaderClosed(resp)
19 21
 	return err
... ...
@@ -10,9 +10,11 @@ import (
10 10
 
11 11
 // ContainerStop stops a container without terminating the process.
12 12
 // The process is blocked until the container stops or the timeout expires.
13
-func (cli *Client) ContainerStop(ctx context.Context, containerID string, timeout time.Duration) error {
13
+func (cli *Client) ContainerStop(ctx context.Context, containerID string, timeout *time.Duration) error {
14 14
 	query := url.Values{}
15
-	query.Set("t", timetypes.DurationToSecondsString(timeout))
15
+	if timeout != nil {
16
+		query.Set("t", timetypes.DurationToSecondsString(*timeout))
17
+	}
16 18
 	resp, err := cli.post(ctx, "/containers/"+containerID+"/stop", query, nil, nil)
17 19
 	ensureReaderClosed(resp)
18 20
 	return err
... ...
@@ -15,26 +15,21 @@ import (
15 15
 
16 16
 // CommonAPIClient is the common methods between stable and experimental versions of APIClient.
17 17
 type CommonAPIClient interface {
18
+	ContainerAPIClient
19
+	ImageAPIClient
20
+	NodeAPIClient
21
+	NetworkAPIClient
22
+	ServiceAPIClient
23
+	SwarmAPIClient
24
+	SystemAPIClient
25
+	VolumeAPIClient
18 26
 	ClientVersion() string
19
-	CheckpointCreate(ctx context.Context, container string, options types.CheckpointCreateOptions) error
20
-	CheckpointDelete(ctx context.Context, container string, checkpointID string) error
21
-	CheckpointList(ctx context.Context, container string) ([]types.Checkpoint, error)
22
-	SwarmInit(ctx context.Context, req swarm.InitRequest) (string, error)
23
-	SwarmJoin(ctx context.Context, req swarm.JoinRequest) error
24
-	SwarmLeave(ctx context.Context, force bool) error
25
-	SwarmInspect(ctx context.Context) (swarm.Swarm, error)
26
-	SwarmUpdate(ctx context.Context, version swarm.Version, swarm swarm.Spec) error
27
-	NodeInspect(ctx context.Context, nodeID string) (swarm.Node, error)
28
-	NodeList(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error)
29
-	NodeRemove(ctx context.Context, nodeID string) error
30
-	NodeUpdate(ctx context.Context, nodeID string, version swarm.Version, node swarm.NodeSpec) error
31
-	ServiceCreate(ctx context.Context, service swarm.ServiceSpec) (types.ServiceCreateResponse, error)
32
-	ServiceInspect(ctx context.Context, serviceID string) (swarm.Service, error)
33
-	ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error)
34
-	ServiceRemove(ctx context.Context, serviceID string) error
35
-	ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec) error
36
-	TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error)
37
-	TaskList(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error)
27
+	ServerVersion(ctx context.Context) (types.Version, error)
28
+	UpdateClientVersion(v string)
29
+}
30
+
31
+// ContainerAPIClient defines API client methods for the containers
32
+type ContainerAPIClient interface {
38 33
 	ContainerAttach(ctx context.Context, container string, options types.ContainerAttachOptions) (types.HijackedResponse, error)
39 34
 	ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.ContainerCommitResponse, error)
40 35
 	ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (types.ContainerCreateResponse, error)
... ...
@@ -54,18 +49,21 @@ type CommonAPIClient interface {
54 54
 	ContainerRemove(ctx context.Context, container string, options types.ContainerRemoveOptions) error
55 55
 	ContainerRename(ctx context.Context, container, newContainerName string) error
56 56
 	ContainerResize(ctx context.Context, container string, options types.ResizeOptions) error
57
-	ContainerRestart(ctx context.Context, container string, timeout time.Duration) error
57
+	ContainerRestart(ctx context.Context, container string, timeout *time.Duration) error
58 58
 	ContainerStatPath(ctx context.Context, container, path string) (types.ContainerPathStat, error)
59 59
 	ContainerStats(ctx context.Context, container string, stream bool) (io.ReadCloser, error)
60 60
 	ContainerStart(ctx context.Context, container string, options types.ContainerStartOptions) error
61
-	ContainerStop(ctx context.Context, container string, timeout time.Duration) error
61
+	ContainerStop(ctx context.Context, container string, timeout *time.Duration) error
62 62
 	ContainerTop(ctx context.Context, container string, arguments []string) (types.ContainerProcessList, error)
63 63
 	ContainerUnpause(ctx context.Context, container string) error
64 64
 	ContainerUpdate(ctx context.Context, container string, updateConfig container.UpdateConfig) error
65 65
 	ContainerWait(ctx context.Context, container string) (int, error)
66 66
 	CopyFromContainer(ctx context.Context, container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error)
67 67
 	CopyToContainer(ctx context.Context, container, path string, content io.Reader, options types.CopyToContainerOptions) error
68
-	Events(ctx context.Context, options types.EventsOptions) (io.ReadCloser, error)
68
+}
69
+
70
+// ImageAPIClient defines API client methods for the images
71
+type ImageAPIClient interface {
69 72
 	ImageBuild(ctx context.Context, context io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error)
70 73
 	ImageCreate(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error)
71 74
 	ImageHistory(ctx context.Context, image string) ([]types.ImageHistory, error)
... ...
@@ -79,7 +77,10 @@ type CommonAPIClient interface {
79 79
 	ImageSearch(ctx context.Context, term string, options types.ImageSearchOptions) ([]registry.SearchResult, error)
80 80
 	ImageSave(ctx context.Context, images []string) (io.ReadCloser, error)
81 81
 	ImageTag(ctx context.Context, image, ref string) error
82
-	Info(ctx context.Context) (types.Info, error)
82
+}
83
+
84
+// NetworkAPIClient defines API client methods for the networks
85
+type NetworkAPIClient interface {
83 86
 	NetworkConnect(ctx context.Context, networkID, container string, config *network.EndpointSettings) error
84 87
 	NetworkCreate(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error)
85 88
 	NetworkDisconnect(ctx context.Context, networkID, container string, force bool) error
... ...
@@ -87,9 +88,45 @@ type CommonAPIClient interface {
87 87
 	NetworkInspectWithRaw(ctx context.Context, networkID string) (types.NetworkResource, []byte, error)
88 88
 	NetworkList(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error)
89 89
 	NetworkRemove(ctx context.Context, networkID string) error
90
+}
91
+
92
+// NodeAPIClient defines API client methods for the nodes
93
+type NodeAPIClient interface {
94
+	NodeInspect(ctx context.Context, nodeID string) (swarm.Node, error)
95
+	NodeList(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error)
96
+	NodeRemove(ctx context.Context, nodeID string) error
97
+	NodeUpdate(ctx context.Context, nodeID string, version swarm.Version, node swarm.NodeSpec) error
98
+}
99
+
100
+// ServiceAPIClient defines API client methods for the services
101
+type ServiceAPIClient interface {
102
+	ServiceCreate(ctx context.Context, service swarm.ServiceSpec) (types.ServiceCreateResponse, error)
103
+	ServiceInspect(ctx context.Context, serviceID string) (swarm.Service, error)
104
+	ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error)
105
+	ServiceRemove(ctx context.Context, serviceID string) error
106
+	ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec) error
107
+	TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error)
108
+	TaskList(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error)
109
+}
110
+
111
+// SwarmAPIClient defines API client methods for the swarm
112
+type SwarmAPIClient interface {
113
+	SwarmInit(ctx context.Context, req swarm.InitRequest) (string, error)
114
+	SwarmJoin(ctx context.Context, req swarm.JoinRequest) error
115
+	SwarmLeave(ctx context.Context, force bool) error
116
+	SwarmInspect(ctx context.Context) (swarm.Swarm, error)
117
+	SwarmUpdate(ctx context.Context, version swarm.Version, swarm swarm.Spec) error
118
+}
119
+
120
+// SystemAPIClient defines API client methods for the system
121
+type SystemAPIClient interface {
122
+	Events(ctx context.Context, options types.EventsOptions) (io.ReadCloser, error)
123
+	Info(ctx context.Context) (types.Info, error)
90 124
 	RegistryLogin(ctx context.Context, auth types.AuthConfig) (types.AuthResponse, error)
91
-	ServerVersion(ctx context.Context) (types.Version, error)
92
-	UpdateClientVersion(v string)
125
+}
126
+
127
+// VolumeAPIClient defines API client methods for the volumes
128
+type VolumeAPIClient interface {
93 129
 	VolumeCreate(ctx context.Context, options types.VolumeCreateRequest) (types.Volume, error)
94 130
 	VolumeInspect(ctx context.Context, volumeID string) (types.Volume, error)
95 131
 	VolumeInspectWithRaw(ctx context.Context, volumeID string) (types.Volume, []byte, error)
... ...
@@ -12,6 +12,19 @@ import (
12 12
 // APIClient is an interface that clients that talk with a docker server must implement.
13 13
 type APIClient interface {
14 14
 	CommonAPIClient
15
+	CheckpointAPIClient
16
+	PluginAPIClient
17
+}
18
+
19
+// CheckpointAPIClient defines API client methods for the checkpoints
20
+type CheckpointAPIClient interface {
21
+	CheckpointCreate(ctx context.Context, container string, options types.CheckpointCreateOptions) error
22
+	CheckpointDelete(ctx context.Context, container string, checkpointID string) error
23
+	CheckpointList(ctx context.Context, container string) ([]types.Checkpoint, error)
24
+}
25
+
26
+// PluginAPIClient defines API client methods for the plugins
27
+type PluginAPIClient interface {
15 28
 	PluginList(ctx context.Context) (types.PluginsListResponse, error)
16 29
 	PluginRemove(ctx context.Context, name string) error
17 30
 	PluginEnable(ctx context.Context, name string) error
... ...
@@ -303,7 +303,7 @@ type HostConfig struct {
303 303
 	PublishAllPorts bool              // Should docker publish all exposed port for the container
304 304
 	ReadonlyRootfs  bool              // Is the container root filesystem in read-only
305 305
 	SecurityOpt     []string          // List of string values to customize labels for MLS systems, such as SELinux.
306
-	StorageOpt      map[string]string // Storage driver options per container.
306
+	StorageOpt      map[string]string `json:",omitempty"` // Storage driver options per container.
307 307
 	Tmpfs           map[string]string `json:",omitempty"` // List of tmpfs (mounts) used for the container
308 308
 	UTSMode         UTSMode           // UTS namespace to use for the container
309 309
 	UsernsMode      UsernsMode        // The user namespace to use for the container
... ...
@@ -29,7 +29,7 @@ type AcceptancePolicy struct {
29 29
 type Policy struct {
30 30
 	Role       NodeRole
31 31
 	Autoaccept bool
32
-	Secret     string `json:",omitempty"`
32
+	Secret     *string `json:",omitempty"`
33 33
 }
34 34
 
35 35
 // OrchestrationConfig represents ochestration configuration.