Merge pull request #13554 from Microsoft/10662-winexec

Windows: The real Windows exec driver is here.
Upstream-commit: b7e8169274ba4b897601e1025b91ebd361902213
Component: engine
This commit is contained in:
Phil Estes
2015-07-10 17:13:10 -04:00
51 changed files with 3482 additions and 85 deletions

View File

@ -74,6 +74,7 @@ type CommonContainer struct {
MountLabel, ProcessLabel string
RestartCount int
UpdateDns bool
HasBeenStartedBefore bool
MountPoints map[string]*mountPoint
Volumes map[string]string // Deprecated since 1.7, kept for backwards compatibility
@ -286,6 +287,7 @@ func (container *Container) Run() error {
if err := container.Start(); err != nil {
return err
}
container.HasBeenStartedBefore = true
container.WaitStop(-1 * time.Second)
return nil
}

View File

@ -92,11 +92,12 @@ func populateCommand(c *Container, env []string) error {
// TODO Windows. Further refactoring required (privileged/user)
processConfig := execdriver.ProcessConfig{
Privileged: c.hostConfig.Privileged,
Entrypoint: c.Path,
Arguments: c.Args,
Tty: c.Config.Tty,
User: c.Config.User,
Privileged: c.hostConfig.Privileged,
Entrypoint: c.Path,
Arguments: c.Args,
Tty: c.Config.Tty,
User: c.Config.User,
ConsoleSize: c.hostConfig.ConsoleSize,
}
processConfig.Env = env
@ -116,6 +117,7 @@ func populateCommand(c *Container, env []string) error {
ProcessConfig: processConfig,
ProcessLabel: c.GetProcessLabel(),
MountLabel: c.GetMountLabel(),
FirstStart: !c.HasBeenStartedBefore,
}
return nil

View File

@ -138,13 +138,14 @@ type Mount struct {
type ProcessConfig struct {
exec.Cmd `json:"-"`
Privileged bool `json:"privileged"`
User string `json:"user"`
Tty bool `json:"tty"`
Entrypoint string `json:"entrypoint"`
Arguments []string `json:"arguments"`
Terminal Terminal `json:"-"` // standard or tty terminal
Console string `json:"-"` // dev/console path
Privileged bool `json:"privileged"`
User string `json:"user"`
Tty bool `json:"tty"`
Entrypoint string `json:"entrypoint"`
Arguments []string `json:"arguments"`
Terminal Terminal `json:"-"` // standard or tty terminal
Console string `json:"-"` // dev/console path
ConsoleSize [2]int `json:"-"` // h,w of initial console size
}
// TODO Windows: Factor out unused fields such as LxcConfig, AppArmorProfile,
@ -175,4 +176,7 @@ type Command struct {
LxcConfig []string `json:"lxc_config"`
AppArmorProfile string `json:"apparmor_profile"`
CgroupParent string `json:"cgroup_parent"` // The parent cgroup for this command.
FirstStart bool `json:"first_start"`
LayerPaths []string `json:"layer_paths"` // Windows needs to know the layer paths and folder for a command
LayerFolder string `json:"layer_folder"`
}

View File

@ -13,7 +13,7 @@ import (
func NewDriver(name string, options []string, root, libPath, initPath string, sysInfo *sysinfo.SysInfo) (execdriver.Driver, error) {
switch name {
case "windows":
return windows.NewDriver(root, initPath)
return windows.NewDriver(root, initPath, options)
}
return nil, fmt.Errorf("unknown exec driver %s", name)
}

View File

@ -0,0 +1,36 @@
// +build windows
package windows
import (
"errors"
"github.com/docker/docker/daemon/execdriver"
)
func checkSupportedOptions(c *execdriver.Command) error {
// Windows doesn't support read-only root filesystem
if c.ReadonlyRootfs {
return errors.New("Windows does not support the read-only root filesystem option")
}
// Windows doesn't support username
if c.ProcessConfig.User != "" {
return errors.New("Windows does not support the username option")
}
// Windows doesn't support custom lxc options
if c.LxcConfig != nil {
return errors.New("Windows does not support lxc options")
}
// Windows doesn't support ulimit
if c.Resources.Rlimits != nil {
return errors.New("Windows does not support ulimit options")
}
// TODO Windows: Validate other fields which Windows doesn't support, factor
// out where applicable per platform.
return nil
}

View File

@ -0,0 +1,7 @@
// +build windows
package windows
func (d *driver) Clean(id string) error {
return nil
}

View File

@ -0,0 +1,144 @@
// +build windows
package windows
import (
"errors"
"fmt"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/daemon/execdriver"
"github.com/docker/docker/pkg/stringid"
"github.com/microsoft/hcsshim"
"github.com/natefinch/npipe"
)
func (d *driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
var (
inListen, outListen, errListen *npipe.PipeListener
term execdriver.Terminal
err error
randomID string = stringid.GenerateRandomID()
serverPipeFormat, clientPipeFormat string
pid uint32
exitCode int32
)
active := d.activeContainers[c.ID]
if active == nil {
return -1, fmt.Errorf("Exec - No active container exists with ID %s", c.ID)
}
createProcessParms := hcsshim.CreateProcessParams{
EmulateConsole: processConfig.Tty, // Note NOT c.ProcessConfig.Tty
WorkingDirectory: c.WorkingDir,
}
// Configure the environment for the process // Note NOT c.ProcessConfig.Tty
createProcessParms.Environment = setupEnvironmentVariables(processConfig.Env)
// We use another unique ID here for each exec instance otherwise it
// may conflict with the pipe name being used by RUN.
// We use a different pipe name between real and dummy mode in the HCS
if dummyMode {
clientPipeFormat = `\\.\pipe\docker-exec-%[1]s-%[2]s-%[3]s`
serverPipeFormat = clientPipeFormat
} else {
clientPipeFormat = `\\.\pipe\docker-exec-%[2]s-%[3]s`
serverPipeFormat = `\\.\Containers\%[1]s\Device\NamedPipe\docker-exec-%[2]s-%[3]s`
}
// Connect stdin
if pipes.Stdin != nil {
stdInPipe := fmt.Sprintf(serverPipeFormat, c.ID, randomID, "stdin")
createProcessParms.StdInPipe = fmt.Sprintf(clientPipeFormat, c.ID, randomID, "stdin")
// Listen on the named pipe
inListen, err = npipe.Listen(stdInPipe)
if err != nil {
logrus.Errorf("stdin failed to listen on %s %s ", stdInPipe, err)
return -1, err
}
defer inListen.Close()
// Launch a goroutine to do the accept. We do this so that we can
// cause an otherwise blocking goroutine to gracefully close when
// the caller (us) closes the listener
go stdinAccept(inListen, stdInPipe, pipes.Stdin)
}
// Connect stdout
stdOutPipe := fmt.Sprintf(serverPipeFormat, c.ID, randomID, "stdout")
createProcessParms.StdOutPipe = fmt.Sprintf(clientPipeFormat, c.ID, randomID, "stdout")
outListen, err = npipe.Listen(stdOutPipe)
if err != nil {
logrus.Errorf("stdout failed to listen on %s %s", stdOutPipe, err)
return -1, err
}
defer outListen.Close()
go stdouterrAccept(outListen, stdOutPipe, pipes.Stdout)
// No stderr on TTY. Note NOT c.ProcessConfig.Tty
if !processConfig.Tty {
// Connect stderr
stdErrPipe := fmt.Sprintf(serverPipeFormat, c.ID, randomID, "stderr")
createProcessParms.StdErrPipe = fmt.Sprintf(clientPipeFormat, c.ID, randomID, "stderr")
errListen, err = npipe.Listen(stdErrPipe)
if err != nil {
logrus.Errorf("Stderr failed to listen on %s %s", stdErrPipe, err)
return -1, err
}
defer errListen.Close()
go stdouterrAccept(errListen, stdErrPipe, pipes.Stderr)
}
// While this should get caught earlier, just in case, validate that we
// have something to run.
if processConfig.Entrypoint == "" {
err = errors.New("No entrypoint specified")
logrus.Error(err)
return -1, err
}
// Build the command line of the process
createProcessParms.CommandLine = processConfig.Entrypoint
for _, arg := range processConfig.Arguments {
logrus.Debugln("appending ", arg)
createProcessParms.CommandLine += " " + arg
}
logrus.Debugln("commandLine: ", createProcessParms.CommandLine)
// Start the command running in the container.
pid, err = hcsshim.CreateProcessInComputeSystem(c.ID, createProcessParms)
if err != nil {
logrus.Errorf("CreateProcessInComputeSystem() failed %s", err)
return -1, err
}
// Note NOT c.ProcessConfig.Tty
if processConfig.Tty {
term = NewTtyConsole(c.ID, pid)
} else {
term = NewStdConsole()
}
processConfig.Terminal = term
// Invoke the start callback
if startCallback != nil {
startCallback(&c.ProcessConfig, int(pid))
}
if exitCode, err = hcsshim.WaitForProcessInComputeSystem(c.ID, pid); err != nil {
logrus.Errorf("Failed to WaitForProcessInComputeSystem %s", err)
return -1, err
}
// TODO Windows - Do something with this exit code
logrus.Debugln("Exiting Run() with ExitCode 0", c.ID)
return int(exitCode), nil
}

View File

@ -0,0 +1,10 @@
// +build windows
package windows
import "fmt"
func (d *driver) GetPidsForContainer(id string) ([]int, error) {
// TODO Windows: Implementation required.
return nil, fmt.Errorf("GetPidsForContainer: GetPidsForContainer() not implemented")
}

View File

@ -0,0 +1,23 @@
// +build windows
package windows
import "github.com/docker/docker/daemon/execdriver"
type info struct {
ID string
driver *driver
}
func (d *driver) Info(id string) execdriver.Info {
return &info{
ID: id,
driver: d,
}
}
func (i *info) IsRunning() bool {
var running bool
running = true // TODO Need an HCS API
return running
}

View File

@ -0,0 +1,82 @@
// +build windows
package windows
import (
"io"
"github.com/Sirupsen/logrus"
"github.com/natefinch/npipe"
)
// stdinAccept runs as a go function. It waits for the container system
// to accept our offer of a named pipe for stdin. Once accepted, if we are
// running "attached" to the container (eg docker run -i), then we spin up
// a further thread to copy anything from the client into the container.
//
// Important design note. This function is run as a go function for a very
// good reason. The named pipe Accept call is blocking until one of two things
// happen. Either someone connects to it, or it is forcibly closed. Let's
// assume that no-one connects to it, the only way otherwise the Run()
// method would continue is by closing it. However, as that would be the same
// thread, it can't close it. Hence we run as another thread allowing Run()
// to close the named pipe.
func stdinAccept(inListen *npipe.PipeListener, pipeName string, copyfrom io.ReadCloser) {
// Wait for the pipe to be connected to by the shim
logrus.Debugln("stdinAccept: Waiting on ", pipeName)
stdinConn, err := inListen.Accept()
if err != nil {
logrus.Errorf("Failed to accept on pipe %s %s", pipeName, err)
return
}
logrus.Debugln("Connected to ", stdinConn.RemoteAddr())
// Anything that comes from the client stdin should be copied
// across to the stdin named pipe of the container.
if copyfrom != nil {
go func() {
defer stdinConn.Close()
logrus.Debugln("Calling io.Copy on stdin")
bytes, err := io.Copy(stdinConn, copyfrom)
logrus.Debugf("Finished io.Copy on stdin bytes=%d err=%s pipe=%s", bytes, err, stdinConn.RemoteAddr())
}()
} else {
defer stdinConn.Close()
}
}
// stdouterrAccept runs as a go function. It waits for the container system to
// accept our offer of a named pipe - in fact two of them - one for stdout
// and one for stderr (we are called twice). Once the named pipe is accepted,
// if we are running "attached" to the container (eg docker run -i), then we
// spin up a further thread to copy anything from the containers output channels
// to the client.
func stdouterrAccept(outerrListen *npipe.PipeListener, pipeName string, copyto io.Writer) {
// Wait for the pipe to be connected to by the shim
logrus.Debugln("out/err: Waiting on ", pipeName)
outerrConn, err := outerrListen.Accept()
if err != nil {
logrus.Errorf("Failed to accept on pipe %s %s", pipeName, err)
return
}
logrus.Debugln("Connected to ", outerrConn.RemoteAddr())
// Anything that comes from the container named pipe stdout/err should be copied
// across to the stdout/err of the client
if copyto != nil {
go func() {
defer outerrConn.Close()
logrus.Debugln("Calling io.Copy on ", pipeName)
bytes, err := io.Copy(copyto, outerrConn)
logrus.Debugf("Copied %d bytes from pipe=%s", bytes, outerrConn.RemoteAddr())
if err != nil {
// Not fatal, just debug log it
logrus.Debugf("Error hit during copy %s", err)
}
}()
} else {
defer outerrConn.Close()
}
}

View File

@ -0,0 +1,17 @@
// +build windows
package windows
import (
"fmt"
"github.com/docker/docker/daemon/execdriver"
)
func (d *driver) Pause(c *execdriver.Command) error {
return fmt.Errorf("Windows: Containers cannot be paused")
}
func (d *driver) Unpause(c *execdriver.Command) error {
return fmt.Errorf("Windows: Containers cannot be paused")
}

View File

@ -0,0 +1,271 @@
// +build windows
package windows
// Note this is alpha code for the bring up of containers on Windows.
import (
"encoding/json"
"errors"
"fmt"
"strings"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/daemon/execdriver"
"github.com/microsoft/hcsshim"
"github.com/natefinch/npipe"
)
type layer struct {
Id string
Path string
}
type defConfig struct {
DefFile string
}
type networkConnection struct {
NetworkName string
EnableNat bool
}
type networkSettings struct {
MacAddress string
}
type device struct {
DeviceType string
Connection interface{}
Settings interface{}
}
type containerInit struct {
SystemType string
Name string
IsDummy bool
VolumePath string
Devices []device
IgnoreFlushesDuringBoot bool
LayerFolderPath string
Layers []layer
}
func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (execdriver.ExitStatus, error) {
var (
term execdriver.Terminal
err error
inListen, outListen, errListen *npipe.PipeListener
)
// Make sure the client isn't asking for options which aren't supported
err = checkSupportedOptions(c)
if err != nil {
return execdriver.ExitStatus{ExitCode: -1}, err
}
cu := &containerInit{
SystemType: "Container",
Name: c.ID,
IsDummy: dummyMode,
VolumePath: c.Rootfs,
IgnoreFlushesDuringBoot: c.FirstStart,
LayerFolderPath: c.LayerFolder,
}
for i := 0; i < len(c.LayerPaths); i++ {
cu.Layers = append(cu.Layers, layer{
Id: hcsshim.NewGUID(c.LayerPaths[i]).ToString(),
Path: c.LayerPaths[i],
})
}
if c.Network.Interface != nil {
// TODO Windows: Temporary
c.Network.Interface.Bridge = "Virtual Switch"
dev := device{
DeviceType: "Network",
Connection: &networkConnection{
NetworkName: c.Network.Interface.Bridge,
EnableNat: false,
},
}
if c.Network.Interface.MacAddress != "" {
windowsStyleMAC := strings.Replace(
c.Network.Interface.MacAddress, ":", "-", -1)
dev.Settings = networkSettings{
MacAddress: windowsStyleMAC,
}
}
cu.Devices = append(cu.Devices, dev)
}
configurationb, err := json.Marshal(cu)
if err != nil {
return execdriver.ExitStatus{ExitCode: -1}, err
}
configuration := string(configurationb)
err = hcsshim.CreateComputeSystem(c.ID, configuration)
if err != nil {
logrus.Debugln("Failed to create temporary container ", err)
return execdriver.ExitStatus{ExitCode: -1}, err
}
// Start the container
logrus.Debugln("Starting container ", c.ID)
err = hcsshim.StartComputeSystem(c.ID)
if err != nil {
logrus.Errorf("Failed to start compute system: %s", err)
return execdriver.ExitStatus{ExitCode: -1}, err
}
defer func() {
// Stop the container
if terminateMode {
logrus.Debugf("Terminating container %s", c.ID)
if err := hcsshim.TerminateComputeSystem(c.ID); err != nil {
// IMPORTANT: Don't fail if fails to change state. It could already
// have been stopped through kill().
// Otherwise, the docker daemon will hang in job wait()
logrus.Warnf("Ignoring error from TerminateComputeSystem %s", err)
}
} else {
logrus.Debugf("Shutting down container %s", c.ID)
if err := hcsshim.ShutdownComputeSystem(c.ID); err != nil {
// IMPORTANT: Don't fail if fails to change state. It could already
// have been stopped through kill().
// Otherwise, the docker daemon will hang in job wait()
logrus.Warnf("Ignoring error from ShutdownComputeSystem %s", err)
}
}
}()
// We use a different pipe name between real and dummy mode in the HCS
var serverPipeFormat, clientPipeFormat string
if dummyMode {
clientPipeFormat = `\\.\pipe\docker-run-%[1]s-%[2]s`
serverPipeFormat = clientPipeFormat
} else {
clientPipeFormat = `\\.\pipe\docker-run-%[2]s`
serverPipeFormat = `\\.\Containers\%[1]s\Device\NamedPipe\docker-run-%[2]s`
}
createProcessParms := hcsshim.CreateProcessParams{
EmulateConsole: c.ProcessConfig.Tty,
WorkingDirectory: c.WorkingDir,
ConsoleSize: c.ProcessConfig.ConsoleSize,
}
// Configure the environment for the process
createProcessParms.Environment = setupEnvironmentVariables(c.ProcessConfig.Env)
// Connect stdin
if pipes.Stdin != nil {
stdInPipe := fmt.Sprintf(serverPipeFormat, c.ID, "stdin")
createProcessParms.StdInPipe = fmt.Sprintf(clientPipeFormat, c.ID, "stdin")
// Listen on the named pipe
inListen, err = npipe.Listen(stdInPipe)
if err != nil {
logrus.Errorf("stdin failed to listen on %s err=%s", stdInPipe, err)
return execdriver.ExitStatus{ExitCode: -1}, err
}
defer inListen.Close()
// Launch a goroutine to do the accept. We do this so that we can
// cause an otherwise blocking goroutine to gracefully close when
// the caller (us) closes the listener
go stdinAccept(inListen, stdInPipe, pipes.Stdin)
}
// Connect stdout
stdOutPipe := fmt.Sprintf(serverPipeFormat, c.ID, "stdout")
createProcessParms.StdOutPipe = fmt.Sprintf(clientPipeFormat, c.ID, "stdout")
outListen, err = npipe.Listen(stdOutPipe)
if err != nil {
logrus.Errorf("stdout failed to listen on %s err=%s", stdOutPipe, err)
return execdriver.ExitStatus{ExitCode: -1}, err
}
defer outListen.Close()
go stdouterrAccept(outListen, stdOutPipe, pipes.Stdout)
// No stderr on TTY.
if !c.ProcessConfig.Tty {
// Connect stderr
stdErrPipe := fmt.Sprintf(serverPipeFormat, c.ID, "stderr")
createProcessParms.StdErrPipe = fmt.Sprintf(clientPipeFormat, c.ID, "stderr")
errListen, err = npipe.Listen(stdErrPipe)
if err != nil {
logrus.Errorf("stderr failed to listen on %s err=%s", stdErrPipe, err)
return execdriver.ExitStatus{ExitCode: -1}, err
}
defer errListen.Close()
go stdouterrAccept(errListen, stdErrPipe, pipes.Stderr)
}
// This should get caught earlier, but just in case - validate that we
// have something to run
if c.ProcessConfig.Entrypoint == "" {
err = errors.New("No entrypoint specified")
logrus.Error(err)
return execdriver.ExitStatus{ExitCode: -1}, err
}
// Build the command line of the process
createProcessParms.CommandLine = c.ProcessConfig.Entrypoint
for _, arg := range c.ProcessConfig.Arguments {
logrus.Debugln("appending ", arg)
createProcessParms.CommandLine += " " + arg
}
logrus.Debugf("CommandLine: %s", createProcessParms.CommandLine)
// Start the command running in the container.
var pid uint32
pid, err = hcsshim.CreateProcessInComputeSystem(c.ID, createProcessParms)
if err != nil {
logrus.Errorf("CreateProcessInComputeSystem() failed %s", err)
return execdriver.ExitStatus{ExitCode: -1}, err
}
//Save the PID as we'll need this in Kill()
logrus.Debugf("PID %d", pid)
c.ContainerPid = int(pid)
if c.ProcessConfig.Tty {
term = NewTtyConsole(c.ID, pid)
} else {
term = NewStdConsole()
}
c.ProcessConfig.Terminal = term
// Maintain our list of active containers. We'll need this later for exec
// and other commands.
d.Lock()
d.activeContainers[c.ID] = &activeContainer{
command: c,
}
d.Unlock()
// Invoke the start callback
if startCallback != nil {
startCallback(&c.ProcessConfig, int(pid))
}
var exitCode int32
exitCode, err = hcsshim.WaitForProcessInComputeSystem(c.ID, pid)
if err != nil {
logrus.Errorf("Failed to WaitForProcessInComputeSystem %s", err)
return execdriver.ExitStatus{ExitCode: -1}, err
}
logrus.Debugf("Exiting Run() exitCode %d id=%s", exitCode, c.ID)
return execdriver.ExitStatus{ExitCode: int(exitCode)}, nil
}

View File

@ -0,0 +1,13 @@
// +build windows
package windows
import (
"fmt"
"github.com/docker/docker/daemon/execdriver"
)
func (d *driver) Stats(id string) (*execdriver.ResourceStats, error) {
return nil, fmt.Errorf("Windows: Stats not implemented")
}

View File

@ -0,0 +1,21 @@
// +build windows
package windows
// StdConsole is for when using a container non-interactively
type StdConsole struct {
}
func NewStdConsole() *StdConsole {
return &StdConsole{}
}
func (s *StdConsole) Resize(h, w int) error {
// we do not need to resize a non tty
return nil
}
func (s *StdConsole) Close() error {
// nothing to close here
return nil
}

View File

@ -0,0 +1,45 @@
// +build windows
package windows
import (
"github.com/Sirupsen/logrus"
"github.com/docker/docker/daemon/execdriver"
"github.com/microsoft/hcsshim"
)
func (d *driver) Terminate(p *execdriver.Command) error {
logrus.Debugf("WindowsExec: Terminate() id=%s", p.ID)
return kill(p.ID, p.ContainerPid)
}
func (d *driver) Kill(p *execdriver.Command, sig int) error {
logrus.Debugf("WindowsExec: Kill() id=%s sig=%d", p.ID, sig)
return kill(p.ID, p.ContainerPid)
}
func kill(id string, pid int) error {
logrus.Debugln("kill() ", id, pid)
var err error
// Terminate Process
if err = hcsshim.TerminateProcessInComputeSystem(id, uint32(pid)); err != nil {
logrus.Warnf("Failed to terminate pid %d in %s", id, pid, err)
// Ignore errors
err = nil
}
if terminateMode {
// Terminate the compute system
if err = hcsshim.TerminateComputeSystem(id); err != nil {
logrus.Errorf("Failed to terminate %s - %s", id, err)
}
} else {
// Shutdown the compute system
if err = hcsshim.TerminateComputeSystem(id); err != nil {
logrus.Errorf("Failed to shutdown %s - %s", id, err)
}
}
return err
}

View File

@ -0,0 +1,31 @@
// +build windows
package windows
import (
"github.com/microsoft/hcsshim"
)
// TtyConsole is for when using a container interactively
type TtyConsole struct {
id string
processid uint32
}
func NewTtyConsole(id string, processid uint32) *TtyConsole {
tty := &TtyConsole{
id: id,
processid: processid,
}
return tty
}
func (t *TtyConsole) Resize(h, w int) error {
// TODO Windows: This is not implemented in HCS. Needs plumbing through
// along with mechanism for buffering
return hcsshim.ResizeConsoleInComputeSystem(t.id, t.processid, h, w)
}
func (t *TtyConsole) Close() error {
return nil
}

View File

@ -1,23 +1,28 @@
// +build windows
/*
This is the Windows driver for containers.
TODO Windows: It is currently a placeholder to allow compilation of the
daemon. Future PRs will have an implementation of this driver.
*/
package windows
import (
"fmt"
"strings"
"sync"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/autogen/dockerversion"
"github.com/docker/docker/daemon/execdriver"
"github.com/docker/docker/pkg/parsers"
)
const (
DriverName = "Windows"
Version = "Placeholder"
// This is a daemon development variable only and should not be
// used for running production containers on Windows.
var dummyMode bool
// This allows the daemon to terminate containers rather than shutdown
var terminateMode bool
var (
DriverName = "Windows 1854"
Version = dockerversion.VERSION + " " + dockerversion.GITCOMMIT
)
type activeContainer struct {
@ -25,73 +30,61 @@ type activeContainer struct {
}
type driver struct {
root string
initPath string
}
type info struct {
ID string
driver *driver
}
func NewDriver(root, initPath string) (*driver, error) {
return &driver{
root: root,
initPath: initPath,
}, nil
}
func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (execdriver.ExitStatus, error) {
return execdriver.ExitStatus{ExitCode: 0}, nil
}
func (d *driver) Terminate(p *execdriver.Command) error {
return nil
}
func (d *driver) Kill(p *execdriver.Command, sig int) error {
return nil
}
func kill(ID string, PID int) error {
return nil
}
func (d *driver) Pause(c *execdriver.Command) error {
return fmt.Errorf("Windows: Containers cannot be paused")
}
func (d *driver) Unpause(c *execdriver.Command) error {
return fmt.Errorf("Windows: Containers cannot be paused")
}
func (i *info) IsRunning() bool {
return false
}
func (d *driver) Info(id string) execdriver.Info {
return &info{
ID: id,
driver: d,
}
root string
initPath string
activeContainers map[string]*activeContainer
sync.Mutex
}
func (d *driver) Name() string {
return fmt.Sprintf("%s Date %s", DriverName, Version)
return fmt.Sprintf("%s %s", DriverName, Version)
}
func (d *driver) GetPidsForContainer(id string) ([]int, error) {
return nil, fmt.Errorf("GetPidsForContainer: GetPidsForContainer() not implemented")
func NewDriver(root, initPath string, options []string) (*driver, error) {
for _, option := range options {
key, val, err := parsers.ParseKeyValueOpt(option)
if err != nil {
return nil, err
}
key = strings.ToLower(key)
switch key {
case "dummy":
switch val {
case "1":
dummyMode = true
logrus.Warn("Using dummy mode in Windows exec driver. This is for development use only!")
}
case "terminate":
switch val {
case "1":
terminateMode = true
logrus.Warn("Using terminate mode in Windows exec driver. This is for testing purposes only.")
}
default:
return nil, fmt.Errorf("Unrecognised exec driver option %s\n", key)
}
}
return &driver{
root: root,
initPath: initPath,
activeContainers: make(map[string]*activeContainer),
}, nil
}
func (d *driver) Clean(id string) error {
return nil
}
func (d *driver) Stats(id string) (*execdriver.ResourceStats, error) {
return nil, fmt.Errorf("Windows: Stats not implemented")
}
func (d *driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
return 0, nil
// setupEnvironmentVariables convert a string array of environment variables
// into a map as required by the HCS. Source array is in format [v1=k1] [v2=k2] etc.
func setupEnvironmentVariables(a []string) map[string]string {
r := make(map[string]string)
for _, s := range a {
arr := strings.Split(s, "=")
if len(arr) == 2 {
r[arr[0]] = arr[1]
}
}
return r
}