Skip to content

Commit

Permalink
Gate NoCgroups behind an OCI runtime check
Browse files Browse the repository at this point in the history
`runc` does not have support at present. As such, we only want to
use this when the `crun` runtime is in use.

Make a few further tweaks to enable use with `crun` - most
significantly, blank the resource limits part of the OCI spec
when requesting no cgroups, as `crun` rightfully complains that
it can't do resource limits without cgroups. Also, complain if
the user explicitly set resource limits when requesting this - we
can't do limits.

Signed-off-by: Matthew Heon <matthew.heon@pm.me>
  • Loading branch information
mheon committed Aug 26, 2019
1 parent 62fe638 commit eb2ab6a
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 59 deletions.
4 changes: 4 additions & 0 deletions libpod.conf
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ runtime = "runc"
# libpod will use it for reporting nicer errors.
runtime_supports_json = ["crun", "runc"]

# List of all the OCI runtimes that support --cgroup-manager=disable to disable
# creation of CGroups for containers.
runtime_supports_nocgroups = ["crun"]

# Paths to look for a valid OCI runtime (runc, runv, etc)
# If the paths are empty or no valid path was found, then the `$PATH`
# environment variable will be used as the fallback.
Expand Down
30 changes: 16 additions & 14 deletions libpod/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,20 @@ const (
// OCIRuntime represents an OCI-compatible runtime that libpod can call into
// to perform container operations
type OCIRuntime struct {
name string
path string
conmonPath string
conmonEnv []string
cgroupManager string
tmpDir string
exitsDir string
socketsDir string
logSizeMax int64
noPivot bool
reservePorts bool
supportsJSON bool
sdNotify bool
name string
path string
conmonPath string
conmonEnv []string
cgroupManager string
tmpDir string
exitsDir string
socketsDir string
logSizeMax int64
noPivot bool
reservePorts bool
supportsJSON bool
supportsNoCgroups bool
sdNotify bool
}

// ociError is used to parse the OCI runtime JSON log. It is not part of the
Expand All @@ -73,7 +74,7 @@ type ociError struct {

// Make a new OCI runtime with provided options.
// The first path that points to a valid executable will be used.
func newOCIRuntime(name string, paths []string, conmonPath string, runtimeCfg *RuntimeConfig, supportsJSON bool) (*OCIRuntime, error) {
func newOCIRuntime(name string, paths []string, conmonPath string, runtimeCfg *RuntimeConfig, supportsJSON, supportsNoCgroups bool) (*OCIRuntime, error) {
if name == "" {
return nil, errors.Wrapf(define.ErrInvalidArg, "the OCI runtime must be provided a non-empty name")
}
Expand All @@ -93,6 +94,7 @@ func newOCIRuntime(name string, paths []string, conmonPath string, runtimeCfg *R
// TODO: probe OCI runtime for feature and enable automatically if
// available.
runtime.supportsJSON = supportsJSON
runtime.supportsNoCgroups = supportsNoCgroups

foundPath := false
for _, path := range paths {
Expand Down
4 changes: 3 additions & 1 deletion libpod/oci_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,10 +428,12 @@ func (r *OCIRuntime) stopContainer(ctr *Container, timeout uint) error {
}

var args []string
if rootless.IsRootless() {
if rootless.IsRootless() || ctr.config.NoCgroups {
// we don't use --all for rootless containers as the OCI runtime might use
// the cgroups to determine the PIDs, but for rootless containers there is
// not any.
// Same logic for NoCgroups - we can't use cgroups as the user
// explicitly requested none be created.
args = []string{"kill", ctr.ID(), "KILL"}
} else {
args = []string{"kill", "--all", ctr.ID(), "KILL"}
Expand Down
90 changes: 46 additions & 44 deletions libpod/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,6 @@ var (
// place of the configuration file pointed to by ConfigPath.
OverrideConfigPath = etcDir + "/containers/libpod.conf"

// DefaultInfraImage to use for infra container

// DefaultInfraCommand to be run in an infra container

// DefaultSHMLockPath is the default path for SHM locks
DefaultSHMLockPath = "/libpod_lock"
// DefaultRootlessSHMLockPath is the default path for rootless SHM locks
Expand Down Expand Up @@ -161,8 +157,12 @@ type RuntimeConfig struct {
OCIRuntime string `toml:"runtime"`
// OCIRuntimes are the set of configured OCI runtimes (default is runc)
OCIRuntimes map[string][]string `toml:"runtimes"`
// RuntimeSupportsJSON is the list of the OCI runtimes that support --format=json
// RuntimeSupportsJSON is the list of the OCI runtimes that support
// --format=json.
RuntimeSupportsJSON []string `toml:"runtime_supports_json"`
// RuntimeSupportsNoCgroups is a list of OCI runtimes that support
// running containers without CGroups.
RuntimeSupportsNoCgroups []string `toml:"runtime_supports_nocgroups"`
// RuntimePath is the path to OCI runtime binary for launching
// containers.
// The first path pointing to a valid file will be used
Expand Down Expand Up @@ -263,21 +263,22 @@ type RuntimeConfig struct {
// If they were not, we may override them with information from the database,
// if it exists and differs from what is present in the system already.
type runtimeConfiguredFrom struct {
storageGraphDriverSet bool
storageGraphRootSet bool
storageRunRootSet bool
libpodStaticDirSet bool
libpodTmpDirSet bool
volPathSet bool
conmonPath bool
conmonEnvVars bool
initPath bool
ociRuntimes bool
runtimePath bool
cniPluginDir bool
noPivotRoot bool
runtimeSupportsJSON bool
ociRuntime bool
storageGraphDriverSet bool
storageGraphRootSet bool
storageRunRootSet bool
libpodStaticDirSet bool
libpodTmpDirSet bool
volPathSet bool
conmonPath bool
conmonEnvVars bool
initPath bool
ociRuntimes bool
runtimePath bool
cniPluginDir bool
noPivotRoot bool
runtimeSupportsJSON bool
runtimeSupportsNoCgroups bool
ociRuntime bool
}

func defaultRuntimeConfig() (RuntimeConfig, error) {
Expand Down Expand Up @@ -600,6 +601,9 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options ..
if tmpConfig.RuntimeSupportsJSON != nil {
runtime.configuredFrom.runtimeSupportsJSON = true
}
if tmpConfig.RuntimeSupportsNoCgroups != nil {
runtime.configuredFrom.runtimeSupportsNoCgroups = true
}
if tmpConfig.OCIRuntime != "" {
runtime.configuredFrom.ociRuntime = true
}
Expand Down Expand Up @@ -646,6 +650,9 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options ..
if !runtime.configuredFrom.runtimeSupportsJSON {
runtime.config.RuntimeSupportsJSON = tmpConfig.RuntimeSupportsJSON
}
if !runtime.configuredFrom.runtimeSupportsNoCgroups {
runtime.config.RuntimeSupportsNoCgroups = tmpConfig.RuntimeSupportsNoCgroups
}
if !runtime.configuredFrom.ociRuntime {
runtime.config.OCIRuntime = tmpConfig.OCIRuntime
}
Expand Down Expand Up @@ -992,6 +999,16 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) {
}
}

// Make lookup tables for runtime support
supportsJSON := make(map[string]bool)
supportsNoCgroups := make(map[string]bool)
for _, r := range runtime.config.RuntimeSupportsJSON {
supportsJSON[r] = true
}
for _, r := range runtime.config.RuntimeSupportsNoCgroups {
supportsNoCgroups[r] = true
}

// Get us at least one working OCI runtime.
runtime.ociRuntimes = make(map[string]*OCIRuntime)

Expand All @@ -1009,15 +1026,10 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) {

name := filepath.Base(runtime.config.RuntimePath[0])

supportsJSON := false
for _, r := range runtime.config.RuntimeSupportsJSON {
if r == name {
supportsJSON = true
break
}
}
json := supportsJSON[name]
nocgroups := supportsNoCgroups[name]

ociRuntime, err := newOCIRuntime(name, runtime.config.RuntimePath, runtime.conmonPath, runtime.config, supportsJSON)
ociRuntime, err := newOCIRuntime(name, runtime.config.RuntimePath, runtime.conmonPath, runtime.config, json, nocgroups)
if err != nil {
return err
}
Expand All @@ -1028,15 +1040,10 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) {

// Initialize remaining OCI runtimes
for name, paths := range runtime.config.OCIRuntimes {
supportsJSON := false
for _, r := range runtime.config.RuntimeSupportsJSON {
if r == name {
supportsJSON = true
break
}
}
json := supportsJSON[name]
nocgroups := supportsNoCgroups[name]

ociRuntime, err := newOCIRuntime(name, paths, runtime.conmonPath, runtime.config, supportsJSON)
ociRuntime, err := newOCIRuntime(name, paths, runtime.conmonPath, runtime.config, json, nocgroups)
if err != nil {
// Don't fatally error.
// This will allow us to ship configs including optional
Expand All @@ -1056,15 +1063,10 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) {
if strings.HasPrefix(runtime.config.OCIRuntime, "/") {
name := filepath.Base(runtime.config.OCIRuntime)

supportsJSON := false
for _, r := range runtime.config.RuntimeSupportsJSON {
if r == name {
supportsJSON = true
break
}
}
json := supportsJSON[name]
nocgroups := supportsNoCgroups[name]

ociRuntime, err := newOCIRuntime(name, []string{runtime.config.OCIRuntime}, runtime.conmonPath, runtime.config, supportsJSON)
ociRuntime, err := newOCIRuntime(name, []string{runtime.config.OCIRuntime}, runtime.conmonPath, runtime.config, json, nocgroups)
if err != nil {
return err
}
Expand Down
7 changes: 7 additions & 0 deletions libpod/runtime_ctr.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,13 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (c *Contai
ctr.ociRuntime = ociRuntime
}

// Check NoCgroups support
if ctr.config.NoCgroups {
if !ctr.ociRuntime.supportsNoCgroups {
return nil, errors.Wrapf(define.ErrInvalidArg, "requested OCI runtime %s is not compatible with NoCgroups", ctr.ociRuntime.name)
}
}

var pod *Pod
if ctr.config.Pod != "" {
// Get the pod from state
Expand Down
6 changes: 6 additions & 0 deletions pkg/spec/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,12 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
configSpec.Linux.Resources = &spec.LinuxResources{}
}
}
if config.NoCgroups {
if addedResources {
return nil, errors.New("cannot specify resource limits when no-cgroups is specified")
}
configSpec.Linux.Resources = &spec.LinuxResources{}
}

// Make sure that the bind mounts keep options like nosuid, noexec, nodev.
mounts, err := pmount.GetMounts()
Expand Down

0 comments on commit eb2ab6a

Please sign in to comment.