Browse code

Merge pull request #15798 from calavera/volume_driver_host_config

Move VolumeDriver to HostConfig to make containers portable.

Brian Goff authored on 2015/09/09 11:05:40
Showing 22 changed files
... ...
@@ -4,7 +4,6 @@ import (
4 4
 	"fmt"
5 5
 	"io"
6 6
 	"net/http"
7
-	"runtime"
8 7
 	"strconv"
9 8
 	"strings"
10 9
 	"time"
... ...
@@ -20,22 +19,6 @@ import (
20 20
 	"github.com/docker/docker/runconfig"
21 21
 )
22 22
 
23
-func (s *Server) getContainersByName(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
24
-	if vars == nil {
25
-		return fmt.Errorf("Missing parameter")
26
-	}
27
-
28
-	if version.LessThan("1.20") && runtime.GOOS != "windows" {
29
-		return getContainersByNameDownlevel(w, s, vars["name"])
30
-	}
31
-
32
-	containerJSON, err := s.daemon.ContainerInspect(vars["name"])
33
-	if err != nil {
34
-		return err
35
-	}
36
-	return writeJSON(w, http.StatusOK, containerJSON)
37
-}
38
-
39 23
 func (s *Server) getContainersJSON(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
40 24
 	if err := parseForm(r); err != nil {
41 25
 		return err
42 26
new file mode 100644
... ...
@@ -0,0 +1,33 @@
0
+package server
1
+
2
+import (
3
+	"fmt"
4
+	"net/http"
5
+
6
+	"github.com/docker/docker/pkg/version"
7
+)
8
+
9
+// getContainersByName inspects containers configuration and serializes it as json.
10
+func (s *Server) getContainersByName(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
11
+	if vars == nil {
12
+		return fmt.Errorf("Missing parameter")
13
+	}
14
+
15
+	var json interface{}
16
+	var err error
17
+
18
+	switch {
19
+	case version.LessThan("1.20"):
20
+		json, err = s.daemon.ContainerInspectPre120(vars["name"])
21
+	case version.Equal("1.20"):
22
+		json, err = s.daemon.ContainerInspect120(vars["name"])
23
+	default:
24
+		json, err = s.daemon.ContainerInspect(vars["name"])
25
+	}
26
+
27
+	if err != nil {
28
+		return err
29
+	}
30
+
31
+	return writeJSON(w, http.StatusOK, json)
32
+}
... ...
@@ -103,16 +103,6 @@ func allocateDaemonPort(addr string) error {
103 103
 	return nil
104 104
 }
105 105
 
106
-// getContainersByNameDownlevel performs processing for pre 1.20 APIs. This
107
-// is only relevant on non-Windows daemons.
108
-func getContainersByNameDownlevel(w http.ResponseWriter, s *Server, namevar string) error {
109
-	containerJSONRaw, err := s.daemon.ContainerInspectPre120(namevar)
110
-	if err != nil {
111
-		return err
112
-	}
113
-	return writeJSON(w, http.StatusOK, containerJSONRaw)
114
-}
115
-
116 106
 // listenFD returns the specified socket activated files as a slice of
117 107
 // net.Listeners or all of the activated files if "*" is given.
118 108
 func listenFD(addr string) ([]net.Listener, error) {
... ...
@@ -56,9 +56,3 @@ func (s *Server) AcceptConnections(d *daemon.Daemon) {
56 56
 func allocateDaemonPort(addr string) error {
57 57
 	return nil
58 58
 }
59
-
60
-// getContainersByNameDownlevel performs processing for pre 1.20 APIs. This
61
-// is only relevant on non-Windows daemons.
62
-func getContainersByNameDownlevel(w http.ResponseWriter, s *Server, namevar string) error {
63
-	return nil
64
-}
... ...
@@ -274,24 +274,39 @@ type ContainerJSON struct {
274 274
 	Config *runconfig.Config
275 275
 }
276 276
 
277
-// ContainerJSONPre120 is a backcompatibility struct along with ContainerConfig.
277
+// ContainerJSON120 is a backcompatibility struct along with ContainerConfig120.
278
+type ContainerJSON120 struct {
279
+	*ContainerJSONBase
280
+	Mounts []MountPoint
281
+	Config *ContainerConfig120
282
+}
283
+
284
+// ContainerJSONPre120 is a backcompatibility struct along with ContainerConfigPre120.
278 285
 // Note this is not used by the Windows daemon.
279 286
 type ContainerJSONPre120 struct {
280 287
 	*ContainerJSONBase
281 288
 	Volumes   map[string]string
282 289
 	VolumesRW map[string]bool
283
-	Config    *ContainerConfig
290
+	Config    *ContainerConfigPre120
284 291
 }
285 292
 
286
-// ContainerConfig is a backcompatibility struct used in ContainerJSONPre120
287
-type ContainerConfig struct {
293
+// ContainerConfigPre120 is a backcompatibility struct used in ContainerJSONPre120
294
+type ContainerConfigPre120 struct {
288 295
 	*runconfig.Config
289 296
 
290 297
 	// backward compatibility, they now live in HostConfig
291
-	Memory     int64
292
-	MemorySwap int64
293
-	CPUShares  int64  `json:"CpuShares"`
294
-	CPUSet     string `json:"CpuSet"`
298
+	VolumeDriver string
299
+	Memory       int64
300
+	MemorySwap   int64
301
+	CPUShares    int64  `json:"CpuShares"`
302
+	CPUSet       string `json:"CpuSet"`
303
+}
304
+
305
+// ContainerConfig120 is a backcompatibility struct used in ContainerJSON120
306
+type ContainerConfig120 struct {
307
+	*runconfig.Config
308
+	// backward compatibility, it lives now in HostConfig
309
+	VolumeDriver string
295 310
 }
296 311
 
297 312
 // MountPoint represents a mount point configuration inside the container.
... ...
@@ -106,7 +106,7 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos
106 106
 	}
107 107
 	defer container.Unmount()
108 108
 
109
-	if err := createContainerPlatformSpecificSettings(container, config, img); err != nil {
109
+	if err := createContainerPlatformSpecificSettings(container, config, hostConfig, img); err != nil {
110 110
 		return nil, nil, err
111 111
 	}
112 112
 
... ...
@@ -16,7 +16,7 @@ import (
16 16
 )
17 17
 
18 18
 // createContainerPlatformSpecificSettings performs platform specific container create functionality
19
-func createContainerPlatformSpecificSettings(container *Container, config *runconfig.Config, img *image.Image) error {
19
+func createContainerPlatformSpecificSettings(container *Container, config *runconfig.Config, hostConfig *runconfig.HostConfig, img *image.Image) error {
20 20
 	for spec := range config.Volumes {
21 21
 		var (
22 22
 			name, destination string
... ...
@@ -44,7 +44,7 @@ func createContainerPlatformSpecificSettings(container *Container, config *runco
44 44
 			return fmt.Errorf("cannot mount volume over existing file, file exists %s", path)
45 45
 		}
46 46
 
47
-		volumeDriver := config.VolumeDriver
47
+		volumeDriver := hostConfig.VolumeDriver
48 48
 		if destination != "" && img != nil {
49 49
 			if _, ok := img.ContainerConfig.Volumes[destination]; ok {
50 50
 				// check for whether bind is not specified and then set to local
... ...
@@ -6,6 +6,6 @@ import (
6 6
 )
7 7
 
8 8
 // createContainerPlatformSpecificSettings performs platform specific container create functionality
9
-func createContainerPlatformSpecificSettings(container *Container, config *runconfig.Config, img *image.Image) error {
9
+func createContainerPlatformSpecificSettings(container *Container, config *runconfig.Config, hostConfig *runconfig.HostConfig, img *image.Image) error {
10 10
 	return nil
11 11
 }
... ...
@@ -29,6 +29,30 @@ func (daemon *Daemon) ContainerInspect(name string) (*types.ContainerJSON, error
29 29
 	return &types.ContainerJSON{base, mountPoints, container.Config}, nil
30 30
 }
31 31
 
32
+// ContainerInspect120 serializes the master version of a container into a json type.
33
+func (daemon *Daemon) ContainerInspect120(name string) (*types.ContainerJSON120, error) {
34
+	container, err := daemon.Get(name)
35
+	if err != nil {
36
+		return nil, err
37
+	}
38
+
39
+	container.Lock()
40
+	defer container.Unlock()
41
+
42
+	base, err := daemon.getInspectData(container)
43
+	if err != nil {
44
+		return nil, err
45
+	}
46
+
47
+	mountPoints := addMountPoints(container)
48
+	config := &types.ContainerConfig120{
49
+		container.Config,
50
+		container.hostConfig.VolumeDriver,
51
+	}
52
+
53
+	return &types.ContainerJSON120{base, mountPoints, config}, nil
54
+}
55
+
32 56
 func (daemon *Daemon) getInspectData(container *Container) (*types.ContainerJSONBase, error) {
33 57
 	// make a copy to play with
34 58
 	hostConfig := *container.hostConfig
... ...
@@ -14,7 +14,7 @@ func setPlatformSpecificContainerFields(container *Container, contJSONBase *type
14 14
 	return contJSONBase
15 15
 }
16 16
 
17
-// ContainerInspectPre120 is for backwards compatibility with pre v1.20 clients.
17
+// ContainerInspectPre120 gets containers for pre 1.20 APIs.
18 18
 func (daemon *Daemon) ContainerInspectPre120(name string) (*types.ContainerJSONPre120, error) {
19 19
 	container, err := daemon.Get(name)
20 20
 	if err != nil {
... ...
@@ -36,8 +36,9 @@ func (daemon *Daemon) ContainerInspectPre120(name string) (*types.ContainerJSONP
36 36
 		volumesRW[m.Destination] = m.RW
37 37
 	}
38 38
 
39
-	config := &types.ContainerConfig{
39
+	config := &types.ContainerConfigPre120{
40 40
 		container.Config,
41
+		container.hostConfig.VolumeDriver,
41 42
 		container.hostConfig.Memory,
42 43
 		container.hostConfig.MemorySwap,
43 44
 		container.hostConfig.CPUShares,
... ...
@@ -10,3 +10,8 @@ func setPlatformSpecificContainerFields(container *Container, contJSONBase *type
10 10
 func addMountPoints(container *Container) []types.MountPoint {
11 11
 	return nil
12 12
 }
13
+
14
+// ContainerInspectPre120 get containers for pre 1.20 APIs.
15
+func (daemon *Daemon) ContainerInspectPre120(name string) (*types.ContainerJSON, error) {
16
+	return daemon.ContainerInspect(name)
17
+}
... ...
@@ -5,7 +5,6 @@ package daemon
5 5
 import (
6 6
 	"testing"
7 7
 
8
-	"github.com/docker/docker/runconfig"
9 8
 	"github.com/docker/docker/volume"
10 9
 	"github.com/docker/docker/volume/drivers"
11 10
 )
... ...
@@ -56,8 +55,7 @@ func TestParseBindMount(t *testing.T) {
56 56
 	}
57 57
 
58 58
 	for _, c := range cases {
59
-		conf := &runconfig.Config{VolumeDriver: c.driver}
60
-		m, err := parseBindMount(c.bind, c.mountLabel, conf)
59
+		m, err := parseBindMount(c.bind, c.mountLabel, c.driver)
61 60
 		if c.fail {
62 61
 			if err == nil {
63 62
 				t.Fatalf("Expected error, was nil, for spec %s\n", c.bind)
... ...
@@ -59,7 +59,7 @@ func (container *Container) setupMounts() ([]execdriver.Mount, error) {
59 59
 }
60 60
 
61 61
 // parseBindMount validates the configuration of mount information in runconfig is valid.
62
-func parseBindMount(spec string, mountLabel string, config *runconfig.Config) (*mountPoint, error) {
62
+func parseBindMount(spec, mountLabel, volumeDriver string) (*mountPoint, error) {
63 63
 	bind := &mountPoint{
64 64
 		RW: true,
65 65
 	}
... ...
@@ -92,7 +92,7 @@ func parseBindMount(spec string, mountLabel string, config *runconfig.Config) (*
92 92
 	}
93 93
 
94 94
 	if len(source) == 0 {
95
-		bind.Driver = config.VolumeDriver
95
+		bind.Driver = volumeDriver
96 96
 		if len(bind.Driver) == 0 {
97 97
 			bind.Driver = volume.DefaultDriverName
98 98
 		}
... ...
@@ -330,7 +330,7 @@ func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runc
330 330
 	// 3. Read bind mounts
331 331
 	for _, b := range hostConfig.Binds {
332 332
 		// #10618
333
-		bind, err := parseBindMount(b, container.MountLabel, container.Config)
333
+		bind, err := parseBindMount(b, container.MountLabel, hostConfig.VolumeDriver)
334 334
 		if err != nil {
335 335
 			return err
336 336
 		}
... ...
@@ -80,6 +80,7 @@ This section lists each version from latest to oldest.  Each listing includes a
80 80
 * `POST /volumes` to create a volume.
81 81
 * `GET /volumes/(name)` get low-level information about a volume.
82 82
 * `DELETE /volumes/(name)`remove a volume with the specified name.
83
+* `VolumeDriver` has been moved from config to hostConfig to make the configuration portable.
83 84
 
84 85
 
85 86
 ### v1.20 API changes
... ...
@@ -196,7 +196,8 @@ Create a container
196 196
              "Ulimits": [{}],
197 197
              "LogConfig": { "Type": "json-file", "Config": {} },
198 198
              "SecurityOpt": [""],
199
-             "CgroupParent": ""
199
+             "CgroupParent": "",
200
+	      "VolumeDriver": ""
200 201
           }
201 202
       }
202 203
 
... ...
@@ -300,6 +301,7 @@ Json Parameters:
300 300
           Available types: `json-file`, `syslog`, `journald`, `gelf`, `none`.
301 301
           `json-file` logging driver.
302 302
     -   **CgroupParent** - Path to `cgroups` under which the container's `cgroup` is created. If the path is not absolute, the path is considered to be relative to the `cgroups` path of the init process. Cgroups are created if they do not already exist.
303
+    -   **VolumeDriver** - Driver that this container users to mount volumes.
303 304
 
304 305
 Query Parameters:
305 306
 
... ...
@@ -407,7 +409,8 @@ Return low-level information on the container `id`
407 407
 			},
408 408
 			"SecurityOpt": null,
409 409
 			"VolumesFrom": null,
410
-			"Ulimits": [{}]
410
+			"Ulimits": [{}],
411
+			"VolumeDriver": ""
411 412
 		},
412 413
 		"HostnamePath": "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname",
413 414
 		"HostsPath": "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts",
... ...
@@ -49,3 +49,65 @@ func (s *DockerSuite) TestInspectApiContainerResponse(c *check.C) {
49 49
 		}
50 50
 	}
51 51
 }
52
+
53
+func (s *DockerSuite) TestInspectApiContainerVolumeDriverLegacy(c *check.C) {
54
+	out, _ := dockerCmd(c, "run", "-d", "busybox", "true")
55
+
56
+	cleanedContainerID := strings.TrimSpace(out)
57
+
58
+	cases := []string{"1.19", "1.20"}
59
+	for _, version := range cases {
60
+		endpoint := fmt.Sprintf("/v%s/containers/%s/json", version, cleanedContainerID)
61
+		status, body, err := sockRequest("GET", endpoint, nil)
62
+		c.Assert(status, check.Equals, http.StatusOK)
63
+		c.Assert(err, check.IsNil)
64
+
65
+		var inspectJSON map[string]interface{}
66
+		if err = json.Unmarshal(body, &inspectJSON); err != nil {
67
+			c.Fatalf("unable to unmarshal body for version %s: %v", version, err)
68
+		}
69
+
70
+		config, ok := inspectJSON["Config"]
71
+		if !ok {
72
+			c.Fatal("Unable to find 'Config'")
73
+		}
74
+		cfg := config.(map[string]interface{})
75
+		if _, ok := cfg["VolumeDriver"]; !ok {
76
+			c.Fatalf("Api version %s expected to include VolumeDriver in 'Config'", version)
77
+		}
78
+	}
79
+}
80
+
81
+func (s *DockerSuite) TestInspectApiContainerVolumeDriver(c *check.C) {
82
+	out, _ := dockerCmd(c, "run", "-d", "busybox", "true")
83
+
84
+	cleanedContainerID := strings.TrimSpace(out)
85
+
86
+	endpoint := fmt.Sprintf("/v1.21/containers/%s/json", cleanedContainerID)
87
+	status, body, err := sockRequest("GET", endpoint, nil)
88
+	c.Assert(status, check.Equals, http.StatusOK)
89
+	c.Assert(err, check.IsNil)
90
+
91
+	var inspectJSON map[string]interface{}
92
+	if err = json.Unmarshal(body, &inspectJSON); err != nil {
93
+		c.Fatalf("unable to unmarshal body for version 1.21: %v", err)
94
+	}
95
+
96
+	config, ok := inspectJSON["Config"]
97
+	if !ok {
98
+		c.Fatal("Unable to find 'Config'")
99
+	}
100
+	cfg := config.(map[string]interface{})
101
+	if _, ok := cfg["VolumeDriver"]; ok {
102
+		c.Fatal("Api version 1.21 expected to not include VolumeDriver in 'Config'")
103
+	}
104
+
105
+	config, ok = inspectJSON["HostConfig"]
106
+	if !ok {
107
+		c.Fatal("Unable to find 'HostConfig'")
108
+	}
109
+	cfg = config.(map[string]interface{})
110
+	if _, ok := cfg["VolumeDriver"]; !ok {
111
+		c.Fatal("Api version 1.21 expected to include VolumeDriver in 'HostConfig'")
112
+	}
113
+}
... ...
@@ -253,15 +253,11 @@ func (s *DockerExternalVolumeSuite) TestStartExternalNamedVolumeDriverCheckBindL
253 253
 
254 254
 	img := "test-checkbindlocalvolume"
255 255
 
256
-	args := []string{"--host", s.d.sock()}
257
-	buildOut, err := buildImageArgs(args, img, dockerfile, true)
258
-	fmt.Println(buildOut)
256
+	_, err := buildImageWithOutInDamon(s.d.sock(), img, dockerfile, true)
257
+	c.Assert(err, check.IsNil)
259 258
 
260 259
 	out, err := s.d.Cmd("run", "--rm", "--name", "test-data-nobind", "-v", "external-volume-test:/tmp/external-volume-test", "--volume-driver", "test-external-volume-driver", img, "cat", "/nobindthenlocalvol/test")
261
-	if err != nil {
262
-		fmt.Println(out)
263
-		c.Fatal(err)
264
-	}
260
+	c.Assert(err, check.IsNil)
265 261
 
266 262
 	if !strings.Contains(out, expected) {
267 263
 		c.Fatalf("External volume mount failed. Output: %s\n", out)
... ...
@@ -1382,22 +1382,14 @@ func createTmpFile(c *check.C, content string) string {
1382 1382
 	return filename
1383 1383
 }
1384 1384
 
1385
-func buildImageArgs(args []string, name, dockerfile string, useCache bool) (string, error) {
1386
-	id, _, err := buildImageWithOutArgs(args, name, dockerfile, useCache)
1387
-	return id, err
1388
-}
1389
-
1390
-func buildImageWithOutArgs(args []string, name, dockerfile string, useCache bool) (string, string, error) {
1385
+func buildImageWithOutInDamon(socket string, name, dockerfile string, useCache bool) (string, error) {
1386
+	args := []string{"--host", socket}
1391 1387
 	buildCmd := buildImageCmdArgs(args, name, dockerfile, useCache)
1392 1388
 	out, exitCode, err := runCommandWithOutput(buildCmd)
1393 1389
 	if err != nil || exitCode != 0 {
1394
-		return "", out, fmt.Errorf("failed to build the image: %s", out)
1390
+		return out, fmt.Errorf("failed to build the image: %s, error: %v", out, err)
1395 1391
 	}
1396
-	id, err := getIDByName(name)
1397
-	if err != nil {
1398
-		return "", out, err
1399
-	}
1400
-	return id, out, nil
1392
+	return out, nil
1401 1393
 }
1402 1394
 
1403 1395
 func buildImageCmdArgs(args []string, name, dockerfile string, useCache bool) *exec.Cmd {
... ...
@@ -28,7 +28,6 @@ type Config struct {
28 28
 	Cmd             *stringutils.StrSlice // Command to run when starting the container
29 29
 	Image           string                // Name of the image as it was passed by the operator (eg. could be symbolic)
30 30
 	Volumes         map[string]struct{}   // List of volumes (mounts) used for the container
31
-	VolumeDriver    string                // Name of the volume driver used to mount volumes
32 31
 	WorkingDir      string                // Current directory (PWD) in the command will be launched
33 32
 	Entrypoint      *stringutils.StrSlice // Entrypoint to run when starting the container
34 33
 	NetworkDisabled bool                  // Is network disabled
... ...
@@ -32,11 +32,17 @@ func (w *ContainerConfigWrapper) getHostConfig() *HostConfig {
32 32
 			w.InnerHostConfig.CpusetCpus = hc.CpusetCpus
33 33
 		}
34 34
 
35
+		if hc.VolumeDriver != "" && w.InnerHostConfig.VolumeDriver == "" {
36
+			w.InnerHostConfig.VolumeDriver = hc.VolumeDriver
37
+		}
38
+
35 39
 		hc = w.InnerHostConfig
36 40
 	}
37 41
 
38
-	if hc != nil && w.Cpuset != "" && hc.CpusetCpus == "" {
39
-		hc.CpusetCpus = w.Cpuset
42
+	if hc != nil {
43
+		if w.Cpuset != "" && hc.CpusetCpus == "" {
44
+			hc.CpusetCpus = w.Cpuset
45
+		}
40 46
 	}
41 47
 
42 48
 	// Make sure NetworkMode has an acceptable value. We do this to ensure
... ...
@@ -251,6 +251,7 @@ type HostConfig struct {
251 251
 	LogConfig        LogConfig             // Configuration of the logs for this container
252 252
 	CgroupParent     string                // Parent cgroup.
253 253
 	ConsoleSize      [2]int                // Initial console size on Windows
254
+	VolumeDriver     string                // Name of the volume driver used to mount volumes
254 255
 }
255 256
 
256 257
 // DecodeHostConfig creates a HostConfig based on the specified Reader.
... ...
@@ -322,7 +322,6 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
322 322
 		Entrypoint:      entrypoint,
323 323
 		WorkingDir:      *flWorkingDir,
324 324
 		Labels:          convertKVStringsToMap(labels),
325
-		VolumeDriver:    *flVolumeDriver,
326 325
 	}
327 326
 
328 327
 	hostConfig := &HostConfig{
... ...
@@ -362,6 +361,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
362 362
 		Ulimits:          flUlimits.GetList(),
363 363
 		LogConfig:        LogConfig{Type: *flLoggingDriver, Config: loggingOpts},
364 364
 		CgroupParent:     *flCgroupParent,
365
+		VolumeDriver:     *flVolumeDriver,
365 366
 	}
366 367
 
367 368
 	applyExperimentalFlags(expFlags, config, hostConfig)