errors.go 11 KB

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