Skip to content
This repository has been archived by the owner on May 12, 2021. It is now read-only.

Commit

Permalink
virtcontainers: add new package for cgroups
Browse files Browse the repository at this point in the history
virtcontainers/pkg/cgroups contains functions and structures needed to deal
with cgroups and virtual containers

Signed-off-by: Julio Montes <[email protected]>
  • Loading branch information
Julio Montes committed Feb 21, 2020
1 parent d54723a commit 03cdf6c
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 201 deletions.
11 changes: 6 additions & 5 deletions virtcontainers/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
ktu "github.com/kata-containers/runtime/pkg/katatestutils"
"github.com/kata-containers/runtime/virtcontainers/persist"
"github.com/kata-containers/runtime/virtcontainers/pkg/annotations"
vccgroups "github.com/kata-containers/runtime/virtcontainers/pkg/cgroups"
"github.com/kata-containers/runtime/virtcontainers/pkg/mock"
"github.com/kata-containers/runtime/virtcontainers/pkg/rootless"
vcTypes "github.com/kata-containers/runtime/virtcontainers/pkg/types"
Expand Down Expand Up @@ -48,7 +49,7 @@ func newEmptySpec() *specs.Spec {
return &specs.Spec{
Linux: &specs.Linux{
Resources: &specs.LinuxResources{},
CgroupsPath: defaultCgroupPath,
CgroupsPath: vccgroups.DefaultCgroupPath,
},
Process: &specs.Process{
Capabilities: &specs.LinuxCapabilities{},
Expand Down Expand Up @@ -515,7 +516,7 @@ func TestStatusSandboxSuccessfulStateReady(t *testing.T) {
assert := assert.New(t)

config := newTestSandboxConfigNoop()
cgroupPath, err := renameCgroupPath(defaultCgroupPath)
cgroupPath, err := vccgroups.RenameCgroupPath(vccgroups.DefaultCgroupPath)
assert.NoError(err)

hypervisorConfig := HypervisorConfig{
Expand Down Expand Up @@ -574,7 +575,7 @@ func TestStatusSandboxSuccessfulStateRunning(t *testing.T) {
assert := assert.New(t)

config := newTestSandboxConfigNoop()
cgroupPath, err := renameCgroupPath(defaultCgroupPath)
cgroupPath, err := vccgroups.RenameCgroupPath(vccgroups.DefaultCgroupPath)
assert.NoError(err)

hypervisorConfig := HypervisorConfig{
Expand Down Expand Up @@ -1156,7 +1157,7 @@ func TestStatusContainerStateReady(t *testing.T) {
contID := "101"

config := newTestSandboxConfigNoop()
cgroupPath, err := renameCgroupPath(defaultCgroupPath)
cgroupPath, err := vccgroups.RenameCgroupPath(vccgroups.DefaultCgroupPath)
assert.NoError(err)

ctx := context.Background()
Expand Down Expand Up @@ -1217,7 +1218,7 @@ func TestStatusContainerStateRunning(t *testing.T) {
contID := "101"

config := newTestSandboxConfigNoop()
cgroupPath, err := renameCgroupPath(defaultCgroupPath)
cgroupPath, err := vccgroups.RenameCgroupPath(vccgroups.DefaultCgroupPath)
assert.NoError(err)

ctx := context.Background()
Expand Down
103 changes: 0 additions & 103 deletions virtcontainers/cgroups.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,9 @@ import (
"fmt"
"os"
"path/filepath"
"regexp"
"strings"

"github.com/containerd/cgroups"
"github.com/kata-containers/runtime/virtcontainers/pkg/rootless"
libcontcgroups "github.com/opencontainers/runc/libcontainer/cgroups"
libcontcgroupsfs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
libcontcgroupssystemd "github.com/opencontainers/runc/libcontainer/cgroups/systemd"
"github.com/opencontainers/runc/libcontainer/configs"
specconv "github.com/opencontainers/runc/libcontainer/specconv"
specs "github.com/opencontainers/runtime-spec/specs-go"
)

Expand All @@ -34,15 +27,6 @@ type cgroupPather interface {
// where path is defined by the containers manager
const cgroupKataPath = "/kata/"

// prepend a kata specific string to oci cgroup path to
// form a different cgroup path, thus cAdvisor couldn't
// find kata containers cgroup path on host to prevent it
// from grabbing the stats data.
const cgroupKataPrefix = "kata"

// DefaultCgroupPath runtime-determined location in the cgroups hierarchy.
const defaultCgroupPath = "/vc"

var cgroupsLoadFunc = cgroups.Load
var cgroupsNewFunc = cgroups.New

Expand Down Expand Up @@ -181,90 +165,3 @@ func validCPUResources(cpuSpec *specs.LinuxCPU) *specs.LinuxCPU {

return &cpu
}

func renameCgroupPath(path string) (string, error) {
if path == "" {
return "", fmt.Errorf("Cgroup path is empty")
}

cgroupPathDir := filepath.Dir(path)
cgroupPathName := fmt.Sprintf("%s_%s", cgroupKataPrefix, filepath.Base(path))
return filepath.Join(cgroupPathDir, cgroupPathName), nil

}

// validCgroupPath returns a valid cgroup path.
// see https://github.com/opencontainers/runtime-spec/blob/master/config-linux.md#cgroups-path
func validCgroupPath(path string, systemdCgroup bool) (string, error) {
if isSystemdCgroup(path) {
return path, nil
}

if systemdCgroup {
return "", fmt.Errorf("malformed systemd path '%v': expected to be of form 'slice:prefix:name'", path)
}

// In the case of an absolute path (starting with /), the runtime MUST
// take the path to be relative to the cgroups mount point.
if filepath.IsAbs(path) {
return renameCgroupPath(filepath.Clean(path))
}

// In the case of a relative path (not starting with /), the runtime MAY
// interpret the path relative to a runtime-determined location in the cgroups hierarchy.
// clean up path and return a new path relative to defaultCgroupPath
return renameCgroupPath(filepath.Join(defaultCgroupPath, filepath.Clean("/"+path)))
}

func isSystemdCgroup(cgroupPath string) bool {
// systemd cgroup path: slice:prefix:name
re := regexp.MustCompile(`([[:alnum:]]|\.)+:([[:alnum:]]|\.)+:([[:alnum:]]|\.)+`)
found := re.FindStringIndex(cgroupPath)

// if found string is equal to cgroupPath then
// it's a correct systemd cgroup path.
return found != nil && cgroupPath[found[0]:found[1]] == cgroupPath
}

func newCgroupManager(cgroups *configs.Cgroup, cgroupPaths map[string]string, spec *specs.Spec) (libcontcgroups.Manager, error) {
var err error

rootless := rootless.IsRootless()
systemdCgroup := isSystemdCgroup(spec.Linux.CgroupsPath)

// Create a new cgroup if the current one is nil
// this cgroups must be saved later
if cgroups == nil {
if cgroups, err = specconv.CreateCgroupConfig(&specconv.CreateOpts{
// cgroup name is taken from spec
CgroupName: "",
UseSystemdCgroup: systemdCgroup,
Spec: spec,
RootlessCgroups: rootless,
}); err != nil {
return nil, fmt.Errorf("Could not create cgroup config: %v", err)
}
}

// Set cgroupPaths to nil when the map is empty, it can and will be
// populated by `Manager.Apply()` when the runtime or any other process
// is moved to the cgroup. See sandbox.setupSandboxCgroup().
if len(cgroupPaths) == 0 {
cgroupPaths = nil
}

if systemdCgroup {
systemdCgroupFunc, err := libcontcgroupssystemd.NewSystemdCgroupsManager()
if err != nil {
return nil, fmt.Errorf("Could not create systemd cgroup manager: %v", err)
}
libcontcgroupssystemd.UseSystemd()
return systemdCgroupFunc(cgroups, cgroupPaths), nil
}

return &libcontcgroupsfs.Manager{
Cgroups: cgroups,
Rootless: rootless,
Paths: cgroupPaths,
}, nil
}
91 changes: 0 additions & 91 deletions virtcontainers/cgroups_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"os"
"os/exec"
"path/filepath"
"strings"
"testing"

"github.com/containerd/cgroups"
Expand Down Expand Up @@ -198,93 +197,3 @@ func TestUpdateCgroups(t *testing.T) {
err = s.cgroupsDelete()
assert.NoError(err)
}

func TestIsSystemdCgroup(t *testing.T) {
assert := assert.New(t)

tests := []struct {
path string
expected bool
}{
{"slice:kata:afhts2e5d4g5s", true},
{"slice.system:kata:afhts2e5d4g5s", true},
{"/kata/afhts2e5d4g5s", false},
{"a:b:c:d", false},
{":::", false},
{"", false},
{":", false},
{"::", false},
{":::", false},
{"a:b", false},
{"a:b:", false},
{":a:b", false},
{"@:@:@", false},
}

for _, t := range tests {
assert.Equal(t.expected, isSystemdCgroup(t.path), "invalid systemd cgroup path: %v", t.path)
}
}

func TestValidCgroupPath(t *testing.T) {
assert := assert.New(t)

for _, t := range []struct {
path string
systemdCgroup bool
error bool
}{
// empty paths
{"../../../", false, false},
{"../", false, false},
{".", false, false},
{"../../../", false, false},
{"./../", false, false},

// valid no-systemd paths
{"../../../foo", false, false},
{"/../hi", false, false},
{"/../hi/foo", false, false},
{"o / m /../ g", false, false},

// invalid systemd paths
{"o / m /../ g", true, true},
{"slice:kata", true, true},
{"/kata/afhts2e5d4g5s", true, true},
{"a:b:c:d", true, true},
{":::", true, true},
{"", true, true},
{":", true, true},
{"::", true, true},
{":::", true, true},
{"a:b", true, true},
{"a:b:", true, true},
{":a:b", true, true},
{"@:@:@", true, true},

// valid system paths
{"slice:kata:55555", true, false},
{"slice.system:kata:afhts2e5d4g5s", true, false},
} {
path, err := validCgroupPath(t.path, t.systemdCgroup)
if t.error {
assert.Error(err)
continue
} else {
assert.NoError(err)
}

if filepath.IsAbs(t.path) {
cleanPath := filepath.Dir(filepath.Clean(t.path))
assert.True(strings.HasPrefix(path, cleanPath),
"%v should have prefix %v", cleanPath)
} else if t.systemdCgroup {
assert.Equal(t.path, path)
} else {
assert.True(strings.HasPrefix(path, "/"+cgroupKataPrefix) ||
strings.HasPrefix(path, defaultCgroupPath),
"%v should have prefix /%v or %v", path, cgroupKataPrefix, defaultCgroupPath)
}
}

}
3 changes: 2 additions & 1 deletion virtcontainers/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"time"

"github.com/containerd/cgroups"
vccgroups "github.com/kata-containers/runtime/virtcontainers/pkg/cgroups"
vcTypes "github.com/kata-containers/runtime/virtcontainers/pkg/types"
"github.com/kata-containers/runtime/virtcontainers/types"
"github.com/kata-containers/runtime/virtcontainers/utils"
Expand Down Expand Up @@ -1409,7 +1410,7 @@ func (c *Container) cgroupsCreate() (err error) {
resources.CPU = validCPUResources(spec.Linux.Resources.CPU)
}

c.state.CgroupPath, err = validCgroupPath(spec.Linux.CgroupsPath, c.sandbox.config.SystemdCgroup)
c.state.CgroupPath, err = vccgroups.ValidCgroupPath(spec.Linux.CgroupsPath, c.sandbox.config.SystemdCgroup)
if err != nil {
return fmt.Errorf("Invalid cgroup path: %v", err)
}
Expand Down
3 changes: 2 additions & 1 deletion virtcontainers/kata_agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/kata-containers/runtime/virtcontainers/device/config"
persistapi "github.com/kata-containers/runtime/virtcontainers/persist/api"
vcAnnotations "github.com/kata-containers/runtime/virtcontainers/pkg/annotations"
vccgroups "github.com/kata-containers/runtime/virtcontainers/pkg/cgroups"
ns "github.com/kata-containers/runtime/virtcontainers/pkg/nsenter"
"github.com/kata-containers/runtime/virtcontainers/pkg/rootless"
vcTypes "github.com/kata-containers/runtime/virtcontainers/pkg/types"
Expand Down Expand Up @@ -1033,7 +1034,7 @@ func (k *kataAgent) constraintGRPCSpec(grpcSpec *grpc.Spec, passSeccomp bool) {
// - Initrd image doesn't have systemd.
// - Nobody will be able to modify the resources of a specific container by using systemctl set-property.
// - docker is not running in the VM.
if isSystemdCgroup(grpcSpec.Linux.CgroupsPath) {
if vccgroups.IsSystemdCgroup(grpcSpec.Linux.CgroupsPath) {
// Convert systemd cgroup to cgroupfs
slice := strings.Split(grpcSpec.Linux.CgroupsPath, ":")
// 0 - slice: system.slice
Expand Down
65 changes: 65 additions & 0 deletions virtcontainers/pkg/cgroups/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) 2020 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//

package cgroups

import (
"fmt"
"path/filepath"
"regexp"
)

// prepend a kata specific string to oci cgroup path to
// form a different cgroup path, thus cAdvisor couldn't
// find kata containers cgroup path on host to prevent it
// from grabbing the stats data.
const CgroupKataPrefix = "kata"

// DefaultCgroupPath runtime-determined location in the cgroups hierarchy.
const DefaultCgroupPath = "/vc"

func RenameCgroupPath(path string) (string, error) {
if path == "" {
return "", fmt.Errorf("Cgroup path is empty")
}

cgroupPathDir := filepath.Dir(path)
cgroupPathName := fmt.Sprintf("%s_%s", CgroupKataPrefix, filepath.Base(path))
return filepath.Join(cgroupPathDir, cgroupPathName), nil

}

// validCgroupPath returns a valid cgroup path.
// see https://github.com/opencontainers/runtime-spec/blob/master/config-linux.md#cgroups-path
func ValidCgroupPath(path string, systemdCgroup bool) (string, error) {
if IsSystemdCgroup(path) {
return path, nil
}

if systemdCgroup {
return "", fmt.Errorf("malformed systemd path '%v': expected to be of form 'slice:prefix:name'", path)
}

// In the case of an absolute path (starting with /), the runtime MUST
// take the path to be relative to the cgroups mount point.
if filepath.IsAbs(path) {
return RenameCgroupPath(filepath.Clean(path))
}

// In the case of a relative path (not starting with /), the runtime MAY
// interpret the path relative to a runtime-determined location in the cgroups hierarchy.
// clean up path and return a new path relative to DefaultCgroupPath
return RenameCgroupPath(filepath.Join(DefaultCgroupPath, filepath.Clean("/"+path)))
}

func IsSystemdCgroup(cgroupPath string) bool {
// systemd cgroup path: slice:prefix:name
re := regexp.MustCompile(`([[:alnum:]]|\.)+:([[:alnum:]]|\.)+:([[:alnum:]]|\.)+`)
found := re.FindStringIndex(cgroupPath)

// if found string is equal to cgroupPath then
// it's a correct systemd cgroup path.
return found != nil && cgroupPath[found[0]:found[1]] == cgroupPath
}
Loading

0 comments on commit 03cdf6c

Please sign in to comment.