Skip to content

Commit

Permalink
hcs: Include error events in errors
Browse files Browse the repository at this point in the history
  • Loading branch information
John Starks committed May 18, 2018
1 parent d93eb2b commit e70197a
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 149 deletions.
17 changes: 12 additions & 5 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ type ProcessError struct {
Operation string
ExtraInfo string
Err error
Events []hcs.ErrorEvent
}

// ContainerError is an error encountered in HCS during an operation on a Container object
Expand All @@ -104,6 +105,7 @@ type ContainerError struct {
Operation string
ExtraInfo string
Err error
Events []hcs.ErrorEvent
}

func (e *ContainerError) Error() string {
Expand All @@ -130,6 +132,10 @@ func (e *ContainerError) Error() string {
s += fmt.Sprintf(": %s", e.Err.Error())
}

for _, ev := range e.Events {
s += "\n" + ev.String()
}

if e.ExtraInfo != "" {
s += " extra info: " + e.ExtraInfo
}
Expand Down Expand Up @@ -174,6 +180,10 @@ func (e *ProcessError) Error() string {
s += fmt.Sprintf(": %s", e.Err.Error())
}

for _, ev := range e.Events {
s += "\n" + ev.String()
}

return s
}

Expand Down Expand Up @@ -210,14 +220,12 @@ func IsAlreadyClosed(err error) bool {
// the requested operation is being completed in the background.
func IsPending(err error) bool {
return hcs.IsPending(getInnerError(err))

}

// IsTimeout returns a boolean indicating whether the error is caused by
// a timeout waiting for the operation to complete.
func IsTimeout(err error) bool {
return hcs.IsTimeout(getInnerError(err))

}

// IsAlreadyStopped returns a boolean indicating whether the error is caused by
Expand All @@ -227,7 +235,6 @@ func IsTimeout(err error) bool {
// will currently return true when the error is ErrElementNotFound or ErrProcNotFound.
func IsAlreadyStopped(err error) bool {
return hcs.IsAlreadyStopped(getInnerError(err))

}

// IsNotSupported returns a boolean indicating whether the error is caused by
Expand All @@ -253,14 +260,14 @@ func getInnerError(err error) error {

func convertSystemError(err error, c *container) error {
if serr, ok := err.(*hcs.SystemError); ok {
return &ContainerError{Container: c, Operation: serr.Operation, ExtraInfo: serr.ExtraInfo, Err: serr.Err}
return &ContainerError{Container: c, Operation: serr.Op, ExtraInfo: serr.Extra, Err: serr.Err, Events: serr.Events}
}
return err
}

func convertProcessError(err error, p *process) error {
if perr, ok := err.(*hcs.ProcessError); ok {
return &ProcessError{Process: p, Operation: perr.Operation, ExtraInfo: perr.ExtraInfo, Err: perr.Err}
return &ProcessError{Process: p, Operation: perr.Op, Err: perr.Err, Events: perr.Events}
}
return err
}
163 changes: 104 additions & 59 deletions internal/hcs/errors.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package hcs

import (
"encoding/json"
"errors"
"fmt"
"syscall"

"github.com/Microsoft/hcsshim/internal/hcserror"
"github.com/Microsoft/hcsshim/internal/interop"
"github.com/sirupsen/logrus"
)

var (
Expand Down Expand Up @@ -74,93 +76,134 @@ var (
ErrPlatformNotSupported = errors.New("unsupported platform request")
)

// ProcessError is an error encountered in HCS during an operation on a Process object
type ProcessError struct {
SystemID string
PID int
Operation string
ExtraInfo string
Err error
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)
}

// SystemError is an error encountered in HCS during an operation on a Container object
type SystemError struct {
ID string
Operation string
ExtraInfo string
Err error
type hcsResult struct {
Error int32
ErrorMessage string
ErrorEvents []ErrorEvent `json:"ErrorEvents,omitempty"`
}

func (e *SystemError) Error() string {
if e == nil {
return "<nil>"
func (ev *ErrorEvent) String() string {
evs := "[Event Detail: " + ev.Message
if ev.StackTrace != "" {
evs += " Stack Trace: " + ev.StackTrace
}

s := "system " + e.ID

if e.Operation != "" {
s += " encountered an error during " + e.Operation
if ev.Provider != "" {
evs += " Provider: " + ev.Provider
}

switch e.Err.(type) {
case nil:
break
case syscall.Errno:
s += fmt.Sprintf(": failure in a Windows system call: %s (0x%x)", e.Err, hcserror.Win32FromError(e.Err))
default:
s += fmt.Sprintf(": %s", e.Err.Error())
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
}

if e.ExtraInfo != "" {
s += " extra info: " + e.ExtraInfo
func processHcsResult(resultp *uint16) []ErrorEvent {
if resultp != nil {
resultj := interop.ConvertAndFreeCoTaskMemString(resultp)
logrus.Debugf("Result: %s", resultj)
result := &hcsResult{}
if err := json.Unmarshal([]byte(resultj), result); err != nil {
logrus.Warnf("Could not unmarshal HCS result %s: %s", resultj, err)
return nil
}
return result.ErrorEvents
}
return nil
}

return s
type HcsError struct {
Op string
Err error
Events []ErrorEvent
}

func makeSystemError(system *System, operation string, extraInfo string, err error) error {
// Don't double wrap errors
if _, ok := err.(*SystemError); ok {
return err
func (e *HcsError) Error() string {
s := e.Op + ": " + e.Err.Error()
for _, ev := range e.Events {
s += "\n" + ev.String()
}
serr := &SystemError{ID: system.ID, Operation: operation, ExtraInfo: extraInfo, Err: err}
return serr
return s
}

func (e *ProcessError) Error() string {
if e == nil {
return "<nil>"
}
// 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
}

s := fmt.Sprintf("process %d", e.PID)
// 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
}

if e.SystemID != "" {
s += " in container " + e.SystemID
func (e *SystemError) Error() string {
s := e.Op + " " + e.ID + ": " + e.Err.Error()
for _, ev := range e.Events {
s += "\n" + ev.String()
}

if e.Operation != "" {
s += " encountered an error during " + e.Operation
if e.Extra != "" {
s += "\n(extra info: " + e.Extra + ")"
}
return s
}

switch e.Err.(type) {
case nil:
break
case syscall.Errno:
s += fmt.Sprintf(": failure in a Windows system call: %s (0x%x)", e.Err, hcserror.Win32FromError(e.Err))
default:
s += fmt.Sprintf(": %s", e.Err.Error())
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 makeProcessError(process *Process, operation string, extraInfo string, err error) error {
func makeProcessError(process *Process, op string, err error, events []ErrorEvent) error {
// Don't double wrap errors
if _, ok := err.(*ProcessError); ok {
return err
}
processError := &ProcessError{PID: process.processID, SystemID: process.system.ID, Operation: operation, ExtraInfo: extraInfo, Err: err}
return processError
return &ProcessError{
PID: process.processID,
SystemID: process.system.ID,
Op: op,
Err: err,
Events: events,
}
}

// IsNotExist checks if an error is caused by the Container or Process not existing.
Expand Down Expand Up @@ -225,6 +268,8 @@ 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:
Expand Down
14 changes: 0 additions & 14 deletions internal/hcs/hcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ package hcs

import (
"syscall"

"github.com/Microsoft/hcsshim/internal/hcserror"
"github.com/Microsoft/hcsshim/internal/interop"
"github.com/sirupsen/logrus"
)

//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go hcs.go
Expand Down Expand Up @@ -38,8 +34,6 @@ import (
//sys hcsRegisterProcessCallback(process hcsProcess, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) = vmcompute.HcsRegisterProcessCallback?
//sys hcsUnregisterProcessCallback(callbackHandle hcsCallback) (hr error) = vmcompute.HcsUnregisterProcessCallback?

type HcsError = hcserror.HcsError

type hcsSystem syscall.Handle
type hcsProcess syscall.Handle
type hcsCallback syscall.Handle
Expand All @@ -51,11 +45,3 @@ type hcsProcessInformation struct {
StdOutput syscall.Handle
StdError syscall.Handle
}

func processHcsResult(err error, resultp *uint16) error {
if resultp != nil {
result := interop.ConvertAndFreeCoTaskMemString(resultp)
logrus.Debugf("Result: %s", result)
}
return err
}
Loading

0 comments on commit e70197a

Please sign in to comment.