mirror of https://github.com/containers/podman.git
				
				
				
			
		
			
				
	
	
		
			344 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			344 lines
		
	
	
		
			11 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")
 | |
| 
 | |
| 	// 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 err == ErrComputeSystemDoesNotExist ||
 | |
| 		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 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 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 == ErrProcessAlreadyStopped ||
 | |
| 		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
 | |
| }
 |