From 4029999d3df2b7155fd8301139909bc165161591 Mon Sep 17 00:00:00 2001 From: liangchenye Date: Fri, 31 Mar 2017 00:12:04 +0800 Subject: [PATCH 1/4] oci error: add error level and reference Signed-off-by: liangchenye --- cmd/runtimetest/main.go | 17 +++++++- completions/bash/oci-runtime-tool | 15 +++++++ man/oci-runtime-tool.1.md | 3 ++ validate/error.go | 69 +++++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 validate/error.go diff --git a/cmd/runtimetest/main.go b/cmd/runtimetest/main.go index f1344e916..8db8b69f9 100644 --- a/cmd/runtimetest/main.go +++ b/cmd/runtimetest/main.go @@ -18,9 +18,11 @@ import ( "github.com/hashicorp/go-multierror" "github.com/mndrix/tap-go" rspec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/opencontainers/runtime-tools/cmd/runtimetest/mount" "github.com/syndtr/gocapability/capability" "github.com/urfave/cli" + + "github.com/opencontainers/runtime-tools/cmd/runtimetest/mount" + ociErr "github.com/opencontainers/runtime-tools/validate" ) // PrGetNoNewPrivs isn't exposed in Golang so we define it ourselves copying the value from @@ -660,11 +662,16 @@ func validate(context *cli.Context) error { t := tap.New() t.Header(0) + complianceLevelString := context.String("compliance-level") + complianceLevel := ociErr.ParseLevel(complianceLevelString) var validationErrors error for _, v := range defaultValidations { err := v.test(spec) t.Ok(err == nil, v.description) if err != nil { + if e, ok := err.(*ociErr.OCIError); ok && e.Level < complianceLevel { + continue + } validationErrors = multierror.Append(validationErrors, err) } } @@ -674,6 +681,9 @@ func validate(context *cli.Context) error { err := v.test(spec) t.Ok(err == nil, v.description) if err != nil { + if e, ok := err.(*ociErr.OCIError); ok && e.Level < complianceLevel { + continue + } validationErrors = multierror.Append(validationErrors, err) } } @@ -700,6 +710,11 @@ func main() { Value: ".", Usage: "Path to the configuration", }, + cli.StringFlag{ + Name: "compliance-level", + Value: "must", + Usage: "Compliance level (must or should)", + }, } app.Action = validate diff --git a/completions/bash/oci-runtime-tool b/completions/bash/oci-runtime-tool index c34eb5925..0cdbe2bce 100644 --- a/completions/bash/oci-runtime-tool +++ b/completions/bash/oci-runtime-tool @@ -119,6 +119,13 @@ __oci-runtime-tool_complete_log_level() { " -- "$cur" ) ) } +__oci-runtime-tool_complete_compliance_level() { + COMPREPLY=( $( compgen -W " + must + should + " -- "$cur" ) ) +} + __oci-runtime-tool_complete_propagations() { COMPREPLY=( $( compgen -W " private @@ -218,6 +225,10 @@ _oci-runtime-tool_oci-runtime-tool() { --log-level " + local options_with_args=" + --compliance-level + " + local boolean_options=" --help -h --host-specific @@ -231,6 +242,10 @@ _oci-runtime-tool_oci-runtime-tool() { __oci-runtime-tool_complete_log_level return ;; + --compliance-level) + __oci-runtime-tool_complete_compliance_level + return + ;; esac case "$cur" in diff --git a/man/oci-runtime-tool.1.md b/man/oci-runtime-tool.1.md index 27b2e1708..7ca93f5bf 100644 --- a/man/oci-runtime-tool.1.md +++ b/man/oci-runtime-tool.1.md @@ -32,6 +32,9 @@ oci-runtime-tool is a collection of tools for working with the [OCI runtime spec **--log-level**=LEVEL Log level (panic, fatal, error, warn, info, or debug) (default: "error"). +**--compliance-level**=LEVEL + Compliance level (must or should) (default: "must"). + **-v**, **--version** Print version information. diff --git a/validate/error.go b/validate/error.go new file mode 100644 index 000000000..037011599 --- /dev/null +++ b/validate/error.go @@ -0,0 +1,69 @@ +package validate + +import ( + "errors" + "fmt" + "strings" +) + +// ComplianceLevel represents the OCI compliance levels +type ComplianceLevel int + +const ( + ComplianceOptional ComplianceLevel = iota + ComplianceMay + ComplianceRecommended + ComplianceShould + ComplianceShouldNot + ComplianceShall + ComplianceShallNot + ComplianceRequired + ComplianceMustNot + ComplianceMust +) + +// OCIErrorCode represents the compliance content +type OCIErrorCode int + +const ( + DefaultFilesystems OCIErrorCode = iota +) + +// OCIError represents an error with compliance level and OCI reference +type OCIError struct { + Level ComplianceLevel + Reference string + Err error +} + +//FIXME: change to tagged spec releases +const referencePrefix = "https://github.com/opencontainers/runtime-spec/blob/master/" + +var ociErrors = map[OCIErrorCode]OCIError{ + DefaultFilesystems: OCIError{Level: ComplianceShould, Reference: "config-linux.md#default-filesystems"}, +} + +// ParseLevel takes a string level and returns the OCI compliance level constant +func ParseLevel(level string) ComplianceLevel { + switch strings.ToUpper(level) { + case "SHOULD": + return ComplianceShould + case "MUST": + return ComplianceMust + default: + return ComplianceMust + } +} + +// NewOCIError creates an OCIError by OCIErrorCode and message +func NewOCIError(code OCIErrorCode, msg string) error { + err := ociErrors[code] + err.Err = errors.New(msg) + + return &err +} + +// Error returns the error message with OCI reference +func (oci *OCIError) Error() string { + return fmt.Sprintf("%s\nRefer to: %s%s", oci.Err.Error(), referencePrefix, oci.Reference) +} From 6b1d25d9823e483878491c450e4355d8c1ed54fa Mon Sep 17 00:00:00 2001 From: liangchenye Date: Fri, 31 Mar 2017 21:38:37 +0800 Subject: [PATCH 2/4] add defaultFS and use OCIError Signed-off-by: liangchenye --- cmd/runtimetest/main.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/cmd/runtimetest/main.go b/cmd/runtimetest/main.go index 8db8b69f9..bbbc12db5 100644 --- a/cmd/runtimetest/main.go +++ b/cmd/runtimetest/main.go @@ -32,6 +32,13 @@ const PrGetNoNewPrivs = 39 const specConfig = "config.json" var ( + defaultFS = map[string]string{ + "/proc": "proc", + "/sys": "sysfs", + "/dev/pts": "devpts", + "/dev/shm": "tmpfs", + } + defaultSymlinks = map[string]string{ "/dev/fd": "/proc/self/fd", "/dev/stdin": "/proc/self/fd/0", @@ -310,6 +317,28 @@ func validateRootFS(spec *rspec.Spec) error { return nil } +func validateDefaultFS(spec *rspec.Spec) error { + logrus.Debugf("validating linux default filesystem") + + mountInfos, err := mount.GetMounts() + if err != nil { + return ociErr.NewOCIError(ociErr.DefaultFilesystems, err.Error()) + } + + mountsMap := make(map[string]string) + for _, mountInfo := range mountInfos { + mountsMap[mountInfo.Mountpoint] = mountInfo.Fstype + } + + for fs, fstype := range defaultFS { + if !(mountsMap[fs] == fstype) { + return ociErr.NewOCIError(ociErr.DefaultFilesystems, fmt.Sprintf("%v must exist and expected type is %v", fs, fstype)) + } + } + + return nil +} + func validateLinuxDevices(spec *rspec.Spec) error { for _, device := range spec.Linux.Devices { fi, err := os.Stat(device.Path) @@ -617,6 +646,10 @@ func validate(context *cli.Context) error { test: validateDefaultSymlinks, description: "default symlinks", }, + { + test: validateDefaultFS, + description: "default file system", + }, { test: validateDefaultDevices, description: "default devices", From 1259e3eacae240972af9ca9d15a1d5214ac6f767 Mon Sep 17 00:00:00 2001 From: liangchenye Date: Sat, 1 Apr 2017 19:15:31 +0800 Subject: [PATCH 3/4] sort compliance level according to RFC2119 Signed-off-by: liangchenye --- cmd/runtimetest/main.go | 10 ++++---- man/oci-runtime-tool.1.md | 2 +- validate/error.go | 52 ++++++++++++++++++++++++++++----------- 3 files changed, 43 insertions(+), 21 deletions(-) diff --git a/cmd/runtimetest/main.go b/cmd/runtimetest/main.go index bbbc12db5..7aeb31d19 100644 --- a/cmd/runtimetest/main.go +++ b/cmd/runtimetest/main.go @@ -322,7 +322,7 @@ func validateDefaultFS(spec *rspec.Spec) error { mountInfos, err := mount.GetMounts() if err != nil { - return ociErr.NewOCIError(ociErr.DefaultFilesystems, err.Error()) + return ociErr.NewError(ociErr.DefaultFilesystems, err.Error()) } mountsMap := make(map[string]string) @@ -332,7 +332,7 @@ func validateDefaultFS(spec *rspec.Spec) error { for fs, fstype := range defaultFS { if !(mountsMap[fs] == fstype) { - return ociErr.NewOCIError(ociErr.DefaultFilesystems, fmt.Sprintf("%v must exist and expected type is %v", fs, fstype)) + return ociErr.NewError(ociErr.DefaultFilesystems, fmt.Sprintf("%v must exist and expected type is %v", fs, fstype)) } } @@ -702,7 +702,7 @@ func validate(context *cli.Context) error { err := v.test(spec) t.Ok(err == nil, v.description) if err != nil { - if e, ok := err.(*ociErr.OCIError); ok && e.Level < complianceLevel { + if e, ok := err.(*ociErr.Error); ok && e.Level < complianceLevel { continue } validationErrors = multierror.Append(validationErrors, err) @@ -714,7 +714,7 @@ func validate(context *cli.Context) error { err := v.test(spec) t.Ok(err == nil, v.description) if err != nil { - if e, ok := err.(*ociErr.OCIError); ok && e.Level < complianceLevel { + if e, ok := err.(*ociErr.Error); ok && e.Level < complianceLevel { continue } validationErrors = multierror.Append(validationErrors, err) @@ -746,7 +746,7 @@ func main() { cli.StringFlag{ Name: "compliance-level", Value: "must", - Usage: "Compliance level (must or should)", + Usage: "Compliance level (may, should or must)", }, } diff --git a/man/oci-runtime-tool.1.md b/man/oci-runtime-tool.1.md index 7ca93f5bf..880b587ff 100644 --- a/man/oci-runtime-tool.1.md +++ b/man/oci-runtime-tool.1.md @@ -33,7 +33,7 @@ oci-runtime-tool is a collection of tools for working with the [OCI runtime spec Log level (panic, fatal, error, warn, info, or debug) (default: "error"). **--compliance-level**=LEVEL - Compliance level (must or should) (default: "must"). + Compliance level (may, should or must) (default: "must"). **-v**, **--version** Print version information. diff --git a/validate/error.go b/validate/error.go index 037011599..ce7d286f3 100644 --- a/validate/error.go +++ b/validate/error.go @@ -10,27 +10,31 @@ import ( type ComplianceLevel int const ( - ComplianceOptional ComplianceLevel = iota - ComplianceMay - ComplianceRecommended + // MAY-level + ComplianceMay ComplianceLevel = iota + ComplianceOptional + // SHOULD-level ComplianceShould ComplianceShouldNot + ComplianceRecommended + ComplianceNotRecommended + // MUST-level + ComplianceMust + ComplianceMustNot ComplianceShall ComplianceShallNot ComplianceRequired - ComplianceMustNot - ComplianceMust ) -// OCIErrorCode represents the compliance content -type OCIErrorCode int +// ErrorCode represents the compliance content +type ErrorCode int const ( - DefaultFilesystems OCIErrorCode = iota + DefaultFilesystems ErrorCode = iota ) -// OCIError represents an error with compliance level and OCI reference -type OCIError struct { +// Error represents an error with compliance level and OCI reference +type Error struct { Level ComplianceLevel Reference string Err error @@ -39,24 +43,42 @@ type OCIError struct { //FIXME: change to tagged spec releases const referencePrefix = "https://github.com/opencontainers/runtime-spec/blob/master/" -var ociErrors = map[OCIErrorCode]OCIError{ - DefaultFilesystems: OCIError{Level: ComplianceShould, Reference: "config-linux.md#default-filesystems"}, +var ociErrors = map[ErrorCode]Error{ + DefaultFilesystems: Error{Level: ComplianceShould, Reference: "config-linux.md#default-filesystems"}, } // ParseLevel takes a string level and returns the OCI compliance level constant func ParseLevel(level string) ComplianceLevel { switch strings.ToUpper(level) { + case "MAY": + fallthrough + case "OPTIONAL": + return ComplianceMay case "SHOULD": + fallthrough + case "SHOULDNOT": + fallthrough + case "RECOMMENDED": + fallthrough + case "NOTRECOMMENDED": return ComplianceShould case "MUST": + fallthrough + case "MUSTNOT": + fallthrough + case "SHALL": + fallthrough + case "SHALLNOT": + fallthrough + case "REQUIRED": return ComplianceMust default: return ComplianceMust } } -// NewOCIError creates an OCIError by OCIErrorCode and message -func NewOCIError(code OCIErrorCode, msg string) error { +// NewError creates an Error by ErrorCode and message +func NewError(code ErrorCode, msg string) error { err := ociErrors[code] err.Err = errors.New(msg) @@ -64,6 +86,6 @@ func NewOCIError(code OCIErrorCode, msg string) error { } // Error returns the error message with OCI reference -func (oci *OCIError) Error() string { +func (oci *Error) Error() string { return fmt.Sprintf("%s\nRefer to: %s%s", oci.Err.Error(), referencePrefix, oci.Reference) } From 6316a4e0243ad864932dff4b3cc3473ca6f3f04e Mon Sep 17 00:00:00 2001 From: liangchenye Date: Wed, 12 Apr 2017 15:37:42 +0800 Subject: [PATCH 4/4] use released version as reference; improve Parse error Signed-off-by: liangchenye --- cmd/runtimetest/main.go | 8 +++++-- completions/bash/oci-runtime-tool | 3 ++- validate/error.go | 37 +++++++++++++++++++++++-------- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/cmd/runtimetest/main.go b/cmd/runtimetest/main.go index 7aeb31d19..d26dfbbff 100644 --- a/cmd/runtimetest/main.go +++ b/cmd/runtimetest/main.go @@ -332,7 +332,7 @@ func validateDefaultFS(spec *rspec.Spec) error { for fs, fstype := range defaultFS { if !(mountsMap[fs] == fstype) { - return ociErr.NewError(ociErr.DefaultFilesystems, fmt.Sprintf("%v must exist and expected type is %v", fs, fstype)) + return ociErr.NewError(ociErr.DefaultFilesystems, fmt.Sprintf("%v SHOULD exist and expected type is %v", fs, fstype)) } } @@ -696,7 +696,11 @@ func validate(context *cli.Context) error { t.Header(0) complianceLevelString := context.String("compliance-level") - complianceLevel := ociErr.ParseLevel(complianceLevelString) + complianceLevel, err := ociErr.ParseLevel(complianceLevelString) + if err != nil { + complianceLevel = ociErr.ComplianceMust + logrus.Warningf("%s, using 'MUST' by default.", err.Error()) + } var validationErrors error for _, v := range defaultValidations { err := v.test(spec) diff --git a/completions/bash/oci-runtime-tool b/completions/bash/oci-runtime-tool index 0cdbe2bce..0ffe13632 100644 --- a/completions/bash/oci-runtime-tool +++ b/completions/bash/oci-runtime-tool @@ -121,8 +121,9 @@ __oci-runtime-tool_complete_log_level() { __oci-runtime-tool_complete_compliance_level() { COMPREPLY=( $( compgen -W " - must + may should + must " -- "$cur" ) ) } diff --git a/validate/error.go b/validate/error.go index ce7d286f3..754d032b5 100644 --- a/validate/error.go +++ b/validate/error.go @@ -4,6 +4,8 @@ import ( "errors" "fmt" "strings" + + rspec "github.com/opencontainers/runtime-spec/specs-go" ) // ComplianceLevel represents the OCI compliance levels @@ -11,18 +13,34 @@ type ComplianceLevel int const ( // MAY-level + + // ComplianceMay represents 'MAY' in RFC2119 ComplianceMay ComplianceLevel = iota + // ComplianceOptional represents 'OPTIONAL' in RFC2119 ComplianceOptional + // SHOULD-level + + // ComplianceShould represents 'SHOULD' in RFC2119 ComplianceShould + // ComplianceShouldNot represents 'SHOULD NOT' in RFC2119 ComplianceShouldNot + // ComplianceRecommended represents 'RECOMMENDED' in RFC2119 ComplianceRecommended + // ComplianceNotRecommended represents 'NOT RECOMMENDED' in RFC2119 ComplianceNotRecommended + // MUST-level + + // ComplianceMust represents 'MUST' in RFC2119 ComplianceMust + // ComplianceMustNot represents 'MUST NOT' in RFC2119 ComplianceMustNot + // ComplianceShall represents 'SHALL' in RFC2119 ComplianceShall + // ComplianceShallNot represents 'SHALL NOT' in RFC2119 ComplianceShallNot + // ComplianceRequired represents 'REQUIRED' in RFC2119 ComplianceRequired ) @@ -30,6 +48,7 @@ const ( type ErrorCode int const ( + // DefaultFilesystems represents the error code of default filesystems test DefaultFilesystems ErrorCode = iota ) @@ -40,20 +59,19 @@ type Error struct { Err error } -//FIXME: change to tagged spec releases -const referencePrefix = "https://github.com/opencontainers/runtime-spec/blob/master/" +const referencePrefix = "https://github.com/opencontainers/runtime-spec/blob" var ociErrors = map[ErrorCode]Error{ DefaultFilesystems: Error{Level: ComplianceShould, Reference: "config-linux.md#default-filesystems"}, } // ParseLevel takes a string level and returns the OCI compliance level constant -func ParseLevel(level string) ComplianceLevel { +func ParseLevel(level string) (ComplianceLevel, error) { switch strings.ToUpper(level) { case "MAY": fallthrough case "OPTIONAL": - return ComplianceMay + return ComplianceMay, nil case "SHOULD": fallthrough case "SHOULDNOT": @@ -61,7 +79,7 @@ func ParseLevel(level string) ComplianceLevel { case "RECOMMENDED": fallthrough case "NOTRECOMMENDED": - return ComplianceShould + return ComplianceShould, nil case "MUST": fallthrough case "MUSTNOT": @@ -71,10 +89,11 @@ func ParseLevel(level string) ComplianceLevel { case "SHALLNOT": fallthrough case "REQUIRED": - return ComplianceMust - default: - return ComplianceMust + return ComplianceMust, nil } + + var l ComplianceLevel + return l, fmt.Errorf("%q is not a valid compliance level", level) } // NewError creates an Error by ErrorCode and message @@ -87,5 +106,5 @@ func NewError(code ErrorCode, msg string) error { // Error returns the error message with OCI reference func (oci *Error) Error() string { - return fmt.Sprintf("%s\nRefer to: %s%s", oci.Err.Error(), referencePrefix, oci.Reference) + return fmt.Sprintf("%s\nRefer to: %s/v%s/%s", oci.Err.Error(), referencePrefix, rspec.Version, oci.Reference) }