
This version brings in some bug fixes to layer handling. The actual fix isn't present in the diff as it's not used here, but the Windows shim is built from the tag present in go.mod, so the fix will be in the Windows shim on a new release of Containerd if this tag is in. Signed-off-by: Daniel Canter <dcanter@microsoft.com>
328 lines
10 KiB
Go
328 lines
10 KiB
Go
package hcs
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"syscall"
|
|
|
|
"github.com/Microsoft/hcsshim/internal/log"
|
|
)
|
|
|
|
var (
|
|
// 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)
|
|
|
|
// ErrElementNotFound is an error encountered when the object being referenced does not exist
|
|
ErrNotSupported = syscall.Errno(0x32)
|
|
|
|
// ErrInvalidData is an error encountered when the request being sent to hcs is invalid/unsupported
|
|
// decimal -2147024883 / hex 0x8007000d
|
|
ErrInvalidData = syscall.Errno(0xd)
|
|
|
|
// 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")
|
|
|
|
// ErrAlreadyClosed is an error encountered when using a handle that has been closed by the Close method
|
|
ErrAlreadyClosed = errors.New("hcsshim: the handle has already been closed")
|
|
|
|
// ErrInvalidNotificationType is an error encountered when an invalid notification type is used
|
|
ErrInvalidNotificationType = errors.New("hcsshim: invalid notification type")
|
|
|
|
// ErrInvalidProcessState is an error encountered when the process is not in a valid state for the requested operation
|
|
ErrInvalidProcessState = errors.New("the process is in an invalid state for the attempted operation")
|
|
|
|
// ErrTimeout is an error encountered when waiting on a notification times out
|
|
ErrTimeout = errors.New("hcsshim: timeout waiting for notification")
|
|
|
|
// 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 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 encountered when hcs returns an invalid value
|
|
ErrUnexpectedValue = errors.New("unexpected value returned from hcs")
|
|
|
|
// 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 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 a procedure look up fails.
|
|
ErrProcNotFound = syscall.Errno(0x7f)
|
|
|
|
// ErrVmcomputeOperationAccessIsDenied is an error which can be encountered when enumerating compute systems in RS1/RS2
|
|
// builds when the underlying silo might be in the process of terminating. HCS was fixed in RS3.
|
|
ErrVmcomputeOperationAccessIsDenied = syscall.Errno(0x5)
|
|
|
|
// ErrVmcomputeInvalidJSON is an error encountered when the compute system does not support/understand the messages sent by management
|
|
ErrVmcomputeInvalidJSON = syscall.Errno(0xc037010d)
|
|
|
|
// ErrVmcomputeUnknownMessage is an error encountered guest compute system doesn't support the message
|
|
ErrVmcomputeUnknownMessage = syscall.Errno(0xc037010b)
|
|
|
|
// ErrVmcomputeUnexpectedExit is an error encountered when the compute system terminates unexpectedly
|
|
ErrVmcomputeUnexpectedExit = syscall.Errno(0xC0370106)
|
|
|
|
// ErrNotSupported is an error encountered when hcs doesn't support the request
|
|
ErrPlatformNotSupported = errors.New("unsupported platform request")
|
|
)
|
|
|
|
type ErrorEvent struct {
|
|
Message string `json:"Message,omitempty"` // Fully formated error message
|
|
StackTrace string `json:"StackTrace,omitempty"` // Stack trace in string form
|
|
Provider string `json:"Provider,omitempty"`
|
|
EventID uint16 `json:"EventId,omitempty"`
|
|
Flags uint32 `json:"Flags,omitempty"`
|
|
Source string `json:"Source,omitempty"`
|
|
//Data []EventData `json:"Data,omitempty"` // Omit this as HCS doesn't encode this well. It's more confusing to include. It is however logged in debug mode (see processHcsResult function)
|
|
}
|
|
|
|
type hcsResult struct {
|
|
Error int32
|
|
ErrorMessage string
|
|
ErrorEvents []ErrorEvent `json:"ErrorEvents,omitempty"`
|
|
}
|
|
|
|
func (ev *ErrorEvent) String() string {
|
|
evs := "[Event Detail: " + ev.Message
|
|
if ev.StackTrace != "" {
|
|
evs += " Stack Trace: " + ev.StackTrace
|
|
}
|
|
if ev.Provider != "" {
|
|
evs += " Provider: " + ev.Provider
|
|
}
|
|
if ev.EventID != 0 {
|
|
evs = fmt.Sprintf("%s EventID: %d", evs, ev.EventID)
|
|
}
|
|
if ev.Flags != 0 {
|
|
evs = fmt.Sprintf("%s flags: %d", evs, ev.Flags)
|
|
}
|
|
if ev.Source != "" {
|
|
evs += " Source: " + ev.Source
|
|
}
|
|
evs += "]"
|
|
return evs
|
|
}
|
|
|
|
func processHcsResult(ctx context.Context, resultJSON string) []ErrorEvent {
|
|
if resultJSON != "" {
|
|
result := &hcsResult{}
|
|
if err := json.Unmarshal([]byte(resultJSON), result); err != nil {
|
|
log.G(ctx).WithError(err).Warning("Could not unmarshal HCS result")
|
|
return nil
|
|
}
|
|
return result.ErrorEvents
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type HcsError struct {
|
|
Op string
|
|
Err error
|
|
Events []ErrorEvent
|
|
}
|
|
|
|
var _ net.Error = &HcsError{}
|
|
|
|
func (e *HcsError) Error() string {
|
|
s := e.Op + ": " + e.Err.Error()
|
|
for _, ev := range e.Events {
|
|
s += "\n" + ev.String()
|
|
}
|
|
return s
|
|
}
|
|
|
|
func (e *HcsError) Temporary() bool {
|
|
err, ok := e.Err.(net.Error)
|
|
return ok && err.Temporary()
|
|
}
|
|
|
|
func (e *HcsError) Timeout() bool {
|
|
err, ok := e.Err.(net.Error)
|
|
return ok && err.Timeout()
|
|
}
|
|
|
|
// ProcessError is an error encountered in HCS during an operation on a Process object
|
|
type ProcessError struct {
|
|
SystemID string
|
|
Pid int
|
|
Op string
|
|
Err error
|
|
Events []ErrorEvent
|
|
}
|
|
|
|
var _ net.Error = &ProcessError{}
|
|
|
|
// SystemError is an error encountered in HCS during an operation on a Container object
|
|
type SystemError struct {
|
|
ID string
|
|
Op string
|
|
Err error
|
|
Events []ErrorEvent
|
|
}
|
|
|
|
var _ net.Error = &SystemError{}
|
|
|
|
func (e *SystemError) Error() string {
|
|
s := e.Op + " " + e.ID + ": " + e.Err.Error()
|
|
for _, ev := range e.Events {
|
|
s += "\n" + ev.String()
|
|
}
|
|
return s
|
|
}
|
|
|
|
func (e *SystemError) Temporary() bool {
|
|
err, ok := e.Err.(net.Error)
|
|
return ok && err.Temporary()
|
|
}
|
|
|
|
func (e *SystemError) Timeout() bool {
|
|
err, ok := e.Err.(net.Error)
|
|
return ok && err.Timeout()
|
|
}
|
|
|
|
func makeSystemError(system *System, op string, err error, events []ErrorEvent) error {
|
|
// Don't double wrap errors
|
|
if _, ok := err.(*SystemError); ok {
|
|
return err
|
|
}
|
|
return &SystemError{
|
|
ID: system.ID(),
|
|
Op: op,
|
|
Err: err,
|
|
Events: events,
|
|
}
|
|
}
|
|
|
|
func (e *ProcessError) Error() string {
|
|
s := fmt.Sprintf("%s %s:%d: %s", e.Op, e.SystemID, e.Pid, e.Err.Error())
|
|
for _, ev := range e.Events {
|
|
s += "\n" + ev.String()
|
|
}
|
|
return s
|
|
}
|
|
|
|
func (e *ProcessError) Temporary() bool {
|
|
err, ok := e.Err.(net.Error)
|
|
return ok && err.Temporary()
|
|
}
|
|
|
|
func (e *ProcessError) Timeout() bool {
|
|
err, ok := e.Err.(net.Error)
|
|
return ok && err.Timeout()
|
|
}
|
|
|
|
func makeProcessError(process *Process, op string, err error, events []ErrorEvent) error {
|
|
// Don't double wrap errors
|
|
if _, ok := err.(*ProcessError); ok {
|
|
return err
|
|
}
|
|
return &ProcessError{
|
|
Pid: process.Pid(),
|
|
SystemID: process.SystemID(),
|
|
Op: op,
|
|
Err: err,
|
|
Events: events,
|
|
}
|
|
}
|
|
|
|
// 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.
|
|
func IsNotExist(err error) bool {
|
|
err = getInnerError(err)
|
|
return err == ErrComputeSystemDoesNotExist ||
|
|
err == ErrElementNotFound
|
|
}
|
|
|
|
// IsAlreadyClosed checks if an error is caused by the Container or Process having been
|
|
// already closed by a call to the Close() method.
|
|
func IsAlreadyClosed(err error) bool {
|
|
err = getInnerError(err)
|
|
return err == ErrAlreadyClosed
|
|
}
|
|
|
|
// 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 {
|
|
if err, ok := err.(net.Error); ok && err.Timeout() {
|
|
return true
|
|
}
|
|
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.
|
|
func IsAlreadyStopped(err error) bool {
|
|
err = getInnerError(err)
|
|
return err == ErrVmcomputeAlreadyStopped ||
|
|
err == ErrElementNotFound
|
|
}
|
|
|
|
// IsNotSupported returns a boolean indicating whether the error is caused by
|
|
// unsupported platform requests
|
|
// Note: Currently Unsupported platform requests can be mean either
|
|
// ErrVmcomputeInvalidJSON, ErrInvalidData, ErrNotSupported or ErrVmcomputeUnknownMessage
|
|
// is thrown from the Platform
|
|
func IsNotSupported(err error) bool {
|
|
err = getInnerError(err)
|
|
// If Platform doesn't recognize or support the request sent, below errors are seen
|
|
return err == ErrVmcomputeInvalidJSON ||
|
|
err == ErrInvalidData ||
|
|
err == ErrNotSupported ||
|
|
err == ErrVmcomputeUnknownMessage
|
|
}
|
|
|
|
// IsOperationInvalidState returns true when err is caused by
|
|
// `ErrVmcomputeOperationInvalidState`.
|
|
func IsOperationInvalidState(err error) bool {
|
|
err = getInnerError(err)
|
|
return err == ErrVmcomputeOperationInvalidState
|
|
}
|
|
|
|
// IsAccessIsDenied returns true when err is caused by
|
|
// `ErrVmcomputeOperationAccessIsDenied`.
|
|
func IsAccessIsDenied(err error) bool {
|
|
err = getInnerError(err)
|
|
return err == ErrVmcomputeOperationAccessIsDenied
|
|
}
|
|
|
|
func getInnerError(err error) error {
|
|
switch pe := err.(type) {
|
|
case nil:
|
|
return nil
|
|
case *HcsError:
|
|
err = pe.Err
|
|
case *SystemError:
|
|
err = pe.Err
|
|
case *ProcessError:
|
|
err = pe.Err
|
|
}
|
|
return err
|
|
}
|