moby/daemon/oci_windows.go
John Starks 6508c015fe Windows: Use image version, not OS version for TTY fixup
A previous change added a TTY fixup for stdin on older Windows versions to
work around a Windows issue with backspace/delete behavior. This change
used the OS version to determine whether to activate the behavior.
However, the Windows bug is actually in the image, not the OS, so it
should have used the image's OS version.

This ensures that a Server TP5 container running on Windows 10 will have
reasonable console behavior.

Signed-off-by: John Starks <jostarks@microsoft.com>
2016-05-25 12:22:52 -07:00

200 lines
5.5 KiB
Go

package daemon
import (
"errors"
"fmt"
"os"
"path/filepath"
"syscall"
"github.com/docker/docker/container"
"github.com/docker/docker/image"
"github.com/docker/docker/layer"
"github.com/docker/docker/libcontainerd"
"github.com/docker/docker/libcontainerd/windowsoci"
"github.com/docker/docker/oci"
)
func (daemon *Daemon) createSpec(c *container.Container) (*libcontainerd.Spec, error) {
s := oci.DefaultSpec()
linkedEnv, err := daemon.setupLinkedContainers(c)
if err != nil {
return nil, err
}
// TODO Windows - this can be removed. Not used (UID/GID)
rootUID, rootGID := daemon.GetRemappedUIDGID()
if err := c.SetupWorkingDirectory(rootUID, rootGID); err != nil {
return nil, err
}
img, err := daemon.imageStore.Get(c.ImageID)
if err != nil {
return nil, fmt.Errorf("Failed to graph.Get on ImageID %s - %s", c.ImageID, err)
}
s.Platform.OSVersion = img.OSVersion
// In base spec
s.Hostname = c.FullHostname()
// In s.Mounts
mounts, err := daemon.setupMounts(c)
if err != nil {
return nil, err
}
for _, mount := range mounts {
s.Mounts = append(s.Mounts, windowsoci.Mount{
Source: mount.Source,
Destination: mount.Destination,
Readonly: !mount.Writable,
})
}
// In s.Process
s.Process.Args = append([]string{c.Path}, c.Args...)
if !c.Config.ArgsEscaped {
s.Process.Args = escapeArgs(s.Process.Args)
}
s.Process.Cwd = c.Config.WorkingDir
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:\`
}
s.Process.Env = c.CreateDaemonEnvironment(linkedEnv)
s.Process.InitialConsoleSize = c.HostConfig.ConsoleSize
s.Process.Terminal = c.Config.Tty
s.Process.User.User = c.Config.User
// In spec.Root
s.Root.Path = c.BaseFS
s.Root.Readonly = c.HostConfig.ReadonlyRootfs
// In s.Windows
s.Windows.FirstStart = !c.HasBeenStartedBefore
// s.Windows.LayerFolder.
m, err := c.RWLayer.Metadata()
if err != nil {
return nil, fmt.Errorf("Failed to get layer metadata - %s", err)
}
s.Windows.LayerFolder = m["dir"]
// s.Windows.LayerPaths
var layerPaths []string
if img.RootFS != nil && (img.RootFS.Type == image.TypeLayers || img.RootFS.Type == image.TypeLayersWithBase) {
// Get the layer path for each layer.
start := 1
if img.RootFS.Type == image.TypeLayersWithBase {
// Include an empty slice to get the base layer ID.
start = 0
}
max := len(img.RootFS.DiffIDs)
for i := start; i <= max; i++ {
img.RootFS.DiffIDs = img.RootFS.DiffIDs[:i]
path, err := layer.GetLayerPath(daemon.layerStore, img.RootFS.ChainID())
if err != nil {
return nil, fmt.Errorf("Failed to get layer path from graphdriver %s for ImageID %s - %s", daemon.layerStore, img.RootFS.ChainID(), err)
}
// Reverse order, expecting parent most first
layerPaths = append([]string{path}, layerPaths...)
}
}
s.Windows.LayerPaths = layerPaths
// Are we going to run as a Hyper-V container?
hv := false
if c.HostConfig.Isolation.IsDefault() {
// Container is set to use the default, so take the default from the daemon configuration
hv = daemon.defaultIsolation.IsHyperV()
} else {
// Container is requesting an isolation mode. Honour it.
hv = c.HostConfig.Isolation.IsHyperV()
}
if hv {
hvr := &windowsoci.HvRuntime{}
if img.RootFS != nil && img.RootFS.Type == image.TypeLayers {
// For TP5, the utility VM is part of the base layer.
// TODO-jstarks: Add support for separate utility VM images
// once it is decided how they can be stored.
uvmpath := filepath.Join(layerPaths[len(layerPaths)-1], "UtilityVM")
_, err = os.Stat(uvmpath)
if err != nil {
if os.IsNotExist(err) {
err = errors.New("container image does not contain a utility VM")
}
return nil, err
}
hvr.ImagePath = uvmpath
}
s.Windows.HvRuntime = hvr
}
// In s.Windows.Networking
// Connect all the libnetwork allocated networks to the container
var epList []string
if c.NetworkSettings != nil {
for n := range c.NetworkSettings.Networks {
sn, err := daemon.FindNetwork(n)
if err != nil {
continue
}
ep, err := c.GetEndpointInNetwork(sn)
if err != nil {
continue
}
data, err := ep.DriverInfo()
if err != nil {
continue
}
if data["hnsid"] != nil {
epList = append(epList, data["hnsid"].(string))
}
}
}
s.Windows.Networking = &windowsoci.Networking{
EndpointList: epList,
}
// In s.Windows.Resources
// @darrenstahlmsft implement these resources
cpuShares := uint64(c.HostConfig.CPUShares)
s.Windows.Resources = &windowsoci.Resources{
CPU: &windowsoci.CPU{
Percent: &c.HostConfig.CPUPercent,
Shares: &cpuShares,
},
Memory: &windowsoci.Memory{
//TODO Limit: ...,
//TODO Reservation: ...,
},
Network: &windowsoci.Network{
//TODO Bandwidth: ...,
},
Storage: &windowsoci.Storage{
Bps: &c.HostConfig.IOMaximumBandwidth,
Iops: &c.HostConfig.IOMaximumIOps,
//TODO SandboxSize: ...,
},
}
return (*libcontainerd.Spec)(&s), nil
}
func escapeArgs(args []string) []string {
escapedArgs := make([]string, len(args))
for i, a := range args {
escapedArgs[i] = syscall.EscapeArg(a)
}
return escapedArgs
}