Merge pull request #15798 from calavera/volume_driver_host_config
Move VolumeDriver to HostConfig to make containers portable.
This commit is contained in:
commit
9ca4aa4797
22 changed files with 181 additions and 78 deletions
|
@ -4,7 +4,6 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -20,22 +19,6 @@ import (
|
|||
"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 {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
|
|
33
api/server/inspect.go
Normal file
33
api/server/inspect.go
Normal 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)
|
||||
}
|
|
@ -103,16 +103,6 @@ func allocateDaemonPort(addr string) error {
|
|||
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
|
||||
// net.Listeners or all of the activated files if "*" is given.
|
||||
func listenFD(addr string) ([]net.Listener, error) {
|
||||
|
|
|
@ -56,9 +56,3 @@ func (s *Server) AcceptConnections(d *daemon.Daemon) {
|
|||
func allocateDaemonPort(addr string) error {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -274,24 +274,39 @@ type ContainerJSON struct {
|
|||
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.
|
||||
type ContainerJSONPre120 struct {
|
||||
*ContainerJSONBase
|
||||
Volumes map[string]string
|
||||
VolumesRW map[string]bool
|
||||
Config *ContainerConfig
|
||||
Config *ContainerConfigPre120
|
||||
}
|
||||
|
||||
// ContainerConfig is a backcompatibility struct used in ContainerJSONPre120
|
||||
type ContainerConfig struct {
|
||||
// ContainerConfigPre120 is a backcompatibility struct used in ContainerJSONPre120
|
||||
type ContainerConfigPre120 struct {
|
||||
*runconfig.Config
|
||||
|
||||
// backward compatibility, they now live in HostConfig
|
||||
Memory int64
|
||||
MemorySwap int64
|
||||
CPUShares int64 `json:"CpuShares"`
|
||||
CPUSet string `json:"CpuSet"`
|
||||
VolumeDriver string
|
||||
Memory int64
|
||||
MemorySwap int64
|
||||
CPUShares int64 `json:"CpuShares"`
|
||||
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.
|
||||
|
|
|
@ -106,7 +106,7 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos
|
|||
}
|
||||
defer container.Unmount()
|
||||
|
||||
if err := createContainerPlatformSpecificSettings(container, config, img); err != nil {
|
||||
if err := createContainerPlatformSpecificSettings(container, config, hostConfig, img); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
)
|
||||
|
||||
// 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 {
|
||||
var (
|
||||
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)
|
||||
}
|
||||
|
||||
volumeDriver := config.VolumeDriver
|
||||
volumeDriver := hostConfig.VolumeDriver
|
||||
if destination != "" && img != nil {
|
||||
if _, ok := img.ContainerConfig.Volumes[destination]; ok {
|
||||
// check for whether bind is not specified and then set to local
|
||||
|
|
|
@ -6,6 +6,6 @@ import (
|
|||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
|
|
@ -29,6 +29,30 @@ func (daemon *Daemon) ContainerInspect(name string) (*types.ContainerJSON, error
|
|||
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) {
|
||||
// make a copy to play with
|
||||
hostConfig := *container.hostConfig
|
||||
|
|
|
@ -14,7 +14,7 @@ func setPlatformSpecificContainerFields(container *Container, contJSONBase *type
|
|||
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) {
|
||||
container, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
|
@ -36,8 +36,9 @@ func (daemon *Daemon) ContainerInspectPre120(name string) (*types.ContainerJSONP
|
|||
volumesRW[m.Destination] = m.RW
|
||||
}
|
||||
|
||||
config := &types.ContainerConfig{
|
||||
config := &types.ContainerConfigPre120{
|
||||
container.Config,
|
||||
container.hostConfig.VolumeDriver,
|
||||
container.hostConfig.Memory,
|
||||
container.hostConfig.MemorySwap,
|
||||
container.hostConfig.CPUShares,
|
||||
|
|
|
@ -10,3 +10,8 @@ func setPlatformSpecificContainerFields(container *Container, contJSONBase *type
|
|||
func addMountPoints(container *Container) []types.MountPoint {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContainerInspectPre120 get containers for pre 1.20 APIs.
|
||||
func (daemon *Daemon) ContainerInspectPre120(name string) (*types.ContainerJSON, error) {
|
||||
return daemon.ContainerInspect(name)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ package daemon
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/docker/volume"
|
||||
"github.com/docker/docker/volume/drivers"
|
||||
)
|
||||
|
@ -56,8 +55,7 @@ func TestParseBindMount(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, c := range cases {
|
||||
conf := &runconfig.Config{VolumeDriver: c.driver}
|
||||
m, err := parseBindMount(c.bind, c.mountLabel, conf)
|
||||
m, err := parseBindMount(c.bind, c.mountLabel, c.driver)
|
||||
if c.fail {
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error, was nil, for spec %s\n", c.bind)
|
||||
|
|
|
@ -59,7 +59,7 @@ func (container *Container) setupMounts() ([]execdriver.Mount, error) {
|
|||
}
|
||||
|
||||
// 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{
|
||||
RW: true,
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ func parseBindMount(spec string, mountLabel string, config *runconfig.Config) (*
|
|||
}
|
||||
|
||||
if len(source) == 0 {
|
||||
bind.Driver = config.VolumeDriver
|
||||
bind.Driver = volumeDriver
|
||||
if len(bind.Driver) == 0 {
|
||||
bind.Driver = volume.DefaultDriverName
|
||||
}
|
||||
|
@ -330,7 +330,7 @@ func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runc
|
|||
// 3. Read bind mounts
|
||||
for _, b := range hostConfig.Binds {
|
||||
// #10618
|
||||
bind, err := parseBindMount(b, container.MountLabel, container.Config)
|
||||
bind, err := parseBindMount(b, container.MountLabel, hostConfig.VolumeDriver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ This section lists each version from latest to oldest. Each listing includes a
|
|||
* `POST /volumes` to create a volume.
|
||||
* `GET /volumes/(name)` get low-level information about a volume.
|
||||
* `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
|
||||
|
|
|
@ -196,7 +196,8 @@ Create a container
|
|||
"Ulimits": [{}],
|
||||
"LogConfig": { "Type": "json-file", "Config": {} },
|
||||
"SecurityOpt": [""],
|
||||
"CgroupParent": ""
|
||||
"CgroupParent": "",
|
||||
"VolumeDriver": ""
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -300,6 +301,7 @@ Json Parameters:
|
|||
Available types: `json-file`, `syslog`, `journald`, `gelf`, `none`.
|
||||
`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.
|
||||
- **VolumeDriver** - Driver that this container users to mount volumes.
|
||||
|
||||
Query Parameters:
|
||||
|
||||
|
@ -407,7 +409,8 @@ Return low-level information on the container `id`
|
|||
},
|
||||
"SecurityOpt": null,
|
||||
"VolumesFrom": null,
|
||||
"Ulimits": [{}]
|
||||
"Ulimits": [{}],
|
||||
"VolumeDriver": ""
|
||||
},
|
||||
"HostnamePath": "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname",
|
||||
"HostsPath": "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts",
|
||||
|
|
|
@ -49,3 +49,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'")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -253,15 +253,11 @@ func (s *DockerExternalVolumeSuite) TestStartExternalNamedVolumeDriverCheckBindL
|
|||
|
||||
img := "test-checkbindlocalvolume"
|
||||
|
||||
args := []string{"--host", s.d.sock()}
|
||||
buildOut, err := buildImageArgs(args, img, dockerfile, true)
|
||||
fmt.Println(buildOut)
|
||||
_, err := buildImageWithOutInDamon(s.d.sock(), img, dockerfile, true)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
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 {
|
||||
fmt.Println(out)
|
||||
c.Fatal(err)
|
||||
}
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
if !strings.Contains(out, expected) {
|
||||
c.Fatalf("External volume mount failed. Output: %s\n", out)
|
||||
|
|
|
@ -1382,22 +1382,14 @@ func createTmpFile(c *check.C, content string) string {
|
|||
return filename
|
||||
}
|
||||
|
||||
func buildImageArgs(args []string, name, dockerfile string, useCache bool) (string, error) {
|
||||
id, _, err := buildImageWithOutArgs(args, name, dockerfile, useCache)
|
||||
return id, err
|
||||
}
|
||||
|
||||
func buildImageWithOutArgs(args []string, name, dockerfile string, useCache bool) (string, string, error) {
|
||||
func buildImageWithOutInDamon(socket string, name, dockerfile string, useCache bool) (string, error) {
|
||||
args := []string{"--host", socket}
|
||||
buildCmd := buildImageCmdArgs(args, name, dockerfile, useCache)
|
||||
out, exitCode, err := runCommandWithOutput(buildCmd)
|
||||
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)
|
||||
if err != nil {
|
||||
return "", out, err
|
||||
}
|
||||
return id, out, nil
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func buildImageCmdArgs(args []string, name, dockerfile string, useCache bool) *exec.Cmd {
|
||||
|
|
|
@ -28,7 +28,6 @@ type Config struct {
|
|||
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)
|
||||
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
|
||||
Entrypoint *stringutils.StrSlice // Entrypoint to run when starting the container
|
||||
NetworkDisabled bool // Is network disabled
|
||||
|
|
|
@ -32,11 +32,17 @@ func (w *ContainerConfigWrapper) getHostConfig() *HostConfig {
|
|||
w.InnerHostConfig.CpusetCpus = hc.CpusetCpus
|
||||
}
|
||||
|
||||
if hc.VolumeDriver != "" && w.InnerHostConfig.VolumeDriver == "" {
|
||||
w.InnerHostConfig.VolumeDriver = hc.VolumeDriver
|
||||
}
|
||||
|
||||
hc = w.InnerHostConfig
|
||||
}
|
||||
|
||||
if hc != nil && w.Cpuset != "" && hc.CpusetCpus == "" {
|
||||
hc.CpusetCpus = w.Cpuset
|
||||
if hc != nil {
|
||||
if w.Cpuset != "" && hc.CpusetCpus == "" {
|
||||
hc.CpusetCpus = w.Cpuset
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure NetworkMode has an acceptable value. We do this to ensure
|
||||
|
|
|
@ -251,6 +251,7 @@ type HostConfig struct {
|
|||
LogConfig LogConfig // Configuration of the logs for this container
|
||||
CgroupParent string // Parent cgroup.
|
||||
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.
|
||||
|
|
|
@ -322,7 +322,6 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
|
|||
Entrypoint: entrypoint,
|
||||
WorkingDir: *flWorkingDir,
|
||||
Labels: convertKVStringsToMap(labels),
|
||||
VolumeDriver: *flVolumeDriver,
|
||||
}
|
||||
|
||||
hostConfig := &HostConfig{
|
||||
|
@ -362,6 +361,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
|
|||
Ulimits: flUlimits.GetList(),
|
||||
LogConfig: LogConfig{Type: *flLoggingDriver, Config: loggingOpts},
|
||||
CgroupParent: *flCgroupParent,
|
||||
VolumeDriver: *flVolumeDriver,
|
||||
}
|
||||
|
||||
applyExperimentalFlags(expFlags, config, hostConfig)
|
||||
|
|
Loading…
Reference in a new issue