aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go
blob: bca824b8e18f07f9ac087ef8c5132e32553f3cdf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
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 the the process cannot be found
	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")
)

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
	Extra  string
	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()
	}
	if e.Extra != "" {
		s += "\n(extra info: " + e.Extra + ")"
	}
	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, extra 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,
		Extra:  extra,
		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 or ErrProcNotFound.
func IsNotExist(err error) bool {
	err = getInnerError(err)
	return err == ErrComputeSystemDoesNotExist ||
		err == ErrElementNotFound ||
		err == ErrProcNotFound
}

// 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 or ErrProcNotFound.
func IsAlreadyStopped(err error) bool {
	err = getInnerError(err)
	return err == ErrVmcomputeAlreadyStopped ||
		err == ErrElementNotFound ||
		err == ErrProcNotFound
}

// 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
}

func getOperationLogResult(err error) (string, error) {
	switch err {
	case nil:
		return "Success", nil
	default:
		return "Error", err
	}
}