diff --git a/cmd/oci-runtime-tool/generate.go b/cmd/oci-runtime-tool/generate.go index 63aedbbb6..af64bb077 100644 --- a/cmd/oci-runtime-tool/generate.go +++ b/cmd/oci-runtime-tool/generate.go @@ -14,6 +14,7 @@ import ( rspec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/opencontainers/runtime-tools/generate/seccomp" + "github.com/opencontainers/runtime-tools/utils" "github.com/urfave/cli" ) @@ -401,7 +402,7 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { } if context.IsSet("linux-cpus") { - if err := uintListValid(context.String("linux-cpus")); err != nil { + if err := utils.UnitListValid(context.String("linux-cpus")); err != nil { return err } else { g.SetLinuxResourcesCPUCpus(context.String("linux-cpus")) @@ -409,7 +410,7 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { } if context.IsSet("linux-mems") { - if err := uintListValid(context.String("linux-mems")); err != nil { + if err := utils.UnitListValid(context.String("linux-mems")); err != nil { return err } else { g.SetLinuxResourcesCPUMems(context.String("linux-mems")) @@ -523,38 +524,6 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { return err } -func uintListValid(val string) error { - if val == "" { - return nil - } - - split := strings.Split(val, ",") - errInvalidFormat := fmt.Errorf("invalid format: %s", val) - - for _, r := range split { - if !strings.Contains(r, "-") { - _, err := strconv.Atoi(r) - if err != nil { - return errInvalidFormat - } - } else { - split := strings.SplitN(r, "-", 2) - min, err := strconv.Atoi(split[0]) - if err != nil { - return errInvalidFormat - } - max, err := strconv.Atoi(split[1]) - if err != nil { - return errInvalidFormat - } - if max < min { - return errInvalidFormat - } - } - } - return nil -} - func parseIDMapping(idms string) (uint32, uint32, uint32, error) { idm := strings.Split(idms, ":") if len(idm) != 3 { diff --git a/cmd/runtimetest/main.go b/cmd/runtimetest/main.go index 0aaa5e2ca..2ff4283bb 100644 --- a/cmd/runtimetest/main.go +++ b/cmd/runtimetest/main.go @@ -18,6 +18,7 @@ import ( "github.com/mndrix/tap-go" rspec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/cmd/runtimetest/mount" + "github.com/opencontainers/runtime-tools/utils" "github.com/syndtr/gocapability/capability" "github.com/urfave/cli" ) @@ -154,11 +155,7 @@ func validateLinuxProcess(spec *rspec.Spec) error { func validateCapabilities(spec *rspec.Spec) error { logrus.Debugf("validating capabilities") - last := capability.CAP_LAST_CAP - // workaround for RHEL6 which has no /proc/sys/kernel/cap_last_cap - if last == capability.Cap(63) { - last = capability.CAP_BLOCK_SUSPEND - } + last := utils.LastCap() processCaps, err := capability.NewPid(0) if err != nil { diff --git a/generate/generate.go b/generate/generate.go index f8a6294ec..8cf8e86db 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -11,7 +11,7 @@ import ( rspec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate/seccomp" - "github.com/opencontainers/runtime-tools/validate" + "github.com/opencontainers/runtime-tools/utils" "github.com/syndtr/gocapability/capability" ) @@ -824,7 +824,7 @@ func (g *Generator) SetupPrivileged(privileged bool) { if privileged { // Add all capabilities in privileged mode. var finalCapList []string for _, cap := range capability.List() { - if g.HostSpecific && cap > validate.LastCap() { + if g.HostSpecific && cap > utils.LastCap() { continue } finalCapList = append(finalCapList, fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String()))) @@ -856,7 +856,7 @@ func (g *Generator) ClearProcessCapabilities() { // AddProcessCapability adds a process capability into g.spec.Process.Capabilities. func (g *Generator) AddProcessCapability(c string) error { cp := strings.ToUpper(c) - if err := validate.CapValid(cp, g.HostSpecific); err != nil { + if err := utils.CapValid(cp, g.HostSpecific); err != nil { return err } @@ -903,7 +903,7 @@ func (g *Generator) AddProcessCapability(c string) error { // DropProcessCapability drops a process capability from g.spec.Process.Capabilities. func (g *Generator) DropProcessCapability(c string) error { cp := strings.ToUpper(c) - if err := validate.CapValid(cp, g.HostSpecific); err != nil { + if err := utils.CapValid(cp, g.HostSpecific); err != nil { return err } diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 000000000..4015d3546 --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,82 @@ +package utils + +import ( + "fmt" + "strconv" + "strings" + + "github.com/syndtr/gocapability/capability" +) + +// CapValid checks whether a capability is valid +func CapValid(c string, hostSpecific bool) error { + isValid := false + + if !strings.HasPrefix(c, "CAP_") { + return fmt.Errorf("capability %s must start with CAP_", c) + } + for _, cap := range capability.List() { + if c == fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String())) { + if hostSpecific && cap > LastCap() { + return fmt.Errorf("CAP_%s is not supported on the current host", c) + } + isValid = true + break + } + } + + if !isValid { + return fmt.Errorf("Invalid capability: %s", c) + } + return nil +} + +// LastCap return last cap of system +func LastCap() capability.Cap { + last := capability.CAP_LAST_CAP + // hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap + if last == capability.Cap(63) { + last = capability.CAP_BLOCK_SUSPEND + } + + return last +} + +// UnitListValid checks strings whether is valid for +// cpuset.cpus and cpuset.mems, duplicates are allowed +// Supported formats: +// 1 +// 0-3 +// 0-2,1,3 +// 0-2,1-3,4 +func UnitListValid(val string) error { + if val == "" { + return nil + } + + split := strings.Split(val, ",") + errInvalidFormat := fmt.Errorf("invalid format: %s", val) + + for _, r := range split { + if !strings.Contains(r, "-") { + _, err := strconv.Atoi(r) + if err != nil { + return errInvalidFormat + } + } else { + split := strings.SplitN(r, "-", 2) + min, err := strconv.Atoi(split[0]) + if err != nil { + return errInvalidFormat + } + max, err := strconv.Atoi(split[1]) + if err != nil { + return errInvalidFormat + } + if max < min { + return errInvalidFormat + } + } + } + return nil +} diff --git a/validate/validate.go b/validate/validate.go index 95172e9ad..7534bfd2a 100644 --- a/validate/validate.go +++ b/validate/validate.go @@ -16,7 +16,7 @@ import ( "github.com/Sirupsen/logrus" "github.com/blang/semver" rspec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/syndtr/gocapability/capability" + "github.com/opencontainers/runtime-tools/utils" ) const specConfig = "config.json" @@ -314,7 +314,7 @@ func (v *Validator) CheckCapablities() (msgs []string) { } for _, capability := range caps { - if err := CapValid(capability, v.HostSpecific); err != nil { + if err := utils.CapValid(capability, v.HostSpecific); err != nil { msgs = append(msgs, fmt.Sprintf("capability %q is not valid, man capabilities(7)", capability)) } } @@ -543,6 +543,20 @@ func (v *Validator) CheckLinuxResources() (msgs []string) { logrus.Debugf("check linux resources") r := v.spec.Linux.Resources + + if r.CPU != nil { + if r.CPU.Cpus != "" { + if err := utils.UnitListValid(r.CPU.Cpus); err != nil { + msgs = append(msgs, err.Error()) + } + } + if r.CPU.Mems != "" { + if err := utils.UnitListValid(r.CPU.Mems); err != nil { + msgs = append(msgs, err.Error()) + } + } + } + if r.Memory != nil { if r.Memory.Limit != nil && r.Memory.Swap != nil && uint64(*r.Memory.Limit) > uint64(*r.Memory.Swap) { msgs = append(msgs, fmt.Sprintf("Minimum memoryswap should be larger than memory limit")) @@ -551,6 +565,7 @@ func (v *Validator) CheckLinuxResources() (msgs []string) { msgs = append(msgs, fmt.Sprintf("Minimum memory limit should be larger than memory reservation")) } } + if r.Network != nil && v.HostSpecific { var exist bool interfaces, err := net.Interfaces() @@ -616,40 +631,6 @@ func (v *Validator) CheckSeccomp() (msgs []string) { return } -// CapValid checks whether a capability is valid -func CapValid(c string, hostSpecific bool) error { - isValid := false - - if !strings.HasPrefix(c, "CAP_") { - return fmt.Errorf("capability %s must start with CAP_", c) - } - for _, cap := range capability.List() { - if c == fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String())) { - if hostSpecific && cap > LastCap() { - return fmt.Errorf("CAP_%s is not supported on the current host", c) - } - isValid = true - break - } - } - - if !isValid { - return fmt.Errorf("Invalid capability: %s", c) - } - return nil -} - -// LastCap return last cap of system -func LastCap() capability.Cap { - last := capability.CAP_LAST_CAP - // hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap - if last == capability.Cap(63) { - last = capability.CAP_BLOCK_SUSPEND - } - - return last -} - func envValid(env string) bool { items := strings.Split(env, "=") if len(items) < 2 {