Skip to content

Commit

Permalink
tests/int: add a "update cpu period with pod limit set" test
Browse files Browse the repository at this point in the history
Add a test case for an issue fixed by the previous commit.

Unfortunately, this is somewhat complicated as there's no easy way to
create a transient unit, so a binary, sd-helper, had to be added. On top
of that, an ability to create a parent/pod cgroup is added to
helpers.bash, which might be useful for future integration tests.

Signed-off-by: Kir Kolyshkin <[email protected]>
  • Loading branch information
kolyshkin committed Jul 28, 2021
1 parent 374bdd2 commit c08f40b
Show file tree
Hide file tree
Showing 5 changed files with 234 additions and 9 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ vendor/pkg
/runc
/runc-*
contrib/cmd/recvtty/recvtty
contrib/cmd/sd-helper/sd-helper
man/man8
release
Vagrantfile
Expand Down
10 changes: 6 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,15 @@ GO_BUILD_STATIC := CGO_ENABLED=1 $(GO) build -trimpath $(EXTRA_FLAGS) -tags "$(B
runc:
$(GO_BUILD) -o runc .

all: runc recvtty
all: runc recvtty sd-helper

recvtty:
$(GO_BUILD) -o contrib/cmd/recvtty/recvtty ./contrib/cmd/recvtty
recvtty sd-helper:
$(GO_BUILD) -o contrib/cmd/$@/$@ ./contrib/cmd/$@

static:
$(GO_BUILD_STATIC) -o runc .
$(GO_BUILD_STATIC) -o contrib/cmd/recvtty/recvtty ./contrib/cmd/recvtty
$(GO_BUILD_STATIC) -o contrib/cmd/sd-helper/sd-helper ./contrib/cmd/sd-helper

release:
script/release.sh -r release/$(VERSION) -v $(VERSION)
Expand Down Expand Up @@ -110,6 +111,7 @@ install-man: man
clean:
rm -f runc runc-*
rm -f contrib/cmd/recvtty/recvtty
rm -f contrib/cmd/sd-helper/sd-helper
rm -rf release
rm -rf man/man8

Expand Down Expand Up @@ -147,7 +149,7 @@ localcross:
CGO_ENABLED=1 GOARCH=arm64 CC=aarch64-linux-gnu-gcc $(GO_BUILD) -o runc-arm64 .
CGO_ENABLED=1 GOARCH=ppc64le CC=powerpc64le-linux-gnu-gcc $(GO_BUILD) -o runc-ppc64le .

.PHONY: runc all recvtty static release dbuild lint man runcimage \
.PHONY: runc all recvtty sd-helper static release dbuild lint man runcimage \
test localtest unittest localunittest integration localintegration \
rootlessintegration localrootlessintegration shell install install-bash \
install-man clean cfmt shfmt shellcheck \
Expand Down
122 changes: 122 additions & 0 deletions contrib/cmd/sd-helper/helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package main

import (
"errors"
"os"
"strings"

"github.com/sirupsen/logrus"
"github.com/urfave/cli"

"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/cgroups/systemd"
"github.com/opencontainers/runc/libcontainer/configs"
)

// version will be populated by the Makefile, read from
// VERSION file of the source code.
var version = ""

// gitCommit will be the hash that the binary was built from
// and will be populated by the Makefile.
var gitCommit = ""

const (
usage = `Open Container Initiative contrib/cmd/sd-helper
sd-helper is a tool that uses runc/libcontainer/cgroups/systemd package
functionality to communicate to systemd in order to perform various operations.
Currently this is limited to starting and stopping systemd transient slice
units.
Example:
sd-helper start system-pod123.slice
`
)

func main() {
if !systemd.IsRunningSystemd() {
logrus.Fatal("systemd is required")
}

app := cli.NewApp()
app.Name = "sd-helper"
app.Usage = usage

// Set version to be the same as runc.
var v []string
if version != "" {
v = append(v, version)
}
if gitCommit != "" {
v = append(v, "commit: "+gitCommit)
}
app.Version = strings.Join(v, "\n")

// Set the flags.
app.Flags = []cli.Flag{
cli.BoolFlag{
Name: "debug",
Usage: "Enable debug output",
},
cli.StringFlag{
Name: "parent, p",
Usage: "parent unit name",
},
}
app.Commands = []cli.Command{
{
Name: "start",
Usage: "start a transient unit",
Action: func(c *cli.Context) error {
return unitCommand("start", c)
},
},
{
Name: "stop",
Usage: "stop a transient unit",
Action: func(c *cli.Context) error {
return unitCommand("stop", c)
},
},
}
err := app.Run(os.Args)
if err != nil {
logrus.Fatal(err)
}
}

func newManager(config *configs.Cgroup) cgroups.Manager {
if cgroups.IsCgroup2UnifiedMode() {
return systemd.NewUnifiedManager(config, "", false)
}
return systemd.NewLegacyManager(config, nil)
}

func unitCommand(cmd string, c *cli.Context) error {
if c.Bool("debug") {
logrus.SetLevel(logrus.DebugLevel)
}
name := c.Args().First()
if name == "" {
return errors.New("unit name is required")
}

podConfig := &configs.Cgroup{
Name: name,
Parent: c.String("parent"),
Resources: &configs.Resources{},
}
pm := newManager(podConfig)

switch cmd {
case "start":
return pm.Apply(-1)
case "stop":
return pm.Destroy()
}

// Should not happen.
return errors.New("invalid command")
}
75 changes: 70 additions & 5 deletions tests/integration/helpers.bash
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ unset IMAGES

RUNC="${INTEGRATION_ROOT}/../../runc"
RECVTTY="${INTEGRATION_ROOT}/../../contrib/cmd/recvtty/recvtty"
SD_HELPER="${INTEGRATION_ROOT}/../../contrib/cmd/sd-helper/sd-helper"

# Test data path.
TESTDATA="${INTEGRATION_ROOT}/testdata"
Expand Down Expand Up @@ -128,24 +129,85 @@ function init_cgroup_paths() {
fi
}

function create_parent() {
if [ -n "$RUNC_USE_SYSTEMD" ]; then
[ -z "$SD_PARENT_NAME" ] && return
"$SD_HELPER" --parent machine.slice start "$SD_PARENT_NAME"
else
[ -z "$REL_PARENT_PATH" ] && return
if [ "$CGROUP_UNIFIED" == "yes" ]; then
mkdir "/sys/fs/cgroup$REL_PARENT_PATH"
else
local subsys
for subsys in ${CGROUP_SUBSYSTEMS}; do
# Have to ignore EEXIST (-p) as some subsystems
# are mounted together (e.g. cpu,cpuacct), so
# the path is created more than once.
mkdir -p "/sys/fs/cgroup/$subsys$REL_PARENT_PATH"
done
fi
fi
}

function remove_parent() {
if [ -n "$RUNC_USE_SYSTEMD" ]; then
[ -z "$SD_PARENT_NAME" ] && return
"$SD_HELPER" --parent machine.slice stop "$SD_PARENT_NAME"
else
[ -z "$REL_PARENT_PATH" ] && return
if [ "$CGROUP_UNIFIED" == "yes" ]; then
rmdir "/sys/fs/cgroup/$REL_PARENT_PATH"
else
local subsys
for subsys in ${CGROUP_SUBSYSTEMS} systemd; do
rmdir "/sys/fs/cgroup/$subsys/$REL_PARENT_PATH"
done
fi
fi
unset SD_PARENT_NAME
unset REL_PARENT_PATH
}

function set_parent_systemd_properties() {
[ -z "$SD_PARENT_NAME" ] && return
local user
[ "$(id -u)" != "0" ] && user="--user"
systemctl set-property $user "$SD_PARENT_NAME" "$@"
}

# Randomize cgroup path(s), and update cgroupsPath in config.json.
# This function sets a few cgroup-related variables.
#
# Optional parameter $1 is a pod/parent name. If set, a parent/pod cgroup is
# created, and variables $REL_PARENT_PATH and $SD_PARENT_NAME can be used to
# refer to it.
function set_cgroups_path() {
init_cgroup_paths
local pod dash_pod slash_pod pod_slice
if [ "$#" -ne 0 ] && [ "$1" != "" ]; then
# Set up a parent/pod cgroup.
pod="$1"
dash_pod="-$pod"
slash_pod="/$pod"
SD_PARENT_NAME="machine-${pod}.slice"
pod_slice="/$SD_PARENT_NAME"
fi

local rnd="$RANDOM"
if [ -n "${RUNC_USE_SYSTEMD}" ]; then
SD_UNIT_NAME="runc-cgroups-integration-test-${rnd}.scope"
if [ "$(id -u)" = "0" ]; then
REL_CGROUPS_PATH="/machine.slice/$SD_UNIT_NAME"
OCI_CGROUPS_PATH="machine.slice:runc-cgroups:integration-test-${rnd}"
REL_PARENT_PATH="/machine.slice${pod_slice}"
OCI_CGROUPS_PATH="machine${dash_pod}.slice:runc-cgroups:integration-test-${rnd}"
else
REL_CGROUPS_PATH="/user.slice/user-$(id -u).slice/user@$(id -u).service/machine.slice/$SD_UNIT_NAME"
REL_PARENT_PATH="/user.slice/user-$(id -u).slice/user@$(id -u).service/machine.slice${pod_slice}"
# OCI path doesn't contain "/user.slice/user-$(id -u).slice/user@$(id -u).service/" prefix
OCI_CGROUPS_PATH="machine.slice:runc-cgroups:integration-test-${rnd}"
OCI_CGROUPS_PATH="machine${dash_pod}.slice:runc-cgroups:integration-test-${rnd}"
fi
REL_CGROUPS_PATH="$REL_PARENT_PATH/$SD_UNIT_NAME"
else
REL_CGROUPS_PATH="/runc-cgroups-integration-test/test-cgroup-${rnd}"
REL_PARENT_PATH="/runc-cgroups-integration-test${slash_pod}"
REL_CGROUPS_PATH="$REL_PARENT_PATH/test-cgroup-${rnd}"
OCI_CGROUPS_PATH=$REL_CGROUPS_PATH
fi

Expand All @@ -154,6 +216,8 @@ function set_cgroups_path() {
CGROUP_PATH=${CGROUP_BASE_PATH}${REL_CGROUPS_PATH}
fi

[ -n "$pod" ] && create_parent

update_config '.linux.cgroupsPath |= "'"${OCI_CGROUPS_PATH}"'"'
}

Expand Down Expand Up @@ -475,4 +539,5 @@ function teardown_bundle() {
__runc delete -f "$ct"
done
rm -rf "$ROOT"
remove_parent
}
35 changes: 35 additions & 0 deletions tests/integration/update.bats
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,41 @@ EOF
check_cpu_quota 30000 100000 "300ms"
}

@test "update cpu period in a pod cgroup with pod limit set" {
requires cgroups_v1
[[ "$ROOTLESS" -ne 0 ]] && requires rootless_cgroup

set_cgroups_path "pod_${RANDOM}"

# Set parent/pod CPU quota limit to 50%.
if [ -n "${RUNC_USE_SYSTEMD}" ]; then
set_parent_systemd_properties CPUQuota="50%"
else
echo 50000 >"/sys/fs/cgroup/cpu/$REL_PARENT_PATH/cpu.cfs_quota_us"
fi
# Sanity checks.
run cat "/sys/fs/cgroup/cpu$REL_PARENT_PATH/cpu.cfs_period_us"
[ "$output" -eq 100000 ]
run cat "/sys/fs/cgroup/cpu$REL_PARENT_PATH/cpu.cfs_quota_us"
[ "$output" -eq 50000 ]

runc run -d --console-socket "$CONSOLE_SOCKET" test_update
[ "$status" -eq 0 ]
# Get the current period.
local cur
cur=$(get_cgroup_value cpu.cfs_period_us)

# Sanity check: as the parent cgroup sets the limit to 50%,
# setting a higher limit (e.g. 60%) is expected to fail.
runc update --cpu-quota $((cur * 6 / 10)) test_update
[ "$status" -eq 1 ]

# Finally, the test itself: set 30% limit but with lower period.
runc update --cpu-period 10000 --cpu-quota 3000 test_update
[ "$status" -eq 0 ]
check_cpu_quota 3000 10000 "300ms"
}

@test "update cgroup v2 resources via unified map" {
[[ "$ROOTLESS" -ne 0 ]] && requires rootless_cgroup
requires cgroups_v2
Expand Down

0 comments on commit c08f40b

Please sign in to comment.