1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #25634 from darrenstahlmsft/RevendorHcsshim

Revendor hcsshim to v0.4.1
This commit is contained in:
Sebastiaan van Stijn 2016-08-17 10:50:00 +02:00 committed by GitHub
commit 5e2a519957
19 changed files with 239 additions and 371 deletions

View file

@ -43,7 +43,7 @@ esac
# the following lines are in sorted order, FYI # the following lines are in sorted order, FYI
clone git github.com/Azure/go-ansiterm 388960b655244e76e24c75f48631564eaefade62 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/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/Sirupsen/logrus v0.10.0 # logrus is a common dependency among multiple deps
clone git github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a clone git github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a

View file

@ -294,13 +294,13 @@ func (clnt *client) Signal(containerID string, sig int) error {
if syscall.Signal(sig) == syscall.SIGKILL { if syscall.Signal(sig) == syscall.SIGKILL {
// Terminate the compute system // Terminate the compute system
if err := cont.hcsContainer.Terminate(); err != nil { 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) logrus.Errorf("libcontainerd: failed to terminate %s - %q", containerID, err)
} }
} }
} else { } else {
// Terminate Process // Terminate Process
if err := cont.hcsProcess.Kill(); err != nil { if err := cont.hcsProcess.Kill(); err != nil && !hcsshim.IsAlreadyStopped(err) {
// ignore errors // ignore errors
logrus.Warnf("libcontainerd: failed to terminate pid %d in %s: %q", cont.systemPid, containerID, err) logrus.Warnf("libcontainerd: failed to terminate pid %d in %s: %q", cont.systemPid, containerID, err)
} }

View file

@ -281,10 +281,10 @@ func (ctr *container) waitExit(process *process, isFirstProcessToStart bool) err
func (ctr *container) shutdown() error { func (ctr *container) shutdown() error {
const shutdownTimeout = time.Minute * 5 const shutdownTimeout = time.Minute * 5
err := ctr.hcsContainer.Shutdown() err := ctr.hcsContainer.Shutdown()
if err == hcsshim.ErrVmcomputeOperationPending { if hcsshim.IsPending(err) {
// Explicit timeout to avoid a (remote) possibility that shutdown hangs indefinitely. // Explicit timeout to avoid a (remote) possibility that shutdown hangs indefinitely.
err = ctr.hcsContainer.WaitTimeout(shutdownTimeout) err = ctr.hcsContainer.WaitTimeout(shutdownTimeout)
} else if err == hcsshim.ErrVmcomputeAlreadyStopped { } else if hcsshim.IsAlreadyStopped(err) {
err = nil err = nil
} }
@ -303,9 +303,9 @@ func (ctr *container) terminate() error {
const terminateTimeout = time.Minute * 5 const terminateTimeout = time.Minute * 5
err := ctr.hcsContainer.Terminate() err := ctr.hcsContainer.Terminate()
if err == hcsshim.ErrVmcomputeOperationPending { if hcsshim.IsPending(err) {
err = ctr.hcsContainer.WaitTimeout(terminateTimeout) err = ctr.hcsContainer.WaitTimeout(terminateTimeout)
} else if err == hcsshim.ErrVmcomputeAlreadyStopped { } else if hcsshim.IsAlreadyStopped(err) {
err = nil err = nil
} }

View file

@ -15,6 +15,12 @@ type baseLayerWriter struct {
bw *winio.BackupFileWriter bw *winio.BackupFileWriter
err error err error
hasUtilityVM bool hasUtilityVM bool
dirInfo []dirInfo
}
type dirInfo struct {
path string
fileInfo winio.FileBasicInfo
} }
func (w *baseLayerWriter) closeCurrentFile() error { func (w *baseLayerWriter) closeCurrentFile() error {
@ -69,17 +75,20 @@ func (w *baseLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) (err e
return err return err
} }
createmode = syscall.OPEN_EXISTING 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) 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) f, err = winio.OpenForBackup(path, mode, syscall.FILE_SHARE_READ, createmode)
if err != nil { if err != nil {
return err return makeError(err, "Failed to OpenForBackup", path)
} }
err = winio.SetFileBasicInfo(f, fileInfo) err = winio.SetFileBasicInfo(f, fileInfo)
if err != nil { if err != nil {
return err return makeError(err, "Failed to SetFileBasicInfo", path)
} }
w.f = f w.f = f
@ -131,6 +140,22 @@ func (w *baseLayerWriter) Close() error {
return err return err
} }
if w.err == nil { 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) err = ProcessBaseLayer(w.root)
if err != nil { if err != nil {
return err return err

View file

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

View file

@ -13,7 +13,10 @@ var (
defaultTimeout = time.Minute * 4 defaultTimeout = time.Minute * 4
) )
const pendingUpdatesQuery = `{ "PropertyTypes" : ["PendingUpdates"]}` const (
pendingUpdatesQuery = `{ "PropertyTypes" : ["PendingUpdates"]}`
statisticsQuery = `{ "PropertyTypes" : ["Statistics"]}`
)
type container struct { type container struct {
handle hcsSystem handle hcsSystem
@ -26,12 +29,59 @@ type containerProperties struct {
Name string Name string
SystemType string SystemType string
Owner string Owner string
SiloGUID string `json:"SiloGuid,omitempty"` SiloGUID string `json:"SiloGuid,omitempty"`
IsDummy bool `json:",omitempty"` IsDummy bool `json:",omitempty"`
RuntimeID string `json:"RuntimeId,omitempty"` RuntimeID string `json:"RuntimeId,omitempty"`
Stopped bool `json:",omitempty"` Stopped bool `json:",omitempty"`
ExitType string `json:",omitempty"` ExitType string `json:",omitempty"`
AreUpdatesPending bool `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. // 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 var identity syscall.Handle
createError = hcsCreateComputeSystem(id, configuration, identity, &container.handle, &resultp) createError = hcsCreateComputeSystem(id, configuration, identity, &container.handle, &resultp)
if createError == nil || createError == ErrVmcomputeOperationPending { if createError == nil || IsPending(createError) {
if err := container.registerCallback(); err != nil { if err := container.registerCallback(); err != nil {
return nil, makeContainerError(container, operation, "", err) return nil, makeContainerError(container, operation, "", err)
} }
@ -122,8 +172,8 @@ func (container *container) Start() error {
return nil return nil
} }
// Shutdown requests a container shutdown, but it may not actually be shut down until Wait() succeeds. // Shutdown requests a container shutdown, if IsPending() on the error returned is true,
// It returns ErrVmcomputeOperationPending if the shutdown is in progress, nil if the shutdown is complete. // it may not actually be shut down until Wait() succeeds.
func (container *container) Shutdown() error { func (container *container) Shutdown() error {
operation := "Shutdown" operation := "Shutdown"
title := "HCSShim::Container::" + operation title := "HCSShim::Container::" + operation
@ -133,9 +183,6 @@ func (container *container) Shutdown() error {
err := hcsShutdownComputeSystemTP5(container.handle, nil, &resultp) err := hcsShutdownComputeSystemTP5(container.handle, nil, &resultp)
err = processHcsResult(err, resultp) err = processHcsResult(err, resultp)
if err != nil { if err != nil {
if err == ErrVmcomputeOperationPending {
return ErrVmcomputeOperationPending
}
return makeContainerError(container, operation, "", err) return makeContainerError(container, operation, "", err)
} }
@ -143,8 +190,8 @@ func (container *container) Shutdown() error {
return nil return nil
} }
// Terminate requests a container terminate, but it may not actually be terminated until Wait() succeeds. // Terminate requests a container terminate, if IsPending() on the error returned is true,
// It returns ErrVmcomputeOperationPending if the shutdown is in progress, nil if the shutdown is complete. // it may not actually be shut down until Wait() succeeds.
func (container *container) Terminate() error { func (container *container) Terminate() error {
operation := "Terminate" operation := "Terminate"
title := "HCSShim::Container::" + operation title := "HCSShim::Container::" + operation
@ -154,9 +201,6 @@ func (container *container) Terminate() error {
err := hcsTerminateComputeSystemTP5(container.handle, nil, &resultp) err := hcsTerminateComputeSystemTP5(container.handle, nil, &resultp)
err = processHcsResult(err, resultp) err = processHcsResult(err, resultp)
if err != nil { if err != nil {
if err == ErrVmcomputeOperationPending {
return ErrVmcomputeOperationPending
}
return makeContainerError(container, operation, "", err) return makeContainerError(container, operation, "", err)
} }
@ -190,8 +234,8 @@ func (container *container) waitTimeoutInternal(timeout uint32) (bool, error) {
return waitTimeoutInternalHelper(container, timeout) return waitTimeoutInternalHelper(container, timeout)
} }
// WaitTimeout synchronously waits for the container to terminate or the duration to elapse. It returns // WaitTimeout synchronously waits for the container to terminate or the duration to elapse.
// ErrTimeout if the timeout duration expires before the container is shut down. // If the timeout expires, IsTimeout(err) == true
func (container *container) WaitTimeout(timeout time.Duration) error { func (container *container) WaitTimeout(timeout time.Duration) error {
operation := "WaitTimeout" operation := "WaitTimeout"
title := "HCSShim::Container::" + operation title := "HCSShim::Container::" + operation
@ -205,8 +249,9 @@ func (container *container) WaitTimeout(timeout time.Duration) error {
} else { } else {
finished, err := waitTimeoutHelper(container, timeout) finished, err := waitTimeoutHelper(container, timeout)
if !finished { if !finished {
return ErrTimeout err = ErrTimeout
} else if err != nil { }
if err != nil {
return makeContainerError(container, operation, "", err) return makeContainerError(container, operation, "", err)
} }
} }
@ -246,12 +291,10 @@ func (container *container) properties(query string) (*containerProperties, erro
return nil, ErrUnexpectedValue return nil, ErrUnexpectedValue
} }
propertiesRaw := convertAndFreeCoTaskMemBytes(propertiesp) propertiesRaw := convertAndFreeCoTaskMemBytes(propertiesp)
properties := &containerProperties{} properties := &containerProperties{}
if err := json.Unmarshal(propertiesRaw, properties); err != nil { if err := json.Unmarshal(propertiesRaw, properties); err != nil {
return nil, err return nil, err
} }
return properties, nil return properties, nil
} }
@ -269,6 +312,20 @@ func (container *container) HasPendingUpdates() (bool, error) {
return properties.AreUpdatesPending, nil 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. // Pause pauses the execution of the container. This feature is not enabled in TP5.
func (container *container) Pause() error { func (container *container) Pause() error {
operation := "Pause" operation := "Pause"
@ -323,7 +380,7 @@ func (container *container) CreateProcess(c *ProcessConfig) (Process, error) {
configurationb, err := json.Marshal(c) configurationb, err := json.Marshal(c)
if err != nil { if err != nil {
return nil, err return nil, makeContainerError(container, operation, "", err)
} }
configuration := string(configurationb) configuration := string(configurationb)

View file

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

View file

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

View file

@ -4,12 +4,16 @@ import (
"errors" "errors"
"fmt" "fmt"
"syscall" "syscall"
"github.com/Sirupsen/logrus"
) )
var ( 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") ErrHandleClose = errors.New("hcsshim: the handle generating this notification has been closed")
// ErrInvalidNotificationType is an error encountered when an invalid notification type is used // 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 is an error encountered when waiting on a notification times out
ErrTimeout = errors.New("hcsshim: timeout waiting for notification") 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 // a different expected notification
ErrUnexpectedContainerExit = errors.New("unexpected container exit") 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 // is lost while waiting for a notification
ErrUnexpectedProcessAbort = errors.New("lost communication with compute service") 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") 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) 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) 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 // ProcessError is an error encountered in HCS during an operation on a Process object
@ -55,23 +65,6 @@ type ContainerError struct {
Err error 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 { func (e *ContainerError) Error() string {
if e == nil { if e == nil {
return "<nil>" return "<nil>"
@ -99,14 +92,11 @@ func (e *ContainerError) Error() string {
} }
func makeContainerError(container *container, operation string, extraInfo string, err error) error { func makeContainerError(container *container, operation string, extraInfo string, err error) error {
// Return known errors to the client // Don't double wrap errors
if isKnownError(err) { if _, ok := err.(*ContainerError); ok {
return err return err
} }
// Log any unexpected errors
containerError := &ContainerError{Container: container, Operation: operation, ExtraInfo: extraInfo, Err: err} containerError := &ContainerError{Container: container, Operation: operation, ExtraInfo: extraInfo, Err: err}
logrus.Error(containerError)
return containerError return containerError
} }
@ -129,21 +119,72 @@ func (e *ProcessError) Error() string {
s += " " + e.Operation 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)) s += fmt.Sprintf(" failed in Win32: %s (0x%x)", e.Err, win32FromError(e.Err))
default:
s += fmt.Sprintf(" failed: %s", e.Error())
} }
return s return s
} }
func makeProcessError(process *process, operation string, extraInfo string, err error) error { func makeProcessError(process *process, operation string, extraInfo string, err error) error {
// Return known errors to the client // Don't double wrap errors
if isKnownError(err) { if _, ok := err.(*ProcessError); ok {
return err return err
} }
// Log any unexpected errors
processError := &ProcessError{Process: process, Operation: operation, ExtraInfo: extraInfo, Err: err} processError := &ProcessError{Process: process, Operation: operation, ExtraInfo: extraInfo, Err: err}
logrus.Error(processError)
return 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
}

View file

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

View file

@ -90,6 +90,9 @@ type Container interface {
// HasPendingUpdates returns true if the container has updates pending to install. // HasPendingUpdates returns true if the container has updates pending to install.
HasPendingUpdates() (bool, error) HasPendingUpdates() (bool, error)
// Statistics returns statistics for a container.
Statistics() (Statistics, error)
// CreateProcess launches a new process within the container. // CreateProcess launches a new process within the container.
CreateProcess(c *ProcessConfig) (Process, error) CreateProcess(c *ProcessConfig) (Process, error)

View file

@ -71,9 +71,7 @@ func (process *process) Kill() error {
var resultp *uint16 var resultp *uint16
err := hcsTerminateProcess(process.handle, &resultp) err := hcsTerminateProcess(process.handle, &resultp)
err = processHcsResult(err, resultp) err = processHcsResult(err, resultp)
if err == ErrVmcomputeOperationPending { if err != nil {
return ErrVmcomputeOperationPending
} else if err != nil {
return makeProcessError(process, operation, "", err) return makeProcessError(process, operation, "", err)
} }
@ -118,8 +116,9 @@ func (process *process) WaitTimeout(timeout time.Duration) error {
} else { } else {
finished, err := waitTimeoutHelper(process, timeout) finished, err := waitTimeoutHelper(process, timeout)
if !finished { if !finished {
return ErrTimeout err = ErrTimeout
} else if err != nil { }
if err != nil {
return makeProcessError(process, operation, "", err) return makeProcessError(process, operation, "", err)
} }
} }
@ -160,7 +159,7 @@ func (process *process) ExitCode() (int, error) {
} }
if properties.Exited == false { 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) 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 := hcsGetProcessProperties(process.handle, &propertiesp, &resultp)
err = processHcsResult(err, resultp) err = processHcsResult(err, resultp)
if err != nil { if err != nil {
return nil, makeProcessError(process, operation, "", err) return nil, err
} }
if propertiesp == nil { 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}) pipes, err := makeOpenFiles([]syscall.Handle{stdIn, stdOut, stdErr})
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, makeProcessError(process, operation, "", err)
} }
logrus.Debugf(title+" succeeded processid=%d", process.processID) logrus.Debugf(title+" succeeded processid=%d", process.processID)

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +1,10 @@
package hcsshim package hcsshim
import ( import (
"io"
"syscall" "syscall"
"github.com/Microsoft/go-winio"
) )
var ( var (
@ -9,3 +12,28 @@ var (
hcsCallbackAPI = vmcomputedll.NewProc("HcsRegisterComputeSystemCallback") hcsCallbackAPI = vmcomputedll.NewProc("HcsRegisterComputeSystemCallback")
hcsCallbacksSupported = hcsCallbackAPI.Find() == nil 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
}

View file

@ -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 { func processAsyncHcsResult(err error, resultp *uint16, callbackNumber uintptr, expectedNotification hcsNotification, timeout *time.Duration) error {
err = processHcsResult(err, resultp) err = processHcsResult(err, resultp)
if err == ErrVmcomputeOperationPending { if IsPending(err) {
return waitForNotification(callbackNumber, expectedNotification, timeout) return waitForNotification(callbackNumber, expectedNotification, timeout)
} }

View file

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