Windows: Use new error code mechanism from HCS

Signed-off-by: John Howard <jhoward@microsoft.com>
This commit is contained in:
John Howard 2016-02-03 13:04:41 -08:00
parent fadbbd335c
commit 54263a9393
3 changed files with 45 additions and 35 deletions

View file

@ -4,6 +4,7 @@ package windows
import (
"fmt"
"syscall"
"github.com/Microsoft/hcsshim"
"github.com/Sirupsen/logrus"
@ -17,7 +18,6 @@ func (d *Driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessCo
term execdriver.Terminal
err error
exitCode int32
errno uint32
)
active := d.activeContainers[c.ID]
@ -41,13 +41,13 @@ func (d *Driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessCo
}
// Start the command running in the container.
pid, stdin, stdout, stderr, rc, err := hcsshim.CreateProcessInComputeSystem(c.ID, pipes.Stdin != nil, true, !processConfig.Tty, createProcessParms)
pid, stdin, stdout, stderr, 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 {
if herr, ok := err.(*hcsshim.HcsError); ok && herr.Err == hcsshim.WSAEINVAL {
return -1, fmt.Errorf("The limit of docker execs per Hyper-V container has been exceeded")
}
logrus.Errorf("CreateProcessInComputeSystem() failed %s", err)
@ -75,12 +75,12 @@ func (d *Driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessCo
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)
if exitCode, err = hcsshim.WaitForProcessInComputeSystem(c.ID, pid, hcsshim.TimeoutInfinite); err != nil {
if herr, ok := err.(*hcsshim.HcsError); ok && herr.Err == syscall.ERROR_BROKEN_PIPE {
logrus.Debugf("Exiting Run() after WaitForProcessInComputeSystem failed with recognised error %s", err)
return hcsshim.WaitErrExecFailed, nil
}
logrus.Warnf("WaitForProcessInComputeSystem failed (container may have been killed): 0x%X %s", errno, err)
logrus.Warnf("WaitForProcessInComputeSystem failed (container may have been killed): %s", err)
return -1, err
}

View file

@ -9,6 +9,7 @@ import (
"path/filepath"
"strconv"
"strings"
"syscall"
"time"
"github.com/Microsoft/hcsshim"
@ -20,6 +21,16 @@ import (
// preconfigured on the server.
const defaultContainerNAT = "ContainerNAT"
// Win32 error codes that are used for various workarounds
// These really should be ALL_CAPS to match golangs syscall library and standard
// Win32 error conventions, but golint insists on CamelCase.
const (
CoEClassstring = syscall.Errno(0x800401F3) // Invalid class string
ErrorNoNetwork = syscall.Errno(1222) // The network is not present or not started
ErrorBadPathname = syscall.Errno(161) // The specified path is invalid
ErrorInvalidObject = syscall.Errno(0x800710D8) // The object identifier does not represent a valid object
)
type layer struct {
ID string
Path string
@ -237,17 +248,19 @@ func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, hooks execd
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
!strings.Contains(err.Error(), `Win32 API call returned error r1=0x800710d8`) { // The object identifier does not represent a valid object
logrus.Debugln("Failed to create temporary container ", err)
return execdriver.ExitStatus{ExitCode: -1}, err
if herr, ok := err.(*hcsshim.HcsError); ok {
if herr.Err != syscall.ERROR_NOT_FOUND && // Element not found
herr.Err != syscall.ERROR_FILE_NOT_FOUND && // The system cannot find the file specified
herr.Err != ErrorNoNetwork && // The network is not present or not started
herr.Err != ErrorBadPathname && // The specified path is invalid
herr.Err != CoEClassstring && // Invalid class string
herr.Err != ErrorInvalidObject { // The object identifier does not represent a valid object
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)
}
logrus.Warnf("Invoking Windows TP4 retry hack (%d of %d)", i, maxAttempts-1)
time.Sleep(50 * time.Millisecond)
}
} else {
break
@ -265,16 +278,17 @@ func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, hooks execd
// 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)
if err := hcsshim.TerminateComputeSystem(c.ID, hcsshim.TimeoutInfinite, "exec-run-defer"); err != nil {
logrus.Warnf("Ignoring error from TerminateComputeSystem %s", 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)
if err := hcsshim.ShutdownComputeSystem(c.ID, hcsshim.TimeoutInfinite, "exec-run-defer"); err != nil {
if herr, ok := err.(*hcsshim.HcsError); !ok ||
(herr.Err != hcsshim.ERROR_SHUTDOWN_IN_PROGRESS &&
herr.Err != ErrorBadPathname &&
herr.Err != syscall.ERROR_PATH_NOT_FOUND) {
logrus.Warnf("Ignoring error from ShutdownComputeSystem %s", err)
}
}
}
@ -296,7 +310,7 @@ func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, hooks execd
}
// Start the command running in the container.
pid, stdin, stdout, stderr, _, err := hcsshim.CreateProcessInComputeSystem(c.ID, pipes.Stdin != nil, true, !c.ProcessConfig.Tty, createProcessParms)
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
@ -333,13 +347,9 @@ func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, hooks execd
hooks.Start(&c.ProcessConfig, int(pid), chOOM)
}
var (
exitCode int32
errno uint32
)
exitCode, errno, err = hcsshim.WaitForProcessInComputeSystem(c.ID, pid, hcsshim.TimeoutInfinite)
exitCode, err := hcsshim.WaitForProcessInComputeSystem(c.ID, pid, hcsshim.TimeoutInfinite)
if err != nil {
if errno != hcsshim.Win32PipeHasBeenEnded {
if herr, ok := err.(*hcsshim.HcsError); ok && herr.Err != syscall.ERROR_BROKEN_PIPE {
logrus.Warnf("WaitForProcessInComputeSystem failed (container may have been killed): %s", err)
}
// Do NOT return err here as the container would have

View file

@ -28,8 +28,8 @@ func kill(id string, pid int, sig syscall.Signal) error {
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)
if err := hcsshim.TerminateComputeSystem(id, hcsshim.TimeoutInfinite, context); err != nil {
logrus.Errorf("Failed to terminate %s - %q", id, err)
}
} else {
@ -41,8 +41,8 @@ func kill(id string, pid int, sig syscall.Signal) error {
}
// 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)
if err := hcsshim.ShutdownComputeSystem(id, hcsshim.TimeoutInfinite, context); err != nil {
logrus.Errorf("Failed to shutdown %s - %q", id, err)
}
}
return err