瀏覽代碼

api/client/service: mount option defaults and aliases

Simplifies the mount option usage by providing common aliases for
`source` and `target`. The default mount type is now volume.

Signed-off-by: Stephen J Day <stephen.day@docker.com>
Stephen J Day 9 年之前
父節點
當前提交
634f54a047
共有 2 個文件被更改,包括 44 次插入26 次删除
  1. 17 12
      api/client/service/opts.go
  2. 27 14
      api/client/service/opts_test.go

+ 17 - 12
api/client/service/opts.go

@@ -176,32 +176,37 @@ func (m *MountOpt) Set(value string) error {
 		}
 		}
 	}
 	}
 
 
+	mount.Type = swarm.MountTypeVolume // default to volume mounts
 	// Set writable as the default
 	// Set writable as the default
 	for _, field := range fields {
 	for _, field := range fields {
 		parts := strings.SplitN(field, "=", 2)
 		parts := strings.SplitN(field, "=", 2)
-		if len(parts) == 1 && strings.ToLower(parts[0]) == "readonly" {
-			mount.ReadOnly = true
-			continue
-		}
+		key := strings.ToLower(parts[0])
 
 
-		if len(parts) == 1 && strings.ToLower(parts[0]) == "volume-nocopy" {
-			volumeOptions().NoCopy = true
-			continue
+		if len(parts) == 1 {
+			if key == "readonly" || key == "ro" {
+				mount.ReadOnly = true
+				continue
+			}
+
+			if key == "volume-nocopy" {
+				volumeOptions().NoCopy = true
+				continue
+			}
 		}
 		}
 
 
 		if len(parts) != 2 {
 		if len(parts) != 2 {
 			return fmt.Errorf("invalid field '%s' must be a key=value pair", field)
 			return fmt.Errorf("invalid field '%s' must be a key=value pair", field)
 		}
 		}
 
 
-		key, value := parts[0], parts[1]
-		switch strings.ToLower(key) {
+		value := parts[1]
+		switch key {
 		case "type":
 		case "type":
 			mount.Type = swarm.MountType(strings.ToLower(value))
 			mount.Type = swarm.MountType(strings.ToLower(value))
-		case "source":
+		case "source", "name", "src":
 			mount.Source = value
 			mount.Source = value
-		case "target":
+		case "target", "dst", "dest", "destination", "path":
 			mount.Target = value
 			mount.Target = value
-		case "readonly":
+		case "readonly", "ro":
 			ro, err := strconv.ParseBool(value)
 			ro, err := strconv.ParseBool(value)
 			if err != nil {
 			if err != nil {
 				return fmt.Errorf("invalid value for readonly: %s", value)
 				return fmt.Errorf("invalid value for readonly: %s", value)

+ 27 - 14
api/client/service/opts_test.go

@@ -77,21 +77,33 @@ func TestMountOptString(t *testing.T) {
 }
 }
 
 
 func TestMountOptSetNoError(t *testing.T) {
 func TestMountOptSetNoError(t *testing.T) {
-	var mount MountOpt
-	assert.NilError(t, mount.Set("type=bind,target=/target,source=/foo"))
-
-	mounts := mount.Value()
-	assert.Equal(t, len(mounts), 1)
-	assert.Equal(t, mounts[0], swarm.Mount{
-		Type:   swarm.MountTypeBind,
-		Source: "/foo",
-		Target: "/target",
-	})
+	for _, testcase := range []string{
+		// tests several aliases that should have same result.
+		"type=bind,target=/target,source=/source",
+		"type=bind,src=/source,dst=/target",
+		"type=bind,name=/source,dst=/target",
+		"type=bind,name=/source,path=/target",
+	} {
+		var mount MountOpt
+
+		assert.NilError(t, mount.Set(testcase))
+
+		mounts := mount.Value()
+		assert.Equal(t, len(mounts), 1)
+		assert.Equal(t, mounts[0], swarm.Mount{
+			Type:   swarm.MountTypeBind,
+			Source: "/source",
+			Target: "/target",
+		})
+	}
 }
 }
 
 
-func TestMountOptSetErrorNoType(t *testing.T) {
+// TestMountOptDefaultType ensures that a mount without the type defaults to a
+// volume mount.
+func TestMountOptDefaultType(t *testing.T) {
 	var mount MountOpt
 	var mount MountOpt
-	assert.Error(t, mount.Set("target=/target,source=/foo"), "type is required")
+	assert.NilError(t, mount.Set("target=/target,source=/foo"))
+	assert.Equal(t, mount.values[0].Type, swarm.MountTypeVolume)
 }
 }
 
 
 func TestMountOptSetErrorNoTarget(t *testing.T) {
 func TestMountOptSetErrorNoTarget(t *testing.T) {
@@ -109,12 +121,13 @@ func TestMountOptSetErrorInvalidField(t *testing.T) {
 	assert.Error(t, mount.Set("type=volume,bogus"), "invalid field 'bogus'")
 	assert.Error(t, mount.Set("type=volume,bogus"), "invalid field 'bogus'")
 }
 }
 
 
-func TestMountOptSetErrorInvalidWritable(t *testing.T) {
+func TestMountOptSetErrorInvalidReadOnly(t *testing.T) {
 	var mount MountOpt
 	var mount MountOpt
 	assert.Error(t, mount.Set("type=volume,readonly=no"), "invalid value for readonly: no")
 	assert.Error(t, mount.Set("type=volume,readonly=no"), "invalid value for readonly: no")
+	assert.Error(t, mount.Set("type=volume,readonly=invalid"), "invalid value for readonly: invalid")
 }
 }
 
 
-func TestMountOptDefaultEnableWritable(t *testing.T) {
+func TestMountOptDefaultEnableReadOnly(t *testing.T) {
 	var m MountOpt
 	var m MountOpt
 	assert.NilError(t, m.Set("type=bind,target=/foo,source=/foo"))
 	assert.NilError(t, m.Set("type=bind,target=/foo,source=/foo"))
 	assert.Equal(t, m.values[0].ReadOnly, false)
 	assert.Equal(t, m.values[0].ReadOnly, false)