Browse code

revendor engine-api

Signed-off-by: Brian Goff <cpuguy83@gmail.com>

Brian Goff authored on 2016/08/16 01:13:18
Showing 36 changed files
... ...
@@ -10,6 +10,7 @@ import (
10 10
 
11 11
 	"github.com/docker/docker/opts"
12 12
 	runconfigopts "github.com/docker/docker/runconfig/opts"
13
+	mounttypes "github.com/docker/engine-api/types/mount"
13 14
 	"github.com/docker/engine-api/types/swarm"
14 15
 	"github.com/docker/go-connections/nat"
15 16
 	units "github.com/docker/go-units"
... ...
@@ -130,7 +131,7 @@ func (i *Uint64Opt) Value() *uint64 {
130 130
 
131 131
 // MountOpt is a Value type for parsing mounts
132 132
 type MountOpt struct {
133
-	values []swarm.Mount
133
+	values []mounttypes.Mount
134 134
 }
135 135
 
136 136
 // Set a new mount value
... ...
@@ -141,23 +142,23 @@ func (m *MountOpt) Set(value string) error {
141 141
 		return err
142 142
 	}
143 143
 
144
-	mount := swarm.Mount{}
144
+	mount := mounttypes.Mount{}
145 145
 
146
-	volumeOptions := func() *swarm.VolumeOptions {
146
+	volumeOptions := func() *mounttypes.VolumeOptions {
147 147
 		if mount.VolumeOptions == nil {
148
-			mount.VolumeOptions = &swarm.VolumeOptions{
148
+			mount.VolumeOptions = &mounttypes.VolumeOptions{
149 149
 				Labels: make(map[string]string),
150 150
 			}
151 151
 		}
152 152
 		if mount.VolumeOptions.DriverConfig == nil {
153
-			mount.VolumeOptions.DriverConfig = &swarm.Driver{}
153
+			mount.VolumeOptions.DriverConfig = &mounttypes.Driver{}
154 154
 		}
155 155
 		return mount.VolumeOptions
156 156
 	}
157 157
 
158
-	bindOptions := func() *swarm.BindOptions {
158
+	bindOptions := func() *mounttypes.BindOptions {
159 159
 		if mount.BindOptions == nil {
160
-			mount.BindOptions = new(swarm.BindOptions)
160
+			mount.BindOptions = new(mounttypes.BindOptions)
161 161
 		}
162 162
 		return mount.BindOptions
163 163
 	}
... ...
@@ -171,7 +172,7 @@ func (m *MountOpt) Set(value string) error {
171 171
 		}
172 172
 	}
173 173
 
174
-	mount.Type = swarm.MountTypeVolume // default to volume mounts
174
+	mount.Type = mounttypes.TypeVolume // default to volume mounts
175 175
 	// Set writable as the default
176 176
 	for _, field := range fields {
177 177
 		parts := strings.SplitN(field, "=", 2)
... ...
@@ -195,7 +196,7 @@ func (m *MountOpt) Set(value string) error {
195 195
 		value := parts[1]
196 196
 		switch key {
197 197
 		case "type":
198
-			mount.Type = swarm.MountType(strings.ToLower(value))
198
+			mount.Type = mounttypes.Type(strings.ToLower(value))
199 199
 		case "source", "src":
200 200
 			mount.Source = value
201 201
 		case "target", "dst", "destination":
... ...
@@ -206,7 +207,7 @@ func (m *MountOpt) Set(value string) error {
206 206
 				return fmt.Errorf("invalid value for %s: %s", key, value)
207 207
 			}
208 208
 		case "bind-propagation":
209
-			bindOptions().Propagation = swarm.MountPropagation(strings.ToLower(value))
209
+			bindOptions().Propagation = mounttypes.Propagation(strings.ToLower(value))
210 210
 		case "volume-nocopy":
211 211
 			volumeOptions().NoCopy, err = strconv.ParseBool(value)
212 212
 			if err != nil {
... ...
@@ -238,11 +239,11 @@ func (m *MountOpt) Set(value string) error {
238 238
 		return fmt.Errorf("source is required when specifying volume-* options")
239 239
 	}
240 240
 
241
-	if mount.Type == swarm.MountTypeBind && mount.VolumeOptions != nil {
242
-		return fmt.Errorf("cannot mix 'volume-*' options with mount type '%s'", swarm.MountTypeBind)
241
+	if mount.Type == mounttypes.TypeBind && mount.VolumeOptions != nil {
242
+		return fmt.Errorf("cannot mix 'volume-*' options with mount type '%s'", mounttypes.TypeBind)
243 243
 	}
244
-	if mount.Type == swarm.MountTypeVolume && mount.BindOptions != nil {
245
-		return fmt.Errorf("cannot mix 'bind-*' options with mount type '%s'", swarm.MountTypeVolume)
244
+	if mount.Type == mounttypes.TypeVolume && mount.BindOptions != nil {
245
+		return fmt.Errorf("cannot mix 'bind-*' options with mount type '%s'", mounttypes.TypeVolume)
246 246
 	}
247 247
 
248 248
 	m.values = append(m.values, mount)
... ...
@@ -265,7 +266,7 @@ func (m *MountOpt) String() string {
265 265
 }
266 266
 
267 267
 // Value returns the mounts
268
-func (m *MountOpt) Value() []swarm.Mount {
268
+func (m *MountOpt) Value() []mounttypes.Mount {
269 269
 	return m.values
270 270
 }
271 271
 
... ...
@@ -5,7 +5,7 @@ import (
5 5
 	"time"
6 6
 
7 7
 	"github.com/docker/docker/pkg/testutil/assert"
8
-	"github.com/docker/engine-api/types/swarm"
8
+	mounttypes "github.com/docker/engine-api/types/mount"
9 9
 )
10 10
 
11 11
 func TestMemBytesString(t *testing.T) {
... ...
@@ -59,14 +59,14 @@ func TestUint64OptSetAndValue(t *testing.T) {
59 59
 
60 60
 func TestMountOptString(t *testing.T) {
61 61
 	mount := MountOpt{
62
-		values: []swarm.Mount{
62
+		values: []mounttypes.Mount{
63 63
 			{
64
-				Type:   swarm.MountTypeBind,
64
+				Type:   mounttypes.TypeBind,
65 65
 				Source: "/home/path",
66 66
 				Target: "/target",
67 67
 			},
68 68
 			{
69
-				Type:   swarm.MountTypeVolume,
69
+				Type:   mounttypes.TypeVolume,
70 70
 				Source: "foo",
71 71
 				Target: "/target/foo",
72 72
 			},
... ...
@@ -90,8 +90,8 @@ func TestMountOptSetNoError(t *testing.T) {
90 90
 
91 91
 		mounts := mount.Value()
92 92
 		assert.Equal(t, len(mounts), 1)
93
-		assert.Equal(t, mounts[0], swarm.Mount{
94
-			Type:   swarm.MountTypeBind,
93
+		assert.Equal(t, mounts[0], mounttypes.Mount{
94
+			Type:   mounttypes.TypeBind,
95 95
 			Source: "/source",
96 96
 			Target: "/target",
97 97
 		})
... ...
@@ -103,7 +103,7 @@ func TestMountOptSetNoError(t *testing.T) {
103 103
 func TestMountOptDefaultType(t *testing.T) {
104 104
 	var mount MountOpt
105 105
 	assert.NilError(t, mount.Set("target=/target,source=/foo"))
106
-	assert.Equal(t, mount.values[0].Type, swarm.MountTypeVolume)
106
+	assert.Equal(t, mount.values[0].Type, mounttypes.TypeVolume)
107 107
 }
108 108
 
109 109
 func TestMountOptSetErrorNoTarget(t *testing.T) {
... ...
@@ -12,6 +12,7 @@ import (
12 12
 	"github.com/docker/docker/opts"
13 13
 	runconfigopts "github.com/docker/docker/runconfig/opts"
14 14
 	"github.com/docker/engine-api/types"
15
+	mounttypes "github.com/docker/engine-api/types/mount"
15 16
 	"github.com/docker/engine-api/types/swarm"
16 17
 	"github.com/docker/go-connections/nat"
17 18
 	shlex "github.com/flynn-archive/go-shlex"
... ...
@@ -353,14 +354,14 @@ func removeItems(
353 353
 	return newSeq
354 354
 }
355 355
 
356
-func updateMounts(flags *pflag.FlagSet, mounts *[]swarm.Mount) {
356
+func updateMounts(flags *pflag.FlagSet, mounts *[]mounttypes.Mount) {
357 357
 	if flags.Changed(flagMountAdd) {
358 358
 		values := flags.Lookup(flagMountAdd).Value.(*MountOpt).Value()
359 359
 		*mounts = append(*mounts, values...)
360 360
 	}
361 361
 	toRemove := buildToRemoveSet(flags, flagMountRemove)
362 362
 
363
-	newMounts := []swarm.Mount{}
363
+	newMounts := []mounttypes.Mount{}
364 364
 	for _, mount := range *mounts {
365 365
 		if _, exists := toRemove[mount.Target]; !exists {
366 366
 			newMounts = append(newMounts, mount)
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"testing"
6 6
 
7 7
 	"github.com/docker/docker/pkg/testutil/assert"
8
+	mounttypes "github.com/docker/engine-api/types/mount"
8 9
 	"github.com/docker/engine-api/types/swarm"
9 10
 )
10 11
 
... ...
@@ -104,9 +105,9 @@ func TestUpdateMounts(t *testing.T) {
104 104
 	flags.Set("mount-add", "type=volume,target=/toadd")
105 105
 	flags.Set("mount-rm", "/toremove")
106 106
 
107
-	mounts := []swarm.Mount{
108
-		{Target: "/toremove", Type: swarm.MountTypeBind},
109
-		{Target: "/tokeep", Type: swarm.MountTypeBind},
107
+	mounts := []mounttypes.Mount{
108
+		{Target: "/toremove", Type: mounttypes.TypeBind},
109
+		{Target: "/tokeep", Type: mounttypes.TypeBind},
110 110
 	}
111 111
 
112 112
 	updateMounts(flags, &mounts)
... ...
@@ -54,7 +54,7 @@ func runInspect(dockerCli *client.DockerCli, opts inspectOptions) error {
54 54
 		}
55 55
 	case "image":
56 56
 		getRefFunc = func(ref string) (interface{}, []byte, error) {
57
-			return client.ImageInspectWithRaw(ctx, ref, opts.size)
57
+			return client.ImageInspectWithRaw(ctx, ref)
58 58
 		}
59 59
 	case "task":
60 60
 		if opts.size {
... ...
@@ -81,7 +81,7 @@ func inspectAll(ctx context.Context, dockerCli *client.DockerCli, getSize bool)
81 81
 			return c, rawContainer, err
82 82
 		}
83 83
 		// Search for image with that id if a container doesn't exist.
84
-		i, rawImage, err := client.ImageInspectWithRaw(ctx, ref, getSize)
84
+		i, rawImage, err := client.ImageInspectWithRaw(ctx, ref)
85 85
 		if err == nil || !apiclient.IsErrNotFound(err) {
86 86
 			return i, rawImage, err
87 87
 		}
... ...
@@ -30,7 +30,7 @@ func runRemove(dockerCli *client.DockerCli, volumes []string) error {
30 30
 	status := 0
31 31
 
32 32
 	for _, name := range volumes {
33
-		if err := client.VolumeRemove(ctx, name); err != nil {
33
+		if err := client.VolumeRemove(ctx, name, false); err != nil {
34 34
 			fmt.Fprintf(dockerCli.Err(), "%s\n", err)
35 35
 			status = 1
36 36
 			continue
... ...
@@ -15,6 +15,7 @@ import (
15 15
 	"github.com/docker/engine-api/types/events"
16 16
 	"github.com/docker/engine-api/types/filters"
17 17
 	timetypes "github.com/docker/engine-api/types/time"
18
+	"github.com/docker/engine-api/types/versions"
18 19
 	"golang.org/x/net/context"
19 20
 )
20 21
 
... ...
@@ -37,6 +38,14 @@ func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *ht
37 37
 		info.Swarm = s.clusterProvider.Info()
38 38
 	}
39 39
 
40
+	if versions.LessThan("1.25", httputils.VersionFromContext(ctx)) {
41
+		// TODO: handle this conversion in engine-api
42
+		type oldInfo struct {
43
+			*types.Info
44
+			ExecutionDriver string
45
+		}
46
+		return httputils.WriteJSON(w, http.StatusOK, &oldInfo{Info: info, ExecutionDriver: "<not supported>"})
47
+	}
40 48
 	return httputils.WriteJSON(w, http.StatusOK, info)
41 49
 }
42 50
 
... ...
@@ -129,7 +129,7 @@ func (container *Container) NetworkMounts() []Mount {
129 129
 				Source:      container.ResolvConfPath,
130 130
 				Destination: "/etc/resolv.conf",
131 131
 				Writable:    writable,
132
-				Propagation: volume.DefaultPropagationMode,
132
+				Propagation: string(volume.DefaultPropagationMode),
133 133
 			})
134 134
 		}
135 135
 	}
... ...
@@ -148,7 +148,7 @@ func (container *Container) NetworkMounts() []Mount {
148 148
 				Source:      container.HostnamePath,
149 149
 				Destination: "/etc/hostname",
150 150
 				Writable:    writable,
151
-				Propagation: volume.DefaultPropagationMode,
151
+				Propagation: string(volume.DefaultPropagationMode),
152 152
 			})
153 153
 		}
154 154
 	}
... ...
@@ -167,7 +167,7 @@ func (container *Container) NetworkMounts() []Mount {
167 167
 				Source:      container.HostsPath,
168 168
 				Destination: "/etc/hosts",
169 169
 				Writable:    writable,
170
-				Propagation: volume.DefaultPropagationMode,
170
+				Propagation: string(volume.DefaultPropagationMode),
171 171
 			})
172 172
 		}
173 173
 	}
... ...
@@ -249,7 +249,7 @@ func (container *Container) IpcMounts() []Mount {
249 249
 			Source:      container.ShmPath,
250 250
 			Destination: "/dev/shm",
251 251
 			Writable:    true,
252
-			Propagation: volume.DefaultPropagationMode,
252
+			Propagation: string(volume.DefaultPropagationMode),
253 253
 		})
254 254
 	}
255 255
 
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"fmt"
5 5
 	"strings"
6 6
 
7
+	mounttypes "github.com/docker/engine-api/types/mount"
7 8
 	types "github.com/docker/engine-api/types/swarm"
8 9
 	swarmapi "github.com/docker/swarmkit/api"
9 10
 	"github.com/docker/swarmkit/protobuf/ptypes"
... ...
@@ -22,26 +23,26 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) types.ContainerSpec {
22 22
 
23 23
 	// Mounts
24 24
 	for _, m := range c.Mounts {
25
-		mount := types.Mount{
25
+		mount := mounttypes.Mount{
26 26
 			Target:   m.Target,
27 27
 			Source:   m.Source,
28
-			Type:     types.MountType(strings.ToLower(swarmapi.Mount_MountType_name[int32(m.Type)])),
28
+			Type:     mounttypes.Type(strings.ToLower(swarmapi.Mount_MountType_name[int32(m.Type)])),
29 29
 			ReadOnly: m.ReadOnly,
30 30
 		}
31 31
 
32 32
 		if m.BindOptions != nil {
33
-			mount.BindOptions = &types.BindOptions{
34
-				Propagation: types.MountPropagation(strings.ToLower(swarmapi.Mount_BindOptions_MountPropagation_name[int32(m.BindOptions.Propagation)])),
33
+			mount.BindOptions = &mounttypes.BindOptions{
34
+				Propagation: mounttypes.Propagation(strings.ToLower(swarmapi.Mount_BindOptions_MountPropagation_name[int32(m.BindOptions.Propagation)])),
35 35
 			}
36 36
 		}
37 37
 
38 38
 		if m.VolumeOptions != nil {
39
-			mount.VolumeOptions = &types.VolumeOptions{
39
+			mount.VolumeOptions = &mounttypes.VolumeOptions{
40 40
 				NoCopy: m.VolumeOptions.NoCopy,
41 41
 				Labels: m.VolumeOptions.Labels,
42 42
 			}
43 43
 			if m.VolumeOptions.DriverConfig != nil {
44
-				mount.VolumeOptions.DriverConfig = &types.Driver{
44
+				mount.VolumeOptions.DriverConfig = &mounttypes.Driver{
45 45
 					Name:    m.VolumeOptions.DriverConfig.Name,
46 46
 					Options: m.VolumeOptions.DriverConfig.Options,
47 47
 				}
... ...
@@ -481,7 +481,7 @@ func setMounts(daemon *Daemon, s *specs.Spec, c *container.Container, mounts []c
481 481
 
482 482
 		if m.Source == "tmpfs" {
483 483
 			data := c.HostConfig.Tmpfs[m.Destination]
484
-			options := []string{"noexec", "nosuid", "nodev", volume.DefaultPropagationMode}
484
+			options := []string{"noexec", "nosuid", "nodev", string(volume.DefaultPropagationMode)}
485 485
 			if data != "" {
486 486
 				options = append(options, strings.Split(data, ",")...)
487 487
 			}
... ...
@@ -37,7 +37,7 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er
37 37
 				Source:      path,
38 38
 				Destination: m.Destination,
39 39
 				Writable:    m.RW,
40
-				Propagation: m.Propagation,
40
+				Propagation: string(m.Propagation),
41 41
 			}
42 42
 			if m.Volume != nil {
43 43
 				attributes := map[string]string{
... ...
@@ -45,7 +45,7 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er
45 45
 					"container":   c.ID,
46 46
 					"destination": m.Destination,
47 47
 					"read/write":  strconv.FormatBool(m.RW),
48
-					"propagation": m.Propagation,
48
+					"propagation": string(m.Propagation),
49 49
 				}
50 50
 				daemon.LogVolumeEvent(m.Volume.Name(), "mount", attributes)
51 51
 			}
... ...
@@ -61,7 +61,7 @@ clone git golang.org/x/sys eb2c74142fd19a79b3f237334c7384d5167b1b46 https://gith
61 61
 clone git github.com/docker/go-units eb879ae3e2b84e2a142af415b679ddeda47ec71c
62 62
 clone git github.com/docker/go-connections fa2850ff103453a9ad190da0df0af134f0314b3d
63 63
 
64
-clone git github.com/docker/engine-api 603ec41824c63d1e6498a22271987fa1f268c0c0
64
+clone git github.com/docker/engine-api ebc51d1954fc8934307dd15841b8d64f7cd505df
65 65
 clone git github.com/RackSec/srslog 259aed10dfa74ea2961eddd1d9847619f6e98837
66 66
 clone git github.com/imdario/mergo 0.2.1
67 67
 
... ...
@@ -34,7 +34,7 @@ func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config
34 34
 
35 35
 	serverResp, err := cli.post(ctx, "/containers/create", query, body, nil)
36 36
 	if err != nil {
37
-		if serverResp != nil && serverResp.statusCode == 404 && strings.Contains(err.Error(), "No such image") {
37
+		if serverResp.statusCode == 404 && strings.Contains(err.Error(), "No such image") {
38 38
 			return response, imageNotFoundError{config.Image}
39 39
 		}
40 40
 		return response, err
... ...
@@ -74,6 +74,10 @@ func imageBuildOptionsToQuery(options types.ImageBuildOptions) (url.Values, erro
74 74
 		query.Set("pull", "1")
75 75
 	}
76 76
 
77
+	if options.Squash {
78
+		query.Set("squash", "1")
79
+	}
80
+
77 81
 	if !container.Isolation.IsDefault(options.Isolation) {
78 82
 		query.Set("isolation", string(options.Isolation))
79 83
 	}
... ...
@@ -28,7 +28,7 @@ func (cli *Client) ImageCreate(ctx context.Context, parentReference string, opti
28 28
 	return resp.body, nil
29 29
 }
30 30
 
31
-func (cli *Client) tryImageCreate(ctx context.Context, query url.Values, registryAuth string) (*serverResponse, error) {
31
+func (cli *Client) tryImageCreate(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) {
32 32
 	headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
33 33
 	return cli.post(ctx, "/images/create", query, nil, headers)
34 34
 }
... ...
@@ -5,19 +5,14 @@ import (
5 5
 	"encoding/json"
6 6
 	"io/ioutil"
7 7
 	"net/http"
8
-	"net/url"
9 8
 
10 9
 	"github.com/docker/engine-api/types"
11 10
 	"golang.org/x/net/context"
12 11
 )
13 12
 
14 13
 // ImageInspectWithRaw returns the image information and its raw representation.
15
-func (cli *Client) ImageInspectWithRaw(ctx context.Context, imageID string, getSize bool) (types.ImageInspect, []byte, error) {
16
-	query := url.Values{}
17
-	if getSize {
18
-		query.Set("size", "1")
19
-	}
20
-	serverResp, err := cli.get(ctx, "/images/"+imageID+"/json", query, nil)
14
+func (cli *Client) ImageInspectWithRaw(ctx context.Context, imageID string) (types.ImageInspect, []byte, error) {
15
+	serverResp, err := cli.get(ctx, "/images/"+imageID+"/json", nil, nil)
21 16
 	if err != nil {
22 17
 		if serverResp.statusCode == http.StatusNotFound {
23 18
 			return types.ImageInspect{}, nil, imageNotFoundError{imageID}
... ...
@@ -48,7 +48,7 @@ func (cli *Client) ImagePush(ctx context.Context, ref string, options types.Imag
48 48
 	return resp.body, nil
49 49
 }
50 50
 
51
-func (cli *Client) tryImagePush(ctx context.Context, imageID string, query url.Values, registryAuth string) (*serverResponse, error) {
51
+func (cli *Client) tryImagePush(ctx context.Context, imageID string, query url.Values, registryAuth string) (serverResponse, error) {
52 52
 	headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
53 53
 	return cli.post(ctx, "/images/"+imageID+"/push", query, nil, headers)
54 54
 }
... ...
@@ -45,7 +45,7 @@ func (cli *Client) ImageSearch(ctx context.Context, term string, options types.I
45 45
 	return results, err
46 46
 }
47 47
 
48
-func (cli *Client) tryImageSearch(ctx context.Context, query url.Values, registryAuth string) (*serverResponse, error) {
48
+func (cli *Client) tryImageSearch(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) {
49 49
 	headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
50 50
 	return cli.get(ctx, "/images/search", query, headers)
51 51
 }
... ...
@@ -68,7 +68,7 @@ type ImageAPIClient interface {
68 68
 	ImageCreate(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error)
69 69
 	ImageHistory(ctx context.Context, image string) ([]types.ImageHistory, error)
70 70
 	ImageImport(ctx context.Context, source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error)
71
-	ImageInspectWithRaw(ctx context.Context, image string, getSize bool) (types.ImageInspect, []byte, error)
71
+	ImageInspectWithRaw(ctx context.Context, image string) (types.ImageInspect, []byte, error)
72 72
 	ImageList(ctx context.Context, options types.ImageListOptions) ([]types.Image, error)
73 73
 	ImageLoad(ctx context.Context, input io.Reader, quiet bool) (types.ImageLoadResponse, error)
74 74
 	ImagePull(ctx context.Context, ref string, options types.ImagePullOptions) (io.ReadCloser, error)
... ...
@@ -131,5 +131,5 @@ type VolumeAPIClient interface {
131 131
 	VolumeInspect(ctx context.Context, volumeID string) (types.Volume, error)
132 132
 	VolumeInspectWithRaw(ctx context.Context, volumeID string) (types.Volume, []byte, error)
133 133
 	VolumeList(ctx context.Context, filter filters.Args) (types.VolumesListResponse, error)
134
-	VolumeRemove(ctx context.Context, volumeID string) error
134
+	VolumeRemove(ctx context.Context, volumeID string, force bool) error
135 135
 }
... ...
@@ -14,7 +14,7 @@ import (
14 14
 func (cli *Client) RegistryLogin(ctx context.Context, auth types.AuthConfig) (types.AuthResponse, error) {
15 15
 	resp, err := cli.post(ctx, "/auth", url.Values{}, auth, nil)
16 16
 
17
-	if resp != nil && resp.statusCode == http.StatusUnauthorized {
17
+	if resp.statusCode == http.StatusUnauthorized {
18 18
 		return types.AuthResponse{}, unauthorizedError{err}
19 19
 	}
20 20
 	if err != nil {
... ...
@@ -53,7 +53,7 @@ func (cli *Client) PluginInstall(ctx context.Context, name string, options types
53 53
 	return cli.PluginEnable(ctx, name)
54 54
 }
55 55
 
56
-func (cli *Client) tryPluginPull(ctx context.Context, query url.Values, registryAuth string) (*serverResponse, error) {
56
+func (cli *Client) tryPluginPull(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) {
57 57
 	headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
58 58
 	return cli.post(ctx, "/plugins/pull", query, nil, headers)
59 59
 }
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"fmt"
7 7
 	"io"
8 8
 	"io/ioutil"
9
+	"net"
9 10
 	"net/http"
10 11
 	"net/url"
11 12
 	"strings"
... ...
@@ -24,47 +25,47 @@ type serverResponse struct {
24 24
 }
25 25
 
26 26
 // head sends an http request to the docker API using the method HEAD.
27
-func (cli *Client) head(ctx context.Context, path string, query url.Values, headers map[string][]string) (*serverResponse, error) {
27
+func (cli *Client) head(ctx context.Context, path string, query url.Values, headers map[string][]string) (serverResponse, error) {
28 28
 	return cli.sendRequest(ctx, "HEAD", path, query, nil, headers)
29 29
 }
30 30
 
31 31
 // getWithContext sends an http request to the docker API using the method GET with a specific go context.
32
-func (cli *Client) get(ctx context.Context, path string, query url.Values, headers map[string][]string) (*serverResponse, error) {
32
+func (cli *Client) get(ctx context.Context, path string, query url.Values, headers map[string][]string) (serverResponse, error) {
33 33
 	return cli.sendRequest(ctx, "GET", path, query, nil, headers)
34 34
 }
35 35
 
36 36
 // postWithContext sends an http request to the docker API using the method POST with a specific go context.
37
-func (cli *Client) post(ctx context.Context, path string, query url.Values, obj interface{}, headers map[string][]string) (*serverResponse, error) {
37
+func (cli *Client) post(ctx context.Context, path string, query url.Values, obj interface{}, headers map[string][]string) (serverResponse, error) {
38 38
 	return cli.sendRequest(ctx, "POST", path, query, obj, headers)
39 39
 }
40 40
 
41
-func (cli *Client) postRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers map[string][]string) (*serverResponse, error) {
41
+func (cli *Client) postRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers map[string][]string) (serverResponse, error) {
42 42
 	return cli.sendClientRequest(ctx, "POST", path, query, body, headers)
43 43
 }
44 44
 
45 45
 // put sends an http request to the docker API using the method PUT.
46
-func (cli *Client) put(ctx context.Context, path string, query url.Values, obj interface{}, headers map[string][]string) (*serverResponse, error) {
46
+func (cli *Client) put(ctx context.Context, path string, query url.Values, obj interface{}, headers map[string][]string) (serverResponse, error) {
47 47
 	return cli.sendRequest(ctx, "PUT", path, query, obj, headers)
48 48
 }
49 49
 
50 50
 // put sends an http request to the docker API using the method PUT.
51
-func (cli *Client) putRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers map[string][]string) (*serverResponse, error) {
51
+func (cli *Client) putRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers map[string][]string) (serverResponse, error) {
52 52
 	return cli.sendClientRequest(ctx, "PUT", path, query, body, headers)
53 53
 }
54 54
 
55 55
 // delete sends an http request to the docker API using the method DELETE.
56
-func (cli *Client) delete(ctx context.Context, path string, query url.Values, headers map[string][]string) (*serverResponse, error) {
56
+func (cli *Client) delete(ctx context.Context, path string, query url.Values, headers map[string][]string) (serverResponse, error) {
57 57
 	return cli.sendRequest(ctx, "DELETE", path, query, nil, headers)
58 58
 }
59 59
 
60
-func (cli *Client) sendRequest(ctx context.Context, method, path string, query url.Values, obj interface{}, headers map[string][]string) (*serverResponse, error) {
60
+func (cli *Client) sendRequest(ctx context.Context, method, path string, query url.Values, obj interface{}, headers map[string][]string) (serverResponse, error) {
61 61
 	var body io.Reader
62 62
 
63 63
 	if obj != nil {
64 64
 		var err error
65 65
 		body, err = encodeData(obj)
66 66
 		if err != nil {
67
-			return nil, err
67
+			return serverResponse{}, err
68 68
 		}
69 69
 		if headers == nil {
70 70
 			headers = make(map[string][]string)
... ...
@@ -75,8 +76,8 @@ func (cli *Client) sendRequest(ctx context.Context, method, path string, query u
75 75
 	return cli.sendClientRequest(ctx, method, path, query, body, headers)
76 76
 }
77 77
 
78
-func (cli *Client) sendClientRequest(ctx context.Context, method, path string, query url.Values, body io.Reader, headers map[string][]string) (*serverResponse, error) {
79
-	serverResp := &serverResponse{
78
+func (cli *Client) sendClientRequest(ctx context.Context, method, path string, query url.Values, body io.Reader, headers map[string][]string) (serverResponse, error) {
79
+	serverResp := serverResponse{
80 80
 		body:       nil,
81 81
 		statusCode: -1,
82 82
 	}
... ...
@@ -105,10 +106,6 @@ func (cli *Client) sendClientRequest(ctx context.Context, method, path string, q
105 105
 
106 106
 	resp, err := cancellable.Do(ctx, cli.transport, req)
107 107
 	if err != nil {
108
-		if isTimeout(err) || strings.Contains(err.Error(), "connection refused") || strings.Contains(err.Error(), "dial unix") {
109
-			return serverResp, ErrConnectionFailed
110
-		}
111
-
112 108
 		if !cli.transport.Secure() && strings.Contains(err.Error(), "malformed HTTP response") {
113 109
 			return serverResp, fmt.Errorf("%v.\n* Are you trying to connect to a TLS-enabled daemon without TLS?", err)
114 110
 		}
... ...
@@ -117,6 +114,23 @@ func (cli *Client) sendClientRequest(ctx context.Context, method, path string, q
117 117
 			return serverResp, fmt.Errorf("The server probably has client authentication (--tlsverify) enabled. Please check your TLS client certification settings: %v", err)
118 118
 		}
119 119
 
120
+		// Don't decorate context sentinel errors; users may be comparing to
121
+		// them directly.
122
+		switch err {
123
+		case context.Canceled, context.DeadlineExceeded:
124
+			return serverResp, err
125
+		}
126
+
127
+		if err, ok := err.(net.Error); ok {
128
+			if err.Timeout() {
129
+				return serverResp, ErrConnectionFailed
130
+			}
131
+			if !err.Temporary() {
132
+				if strings.Contains(err.Error(), "connection refused") || strings.Contains(err.Error(), "dial unix") {
133
+					return serverResp, ErrConnectionFailed
134
+				}
135
+			}
136
+		}
120 137
 		return serverResp, fmt.Errorf("An error occurred trying to connect: %v", err)
121 138
 	}
122 139
 
... ...
@@ -185,23 +199,10 @@ func encodeData(data interface{}) (*bytes.Buffer, error) {
185 185
 	return params, nil
186 186
 }
187 187
 
188
-func ensureReaderClosed(response *serverResponse) {
189
-	if response != nil && response.body != nil {
188
+func ensureReaderClosed(response serverResponse) {
189
+	if body := response.body; body != nil {
190 190
 		// Drain up to 512 bytes and close the body to let the Transport reuse the connection
191
-		io.CopyN(ioutil.Discard, response.body, 512)
191
+		io.CopyN(ioutil.Discard, body, 512)
192 192
 		response.body.Close()
193 193
 	}
194 194
 }
195
-
196
-func isTimeout(err error) bool {
197
-	type timeout interface {
198
-		Timeout() bool
199
-	}
200
-	e := err
201
-	switch urlErr := err.(type) {
202
-	case *url.Error:
203
-		e = urlErr.Err
204
-	}
205
-	t, ok := e.(timeout)
206
-	return ok && t.Timeout()
207
-}
... ...
@@ -8,6 +8,7 @@ package cancellable
8 8
 import (
9 9
 	"io"
10 10
 	"net/http"
11
+	"sync"
11 12
 
12 13
 	"github.com/docker/engine-api/client/transport"
13 14
 
... ...
@@ -82,7 +83,7 @@ func Do(ctx context.Context, client transport.Sender, req *http.Request) (*http.
82 82
 			// The response's Body is closed.
83 83
 		}
84 84
 	}()
85
-	resp.Body = &notifyingReader{resp.Body, c}
85
+	resp.Body = &notifyingReader{ReadCloser: resp.Body, notify: c}
86 86
 
87 87
 	return resp, nil
88 88
 }
... ...
@@ -91,23 +92,24 @@ func Do(ctx context.Context, client transport.Sender, req *http.Request) (*http.
91 91
 // Close is called or a Read fails on the underlying ReadCloser.
92 92
 type notifyingReader struct {
93 93
 	io.ReadCloser
94
-	notify chan<- struct{}
94
+	notify     chan<- struct{}
95
+	notifyOnce sync.Once
95 96
 }
96 97
 
97 98
 func (r *notifyingReader) Read(p []byte) (int, error) {
98 99
 	n, err := r.ReadCloser.Read(p)
99
-	if err != nil && r.notify != nil {
100
-		close(r.notify)
101
-		r.notify = nil
100
+	if err != nil {
101
+		r.notifyOnce.Do(func() {
102
+			close(r.notify)
103
+		})
102 104
 	}
103 105
 	return n, err
104 106
 }
105 107
 
106 108
 func (r *notifyingReader) Close() error {
107 109
 	err := r.ReadCloser.Close()
108
-	if r.notify != nil {
110
+	r.notifyOnce.Do(func() {
109 111
 		close(r.notify)
110
-		r.notify = nil
111
-	}
112
+	})
112 113
 	return err
113 114
 }
... ...
@@ -1,10 +1,18 @@
1 1
 package client
2 2
 
3
-import "golang.org/x/net/context"
3
+import (
4
+	"net/url"
5
+
6
+	"golang.org/x/net/context"
7
+)
4 8
 
5 9
 // VolumeRemove removes a volume from the docker host.
6
-func (cli *Client) VolumeRemove(ctx context.Context, volumeID string) error {
7
-	resp, err := cli.delete(ctx, "/volumes/"+volumeID, nil, nil)
10
+func (cli *Client) VolumeRemove(ctx context.Context, volumeID string, force bool) error {
11
+	query := url.Values{}
12
+	if force {
13
+		query.Set("force", "1")
14
+	}
15
+	resp, err := cli.delete(ctx, "/volumes/"+volumeID, query, nil)
8 16
 	ensureReaderClosed(resp)
9 17
 	return err
10 18
 }
... ...
@@ -147,6 +147,10 @@ type ImageBuildOptions struct {
147 147
 	AuthConfigs    map[string]AuthConfig
148 148
 	Context        io.Reader
149 149
 	Labels         map[string]string
150
+	// squash the resulting image's layers to the parent
151
+	// preserves the original image and creates a new one from the parent with all
152
+	// the changes applied to a single layer
153
+	Squash bool
150 154
 }
151 155
 
152 156
 // ImageBuildResponse holds information
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"strings"
5 5
 
6 6
 	"github.com/docker/engine-api/types/blkiodev"
7
+	"github.com/docker/engine-api/types/mount"
7 8
 	"github.com/docker/engine-api/types/strslice"
8 9
 	"github.com/docker/go-connections/nat"
9 10
 	"github.com/docker/go-units"
... ...
@@ -317,4 +318,7 @@ type HostConfig struct {
317 317
 
318 318
 	// Contains container's resources (cgroups, ulimits)
319 319
 	Resources
320
+
321
+	// Mounts specs used by the container
322
+	Mounts []mount.Mount `json:",omitempty"`
320 323
 }
321 324
new file mode 100644
... ...
@@ -0,0 +1,58 @@
0
+package mount
1
+
2
+// Type represents the type of a mount.
3
+type Type string
4
+
5
+const (
6
+	// TypeBind BIND
7
+	TypeBind Type = "bind"
8
+	// TypeVolume VOLUME
9
+	TypeVolume Type = "volume"
10
+)
11
+
12
+// Mount represents a mount (volume).
13
+type Mount struct {
14
+	Type     Type   `json:",omitempty"`
15
+	Source   string `json:",omitempty"`
16
+	Target   string `json:",omitempty"`
17
+	ReadOnly bool   `json:",omitempty"`
18
+
19
+	BindOptions   *BindOptions   `json:",omitempty"`
20
+	VolumeOptions *VolumeOptions `json:",omitempty"`
21
+}
22
+
23
+// Propagation represents the propagation of a mount.
24
+type Propagation string
25
+
26
+const (
27
+	// PropagationRPrivate RPRIVATE
28
+	PropagationRPrivate Propagation = "rprivate"
29
+	// PropagationPrivate PRIVATE
30
+	PropagationPrivate Propagation = "private"
31
+	// PropagationRShared RSHARED
32
+	PropagationRShared Propagation = "rshared"
33
+	// PropagationShared SHARED
34
+	PropagationShared Propagation = "shared"
35
+	// PropagationRSlave RSLAVE
36
+	PropagationRSlave Propagation = "rslave"
37
+	// PropagationSlave SLAVE
38
+	PropagationSlave Propagation = "slave"
39
+)
40
+
41
+// BindOptions defines options specific to mounts of type "bind".
42
+type BindOptions struct {
43
+	Propagation Propagation `json:",omitempty"`
44
+}
45
+
46
+// VolumeOptions represents the options for a mount of type volume.
47
+type VolumeOptions struct {
48
+	NoCopy       bool              `json:",omitempty"`
49
+	Labels       map[string]string `json:",omitempty"`
50
+	DriverConfig *Driver           `json:",omitempty"`
51
+}
52
+
53
+// Driver represents a volume driver.
54
+type Driver struct {
55
+	Name    string            `json:",omitempty"`
56
+	Options map[string]string `json:",omitempty"`
57
+}
... ...
@@ -16,6 +16,11 @@ type ServiceConfig struct {
16 16
 // unmarshalled to JSON
17 17
 type NetIPNet net.IPNet
18 18
 
19
+// String returns the CIDR notation of ipnet
20
+func (ipnet *NetIPNet) String() string {
21
+	return (*net.IPNet)(ipnet).String()
22
+}
23
+
19 24
 // MarshalJSON returns the JSON representation of the IPNet
20 25
 func (ipnet *NetIPNet) MarshalJSON() ([]byte, error) {
21 26
 	return json.Marshal((*net.IPNet)(ipnet).String())
... ...
@@ -1,6 +1,10 @@
1 1
 package swarm
2 2
 
3
-import "time"
3
+import (
4
+	"time"
5
+
6
+	"github.com/docker/engine-api/types/mount"
7
+)
4 8
 
5 9
 // ContainerSpec represents the spec of a container.
6 10
 type ContainerSpec struct {
... ...
@@ -11,57 +15,8 @@ type ContainerSpec struct {
11 11
 	Env             []string          `json:",omitempty"`
12 12
 	Dir             string            `json:",omitempty"`
13 13
 	User            string            `json:",omitempty"`
14
-	Mounts          []Mount           `json:",omitempty"`
14
+	Groups          []string          `json:",omitempty"`
15
+	TTY             bool              `json:",omitempty"`
16
+	Mounts          []mount.Mount     `json:",omitempty"`
15 17
 	StopGracePeriod *time.Duration    `json:",omitempty"`
16 18
 }
17
-
18
-// MountType represents the type of a mount.
19
-type MountType string
20
-
21
-const (
22
-	// MountTypeBind BIND
23
-	MountTypeBind MountType = "bind"
24
-	// MountTypeVolume VOLUME
25
-	MountTypeVolume MountType = "volume"
26
-)
27
-
28
-// Mount represents a mount (volume).
29
-type Mount struct {
30
-	Type     MountType `json:",omitempty"`
31
-	Source   string    `json:",omitempty"`
32
-	Target   string    `json:",omitempty"`
33
-	ReadOnly bool      `json:",omitempty"`
34
-
35
-	BindOptions   *BindOptions   `json:",omitempty"`
36
-	VolumeOptions *VolumeOptions `json:",omitempty"`
37
-}
38
-
39
-// MountPropagation represents the propagation of a mount.
40
-type MountPropagation string
41
-
42
-const (
43
-	// MountPropagationRPrivate RPRIVATE
44
-	MountPropagationRPrivate MountPropagation = "rprivate"
45
-	// MountPropagationPrivate PRIVATE
46
-	MountPropagationPrivate MountPropagation = "private"
47
-	// MountPropagationRShared RSHARED
48
-	MountPropagationRShared MountPropagation = "rshared"
49
-	// MountPropagationShared SHARED
50
-	MountPropagationShared MountPropagation = "shared"
51
-	// MountPropagationRSlave RSLAVE
52
-	MountPropagationRSlave MountPropagation = "rslave"
53
-	// MountPropagationSlave SLAVE
54
-	MountPropagationSlave MountPropagation = "slave"
55
-)
56
-
57
-// BindOptions defines options specific to mounts of type "bind".
58
-type BindOptions struct {
59
-	Propagation MountPropagation `json:",omitempty"`
60
-}
61
-
62
-// VolumeOptions represents the options for a mount of type volume.
63
-type VolumeOptions struct {
64
-	NoCopy       bool              `json:",omitempty"`
65
-	Labels       map[string]string `json:",omitempty"`
66
-	DriverConfig *Driver           `json:",omitempty"`
67
-}
... ...
@@ -92,7 +92,7 @@ type IPAMConfig struct {
92 92
 	Gateway string `json:",omitempty"`
93 93
 }
94 94
 
95
-// Driver represents a driver (network/volume).
95
+// Driver represents a network driver.
96 96
 type Driver struct {
97 97
 	Name    string            `json:",omitempty"`
98 98
 	Options map[string]string `json:",omitempty"`
... ...
@@ -2,7 +2,7 @@ package swarm
2 2
 
3 3
 import "time"
4 4
 
5
-// ClusterInfo represents info about a the cluster for outputing in "info"
5
+// ClusterInfo represents info about the cluster for outputing in "info"
6 6
 // it contains the same information as "Swarm", but without the JoinTokens
7 7
 type ClusterInfo struct {
8 8
 	ID string
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"time"
6 6
 
7 7
 	"github.com/docker/engine-api/types/container"
8
+	"github.com/docker/engine-api/types/mount"
8 9
 	"github.com/docker/engine-api/types/network"
9 10
 	"github.com/docker/engine-api/types/registry"
10 11
 	"github.com/docker/engine-api/types/swarm"
... ...
@@ -230,7 +231,6 @@ type Info struct {
230 230
 	OomKillDisable     bool
231 231
 	NGoroutines        int
232 232
 	SystemTime         string
233
-	ExecutionDriver    string
234 233
 	LoggingDriver      string
235 234
 	CgroupDriver       string
236 235
 	NEventsListener    int
... ...
@@ -409,14 +409,16 @@ type DefaultNetworkSettings struct {
409 409
 }
410 410
 
411 411
 // MountPoint represents a mount point configuration inside the container.
412
+// This is used for reporting the mountpoints in use by a container.
412 413
 type MountPoint struct {
413
-	Name        string `json:",omitempty"`
414
+	Type        mount.Type `json:",omitempty"`
415
+	Name        string     `json:",omitempty"`
414 416
 	Source      string
415 417
 	Destination string
416 418
 	Driver      string `json:",omitempty"`
417 419
 	Mode        string
418 420
 	RW          bool
419
-	Propagation string
421
+	Propagation mount.Propagation
420 422
 }
421 423
 
422 424
 // Volume represents the configuration of a volume for the remote API
... ...
@@ -8,6 +8,7 @@ import (
8 8
 
9 9
 	"github.com/docker/docker/pkg/stringid"
10 10
 	"github.com/docker/docker/pkg/system"
11
+	mounttypes "github.com/docker/engine-api/types/mount"
11 12
 	"github.com/opencontainers/runc/libcontainer/label"
12 13
 )
13 14
 
... ...
@@ -92,8 +93,8 @@ type MountPoint struct {
92 92
 	Mode string `json:"Relabel"` // Originally field was `Relabel`"
93 93
 
94 94
 	// Note Propagation is not used on Windows
95
-	Propagation string // Mount propagation string
96
-	Named       bool   // specifies if the mountpoint was specified by name
95
+	Propagation mounttypes.Propagation // Mount propagation string
96
+	Named       bool                   // specifies if the mountpoint was specified by name
97 97
 
98 98
 	// Specifies if data should be copied from the container before the first mount
99 99
 	// Use a pointer here so we can tell if the user set this value explicitly
... ...
@@ -4,28 +4,31 @@ package volume
4 4
 
5 5
 import (
6 6
 	"strings"
7
+
8
+	mounttypes "github.com/docker/engine-api/types/mount"
7 9
 )
8 10
 
9 11
 // DefaultPropagationMode defines what propagation mode should be used by
10 12
 // default if user has not specified one explicitly.
11
-const DefaultPropagationMode string = "rprivate"
13
+const DefaultPropagationMode mounttypes.Propagation = "rprivate"
12 14
 
13 15
 // propagation modes
14
-var propagationModes = map[string]bool{
15
-	"private":  true,
16
-	"rprivate": true,
17
-	"slave":    true,
18
-	"rslave":   true,
19
-	"shared":   true,
20
-	"rshared":  true,
16
+var propagationModes = map[mounttypes.Propagation]bool{
17
+	mounttypes.PropagationPrivate:  true,
18
+	mounttypes.PropagationRPrivate: true,
19
+	mounttypes.PropagationSlave:    true,
20
+	mounttypes.PropagationRSlave:   true,
21
+	mounttypes.PropagationShared:   true,
22
+	mounttypes.PropagationRShared:  true,
21 23
 }
22 24
 
23 25
 // GetPropagation extracts and returns the mount propagation mode. If there
24 26
 // are no specifications, then by default it is "private".
25
-func GetPropagation(mode string) string {
27
+func GetPropagation(mode string) mounttypes.Propagation {
26 28
 	for _, o := range strings.Split(mode, ",") {
27
-		if propagationModes[o] {
28
-			return o
29
+		prop := mounttypes.Propagation(o)
30
+		if propagationModes[prop] {
31
+			return prop
29 32
 		}
30 33
 	}
31 34
 	return DefaultPropagationMode
... ...
@@ -36,7 +39,7 @@ func GetPropagation(mode string) string {
36 36
 // present, false otherwise.
37 37
 func HasPropagation(mode string) bool {
38 38
 	for _, o := range strings.Split(mode, ",") {
39
-		if propagationModes[o] {
39
+		if propagationModes[mounttypes.Propagation(o)] {
40 40
 			return true
41 41
 		}
42 42
 	}
... ...
@@ -2,15 +2,17 @@
2 2
 
3 3
 package volume
4 4
 
5
+import mounttypes "github.com/docker/engine-api/types/mount"
6
+
5 7
 // DefaultPropagationMode is used only in linux. In other cases it returns
6 8
 // empty string.
7
-const DefaultPropagationMode string = ""
9
+const DefaultPropagationMode mounttypes.Propagation = ""
8 10
 
9 11
 // propagation modes not supported on this platform.
10 12
 var propagationModes = map[string]bool{}
11 13
 
12 14
 // GetPropagation is not supported. Return empty string.
13
-func GetPropagation(mode string) string {
15
+func GetPropagation(mode string) mounttypes.Propagation {
14 16
 	return DefaultPropagationMode
15 17
 }
16 18
 
... ...
@@ -6,6 +6,8 @@ import (
6 6
 	"fmt"
7 7
 	"path/filepath"
8 8
 	"strings"
9
+
10
+	mounttypes "github.com/docker/engine-api/types/mount"
9 11
 )
10 12
 
11 13
 // read-write modes
... ...
@@ -152,7 +154,7 @@ func ValidMountMode(mode string) bool {
152 152
 			rwModeCount++
153 153
 		case labelModes[o]:
154 154
 			labelModeCount++
155
-		case propagationModes[o]:
155
+		case propagationModes[mounttypes.Propagation(o)]:
156 156
 			propagationModeCount++
157 157
 		case copyModeExists(o):
158 158
 			copyModeCount++