mirror of https://github.com/k3s-io/k3s
153 lines
4.2 KiB
Go
153 lines
4.2 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()
|
||
|
}
|
||
|
|
||
|
// NewRFCError creates an rfc2119.Error referencing a spec violation.
|
||
|
//
|
||
|
// A version string (for the version of the spec that was violated)
|
||
|
// must be set to get a working URL.
|
||
|
func NewRFCError(code Code, err error, version string) (*rfc2119.Error, error) {
|
||
|
template := ociErrors[code]
|
||
|
reference, err2 := template.Reference(version)
|
||
|
if err2 != nil {
|
||
|
return nil, err2
|
||
|
}
|
||
|
return &rfc2119.Error{
|
||
|
Level: template.Level,
|
||
|
Reference: reference,
|
||
|
Err: err,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// NewRFCErrorOrPanic creates an rfc2119.Error referencing a spec
|
||
|
// violation and panics on failure. This is handy for situations
|
||
|
// where you can't be bothered to check NewRFCError for failure.
|
||
|
func NewRFCErrorOrPanic(code Code, err error, version string) *rfc2119.Error {
|
||
|
rfcError, err2 := NewRFCError(code, err, version)
|
||
|
if err2 != nil {
|
||
|
panic(err2.Error())
|
||
|
}
|
||
|
return rfcError
|
||
|
}
|
||
|
|
||
|
// 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 {
|
||
|
rfcError, err2 := NewRFCError(code, err, version)
|
||
|
if err2 != nil {
|
||
|
return err2
|
||
|
}
|
||
|
return &Error{
|
||
|
Err: *rfcError,
|
||
|
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
|
||
|
}
|