Initial commit
This commit is contained in:
8
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/clean.go
generated
vendored
Normal file
8
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/clean.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build windows
|
||||
|
||||
package windows
|
||||
|
||||
// Clean implements the exec driver Driver interface.
|
||||
func (d *Driver) Clean(id string) error {
|
||||
return nil
|
||||
}
|
||||
36
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/commandlinebuilder.go
generated
vendored
Normal file
36
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/commandlinebuilder.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
//+build windows
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/hyperhq/hypercli/daemon/execdriver"
|
||||
)
|
||||
|
||||
// createCommandLine creates a command line from the Entrypoint and args
|
||||
// of the ProcessConfig. It escapes the arguments if they are not already
|
||||
// escaped
|
||||
func createCommandLine(processConfig *execdriver.ProcessConfig, alreadyEscaped bool) (commandLine string, err error) {
|
||||
// While this should get caught earlier, just in case, validate that we
|
||||
// have something to run.
|
||||
if processConfig.Entrypoint == "" {
|
||||
return "", errors.New("No entrypoint specified")
|
||||
}
|
||||
|
||||
// Build the command line of the process
|
||||
commandLine = processConfig.Entrypoint
|
||||
logrus.Debugf("Entrypoint: %s", processConfig.Entrypoint)
|
||||
for _, arg := range processConfig.Arguments {
|
||||
logrus.Debugf("appending %s", arg)
|
||||
if !alreadyEscaped {
|
||||
arg = syscall.EscapeArg(arg)
|
||||
}
|
||||
commandLine += " " + arg
|
||||
}
|
||||
|
||||
logrus.Debugf("commandLine: %s", commandLine)
|
||||
return commandLine, nil
|
||||
}
|
||||
89
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/exec.go
generated
vendored
Normal file
89
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/exec.go
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
// +build windows
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Microsoft/hcsshim"
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/hyperhq/hypercli/daemon/execdriver"
|
||||
)
|
||||
|
||||
// Exec implements the exec driver Driver interface.
|
||||
func (d *Driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes, hooks execdriver.Hooks) (int, error) {
|
||||
|
||||
var (
|
||||
term execdriver.Terminal
|
||||
err error
|
||||
exitCode int32
|
||||
errno uint32
|
||||
)
|
||||
|
||||
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.Env
|
||||
createProcessParms.Environment = setupEnvironmentVariables(processConfig.Env)
|
||||
|
||||
// Create the commandline for the process // Note NOT c.ProcessConfig
|
||||
createProcessParms.CommandLine, err = createCommandLine(processConfig, false)
|
||||
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
// Start the command running in the container.
|
||||
pid, stdin, stdout, stderr, rc, err := hcsshim.CreateProcessInComputeSystem(c.ID, pipes.Stdin != nil, true, !processConfig.Tty, createProcessParms)
|
||||
if err != nil {
|
||||
// TODO Windows: TP4 Workaround. In Hyper-V containers, there is a limitation
|
||||
// of one exec per container. This should be fixed post TP4. CreateProcessInComputeSystem
|
||||
// will return a specific error which we handle here to give a good error message
|
||||
// back to the user instead of an inactionable "An invalid argument was supplied"
|
||||
if rc == hcsshim.Win32InvalidArgument {
|
||||
return -1, fmt.Errorf("The limit of docker execs per Hyper-V container has been exceeded")
|
||||
}
|
||||
logrus.Errorf("CreateProcessInComputeSystem() failed %s", err)
|
||||
return -1, err
|
||||
}
|
||||
|
||||
// Now that the process has been launched, begin copying data to and from
|
||||
// the named pipes for the std handles.
|
||||
setupPipes(stdin, stdout, stderr, pipes)
|
||||
|
||||
// Note NOT c.ProcessConfig.Tty
|
||||
if processConfig.Tty {
|
||||
term = NewTtyConsole(c.ID, pid)
|
||||
} else {
|
||||
term = NewStdConsole()
|
||||
}
|
||||
processConfig.Terminal = term
|
||||
|
||||
// Invoke the start callback
|
||||
if hooks.Start != nil {
|
||||
// A closed channel for OOM is returned here as it will be
|
||||
// non-blocking and return the correct result when read.
|
||||
chOOM := make(chan struct{})
|
||||
close(chOOM)
|
||||
hooks.Start(&c.ProcessConfig, int(pid), chOOM)
|
||||
}
|
||||
|
||||
if exitCode, errno, err = hcsshim.WaitForProcessInComputeSystem(c.ID, pid, hcsshim.TimeoutInfinite); err != nil {
|
||||
if errno == hcsshim.Win32PipeHasBeenEnded {
|
||||
logrus.Debugf("Exiting Run() after WaitForProcessInComputeSystem failed with recognised error 0x%X", errno)
|
||||
return hcsshim.WaitErrExecFailed, nil
|
||||
}
|
||||
logrus.Warnf("WaitForProcessInComputeSystem failed (container may have been killed): 0x%X %s", errno, err)
|
||||
return -1, err
|
||||
}
|
||||
|
||||
logrus.Debugln("Exiting Run()", c.ID)
|
||||
return int(exitCode), nil
|
||||
}
|
||||
11
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/getpids.go
generated
vendored
Normal file
11
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/getpids.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// +build windows
|
||||
|
||||
package windows
|
||||
|
||||
import "fmt"
|
||||
|
||||
// GetPidsForContainer implements the exec driver Driver interface.
|
||||
func (d *Driver) GetPidsForContainer(id string) ([]int, error) {
|
||||
// TODO Windows: Implementation required.
|
||||
return nil, fmt.Errorf("GetPidsForContainer: GetPidsForContainer() not implemented")
|
||||
}
|
||||
63
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/namedpipes.go
generated
vendored
Normal file
63
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/namedpipes.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
// +build windows
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/hyperhq/hypercli/daemon/execdriver"
|
||||
)
|
||||
|
||||
// General comment. Handling I/O for a container is very different to Linux.
|
||||
// We use a named pipe to HCS to copy I/O both in and out of the container,
|
||||
// very similar to how docker daemon communicates with a CLI.
|
||||
|
||||
// startStdinCopy asynchronously copies an io.Reader to the container's
|
||||
// process's stdin pipe and closes the pipe when there is no more data to copy.
|
||||
func startStdinCopy(dst io.WriteCloser, src io.Reader) {
|
||||
|
||||
// Anything that comes from the client stdin should be copied
|
||||
// across to the stdin named pipe of the container.
|
||||
go func() {
|
||||
defer dst.Close()
|
||||
bytes, err := io.Copy(dst, src)
|
||||
log := fmt.Sprintf("Copied %d bytes from stdin.", bytes)
|
||||
if err != nil {
|
||||
log = log + " err=" + err.Error()
|
||||
}
|
||||
logrus.Debugf(log)
|
||||
}()
|
||||
}
|
||||
|
||||
// startStdouterrCopy asynchronously copies data from the container's process's
|
||||
// stdout or stderr pipe to an io.Writer and closes the pipe when there is no
|
||||
// more data to copy.
|
||||
func startStdouterrCopy(dst io.Writer, src io.ReadCloser, name string) {
|
||||
// Anything that comes from the container named pipe stdout/err should be copied
|
||||
// across to the stdout/err of the client
|
||||
go func() {
|
||||
defer src.Close()
|
||||
bytes, err := io.Copy(dst, src)
|
||||
log := fmt.Sprintf("Copied %d bytes from %s.", bytes, name)
|
||||
if err != nil {
|
||||
log = log + " err=" + err.Error()
|
||||
}
|
||||
logrus.Debugf(log)
|
||||
}()
|
||||
}
|
||||
|
||||
// setupPipes starts the asynchronous copying of data to and from the named
|
||||
// pipes used byt he HCS for the std handles.
|
||||
func setupPipes(stdin io.WriteCloser, stdout, stderr io.ReadCloser, pipes *execdriver.Pipes) {
|
||||
if stdin != nil {
|
||||
startStdinCopy(stdin, pipes.Stdin)
|
||||
}
|
||||
if stdout != nil {
|
||||
startStdouterrCopy(pipes.Stdout, stdout, "stdout")
|
||||
}
|
||||
if stderr != nil {
|
||||
startStdouterrCopy(pipes.Stderr, stderr, "stderr")
|
||||
}
|
||||
}
|
||||
19
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/pauseunpause.go
generated
vendored
Normal file
19
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/pauseunpause.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
// +build windows
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hyperhq/hypercli/daemon/execdriver"
|
||||
)
|
||||
|
||||
// Pause implements the exec driver Driver interface.
|
||||
func (d *Driver) Pause(c *execdriver.Command) error {
|
||||
return fmt.Errorf("Windows: Containers cannot be paused")
|
||||
}
|
||||
|
||||
// Unpause implements the exec driver Driver interface.
|
||||
func (d *Driver) Unpause(c *execdriver.Command) error {
|
||||
return fmt.Errorf("Windows: Containers cannot be paused")
|
||||
}
|
||||
359
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/run.go
generated
vendored
Normal file
359
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/run.go
generated
vendored
Normal file
@@ -0,0 +1,359 @@
|
||||
// +build windows
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Microsoft/hcsshim"
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/hyperhq/hypercli/daemon/execdriver"
|
||||
)
|
||||
|
||||
// defaultContainerNAT is the default name of the container NAT device that is
|
||||
// preconfigured on the server.
|
||||
const defaultContainerNAT = "ContainerNAT"
|
||||
|
||||
type layer struct {
|
||||
ID string
|
||||
Path string
|
||||
}
|
||||
|
||||
type defConfig struct {
|
||||
DefFile string
|
||||
}
|
||||
|
||||
type portBinding struct {
|
||||
Protocol string
|
||||
InternalPort int
|
||||
ExternalPort int
|
||||
}
|
||||
|
||||
type natSettings struct {
|
||||
Name string
|
||||
PortBindings []portBinding
|
||||
}
|
||||
|
||||
type networkConnection struct {
|
||||
NetworkName string
|
||||
// TODO Windows: Add Ip4Address string to this structure when hooked up in
|
||||
// docker CLI. This is present in the HCS JSON handler.
|
||||
EnableNat bool
|
||||
Nat natSettings
|
||||
}
|
||||
type networkSettings struct {
|
||||
MacAddress string
|
||||
}
|
||||
|
||||
type device struct {
|
||||
DeviceType string
|
||||
Connection interface{}
|
||||
Settings interface{}
|
||||
}
|
||||
|
||||
type mappedDir struct {
|
||||
HostPath string
|
||||
ContainerPath string
|
||||
ReadOnly bool
|
||||
}
|
||||
|
||||
type containerInit struct {
|
||||
SystemType string // HCS requires this to be hard-coded to "Container"
|
||||
Name string // Name of the container. We use the docker ID.
|
||||
Owner string // The management platform that created this container
|
||||
IsDummy bool // Used for development purposes.
|
||||
VolumePath string // Windows volume path for scratch space
|
||||
Devices []device // Devices used by the container
|
||||
IgnoreFlushesDuringBoot bool // Optimisation hint for container startup in Windows
|
||||
LayerFolderPath string // Where the layer folders are located
|
||||
Layers []layer // List of storage layers
|
||||
ProcessorWeight int64 `json:",omitempty"` // CPU Shares 0..10000 on Windows; where 0 will be ommited and HCS will default.
|
||||
HostName string // Hostname
|
||||
MappedDirectories []mappedDir // List of mapped directories (volumes/mounts)
|
||||
SandboxPath string // Location of unmounted sandbox (used for Hyper-V containers, not Windows Server containers)
|
||||
HvPartition bool // True if it a Hyper-V Container
|
||||
}
|
||||
|
||||
// defaultOwner is a tag passed to HCS to allow it to differentiate between
|
||||
// container creator management stacks. We hard code "docker" in the case
|
||||
// of docker.
|
||||
const defaultOwner = "docker"
|
||||
|
||||
// Run implements the exec driver Driver interface
|
||||
func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, hooks execdriver.Hooks) (execdriver.ExitStatus, error) {
|
||||
|
||||
var (
|
||||
term execdriver.Terminal
|
||||
err error
|
||||
)
|
||||
|
||||
cu := &containerInit{
|
||||
SystemType: "Container",
|
||||
Name: c.ID,
|
||||
Owner: defaultOwner,
|
||||
IsDummy: dummyMode,
|
||||
VolumePath: c.Rootfs,
|
||||
IgnoreFlushesDuringBoot: c.FirstStart,
|
||||
LayerFolderPath: c.LayerFolder,
|
||||
ProcessorWeight: c.Resources.CPUShares,
|
||||
HostName: c.Hostname,
|
||||
}
|
||||
|
||||
cu.HvPartition = c.HvPartition
|
||||
|
||||
if cu.HvPartition {
|
||||
cu.SandboxPath = filepath.Dir(c.LayerFolder)
|
||||
} else {
|
||||
cu.VolumePath = c.Rootfs
|
||||
cu.LayerFolderPath = c.LayerFolder
|
||||
}
|
||||
|
||||
for _, layerPath := range c.LayerPaths {
|
||||
_, filename := filepath.Split(layerPath)
|
||||
g, err := hcsshim.NameToGuid(filename)
|
||||
if err != nil {
|
||||
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||
}
|
||||
cu.Layers = append(cu.Layers, layer{
|
||||
ID: g.ToString(),
|
||||
Path: layerPath,
|
||||
})
|
||||
}
|
||||
|
||||
// Add the mounts (volumes, bind mounts etc) to the structure
|
||||
mds := make([]mappedDir, len(c.Mounts))
|
||||
for i, mount := range c.Mounts {
|
||||
mds[i] = mappedDir{
|
||||
HostPath: mount.Source,
|
||||
ContainerPath: mount.Destination,
|
||||
ReadOnly: !mount.Writable}
|
||||
}
|
||||
cu.MappedDirectories = mds
|
||||
|
||||
// TODO Windows. At some point, when there is CLI on docker run to
|
||||
// enable the IP Address of the container to be passed into docker run,
|
||||
// the IP Address needs to be wired through to HCS in the JSON. It
|
||||
// would be present in c.Network.Interface.IPAddress. See matching
|
||||
// TODO in daemon\container_windows.go, function populateCommand.
|
||||
|
||||
if c.Network.Interface != nil {
|
||||
|
||||
var pbs []portBinding
|
||||
|
||||
// Enumerate through the port bindings specified by the user and convert
|
||||
// them into the internal structure matching the JSON blob that can be
|
||||
// understood by the HCS.
|
||||
for i, v := range c.Network.Interface.PortBindings {
|
||||
proto := strings.ToUpper(i.Proto())
|
||||
if proto != "TCP" && proto != "UDP" {
|
||||
return execdriver.ExitStatus{ExitCode: -1}, fmt.Errorf("invalid protocol %s", i.Proto())
|
||||
}
|
||||
|
||||
if len(v) > 1 {
|
||||
return execdriver.ExitStatus{ExitCode: -1}, fmt.Errorf("Windows does not support more than one host port in NAT settings")
|
||||
}
|
||||
|
||||
for _, v2 := range v {
|
||||
var (
|
||||
iPort, ePort int
|
||||
err error
|
||||
)
|
||||
if len(v2.HostIP) != 0 {
|
||||
return execdriver.ExitStatus{ExitCode: -1}, fmt.Errorf("Windows does not support host IP addresses in NAT settings")
|
||||
}
|
||||
if ePort, err = strconv.Atoi(v2.HostPort); err != nil {
|
||||
return execdriver.ExitStatus{ExitCode: -1}, fmt.Errorf("invalid container port %s: %s", v2.HostPort, err)
|
||||
}
|
||||
if iPort, err = strconv.Atoi(i.Port()); err != nil {
|
||||
return execdriver.ExitStatus{ExitCode: -1}, fmt.Errorf("invalid internal port %s: %s", i.Port(), err)
|
||||
}
|
||||
if iPort < 0 || iPort > 65535 || ePort < 0 || ePort > 65535 {
|
||||
return execdriver.ExitStatus{ExitCode: -1}, fmt.Errorf("specified NAT port is not in allowed range")
|
||||
}
|
||||
pbs = append(pbs,
|
||||
portBinding{ExternalPort: ePort,
|
||||
InternalPort: iPort,
|
||||
Protocol: proto})
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Windows: TP3 workaround. Allow the user to override the name of
|
||||
// the Container NAT device through an environment variable. This will
|
||||
// ultimately be a global daemon parameter on Windows, similar to -b
|
||||
// for the name of the virtual switch (aka bridge).
|
||||
cn := os.Getenv("DOCKER_CONTAINER_NAT")
|
||||
if len(cn) == 0 {
|
||||
cn = defaultContainerNAT
|
||||
}
|
||||
|
||||
dev := device{
|
||||
DeviceType: "Network",
|
||||
Connection: &networkConnection{
|
||||
NetworkName: c.Network.Interface.Bridge,
|
||||
// TODO Windows: Fixme, next line. Needs HCS fix.
|
||||
EnableNat: false,
|
||||
Nat: natSettings{
|
||||
Name: cn,
|
||||
PortBindings: pbs,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if c.Network.Interface.MacAddress != "" {
|
||||
windowsStyleMAC := strings.Replace(
|
||||
c.Network.Interface.MacAddress, ":", "-", -1)
|
||||
dev.Settings = networkSettings{
|
||||
MacAddress: windowsStyleMAC,
|
||||
}
|
||||
}
|
||||
cu.Devices = append(cu.Devices, dev)
|
||||
} else {
|
||||
logrus.Debugln("No network interface")
|
||||
}
|
||||
|
||||
configurationb, err := json.Marshal(cu)
|
||||
if err != nil {
|
||||
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||
}
|
||||
|
||||
configuration := string(configurationb)
|
||||
|
||||
// TODO Windows TP5 timeframe. Remove when TP4 is no longer supported.
|
||||
// The following a workaround for Windows TP4 which has a networking
|
||||
// bug which fairly frequently returns an error. Back off and retry.
|
||||
maxAttempts := 1
|
||||
if TP4RetryHack {
|
||||
maxAttempts = 5
|
||||
}
|
||||
i := 0
|
||||
for i < maxAttempts {
|
||||
i++
|
||||
err = hcsshim.CreateComputeSystem(c.ID, configuration)
|
||||
if err != nil {
|
||||
if TP4RetryHack {
|
||||
if !strings.Contains(err.Error(), `Win32 API call returned error r1=0x800401f3`) && // Invalid class string
|
||||
!strings.Contains(err.Error(), `Win32 API call returned error r1=0x80070490`) && // Element not found
|
||||
!strings.Contains(err.Error(), `Win32 API call returned error r1=0x80070002`) && // The system cannot find the file specified
|
||||
!strings.Contains(err.Error(), `Win32 API call returned error r1=0x800704c6`) && // The network is not present or not started
|
||||
!strings.Contains(err.Error(), `Win32 API call returned error r1=0x800700a1`) { // The specified path is invalid
|
||||
logrus.Debugln("Failed to create temporary container ", err)
|
||||
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||
}
|
||||
logrus.Warnf("Invoking Windows TP4 retry hack (%d of %d)", i, maxAttempts-1)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 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 forceKill {
|
||||
logrus.Debugf("Forcibly terminating container %s", c.ID)
|
||||
if errno, err := hcsshim.TerminateComputeSystem(c.ID, hcsshim.TimeoutInfinite, "exec-run-defer"); err != nil {
|
||||
logrus.Warnf("Ignoring error from TerminateComputeSystem 0x%X %s", errno, err)
|
||||
}
|
||||
} else {
|
||||
logrus.Debugf("Shutting down container %s", c.ID)
|
||||
if errno, err := hcsshim.ShutdownComputeSystem(c.ID, hcsshim.TimeoutInfinite, "exec-run-defer"); err != nil {
|
||||
if errno != hcsshim.Win32SystemShutdownIsInProgress &&
|
||||
errno != hcsshim.Win32SpecifiedPathInvalid &&
|
||||
errno != hcsshim.Win32SystemCannotFindThePathSpecified {
|
||||
logrus.Warnf("Ignoring error from ShutdownComputeSystem 0x%X %s", errno, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
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)
|
||||
|
||||
createProcessParms.CommandLine, err = createCommandLine(&c.ProcessConfig, c.ArgsEscaped)
|
||||
|
||||
if err != nil {
|
||||
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||
}
|
||||
|
||||
// Start the command running in the container.
|
||||
pid, stdin, stdout, stderr, _, err := hcsshim.CreateProcessInComputeSystem(c.ID, pipes.Stdin != nil, true, !c.ProcessConfig.Tty, createProcessParms)
|
||||
if err != nil {
|
||||
logrus.Errorf("CreateProcessInComputeSystem() failed %s", err)
|
||||
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||
}
|
||||
|
||||
// Now that the process has been launched, begin copying data to and from
|
||||
// the named pipes for the std handles.
|
||||
setupPipes(stdin, stdout, stderr, pipes)
|
||||
|
||||
//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()
|
||||
|
||||
if hooks.Start != nil {
|
||||
// A closed channel for OOM is returned here as it will be
|
||||
// non-blocking and return the correct result when read.
|
||||
chOOM := make(chan struct{})
|
||||
close(chOOM)
|
||||
hooks.Start(&c.ProcessConfig, int(pid), chOOM)
|
||||
}
|
||||
|
||||
var (
|
||||
exitCode int32
|
||||
errno uint32
|
||||
)
|
||||
exitCode, errno, err = hcsshim.WaitForProcessInComputeSystem(c.ID, pid, hcsshim.TimeoutInfinite)
|
||||
if err != nil {
|
||||
if errno != hcsshim.Win32PipeHasBeenEnded {
|
||||
logrus.Warnf("WaitForProcessInComputeSystem failed (container may have been killed): %s", err)
|
||||
}
|
||||
// Do NOT return err here as the container would have
|
||||
// started, otherwise docker will deadlock. It's perfectly legitimate
|
||||
// for WaitForProcessInComputeSystem to fail in situations such
|
||||
// as the container being killed on another thread.
|
||||
return execdriver.ExitStatus{ExitCode: hcsshim.WaitErrExecFailed}, nil
|
||||
}
|
||||
|
||||
logrus.Debugf("Exiting Run() exitCode %d id=%s", exitCode, c.ID)
|
||||
return execdriver.ExitStatus{ExitCode: int(exitCode)}, nil
|
||||
}
|
||||
|
||||
// SupportsHooks implements the execdriver Driver interface.
|
||||
// The windows driver does not support the hook mechanism
|
||||
func (d *Driver) SupportsHooks() bool {
|
||||
return false
|
||||
}
|
||||
14
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/stats.go
generated
vendored
Normal file
14
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/stats.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// +build windows
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hyperhq/hypercli/daemon/execdriver"
|
||||
)
|
||||
|
||||
// Stats implements the exec driver Driver interface.
|
||||
func (d *Driver) Stats(id string) (*execdriver.ResourceStats, error) {
|
||||
return nil, fmt.Errorf("Windows: Stats not implemented")
|
||||
}
|
||||
24
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/stdconsole.go
generated
vendored
Normal file
24
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/stdconsole.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
// +build windows
|
||||
|
||||
package windows
|
||||
|
||||
// StdConsole is for when using a container non-interactively
|
||||
type StdConsole struct {
|
||||
}
|
||||
|
||||
// NewStdConsole returns a new StdConsole struct.
|
||||
func NewStdConsole() *StdConsole {
|
||||
return &StdConsole{}
|
||||
}
|
||||
|
||||
// Resize implements Resize method of Terminal interface.
|
||||
func (s *StdConsole) Resize(h, w int) error {
|
||||
// we do not need to resize a non tty
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close implements Close method of Terminal interface.
|
||||
func (s *StdConsole) Close() error {
|
||||
// nothing to close here
|
||||
return nil
|
||||
}
|
||||
49
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/terminatekill.go
generated
vendored
Normal file
49
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/terminatekill.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
// +build windows
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
|
||||
"github.com/Microsoft/hcsshim"
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/hyperhq/hypercli/daemon/execdriver"
|
||||
)
|
||||
|
||||
// Terminate implements the exec driver Driver interface.
|
||||
func (d *Driver) Terminate(p *execdriver.Command) error {
|
||||
return kill(p.ID, p.ContainerPid, syscall.SIGTERM)
|
||||
}
|
||||
|
||||
// Kill implements the exec driver Driver interface.
|
||||
func (d *Driver) Kill(p *execdriver.Command, sig int) error {
|
||||
return kill(p.ID, p.ContainerPid, syscall.Signal(sig))
|
||||
}
|
||||
|
||||
func kill(id string, pid int, sig syscall.Signal) error {
|
||||
logrus.Debugf("WindowsExec: kill() id=%s pid=%d sig=%d", id, pid, sig)
|
||||
var err error
|
||||
context := fmt.Sprintf("kill: sig=%d pid=%d", sig, pid)
|
||||
|
||||
if sig == syscall.SIGKILL || forceKill {
|
||||
// Terminate the compute system
|
||||
if errno, err := hcsshim.TerminateComputeSystem(id, hcsshim.TimeoutInfinite, context); err != nil {
|
||||
logrus.Errorf("Failed to terminate %s - 0x%X %q", id, errno, err)
|
||||
}
|
||||
|
||||
} else {
|
||||
// Terminate Process
|
||||
if err = hcsshim.TerminateProcessInComputeSystem(id, uint32(pid)); err != nil {
|
||||
logrus.Warnf("Failed to terminate pid %d in %s: %q", pid, id, err)
|
||||
// Ignore errors
|
||||
err = nil
|
||||
}
|
||||
|
||||
// Shutdown the compute system
|
||||
if errno, err := hcsshim.ShutdownComputeSystem(id, hcsshim.TimeoutInfinite, context); err != nil {
|
||||
logrus.Errorf("Failed to shutdown %s - 0x%X %q", id, errno, err)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
32
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/ttyconsole.go
generated
vendored
Normal file
32
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/ttyconsole.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
// +build windows
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"github.com/Microsoft/hcsshim"
|
||||
)
|
||||
|
||||
// TtyConsole implements the exec driver Terminal interface.
|
||||
type TtyConsole struct {
|
||||
id string
|
||||
processid uint32
|
||||
}
|
||||
|
||||
// NewTtyConsole returns a new TtyConsole struct.
|
||||
func NewTtyConsole(id string, processid uint32) *TtyConsole {
|
||||
tty := &TtyConsole{
|
||||
id: id,
|
||||
processid: processid,
|
||||
}
|
||||
return tty
|
||||
}
|
||||
|
||||
// Resize implements Resize method of Terminal interface.
|
||||
func (t *TtyConsole) Resize(h, w int) error {
|
||||
return hcsshim.ResizeConsoleInComputeSystem(t.id, t.processid, h, w)
|
||||
}
|
||||
|
||||
// Close implements Close method of Terminal interface.
|
||||
func (t *TtyConsole) Close() error {
|
||||
return nil
|
||||
}
|
||||
14
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/unsupported.go
generated
vendored
Normal file
14
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/unsupported.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// +build !windows
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hyperhq/hypercli/daemon/execdriver"
|
||||
)
|
||||
|
||||
// NewDriver returns a new execdriver.Driver
|
||||
func NewDriver(root, initPath string) (execdriver.Driver, error) {
|
||||
return nil, fmt.Errorf("Windows driver not supported on non-Windows")
|
||||
}
|
||||
14
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/update.go
generated
vendored
Normal file
14
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/update.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// +build windows
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hyperhq/hypercli/daemon/execdriver"
|
||||
)
|
||||
|
||||
// Update updates resource configs for a container.
|
||||
func (d *Driver) Update(c *execdriver.Command) error {
|
||||
return fmt.Errorf("Windows: Update not implemented")
|
||||
}
|
||||
146
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/windows.go
generated
vendored
Normal file
146
vendor/github.com/hyperhq/hypercli/daemon/execdriver/windows/windows.go
generated
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
// +build windows
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/hyperhq/hypercli/daemon/execdriver"
|
||||
"github.com/hyperhq/hypercli/dockerversion"
|
||||
"github.com/hyperhq/hypercli/pkg/parsers"
|
||||
"github.com/docker/engine-api/types/container"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
// TP4RetryHack is a hack to retry CreateComputeSystem if it fails with
|
||||
// known return codes from Windows due to bugs in TP4.
|
||||
var TP4RetryHack bool
|
||||
|
||||
// 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
|
||||
// This allows the daemon to force kill (HCS terminate) rather than shutdown
|
||||
var forceKill bool
|
||||
|
||||
// DefaultIsolation allows users to specify a default isolation mode for
|
||||
// when running a container on Windows. For example docker daemon -D
|
||||
// --exec-opt isolation=hyperv will cause Windows to always run containers
|
||||
// as Hyper-V containers unless otherwise specified.
|
||||
var DefaultIsolation container.IsolationLevel = "process"
|
||||
|
||||
// Define name and version for windows
|
||||
var (
|
||||
DriverName = "Windows 1854"
|
||||
Version = dockerversion.Version + " " + dockerversion.GitCommit
|
||||
)
|
||||
|
||||
type activeContainer struct {
|
||||
command *execdriver.Command
|
||||
}
|
||||
|
||||
// Driver contains all information for windows driver,
|
||||
// it implements execdriver.Driver
|
||||
type Driver struct {
|
||||
root string
|
||||
activeContainers map[string]*activeContainer
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// Name implements the exec driver Driver interface.
|
||||
func (d *Driver) Name() string {
|
||||
return fmt.Sprintf("\n Name: %s\n Build: %s \n Default Isolation: %s", DriverName, Version, DefaultIsolation)
|
||||
}
|
||||
|
||||
// NewDriver returns a new windows driver, called from NewDriver of execdriver.
|
||||
func NewDriver(root 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 "forcekill":
|
||||
switch val {
|
||||
case "1":
|
||||
forceKill = true
|
||||
logrus.Warn("Using force kill mode in Windows exec driver. This is for testing purposes only.")
|
||||
}
|
||||
|
||||
case "isolation":
|
||||
if !container.IsolationLevel(val).IsValid() {
|
||||
return nil, fmt.Errorf("Unrecognised exec driver option 'isolation':'%s'", val)
|
||||
}
|
||||
if container.IsolationLevel(val).IsHyperV() {
|
||||
DefaultIsolation = "hyperv"
|
||||
}
|
||||
logrus.Infof("Windows default isolation level: '%s'", val)
|
||||
default:
|
||||
return nil, fmt.Errorf("Unrecognised exec driver option %s\n", key)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Windows TP5 timeframe. Remove this next block of code once TP4
|
||||
// is no longer supported. Also remove the workaround in run.go.
|
||||
//
|
||||
// Hack for TP4 - determine the version of Windows from the registry.
|
||||
// This overcomes an issue on TP4 which causes CreateComputeSystem to
|
||||
// intermittently fail. It's predominantly here to make Windows to Windows
|
||||
// CI more reliable.
|
||||
TP4RetryHack = false
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
return &Driver{}, err
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
s, _, err := k.GetStringValue("BuildLab")
|
||||
if err != nil {
|
||||
return &Driver{}, err
|
||||
}
|
||||
parts := strings.Split(s, ".")
|
||||
if len(parts) < 1 {
|
||||
return &Driver{}, err
|
||||
}
|
||||
var val int
|
||||
if val, err = strconv.Atoi(parts[0]); err != nil {
|
||||
return &Driver{}, err
|
||||
}
|
||||
if val < 14250 {
|
||||
TP4RetryHack = true
|
||||
}
|
||||
// End of Windows TP4 hack
|
||||
|
||||
return &Driver{
|
||||
root: root,
|
||||
activeContainers: make(map[string]*activeContainer),
|
||||
}, 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
|
||||
}
|
||||
Reference in New Issue
Block a user