...

Source file src/github.com/Microsoft/hcsshim/internal/hcs/errors.go

Documentation: github.com/Microsoft/hcsshim/internal/hcs

     1  //go:build windows
     2  
     3  package hcs
     4  
     5  import (
     6  	"context"
     7  	"encoding/json"
     8  	"errors"
     9  	"fmt"
    10  	"net"
    11  	"syscall"
    12  
    13  	"github.com/Microsoft/hcsshim/internal/log"
    14  )
    15  
    16  var (
    17  	// ErrComputeSystemDoesNotExist is an error encountered when the container being operated on no longer exists
    18  	ErrComputeSystemDoesNotExist = syscall.Errno(0xc037010e)
    19  
    20  	// ErrElementNotFound is an error encountered when the object being referenced does not exist
    21  	ErrElementNotFound = syscall.Errno(0x490)
    22  
    23  	// ErrElementNotFound is an error encountered when the object being referenced does not exist
    24  	ErrNotSupported = syscall.Errno(0x32)
    25  
    26  	// ErrInvalidData is an error encountered when the request being sent to hcs is invalid/unsupported
    27  	// decimal -2147024883 / hex 0x8007000d
    28  	ErrInvalidData = syscall.Errno(0xd)
    29  
    30  	// ErrHandleClose is an error encountered when the handle generating the notification being waited on has been closed
    31  	ErrHandleClose = errors.New("hcsshim: the handle generating this notification has been closed")
    32  
    33  	// ErrAlreadyClosed is an error encountered when using a handle that has been closed by the Close method
    34  	ErrAlreadyClosed = errors.New("hcsshim: the handle has already been closed")
    35  
    36  	// ErrInvalidNotificationType is an error encountered when an invalid notification type is used
    37  	ErrInvalidNotificationType = errors.New("hcsshim: invalid notification type")
    38  
    39  	// ErrInvalidProcessState is an error encountered when the process is not in a valid state for the requested operation
    40  	ErrInvalidProcessState = errors.New("the process is in an invalid state for the attempted operation")
    41  
    42  	// ErrTimeout is an error encountered when waiting on a notification times out
    43  	ErrTimeout = errors.New("hcsshim: timeout waiting for notification")
    44  
    45  	// ErrUnexpectedContainerExit is the error encountered when a container exits while waiting for
    46  	// a different expected notification
    47  	ErrUnexpectedContainerExit = errors.New("unexpected container exit")
    48  
    49  	// ErrUnexpectedProcessAbort is the error encountered when communication with the compute service
    50  	// is lost while waiting for a notification
    51  	ErrUnexpectedProcessAbort = errors.New("lost communication with compute service")
    52  
    53  	// ErrUnexpectedValue is an error encountered when hcs returns an invalid value
    54  	ErrUnexpectedValue = errors.New("unexpected value returned from hcs")
    55  
    56  	// ErrOperationDenied is an error when hcs attempts an operation that is explicitly denied
    57  	ErrOperationDenied = errors.New("operation denied")
    58  
    59  	// ErrVmcomputeAlreadyStopped is an error encountered when a shutdown or terminate request is made on a stopped container
    60  	ErrVmcomputeAlreadyStopped = syscall.Errno(0xc0370110)
    61  
    62  	// ErrVmcomputeOperationPending is an error encountered when the operation is being completed asynchronously
    63  	ErrVmcomputeOperationPending = syscall.Errno(0xC0370103)
    64  
    65  	// ErrVmcomputeOperationInvalidState is an error encountered when the compute system is not in a valid state for the requested operation
    66  	ErrVmcomputeOperationInvalidState = syscall.Errno(0xc0370105)
    67  
    68  	// ErrProcNotFound is an error encountered when a procedure look up fails.
    69  	ErrProcNotFound = syscall.Errno(0x7f)
    70  
    71  	// ErrVmcomputeOperationAccessIsDenied is an error which can be encountered when enumerating compute systems in RS1/RS2
    72  	// builds when the underlying silo might be in the process of terminating. HCS was fixed in RS3.
    73  	ErrVmcomputeOperationAccessIsDenied = syscall.Errno(0x5)
    74  
    75  	// ErrVmcomputeInvalidJSON is an error encountered when the compute system does not support/understand the messages sent by management
    76  	ErrVmcomputeInvalidJSON = syscall.Errno(0xc037010d)
    77  
    78  	// ErrVmcomputeUnknownMessage is an error encountered guest compute system doesn't support the message
    79  	ErrVmcomputeUnknownMessage = syscall.Errno(0xc037010b)
    80  
    81  	// ErrVmcomputeUnexpectedExit is an error encountered when the compute system terminates unexpectedly
    82  	ErrVmcomputeUnexpectedExit = syscall.Errno(0xC0370106)
    83  
    84  	// ErrNotSupported is an error encountered when hcs doesn't support the request
    85  	ErrPlatformNotSupported = errors.New("unsupported platform request")
    86  
    87  	// ErrProcessAlreadyStopped is returned by hcs if the process we're trying to kill has already been stopped.
    88  	ErrProcessAlreadyStopped = syscall.Errno(0x8037011f)
    89  
    90  	// ErrInvalidHandle is an error that can be encountered when querying the properties of a compute system when the handle to that
    91  	// compute system has already been closed.
    92  	ErrInvalidHandle = syscall.Errno(0x6)
    93  )
    94  
    95  type ErrorEvent struct {
    96  	Message    string `json:"Message,omitempty"`    // Fully formated error message
    97  	StackTrace string `json:"StackTrace,omitempty"` // Stack trace in string form
    98  	Provider   string `json:"Provider,omitempty"`
    99  	EventID    uint16 `json:"EventId,omitempty"`
   100  	Flags      uint32 `json:"Flags,omitempty"`
   101  	Source     string `json:"Source,omitempty"`
   102  	//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)
   103  }
   104  
   105  type hcsResult struct {
   106  	Error        int32
   107  	ErrorMessage string
   108  	ErrorEvents  []ErrorEvent `json:"ErrorEvents,omitempty"`
   109  }
   110  
   111  func (ev *ErrorEvent) String() string {
   112  	evs := "[Event Detail: " + ev.Message
   113  	if ev.StackTrace != "" {
   114  		evs += " Stack Trace: " + ev.StackTrace
   115  	}
   116  	if ev.Provider != "" {
   117  		evs += " Provider: " + ev.Provider
   118  	}
   119  	if ev.EventID != 0 {
   120  		evs = fmt.Sprintf("%s EventID: %d", evs, ev.EventID)
   121  	}
   122  	if ev.Flags != 0 {
   123  		evs = fmt.Sprintf("%s flags: %d", evs, ev.Flags)
   124  	}
   125  	if ev.Source != "" {
   126  		evs += " Source: " + ev.Source
   127  	}
   128  	evs += "]"
   129  	return evs
   130  }
   131  
   132  func processHcsResult(ctx context.Context, resultJSON string) []ErrorEvent {
   133  	if resultJSON != "" {
   134  		result := &hcsResult{}
   135  		if err := json.Unmarshal([]byte(resultJSON), result); err != nil {
   136  			log.G(ctx).WithError(err).Warning("Could not unmarshal HCS result")
   137  			return nil
   138  		}
   139  		return result.ErrorEvents
   140  	}
   141  	return nil
   142  }
   143  
   144  type HcsError struct {
   145  	Op     string
   146  	Err    error
   147  	Events []ErrorEvent
   148  }
   149  
   150  var _ net.Error = &HcsError{}
   151  
   152  func (e *HcsError) Error() string {
   153  	s := e.Op + ": " + e.Err.Error()
   154  	for _, ev := range e.Events {
   155  		s += "\n" + ev.String()
   156  	}
   157  	return s
   158  }
   159  
   160  func (e *HcsError) Is(target error) bool {
   161  	return errors.Is(e.Err, target)
   162  }
   163  
   164  // unwrap isnt really needed, but helpful convince function
   165  
   166  func (e *HcsError) Unwrap() error {
   167  	return e.Err
   168  }
   169  
   170  // Deprecated: net.Error.Temporary is deprecated.
   171  func (e *HcsError) Temporary() bool {
   172  	err := e.netError()
   173  	return (err != nil) && err.Temporary()
   174  }
   175  
   176  func (e *HcsError) Timeout() bool {
   177  	err := e.netError()
   178  	return (err != nil) && err.Timeout()
   179  }
   180  
   181  func (e *HcsError) netError() (err net.Error) {
   182  	if errors.As(e.Unwrap(), &err) {
   183  		return err
   184  	}
   185  	return nil
   186  }
   187  
   188  // SystemError is an error encountered in HCS during an operation on a Container object
   189  type SystemError struct {
   190  	HcsError
   191  	ID string
   192  }
   193  
   194  var _ net.Error = &SystemError{}
   195  
   196  func (e *SystemError) Error() string {
   197  	s := e.Op + " " + e.ID + ": " + e.Err.Error()
   198  	for _, ev := range e.Events {
   199  		s += "\n" + ev.String()
   200  	}
   201  	return s
   202  }
   203  
   204  func makeSystemError(system *System, op string, err error, events []ErrorEvent) error {
   205  	// Don't double wrap errors
   206  	var e *SystemError
   207  	if errors.As(err, &e) {
   208  		return err
   209  	}
   210  
   211  	return &SystemError{
   212  		ID: system.ID(),
   213  		HcsError: HcsError{
   214  			Op:     op,
   215  			Err:    err,
   216  			Events: events,
   217  		},
   218  	}
   219  }
   220  
   221  // ProcessError is an error encountered in HCS during an operation on a Process object
   222  type ProcessError struct {
   223  	HcsError
   224  	SystemID string
   225  	Pid      int
   226  }
   227  
   228  var _ net.Error = &ProcessError{}
   229  
   230  func (e *ProcessError) Error() string {
   231  	s := fmt.Sprintf("%s %s:%d: %s", e.Op, e.SystemID, e.Pid, e.Err.Error())
   232  	for _, ev := range e.Events {
   233  		s += "\n" + ev.String()
   234  	}
   235  	return s
   236  }
   237  
   238  func makeProcessError(process *Process, op string, err error, events []ErrorEvent) error {
   239  	// Don't double wrap errors
   240  	var e *ProcessError
   241  	if errors.As(err, &e) {
   242  		return err
   243  	}
   244  	return &ProcessError{
   245  		Pid:      process.Pid(),
   246  		SystemID: process.SystemID(),
   247  		HcsError: HcsError{
   248  			Op:     op,
   249  			Err:    err,
   250  			Events: events,
   251  		},
   252  	}
   253  }
   254  
   255  // IsNotExist checks if an error is caused by the Container or Process not existing.
   256  // Note: Currently, ErrElementNotFound can mean that a Process has either
   257  // already exited, or does not exist. Both IsAlreadyStopped and IsNotExist
   258  // will currently return true when the error is ErrElementNotFound.
   259  func IsNotExist(err error) bool {
   260  	return IsAny(err, ErrComputeSystemDoesNotExist, ErrElementNotFound)
   261  }
   262  
   263  // IsErrorInvalidHandle checks whether the error is the result of an operation carried
   264  // out on a handle that is invalid/closed. This error popped up while trying to query
   265  // stats on a container in the process of being stopped.
   266  func IsErrorInvalidHandle(err error) bool {
   267  	return errors.Is(err, ErrInvalidHandle)
   268  }
   269  
   270  // IsAlreadyClosed checks if an error is caused by the Container or Process having been
   271  // already closed by a call to the Close() method.
   272  func IsAlreadyClosed(err error) bool {
   273  	return errors.Is(err, ErrAlreadyClosed)
   274  }
   275  
   276  // IsPending returns a boolean indicating whether the error is that
   277  // the requested operation is being completed in the background.
   278  func IsPending(err error) bool {
   279  	return errors.Is(err, ErrVmcomputeOperationPending)
   280  }
   281  
   282  // IsTimeout returns a boolean indicating whether the error is caused by
   283  // a timeout waiting for the operation to complete.
   284  func IsTimeout(err error) bool {
   285  	// HcsError and co. implement Timeout regardless of whether the errors they wrap do,
   286  	// so `errors.As(err, net.Error)`` will always be true.
   287  	// Using `errors.As(err.Unwrap(), net.Err)` wont work for general errors.
   288  	// So first check if there an `ErrTimeout` in the chain, then convert to a net error.
   289  	if errors.Is(err, ErrTimeout) {
   290  		return true
   291  	}
   292  
   293  	var nerr net.Error
   294  	return errors.As(err, &nerr) && nerr.Timeout()
   295  }
   296  
   297  // IsAlreadyStopped returns a boolean indicating whether the error is caused by
   298  // a Container or Process being already stopped.
   299  // Note: Currently, ErrElementNotFound can mean that a Process has either
   300  // already exited, or does not exist. Both IsAlreadyStopped and IsNotExist
   301  // will currently return true when the error is ErrElementNotFound.
   302  func IsAlreadyStopped(err error) bool {
   303  	return IsAny(err, ErrVmcomputeAlreadyStopped, ErrProcessAlreadyStopped, ErrElementNotFound)
   304  }
   305  
   306  // IsNotSupported returns a boolean indicating whether the error is caused by
   307  // unsupported platform requests
   308  // Note: Currently Unsupported platform requests can be mean either
   309  // ErrVmcomputeInvalidJSON, ErrInvalidData, ErrNotSupported or ErrVmcomputeUnknownMessage
   310  // is thrown from the Platform
   311  func IsNotSupported(err error) bool {
   312  	// If Platform doesn't recognize or support the request sent, below errors are seen
   313  	return IsAny(err, ErrVmcomputeInvalidJSON, ErrInvalidData, ErrNotSupported, ErrVmcomputeUnknownMessage)
   314  }
   315  
   316  // IsOperationInvalidState returns true when err is caused by
   317  // `ErrVmcomputeOperationInvalidState`.
   318  func IsOperationInvalidState(err error) bool {
   319  	return errors.Is(err, ErrVmcomputeOperationInvalidState)
   320  }
   321  
   322  // IsAccessIsDenied returns true when err is caused by
   323  // `ErrVmcomputeOperationAccessIsDenied`.
   324  func IsAccessIsDenied(err error) bool {
   325  	return errors.Is(err, ErrVmcomputeOperationAccessIsDenied)
   326  }
   327  
   328  // IsAny is a vectorized version of [errors.Is], it returns true if err is one of targets.
   329  func IsAny(err error, targets ...error) bool {
   330  	for _, e := range targets {
   331  		if errors.Is(err, e) {
   332  			return true
   333  		}
   334  	}
   335  	return false
   336  }
   337  

View as plain text