errors.go 10 KB

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