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
} |