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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,9 +341,7 @@ func (container *Container) cleanup() {
|
||||||
logrus.Errorf("%s: Failed to umount ipc filesystems: %v", container.ID, err)
|
logrus.Errorf("%s: Failed to umount ipc filesystems: %v", container.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := container.Unmount(); err != nil {
|
container.conditionalUnmountOnCleanup()
|
||||||
logrus.Errorf("%s: Failed to umount filesystem: %v", container.ID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, eConfig := range container.execCommands.s {
|
for _, eConfig := range container.execCommands.s {
|
||||||
container.daemon.unregisterExecCommand(eConfig)
|
container.daemon.unregisterExecCommand(eConfig)
|
||||||
|
|
|
@ -1433,3 +1433,20 @@ func (container *Container) ipcMounts() []execdriver.Mount {
|
||||||
func detachMounted(path string) error {
|
func detachMounted(path string) error {
|
||||||
return syscall.Unmount(path, syscall.MNT_DETACH)
|
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 (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/daemon/execdriver"
|
"github.com/docker/docker/daemon/execdriver"
|
||||||
derr "github.com/docker/docker/errors"
|
derr "github.com/docker/docker/errors"
|
||||||
"github.com/docker/docker/volume"
|
"github.com/docker/docker/volume"
|
||||||
|
@ -144,6 +145,7 @@ func populateCommand(c *Container, env []string) error {
|
||||||
LayerFolder: layerFolder,
|
LayerFolder: layerFolder,
|
||||||
LayerPaths: layerPaths,
|
LayerPaths: layerPaths,
|
||||||
Hostname: c.Config.Hostname,
|
Hostname: c.Config.Hostname,
|
||||||
|
Isolated: c.hostConfig.Isolation.IsHyperV(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -194,3 +196,26 @@ func (container *Container) ipcMounts() []execdriver.Mount {
|
||||||
func getDefaultRouteMtu() (int, error) {
|
func getDefaultRouteMtu() (int, error) {
|
||||||
return -1, errSystemNotSupported
|
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
|
LayerPaths []string `json:"layer_paths"` // Windows needs to know the layer paths and folder for a command
|
||||||
LayerFolder string `json:"layer_folder"`
|
LayerFolder string `json:"layer_folder"`
|
||||||
Hostname string `json:"hostname"` // Windows sets the hostname in the execdriver
|
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.
|
ProcessorWeight int64 // CPU Shares 1..9 on Windows; or 0 is platform default.
|
||||||
HostName string // Hostname
|
HostName string // Hostname
|
||||||
MappedDirectories []mappedDir // List of mapped directories (volumes/mounts)
|
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
|
// 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,
|
LayerFolderPath: c.LayerFolder,
|
||||||
ProcessorWeight: c.Resources.CPUShares,
|
ProcessorWeight: c.Resources.CPUShares,
|
||||||
HostName: c.Hostname,
|
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 {
|
for _, layerPath := range c.LayerPaths {
|
||||||
|
|
|
@ -75,6 +75,10 @@ func DecodeContainerConfig(src io.Reader) (*Config, *HostConfig, error) {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate the isolation level
|
||||||
|
if err := ValidateIsolationLevel(hc); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
return w.Config, hc, nil
|
return w.Config, hc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,11 @@ package runconfig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/stringutils"
|
"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.
|
// NetworkMode represents the container network stack.
|
||||||
type NetworkMode string
|
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.
|
// IpcMode represents the container ipc stack.
|
||||||
type IpcMode string
|
type IpcMode string
|
||||||
|
|
||||||
|
@ -254,6 +264,7 @@ type HostConfig struct {
|
||||||
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
|
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.
|
// DecodeHostConfig creates a HostConfig based on the specified Reader.
|
||||||
|
|
|
@ -6,6 +6,11 @@ import (
|
||||||
"strings"
|
"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.
|
// IsPrivate indicates whether container uses it's private network stack.
|
||||||
func (n NetworkMode) IsPrivate() bool {
|
func (n NetworkMode) IsPrivate() bool {
|
||||||
return !(n.IsHost() || n.IsContainer())
|
return !(n.IsHost() || n.IsContainer())
|
||||||
|
|
|
@ -1,10 +1,23 @@
|
||||||
package runconfig
|
package runconfig
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
// IsDefault indicates whether container uses the default network stack.
|
// IsDefault indicates whether container uses the default network stack.
|
||||||
func (n NetworkMode) IsDefault() bool {
|
func (n NetworkMode) IsDefault() bool {
|
||||||
return n == "default"
|
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
|
// DefaultDaemonNetworkMode returns the default network stack the daemon should
|
||||||
// use.
|
// use.
|
||||||
func DefaultDaemonNetworkMode() NetworkMode {
|
func DefaultDaemonNetworkMode() NetworkMode {
|
||||||
|
|
|
@ -70,7 +70,6 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
|
||||||
flSecurityOpt = opts.NewListOpts(nil)
|
flSecurityOpt = opts.NewListOpts(nil)
|
||||||
flLabelsFile = opts.NewListOpts(nil)
|
flLabelsFile = opts.NewListOpts(nil)
|
||||||
flLoggingOpts = opts.NewListOpts(nil)
|
flLoggingOpts = opts.NewListOpts(nil)
|
||||||
|
|
||||||
flNetwork = cmd.Bool([]string{"#n", "#-networking"}, true, "Enable networking for this container")
|
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")
|
flPrivileged = cmd.Bool([]string{"#privileged", "-privileged"}, false, "Give extended privileges to this container")
|
||||||
flPidMode = cmd.String([]string{"-pid"}, "", "PID namespace to use")
|
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")
|
flCgroupParent = cmd.String([]string{"-cgroup-parent"}, "", "Optional parent cgroup for the container")
|
||||||
flVolumeDriver = cmd.String([]string{"-volume-driver"}, "", "Optional volume driver 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))
|
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")
|
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},
|
LogConfig: LogConfig{Type: *flLoggingDriver, Config: loggingOpts},
|
||||||
CgroupParent: *flCgroupParent,
|
CgroupParent: *flCgroupParent,
|
||||||
VolumeDriver: *flVolumeDriver,
|
VolumeDriver: *flVolumeDriver,
|
||||||
|
Isolation: IsolationLevel(*flIsolation),
|
||||||
}
|
}
|
||||||
|
|
||||||
// When allocating stdin in attached mode, close stdin at client disconnect
|
// When allocating stdin in attached mode, close stdin at client disconnect
|
||||||
|
|
|
@ -4,6 +4,7 @@ package runconfig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -58,3 +59,17 @@ func ValidateNetMode(c *Config, hc *HostConfig) error {
|
||||||
}
|
}
|
||||||
return nil
|
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
|
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
Reference in a new issue