cri-o/vendor/github.com/opencontainers/runtime-tools/specerror/error.go

130 lines
3.4 KiB
Go

// Package specerror implements runtime-spec-specific tooling for
// tracking RFC 2119 violations.
package specerror
import (
"fmt"
"github.com/hashicorp/go-multierror"
rfc2119 "github.com/opencontainers/runtime-tools/error"
)
const referenceTemplate = "https://github.com/opencontainers/runtime-spec/blob/v%s/%s"
// Code represents the spec violation, enumerating both
// configuration violations and runtime violations.
type Code int64
const (
// NonError represents that an input is not an error
NonError Code = 0x1a001 + iota
// NonRFCError represents that an error is not a rfc2119 error
NonRFCError
)
type errorTemplate struct {
Level rfc2119.Level
Reference func(version string) (reference string, err error)
}
// Error represents a runtime-spec violation.
type Error struct {
// Err holds the RFC 2119 violation.
Err rfc2119.Error
// Code is a matchable holds a Code
Code Code
}
// LevelErrors represents Errors filtered into fatal and warnings.
type LevelErrors struct {
// Warnings holds Errors that were below a compliance-level threshold.
Warnings []*Error
// Error holds errors that were at or above a compliance-level
// threshold, as well as errors that are not Errors.
Error *multierror.Error
}
var ociErrors = map[Code]errorTemplate{}
func register(code Code, level rfc2119.Level, ref func(versiong string) (string, error)) {
if _, ok := ociErrors[code]; ok {
panic(fmt.Sprintf("should not regist a same code twice: %v", code))
}
ociErrors[code] = errorTemplate{Level: level, Reference: ref}
}
// Error returns the error message with specification reference.
func (err *Error) Error() string {
return err.Err.Error()
}
// NewError creates an Error referencing a spec violation. The error
// can be cast to an *Error for extracting structured information
// about the level of the violation and a reference to the violated
// spec condition.
//
// A version string (for the version of the spec that was violated)
// must be set to get a working URL.
func NewError(code Code, err error, version string) error {
template := ociErrors[code]
reference, err2 := template.Reference(version)
if err2 != nil {
return err2
}
return &Error{
Err: rfc2119.Error{
Level: template.Level,
Reference: reference,
Err: err,
},
Code: code,
}
}
// FindError finds an error from a source error (multiple error) and
// returns the error code if found.
// If the source error is nil or empty, return NonError.
// If the source error is not a multiple error, return NonRFCError.
func FindError(err error, code Code) Code {
if err == nil {
return NonError
}
if merr, ok := err.(*multierror.Error); ok {
if merr.ErrorOrNil() == nil {
return NonError
}
for _, e := range merr.Errors {
if rfcErr, ok := e.(*Error); ok {
if rfcErr.Code == code {
return code
}
}
}
}
return NonRFCError
}
// SplitLevel removes RFC 2119 errors with a level less than 'level'
// from the source error. If the source error is not a multierror, it
// is returned unchanged.
func SplitLevel(errIn error, level rfc2119.Level) (levelErrors LevelErrors, errOut error) {
merr, ok := errIn.(*multierror.Error)
if !ok {
return levelErrors, errIn
}
for _, err := range merr.Errors {
e, ok := err.(*Error)
if ok && e.Err.Level < level {
fmt.Println(e)
levelErrors.Warnings = append(levelErrors.Warnings, e)
continue
}
levelErrors.Error = multierror.Append(levelErrors.Error, err)
}
return levelErrors, nil
}