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) +}