mirror of
				https://github.com/moby/moby.git
				synced 2022-11-09 12:21:53 -05:00 
			
		
		
		
	Windows: Adds support for Hyper-V Containers
Signed-off-by: John Howard <jhoward@microsoft.com>
This commit is contained in:
		
							parent
							
								
									2eaa25d355
								
							
						
					
					
						commit
						15e35c4470
					
				
					 13 changed files with 192 additions and 20 deletions
				
			
		| 
						 | 
				
			
			@ -269,7 +269,7 @@ func (container *Container) Start() (err error) {
 | 
			
		|||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	if err := container.Mount(); err != nil {
 | 
			
		||||
	if err := container.conditionalMountOnStart(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -341,9 +341,7 @@ func (container *Container) cleanup() {
 | 
			
		|||
		logrus.Errorf("%s: Failed to umount ipc filesystems: %v", container.ID, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := container.Unmount(); err != nil {
 | 
			
		||||
		logrus.Errorf("%s: Failed to umount filesystem: %v", container.ID, err)
 | 
			
		||||
	}
 | 
			
		||||
	container.conditionalUnmountOnCleanup()
 | 
			
		||||
 | 
			
		||||
	for _, eConfig := range container.execCommands.s {
 | 
			
		||||
		container.daemon.unregisterExecCommand(eConfig)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1433,3 +1433,20 @@ func (container *Container) ipcMounts() []execdriver.Mount {
 | 
			
		|||
func detachMounted(path string) error {
 | 
			
		||||
	return syscall.Unmount(path, syscall.MNT_DETACH)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// conditionalMountOnStart is a platform specific helper function during the
 | 
			
		||||
// container start to call mount.
 | 
			
		||||
func (container *Container) conditionalMountOnStart() error {
 | 
			
		||||
	if err := container.Mount(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// conditionalUnmountOnCleanup is a platform specific helper function called
 | 
			
		||||
// during the cleanup of a container to unmount.
 | 
			
		||||
func (container *Container) conditionalUnmountOnCleanup() {
 | 
			
		||||
	if err := container.Unmount(); err != nil {
 | 
			
		||||
		logrus.Errorf("%v: Failed to umount filesystem: %v", container.ID, err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,6 +5,7 @@ package daemon
 | 
			
		|||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/docker/docker/daemon/execdriver"
 | 
			
		||||
	derr "github.com/docker/docker/errors"
 | 
			
		||||
	"github.com/docker/docker/volume"
 | 
			
		||||
| 
						 | 
				
			
			@ -144,6 +145,7 @@ func populateCommand(c *Container, env []string) error {
 | 
			
		|||
		LayerFolder:    layerFolder,
 | 
			
		||||
		LayerPaths:     layerPaths,
 | 
			
		||||
		Hostname:       c.Config.Hostname,
 | 
			
		||||
		Isolated:       c.hostConfig.Isolation.IsHyperV(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
| 
						 | 
				
			
			@ -194,3 +196,26 @@ func (container *Container) ipcMounts() []execdriver.Mount {
 | 
			
		|||
func getDefaultRouteMtu() (int, error) {
 | 
			
		||||
	return -1, errSystemNotSupported
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// conditionalMountOnStart is a platform specific helper function during the
 | 
			
		||||
// container start to call mount.
 | 
			
		||||
func (container *Container) conditionalMountOnStart() error {
 | 
			
		||||
	// We do not mount if a Hyper-V container
 | 
			
		||||
	if !container.hostConfig.Isolation.IsHyperV() {
 | 
			
		||||
		if err := container.Mount(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// conditionalUnmountOnCleanup is a platform specific helper function called
 | 
			
		||||
// during the cleanup of a container to unmount.
 | 
			
		||||
func (container *Container) conditionalUnmountOnCleanup() {
 | 
			
		||||
	// We do not unmount if a Hyper-V container
 | 
			
		||||
	if !container.hostConfig.Isolation.IsHyperV() {
 | 
			
		||||
		if err := container.Unmount(); err != nil {
 | 
			
		||||
			logrus.Errorf("%v: Failed to umount filesystem: %v", container.ID, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -210,4 +210,5 @@ type Command struct {
 | 
			
		|||
	LayerPaths         []string          `json:"layer_paths"` // Windows needs to know the layer paths and folder for a command
 | 
			
		||||
	LayerFolder        string            `json:"layer_folder"`
 | 
			
		||||
	Hostname           string            `json:"hostname"` // Windows sets the hostname in the execdriver
 | 
			
		||||
	Isolated           bool              `json:"isolated"` // Windows: Isolated is a Hyper-V container rather than Windows Server Container
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -77,6 +77,8 @@ type containerInit struct {
 | 
			
		|||
	ProcessorWeight         int64       // CPU Shares 1..9 on Windows; or 0 is platform default.
 | 
			
		||||
	HostName                string      // Hostname
 | 
			
		||||
	MappedDirectories       []mappedDir // List of mapped directories (volumes/mounts)
 | 
			
		||||
	SandboxPath             string      // Location of unmounted sandbox (used for Hyper-V containers, not Windows Server containers)
 | 
			
		||||
	HvPartition             bool        // True if it a Hyper-V Container
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// defaultOwner is a tag passed to HCS to allow it to differentiate between
 | 
			
		||||
| 
						 | 
				
			
			@ -108,6 +110,14 @@ func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, hooks execd
 | 
			
		|||
		LayerFolderPath:         c.LayerFolder,
 | 
			
		||||
		ProcessorWeight:         c.Resources.CPUShares,
 | 
			
		||||
		HostName:                c.Hostname,
 | 
			
		||||
		HvPartition:             c.Isolated,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Isolated {
 | 
			
		||||
		cu.SandboxPath = filepath.Dir(c.LayerFolder)
 | 
			
		||||
	} else {
 | 
			
		||||
		cu.VolumePath = c.Rootfs
 | 
			
		||||
		cu.LayerFolderPath = c.LayerFolder
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, layerPath := range c.LayerPaths {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -75,6 +75,10 @@ func DecodeContainerConfig(src io.Reader) (*Config, *HostConfig, error) {
 | 
			
		|||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Validate the isolation level
 | 
			
		||||
	if err := ValidateIsolationLevel(hc); err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return w.Config, hc, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,9 +2,11 @@ package runconfig
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/docker/docker/pkg/stringutils"
 | 
			
		||||
| 
						 | 
				
			
			@ -60,3 +62,58 @@ func TestDecodeContainerConfig(t *testing.T) {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TestDecodeContainerConfigIsolation validates the isolation level passed
 | 
			
		||||
// to the daemon in the hostConfig structure. Note this is platform specific
 | 
			
		||||
// as to what level of container isolation is supported.
 | 
			
		||||
func TestDecodeContainerConfigIsolation(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
	// An invalid isolation level
 | 
			
		||||
	if _, _, err := callDecodeContainerConfigIsolation("invalid"); err != nil {
 | 
			
		||||
		if !strings.Contains(err.Error(), `invalid --isolation: "invalid"`) {
 | 
			
		||||
			t.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Blank isolation level (== default)
 | 
			
		||||
	if _, _, err := callDecodeContainerConfigIsolation(""); err != nil {
 | 
			
		||||
		t.Fatal("Blank isolation should have succeeded")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Default isolation level
 | 
			
		||||
	if _, _, err := callDecodeContainerConfigIsolation("default"); err != nil {
 | 
			
		||||
		t.Fatal("default isolation should have succeeded")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Hyper-V Containers isolation level (Valid on Windows only)
 | 
			
		||||
	if runtime.GOOS == "windows" {
 | 
			
		||||
		if _, _, err := callDecodeContainerConfigIsolation("hyperv"); err != nil {
 | 
			
		||||
			t.Fatal("hyperv isolation should have succeeded")
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if _, _, err := callDecodeContainerConfigIsolation("hyperv"); err != nil {
 | 
			
		||||
			if !strings.Contains(err.Error(), `invalid --isolation: "hyperv"`) {
 | 
			
		||||
				t.Fatal(err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// callDecodeContainerConfigIsolation is a utility function to call
 | 
			
		||||
// DecodeContainerConfig for validating isolation levels
 | 
			
		||||
func callDecodeContainerConfigIsolation(isolation string) (*Config, *HostConfig, error) {
 | 
			
		||||
	var (
 | 
			
		||||
		b   []byte
 | 
			
		||||
		err error
 | 
			
		||||
	)
 | 
			
		||||
	w := ContainerConfigWrapper{
 | 
			
		||||
		Config: &Config{},
 | 
			
		||||
		HostConfig: &HostConfig{
 | 
			
		||||
			NetworkMode: "none",
 | 
			
		||||
			Isolation:   IsolationLevel(isolation)},
 | 
			
		||||
	}
 | 
			
		||||
	if b, err = json.Marshal(w); err != nil {
 | 
			
		||||
		return nil, nil, fmt.Errorf("Error on marshal %s", err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	return DecodeContainerConfig(bytes.NewReader(b))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,6 +19,16 @@ type KeyValuePair struct {
 | 
			
		|||
// NetworkMode represents the container network stack.
 | 
			
		||||
type NetworkMode string
 | 
			
		||||
 | 
			
		||||
// IsolationLevel represents the isolation level of a container. The supported
 | 
			
		||||
// values are platform specific
 | 
			
		||||
type IsolationLevel string
 | 
			
		||||
 | 
			
		||||
// IsDefault indicates the default isolation level of a container. On Linux this
 | 
			
		||||
// is LXC. On Windows, this is a Windows Server Container.
 | 
			
		||||
func (i IsolationLevel) IsDefault() bool {
 | 
			
		||||
	return strings.ToLower(string(i)) == "default" || string(i) == ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IpcMode represents the container ipc stack.
 | 
			
		||||
type IpcMode string
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -254,6 +264,7 @@ type HostConfig struct {
 | 
			
		|||
	CgroupParent      string                // Parent cgroup.
 | 
			
		||||
	ConsoleSize       [2]int                // Initial console size on Windows
 | 
			
		||||
	VolumeDriver      string                // Name of the volume driver used to mount volumes
 | 
			
		||||
	Isolation         IsolationLevel        // Isolation level of the container (eg default, hyperv)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DecodeHostConfig creates a HostConfig based on the specified Reader.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,11 @@ import (
 | 
			
		|||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// IsValid indicates is an isolation level is valid
 | 
			
		||||
func (i IsolationLevel) IsValid() bool {
 | 
			
		||||
	return i.IsDefault()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsPrivate indicates whether container uses it's private network stack.
 | 
			
		||||
func (n NetworkMode) IsPrivate() bool {
 | 
			
		||||
	return !(n.IsHost() || n.IsContainer())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,23 @@
 | 
			
		|||
package runconfig
 | 
			
		||||
 | 
			
		||||
import "strings"
 | 
			
		||||
 | 
			
		||||
// IsDefault indicates whether container uses the default network stack.
 | 
			
		||||
func (n NetworkMode) IsDefault() bool {
 | 
			
		||||
	return n == "default"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsHyperV indicates the use of Hyper-V Containers for isolation (as opposed
 | 
			
		||||
// to Windows Server Containers
 | 
			
		||||
func (i IsolationLevel) IsHyperV() bool {
 | 
			
		||||
	return strings.ToLower(string(i)) == "hyperv"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsValid indicates is an isolation level is valid
 | 
			
		||||
func (i IsolationLevel) IsValid() bool {
 | 
			
		||||
	return i.IsDefault() || i.IsHyperV()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DefaultDaemonNetworkMode returns the default network stack the daemon should
 | 
			
		||||
// use.
 | 
			
		||||
func DefaultDaemonNetworkMode() NetworkMode {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -55,22 +55,21 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
 | 
			
		|||
 | 
			
		||||
		flUlimits = opts.NewUlimitOpt(nil)
 | 
			
		||||
 | 
			
		||||
		flPublish     = opts.NewListOpts(nil)
 | 
			
		||||
		flExpose      = opts.NewListOpts(nil)
 | 
			
		||||
		flDNS         = opts.NewListOpts(opts.ValidateIPAddress)
 | 
			
		||||
		flDNSSearch   = opts.NewListOpts(opts.ValidateDNSSearch)
 | 
			
		||||
		flDNSOptions  = opts.NewListOpts(nil)
 | 
			
		||||
		flExtraHosts  = opts.NewListOpts(opts.ValidateExtraHost)
 | 
			
		||||
		flVolumesFrom = opts.NewListOpts(nil)
 | 
			
		||||
		flLxcOpts     = opts.NewListOpts(nil)
 | 
			
		||||
		flEnvFile     = opts.NewListOpts(nil)
 | 
			
		||||
		flCapAdd      = opts.NewListOpts(nil)
 | 
			
		||||
		flCapDrop     = opts.NewListOpts(nil)
 | 
			
		||||
		flGroupAdd    = opts.NewListOpts(nil)
 | 
			
		||||
		flSecurityOpt = opts.NewListOpts(nil)
 | 
			
		||||
		flLabelsFile  = opts.NewListOpts(nil)
 | 
			
		||||
		flLoggingOpts = opts.NewListOpts(nil)
 | 
			
		||||
 | 
			
		||||
		flPublish           = opts.NewListOpts(nil)
 | 
			
		||||
		flExpose            = opts.NewListOpts(nil)
 | 
			
		||||
		flDNS               = opts.NewListOpts(opts.ValidateIPAddress)
 | 
			
		||||
		flDNSSearch         = opts.NewListOpts(opts.ValidateDNSSearch)
 | 
			
		||||
		flDNSOptions        = opts.NewListOpts(nil)
 | 
			
		||||
		flExtraHosts        = opts.NewListOpts(opts.ValidateExtraHost)
 | 
			
		||||
		flVolumesFrom       = opts.NewListOpts(nil)
 | 
			
		||||
		flLxcOpts           = opts.NewListOpts(nil)
 | 
			
		||||
		flEnvFile           = opts.NewListOpts(nil)
 | 
			
		||||
		flCapAdd            = opts.NewListOpts(nil)
 | 
			
		||||
		flCapDrop           = opts.NewListOpts(nil)
 | 
			
		||||
		flGroupAdd          = opts.NewListOpts(nil)
 | 
			
		||||
		flSecurityOpt       = opts.NewListOpts(nil)
 | 
			
		||||
		flLabelsFile        = opts.NewListOpts(nil)
 | 
			
		||||
		flLoggingOpts       = opts.NewListOpts(nil)
 | 
			
		||||
		flNetwork           = cmd.Bool([]string{"#n", "#-networking"}, true, "Enable networking for this container")
 | 
			
		||||
		flPrivileged        = cmd.Bool([]string{"#privileged", "-privileged"}, false, "Give extended privileges to this container")
 | 
			
		||||
		flPidMode           = cmd.String([]string{"-pid"}, "", "PID namespace to use")
 | 
			
		||||
| 
						 | 
				
			
			@ -104,6 +103,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
 | 
			
		|||
		flCgroupParent      = cmd.String([]string{"-cgroup-parent"}, "", "Optional parent cgroup for the container")
 | 
			
		||||
		flVolumeDriver      = cmd.String([]string{"-volume-driver"}, "", "Optional volume driver for the container")
 | 
			
		||||
		flStopSignal        = cmd.String([]string{"-stop-signal"}, signal.DefaultStopSignal, fmt.Sprintf("Signal to stop a container, %v by default", signal.DefaultStopSignal))
 | 
			
		||||
		flIsolation         = cmd.String([]string{"-isolation"}, "default", "Container isolation level")
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to STDIN, STDOUT or STDERR")
 | 
			
		||||
| 
						 | 
				
			
			@ -377,6 +377,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
 | 
			
		|||
		LogConfig:         LogConfig{Type: *flLoggingDriver, Config: loggingOpts},
 | 
			
		||||
		CgroupParent:      *flCgroupParent,
 | 
			
		||||
		VolumeDriver:      *flVolumeDriver,
 | 
			
		||||
		Isolation:         IsolationLevel(*flIsolation),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// When allocating stdin in attached mode, close stdin at client disconnect
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,7 @@ package runconfig
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -58,3 +59,17 @@ func ValidateNetMode(c *Config, hc *HostConfig) error {
 | 
			
		|||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ValidateIsolationLevel performs platform specific validation of the
 | 
			
		||||
// isolation level in the hostconfig structure. Linux only supports "default"
 | 
			
		||||
// which is LXC container isolation
 | 
			
		||||
func ValidateIsolationLevel(hc *HostConfig) error {
 | 
			
		||||
	// We may not be passed a host config, such as in the case of docker commit
 | 
			
		||||
	if hc == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if !hc.Isolation.IsValid() {
 | 
			
		||||
		return fmt.Errorf("invalid --isolation: %q - %s only supports 'default'", hc.Isolation, runtime.GOOS)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,3 +20,18 @@ func ValidateNetMode(c *Config, hc *HostConfig) error {
 | 
			
		|||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ValidateIsolationLevel performs platform specific validation of the
 | 
			
		||||
// isolation level in the hostconfig structure. Windows supports 'default' (or
 | 
			
		||||
// blank), and 'hyperv'. These refer to Windows Server Containers and
 | 
			
		||||
// Hyper-V Containers respectively.
 | 
			
		||||
func ValidateIsolationLevel(hc *HostConfig) error {
 | 
			
		||||
	// We may not be passed a host config, such as in the case of docker commit
 | 
			
		||||
	if hc == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if !hc.Isolation.IsValid() {
 | 
			
		||||
		return fmt.Errorf("invalid --isolation: %q. Windows supports 'default' (Windows Server Container) or 'hyperv' (Hyper-V Container)", hc.Isolation)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue