Merge pull request #25634 from darrenstahlmsft/RevendorHcsshim
Revendor hcsshim to v0.4.1
This commit is contained in:
commit
5e2a519957
19 changed files with 239 additions and 371 deletions
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
vendor/src/github.com/Microsoft/hcsshim/cgo.go
vendored
Normal file
7
vendor/src/github.com/Microsoft/hcsshim/cgo.go
vendored
Normal 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
|
107
vendor/src/github.com/Microsoft/hcsshim/container.go
vendored
107
vendor/src/github.com/Microsoft/hcsshim/container.go
vendored
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
113
vendor/src/github.com/Microsoft/hcsshim/errors.go
vendored
113
vendor/src/github.com/Microsoft/hcsshim/errors.go
vendored
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
vendor/src/github.com/Microsoft/hcsshim/utils.go
vendored
28
vendor/src/github.com/Microsoft/hcsshim/utils.go
vendored
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
Loading…
Reference in a new issue