Skip to content

Commit 3a21ae6

Browse files
author
Ma Shimiao
authored
Merge pull request #354 from liangchenye/master
add OCIError to differentiate 'MUST/SHOULD' and add reference support
2 parents 0f24b50 + 6316a4e commit 3a21ae6

File tree

4 files changed

+182
-1
lines changed

4 files changed

+182
-1
lines changed

cmd/runtimetest/main.go

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ import (
1818
"github.com/hashicorp/go-multierror"
1919
"github.com/mndrix/tap-go"
2020
rspec "github.com/opencontainers/runtime-spec/specs-go"
21-
"github.com/opencontainers/runtime-tools/cmd/runtimetest/mount"
2221
"github.com/syndtr/gocapability/capability"
2322
"github.com/urfave/cli"
23+
24+
"github.com/opencontainers/runtime-tools/cmd/runtimetest/mount"
25+
ociErr "github.com/opencontainers/runtime-tools/validate"
2426
)
2527

2628
// PrGetNoNewPrivs isn't exposed in Golang so we define it ourselves copying the value from
@@ -30,6 +32,13 @@ const PrGetNoNewPrivs = 39
3032
const specConfig = "config.json"
3133

3234
var (
35+
defaultFS = map[string]string{
36+
"/proc": "proc",
37+
"/sys": "sysfs",
38+
"/dev/pts": "devpts",
39+
"/dev/shm": "tmpfs",
40+
}
41+
3342
defaultSymlinks = map[string]string{
3443
"/dev/fd": "/proc/self/fd",
3544
"/dev/stdin": "/proc/self/fd/0",
@@ -308,6 +317,28 @@ func validateRootFS(spec *rspec.Spec) error {
308317
return nil
309318
}
310319

320+
func validateDefaultFS(spec *rspec.Spec) error {
321+
logrus.Debugf("validating linux default filesystem")
322+
323+
mountInfos, err := mount.GetMounts()
324+
if err != nil {
325+
return ociErr.NewError(ociErr.DefaultFilesystems, err.Error())
326+
}
327+
328+
mountsMap := make(map[string]string)
329+
for _, mountInfo := range mountInfos {
330+
mountsMap[mountInfo.Mountpoint] = mountInfo.Fstype
331+
}
332+
333+
for fs, fstype := range defaultFS {
334+
if !(mountsMap[fs] == fstype) {
335+
return ociErr.NewError(ociErr.DefaultFilesystems, fmt.Sprintf("%v SHOULD exist and expected type is %v", fs, fstype))
336+
}
337+
}
338+
339+
return nil
340+
}
341+
311342
func validateLinuxDevices(spec *rspec.Spec) error {
312343
for _, device := range spec.Linux.Devices {
313344
fi, err := os.Stat(device.Path)
@@ -620,6 +651,10 @@ func validate(context *cli.Context) error {
620651
test: validateDefaultSymlinks,
621652
description: "default symlinks",
622653
},
654+
{
655+
test: validateDefaultFS,
656+
description: "default file system",
657+
},
623658
{
624659
test: validateDefaultDevices,
625660
description: "default devices",
@@ -665,11 +700,20 @@ func validate(context *cli.Context) error {
665700
t := tap.New()
666701
t.Header(0)
667702

703+
complianceLevelString := context.String("compliance-level")
704+
complianceLevel, err := ociErr.ParseLevel(complianceLevelString)
705+
if err != nil {
706+
complianceLevel = ociErr.ComplianceMust
707+
logrus.Warningf("%s, using 'MUST' by default.", err.Error())
708+
}
668709
var validationErrors error
669710
for _, v := range defaultValidations {
670711
err := v.test(spec)
671712
t.Ok(err == nil, v.description)
672713
if err != nil {
714+
if e, ok := err.(*ociErr.Error); ok && e.Level < complianceLevel {
715+
continue
716+
}
673717
validationErrors = multierror.Append(validationErrors, err)
674718
}
675719
}
@@ -679,6 +723,9 @@ func validate(context *cli.Context) error {
679723
err := v.test(spec)
680724
t.Ok(err == nil, v.description)
681725
if err != nil {
726+
if e, ok := err.(*ociErr.Error); ok && e.Level < complianceLevel {
727+
continue
728+
}
682729
validationErrors = multierror.Append(validationErrors, err)
683730
}
684731
}
@@ -705,6 +752,11 @@ func main() {
705752
Value: ".",
706753
Usage: "Path to the configuration",
707754
},
755+
cli.StringFlag{
756+
Name: "compliance-level",
757+
Value: "must",
758+
Usage: "Compliance level (may, should or must)",
759+
},
708760
}
709761

710762
app.Action = validate

completions/bash/oci-runtime-tool

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,14 @@ __oci-runtime-tool_complete_log_level() {
119119
" -- "$cur" ) )
120120
}
121121

122+
__oci-runtime-tool_complete_compliance_level() {
123+
COMPREPLY=( $( compgen -W "
124+
may
125+
should
126+
must
127+
" -- "$cur" ) )
128+
}
129+
122130
__oci-runtime-tool_complete_propagations() {
123131
COMPREPLY=( $( compgen -W "
124132
private
@@ -218,6 +226,10 @@ _oci-runtime-tool_oci-runtime-tool() {
218226
--log-level
219227
"
220228

229+
local options_with_args="
230+
--compliance-level
231+
"
232+
221233
local boolean_options="
222234
--help -h
223235
--host-specific
@@ -231,6 +243,10 @@ _oci-runtime-tool_oci-runtime-tool() {
231243
__oci-runtime-tool_complete_log_level
232244
return
233245
;;
246+
--compliance-level)
247+
__oci-runtime-tool_complete_compliance_level
248+
return
249+
;;
234250
esac
235251

236252
case "$cur" in

man/oci-runtime-tool.1.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ oci-runtime-tool is a collection of tools for working with the [OCI runtime spec
3232
**--log-level**=LEVEL
3333
Log level (panic, fatal, error, warn, info, or debug) (default: "error").
3434

35+
**--compliance-level**=LEVEL
36+
Compliance level (may, should or must) (default: "must").
37+
3538
**-v**, **--version**
3639
Print version information.
3740

validate/error.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package validate
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"strings"
7+
8+
rspec "github.com/opencontainers/runtime-spec/specs-go"
9+
)
10+
11+
// ComplianceLevel represents the OCI compliance levels
12+
type ComplianceLevel int
13+
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
48+
type ErrorCode int
49+
50+
const (
51+
// DefaultFilesystems represents the error code of default filesystems test
52+
DefaultFilesystems ErrorCode = iota
53+
)
54+
55+
// Error represents an error with compliance level and OCI reference
56+
type Error struct {
57+
Level ComplianceLevel
58+
Reference string
59+
Err error
60+
}
61+
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"},
66+
}
67+
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
93+
}
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)
110+
}

0 commit comments

Comments
 (0)