Signed-off-by: John Howard <jhoward@microsoft.com>
| ... | ... |
@@ -269,7 +269,7 @@ func (container *Container) Start() (err error) {
|
| 269 | 269 |
} |
| 270 | 270 |
}() |
| 271 | 271 |
|
| 272 |
- if err := container.Mount(); err != nil {
|
|
| 272 |
+ if err := container.conditionalMountOnStart(); err != nil {
|
|
| 273 | 273 |
return err |
| 274 | 274 |
} |
| 275 | 275 |
|
| ... | ... |
@@ -341,9 +341,7 @@ func (container *Container) cleanup() {
|
| 341 | 341 |
logrus.Errorf("%s: Failed to umount ipc filesystems: %v", container.ID, err)
|
| 342 | 342 |
} |
| 343 | 343 |
|
| 344 |
- if err := container.Unmount(); err != nil {
|
|
| 345 |
- logrus.Errorf("%s: Failed to umount filesystem: %v", container.ID, err)
|
|
| 346 |
- } |
|
| 344 |
+ container.conditionalUnmountOnCleanup() |
|
| 347 | 345 |
|
| 348 | 346 |
for _, eConfig := range container.execCommands.s {
|
| 349 | 347 |
container.daemon.unregisterExecCommand(eConfig) |
| ... | ... |
@@ -1433,3 +1433,20 @@ func (container *Container) ipcMounts() []execdriver.Mount {
|
| 1433 | 1433 |
func detachMounted(path string) error {
|
| 1434 | 1434 |
return syscall.Unmount(path, syscall.MNT_DETACH) |
| 1435 | 1435 |
} |
| 1436 |
+ |
|
| 1437 |
+// conditionalMountOnStart is a platform specific helper function during the |
|
| 1438 |
+// container start to call mount. |
|
| 1439 |
+func (container *Container) conditionalMountOnStart() error {
|
|
| 1440 |
+ if err := container.Mount(); err != nil {
|
|
| 1441 |
+ return err |
|
| 1442 |
+ } |
|
| 1443 |
+ return nil |
|
| 1444 |
+} |
|
| 1445 |
+ |
|
| 1446 |
+// conditionalUnmountOnCleanup is a platform specific helper function called |
|
| 1447 |
+// during the cleanup of a container to unmount. |
|
| 1448 |
+func (container *Container) conditionalUnmountOnCleanup() {
|
|
| 1449 |
+ if err := container.Unmount(); err != nil {
|
|
| 1450 |
+ logrus.Errorf("%v: Failed to umount filesystem: %v", container.ID, err)
|
|
| 1451 |
+ } |
|
| 1452 |
+} |
| ... | ... |
@@ -5,6 +5,7 @@ package daemon |
| 5 | 5 |
import ( |
| 6 | 6 |
"strings" |
| 7 | 7 |
|
| 8 |
+ "github.com/Sirupsen/logrus" |
|
| 8 | 9 |
"github.com/docker/docker/daemon/execdriver" |
| 9 | 10 |
derr "github.com/docker/docker/errors" |
| 10 | 11 |
"github.com/docker/docker/volume" |
| ... | ... |
@@ -144,6 +145,7 @@ func populateCommand(c *Container, env []string) error {
|
| 144 | 144 |
LayerFolder: layerFolder, |
| 145 | 145 |
LayerPaths: layerPaths, |
| 146 | 146 |
Hostname: c.Config.Hostname, |
| 147 |
+ Isolated: c.hostConfig.Isolation.IsHyperV(), |
|
| 147 | 148 |
} |
| 148 | 149 |
|
| 149 | 150 |
return nil |
| ... | ... |
@@ -194,3 +196,26 @@ func (container *Container) ipcMounts() []execdriver.Mount {
|
| 194 | 194 |
func getDefaultRouteMtu() (int, error) {
|
| 195 | 195 |
return -1, errSystemNotSupported |
| 196 | 196 |
} |
| 197 |
+ |
|
| 198 |
+// conditionalMountOnStart is a platform specific helper function during the |
|
| 199 |
+// container start to call mount. |
|
| 200 |
+func (container *Container) conditionalMountOnStart() error {
|
|
| 201 |
+ // We do not mount if a Hyper-V container |
|
| 202 |
+ if !container.hostConfig.Isolation.IsHyperV() {
|
|
| 203 |
+ if err := container.Mount(); err != nil {
|
|
| 204 |
+ return err |
|
| 205 |
+ } |
|
| 206 |
+ } |
|
| 207 |
+ return nil |
|
| 208 |
+} |
|
| 209 |
+ |
|
| 210 |
+// conditionalUnmountOnCleanup is a platform specific helper function called |
|
| 211 |
+// during the cleanup of a container to unmount. |
|
| 212 |
+func (container *Container) conditionalUnmountOnCleanup() {
|
|
| 213 |
+ // We do not unmount if a Hyper-V container |
|
| 214 |
+ if !container.hostConfig.Isolation.IsHyperV() {
|
|
| 215 |
+ if err := container.Unmount(); err != nil {
|
|
| 216 |
+ logrus.Errorf("%v: Failed to umount filesystem: %v", container.ID, err)
|
|
| 217 |
+ } |
|
| 218 |
+ } |
|
| 219 |
+} |
| ... | ... |
@@ -210,4 +210,5 @@ type Command struct {
|
| 210 | 210 |
LayerPaths []string `json:"layer_paths"` // Windows needs to know the layer paths and folder for a command |
| 211 | 211 |
LayerFolder string `json:"layer_folder"` |
| 212 | 212 |
Hostname string `json:"hostname"` // Windows sets the hostname in the execdriver |
| 213 |
+ Isolated bool `json:"isolated"` // Windows: Isolated is a Hyper-V container rather than Windows Server Container |
|
| 213 | 214 |
} |
| ... | ... |
@@ -77,6 +77,8 @@ type containerInit struct {
|
| 77 | 77 |
ProcessorWeight int64 // CPU Shares 1..9 on Windows; or 0 is platform default. |
| 78 | 78 |
HostName string // Hostname |
| 79 | 79 |
MappedDirectories []mappedDir // List of mapped directories (volumes/mounts) |
| 80 |
+ SandboxPath string // Location of unmounted sandbox (used for Hyper-V containers, not Windows Server containers) |
|
| 81 |
+ HvPartition bool // True if it a Hyper-V Container |
|
| 80 | 82 |
} |
| 81 | 83 |
|
| 82 | 84 |
// 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 |
| 108 | 108 |
LayerFolderPath: c.LayerFolder, |
| 109 | 109 |
ProcessorWeight: c.Resources.CPUShares, |
| 110 | 110 |
HostName: c.Hostname, |
| 111 |
+ HvPartition: c.Isolated, |
|
| 112 |
+ } |
|
| 113 |
+ |
|
| 114 |
+ if c.Isolated {
|
|
| 115 |
+ cu.SandboxPath = filepath.Dir(c.LayerFolder) |
|
| 116 |
+ } else {
|
|
| 117 |
+ cu.VolumePath = c.Rootfs |
|
| 118 |
+ cu.LayerFolderPath = c.LayerFolder |
|
| 111 | 119 |
} |
| 112 | 120 |
|
| 113 | 121 |
for _, layerPath := range c.LayerPaths {
|
| ... | ... |
@@ -75,6 +75,10 @@ func DecodeContainerConfig(src io.Reader) (*Config, *HostConfig, error) {
|
| 75 | 75 |
return nil, nil, err |
| 76 | 76 |
} |
| 77 | 77 |
|
| 78 |
+ // Validate the isolation level |
|
| 79 |
+ if err := ValidateIsolationLevel(hc); err != nil {
|
|
| 80 |
+ return nil, nil, err |
|
| 81 |
+ } |
|
| 78 | 82 |
return w.Config, hc, nil |
| 79 | 83 |
} |
| 80 | 84 |
|
| ... | ... |
@@ -2,9 +2,11 @@ package runconfig |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"bytes" |
| 5 |
+ "encoding/json" |
|
| 5 | 6 |
"fmt" |
| 6 | 7 |
"io/ioutil" |
| 7 | 8 |
"runtime" |
| 9 |
+ "strings" |
|
| 8 | 10 |
"testing" |
| 9 | 11 |
|
| 10 | 12 |
"github.com/docker/docker/pkg/stringutils" |
| ... | ... |
@@ -60,3 +62,58 @@ func TestDecodeContainerConfig(t *testing.T) {
|
| 60 | 60 |
} |
| 61 | 61 |
} |
| 62 | 62 |
} |
| 63 |
+ |
|
| 64 |
+// TestDecodeContainerConfigIsolation validates the isolation level passed |
|
| 65 |
+// to the daemon in the hostConfig structure. Note this is platform specific |
|
| 66 |
+// as to what level of container isolation is supported. |
|
| 67 |
+func TestDecodeContainerConfigIsolation(t *testing.T) {
|
|
| 68 |
+ |
|
| 69 |
+ // An invalid isolation level |
|
| 70 |
+ if _, _, err := callDecodeContainerConfigIsolation("invalid"); err != nil {
|
|
| 71 |
+ if !strings.Contains(err.Error(), `invalid --isolation: "invalid"`) {
|
|
| 72 |
+ t.Fatal(err) |
|
| 73 |
+ } |
|
| 74 |
+ } |
|
| 75 |
+ |
|
| 76 |
+ // Blank isolation level (== default) |
|
| 77 |
+ if _, _, err := callDecodeContainerConfigIsolation(""); err != nil {
|
|
| 78 |
+ t.Fatal("Blank isolation should have succeeded")
|
|
| 79 |
+ } |
|
| 80 |
+ |
|
| 81 |
+ // Default isolation level |
|
| 82 |
+ if _, _, err := callDecodeContainerConfigIsolation("default"); err != nil {
|
|
| 83 |
+ t.Fatal("default isolation should have succeeded")
|
|
| 84 |
+ } |
|
| 85 |
+ |
|
| 86 |
+ // Hyper-V Containers isolation level (Valid on Windows only) |
|
| 87 |
+ if runtime.GOOS == "windows" {
|
|
| 88 |
+ if _, _, err := callDecodeContainerConfigIsolation("hyperv"); err != nil {
|
|
| 89 |
+ t.Fatal("hyperv isolation should have succeeded")
|
|
| 90 |
+ } |
|
| 91 |
+ } else {
|
|
| 92 |
+ if _, _, err := callDecodeContainerConfigIsolation("hyperv"); err != nil {
|
|
| 93 |
+ if !strings.Contains(err.Error(), `invalid --isolation: "hyperv"`) {
|
|
| 94 |
+ t.Fatal(err) |
|
| 95 |
+ } |
|
| 96 |
+ } |
|
| 97 |
+ } |
|
| 98 |
+} |
|
| 99 |
+ |
|
| 100 |
+// callDecodeContainerConfigIsolation is a utility function to call |
|
| 101 |
+// DecodeContainerConfig for validating isolation levels |
|
| 102 |
+func callDecodeContainerConfigIsolation(isolation string) (*Config, *HostConfig, error) {
|
|
| 103 |
+ var ( |
|
| 104 |
+ b []byte |
|
| 105 |
+ err error |
|
| 106 |
+ ) |
|
| 107 |
+ w := ContainerConfigWrapper{
|
|
| 108 |
+ Config: &Config{},
|
|
| 109 |
+ HostConfig: &HostConfig{
|
|
| 110 |
+ NetworkMode: "none", |
|
| 111 |
+ Isolation: IsolationLevel(isolation)}, |
|
| 112 |
+ } |
|
| 113 |
+ if b, err = json.Marshal(w); err != nil {
|
|
| 114 |
+ return nil, nil, fmt.Errorf("Error on marshal %s", err.Error())
|
|
| 115 |
+ } |
|
| 116 |
+ return DecodeContainerConfig(bytes.NewReader(b)) |
|
| 117 |
+} |
| ... | ... |
@@ -19,6 +19,16 @@ type KeyValuePair struct {
|
| 19 | 19 |
// NetworkMode represents the container network stack. |
| 20 | 20 |
type NetworkMode string |
| 21 | 21 |
|
| 22 |
+// IsolationLevel represents the isolation level of a container. The supported |
|
| 23 |
+// values are platform specific |
|
| 24 |
+type IsolationLevel string |
|
| 25 |
+ |
|
| 26 |
+// IsDefault indicates the default isolation level of a container. On Linux this |
|
| 27 |
+// is LXC. On Windows, this is a Windows Server Container. |
|
| 28 |
+func (i IsolationLevel) IsDefault() bool {
|
|
| 29 |
+ return strings.ToLower(string(i)) == "default" || string(i) == "" |
|
| 30 |
+} |
|
| 31 |
+ |
|
| 22 | 32 |
// IpcMode represents the container ipc stack. |
| 23 | 33 |
type IpcMode string |
| 24 | 34 |
|
| ... | ... |
@@ -254,6 +264,7 @@ type HostConfig struct {
|
| 254 | 254 |
CgroupParent string // Parent cgroup. |
| 255 | 255 |
ConsoleSize [2]int // Initial console size on Windows |
| 256 | 256 |
VolumeDriver string // Name of the volume driver used to mount volumes |
| 257 |
+ Isolation IsolationLevel // Isolation level of the container (eg default, hyperv) |
|
| 257 | 258 |
} |
| 258 | 259 |
|
| 259 | 260 |
// DecodeHostConfig creates a HostConfig based on the specified Reader. |
| ... | ... |
@@ -6,6 +6,11 @@ import ( |
| 6 | 6 |
"strings" |
| 7 | 7 |
) |
| 8 | 8 |
|
| 9 |
+// IsValid indicates is an isolation level is valid |
|
| 10 |
+func (i IsolationLevel) IsValid() bool {
|
|
| 11 |
+ return i.IsDefault() |
|
| 12 |
+} |
|
| 13 |
+ |
|
| 9 | 14 |
// IsPrivate indicates whether container uses it's private network stack. |
| 10 | 15 |
func (n NetworkMode) IsPrivate() bool {
|
| 11 | 16 |
return !(n.IsHost() || n.IsContainer()) |
| ... | ... |
@@ -1,10 +1,23 @@ |
| 1 | 1 |
package runconfig |
| 2 | 2 |
|
| 3 |
+import "strings" |
|
| 4 |
+ |
|
| 3 | 5 |
// IsDefault indicates whether container uses the default network stack. |
| 4 | 6 |
func (n NetworkMode) IsDefault() bool {
|
| 5 | 7 |
return n == "default" |
| 6 | 8 |
} |
| 7 | 9 |
|
| 10 |
+// IsHyperV indicates the use of Hyper-V Containers for isolation (as opposed |
|
| 11 |
+// to Windows Server Containers |
|
| 12 |
+func (i IsolationLevel) IsHyperV() bool {
|
|
| 13 |
+ return strings.ToLower(string(i)) == "hyperv" |
|
| 14 |
+} |
|
| 15 |
+ |
|
| 16 |
+// IsValid indicates is an isolation level is valid |
|
| 17 |
+func (i IsolationLevel) IsValid() bool {
|
|
| 18 |
+ return i.IsDefault() || i.IsHyperV() |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 8 | 21 |
// DefaultDaemonNetworkMode returns the default network stack the daemon should |
| 9 | 22 |
// use. |
| 10 | 23 |
func DefaultDaemonNetworkMode() NetworkMode {
|
| ... | ... |
@@ -55,22 +55,21 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe |
| 55 | 55 |
|
| 56 | 56 |
flUlimits = opts.NewUlimitOpt(nil) |
| 57 | 57 |
|
| 58 |
- flPublish = opts.NewListOpts(nil) |
|
| 59 |
- flExpose = opts.NewListOpts(nil) |
|
| 60 |
- flDNS = opts.NewListOpts(opts.ValidateIPAddress) |
|
| 61 |
- flDNSSearch = opts.NewListOpts(opts.ValidateDNSSearch) |
|
| 62 |
- flDNSOptions = opts.NewListOpts(nil) |
|
| 63 |
- flExtraHosts = opts.NewListOpts(opts.ValidateExtraHost) |
|
| 64 |
- flVolumesFrom = opts.NewListOpts(nil) |
|
| 65 |
- flLxcOpts = opts.NewListOpts(nil) |
|
| 66 |
- flEnvFile = opts.NewListOpts(nil) |
|
| 67 |
- flCapAdd = opts.NewListOpts(nil) |
|
| 68 |
- flCapDrop = opts.NewListOpts(nil) |
|
| 69 |
- flGroupAdd = opts.NewListOpts(nil) |
|
| 70 |
- flSecurityOpt = opts.NewListOpts(nil) |
|
| 71 |
- flLabelsFile = opts.NewListOpts(nil) |
|
| 72 |
- flLoggingOpts = opts.NewListOpts(nil) |
|
| 73 |
- |
|
| 58 |
+ flPublish = opts.NewListOpts(nil) |
|
| 59 |
+ flExpose = opts.NewListOpts(nil) |
|
| 60 |
+ flDNS = opts.NewListOpts(opts.ValidateIPAddress) |
|
| 61 |
+ flDNSSearch = opts.NewListOpts(opts.ValidateDNSSearch) |
|
| 62 |
+ flDNSOptions = opts.NewListOpts(nil) |
|
| 63 |
+ flExtraHosts = opts.NewListOpts(opts.ValidateExtraHost) |
|
| 64 |
+ flVolumesFrom = opts.NewListOpts(nil) |
|
| 65 |
+ flLxcOpts = opts.NewListOpts(nil) |
|
| 66 |
+ flEnvFile = opts.NewListOpts(nil) |
|
| 67 |
+ flCapAdd = opts.NewListOpts(nil) |
|
| 68 |
+ flCapDrop = opts.NewListOpts(nil) |
|
| 69 |
+ flGroupAdd = opts.NewListOpts(nil) |
|
| 70 |
+ flSecurityOpt = opts.NewListOpts(nil) |
|
| 71 |
+ flLabelsFile = opts.NewListOpts(nil) |
|
| 72 |
+ flLoggingOpts = opts.NewListOpts(nil) |
|
| 74 | 73 |
flNetwork = cmd.Bool([]string{"#n", "#-networking"}, true, "Enable networking for this container")
|
| 75 | 74 |
flPrivileged = cmd.Bool([]string{"#privileged", "-privileged"}, false, "Give extended privileges to this container")
|
| 76 | 75 |
flPidMode = cmd.String([]string{"-pid"}, "", "PID namespace to use")
|
| ... | ... |
@@ -104,6 +103,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe |
| 104 | 104 |
flCgroupParent = cmd.String([]string{"-cgroup-parent"}, "", "Optional parent cgroup for the container")
|
| 105 | 105 |
flVolumeDriver = cmd.String([]string{"-volume-driver"}, "", "Optional volume driver for the container")
|
| 106 | 106 |
flStopSignal = cmd.String([]string{"-stop-signal"}, signal.DefaultStopSignal, fmt.Sprintf("Signal to stop a container, %v by default", signal.DefaultStopSignal))
|
| 107 |
+ flIsolation = cmd.String([]string{"-isolation"}, "default", "Container isolation level")
|
|
| 107 | 108 |
) |
| 108 | 109 |
|
| 109 | 110 |
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 |
| 377 | 377 |
LogConfig: LogConfig{Type: *flLoggingDriver, Config: loggingOpts},
|
| 378 | 378 |
CgroupParent: *flCgroupParent, |
| 379 | 379 |
VolumeDriver: *flVolumeDriver, |
| 380 |
+ Isolation: IsolationLevel(*flIsolation), |
|
| 380 | 381 |
} |
| 381 | 382 |
|
| 382 | 383 |
// When allocating stdin in attached mode, close stdin at client disconnect |
| ... | ... |
@@ -4,6 +4,7 @@ package runconfig |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 | 6 |
"fmt" |
| 7 |
+ "runtime" |
|
| 7 | 8 |
"strings" |
| 8 | 9 |
) |
| 9 | 10 |
|
| ... | ... |
@@ -58,3 +59,17 @@ func ValidateNetMode(c *Config, hc *HostConfig) error {
|
| 58 | 58 |
} |
| 59 | 59 |
return nil |
| 60 | 60 |
} |
| 61 |
+ |
|
| 62 |
+// ValidateIsolationLevel performs platform specific validation of the |
|
| 63 |
+// isolation level in the hostconfig structure. Linux only supports "default" |
|
| 64 |
+// which is LXC container isolation |
|
| 65 |
+func ValidateIsolationLevel(hc *HostConfig) error {
|
|
| 66 |
+ // We may not be passed a host config, such as in the case of docker commit |
|
| 67 |
+ if hc == nil {
|
|
| 68 |
+ return nil |
|
| 69 |
+ } |
|
| 70 |
+ if !hc.Isolation.IsValid() {
|
|
| 71 |
+ return fmt.Errorf("invalid --isolation: %q - %s only supports 'default'", hc.Isolation, runtime.GOOS)
|
|
| 72 |
+ } |
|
| 73 |
+ return nil |
|
| 74 |
+} |
| ... | ... |
@@ -20,3 +20,18 @@ func ValidateNetMode(c *Config, hc *HostConfig) error {
|
| 20 | 20 |
} |
| 21 | 21 |
return nil |
| 22 | 22 |
} |
| 23 |
+ |
|
| 24 |
+// ValidateIsolationLevel performs platform specific validation of the |
|
| 25 |
+// isolation level in the hostconfig structure. Windows supports 'default' (or |
|
| 26 |
+// blank), and 'hyperv'. These refer to Windows Server Containers and |
|
| 27 |
+// Hyper-V Containers respectively. |
|
| 28 |
+func ValidateIsolationLevel(hc *HostConfig) error {
|
|
| 29 |
+ // We may not be passed a host config, such as in the case of docker commit |
|
| 30 |
+ if hc == nil {
|
|
| 31 |
+ return nil |
|
| 32 |
+ } |
|
| 33 |
+ if !hc.Isolation.IsValid() {
|
|
| 34 |
+ return fmt.Errorf("invalid --isolation: %q. Windows supports 'default' (Windows Server Container) or 'hyperv' (Hyper-V Container)", hc.Isolation)
|
|
| 35 |
+ } |
|
| 36 |
+ return nil |
|
| 37 |
+} |