Skip to content

Commit

Permalink
Merge pull request #354 from liangchenye/master
Browse files Browse the repository at this point in the history
add OCIError to differentiate 'MUST/SHOULD' and add reference support
  • Loading branch information
Ma Shimiao authored Jul 28, 2017
2 parents 0f24b50 + 6316a4e commit 3a21ae6
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 1 deletion.
54 changes: 53 additions & 1 deletion cmd/runtimetest/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -30,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",
Expand Down Expand Up @@ -308,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.NewError(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.NewError(ociErr.DefaultFilesystems, fmt.Sprintf("%v SHOULD 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)
Expand Down Expand Up @@ -620,6 +651,10 @@ func validate(context *cli.Context) error {
test: validateDefaultSymlinks,
description: "default symlinks",
},
{
test: validateDefaultFS,
description: "default file system",
},
{
test: validateDefaultDevices,
description: "default devices",
Expand Down Expand Up @@ -665,11 +700,20 @@ func validate(context *cli.Context) error {
t := tap.New()
t.Header(0)

complianceLevelString := context.String("compliance-level")
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)
t.Ok(err == nil, v.description)
if err != nil {
if e, ok := err.(*ociErr.Error); ok && e.Level < complianceLevel {
continue
}
validationErrors = multierror.Append(validationErrors, err)
}
}
Expand All @@ -679,6 +723,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.Error); ok && e.Level < complianceLevel {
continue
}
validationErrors = multierror.Append(validationErrors, err)
}
}
Expand All @@ -705,6 +752,11 @@ func main() {
Value: ".",
Usage: "Path to the configuration",
},
cli.StringFlag{
Name: "compliance-level",
Value: "must",
Usage: "Compliance level (may, should or must)",
},
}

app.Action = validate
Expand Down
16 changes: 16 additions & 0 deletions completions/bash/oci-runtime-tool
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,14 @@ __oci-runtime-tool_complete_log_level() {
" -- "$cur" ) )
}

__oci-runtime-tool_complete_compliance_level() {
COMPREPLY=( $( compgen -W "
may
should
must
" -- "$cur" ) )
}

__oci-runtime-tool_complete_propagations() {
COMPREPLY=( $( compgen -W "
private
Expand Down Expand Up @@ -218,6 +226,10 @@ _oci-runtime-tool_oci-runtime-tool() {
--log-level
"

local options_with_args="
--compliance-level
"

local boolean_options="
--help -h
--host-specific
Expand All @@ -231,6 +243,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
Expand Down
3 changes: 3 additions & 0 deletions man/oci-runtime-tool.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 (may, should or must) (default: "must").

**-v**, **--version**
Print version information.

Expand Down
110 changes: 110 additions & 0 deletions validate/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package validate

import (
"errors"
"fmt"
"strings"

rspec "github.com/opencontainers/runtime-spec/specs-go"
)

// ComplianceLevel represents the OCI compliance levels
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
)

// ErrorCode represents the compliance content
type ErrorCode int

const (
// DefaultFilesystems represents the error code of default filesystems test
DefaultFilesystems ErrorCode = iota
)

// Error represents an error with compliance level and OCI reference
type Error struct {
Level ComplianceLevel
Reference string
Err error
}

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, error) {
switch strings.ToUpper(level) {
case "MAY":
fallthrough
case "OPTIONAL":
return ComplianceMay, nil
case "SHOULD":
fallthrough
case "SHOULDNOT":
fallthrough
case "RECOMMENDED":
fallthrough
case "NOTRECOMMENDED":
return ComplianceShould, nil
case "MUST":
fallthrough
case "MUSTNOT":
fallthrough
case "SHALL":
fallthrough
case "SHALLNOT":
fallthrough
case "REQUIRED":
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
func NewError(code ErrorCode, msg string) error {
err := ociErrors[code]
err.Err = errors.New(msg)

return &err
}

// Error returns the error message with OCI reference
func (oci *Error) Error() string {
return fmt.Sprintf("%s\nRefer to: %s/v%s/%s", oci.Err.Error(), referencePrefix, rspec.Version, oci.Reference)
}

0 comments on commit 3a21ae6

Please sign in to comment.