6 containertypes "github.com/docker/docker/api/types/container"
7 "github.com/docker/docker/container"
8 "github.com/docker/docker/oci"
9 "github.com/docker/docker/pkg/sysinfo"
10 "github.com/docker/docker/pkg/system"
11 "github.com/opencontainers/runtime-spec/specs-go"
14 func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
15 img, err := daemon.GetImage(string(c.ImageID))
20 s := oci.DefaultOSSpec(img.OS)
22 linkedEnv, err := daemon.setupLinkedContainers(c)
27 // Note, unlike Unix, we do NOT call into SetupWorkingDirectory as
28 // this is done in VMCompute. Further, we couldn't do it for Hyper-V
32 s.Hostname = c.FullHostname()
34 if err := daemon.setupSecretDir(c); err != nil {
38 if err := daemon.setupConfigDir(c); err != nil {
43 mounts, err := daemon.setupMounts(c)
49 if c.HostConfig.Isolation.IsDefault() {
50 // Container using default isolation, so take the default from the daemon configuration
51 isHyperV = daemon.defaultIsolation.IsHyperV()
53 // Container may be requesting an explicit isolation mode.
54 isHyperV = c.HostConfig.Isolation.IsHyperV()
57 // If the container has not been started, and has configs or secrets
58 // secrets, create symlinks to each confing and secret. If it has been
59 // started before, the symlinks should have already been created. Also, it
60 // is important to not mount a Hyper-V container that has been started
61 // before, to protect the host from the container; for example, from
62 // malicious mutation of NTFS data structures.
63 if !c.HasBeenStartedBefore && (len(c.SecretReferences) > 0 || len(c.ConfigReferences) > 0) {
64 // The container file system is mounted before this function is called,
65 // except for Hyper-V containers, so mount it here in that case.
67 if err := daemon.Mount(c); err != nil {
70 defer daemon.Unmount(c)
72 if err := c.CreateSecretSymlinks(); err != nil {
75 if err := c.CreateConfigSymlinks(); err != nil {
80 if m := c.SecretMounts(); m != nil {
81 mounts = append(mounts, m...)
84 if m := c.ConfigMounts(); m != nil {
85 mounts = append(mounts, m...)
88 for _, mount := range mounts {
91 Destination: mount.Destination,
94 m.Options = append(m.Options, "ro")
96 s.Mounts = append(s.Mounts, m)
100 s.Process.Args = append([]string{c.Path}, c.Args...)
101 if !c.Config.ArgsEscaped && img.OS == "windows" {
102 s.Process.Args = escapeArgs(s.Process.Args)
105 s.Process.Cwd = c.Config.WorkingDir
106 s.Process.Env = c.CreateDaemonEnvironment(c.Config.Tty, linkedEnv)
108 s.Process.Terminal = c.Config.Tty
109 s.Process.ConsoleSize.Height = c.HostConfig.ConsoleSize[0]
110 s.Process.ConsoleSize.Width = c.HostConfig.ConsoleSize[1]
112 s.Process.User.Username = c.Config.User
114 if img.OS == "windows" {
115 daemon.createSpecWindowsFields(c, &s, isHyperV)
117 // TODO @jhowardmsft LCOW Support. Modify this check when running in dual-mode
118 if system.LCOWSupported() && img.OS == "linux" {
119 daemon.createSpecLinuxFields(c, &s)
123 return (*specs.Spec)(&s), nil
126 // Sets the Windows-specific fields of the OCI spec
127 func (daemon *Daemon) createSpecWindowsFields(c *container.Container, s *specs.Spec, isHyperV bool) {
128 if len(s.Process.Cwd) == 0 {
129 // We default to C:\ to workaround the oddity of the case that the
130 // default directory for cmd running as LocalSystem (or
131 // ContainerAdministrator) is c:\windows\system32. Hence docker run
132 // <image> cmd will by default end in c:\windows\system32, rather
133 // than 'root' (/) on Linux. The oddity is that if you have a dockerfile
134 // which has no WORKDIR and has a COPY file ., . will be interpreted
135 // as c:\. Hence, setting it to default of c:\ makes for consistency.
136 s.Process.Cwd = `C:\`
139 s.Root.Readonly = false // Windows does not support a read-only root filesystem
141 s.Root.Path = c.BaseFS // This is not set for Hyper-V containers
144 // In s.Windows.Resources
145 cpuShares := uint16(c.HostConfig.CPUShares)
146 cpuMaximum := uint16(c.HostConfig.CPUPercent) * 100
147 cpuCount := uint64(c.HostConfig.CPUCount)
148 if c.HostConfig.NanoCPUs > 0 {
150 cpuCount = uint64(c.HostConfig.NanoCPUs / 1e9)
151 leftoverNanoCPUs := c.HostConfig.NanoCPUs % 1e9
152 if leftoverNanoCPUs != 0 {
154 cpuMaximum = uint16(c.HostConfig.NanoCPUs / int64(cpuCount) / (1e9 / 10000))
156 // The requested NanoCPUs is so small that we rounded to 0, use 1 instead
161 cpuMaximum = uint16(c.HostConfig.NanoCPUs / int64(sysinfo.NumCPU()) / (1e9 / 10000))
163 // The requested NanoCPUs is so small that we rounded to 0, use 1 instead
168 memoryLimit := uint64(c.HostConfig.Memory)
169 s.Windows.Resources = &specs.WindowsResources{
170 CPU: &specs.WindowsCPUResources{
171 Maximum: &cpuMaximum,
175 Memory: &specs.WindowsMemoryResources{
178 Storage: &specs.WindowsStorageResources{
179 Bps: &c.HostConfig.IOMaximumBandwidth,
180 Iops: &c.HostConfig.IOMaximumIOps,
185 // Sets the Linux-specific fields of the OCI spec
186 // TODO: @jhowardmsft LCOW Support. We need to do a lot more pulling in what can
187 // be pulled in from oci_linux.go.
188 func (daemon *Daemon) createSpecLinuxFields(c *container.Container, s *specs.Spec) {
189 if len(s.Process.Cwd) == 0 {
192 s.Root.Path = "rootfs"
193 s.Root.Readonly = c.HostConfig.ReadonlyRootfs
196 func escapeArgs(args []string) []string {
197 escapedArgs := make([]string, len(args))
198 for i, a := range args {
199 escapedArgs[i] = syscall.EscapeArg(a)
204 // mergeUlimits merge the Ulimits from HostConfig with daemon defaults, and update HostConfig
205 // It will do nothing on non-Linux platform
206 func (daemon *Daemon) mergeUlimits(c *containertypes.HostConfig) {