This contains quite a bit (also bumps google/uuid to 1.3.0). Some HostProcess container improvements to get ready for whenever it goes to stable in Kubernetes, Hyper-V (windows) container support for CRI, and a plethora of other small additions and fixes. Signed-off-by: Daniel Canter <dcanter@microsoft.com>
		
			
				
	
	
		
			349 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			349 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
//go:build windows
 | 
						|
 | 
						|
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")
 | 
						|
 | 
						|
	// ErrOperationDenied is an error when hcs attempts an operation that is explicitly denied
 | 
						|
	ErrOperationDenied = errors.New("operation denied")
 | 
						|
 | 
						|
	// 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")
 | 
						|
 | 
						|
	// ErrProcessAlreadyStopped is returned by hcs if the process we're trying to kill has already been stopped.
 | 
						|
	ErrProcessAlreadyStopped = syscall.Errno(0x8037011f)
 | 
						|
 | 
						|
	// ErrInvalidHandle is an error that can be encountrered when querying the properties of a compute system when the handle to that
 | 
						|
	// compute system has already been closed.
 | 
						|
	ErrInvalidHandle = syscall.Errno(0x6)
 | 
						|
)
 | 
						|
 | 
						|
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 errors.Is(err, ErrComputeSystemDoesNotExist) ||
 | 
						|
		errors.Is(err, ErrElementNotFound)
 | 
						|
}
 | 
						|
 | 
						|
// IsErrorInvalidHandle checks whether the error is the result of an operation carried
 | 
						|
// out on a handle that is invalid/closed. This error popped up while trying to query
 | 
						|
// stats on a container in the process of being stopped.
 | 
						|
func IsErrorInvalidHandle(err error) bool {
 | 
						|
	err = getInnerError(err)
 | 
						|
	return errors.Is(err, ErrInvalidHandle)
 | 
						|
}
 | 
						|
 | 
						|
// 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 errors.Is(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 errors.Is(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 errors.Is(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 errors.Is(err, ErrVmcomputeAlreadyStopped) ||
 | 
						|
		errors.Is(err, ErrProcessAlreadyStopped) ||
 | 
						|
		errors.Is(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 errors.Is(err, ErrVmcomputeInvalidJSON) ||
 | 
						|
		errors.Is(err, ErrInvalidData) ||
 | 
						|
		errors.Is(err, ErrNotSupported) ||
 | 
						|
		errors.Is(err, ErrVmcomputeUnknownMessage)
 | 
						|
}
 | 
						|
 | 
						|
// IsOperationInvalidState returns true when err is caused by
 | 
						|
// `ErrVmcomputeOperationInvalidState`.
 | 
						|
func IsOperationInvalidState(err error) bool {
 | 
						|
	err = getInnerError(err)
 | 
						|
	return errors.Is(err, ErrVmcomputeOperationInvalidState)
 | 
						|
}
 | 
						|
 | 
						|
// IsAccessIsDenied returns true when err is caused by
 | 
						|
// `ErrVmcomputeOperationAccessIsDenied`.
 | 
						|
func IsAccessIsDenied(err error) bool {
 | 
						|
	err = getInnerError(err)
 | 
						|
	return errors.Is(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
 | 
						|
}
 |