daemon/oci_windows.go
94d70d83
 package daemon
 
 import (
 	"syscall"
 
7d705a73
 	containertypes "github.com/docker/docker/api/types/container"
94d70d83
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/oci"
846baf1f
 	"github.com/docker/docker/pkg/sysinfo"
f1545882
 	"github.com/docker/docker/pkg/system"
02309170
 	"github.com/opencontainers/runtime-spec/specs-go"
94d70d83
 )
 
02309170
 func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
f1545882
 	img, err := daemon.GetImage(string(c.ImageID))
 	if err != nil {
 		return nil, err
 	}
 
 	s := oci.DefaultOSSpec(img.OS)
94d70d83
 
 	linkedEnv, err := daemon.setupLinkedContainers(c)
 	if err != nil {
 		return nil, err
 	}
 
f42033ba
 	// Note, unlike Unix, we do NOT call into SetupWorkingDirectory as
 	// this is done in VMCompute. Further, we couldn't do it for Hyper-V
 	// containers anyway.
94d70d83
 
 	// In base spec
 	s.Hostname = c.FullHostname()
 
bd4e8aa6
 	if err := daemon.setupSecretDir(c); err != nil {
 		return nil, err
 	}
 
e0d533b1
 	if err := daemon.setupConfigDir(c); err != nil {
 		return nil, err
 	}
 
94d70d83
 	// In s.Mounts
 	mounts, err := daemon.setupMounts(c)
 	if err != nil {
 		return nil, err
 	}
bd4e8aa6
 
 	var isHyperV bool
 	if c.HostConfig.Isolation.IsDefault() {
 		// Container using default isolation, so take the default from the daemon configuration
 		isHyperV = daemon.defaultIsolation.IsHyperV()
 	} else {
 		// Container may be requesting an explicit isolation mode.
 		isHyperV = c.HostConfig.Isolation.IsHyperV()
 	}
 
e0d533b1
 	// If the container has not been started, and has configs or secrets
 	// secrets, create symlinks to each confing and secret. If it has been
 	// started before, the symlinks should have already been created. Also, it
 	// is important to not mount a Hyper-V  container that has been started
 	// before, to protect the host from the container; for example, from
 	// malicious mutation of NTFS data structures.
 	if !c.HasBeenStartedBefore && (len(c.SecretReferences) > 0 || len(c.ConfigReferences) > 0) {
bd4e8aa6
 		// The container file system is mounted before this function is called,
 		// except for Hyper-V containers, so mount it here in that case.
 		if isHyperV {
 			if err := daemon.Mount(c); err != nil {
 				return nil, err
 			}
e0d533b1
 			defer daemon.Unmount(c)
bd4e8aa6
 		}
e0d533b1
 		if err := c.CreateSecretSymlinks(); err != nil {
 			return nil, err
bd4e8aa6
 		}
e0d533b1
 		if err := c.CreateConfigSymlinks(); err != nil {
bd4e8aa6
 			return nil, err
 		}
 	}
 
 	if m := c.SecretMounts(); m != nil {
 		mounts = append(mounts, m...)
 	}
 
e0d533b1
 	if m := c.ConfigMounts(); m != nil {
 		mounts = append(mounts, m...)
 	}
 
94d70d83
 	for _, mount := range mounts {
02309170
 		m := specs.Mount{
94d70d83
 			Source:      mount.Source,
 			Destination: mount.Destination,
bb585b9c
 		}
 		if !mount.Writable {
 			m.Options = append(m.Options, "ro")
 		}
 		s.Mounts = append(s.Mounts, m)
94d70d83
 	}
 
 	// In s.Process
6fa02397
 	s.Process.Args = append([]string{c.Path}, c.Args...)
19645521
 	if !c.Config.ArgsEscaped && img.OS == "windows" {
6fa02397
 		s.Process.Args = escapeArgs(s.Process.Args)
94d70d83
 	}
f1545882
 
94d70d83
 	s.Process.Cwd = c.Config.WorkingDir
f1545882
 	s.Process.Env = c.CreateDaemonEnvironment(c.Config.Tty, linkedEnv)
 	if c.Config.Tty {
 		s.Process.Terminal = c.Config.Tty
 		s.Process.ConsoleSize.Height = c.HostConfig.ConsoleSize[0]
 		s.Process.ConsoleSize.Width = c.HostConfig.ConsoleSize[1]
 	}
 	s.Process.User.Username = c.Config.User
 
 	if img.OS == "windows" {
 		daemon.createSpecWindowsFields(c, &s, isHyperV)
 	} else {
 		// TODO @jhowardmsft LCOW Support. Modify this check when running in dual-mode
 		if system.LCOWSupported() && img.OS == "linux" {
 			daemon.createSpecLinuxFields(c, &s)
 		}
 	}
 
 	return (*specs.Spec)(&s), nil
 }
 
 // Sets the Windows-specific fields of the OCI spec
 func (daemon *Daemon) createSpecWindowsFields(c *container.Container, s *specs.Spec, isHyperV bool) {
c2d18342
 	if len(s.Process.Cwd) == 0 {
 		// We default to C:\ to workaround the oddity of the case that the
 		// default directory for cmd running as LocalSystem (or
 		// ContainerAdministrator) is c:\windows\system32. Hence docker run
 		// <image> cmd will by default end in c:\windows\system32, rather
 		// than 'root' (/) on Linux. The oddity is that if you have a dockerfile
 		// which has no WORKDIR and has a COPY file ., . will be interpreted
 		// as c:\. Hence, setting it to default of c:\ makes for consistency.
 		s.Process.Cwd = `C:\`
 	}
94d70d83
 
f1545882
 	s.Root.Readonly = false // Windows does not support a read-only root filesystem
8f76a1d0
 	if !isHyperV {
f1545882
 		s.Root.Path = c.BaseFS // This is not set for Hyper-V containers
8f76a1d0
 	}
94d70d83
 
 	// In s.Windows.Resources
02309170
 	cpuShares := uint16(c.HostConfig.CPUShares)
425973cb
 	cpuMaximum := uint16(c.HostConfig.CPUPercent) * 100
3b5af0a2
 	cpuCount := uint64(c.HostConfig.CPUCount)
846baf1f
 	if c.HostConfig.NanoCPUs > 0 {
3b5af0a2
 		if isHyperV {
 			cpuCount = uint64(c.HostConfig.NanoCPUs / 1e9)
 			leftoverNanoCPUs := c.HostConfig.NanoCPUs % 1e9
 			if leftoverNanoCPUs != 0 {
 				cpuCount++
425973cb
 				cpuMaximum = uint16(c.HostConfig.NanoCPUs / int64(cpuCount) / (1e9 / 10000))
 				if cpuMaximum < 1 {
 					// The requested NanoCPUs is so small that we rounded to 0, use 1 instead
 					cpuMaximum = 1
 				}
3b5af0a2
 			}
 		} else {
425973cb
 			cpuMaximum = uint16(c.HostConfig.NanoCPUs / int64(sysinfo.NumCPU()) / (1e9 / 10000))
 			if cpuMaximum < 1 {
b3649f40
 				// The requested NanoCPUs is so small that we rounded to 0, use 1 instead
425973cb
 				cpuMaximum = 1
b3649f40
 			}
3b5af0a2
 		}
846baf1f
 	}
02309170
 	memoryLimit := uint64(c.HostConfig.Memory)
 	s.Windows.Resources = &specs.WindowsResources{
 		CPU: &specs.WindowsCPUResources{
425973cb
 			Maximum: &cpuMaximum,
ea8c6908
 			Shares:  &cpuShares,
4e15420b
 			Count:   &cpuCount,
94d70d83
 		},
02309170
 		Memory: &specs.WindowsMemoryResources{
 			Limit: &memoryLimit,
94d70d83
 		},
02309170
 		Storage: &specs.WindowsStorageResources{
8df20663
 			Bps:  &c.HostConfig.IOMaximumBandwidth,
 			Iops: &c.HostConfig.IOMaximumIOps,
94d70d83
 		},
 	}
f1545882
 }
 
 // Sets the Linux-specific fields of the OCI spec
 // TODO: @jhowardmsft LCOW Support. We need to do a lot more pulling in what can
 // be pulled in from oci_linux.go.
 func (daemon *Daemon) createSpecLinuxFields(c *container.Container, s *specs.Spec) {
 	if len(s.Process.Cwd) == 0 {
 		s.Process.Cwd = `/`
 	}
 	s.Root.Path = "rootfs"
 	s.Root.Readonly = c.HostConfig.ReadonlyRootfs
94d70d83
 }
 
 func escapeArgs(args []string) []string {
 	escapedArgs := make([]string, len(args))
 	for i, a := range args {
 		escapedArgs[i] = syscall.EscapeArg(a)
 	}
 	return escapedArgs
 }
7d705a73
 
 // mergeUlimits merge the Ulimits from HostConfig with daemon defaults, and update HostConfig
 // It will do nothing on non-Linux platform
 func (daemon *Daemon) mergeUlimits(c *containertypes.HostConfig) {
 	return
 }