From c58d0358c359e391707698b12850524137ef56ae Mon Sep 17 00:00:00 2001 From: Darren Stahl Date: Wed, 3 Aug 2016 17:47:43 -0700 Subject: [PATCH] Revendor hcsshim Signed-off-by: Darren Stahl --- hack/vendor.sh | 2 +- libcontainerd/client_windows.go | 4 +- libcontainerd/container_windows.go | 8 +- .../github.com/Microsoft/hcsshim/baselayer.go | 29 ++++- .../src/github.com/Microsoft/hcsshim/cgo.go | 7 ++ .../github.com/Microsoft/hcsshim/container.go | 107 +++++++++++++---- .../Microsoft/hcsshim/createcomputesystem.go | 22 ---- .../Microsoft/hcsshim/createprocess.go | 101 ---------------- .../github.com/Microsoft/hcsshim/errors.go | 113 ++++++++++++------ .../hcsshim/getcomputesystemproperties.go | 43 ------- .../github.com/Microsoft/hcsshim/interface.go | 3 + .../github.com/Microsoft/hcsshim/process.go | 15 ++- .../Microsoft/hcsshim/resizeconsole.go | 22 ---- .../hcsshim/shutdownterminatecomputesystem.go | 43 ------- .../Microsoft/hcsshim/startcomputesystem.go | 21 ---- .../Microsoft/hcsshim/terminateprocess.go | 20 ---- .../src/github.com/Microsoft/hcsshim/utils.go | 28 +++++ .../Microsoft/hcsshim/waithelper.go | 2 +- .../Microsoft/hcsshim/waitprocess.go | 20 ---- 19 files changed, 239 insertions(+), 371 deletions(-) create mode 100644 vendor/src/github.com/Microsoft/hcsshim/cgo.go delete mode 100644 vendor/src/github.com/Microsoft/hcsshim/createcomputesystem.go delete mode 100644 vendor/src/github.com/Microsoft/hcsshim/createprocess.go delete mode 100644 vendor/src/github.com/Microsoft/hcsshim/getcomputesystemproperties.go delete mode 100644 vendor/src/github.com/Microsoft/hcsshim/resizeconsole.go delete mode 100644 vendor/src/github.com/Microsoft/hcsshim/shutdownterminatecomputesystem.go delete mode 100644 vendor/src/github.com/Microsoft/hcsshim/startcomputesystem.go delete mode 100644 vendor/src/github.com/Microsoft/hcsshim/terminateprocess.go delete mode 100644 vendor/src/github.com/Microsoft/hcsshim/waitprocess.go diff --git a/hack/vendor.sh b/hack/vendor.sh index 33dcddf20e..4cd67a71bc 100755 --- a/hack/vendor.sh +++ b/hack/vendor.sh @@ -43,7 +43,7 @@ esac # the following lines are in sorted order, FYI clone git github.com/Azure/go-ansiterm 388960b655244e76e24c75f48631564eaefade62 -clone git github.com/Microsoft/hcsshim v0.3.6 +clone git github.com/Microsoft/hcsshim v0.4.1 clone git github.com/Microsoft/go-winio v0.3.4 clone git github.com/Sirupsen/logrus v0.10.0 # logrus is a common dependency among multiple deps clone git github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a diff --git a/libcontainerd/client_windows.go b/libcontainerd/client_windows.go index 39ca2e4cb6..95e508478b 100644 --- a/libcontainerd/client_windows.go +++ b/libcontainerd/client_windows.go @@ -294,13 +294,13 @@ func (clnt *client) Signal(containerID string, sig int) error { if syscall.Signal(sig) == syscall.SIGKILL { // Terminate the compute system if err := cont.hcsContainer.Terminate(); err != nil { - if err != hcsshim.ErrVmcomputeOperationPending { + if !hcsshim.IsPending(err) { logrus.Errorf("libcontainerd: failed to terminate %s - %q", containerID, err) } } } else { // Terminate Process - if err := cont.hcsProcess.Kill(); err != nil { + if err := cont.hcsProcess.Kill(); err != nil && !hcsshim.IsAlreadyStopped(err) { // ignore errors logrus.Warnf("libcontainerd: failed to terminate pid %d in %s: %q", cont.systemPid, containerID, err) } diff --git a/libcontainerd/container_windows.go b/libcontainerd/container_windows.go index cc47934b3d..452d4a0549 100644 --- a/libcontainerd/container_windows.go +++ b/libcontainerd/container_windows.go @@ -281,10 +281,10 @@ func (ctr *container) waitExit(process *process, isFirstProcessToStart bool) err func (ctr *container) shutdown() error { const shutdownTimeout = time.Minute * 5 err := ctr.hcsContainer.Shutdown() - if err == hcsshim.ErrVmcomputeOperationPending { + if hcsshim.IsPending(err) { // Explicit timeout to avoid a (remote) possibility that shutdown hangs indefinitely. err = ctr.hcsContainer.WaitTimeout(shutdownTimeout) - } else if err == hcsshim.ErrVmcomputeAlreadyStopped { + } else if hcsshim.IsAlreadyStopped(err) { err = nil } @@ -303,9 +303,9 @@ func (ctr *container) terminate() error { const terminateTimeout = time.Minute * 5 err := ctr.hcsContainer.Terminate() - if err == hcsshim.ErrVmcomputeOperationPending { + if hcsshim.IsPending(err) { err = ctr.hcsContainer.WaitTimeout(terminateTimeout) - } else if err == hcsshim.ErrVmcomputeAlreadyStopped { + } else if hcsshim.IsAlreadyStopped(err) { err = nil } diff --git a/vendor/src/github.com/Microsoft/hcsshim/baselayer.go b/vendor/src/github.com/Microsoft/hcsshim/baselayer.go index 63bde87a4b..4b04a681e9 100644 --- a/vendor/src/github.com/Microsoft/hcsshim/baselayer.go +++ b/vendor/src/github.com/Microsoft/hcsshim/baselayer.go @@ -15,6 +15,12 @@ type baseLayerWriter struct { bw *winio.BackupFileWriter err error hasUtilityVM bool + dirInfo []dirInfo +} + +type dirInfo struct { + path string + fileInfo winio.FileBasicInfo } func (w *baseLayerWriter) closeCurrentFile() error { @@ -69,17 +75,20 @@ func (w *baseLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) (err e return err } createmode = syscall.OPEN_EXISTING + if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 { + w.dirInfo = append(w.dirInfo, dirInfo{path, *fileInfo}) + } } mode := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | winio.WRITE_DAC | winio.WRITE_OWNER | winio.ACCESS_SYSTEM_SECURITY) f, err = winio.OpenForBackup(path, mode, syscall.FILE_SHARE_READ, createmode) if err != nil { - return err + return makeError(err, "Failed to OpenForBackup", path) } err = winio.SetFileBasicInfo(f, fileInfo) if err != nil { - return err + return makeError(err, "Failed to SetFileBasicInfo", path) } w.f = f @@ -131,6 +140,22 @@ func (w *baseLayerWriter) Close() error { return err } if w.err == nil { + // Restore the file times of all the directories, since they may have + // been modified by creating child directories. + for i := range w.dirInfo { + di := &w.dirInfo[len(w.dirInfo)-i-1] + f, err := winio.OpenForBackup(di.path, uint32(syscall.GENERIC_READ|syscall.GENERIC_WRITE), syscall.FILE_SHARE_READ, syscall.OPEN_EXISTING) + if err != nil { + return makeError(err, "Failed to OpenForBackup", di.path) + } + + err = winio.SetFileBasicInfo(f, &di.fileInfo) + f.Close() + if err != nil { + return makeError(err, "Failed to SetFileBasicInfo", di.path) + } + } + err = ProcessBaseLayer(w.root) if err != nil { return err diff --git a/vendor/src/github.com/Microsoft/hcsshim/cgo.go b/vendor/src/github.com/Microsoft/hcsshim/cgo.go new file mode 100644 index 0000000000..2003332330 --- /dev/null +++ b/vendor/src/github.com/Microsoft/hcsshim/cgo.go @@ -0,0 +1,7 @@ +package hcsshim + +import "C" + +// This import is needed to make the library compile as CGO because HCSSHIM +// only works with CGO due to callbacks from HCS comming back from a C thread +// which is not supported without CGO. See https://github.com/golang/go/issues/10973 diff --git a/vendor/src/github.com/Microsoft/hcsshim/container.go b/vendor/src/github.com/Microsoft/hcsshim/container.go index a705c56f3f..9e1ab0ffa2 100644 --- a/vendor/src/github.com/Microsoft/hcsshim/container.go +++ b/vendor/src/github.com/Microsoft/hcsshim/container.go @@ -13,7 +13,10 @@ var ( defaultTimeout = time.Minute * 4 ) -const pendingUpdatesQuery = `{ "PropertyTypes" : ["PendingUpdates"]}` +const ( + pendingUpdatesQuery = `{ "PropertyTypes" : ["PendingUpdates"]}` + statisticsQuery = `{ "PropertyTypes" : ["Statistics"]}` +) type container struct { handle hcsSystem @@ -26,12 +29,59 @@ type containerProperties struct { Name string SystemType string Owner string - SiloGUID string `json:"SiloGuid,omitempty"` - IsDummy bool `json:",omitempty"` - RuntimeID string `json:"RuntimeId,omitempty"` - Stopped bool `json:",omitempty"` - ExitType string `json:",omitempty"` - AreUpdatesPending bool `json:",omitempty"` + SiloGUID string `json:"SiloGuid,omitempty"` + IsDummy bool `json:",omitempty"` + RuntimeID string `json:"RuntimeId,omitempty"` + Stopped bool `json:",omitempty"` + ExitType string `json:",omitempty"` + AreUpdatesPending bool `json:",omitempty"` + ObRoot string `json:",omitempty"` + Statistics Statistics `json:",omitempty"` +} + +// MemoryStats holds the memory statistics for a container +type MemoryStats struct { + UsageCommitBytes uint64 `json:"MemoryUsageCommitBytes,omitempty"` + UsageCommitPeakBytes uint64 `json:"MemoryUsageCommitPeakBytes,omitempty"` + UsagePrivateWorkingSetBytes uint64 `json:"MemoryUsagePrivateWorkingSetBytes,omitempty"` +} + +// ProcessorStats holds the processor statistics for a container +type ProcessorStats struct { + TotalRuntime100ns uint64 `json:",omitempty"` + RuntimeUser100ns uint64 `json:",omitempty"` + RuntimeKernel100ns uint64 `json:",omitempty"` +} + +// StorageStats holds the storage statistics for a container +type StorageStats struct { + ReadCountNormalized uint64 `json:",omitempty"` + ReadSizeBytes uint64 `json:",omitempty"` + WriteCountNormalized uint64 `json:",omitempty"` + WriteSizeBytes uint64 `json:",omitempty"` +} + +// NetworkStats holds the network statistics for a container +type NetworkStats struct { + BytesReceived uint64 `json:",omitempty"` + BytesSent uint64 `json:",omitempty"` + PacketsReceived uint64 `json:",omitempty"` + PacketsSent uint64 `json:",omitempty"` + DroppedPacketsIncoming uint64 `json:",omitempty"` + DroppedPacketsOutgoing uint64 `json:",omitempty"` + EndpointId string `json:",omitempty"` + InstanceId string `json:",omitempty"` +} + +// Statistics is the structure returned by a statistics call on a container +type Statistics struct { + Timestamp time.Time `json:",omitempty"` + ContainerStartTime time.Time `json:",omitempty"` + Uptime100ns uint64 `json:",omitempty"` + Memory MemoryStats `json:",omitempty"` + Processor ProcessorStats `json:",omitempty"` + Storage StorageStats `json:",omitempty"` + Network []NetworkStats `json:",omitempty"` } // CreateContainer creates a new container with the given configuration but does not start it. @@ -59,7 +109,7 @@ func CreateContainer(id string, c *ContainerConfig) (Container, error) { var identity syscall.Handle createError = hcsCreateComputeSystem(id, configuration, identity, &container.handle, &resultp) - if createError == nil || createError == ErrVmcomputeOperationPending { + if createError == nil || IsPending(createError) { if err := container.registerCallback(); err != nil { return nil, makeContainerError(container, operation, "", err) } @@ -122,8 +172,8 @@ func (container *container) Start() error { return nil } -// Shutdown requests a container shutdown, but it may not actually be shut down until Wait() succeeds. -// It returns ErrVmcomputeOperationPending if the shutdown is in progress, nil if the shutdown is complete. +// Shutdown requests a container shutdown, if IsPending() on the error returned is true, +// it may not actually be shut down until Wait() succeeds. func (container *container) Shutdown() error { operation := "Shutdown" title := "HCSShim::Container::" + operation @@ -133,9 +183,6 @@ func (container *container) Shutdown() error { err := hcsShutdownComputeSystemTP5(container.handle, nil, &resultp) err = processHcsResult(err, resultp) if err != nil { - if err == ErrVmcomputeOperationPending { - return ErrVmcomputeOperationPending - } return makeContainerError(container, operation, "", err) } @@ -143,8 +190,8 @@ func (container *container) Shutdown() error { return nil } -// Terminate requests a container terminate, but it may not actually be terminated until Wait() succeeds. -// It returns ErrVmcomputeOperationPending if the shutdown is in progress, nil if the shutdown is complete. +// Terminate requests a container terminate, if IsPending() on the error returned is true, +// it may not actually be shut down until Wait() succeeds. func (container *container) Terminate() error { operation := "Terminate" title := "HCSShim::Container::" + operation @@ -154,9 +201,6 @@ func (container *container) Terminate() error { err := hcsTerminateComputeSystemTP5(container.handle, nil, &resultp) err = processHcsResult(err, resultp) if err != nil { - if err == ErrVmcomputeOperationPending { - return ErrVmcomputeOperationPending - } return makeContainerError(container, operation, "", err) } @@ -190,8 +234,8 @@ func (container *container) waitTimeoutInternal(timeout uint32) (bool, error) { return waitTimeoutInternalHelper(container, timeout) } -// WaitTimeout synchronously waits for the container to terminate or the duration to elapse. It returns -// ErrTimeout if the timeout duration expires before the container is shut down. +// WaitTimeout synchronously waits for the container to terminate or the duration to elapse. +// If the timeout expires, IsTimeout(err) == true func (container *container) WaitTimeout(timeout time.Duration) error { operation := "WaitTimeout" title := "HCSShim::Container::" + operation @@ -205,8 +249,9 @@ func (container *container) WaitTimeout(timeout time.Duration) error { } else { finished, err := waitTimeoutHelper(container, timeout) if !finished { - return ErrTimeout - } else if err != nil { + err = ErrTimeout + } + if err != nil { return makeContainerError(container, operation, "", err) } } @@ -246,12 +291,10 @@ func (container *container) properties(query string) (*containerProperties, erro return nil, ErrUnexpectedValue } propertiesRaw := convertAndFreeCoTaskMemBytes(propertiesp) - properties := &containerProperties{} if err := json.Unmarshal(propertiesRaw, properties); err != nil { return nil, err } - return properties, nil } @@ -269,6 +312,20 @@ func (container *container) HasPendingUpdates() (bool, error) { return properties.AreUpdatesPending, nil } +// Statistics returns statistics for the container +func (container *container) Statistics() (Statistics, error) { + operation := "Statistics" + title := "HCSShim::Container::" + operation + logrus.Debugf(title+" id=%s", container.id) + properties, err := container.properties(statisticsQuery) + if err != nil { + return Statistics{}, makeContainerError(container, operation, "", err) + } + + logrus.Debugf(title+" succeeded id=%s", container.id) + return properties.Statistics, nil +} + // Pause pauses the execution of the container. This feature is not enabled in TP5. func (container *container) Pause() error { operation := "Pause" @@ -323,7 +380,7 @@ func (container *container) CreateProcess(c *ProcessConfig) (Process, error) { configurationb, err := json.Marshal(c) if err != nil { - return nil, err + return nil, makeContainerError(container, operation, "", err) } configuration := string(configurationb) diff --git a/vendor/src/github.com/Microsoft/hcsshim/createcomputesystem.go b/vendor/src/github.com/Microsoft/hcsshim/createcomputesystem.go deleted file mode 100644 index 3cc12a38ef..0000000000 --- a/vendor/src/github.com/Microsoft/hcsshim/createcomputesystem.go +++ /dev/null @@ -1,22 +0,0 @@ -package hcsshim - -import "github.com/Sirupsen/logrus" - -// CreateComputeSystem creates a container, initializing its configuration in -// the Host Compute Service such that it can be started by a call to the -// StartComputeSystem method. -func CreateComputeSystem(id string, configuration string) error { - - title := "HCSShim::CreateComputeSystem" - logrus.Debugln(title+" id=%s, configuration=%s", id, configuration) - - err := createComputeSystem(id, configuration) - if err != nil { - err = makeErrorf(err, title, "id=%s configuration=%s", id, configuration) - logrus.Error(err) - return err - } - - logrus.Debugf(title+"- succeeded %s", id) - return nil -} diff --git a/vendor/src/github.com/Microsoft/hcsshim/createprocess.go b/vendor/src/github.com/Microsoft/hcsshim/createprocess.go deleted file mode 100644 index a2b6298546..0000000000 --- a/vendor/src/github.com/Microsoft/hcsshim/createprocess.go +++ /dev/null @@ -1,101 +0,0 @@ -package hcsshim - -import ( - "encoding/json" - "io" - "syscall" - - "github.com/Microsoft/go-winio" - "github.com/Sirupsen/logrus" -) - -// CreateProcessParams is used as both the input of CreateProcessInComputeSystem -// and to convert the parameters to JSON for passing onto the HCS -type CreateProcessParams struct { - ApplicationName string - CommandLine string - WorkingDirectory string - Environment map[string]string - EmulateConsole bool - ConsoleSize [2]int -} - -// makeOpenFiles calls winio.MakeOpenFile for each handle in a slice but closes all the handles -// if there is an error. -func makeOpenFiles(hs []syscall.Handle) (_ []io.ReadWriteCloser, err error) { - fs := make([]io.ReadWriteCloser, len(hs)) - for i, h := range hs { - if h != syscall.Handle(0) { - if err == nil { - fs[i], err = winio.MakeOpenFile(h) - } - if err != nil { - syscall.Close(h) - } - } - } - if err != nil { - for _, f := range fs { - if f != nil { - f.Close() - } - } - return nil, err - } - return fs, nil -} - -// CreateProcessInComputeSystem starts a process in a container. This is invoked, for example, -// as a result of docker run, docker exec, or RUN in Dockerfile. If successful, -// it returns the PID of the process. -func CreateProcessInComputeSystem(id string, useStdin bool, useStdout bool, useStderr bool, params CreateProcessParams) (_ uint32, _ io.WriteCloser, _ io.ReadCloser, _ io.ReadCloser, err error) { - title := "HCSShim::CreateProcessInComputeSystem" - logrus.Debugf(title+" id=%s", id) - - // If we are not emulating a console, ignore any console size passed to us - if !params.EmulateConsole { - params.ConsoleSize[0] = 0 - params.ConsoleSize[1] = 0 - } - - paramsJson, err := json.Marshal(params) - if err != nil { - return - } - - logrus.Debugf(title+" - Calling Win32 %s %s", id, paramsJson) - - var pid uint32 - - handles := make([]syscall.Handle, 3) - var stdinParam, stdoutParam, stderrParam *syscall.Handle - if useStdin { - stdinParam = &handles[0] - } - if useStdout { - stdoutParam = &handles[1] - } - if useStderr { - stderrParam = &handles[2] - } - - err = createProcessWithStdHandlesInComputeSystem(id, string(paramsJson), &pid, stdinParam, stdoutParam, stderrParam) - if err != nil { - herr := makeErrorf(err, title, "id=%s params=%v", id, params) - // Windows TP4: Hyper-V Containers may return this error with more than one - // concurrent exec. Do not log it as an error - if err != WSAEINVAL { - logrus.Error(herr) - } - err = herr - return - } - - pipes, err := makeOpenFiles(handles) - if err != nil { - return - } - - logrus.Debugf(title+" - succeeded id=%s params=%s pid=%d", id, paramsJson, pid) - return pid, pipes[0], pipes[1], pipes[2], nil -} diff --git a/vendor/src/github.com/Microsoft/hcsshim/errors.go b/vendor/src/github.com/Microsoft/hcsshim/errors.go index 02ee5ea163..0d4dad5950 100644 --- a/vendor/src/github.com/Microsoft/hcsshim/errors.go +++ b/vendor/src/github.com/Microsoft/hcsshim/errors.go @@ -4,12 +4,16 @@ import ( "errors" "fmt" "syscall" - - "github.com/Sirupsen/logrus" ) var ( - // ErrHandleClose is an error returned when the handle generating the notification being waited on has been closed + // ErrComputeSystemDoesNotExist is an error encountered when the container being operated on no longer exists + ErrComputeSystemDoesNotExist = syscall.Errno(0xc037010e) + + // ErrElementNotFound is an error encountered when the object being referenced does not exist + ErrElementNotFound = syscall.Errno(0x490) + + // ErrHandleClose is an error encountered when the handle generating the notification being waited on has been closed ErrHandleClose = errors.New("hcsshim: the handle generating this notification has been closed") // ErrInvalidNotificationType is an error encountered when an invalid notification type is used @@ -21,22 +25,28 @@ var ( // ErrTimeout is an error encountered when waiting on a notification times out ErrTimeout = errors.New("hcsshim: timeout waiting for notification") - // ErrUnexpectedContainerExit is the error returned when a container exits while waiting for + // ErrUnexpectedContainerExit is the error encountered when a container exits while waiting for // a different expected notification ErrUnexpectedContainerExit = errors.New("unexpected container exit") - // ErrUnexpectedProcessAbort is the error returned when communication with the compute service + // ErrUnexpectedProcessAbort is the error encountered when communication with the compute service // is lost while waiting for a notification ErrUnexpectedProcessAbort = errors.New("lost communication with compute service") - // ErrUnexpectedValue is an error returned when hcs returns an invalid value + // ErrUnexpectedValue is an error encountered when hcs returns an invalid value ErrUnexpectedValue = errors.New("unexpected value returned from hcs") - // ErrVmcomputeAlreadyStopped is an error returned when a shutdown or terminate request is made on a stopped container + // ErrVmcomputeAlreadyStopped is an error encountered when a shutdown or terminate request is made on a stopped container ErrVmcomputeAlreadyStopped = syscall.Errno(0xc0370110) - // ErrVmcomputeOperationPending is an error returned when the operation is being completed asynchronously + // ErrVmcomputeOperationPending is an error encountered when the operation is being completed asynchronously ErrVmcomputeOperationPending = syscall.Errno(0xC0370103) + + // ErrVmcomputeOperationInvalidState is an error encountered when the compute system is not in a valid state for the requested operation + ErrVmcomputeOperationInvalidState = syscall.Errno(0xc0370105) + + // ErrProcNotFound is an error encountered when the the process cannot be found + ErrProcNotFound = syscall.Errno(0x7f) ) // ProcessError is an error encountered in HCS during an operation on a Process object @@ -55,23 +65,6 @@ type ContainerError struct { Err error } -func isKnownError(err error) bool { - // Don't wrap errors created in hcsshim - if err == ErrHandleClose || - err == ErrInvalidNotificationType || - err == ErrInvalidProcessState || - err == ErrTimeout || - err == ErrUnexpectedContainerExit || - err == ErrUnexpectedProcessAbort || - err == ErrUnexpectedValue || - err == ErrVmcomputeAlreadyStopped || - err == ErrVmcomputeOperationPending { - return true - } - - return false -} - func (e *ContainerError) Error() string { if e == nil { return "" @@ -99,14 +92,11 @@ func (e *ContainerError) Error() string { } func makeContainerError(container *container, operation string, extraInfo string, err error) error { - // Return known errors to the client - if isKnownError(err) { + // Don't double wrap errors + if _, ok := err.(*ContainerError); ok { return err } - - // Log any unexpected errors containerError := &ContainerError{Container: container, Operation: operation, ExtraInfo: extraInfo, Err: err} - logrus.Error(containerError) return containerError } @@ -129,21 +119,72 @@ func (e *ProcessError) Error() string { s += " " + e.Operation } - if e.Err != nil { + switch e.Err.(type) { + case nil: + break + case syscall.Errno: s += fmt.Sprintf(" failed in Win32: %s (0x%x)", e.Err, win32FromError(e.Err)) + default: + s += fmt.Sprintf(" failed: %s", e.Error()) } return s } func makeProcessError(process *process, operation string, extraInfo string, err error) error { - // Return known errors to the client - if isKnownError(err) { + // Don't double wrap errors + if _, ok := err.(*ProcessError); ok { return err } - - // Log any unexpected errors processError := &ProcessError{Process: process, Operation: operation, ExtraInfo: extraInfo, Err: err} - logrus.Error(processError) return processError } + +// IsNotExist checks if an error is caused by the Container or Process not existing. +// Note: Currently, ErrElementNotFound can mean that a Process has either +// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist +// will currently return true when the error is ErrElementNotFound or ErrProcNotFound. +func IsNotExist(err error) bool { + err = getInnerError(err) + return err == ErrComputeSystemDoesNotExist || + err == ErrElementNotFound || + err == ErrProcNotFound +} + +// IsPending returns a boolean indicating whether the error is that +// the requested operation is being completed in the background. +func IsPending(err error) bool { + err = getInnerError(err) + return err == ErrVmcomputeOperationPending +} + +// IsTimeout returns a boolean indicating whether the error is caused by +// a timeout waiting for the operation to complete. +func IsTimeout(err error) bool { + err = getInnerError(err) + return err == ErrTimeout +} + +// IsAlreadyStopped returns a boolean indicating whether the error is caused by +// a Container or Process being already stopped. +// Note: Currently, ErrElementNotFound can mean that a Process has either +// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist +// will currently return true when the error is ErrElementNotFound or ErrProcNotFound. +func IsAlreadyStopped(err error) bool { + err = getInnerError(err) + return err == ErrVmcomputeAlreadyStopped || + err == ErrElementNotFound || + err == ErrProcNotFound +} + +func getInnerError(err error) error { + switch pe := err.(type) { + case nil: + return nil + case *ContainerError: + err = pe.Err + case *ProcessError: + err = pe.Err + } + return err +} diff --git a/vendor/src/github.com/Microsoft/hcsshim/getcomputesystemproperties.go b/vendor/src/github.com/Microsoft/hcsshim/getcomputesystemproperties.go deleted file mode 100644 index 3c544492da..0000000000 --- a/vendor/src/github.com/Microsoft/hcsshim/getcomputesystemproperties.go +++ /dev/null @@ -1,43 +0,0 @@ -package hcsshim - -import ( - "encoding/json" - - "github.com/Sirupsen/logrus" -) - -// ComputeSystemProperties is a struct describing the returned properties. -type ComputeSystemProperties struct { - ID string - Name string - Stopped bool - AreUpdatesPending bool -} - -// GetComputeSystemProperties gets the properties for the compute system with the given ID. -func GetComputeSystemProperties(id string, flags uint32) (ComputeSystemProperties, error) { - title := "hcsshim::GetComputeSystemProperties " - - csProps := ComputeSystemProperties{ - Stopped: false, - AreUpdatesPending: false, - } - - logrus.Debugf("Calling proc") - var buffer *uint16 - err := getComputeSystemProperties(id, flags, &buffer) - if err != nil { - err = makeError(err, title, "") - logrus.Error(err) - return csProps, err - } - propData := convertAndFreeCoTaskMemString(buffer) - logrus.Debugf(title+" - succeeded output=%s", propData) - - if err = json.Unmarshal([]byte(propData), &csProps); err != nil { - logrus.Error(err) - return csProps, err - } - - return csProps, nil -} diff --git a/vendor/src/github.com/Microsoft/hcsshim/interface.go b/vendor/src/github.com/Microsoft/hcsshim/interface.go index e35e477f01..0eacca38ce 100644 --- a/vendor/src/github.com/Microsoft/hcsshim/interface.go +++ b/vendor/src/github.com/Microsoft/hcsshim/interface.go @@ -90,6 +90,9 @@ type Container interface { // HasPendingUpdates returns true if the container has updates pending to install. HasPendingUpdates() (bool, error) + // Statistics returns statistics for a container. + Statistics() (Statistics, error) + // CreateProcess launches a new process within the container. CreateProcess(c *ProcessConfig) (Process, error) diff --git a/vendor/src/github.com/Microsoft/hcsshim/process.go b/vendor/src/github.com/Microsoft/hcsshim/process.go index 2946f03fc0..d6e63c9223 100644 --- a/vendor/src/github.com/Microsoft/hcsshim/process.go +++ b/vendor/src/github.com/Microsoft/hcsshim/process.go @@ -71,9 +71,7 @@ func (process *process) Kill() error { var resultp *uint16 err := hcsTerminateProcess(process.handle, &resultp) err = processHcsResult(err, resultp) - if err == ErrVmcomputeOperationPending { - return ErrVmcomputeOperationPending - } else if err != nil { + if err != nil { return makeProcessError(process, operation, "", err) } @@ -118,8 +116,9 @@ func (process *process) WaitTimeout(timeout time.Duration) error { } else { finished, err := waitTimeoutHelper(process, timeout) if !finished { - return ErrTimeout - } else if err != nil { + err = ErrTimeout + } + if err != nil { return makeProcessError(process, operation, "", err) } } @@ -160,7 +159,7 @@ func (process *process) ExitCode() (int, error) { } if properties.Exited == false { - return 0, ErrInvalidProcessState + return 0, makeProcessError(process, operation, "", ErrInvalidProcessState) } logrus.Debugf(title+" succeeded processid=%d exitCode=%d", process.processID, properties.ExitCode) @@ -211,7 +210,7 @@ func (process *process) properties() (*processStatus, error) { err := hcsGetProcessProperties(process.handle, &propertiesp, &resultp) err = processHcsResult(err, resultp) if err != nil { - return nil, makeProcessError(process, operation, "", err) + return nil, err } if propertiesp == nil { @@ -260,7 +259,7 @@ func (process *process) Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, e pipes, err := makeOpenFiles([]syscall.Handle{stdIn, stdOut, stdErr}) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, makeProcessError(process, operation, "", err) } logrus.Debugf(title+" succeeded processid=%d", process.processID) diff --git a/vendor/src/github.com/Microsoft/hcsshim/resizeconsole.go b/vendor/src/github.com/Microsoft/hcsshim/resizeconsole.go deleted file mode 100644 index d04ce70d85..0000000000 --- a/vendor/src/github.com/Microsoft/hcsshim/resizeconsole.go +++ /dev/null @@ -1,22 +0,0 @@ -package hcsshim - -import "github.com/Sirupsen/logrus" - -// ResizeConsoleInComputeSystem updates the height and width of the console -// session for the process with the given id in the container with the given id. -func ResizeConsoleInComputeSystem(id string, processid uint32, h, w int) error { - - title := "HCSShim::ResizeConsoleInComputeSystem" - logrus.Debugf(title+" id=%s processid=%d (%d,%d)", id, processid, h, w) - - err := resizeConsoleInComputeSystem(id, processid, uint16(h), uint16(w), 0) - if err != nil { - err = makeErrorf(err, title, "id=%s pid=%d", id, processid) - logrus.Error(err) - return err - } - - logrus.Debugf(title+" succeeded id=%s processid=%d (%d,%d)", id, processid, h, w) - return nil - -} diff --git a/vendor/src/github.com/Microsoft/hcsshim/shutdownterminatecomputesystem.go b/vendor/src/github.com/Microsoft/hcsshim/shutdownterminatecomputesystem.go deleted file mode 100644 index 27ac734bd8..0000000000 --- a/vendor/src/github.com/Microsoft/hcsshim/shutdownterminatecomputesystem.go +++ /dev/null @@ -1,43 +0,0 @@ -package hcsshim - -import "github.com/Sirupsen/logrus" - -// TerminateComputeSystem force terminates a container. -func TerminateComputeSystem(id string, timeout uint32, context string) error { - return shutdownTerminate(false, id, timeout, context) -} - -// ShutdownComputeSystem shuts down a container by requesting a shutdown within -// the container operating system. -func ShutdownComputeSystem(id string, timeout uint32, context string) error { - return shutdownTerminate(true, id, timeout, context) -} - -// shutdownTerminate is a wrapper for ShutdownComputeSystem and TerminateComputeSystem -// which have very similar calling semantics -func shutdownTerminate(shutdown bool, id string, timeout uint32, context string) error { - - var ( - title = "HCSShim::" - ) - if shutdown { - title = title + "ShutdownComputeSystem" - } else { - title = title + "TerminateComputeSystem" - } - logrus.Debugf(title+" id=%s context=%s", id, context) - - var err error - if shutdown { - err = shutdownComputeSystem(id, timeout) - } else { - err = terminateComputeSystem(id) - } - - if err != nil { - return makeErrorf(err, title, "id=%s context=%s", id, context) - } - - logrus.Debugf(title+" succeeded id=%s context=%s", id, context) - return nil -} diff --git a/vendor/src/github.com/Microsoft/hcsshim/startcomputesystem.go b/vendor/src/github.com/Microsoft/hcsshim/startcomputesystem.go deleted file mode 100644 index 41a7e676f7..0000000000 --- a/vendor/src/github.com/Microsoft/hcsshim/startcomputesystem.go +++ /dev/null @@ -1,21 +0,0 @@ -package hcsshim - -import "github.com/Sirupsen/logrus" - -// StartComputeSystem starts a container that has previously been created via -// CreateComputeSystem. -func StartComputeSystem(id string) error { - - title := "HCSShim::StartComputeSystem" - logrus.Debugf(title+" id=%s", id) - - err := startComputeSystem(id) - if err != nil { - err = makeErrorf(err, title, "id=%s", id) - logrus.Error(err) - return err - } - - logrus.Debugf(title+" succeeded id=%s", id) - return nil -} diff --git a/vendor/src/github.com/Microsoft/hcsshim/terminateprocess.go b/vendor/src/github.com/Microsoft/hcsshim/terminateprocess.go deleted file mode 100644 index 47880afce1..0000000000 --- a/vendor/src/github.com/Microsoft/hcsshim/terminateprocess.go +++ /dev/null @@ -1,20 +0,0 @@ -package hcsshim - -import "github.com/Sirupsen/logrus" - -// TerminateProcessInComputeSystem kills a process in a running container. -func TerminateProcessInComputeSystem(id string, processid uint32) (err error) { - - title := "HCSShim::TerminateProcessInComputeSystem" - logrus.Debugf(title+" id=%s processid=%d", id, processid) - - err = terminateProcessInComputeSystem(id, processid) - if err != nil { - err = makeErrorf(err, title, "err=%s id=%s", id) - logrus.Error(err) - return err - } - - logrus.Debugf(title+" succeeded id=%s", id) - return nil -} diff --git a/vendor/src/github.com/Microsoft/hcsshim/utils.go b/vendor/src/github.com/Microsoft/hcsshim/utils.go index 694b001977..c219e2c07d 100644 --- a/vendor/src/github.com/Microsoft/hcsshim/utils.go +++ b/vendor/src/github.com/Microsoft/hcsshim/utils.go @@ -1,7 +1,10 @@ package hcsshim import ( + "io" "syscall" + + "github.com/Microsoft/go-winio" ) var ( @@ -9,3 +12,28 @@ var ( hcsCallbackAPI = vmcomputedll.NewProc("HcsRegisterComputeSystemCallback") hcsCallbacksSupported = hcsCallbackAPI.Find() == nil ) + +// makeOpenFiles calls winio.MakeOpenFile for each handle in a slice but closes all the handles +// if there is an error. +func makeOpenFiles(hs []syscall.Handle) (_ []io.ReadWriteCloser, err error) { + fs := make([]io.ReadWriteCloser, len(hs)) + for i, h := range hs { + if h != syscall.Handle(0) { + if err == nil { + fs[i], err = winio.MakeOpenFile(h) + } + if err != nil { + syscall.Close(h) + } + } + } + if err != nil { + for _, f := range fs { + if f != nil { + f.Close() + } + } + return nil, err + } + return fs, nil +} diff --git a/vendor/src/github.com/Microsoft/hcsshim/waithelper.go b/vendor/src/github.com/Microsoft/hcsshim/waithelper.go index 3c3599a372..8ce65ae110 100644 --- a/vendor/src/github.com/Microsoft/hcsshim/waithelper.go +++ b/vendor/src/github.com/Microsoft/hcsshim/waithelper.go @@ -50,7 +50,7 @@ func waitForSingleObject(handle syscall.Handle, timeout uint32) (bool, error) { func processAsyncHcsResult(err error, resultp *uint16, callbackNumber uintptr, expectedNotification hcsNotification, timeout *time.Duration) error { err = processHcsResult(err, resultp) - if err == ErrVmcomputeOperationPending { + if IsPending(err) { return waitForNotification(callbackNumber, expectedNotification, timeout) } diff --git a/vendor/src/github.com/Microsoft/hcsshim/waitprocess.go b/vendor/src/github.com/Microsoft/hcsshim/waitprocess.go deleted file mode 100644 index e916140399..0000000000 --- a/vendor/src/github.com/Microsoft/hcsshim/waitprocess.go +++ /dev/null @@ -1,20 +0,0 @@ -package hcsshim - -import "github.com/Sirupsen/logrus" - -// WaitForProcessInComputeSystem waits for a process ID to terminate and returns -// the exit code. Returns exitcode, error -func WaitForProcessInComputeSystem(id string, processid uint32, timeout uint32) (int32, error) { - - title := "HCSShim::WaitForProcessInComputeSystem" - logrus.Debugf(title+" id=%s processid=%d", id, processid) - - var exitCode uint32 - err := waitForProcessInComputeSystem(id, processid, timeout, &exitCode) - if err != nil { - return 0, makeErrorf(err, title, "id=%s", id) - } - - logrus.Debugf(title+" succeeded id=%s processid=%d exitcode=%d", id, processid, exitCode) - return int32(exitCode), nil -}