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