1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Move VolumeDriver to HostConfig to make containers portable.

Signed-off-by: David Calavera <david.calavera@gmail.com>
This commit is contained in:
David Calavera 2015-08-24 13:57:39 -04:00
parent 2434bd8e63
commit 6549d6517b
22 changed files with 181 additions and 78 deletions

View file

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"runtime"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -20,22 +19,6 @@ import (
"github.com/docker/docker/runconfig" "github.com/docker/docker/runconfig"
) )
func (s *Server) getContainersByName(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter")
}
if version.LessThan("1.20") && runtime.GOOS != "windows" {
return getContainersByNameDownlevel(w, s, vars["name"])
}
containerJSON, err := s.daemon.ContainerInspect(vars["name"])
if err != nil {
return err
}
return writeJSON(w, http.StatusOK, containerJSON)
}
func (s *Server) getContainersJSON(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { func (s *Server) getContainersJSON(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil { if err := parseForm(r); err != nil {
return err return err

33
api/server/inspect.go Normal file
View file

@ -0,0 +1,33 @@
package server
import (
"fmt"
"net/http"
"github.com/docker/docker/pkg/version"
)
// getContainersByName inspects containers configuration and serializes it as json.
func (s *Server) getContainersByName(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter")
}
var json interface{}
var err error
switch {
case version.LessThan("1.20"):
json, err = s.daemon.ContainerInspectPre120(vars["name"])
case version.Equal("1.20"):
json, err = s.daemon.ContainerInspect120(vars["name"])
default:
json, err = s.daemon.ContainerInspect(vars["name"])
}
if err != nil {
return err
}
return writeJSON(w, http.StatusOK, json)
}

View file

@ -103,16 +103,6 @@ func allocateDaemonPort(addr string) error {
return nil return nil
} }
// getContainersByNameDownlevel performs processing for pre 1.20 APIs. This
// is only relevant on non-Windows daemons.
func getContainersByNameDownlevel(w http.ResponseWriter, s *Server, namevar string) error {
containerJSONRaw, err := s.daemon.ContainerInspectPre120(namevar)
if err != nil {
return err
}
return writeJSON(w, http.StatusOK, containerJSONRaw)
}
// listenFD returns the specified socket activated files as a slice of // listenFD returns the specified socket activated files as a slice of
// net.Listeners or all of the activated files if "*" is given. // net.Listeners or all of the activated files if "*" is given.
func listenFD(addr string) ([]net.Listener, error) { func listenFD(addr string) ([]net.Listener, error) {

View file

@ -56,9 +56,3 @@ func (s *Server) AcceptConnections(d *daemon.Daemon) {
func allocateDaemonPort(addr string) error { func allocateDaemonPort(addr string) error {
return nil return nil
} }
// getContainersByNameDownlevel performs processing for pre 1.20 APIs. This
// is only relevant on non-Windows daemons.
func getContainersByNameDownlevel(w http.ResponseWriter, s *Server, namevar string) error {
return nil
}

View file

@ -273,26 +273,41 @@ type ContainerJSON struct {
Config *runconfig.Config Config *runconfig.Config
} }
// ContainerJSONPre120 is a backcompatibility struct along with ContainerConfig. // ContainerJSON120 is a backcompatibility struct along with ContainerConfig120.
type ContainerJSON120 struct {
*ContainerJSONBase
Mounts []MountPoint
Config *ContainerConfig120
}
// ContainerJSONPre120 is a backcompatibility struct along with ContainerConfigPre120.
// Note this is not used by the Windows daemon. // Note this is not used by the Windows daemon.
type ContainerJSONPre120 struct { type ContainerJSONPre120 struct {
*ContainerJSONBase *ContainerJSONBase
Volumes map[string]string Volumes map[string]string
VolumesRW map[string]bool VolumesRW map[string]bool
Config *ContainerConfig Config *ContainerConfigPre120
} }
// ContainerConfig is a backcompatibility struct used in ContainerJSONPre120 // ContainerConfigPre120 is a backcompatibility struct used in ContainerJSONPre120
type ContainerConfig struct { type ContainerConfigPre120 struct {
*runconfig.Config *runconfig.Config
// backward compatibility, they now live in HostConfig // backward compatibility, they now live in HostConfig
VolumeDriver string
Memory int64 Memory int64
MemorySwap int64 MemorySwap int64
CPUShares int64 `json:"CpuShares"` CPUShares int64 `json:"CpuShares"`
CPUSet string `json:"CpuSet"` CPUSet string `json:"CpuSet"`
} }
// ContainerConfig120 is a backcompatibility struct used in ContainerJSON120
type ContainerConfig120 struct {
*runconfig.Config
// backward compatibility, it lives now in HostConfig
VolumeDriver string
}
// MountPoint represents a mount point configuration inside the container. // MountPoint represents a mount point configuration inside the container.
type MountPoint struct { type MountPoint struct {
Name string `json:",omitempty"` Name string `json:",omitempty"`

View file

@ -105,7 +105,7 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos
} }
defer container.Unmount() defer container.Unmount()
if err := createContainerPlatformSpecificSettings(container, config, img); err != nil { if err := createContainerPlatformSpecificSettings(container, config, hostConfig, img); err != nil {
return nil, nil, err return nil, nil, err
} }

View file

@ -16,7 +16,7 @@ import (
) )
// createContainerPlatformSpecificSettings performs platform specific container create functionality // createContainerPlatformSpecificSettings performs platform specific container create functionality
func createContainerPlatformSpecificSettings(container *Container, config *runconfig.Config, img *image.Image) error { func createContainerPlatformSpecificSettings(container *Container, config *runconfig.Config, hostConfig *runconfig.HostConfig, img *image.Image) error {
for spec := range config.Volumes { for spec := range config.Volumes {
var ( var (
name, destination string name, destination string
@ -44,7 +44,7 @@ func createContainerPlatformSpecificSettings(container *Container, config *runco
return fmt.Errorf("cannot mount volume over existing file, file exists %s", path) return fmt.Errorf("cannot mount volume over existing file, file exists %s", path)
} }
volumeDriver := config.VolumeDriver volumeDriver := hostConfig.VolumeDriver
if destination != "" && img != nil { if destination != "" && img != nil {
if _, ok := img.ContainerConfig.Volumes[destination]; ok { if _, ok := img.ContainerConfig.Volumes[destination]; ok {
// check for whether bind is not specified and then set to local // check for whether bind is not specified and then set to local

View file

@ -6,6 +6,6 @@ import (
) )
// createContainerPlatformSpecificSettings performs platform specific container create functionality // createContainerPlatformSpecificSettings performs platform specific container create functionality
func createContainerPlatformSpecificSettings(container *Container, config *runconfig.Config, img *image.Image) error { func createContainerPlatformSpecificSettings(container *Container, config *runconfig.Config, hostConfig *runconfig.HostConfig, img *image.Image) error {
return nil return nil
} }

View file

@ -29,6 +29,30 @@ func (daemon *Daemon) ContainerInspect(name string) (*types.ContainerJSON, error
return &types.ContainerJSON{base, mountPoints, container.Config}, nil return &types.ContainerJSON{base, mountPoints, container.Config}, nil
} }
// ContainerInspect120 serializes the master version of a container into a json type.
func (daemon *Daemon) ContainerInspect120(name string) (*types.ContainerJSON120, error) {
container, err := daemon.Get(name)
if err != nil {
return nil, err
}
container.Lock()
defer container.Unlock()
base, err := daemon.getInspectData(container)
if err != nil {
return nil, err
}
mountPoints := addMountPoints(container)
config := &types.ContainerConfig120{
container.Config,
container.hostConfig.VolumeDriver,
}
return &types.ContainerJSON120{base, mountPoints, config}, nil
}
func (daemon *Daemon) getInspectData(container *Container) (*types.ContainerJSONBase, error) { func (daemon *Daemon) getInspectData(container *Container) (*types.ContainerJSONBase, error) {
// make a copy to play with // make a copy to play with
hostConfig := *container.hostConfig hostConfig := *container.hostConfig

View file

@ -14,7 +14,7 @@ func setPlatformSpecificContainerFields(container *Container, contJSONBase *type
return contJSONBase return contJSONBase
} }
// ContainerInspectPre120 is for backwards compatibility with pre v1.20 clients. // ContainerInspectPre120 gets containers for pre 1.20 APIs.
func (daemon *Daemon) ContainerInspectPre120(name string) (*types.ContainerJSONPre120, error) { func (daemon *Daemon) ContainerInspectPre120(name string) (*types.ContainerJSONPre120, error) {
container, err := daemon.Get(name) container, err := daemon.Get(name)
if err != nil { if err != nil {
@ -36,8 +36,9 @@ func (daemon *Daemon) ContainerInspectPre120(name string) (*types.ContainerJSONP
volumesRW[m.Destination] = m.RW volumesRW[m.Destination] = m.RW
} }
config := &types.ContainerConfig{ config := &types.ContainerConfigPre120{
container.Config, container.Config,
container.hostConfig.VolumeDriver,
container.hostConfig.Memory, container.hostConfig.Memory,
container.hostConfig.MemorySwap, container.hostConfig.MemorySwap,
container.hostConfig.CPUShares, container.hostConfig.CPUShares,

View file

@ -10,3 +10,8 @@ func setPlatformSpecificContainerFields(container *Container, contJSONBase *type
func addMountPoints(container *Container) []types.MountPoint { func addMountPoints(container *Container) []types.MountPoint {
return nil return nil
} }
// ContainerInspectPre120 get containers for pre 1.20 APIs.
func (daemon *Daemon) ContainerInspectPre120(name string) (*types.ContainerJSON, error) {
return daemon.ContainerInspect(name)
}

View file

@ -5,7 +5,6 @@ package daemon
import ( import (
"testing" "testing"
"github.com/docker/docker/runconfig"
"github.com/docker/docker/volume" "github.com/docker/docker/volume"
"github.com/docker/docker/volume/drivers" "github.com/docker/docker/volume/drivers"
) )
@ -55,8 +54,7 @@ func TestParseBindMount(t *testing.T) {
} }
for _, c := range cases { for _, c := range cases {
conf := &runconfig.Config{VolumeDriver: c.driver} m, err := parseBindMount(c.bind, c.mountLabel, c.driver)
m, err := parseBindMount(c.bind, c.mountLabel, conf)
if c.fail { if c.fail {
if err == nil { if err == nil {
t.Fatalf("Expected error, was nil, for spec %s\n", c.bind) t.Fatalf("Expected error, was nil, for spec %s\n", c.bind)

View file

@ -59,7 +59,7 @@ func (container *Container) setupMounts() ([]execdriver.Mount, error) {
} }
// parseBindMount validates the configuration of mount information in runconfig is valid. // parseBindMount validates the configuration of mount information in runconfig is valid.
func parseBindMount(spec string, mountLabel string, config *runconfig.Config) (*mountPoint, error) { func parseBindMount(spec, mountLabel, volumeDriver string) (*mountPoint, error) {
bind := &mountPoint{ bind := &mountPoint{
RW: true, RW: true,
} }
@ -87,7 +87,7 @@ func parseBindMount(spec string, mountLabel string, config *runconfig.Config) (*
} }
if len(source) == 0 { if len(source) == 0 {
bind.Driver = config.VolumeDriver bind.Driver = volumeDriver
if len(bind.Driver) == 0 { if len(bind.Driver) == 0 {
bind.Driver = volume.DefaultDriverName bind.Driver = volume.DefaultDriverName
} }
@ -325,7 +325,7 @@ func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runc
// 3. Read bind mounts // 3. Read bind mounts
for _, b := range hostConfig.Binds { for _, b := range hostConfig.Binds {
// #10618 // #10618
bind, err := parseBindMount(b, container.MountLabel, container.Config) bind, err := parseBindMount(b, container.MountLabel, hostConfig.VolumeDriver)
if err != nil { if err != nil {
return err return err
} }

View file

@ -80,6 +80,7 @@ This section lists each version from latest to oldest. Each listing includes a
* `POST /volumes` to create a volume. * `POST /volumes` to create a volume.
* `GET /volumes/(name)` get low-level information about a volume. * `GET /volumes/(name)` get low-level information about a volume.
* `DELETE /volumes/(name)`remove a volume with the specified name. * `DELETE /volumes/(name)`remove a volume with the specified name.
* `VolumeDriver` has been moved from config to hostConfig to make the configuration portable.
### v1.20 API changes ### v1.20 API changes

View file

@ -196,7 +196,8 @@ Create a container
"Ulimits": [{}], "Ulimits": [{}],
"LogConfig": { "Type": "json-file", "Config": {} }, "LogConfig": { "Type": "json-file", "Config": {} },
"SecurityOpt": [""], "SecurityOpt": [""],
"CgroupParent": "" "CgroupParent": "",
"VolumeDriver": ""
} }
} }
@ -300,6 +301,7 @@ Json Parameters:
Available types: `json-file`, `syslog`, `journald`, `gelf`, `none`. Available types: `json-file`, `syslog`, `journald`, `gelf`, `none`.
`json-file` logging driver. `json-file` logging driver.
- **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. - **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.
- **VolumeDriver** - Driver that this container users to mount volumes.
Query Parameters: Query Parameters:
@ -407,7 +409,8 @@ Return low-level information on the container `id`
}, },
"SecurityOpt": null, "SecurityOpt": null,
"VolumesFrom": null, "VolumesFrom": null,
"Ulimits": [{}] "Ulimits": [{}],
"VolumeDriver": ""
}, },
"HostnamePath": "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname", "HostnamePath": "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname",
"HostsPath": "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts", "HostsPath": "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts",

View file

@ -48,3 +48,65 @@ func (s *DockerSuite) TestInspectApiContainerResponse(c *check.C) {
} }
} }
} }
func (s *DockerSuite) TestInspectApiContainerVolumeDriverLegacy(c *check.C) {
out, _ := dockerCmd(c, "run", "-d", "busybox", "true")
cleanedContainerID := strings.TrimSpace(out)
cases := []string{"1.19", "1.20"}
for _, version := range cases {
endpoint := fmt.Sprintf("/v%s/containers/%s/json", version, cleanedContainerID)
status, body, err := sockRequest("GET", endpoint, nil)
c.Assert(status, check.Equals, http.StatusOK)
c.Assert(err, check.IsNil)
var inspectJSON map[string]interface{}
if err = json.Unmarshal(body, &inspectJSON); err != nil {
c.Fatalf("unable to unmarshal body for version %s: %v", version, err)
}
config, ok := inspectJSON["Config"]
if !ok {
c.Fatal("Unable to find 'Config'")
}
cfg := config.(map[string]interface{})
if _, ok := cfg["VolumeDriver"]; !ok {
c.Fatalf("Api version %s expected to include VolumeDriver in 'Config'", version)
}
}
}
func (s *DockerSuite) TestInspectApiContainerVolumeDriver(c *check.C) {
out, _ := dockerCmd(c, "run", "-d", "busybox", "true")
cleanedContainerID := strings.TrimSpace(out)
endpoint := fmt.Sprintf("/v1.21/containers/%s/json", cleanedContainerID)
status, body, err := sockRequest("GET", endpoint, nil)
c.Assert(status, check.Equals, http.StatusOK)
c.Assert(err, check.IsNil)
var inspectJSON map[string]interface{}
if err = json.Unmarshal(body, &inspectJSON); err != nil {
c.Fatalf("unable to unmarshal body for version 1.21: %v", err)
}
config, ok := inspectJSON["Config"]
if !ok {
c.Fatal("Unable to find 'Config'")
}
cfg := config.(map[string]interface{})
if _, ok := cfg["VolumeDriver"]; ok {
c.Fatal("Api version 1.21 expected to not include VolumeDriver in 'Config'")
}
config, ok = inspectJSON["HostConfig"]
if !ok {
c.Fatal("Unable to find 'HostConfig'")
}
cfg = config.(map[string]interface{})
if _, ok := cfg["VolumeDriver"]; !ok {
c.Fatal("Api version 1.21 expected to include VolumeDriver in 'HostConfig'")
}
}

View file

@ -253,15 +253,11 @@ func (s *DockerExternalVolumeSuite) TestStartExternalNamedVolumeDriverCheckBindL
img := "test-checkbindlocalvolume" img := "test-checkbindlocalvolume"
args := []string{"--host", s.d.sock()} _, err := buildImageWithOutInDamon(s.d.sock(), img, dockerfile, true)
buildOut, err := buildImageArgs(args, img, dockerfile, true) c.Assert(err, check.IsNil)
fmt.Println(buildOut)
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") 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")
if err != nil { c.Assert(err, check.IsNil)
fmt.Println(out)
c.Fatal(err)
}
if !strings.Contains(out, expected) { if !strings.Contains(out, expected) {
c.Fatalf("External volume mount failed. Output: %s\n", out) c.Fatalf("External volume mount failed. Output: %s\n", out)

View file

@ -1392,22 +1392,14 @@ func createTmpFile(c *check.C, content string) string {
return filename return filename
} }
func buildImageArgs(args []string, name, dockerfile string, useCache bool) (string, error) { func buildImageWithOutInDamon(socket string, name, dockerfile string, useCache bool) (string, error) {
id, _, err := buildImageWithOutArgs(args, name, dockerfile, useCache) args := []string{"--host", socket}
return id, err
}
func buildImageWithOutArgs(args []string, name, dockerfile string, useCache bool) (string, string, error) {
buildCmd := buildImageCmdArgs(args, name, dockerfile, useCache) buildCmd := buildImageCmdArgs(args, name, dockerfile, useCache)
out, exitCode, err := runCommandWithOutput(buildCmd) out, exitCode, err := runCommandWithOutput(buildCmd)
if err != nil || exitCode != 0 { if err != nil || exitCode != 0 {
return "", out, fmt.Errorf("failed to build the image: %s", out) return out, fmt.Errorf("failed to build the image: %s, error: %v", out, err)
} }
id, err := getIDByName(name) return out, nil
if err != nil {
return "", out, err
}
return id, out, nil
} }
func buildImageCmdArgs(args []string, name, dockerfile string, useCache bool) *exec.Cmd { func buildImageCmdArgs(args []string, name, dockerfile string, useCache bool) *exec.Cmd {

View file

@ -28,7 +28,6 @@ type Config struct {
Cmd *stringutils.StrSlice // Command to run when starting the container Cmd *stringutils.StrSlice // Command to run when starting the container
Image string // Name of the image as it was passed by the operator (eg. could be symbolic) Image string // Name of the image as it was passed by the operator (eg. could be symbolic)
Volumes map[string]struct{} // List of volumes (mounts) used for the container Volumes map[string]struct{} // List of volumes (mounts) used for the container
VolumeDriver string // Name of the volume driver used to mount volumes
WorkingDir string // Current directory (PWD) in the command will be launched WorkingDir string // Current directory (PWD) in the command will be launched
Entrypoint *stringutils.StrSlice // Entrypoint to run when starting the container Entrypoint *stringutils.StrSlice // Entrypoint to run when starting the container
NetworkDisabled bool // Is network disabled NetworkDisabled bool // Is network disabled

View file

@ -32,12 +32,18 @@ func (w *ContainerConfigWrapper) getHostConfig() *HostConfig {
w.InnerHostConfig.CpusetCpus = hc.CpusetCpus w.InnerHostConfig.CpusetCpus = hc.CpusetCpus
} }
if hc.VolumeDriver != "" && w.InnerHostConfig.VolumeDriver == "" {
w.InnerHostConfig.VolumeDriver = hc.VolumeDriver
}
hc = w.InnerHostConfig hc = w.InnerHostConfig
} }
if hc != nil && w.Cpuset != "" && hc.CpusetCpus == "" { if hc != nil {
if w.Cpuset != "" && hc.CpusetCpus == "" {
hc.CpusetCpus = w.Cpuset hc.CpusetCpus = w.Cpuset
} }
}
// Make sure NetworkMode has an acceptable value. We do this to ensure // Make sure NetworkMode has an acceptable value. We do this to ensure
// backwards compatible API behaviour. // backwards compatible API behaviour.

View file

@ -251,6 +251,7 @@ type HostConfig struct {
LogConfig LogConfig // Configuration of the logs for this container LogConfig LogConfig // Configuration of the logs for this container
CgroupParent string // Parent cgroup. CgroupParent string // Parent cgroup.
ConsoleSize [2]int // Initial console size on Windows ConsoleSize [2]int // Initial console size on Windows
VolumeDriver string // Name of the volume driver used to mount volumes
} }
// DecodeHostConfig creates a HostConfig based on the specified Reader. // DecodeHostConfig creates a HostConfig based on the specified Reader.

View file

@ -322,7 +322,6 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
Entrypoint: entrypoint, Entrypoint: entrypoint,
WorkingDir: *flWorkingDir, WorkingDir: *flWorkingDir,
Labels: convertKVStringsToMap(labels), Labels: convertKVStringsToMap(labels),
VolumeDriver: *flVolumeDriver,
} }
hostConfig := &HostConfig{ hostConfig := &HostConfig{
@ -362,6 +361,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
Ulimits: flUlimits.GetList(), Ulimits: flUlimits.GetList(),
LogConfig: LogConfig{Type: *flLoggingDriver, Config: loggingOpts}, LogConfig: LogConfig{Type: *flLoggingDriver, Config: loggingOpts},
CgroupParent: *flCgroupParent, CgroupParent: *flCgroupParent,
VolumeDriver: *flVolumeDriver,
} }
applyExperimentalFlags(expFlags, config, hostConfig) applyExperimentalFlags(expFlags, config, hostConfig)