* CLI properly formats the IP addresses in runner show * LXD provider now waits for an IP address before returning on Create * Added a few mocks for testing Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
227 lines
6.5 KiB
Go
227 lines
6.5 KiB
Go
// Copyright 2014 Canonical Ltd.
|
|
// Licensed under the LGPLv3, see LICENCE file for details.
|
|
|
|
package errors
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
)
|
|
|
|
// Err holds a description of an error along with information about
|
|
// where the error was created.
|
|
//
|
|
// It may be embedded in custom error types to add extra information that
|
|
// this errors package can understand.
|
|
type Err struct {
|
|
// message holds an annotation of the error.
|
|
message string
|
|
|
|
// cause holds the cause of the error as returned
|
|
// by the Cause method.
|
|
cause error
|
|
|
|
// previous holds the previous error in the error stack, if any.
|
|
previous error
|
|
|
|
// function is the package path-qualified function name where the
|
|
// error was created.
|
|
function string
|
|
|
|
// line is the line number the error was created on inside of function
|
|
line int
|
|
}
|
|
|
|
// Locationer is an interface that represents a certain class of errors that
|
|
// contain the location information from where they were raised.
|
|
type Locationer interface {
|
|
// Location returns the path-qualified function name where the error was
|
|
// created and the line number
|
|
Location() (function string, line int)
|
|
}
|
|
|
|
// locationError is the internal implementation of the Locationer interface.
|
|
type locationError struct {
|
|
error
|
|
|
|
// function is the package path-qualified function name where the
|
|
// error was created.
|
|
function string
|
|
|
|
// line is the line number the error was created on inside of function
|
|
line int
|
|
}
|
|
|
|
// newLocationError constructs a new Locationer error from the supplied error
|
|
// with the location set to callDepth in the stack. If a nill error is provided
|
|
// to this function then a new empty error is constructed.
|
|
func newLocationError(err error, callDepth int) *locationError {
|
|
le := &locationError{error: err}
|
|
le.function, le.line = getLocation(callDepth + 1)
|
|
return le
|
|
}
|
|
|
|
// Error implementes the error interface.
|
|
func (l *locationError) Error() string {
|
|
if l.error == nil {
|
|
return ""
|
|
}
|
|
return l.error.Error()
|
|
}
|
|
|
|
// *locationError implements Locationer.Location interface
|
|
func (l *locationError) Location() (string, int) {
|
|
return l.function, l.line
|
|
}
|
|
|
|
func (l *locationError) Unwrap() error {
|
|
return l.error
|
|
}
|
|
|
|
// NewErr is used to return an Err for the purpose of embedding in other
|
|
// structures. The location is not specified, and needs to be set with a call
|
|
// to SetLocation.
|
|
//
|
|
// For example:
|
|
// type FooError struct {
|
|
// errors.Err
|
|
// code int
|
|
// }
|
|
//
|
|
// func NewFooError(code int) error {
|
|
// err := &FooError{errors.NewErr("foo"), code}
|
|
// err.SetLocation(1)
|
|
// return err
|
|
// }
|
|
func NewErr(format string, args ...interface{}) Err {
|
|
return Err{
|
|
message: fmt.Sprintf(format, args...),
|
|
}
|
|
}
|
|
|
|
// NewErrWithCause is used to return an Err with cause by other error for the purpose of embedding in other
|
|
// structures. The location is not specified, and needs to be set with a call
|
|
// to SetLocation.
|
|
//
|
|
// For example:
|
|
// type FooError struct {
|
|
// errors.Err
|
|
// code int
|
|
// }
|
|
//
|
|
// func (e *FooError) Annotate(format string, args ...interface{}) error {
|
|
// err := &FooError{errors.NewErrWithCause(e.Err, format, args...), e.code}
|
|
// err.SetLocation(1)
|
|
// return err
|
|
// })
|
|
func NewErrWithCause(other error, format string, args ...interface{}) Err {
|
|
return Err{
|
|
message: fmt.Sprintf(format, args...),
|
|
cause: Cause(other),
|
|
previous: other,
|
|
}
|
|
}
|
|
|
|
// Location returns the package path-qualified function name and line of where
|
|
// the error was most recently created or annotated.
|
|
func (e *Err) Location() (function string, line int) {
|
|
return e.function, e.line
|
|
}
|
|
|
|
// Underlying returns the previous error in the error stack, if any. A client
|
|
// should not ever really call this method. It is used to build the error
|
|
// stack and should not be introspected by client calls. Or more
|
|
// specifically, clients should not depend on anything but the `Cause` of an
|
|
// error.
|
|
func (e *Err) Underlying() error {
|
|
return e.previous
|
|
}
|
|
|
|
// Cause returns the most recent error in the error stack that
|
|
// meets one of these criteria: the original error that was raised; the new
|
|
// error that was passed into the Wrap function; the most recently masked
|
|
// error; or nil if the error itself is considered the Cause. Normally this
|
|
// method is not invoked directly, but instead through the Cause stand alone
|
|
// function.
|
|
func (e *Err) Cause() error {
|
|
return e.cause
|
|
}
|
|
|
|
// Message returns the message stored with the most recent location. This is
|
|
// the empty string if the most recent call was Trace, or the message stored
|
|
// with Annotate or Mask.
|
|
func (e *Err) Message() string {
|
|
return e.message
|
|
}
|
|
|
|
// Error implements error.Error.
|
|
func (e *Err) Error() string {
|
|
// We want to walk up the stack of errors showing the annotations
|
|
// as long as the cause is the same.
|
|
err := e.previous
|
|
if !sameError(Cause(err), e.cause) && e.cause != nil {
|
|
err = e.cause
|
|
}
|
|
switch {
|
|
case err == nil:
|
|
return e.message
|
|
case e.message == "":
|
|
return err.Error()
|
|
}
|
|
return fmt.Sprintf("%s: %v", e.message, err)
|
|
}
|
|
|
|
// Format implements fmt.Formatter
|
|
// When printing errors with %+v it also prints the stack trace.
|
|
// %#v unsurprisingly will print the real underlying type.
|
|
func (e *Err) Format(s fmt.State, verb rune) {
|
|
switch verb {
|
|
case 'v':
|
|
switch {
|
|
case s.Flag('+'):
|
|
fmt.Fprintf(s, "%s", ErrorStack(e))
|
|
return
|
|
case s.Flag('#'):
|
|
// avoid infinite recursion by wrapping e into a type
|
|
// that doesn't implement Formatter.
|
|
fmt.Fprintf(s, "%#v", (*unformatter)(e))
|
|
return
|
|
}
|
|
fallthrough
|
|
case 's':
|
|
fmt.Fprintf(s, "%s", e.Error())
|
|
case 'q':
|
|
fmt.Fprintf(s, "%q", e.Error())
|
|
default:
|
|
fmt.Fprintf(s, "%%!%c(%T=%s)", verb, e, e.Error())
|
|
}
|
|
}
|
|
|
|
// helper for Format
|
|
type unformatter Err
|
|
|
|
func (unformatter) Format() { /* break the fmt.Formatter interface */ }
|
|
|
|
// SetLocation records the package path-qualified function name of the error at
|
|
// callDepth stack frames above the call.
|
|
func (e *Err) SetLocation(callDepth int) {
|
|
e.function, e.line = getLocation(callDepth + 1)
|
|
}
|
|
|
|
// StackTrace returns one string for each location recorded in the stack of
|
|
// errors. The first value is the originating error, with a line for each
|
|
// other annotation or tracing of the error.
|
|
func (e *Err) StackTrace() []string {
|
|
return errorStack(e)
|
|
}
|
|
|
|
// Ideally we'd have a way to check identity, but deep equals will do.
|
|
func sameError(e1, e2 error) bool {
|
|
return reflect.DeepEqual(e1, e2)
|
|
}
|
|
|
|
// Unwrap is a synonym for Underlying, which allows Err to be used with the
|
|
// Unwrap, Is and As functions in Go's standard `errors` library.
|
|
func (e *Err) Unwrap() error {
|
|
return e.previous
|
|
}
|