aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/opencontainers/runtime-tools/specerror/error.go
blob: 1cfe054c285255aaf94e1845c85f35e359776a76 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// 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 string

const (
	// NonError represents that an input is not an error
	NonError = "the input is not an error"
	// NonRFCError represents that an error is not a rfc2119 error
	NonRFCError = "the error is not a rfc2119 error"
)

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: %s", 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
}