Browse code

Use newer default values for mounts CLI

In the API:
`Writable` changed to `ReadOnly`
`Populate` changed to `NoCopy`

Corresponding CLI options updated to:
`volume-writable` changed to `volume-readonly`
`volume-populate` changed to `volume-nocopy`

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
(cherry picked from commit 56f3422468a0b43da7bae7a01762ce4f0a92d9ff)
Signed-off-by: Tibor Vass <tibor@docker.com>

Brian Goff authored on 2016/07/06 03:43:28
Showing 7 changed files
... ...
@@ -176,10 +176,16 @@ func (m *MountOpt) Set(value string) error {
176 176
 		}
177 177
 	}
178 178
 
179
+	// Set writable as the default
179 180
 	for _, field := range fields {
180 181
 		parts := strings.SplitN(field, "=", 2)
181
-		if len(parts) == 1 && strings.ToLower(parts[0]) == "writable" {
182
-			mount.Writable = true
182
+		if len(parts) == 1 && strings.ToLower(parts[0]) == "readonly" {
183
+			mount.ReadOnly = true
184
+			continue
185
+		}
186
+
187
+		if len(parts) == 1 && strings.ToLower(parts[0]) == "volume-nocopy" {
188
+			volumeOptions().NoCopy = true
183 189
 			continue
184 190
 		}
185 191
 
... ...
@@ -195,15 +201,16 @@ func (m *MountOpt) Set(value string) error {
195 195
 			mount.Source = value
196 196
 		case "target":
197 197
 			mount.Target = value
198
-		case "writable":
199
-			mount.Writable, err = strconv.ParseBool(value)
198
+		case "readonly":
199
+			ro, err := strconv.ParseBool(value)
200 200
 			if err != nil {
201
-				return fmt.Errorf("invalid value for writable: %s", value)
201
+				return fmt.Errorf("invalid value for readonly: %s", value)
202 202
 			}
203
+			mount.ReadOnly = ro
203 204
 		case "bind-propagation":
204 205
 			bindOptions().Propagation = swarm.MountPropagation(strings.ToUpper(value))
205
-		case "volume-populate":
206
-			volumeOptions().Populate, err = strconv.ParseBool(value)
206
+		case "volume-nocopy":
207
+			volumeOptions().NoCopy, err = strconv.ParseBool(value)
207 208
 			if err != nil {
208 209
 				return fmt.Errorf("invalid value for populate: %s", value)
209 210
 			}
... ...
@@ -229,6 +236,17 @@ func (m *MountOpt) Set(value string) error {
229 229
 		return fmt.Errorf("target is required")
230 230
 	}
231 231
 
232
+	if mount.VolumeOptions != nil && mount.Source == "" {
233
+		return fmt.Errorf("source is required when specifying volume-* options")
234
+	}
235
+
236
+	if mount.Type == swarm.MountType("BIND") && mount.VolumeOptions != nil {
237
+		return fmt.Errorf("cannot mix 'volume-*' options with mount type '%s'", swarm.MountTypeBind)
238
+	}
239
+	if mount.Type == swarm.MountType("VOLUME") && mount.BindOptions != nil {
240
+		return fmt.Errorf("cannot mix 'bind-*' options with mount type '%s'", swarm.MountTypeVolume)
241
+	}
242
+
232 243
 	m.values = append(m.values, mount)
233 244
 	return nil
234 245
 }
... ...
@@ -111,5 +111,53 @@ func TestMountOptSetErrorInvalidField(t *testing.T) {
111 111
 
112 112
 func TestMountOptSetErrorInvalidWritable(t *testing.T) {
113 113
 	var mount MountOpt
114
-	assert.Error(t, mount.Set("type=VOLUME,writable=yes"), "invalid value for writable: yes")
114
+	assert.Error(t, mount.Set("type=VOLUME,readonly=no"), "invalid value for readonly: no")
115
+}
116
+
117
+func TestMountOptDefaultEnableWritable(t *testing.T) {
118
+	var m MountOpt
119
+	assert.NilError(t, m.Set("type=bind,target=/foo,source=/foo"))
120
+	assert.Equal(t, m.values[0].ReadOnly, false)
121
+
122
+	m = MountOpt{}
123
+	assert.NilError(t, m.Set("type=bind,target=/foo,source=/foo,readonly"))
124
+	assert.Equal(t, m.values[0].ReadOnly, true)
125
+
126
+	m = MountOpt{}
127
+	assert.NilError(t, m.Set("type=bind,target=/foo,source=/foo,readonly=1"))
128
+	assert.Equal(t, m.values[0].ReadOnly, true)
129
+
130
+	m = MountOpt{}
131
+	assert.NilError(t, m.Set("type=bind,target=/foo,source=/foo,readonly=0"))
132
+	assert.Equal(t, m.values[0].ReadOnly, false)
133
+}
134
+
135
+func TestMountOptVolumeNoCopy(t *testing.T) {
136
+	var m MountOpt
137
+	assert.Error(t, m.Set("type=volume,target=/foo,volume-nocopy"), "source is required")
138
+
139
+	m = MountOpt{}
140
+	assert.NilError(t, m.Set("type=volume,target=/foo,source=foo"))
141
+	assert.Equal(t, m.values[0].VolumeOptions == nil, true)
142
+
143
+	m = MountOpt{}
144
+	assert.NilError(t, m.Set("type=volume,target=/foo,source=foo,volume-nocopy=true"))
145
+	assert.Equal(t, m.values[0].VolumeOptions != nil, true)
146
+	assert.Equal(t, m.values[0].VolumeOptions.NoCopy, true)
147
+
148
+	m = MountOpt{}
149
+	assert.NilError(t, m.Set("type=volume,target=/foo,source=foo,volume-nocopy"))
150
+	assert.Equal(t, m.values[0].VolumeOptions != nil, true)
151
+	assert.Equal(t, m.values[0].VolumeOptions.NoCopy, true)
152
+
153
+	m = MountOpt{}
154
+	assert.NilError(t, m.Set("type=volume,target=/foo,source=foo,volume-nocopy=1"))
155
+	assert.Equal(t, m.values[0].VolumeOptions != nil, true)
156
+	assert.Equal(t, m.values[0].VolumeOptions.NoCopy, true)
157
+}
158
+
159
+func TestMountOptTypeConflict(t *testing.T) {
160
+	var m MountOpt
161
+	assert.Error(t, m.Set("type=bind,target=/foo,source=/foo,volume-nocopy=true"), "cannot mix")
162
+	assert.Error(t, m.Set("type=volume,target=/foo,source=/foo,bind-propagation=rprivate"), "cannot mix")
115 163
 }
... ...
@@ -26,7 +26,7 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) types.ContainerSpec {
26 26
 			Target:   m.Target,
27 27
 			Source:   m.Source,
28 28
 			Type:     types.MountType(strings.ToLower(swarmapi.Mount_MountType_name[int32(m.Type)])),
29
-			Writable: m.Writable,
29
+			ReadOnly: m.ReadOnly,
30 30
 		}
31 31
 
32 32
 		if m.BindOptions != nil {
... ...
@@ -37,8 +37,8 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) types.ContainerSpec {
37 37
 
38 38
 		if m.VolumeOptions != nil {
39 39
 			mount.VolumeOptions = &types.VolumeOptions{
40
-				Populate: m.VolumeOptions.Populate,
41
-				Labels:   m.VolumeOptions.Labels,
40
+				NoCopy: m.VolumeOptions.NoCopy,
41
+				Labels: m.VolumeOptions.Labels,
42 42
 			}
43 43
 			if m.VolumeOptions.DriverConfig != nil {
44 44
 				mount.VolumeOptions.DriverConfig = &types.Driver{
... ...
@@ -77,7 +77,7 @@ func containerToGRPC(c types.ContainerSpec) (*swarmapi.ContainerSpec, error) {
77 77
 		mount := swarmapi.Mount{
78 78
 			Target:   m.Target,
79 79
 			Source:   m.Source,
80
-			Writable: m.Writable,
80
+			ReadOnly: m.ReadOnly,
81 81
 		}
82 82
 
83 83
 		if mountType, ok := swarmapi.Mount_MountType_value[strings.ToUpper(string(m.Type))]; ok {
... ...
@@ -98,8 +98,8 @@ func containerToGRPC(c types.ContainerSpec) (*swarmapi.ContainerSpec, error) {
98 98
 
99 99
 		if m.VolumeOptions != nil {
100 100
 			mount.VolumeOptions = &swarmapi.Mount_VolumeOptions{
101
-				Populate: m.VolumeOptions.Populate,
102
-				Labels:   m.VolumeOptions.Labels,
101
+				NoCopy: m.VolumeOptions.NoCopy,
102
+				Labels: m.VolumeOptions.Labels,
103 103
 			}
104 104
 			if m.VolumeOptions.DriverConfig != nil {
105 105
 				mount.VolumeOptions.DriverConfig = &swarmapi.Driver{
... ...
@@ -163,9 +163,13 @@ func (c *containerConfig) bindMounts() []string {
163 163
 	var r []string
164 164
 
165 165
 	for _, val := range c.spec().Mounts {
166
-		mask := getMountMask(&val)
167 166
 		if val.Type == api.MountTypeBind || (val.Type == api.MountTypeVolume && val.Source != "") {
168
-			r = append(r, fmt.Sprintf("%s:%s:%s", val.Source, val.Target, mask))
167
+			mask := getMountMask(&val)
168
+			spec := fmt.Sprintf("%s:%s", val.Source, val.Target)
169
+			if mask != "" {
170
+				spec = fmt.Sprintf("%s:%s", spec, mask)
171
+			}
172
+			r = append(r, spec)
169 173
 		}
170 174
 	}
171 175
 
... ...
@@ -173,9 +177,9 @@ func (c *containerConfig) bindMounts() []string {
173 173
 }
174 174
 
175 175
 func getMountMask(m *api.Mount) string {
176
-	maskOpts := []string{"ro"}
177
-	if m.Writable {
178
-		maskOpts[0] = "rw"
176
+	var maskOpts []string
177
+	if m.ReadOnly {
178
+		maskOpts = append(maskOpts, "ro")
179 179
 	}
180 180
 
181 181
 	if m.BindOptions != nil {
... ...
@@ -196,7 +200,7 @@ func getMountMask(m *api.Mount) string {
196 196
 	}
197 197
 
198 198
 	if m.VolumeOptions != nil {
199
-		if !m.VolumeOptions.Populate {
199
+		if m.VolumeOptions.NoCopy {
200 200
 			maskOpts = append(maskOpts, "nocopy")
201 201
 		}
202 202
 	}
... ...
@@ -3986,11 +3986,11 @@ JSON Parameters:
3986 3986
             - **Target** – Container path.
3987 3987
             - **Source** – Mount source (e.g. a volume name, a host path).
3988 3988
             - **Type** – The mount type (`bind`, or `volume`).
3989
-            - **Writable** – A boolean indicating whether the mount should be writable.
3989
+            - **ReadOnly** – A boolean indicating whether the mount should be read-only.
3990 3990
             - **BindOptions** - Optional configuration for the `bind` type.
3991 3991
               - **Propagation** – A propagation mode with the value `[r]private`, `[r]shared`, or `[r]slave`.
3992 3992
             - **VolumeOptions** – Optional configuration for the `volume` type.
3993
-                - **Populate** – A boolean indicating if volume should be
3993
+                - **NoCopy** – A boolean indicating if volume should be
3994 3994
                   populated with the data from the target. (Default false)
3995 3995
                 - **Labels** – User-defined name and labels for the volume.
3996 3996
                 - **DriverConfig** – Map of driver-specific options.
... ...
@@ -4204,11 +4204,12 @@ Update the service `id`.
4204 4204
             - **Target** – Container path.
4205 4205
             - **Source** – Mount source (e.g. a volume name, a host path).
4206 4206
             - **Type** – The mount type (`bind`, or `volume`).
4207
-            - **Writable** – A boolean indicating whether the mount should be writable.
4207
+            - **ReadOnly** – A boolean indicating whether the mount should be read-only.
4208 4208
             - **BindOptions** - Optional configuration for the `bind` type
4209 4209
               - **Propagation** – A propagation mode with the value `[r]private`, `[r]shared`, or `[r]slave`.
4210 4210
             - **VolumeOptions** – Optional configuration for the `volume` type.
4211
-                - **Populate** – A boolean indicating if volume should be populated with the data from the target. (Default false)
4211
+                - **NoCopy** – A boolean indicating if volume should be
4212
+                  populated with the data from the target. (Default false)
4212 4213
                 - **Labels** – User-defined name and labels for the volume.
4213 4214
                 - **DriverConfig** – Map of driver-specific options.
4214 4215
                   - **Name** - Name of the driver to use to create the volume
... ...
@@ -41,5 +41,5 @@ func (s *DockerSwarmSuite) TestServiceCreateMountVolume(c *check.C) {
41 41
 
42 42
 	c.Assert(mounts[0].Name, checker.Equals, "foo")
43 43
 	c.Assert(mounts[0].Destination, checker.Equals, "/foo")
44
-	c.Assert(mounts[0].RW, checker.Equals, false)
44
+	c.Assert(mounts[0].RW, checker.Equals, true)
45 45
 }
... ...
@@ -2,6 +2,9 @@
2 2
 package assert
3 3
 
4 4
 import (
5
+	"fmt"
6
+	"path/filepath"
7
+	"runtime"
5 8
 	"strings"
6 9
 )
7 10
 
... ...
@@ -15,7 +18,7 @@ type TestingT interface {
15 15
 // they are not equal.
16 16
 func Equal(t TestingT, actual, expected interface{}) {
17 17
 	if expected != actual {
18
-		t.Fatalf("Expected '%v' (%T) got '%v' (%T)", expected, expected, actual, actual)
18
+		fatal(t, fmt.Sprintf("Expected '%v' (%T) got '%v' (%T)", expected, expected, actual, actual))
19 19
 	}
20 20
 }
21 21
 
... ...
@@ -37,7 +40,7 @@ func EqualStringSlice(t TestingT, actual, expected []string) {
37 37
 // NilError asserts that the error is nil, otherwise it fails the test.
38 38
 func NilError(t TestingT, err error) {
39 39
 	if err != nil {
40
-		t.Fatalf("Expected no error, got: %s", err.Error())
40
+		fatal(t, fmt.Sprintf("Expected no error, got: %s", err.Error()))
41 41
 	}
42 42
 }
43 43
 
... ...
@@ -45,11 +48,11 @@ func NilError(t TestingT, err error) {
45 45
 // otherwise it fails the test.
46 46
 func Error(t TestingT, err error, contains string) {
47 47
 	if err == nil {
48
-		t.Fatalf("Expected an error, but error was nil")
48
+		fatal(t, "Expected an error, but error was nil")
49 49
 	}
50 50
 
51 51
 	if !strings.Contains(err.Error(), contains) {
52
-		t.Fatalf("Expected error to contain '%s', got '%s'", contains, err.Error())
52
+		fatal(t, fmt.Sprintf("Expected error to contain '%s', got '%s'", contains, err.Error()))
53 53
 	}
54 54
 }
55 55
 
... ...
@@ -57,6 +60,11 @@ func Error(t TestingT, err error, contains string) {
57 57
 // test.
58 58
 func Contains(t TestingT, actual, contains string) {
59 59
 	if !strings.Contains(actual, contains) {
60
-		t.Fatalf("Expected '%s' to contain '%s'", actual, contains)
60
+		fatal(t, fmt.Sprintf("Expected '%s' to contain '%s'", actual, contains))
61 61
 	}
62 62
 }
63
+
64
+func fatal(t TestingT, msg string) {
65
+	_, file, line, _ := runtime.Caller(2)
66
+	t.Fatalf("%s:%d: %s", filepath.Base(file), line, msg)
67
+}