Skip to content

Commit c6de221

Browse files
author
Ma Shimiao
authored
Merge pull request #437 from wking/separate-error-package
error: Pull the RFC 2119 error representation into its own package
2 parents 8f34ca0 + 8f4d367 commit c6de221

File tree

3 files changed

+128
-98
lines changed

3 files changed

+128
-98
lines changed

cmd/runtimetest/main.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ import (
2222
"github.com/urfave/cli"
2323

2424
"github.com/opencontainers/runtime-tools/cmd/runtimetest/mount"
25-
ociErr "github.com/opencontainers/runtime-tools/validate"
25+
rfc2119 "github.com/opencontainers/runtime-tools/error"
26+
"github.com/opencontainers/runtime-tools/validate"
2627
)
2728

2829
// PrGetNoNewPrivs isn't exposed in Golang so we define it ourselves copying the value from
@@ -325,7 +326,7 @@ func validateDefaultFS(spec *rspec.Spec) error {
325326

326327
mountInfos, err := mount.GetMounts()
327328
if err != nil {
328-
return ociErr.NewError(ociErr.DefaultFilesystems, err.Error())
329+
validate.NewError(validate.DefaultFilesystems, err.Error(), spec.Version)
329330
}
330331

331332
mountsMap := make(map[string]string)
@@ -335,7 +336,7 @@ func validateDefaultFS(spec *rspec.Spec) error {
335336

336337
for fs, fstype := range defaultFS {
337338
if !(mountsMap[fs] == fstype) {
338-
return ociErr.NewError(ociErr.DefaultFilesystems, fmt.Sprintf("%v SHOULD exist and expected type is %v", fs, fstype))
339+
return validate.NewError(validate.DefaultFilesystems, fmt.Sprintf("%v SHOULD exist and expected type is %v", fs, fstype), spec.Version)
339340
}
340341
}
341342

@@ -629,7 +630,7 @@ func validateMountsExist(spec *rspec.Spec) error {
629630
return nil
630631
}
631632

632-
func validate(context *cli.Context) error {
633+
func run(context *cli.Context) error {
633634
logLevelString := context.String("log-level")
634635
logLevel, err := logrus.ParseLevel(logLevelString)
635636
if err != nil {
@@ -719,17 +720,17 @@ func validate(context *cli.Context) error {
719720
t.Header(0)
720721

721722
complianceLevelString := context.String("compliance-level")
722-
complianceLevel, err := ociErr.ParseLevel(complianceLevelString)
723+
complianceLevel, err := rfc2119.ParseLevel(complianceLevelString)
723724
if err != nil {
724-
complianceLevel = ociErr.ComplianceMust
725+
complianceLevel = rfc2119.Must
725726
logrus.Warningf("%s, using 'MUST' by default.", err.Error())
726727
}
727728
var validationErrors error
728729
for _, v := range defaultValidations {
729730
err := v.test(spec)
730731
t.Ok(err == nil, v.description)
731732
if err != nil {
732-
if e, ok := err.(*ociErr.Error); ok && e.Level < complianceLevel {
733+
if e, ok := err.(*rfc2119.Error); ok && e.Level < complianceLevel {
733734
continue
734735
}
735736
validationErrors = multierror.Append(validationErrors, err)
@@ -741,7 +742,7 @@ func validate(context *cli.Context) error {
741742
err := v.test(spec)
742743
t.Ok(err == nil, v.description)
743744
if err != nil {
744-
if e, ok := err.(*ociErr.Error); ok && e.Level < complianceLevel {
745+
if e, ok := err.(*rfc2119.Error); ok && e.Level < complianceLevel {
745746
continue
746747
}
747748
validationErrors = multierror.Append(validationErrors, err)
@@ -777,7 +778,7 @@ func main() {
777778
},
778779
}
779780

780-
app.Action = validate
781+
app.Action = run
781782
if err := app.Run(os.Args); err != nil {
782783
logrus.Fatal(err)
783784
}

error/error.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Package error implements generic tooling for tracking RFC 2119
2+
// violations and linking back to the appropriate specification section.
3+
package error
4+
5+
import (
6+
"fmt"
7+
"strings"
8+
)
9+
10+
// Level represents the OCI compliance levels
11+
type Level int
12+
13+
const (
14+
// MAY-level
15+
16+
// May represents 'MAY' in RFC 2119.
17+
May Level = iota
18+
// Optional represents 'OPTIONAL' in RFC 2119.
19+
Optional
20+
21+
// SHOULD-level
22+
23+
// Should represents 'SHOULD' in RFC 2119.
24+
Should
25+
// ShouldNot represents 'SHOULD NOT' in RFC 2119.
26+
ShouldNot
27+
// Recommended represents 'RECOMMENDED' in RFC 2119.
28+
Recommended
29+
// NotRecommended represents 'NOT RECOMMENDED' in RFC 2119.
30+
NotRecommended
31+
32+
// MUST-level
33+
34+
// Must represents 'MUST' in RFC 2119
35+
Must
36+
// MustNot represents 'MUST NOT' in RFC 2119.
37+
MustNot
38+
// Shall represents 'SHALL' in RFC 2119.
39+
Shall
40+
// ShallNot represents 'SHALL NOT' in RFC 2119.
41+
ShallNot
42+
// Required represents 'REQUIRED' in RFC 2119.
43+
Required
44+
)
45+
46+
// Error represents an error with compliance level and OCI reference.
47+
type Error struct {
48+
Level Level
49+
Reference string
50+
Err error
51+
}
52+
53+
// ParseLevel takes a string level and returns the OCI compliance level constant.
54+
func ParseLevel(level string) (Level, error) {
55+
switch strings.ToUpper(level) {
56+
case "MAY":
57+
fallthrough
58+
case "OPTIONAL":
59+
return May, nil
60+
case "SHOULD":
61+
fallthrough
62+
case "SHOULDNOT":
63+
fallthrough
64+
case "RECOMMENDED":
65+
fallthrough
66+
case "NOTRECOMMENDED":
67+
return Should, nil
68+
case "MUST":
69+
fallthrough
70+
case "MUSTNOT":
71+
fallthrough
72+
case "SHALL":
73+
fallthrough
74+
case "SHALLNOT":
75+
fallthrough
76+
case "REQUIRED":
77+
return Must, nil
78+
}
79+
80+
var l Level
81+
return l, fmt.Errorf("%q is not a valid compliance level", level)
82+
}
83+
84+
// Error returns the error message with OCI reference
85+
func (err *Error) Error() string {
86+
return fmt.Sprintf("%s\nRefer to: %s", err.Err.Error(), err.Reference)
87+
}

validate/error.go

Lines changed: 31 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -3,108 +3,50 @@ package validate
33
import (
44
"errors"
55
"fmt"
6-
"strings"
76

8-
rspec "github.com/opencontainers/runtime-spec/specs-go"
7+
rfc2119 "github.com/opencontainers/runtime-tools/error"
98
)
109

11-
// ComplianceLevel represents the OCI compliance levels
12-
type ComplianceLevel int
10+
const referenceTemplate = "https://github.com/opencontainers/runtime-spec/blob/v%s/%s"
1311

14-
const (
15-
// MAY-level
16-
17-
// ComplianceMay represents 'MAY' in RFC2119
18-
ComplianceMay ComplianceLevel = iota
19-
// ComplianceOptional represents 'OPTIONAL' in RFC2119
20-
ComplianceOptional
21-
22-
// SHOULD-level
23-
24-
// ComplianceShould represents 'SHOULD' in RFC2119
25-
ComplianceShould
26-
// ComplianceShouldNot represents 'SHOULD NOT' in RFC2119
27-
ComplianceShouldNot
28-
// ComplianceRecommended represents 'RECOMMENDED' in RFC2119
29-
ComplianceRecommended
30-
// ComplianceNotRecommended represents 'NOT RECOMMENDED' in RFC2119
31-
ComplianceNotRecommended
32-
33-
// MUST-level
34-
35-
// ComplianceMust represents 'MUST' in RFC2119
36-
ComplianceMust
37-
// ComplianceMustNot represents 'MUST NOT' in RFC2119
38-
ComplianceMustNot
39-
// ComplianceShall represents 'SHALL' in RFC2119
40-
ComplianceShall
41-
// ComplianceShallNot represents 'SHALL NOT' in RFC2119
42-
ComplianceShallNot
43-
// ComplianceRequired represents 'REQUIRED' in RFC2119
44-
ComplianceRequired
45-
)
46-
47-
// ErrorCode represents the compliance content
12+
// ErrorCode represents the compliance content.
4813
type ErrorCode int
4914

5015
const (
51-
// DefaultFilesystems represents the error code of default filesystems test
16+
// DefaultFilesystems represents the error code of default filesystems test.
5217
DefaultFilesystems ErrorCode = iota
5318
)
5419

55-
// Error represents an error with compliance level and OCI reference
56-
type Error struct {
57-
Level ComplianceLevel
58-
Reference string
59-
Err error
20+
type errorTemplate struct {
21+
Level rfc2119.Level
22+
Reference func(version string) (reference string, err error)
6023
}
6124

62-
const referencePrefix = "https://github.com/opencontainers/runtime-spec/blob"
63-
64-
var ociErrors = map[ErrorCode]Error{
65-
DefaultFilesystems: Error{Level: ComplianceShould, Reference: "config-linux.md#default-filesystems"},
25+
var ociErrors = map[ErrorCode]errorTemplate{
26+
DefaultFilesystems: errorTemplate{
27+
Level: rfc2119.Should,
28+
Reference: func(version string) (reference string, err error) {
29+
return fmt.Sprintf(referenceTemplate, version, "config-linux.md#default-filesystems"), nil
30+
},
31+
},
6632
}
6733

68-
// ParseLevel takes a string level and returns the OCI compliance level constant
69-
func ParseLevel(level string) (ComplianceLevel, error) {
70-
switch strings.ToUpper(level) {
71-
case "MAY":
72-
fallthrough
73-
case "OPTIONAL":
74-
return ComplianceMay, nil
75-
case "SHOULD":
76-
fallthrough
77-
case "SHOULDNOT":
78-
fallthrough
79-
case "RECOMMENDED":
80-
fallthrough
81-
case "NOTRECOMMENDED":
82-
return ComplianceShould, nil
83-
case "MUST":
84-
fallthrough
85-
case "MUSTNOT":
86-
fallthrough
87-
case "SHALL":
88-
fallthrough
89-
case "SHALLNOT":
90-
fallthrough
91-
case "REQUIRED":
92-
return ComplianceMust, nil
34+
// NewError creates an Error referencing a spec violation. The error
35+
// can be cast to a *runtime-tools.error.Error for extracting
36+
// structured information about the level of the violation and a
37+
// reference to the violated spec condition.
38+
//
39+
// A version string (for the version of the spec that was violated)
40+
// must be set to get a working URL.
41+
func NewError(code ErrorCode, msg string, version string) (err error) {
42+
template := ociErrors[code]
43+
reference, err := template.Reference(version)
44+
if err != nil {
45+
return err
46+
}
47+
return &rfc2119.Error{
48+
Level: template.Level,
49+
Reference: reference,
50+
Err: errors.New(msg),
9351
}
94-
95-
var l ComplianceLevel
96-
return l, fmt.Errorf("%q is not a valid compliance level", level)
97-
}
98-
99-
// NewError creates an Error by ErrorCode and message
100-
func NewError(code ErrorCode, msg string) error {
101-
err := ociErrors[code]
102-
err.Err = errors.New(msg)
103-
104-
return &err
105-
}
106-
107-
// Error returns the error message with OCI reference
108-
func (oci *Error) Error() string {
109-
return fmt.Sprintf("%s\nRefer to: %s/v%s/%s", oci.Err.Error(), referencePrefix, rspec.Version, oci.Reference)
11052
}

0 commit comments

Comments
 (0)