Skip to content

Commit

Permalink
libct/int: add device update test
Browse files Browse the repository at this point in the history
... and remove the one from tests/integration.

The idea is similar to the one for the test case being removed -- try
updating device rules many times to make sure we are not leaking eBPF
programs after every update/Set(). This is better though as we can
really change the device rules every time (which "runc update" can't)
and check that the rule is applied.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
  • Loading branch information
kolyshkin committed Jun 4, 2021
1 parent 123a34d commit 73dffdf
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 26 deletions.
106 changes: 106 additions & 0 deletions libcontainer/integration/update_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package integration

import (
"bytes"
"os"
"strings"
"testing"

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

func testUpdateDevices(t *testing.T, systemd bool) {
if testing.Short() {
return
}
rootfs, err := newRootfs()
ok(t, err)
defer remove(rootfs)
config := newTemplateConfig(t, &tParam{
rootfs: rootfs,
systemd: systemd,
})
container, err := newContainer(t, config)
ok(t, err)
defer destroyContainer(container)

// Execute a first process in the container
stdinR, stdinW, err := os.Pipe()
ok(t, err)
process := &libcontainer.Process{
Cwd: "/",
Args: []string{"cat"},
Env: standardEnvironment,
Stdin: stdinR,
Init: true,
}
err = container.Run(process)
_ = stdinR.Close()
defer func() {
_ = stdinW.Close()
if _, err := process.Wait(); err != nil {
t.Log(err)
}
}()
ok(t, err)

var buf bytes.Buffer
devCheck := &libcontainer.Process{
Cwd: "/",
Args: []string{"/bin/sh", "-c", "echo > /dev/full; cat /dev/null; true"},
Env: standardEnvironment,
Stderr: &buf,
}
isAllowed := true
expected := map[bool][]string{
true: {
"write error: No space left on device", // from write to /dev/full
// no error from cat /dev/null
},
false: {
"/dev/full: Operation not permitted",
`cat: can't open '/dev/null': Operation not permitted`,
},
}
defaultDevices := config.Cgroups.Resources.Devices

for i := 0; i < 300; i++ {
// Check the access
buf.Reset()
err = container.Run(devCheck)
ok(t, err)
waitProcess(devCheck, t)

t.Logf("[%d] allowed: %v output: %s", i, isAllowed, buf.String())

for _, exp := range expected[isAllowed] {
if !strings.Contains(buf.String(), exp) {
t.Fatalf("[%d] expected %q, got %q", i, exp, buf.String())
}
}

// Now flip the access permission
isAllowed = !isAllowed
if isAllowed {
config.Cgroups.Resources.Devices = defaultDevices
} else {
config.Cgroups.Resources.Devices = []*devices.Rule{}
}
if err := container.Set(*config); err != nil {
t.Fatal(err)
}
}
}

func TestUpdateDevices(t *testing.T) {
testUpdateDevices(t, false)
}

func TestUpdateDevicesSystemd(t *testing.T) {
if !systemd.IsRunningSystemd() {
t.Skip("Test requires systemd.")
}
testUpdateDevices(t, true)
}
26 changes: 0 additions & 26 deletions tests/integration/update.bats
Original file line number Diff line number Diff line change
Expand Up @@ -648,29 +648,3 @@ EOF
runc resume test_update
[ "$status" -eq 0 ]
}

@test "runc update replaces devices cgroup program" {
[[ "$ROOTLESS" -ne 0 ]] && requires rootless_cgroup

# Unfortunately we can't update device rules directly with runc ("runc
# update" doesn't support it, and adding support would require ironing out
# some long-standing design issues with device configuration). So instead
# we just run "runc update" many times, relying on the fact that runc will
# re-apply devices cgroup rules on each runc update.
#
# In the past runc would not delete old cgroupv2 eBPF programs, so this
# test ensures that once we go past the program limit (64 stacked programs
# at time of writing) you can still run "runc" update.

# Run the container in the background.
runc run -d --console-socket "$CONSOLE_SOCKET" test_update
[ "$status" -eq 0 ]

for new_limit in $(seq 300); do
runc update --pids-limit "$((2 * new_limit))" test_update
[ "$status" -eq 0 ]
done

# The container should still be running.
testcontainer test_update running
}

0 comments on commit 73dffdf

Please sign in to comment.