Преглед изворни кода

Revendor hcsshim

Signed-off-by: Darren Stahl <darst@microsoft.com>
Darren Stahl пре 9 година
родитељ
комит
c58d0358c3

+ 1 - 1
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

+ 2 - 2
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)
 		}

+ 4 - 4
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
 	}
 

+ 27 - 2
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

+ 7 - 0
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

+ 82 - 25
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)

+ 0 - 22
vendor/src/github.com/Microsoft/hcsshim/createcomputesystem.go

@@ -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
-}

+ 0 - 101
vendor/src/github.com/Microsoft/hcsshim/createprocess.go

@@ -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
-}

+ 77 - 36
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 "<nil>"
@@ -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
+}

+ 0 - 43
vendor/src/github.com/Microsoft/hcsshim/getcomputesystemproperties.go

@@ -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
-}

+ 3 - 0
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)
 

+ 7 - 8
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)

+ 0 - 22
vendor/src/github.com/Microsoft/hcsshim/resizeconsole.go

@@ -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
-
-}

+ 0 - 43
vendor/src/github.com/Microsoft/hcsshim/shutdownterminatecomputesystem.go

@@ -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
-}

+ 0 - 21
vendor/src/github.com/Microsoft/hcsshim/startcomputesystem.go

@@ -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
-}

+ 0 - 20
vendor/src/github.com/Microsoft/hcsshim/terminateprocess.go

@@ -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
-}

+ 28 - 0
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
+}

+ 1 - 1
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)
 	}
 

+ 0 - 20
vendor/src/github.com/Microsoft/hcsshim/waitprocess.go

@@ -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
-}