Browse code

Move VolumeDriver to HostConfig to make containers portable.

Signed-off-by: David Calavera <david.calavera@gmail.com>

David Calavera authored on 2015/08/25 02:57:39
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
-}
... ...
@@ -273,24 +273,39 @@ type ContainerJSON struct {
273 273
 	Config *runconfig.Config
274 274
 }
275 275
 
276
-// ContainerJSONPre120 is a backcompatibility struct along with ContainerConfig.
276
+// ContainerJSON120 is a backcompatibility struct along with ContainerConfig120.
277
+type ContainerJSON120 struct {
278
+	*ContainerJSONBase
279
+	Mounts []MountPoint
280
+	Config *ContainerConfig120
281
+}
282
+
283
+// ContainerJSONPre120 is a backcompatibility struct along with ContainerConfigPre120.
277 284
 // Note this is not used by the Windows daemon.
278 285
 type ContainerJSONPre120 struct {
279 286
 	*ContainerJSONBase
280 287
 	Volumes   map[string]string
281 288
 	VolumesRW map[string]bool
282
-	Config    *ContainerConfig
289
+	Config    *ContainerConfigPre120
283 290
 }
284 291
 
285
-// ContainerConfig is a backcompatibility struct used in ContainerJSONPre120
286
-type ContainerConfig struct {
292
+// ContainerConfigPre120 is a backcompatibility struct used in ContainerJSONPre120
293
+type ContainerConfigPre120 struct {
287 294
 	*runconfig.Config
288 295
 
289 296
 	// backward compatibility, they now live in HostConfig
290
-	Memory     int64
291
-	MemorySwap int64
292
-	CPUShares  int64  `json:"CpuShares"`
293
-	CPUSet     string `json:"CpuSet"`
297
+	VolumeDriver string
298
+	Memory       int64
299
+	MemorySwap   int64
300
+	CPUShares    int64  `json:"CpuShares"`
301
+	CPUSet       string `json:"CpuSet"`
302
+}
303
+
304
+// ContainerConfig120 is a backcompatibility struct used in ContainerJSON120
305
+type ContainerConfig120 struct {
306
+	*runconfig.Config
307
+	// backward compatibility, it lives now in HostConfig
308
+	VolumeDriver string
294 309
 }
295 310
 
296 311
 // MountPoint represents a mount point configuration inside the container.
... ...
@@ -105,7 +105,7 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos
105 105
 	}
106 106
 	defer container.Unmount()
107 107
 
108
-	if err := createContainerPlatformSpecificSettings(container, config, img); err != nil {
108
+	if err := createContainerPlatformSpecificSettings(container, config, hostConfig, img); err != nil {
109 109
 		return nil, nil, err
110 110
 	}
111 111
 
... ...
@@ -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
 )
... ...
@@ -55,8 +54,7 @@ func TestParseBindMount(t *testing.T) {
55 55
 	}
56 56
 
57 57
 	for _, c := range cases {
58
-		conf := &runconfig.Config{VolumeDriver: c.driver}
59
-		m, err := parseBindMount(c.bind, c.mountLabel, conf)
58
+		m, err := parseBindMount(c.bind, c.mountLabel, c.driver)
60 59
 		if c.fail {
61 60
 			if err == nil {
62 61
 				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
 	}
... ...
@@ -87,7 +87,7 @@ func parseBindMount(spec string, mountLabel string, config *runconfig.Config) (*
87 87
 	}
88 88
 
89 89
 	if len(source) == 0 {
90
-		bind.Driver = config.VolumeDriver
90
+		bind.Driver = volumeDriver
91 91
 		if len(bind.Driver) == 0 {
92 92
 			bind.Driver = volume.DefaultDriverName
93 93
 		}
... ...
@@ -325,7 +325,7 @@ func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runc
325 325
 	// 3. Read bind mounts
326 326
 	for _, b := range hostConfig.Binds {
327 327
 		// #10618
328
-		bind, err := parseBindMount(b, container.MountLabel, container.Config)
328
+		bind, err := parseBindMount(b, container.MountLabel, hostConfig.VolumeDriver)
329 329
 		if err != nil {
330 330
 			return err
331 331
 		}
... ...
@@ -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",
... ...
@@ -48,3 +48,65 @@ func (s *DockerSuite) TestInspectApiContainerResponse(c *check.C) {
48 48
 		}
49 49
 	}
50 50
 }
51
+
52
+func (s *DockerSuite) TestInspectApiContainerVolumeDriverLegacy(c *check.C) {
53
+	out, _ := dockerCmd(c, "run", "-d", "busybox", "true")
54
+
55
+	cleanedContainerID := strings.TrimSpace(out)
56
+
57
+	cases := []string{"1.19", "1.20"}
58
+	for _, version := range cases {
59
+		endpoint := fmt.Sprintf("/v%s/containers/%s/json", version, cleanedContainerID)
60
+		status, body, err := sockRequest("GET", endpoint, nil)
61
+		c.Assert(status, check.Equals, http.StatusOK)
62
+		c.Assert(err, check.IsNil)
63
+
64
+		var inspectJSON map[string]interface{}
65
+		if err = json.Unmarshal(body, &inspectJSON); err != nil {
66
+			c.Fatalf("unable to unmarshal body for version %s: %v", version, err)
67
+		}
68
+
69
+		config, ok := inspectJSON["Config"]
70
+		if !ok {
71
+			c.Fatal("Unable to find 'Config'")
72
+		}
73
+		cfg := config.(map[string]interface{})
74
+		if _, ok := cfg["VolumeDriver"]; !ok {
75
+			c.Fatalf("Api version %s expected to include VolumeDriver in 'Config'", version)
76
+		}
77
+	}
78
+}
79
+
80
+func (s *DockerSuite) TestInspectApiContainerVolumeDriver(c *check.C) {
81
+	out, _ := dockerCmd(c, "run", "-d", "busybox", "true")
82
+
83
+	cleanedContainerID := strings.TrimSpace(out)
84
+
85
+	endpoint := fmt.Sprintf("/v1.21/containers/%s/json", cleanedContainerID)
86
+	status, body, err := sockRequest("GET", endpoint, nil)
87
+	c.Assert(status, check.Equals, http.StatusOK)
88
+	c.Assert(err, check.IsNil)
89
+
90
+	var inspectJSON map[string]interface{}
91
+	if err = json.Unmarshal(body, &inspectJSON); err != nil {
92
+		c.Fatalf("unable to unmarshal body for version 1.21: %v", err)
93
+	}
94
+
95
+	config, ok := inspectJSON["Config"]
96
+	if !ok {
97
+		c.Fatal("Unable to find 'Config'")
98
+	}
99
+	cfg := config.(map[string]interface{})
100
+	if _, ok := cfg["VolumeDriver"]; ok {
101
+		c.Fatal("Api version 1.21 expected to not include VolumeDriver in 'Config'")
102
+	}
103
+
104
+	config, ok = inspectJSON["HostConfig"]
105
+	if !ok {
106
+		c.Fatal("Unable to find 'HostConfig'")
107
+	}
108
+	cfg = config.(map[string]interface{})
109
+	if _, ok := cfg["VolumeDriver"]; !ok {
110
+		c.Fatal("Api version 1.21 expected to include VolumeDriver in 'HostConfig'")
111
+	}
112
+}
... ...
@@ -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)
... ...
@@ -1392,22 +1392,14 @@ func createTmpFile(c *check.C, content string) string {
1392 1392
 	return filename
1393 1393
 }
1394 1394
 
1395
-func buildImageArgs(args []string, name, dockerfile string, useCache bool) (string, error) {
1396
-	id, _, err := buildImageWithOutArgs(args, name, dockerfile, useCache)
1397
-	return id, err
1398
-}
1399
-
1400
-func buildImageWithOutArgs(args []string, name, dockerfile string, useCache bool) (string, string, error) {
1395
+func buildImageWithOutInDamon(socket string, name, dockerfile string, useCache bool) (string, error) {
1396
+	args := []string{"--host", socket}
1401 1397
 	buildCmd := buildImageCmdArgs(args, name, dockerfile, useCache)
1402 1398
 	out, exitCode, err := runCommandWithOutput(buildCmd)
1403 1399
 	if err != nil || exitCode != 0 {
1404
-		return "", out, fmt.Errorf("failed to build the image: %s", out)
1400
+		return out, fmt.Errorf("failed to build the image: %s, error: %v", out, err)
1405 1401
 	}
1406
-	id, err := getIDByName(name)
1407
-	if err != nil {
1408
-		return "", out, err
1409
-	}
1410
-	return id, out, nil
1402
+	return out, nil
1411 1403
 }
1412 1404
 
1413 1405
 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)