errors.go 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. package hcs
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "syscall"
  7. "github.com/Microsoft/hcsshim/internal/interop"
  8. "github.com/sirupsen/logrus"
  9. )
  10. var (
  11. // ErrComputeSystemDoesNotExist is an error encountered when the container being operated on no longer exists
  12. ErrComputeSystemDoesNotExist = syscall.Errno(0xc037010e)
  13. // ErrElementNotFound is an error encountered when the object being referenced does not exist
  14. ErrElementNotFound = syscall.Errno(0x490)
  15. // ErrElementNotFound is an error encountered when the object being referenced does not exist
  16. ErrNotSupported = syscall.Errno(0x32)
  17. // ErrInvalidData is an error encountered when the request being sent to hcs is invalid/unsupported
  18. // decimal -2147024883 / hex 0x8007000d
  19. ErrInvalidData = syscall.Errno(0xd)
  20. // ErrHandleClose is an error encountered when the handle generating the notification being waited on has been closed
  21. ErrHandleClose = errors.New("hcsshim: the handle generating this notification has been closed")
  22. // ErrAlreadyClosed is an error encountered when using a handle that has been closed by the Close method
  23. ErrAlreadyClosed = errors.New("hcsshim: the handle has already been closed")
  24. // ErrInvalidNotificationType is an error encountered when an invalid notification type is used
  25. ErrInvalidNotificationType = errors.New("hcsshim: invalid notification type")
  26. // ErrInvalidProcessState is an error encountered when the process is not in a valid state for the requested operation
  27. ErrInvalidProcessState = errors.New("the process is in an invalid state for the attempted operation")
  28. // ErrTimeout is an error encountered when waiting on a notification times out
  29. ErrTimeout = errors.New("hcsshim: timeout waiting for notification")
  30. // ErrUnexpectedContainerExit is the error encountered when a container exits while waiting for
  31. // a different expected notification
  32. ErrUnexpectedContainerExit = errors.New("unexpected container exit")
  33. // ErrUnexpectedProcessAbort is the error encountered when communication with the compute service
  34. // is lost while waiting for a notification
  35. ErrUnexpectedProcessAbort = errors.New("lost communication with compute service")
  36. // ErrUnexpectedValue is an error encountered when hcs returns an invalid value
  37. ErrUnexpectedValue = errors.New("unexpected value returned from hcs")
  38. // ErrVmcomputeAlreadyStopped is an error encountered when a shutdown or terminate request is made on a stopped container
  39. ErrVmcomputeAlreadyStopped = syscall.Errno(0xc0370110)
  40. // ErrVmcomputeOperationPending is an error encountered when the operation is being completed asynchronously
  41. ErrVmcomputeOperationPending = syscall.Errno(0xC0370103)
  42. // ErrVmcomputeOperationInvalidState is an error encountered when the compute system is not in a valid state for the requested operation
  43. ErrVmcomputeOperationInvalidState = syscall.Errno(0xc0370105)
  44. // ErrProcNotFound is an error encountered when the the process cannot be found
  45. ErrProcNotFound = syscall.Errno(0x7f)
  46. // ErrVmcomputeOperationAccessIsDenied is an error which can be encountered when enumerating compute systems in RS1/RS2
  47. // builds when the underlying silo might be in the process of terminating. HCS was fixed in RS3.
  48. ErrVmcomputeOperationAccessIsDenied = syscall.Errno(0x5)
  49. // ErrVmcomputeInvalidJSON is an error encountered when the compute system does not support/understand the messages sent by management
  50. ErrVmcomputeInvalidJSON = syscall.Errno(0xc037010d)
  51. // ErrVmcomputeUnknownMessage is an error encountered guest compute system doesn't support the message
  52. ErrVmcomputeUnknownMessage = syscall.Errno(0xc037010b)
  53. // ErrNotSupported is an error encountered when hcs doesn't support the request
  54. ErrPlatformNotSupported = errors.New("unsupported platform request")
  55. )
  56. type ErrorEvent struct {
  57. Message string `json:"Message,omitempty"` // Fully formated error message
  58. StackTrace string `json:"StackTrace,omitempty"` // Stack trace in string form
  59. Provider string `json:"Provider,omitempty"`
  60. EventID uint16 `json:"EventId,omitempty"`
  61. Flags uint32 `json:"Flags,omitempty"`
  62. Source string `json:"Source,omitempty"`
  63. //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)
  64. }
  65. type hcsResult struct {
  66. Error int32
  67. ErrorMessage string
  68. ErrorEvents []ErrorEvent `json:"ErrorEvents,omitempty"`
  69. }
  70. func (ev *ErrorEvent) String() string {
  71. evs := "[Event Detail: " + ev.Message
  72. if ev.StackTrace != "" {
  73. evs += " Stack Trace: " + ev.StackTrace
  74. }
  75. if ev.Provider != "" {
  76. evs += " Provider: " + ev.Provider
  77. }
  78. if ev.EventID != 0 {
  79. evs = fmt.Sprintf("%s EventID: %d", evs, ev.EventID)
  80. }
  81. if ev.Flags != 0 {
  82. evs = fmt.Sprintf("%s flags: %d", evs, ev.Flags)
  83. }
  84. if ev.Source != "" {
  85. evs += " Source: " + ev.Source
  86. }
  87. evs += "]"
  88. return evs
  89. }
  90. func processHcsResult(resultp *uint16) []ErrorEvent {
  91. if resultp != nil {
  92. resultj := interop.ConvertAndFreeCoTaskMemString(resultp)
  93. logrus.Debugf("Result: %s", resultj)
  94. result := &hcsResult{}
  95. if err := json.Unmarshal([]byte(resultj), result); err != nil {
  96. logrus.Warnf("Could not unmarshal HCS result %s: %s", resultj, err)
  97. return nil
  98. }
  99. return result.ErrorEvents
  100. }
  101. return nil
  102. }
  103. type HcsError struct {
  104. Op string
  105. Err error
  106. Events []ErrorEvent
  107. }
  108. func (e *HcsError) Error() string {
  109. s := e.Op + ": " + e.Err.Error()
  110. for _, ev := range e.Events {
  111. s += "\n" + ev.String()
  112. }
  113. return s
  114. }
  115. // ProcessError is an error encountered in HCS during an operation on a Process object
  116. type ProcessError struct {
  117. SystemID string
  118. Pid int
  119. Op string
  120. Err error
  121. Events []ErrorEvent
  122. }
  123. // SystemError is an error encountered in HCS during an operation on a Container object
  124. type SystemError struct {
  125. ID string
  126. Op string
  127. Err error
  128. Extra string
  129. Events []ErrorEvent
  130. }
  131. func (e *SystemError) Error() string {
  132. s := e.Op + " " + e.ID + ": " + e.Err.Error()
  133. for _, ev := range e.Events {
  134. s += "\n" + ev.String()
  135. }
  136. if e.Extra != "" {
  137. s += "\n(extra info: " + e.Extra + ")"
  138. }
  139. return s
  140. }
  141. func makeSystemError(system *System, op string, extra string, err error, events []ErrorEvent) error {
  142. // Don't double wrap errors
  143. if _, ok := err.(*SystemError); ok {
  144. return err
  145. }
  146. return &SystemError{
  147. ID: system.ID(),
  148. Op: op,
  149. Extra: extra,
  150. Err: err,
  151. Events: events,
  152. }
  153. }
  154. func (e *ProcessError) Error() string {
  155. s := fmt.Sprintf("%s %s:%d: %s", e.Op, e.SystemID, e.Pid, e.Err.Error())
  156. for _, ev := range e.Events {
  157. s += "\n" + ev.String()
  158. }
  159. return s
  160. }
  161. func makeProcessError(process *Process, op string, err error, events []ErrorEvent) error {
  162. // Don't double wrap errors
  163. if _, ok := err.(*ProcessError); ok {
  164. return err
  165. }
  166. return &ProcessError{
  167. Pid: process.Pid(),
  168. SystemID: process.SystemID(),
  169. Op: op,
  170. Err: err,
  171. Events: events,
  172. }
  173. }
  174. // IsNotExist checks if an error is caused by the Container or Process not existing.
  175. // Note: Currently, ErrElementNotFound can mean that a Process has either
  176. // already exited, or does not exist. Both IsAlreadyStopped and IsNotExist
  177. // will currently return true when the error is ErrElementNotFound or ErrProcNotFound.
  178. func IsNotExist(err error) bool {
  179. err = getInnerError(err)
  180. return err == ErrComputeSystemDoesNotExist ||
  181. err == ErrElementNotFound ||
  182. err == ErrProcNotFound
  183. }
  184. // IsAlreadyClosed checks if an error is caused by the Container or Process having been
  185. // already closed by a call to the Close() method.
  186. func IsAlreadyClosed(err error) bool {
  187. err = getInnerError(err)
  188. return err == ErrAlreadyClosed
  189. }
  190. // IsPending returns a boolean indicating whether the error is that
  191. // the requested operation is being completed in the background.
  192. func IsPending(err error) bool {
  193. err = getInnerError(err)
  194. return err == ErrVmcomputeOperationPending
  195. }
  196. // IsTimeout returns a boolean indicating whether the error is caused by
  197. // a timeout waiting for the operation to complete.
  198. func IsTimeout(err error) bool {
  199. err = getInnerError(err)
  200. return err == ErrTimeout
  201. }
  202. // IsAlreadyStopped returns a boolean indicating whether the error is caused by
  203. // a Container or Process being already stopped.
  204. // Note: Currently, ErrElementNotFound can mean that a Process has either
  205. // already exited, or does not exist. Both IsAlreadyStopped and IsNotExist
  206. // will currently return true when the error is ErrElementNotFound or ErrProcNotFound.
  207. func IsAlreadyStopped(err error) bool {
  208. err = getInnerError(err)
  209. return err == ErrVmcomputeAlreadyStopped ||
  210. err == ErrElementNotFound ||
  211. err == ErrProcNotFound
  212. }
  213. // IsNotSupported returns a boolean indicating whether the error is caused by
  214. // unsupported platform requests
  215. // Note: Currently Unsupported platform requests can be mean either
  216. // ErrVmcomputeInvalidJSON, ErrInvalidData, ErrNotSupported or ErrVmcomputeUnknownMessage
  217. // is thrown from the Platform
  218. func IsNotSupported(err error) bool {
  219. err = getInnerError(err)
  220. // If Platform doesn't recognize or support the request sent, below errors are seen
  221. return err == ErrVmcomputeInvalidJSON ||
  222. err == ErrInvalidData ||
  223. err == ErrNotSupported ||
  224. err == ErrVmcomputeUnknownMessage
  225. }
  226. func getInnerError(err error) error {
  227. switch pe := err.(type) {
  228. case nil:
  229. return nil
  230. case *HcsError:
  231. err = pe.Err
  232. case *SystemError:
  233. err = pe.Err
  234. case *ProcessError:
  235. err = pe.Err
  236. }
  237. return err
  238. }