Files
athens/pkg/errors/errors.go
Aaron Schlesinger ab9a4c0f41 Moving the fetch logic into 'pkg/module' (#291)
* add module fetch and fetchers

* Breaking module ref out of the fetcher

* disk ref in progress

* very broken go get fetcher

* refactoring ref and fixing up the disk ref

* adding a test for the disk ref

* doing test setup right

* passing version

* fixing syntax err

* removing raw format string

in favor of a function that does the same thing

* removing obsolete godoc

* removing the switch in favor of nested if statements

hopefully this is easier to read

* passing data into the format strings

* refactoring the closer

* creating and looking for files in the root

* genericFetcher => goGetFetcher

* remove the clear method on go get fetcher

the ref returned from fetch does it

* remove unnecessary call to isVgoInstalled

calls to fetch will fail if vgo is not installed

* remove unneeded dirName

* adding preliminary test for the go get fetcher

* adding a test for fetch

* executing fetch test

* adding docs and simplifying the fetcher

* declaring the storage version up front

* fixing the source path

* creating test files in a separate function

* closing files immediately

instead of aggregating them for later. whaaat was I thinking???

* passing context into storage driver

* not closing the source file

* not putting the module name in quotes anymore

* adding a warning about deleting the package

* adding go binary name

* embedding github API limit error

* check files in the success path

* passing errors up

not just messages

* doh

* using vgo for the go get fetcher tests

* not using multierror because it's unneeded

* less code 😄

* custom error

* removing unused import

* return noop refs

* adding link to issue for the TODO

* simplifying

* simplifying moar!

* using config.FmtModVer

* simplifying

* defer closing the zip

* closing the zip

* adding comment about closing the ver.Zip

* GO_BINARY_NAME => GO_BINARY_PATH

* using errors pkg

* fixing compile err

* removing redundant args

* removing requirement that caller calls Clear on fetch error

* fixing build

* fix

* adding masterminds semver

* returning better error

and checking version format

* removing unused error

* using proper semver function

* fixin compile err

* rm that shizz
2018-07-26 13:08:07 -07:00

161 lines
3.6 KiB
Go

package errors
import (
"errors"
"fmt"
"net/http"
"runtime"
"github.com/sirupsen/logrus"
)
// Kind enums
const (
KindNotFound = http.StatusNotFound
KindBadRequest = http.StatusBadRequest
KindUnexpected = http.StatusInternalServerError
KindAlreadyExists = http.StatusConflict
KindRateLimit = http.StatusTooManyRequests
)
// Error is an Athens system error.
// It carries information and behavior
// as to what caused this error so that
// callers can implement logic around it.
type Error struct {
// Kind categories Athens errors into a smaller
// subset of errors. This way we can generalize
// what an error really is: such as "not found",
// "bad request", etc. The official categories
// are HTTP status code but the ones we use are
// imported into this package.
Kind int
Op Op
Module M
Version V
Err error
Severity logrus.Level
}
// Error returns the underlying error's
// string message. The logger takes care
// of filling out the stack levels and
// extra information.
func (e Error) Error() string {
return e.Err.Error()
}
// Op describes any independent function or
// method in Athens. A series of operations
// forms a more readable stack trace.
type Op string
// M represents a module in an error
// this is so that we can distinguish
// a module from a regular error string or version.
type M string
// V represents a module version in an error
type V string
// E is a helper function to construct an Error type
// Operation always comes first, module path and version
// come second, they are optional. Args must have at least
// an error or a string to describe what exactly went wrong.
// You can optionally pass a Logrus severity to indicate
// the log level of an error based on the context it was constructed in.
func E(op Op, args ...interface{}) error {
e := Error{Op: op}
if len(args) == 0 {
msg := "errors.E called with 0 args"
_, file, line, ok := runtime.Caller(1)
if ok {
msg = fmt.Sprintf("%v - %v:%v", msg, file, line)
}
e.Err = errors.New(msg)
}
for _, a := range args {
switch a := a.(type) {
case error:
e.Err = a
case string:
e.Err = errors.New(a)
case M:
e.Module = a
case V:
e.Version = a
case logrus.Level:
e.Severity = a
case int:
e.Kind = a
}
}
if e.Err == nil {
e.Err = errors.New("no error message provided")
}
return e
}
// Severity returns the log level of an error
// if none exists, then the level is Error because
// it is an unexpected.
func Severity(err error) logrus.Level {
e, ok := err.(Error)
if !ok {
return logrus.ErrorLevel
}
// if there's no severity (0 is Panic level in logrus
// which we should not use since cloud providers only have
// debug, info, warn, and error) then look for the
// child's severity.
if e.Severity < logrus.ErrorLevel {
return Severity(e.Err)
}
return e.Severity
}
// Kind recursively searches for the
// first error kind it finds.
func Kind(err error) int {
e, ok := err.(Error)
if !ok {
return KindUnexpected
}
if e.Kind != 0 {
return e.Kind
}
return Kind(e.Err)
}
// KindText returns a friendly string
// of the Kind type. Since we use http
// status codes to represent error kinds,
// this method just deferrs to the net/http
// text representations of statuses.
func KindText(err error) string {
return http.StatusText(Kind(err))
}
// Ops aggregates the error's operation
// with all the embedded errors' operations.
// This way you can construct a queryable
// stack trace.
func Ops(err Error) []Op {
ops := []Op{err.Op}
for {
embeddedErr, ok := err.Err.(Error)
if !ok {
break
}
ops = append(ops, embeddedErr.Op)
err = embeddedErr
}
return ops
}