mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-10 21:53:57 +08:00
591db7d0ca
We are creating the interface for every test, but there is only one
using it. Let's just call the function to create the netdev on the test
that uses it.
I guess that was the reason we had the "ip link del ..." in teardown.
Because in a lot of tests we were just creating and deleting the
interface on the host.
While we are there, as suggested by lifubang, let's make the "ip link
add" line specify the mtu and mac addr. This way, the interface is not
created without that info and we race with host daemons (like udev) that
_might_ want to change it.
Signed-off-by: Rodrigo Campos <rodrigo@amutable.com>
(cherry picked from commit b16ed9a0b8)
521 lines
14 KiB
Bash
521 lines
14 KiB
Bash
#!/usr/bin/env bats
|
|
|
|
load helpers
|
|
|
|
function create_netns() {
|
|
mtu_value=1789
|
|
mac_address="00:11:22:33:44:55"
|
|
global_ip="169.254.169.77/32"
|
|
|
|
# Create a dummy interface to move to the container.
|
|
# Specify all link-level parameters at creation time, this way it is not created without
|
|
# those values unassigned and we might race with host's daemons to set them.
|
|
ip link add dummy0 address "$mac_address" mtu $mtu_value type dummy
|
|
udevadm settle
|
|
ip address add "$global_ip" dev dummy0
|
|
|
|
# Create a temporary name for the test network namespace.
|
|
tmp=$(mktemp -u)
|
|
ns_name=$(basename "$tmp")
|
|
|
|
# Create the network namespace.
|
|
ip netns add "$ns_name"
|
|
ns_path=$(ip netns add "$ns_name" 2>&1 | sed -e 's/.*"\(.*\)".*/\1/')
|
|
}
|
|
|
|
function delete_netns() {
|
|
# The interface shouldn't be on the host, but if we failed to move it to the container, it
|
|
# might. Let's delete it if we created one (i.e. if ns_name is defined).
|
|
[ -v ns_name ] && ip link del dev dummy0 2>/dev/null
|
|
|
|
# Delete the namespace only if the ns_name variable is set.
|
|
[ -v ns_name ] && ip netns del "$ns_name"
|
|
}
|
|
|
|
function setup() {
|
|
# XXX: currently criu require root containers.
|
|
requires criu root
|
|
|
|
setup_busybox
|
|
}
|
|
|
|
function teardown() {
|
|
delete_netns
|
|
teardown_bundle
|
|
}
|
|
|
|
function setup_pipes() {
|
|
# The changes to 'terminal' are needed for running in detached mode
|
|
# shellcheck disable=SC2016
|
|
update_config ' (.. | select(.terminal? != null)) .terminal |= false
|
|
| (.. | select(.[]? == "sh")) += ["-c", "for i in `seq 10`; do read xxx || continue; echo ponG $xxx; done"]'
|
|
|
|
# Create three sets of pipes for __runc run.
|
|
# for stderr
|
|
exec {pipe}<> <(:)
|
|
exec {err_r}</proc/self/fd/$pipe
|
|
exec {err_w}>/proc/self/fd/$pipe
|
|
exec {pipe}>&-
|
|
# for stdout
|
|
exec {pipe}<> <(:)
|
|
exec {out_r}</proc/self/fd/$pipe
|
|
exec {out_w}>/proc/self/fd/$pipe
|
|
exec {pipe}>&-
|
|
# for stdin
|
|
exec {pipe}<> <(:)
|
|
exec {in_r}</proc/self/fd/$pipe
|
|
exec {in_w}>/proc/self/fd/$pipe
|
|
exec {pipe}>&-
|
|
}
|
|
|
|
function check_pipes() {
|
|
local output stderr
|
|
|
|
echo Ping >&${in_w}
|
|
exec {in_w}>&-
|
|
exec {out_w}>&-
|
|
exec {err_w}>&-
|
|
|
|
exec {in_r}>&-
|
|
output=$(cat <&${out_r})
|
|
exec {out_r}>&-
|
|
stderr=$(cat <&${err_r})
|
|
exec {err_r}>&-
|
|
|
|
[[ "${output}" == *"ponG Ping"* ]]
|
|
if [ -n "$stderr" ]; then
|
|
fail "runc stderr: $stderr"
|
|
fi
|
|
}
|
|
|
|
# Usage: runc_run_with_pipes container-name
|
|
function runc_run_with_pipes() {
|
|
# Start a container to be checkpointed, with stdin/stdout redirected
|
|
# so that check_pipes can be used to check it's working fine.
|
|
# We have to redirect stderr as well because otherwise it is
|
|
# redirected to a bats log file, which is not accessible to CRIU
|
|
# (i.e. outside of container) so checkpointing will fail.
|
|
ret=0
|
|
__runc run -d "$1" <&${in_r} >&${out_w} 2>&${err_w} || ret=$?
|
|
if [ "$ret" -ne 0 ]; then
|
|
echo "runc run -d $1 (status: $ret):"
|
|
exec {err_w}>&-
|
|
cat <&${err_r}
|
|
fail "runc run failed"
|
|
fi
|
|
|
|
testcontainer "$1" running
|
|
}
|
|
|
|
# Usage: runc_restore_with_pipes work-dir container-name [optional-arguments ...]
|
|
function runc_restore_with_pipes() {
|
|
workdir="$1"
|
|
shift
|
|
name="$1"
|
|
shift
|
|
|
|
ret=0
|
|
__runc restore -d --work-path "$workdir" --image-path ./image-dir "$@" "$name" <&${in_r} >&${out_w} 2>&${err_w} || ret=$?
|
|
if [ "$ret" -ne 0 ]; then
|
|
echo "__runc restore $name failed (status: $ret)"
|
|
exec {err_w}>&-
|
|
cat <&${err_r}
|
|
fail "runc restore failed"
|
|
fi
|
|
|
|
testcontainer "$name" running
|
|
|
|
runc exec --cwd /bin "$name" echo ok
|
|
[ "$status" -eq 0 ]
|
|
[ "$output" = "ok" ]
|
|
}
|
|
|
|
function simple_cr() {
|
|
runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox
|
|
[ "$status" -eq 0 ]
|
|
|
|
testcontainer test_busybox running
|
|
|
|
for _ in $(seq 2); do
|
|
runc "$@" checkpoint --work-path ./work-dir test_busybox
|
|
[ "$status" -eq 0 ]
|
|
|
|
testcontainer test_busybox checkpointed
|
|
|
|
runc "$@" restore -d --work-path ./work-dir --console-socket "$CONSOLE_SOCKET" test_busybox
|
|
[ "$status" -eq 0 ]
|
|
|
|
testcontainer test_busybox running
|
|
done
|
|
}
|
|
|
|
@test "checkpoint and restore with netdevice" {
|
|
# Tell runc which network namespace to use.
|
|
create_netns
|
|
update_config '(.. | select(.type? == "network")) .path |= "'"$ns_path"'"'
|
|
update_config ' .linux.netDevices |= {"dummy0": {} }'
|
|
runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox_netdevice
|
|
[ "$status" -eq 0 ]
|
|
|
|
testcontainer test_busybox_netdevice running
|
|
runc exec test_busybox_netdevice ip address show dev dummy0
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *" $global_ip "* ]]
|
|
[[ "$output" == *"ether $mac_address "* ]]
|
|
[[ "$output" == *"mtu $mtu_value "* ]]
|
|
|
|
for _ in $(seq 2); do
|
|
runc checkpoint --work-path ./work-dir test_busybox_netdevice
|
|
[ "$status" -eq 0 ]
|
|
|
|
testcontainer test_busybox_netdevice checkpointed
|
|
|
|
runc restore -d --work-path ./work-dir --console-socket "$CONSOLE_SOCKET" test_busybox_netdevice
|
|
[ "$status" -eq 0 ]
|
|
|
|
testcontainer test_busybox_netdevice running
|
|
runc exec test_busybox_netdevice ip address show dev dummy0
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *" $global_ip "* ]]
|
|
[[ "$output" == *"ether $mac_address "* ]]
|
|
[[ "$output" == *"mtu $mtu_value "* ]]
|
|
done
|
|
}
|
|
|
|
@test "checkpoint and restore" {
|
|
simple_cr
|
|
}
|
|
|
|
@test "checkpoint and restore (bind mount, destination is symlink)" {
|
|
mkdir -p rootfs/real/conf
|
|
ln -s /real/conf rootfs/conf
|
|
update_config ' .mounts += [{
|
|
source: ".",
|
|
destination: "/conf",
|
|
options: ["bind"]
|
|
}]'
|
|
simple_cr
|
|
}
|
|
|
|
@test "checkpoint and restore (with --debug)" {
|
|
simple_cr --debug
|
|
}
|
|
|
|
@test "checkpoint and restore (cgroupns)" {
|
|
# cgroupv2 already enables cgroupns so this case was tested above already
|
|
requires cgroups_v1 cgroupns
|
|
|
|
# enable CGROUPNS
|
|
update_config '.linux.namespaces += [{"type": "cgroup"}]'
|
|
|
|
simple_cr
|
|
}
|
|
|
|
@test "checkpoint --pre-dump (bad --parent-path)" {
|
|
runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox
|
|
[ "$status" -eq 0 ]
|
|
|
|
testcontainer test_busybox running
|
|
|
|
# runc should fail with absolute parent image path.
|
|
runc checkpoint --parent-path "$(pwd)"/parent-dir --work-path ./work-dir --image-path ./image-dir test_busybox
|
|
[[ "${output}" == *"--parent-path"* ]]
|
|
[ "$status" -ne 0 ]
|
|
|
|
# runc should fail with invalid parent image path.
|
|
runc checkpoint --parent-path ./parent-dir --work-path ./work-dir --image-path ./image-dir test_busybox
|
|
[[ "${output}" == *"--parent-path"* ]]
|
|
[ "$status" -ne 0 ]
|
|
}
|
|
|
|
@test "checkpoint --pre-dump and restore" {
|
|
# Requires kernel dirty memory tracking (missing on ARM, see
|
|
# https://github.com/checkpoint-restore/criu/issues/1729).
|
|
requires criu_feature_mem_dirty_track
|
|
|
|
setup_pipes
|
|
runc_run_with_pipes test_busybox
|
|
|
|
mkdir parent-dir
|
|
runc checkpoint --pre-dump --image-path ./parent-dir test_busybox
|
|
[ "$status" -eq 0 ]
|
|
|
|
testcontainer test_busybox running
|
|
|
|
mkdir image-dir
|
|
mkdir work-dir
|
|
runc checkpoint --parent-path ../parent-dir --work-path ./work-dir --image-path ./image-dir test_busybox
|
|
[ "$status" -eq 0 ]
|
|
|
|
# check parent path is valid
|
|
[ -e ./image-dir/parent ]
|
|
|
|
testcontainer test_busybox checkpointed
|
|
|
|
runc_restore_with_pipes ./work-dir test_busybox
|
|
check_pipes
|
|
}
|
|
|
|
@test "checkpoint --lazy-pages and restore" {
|
|
# Requires lazy-pages support.
|
|
requires criu_feature_uffd-noncoop
|
|
|
|
setup_pipes
|
|
runc_run_with_pipes test_busybox
|
|
|
|
mkdir image-dir
|
|
mkdir work-dir
|
|
|
|
# For lazy migration we need to know when CRIU is ready to serve
|
|
# the memory pages via TCP.
|
|
exec {pipe}<> <(:)
|
|
# shellcheck disable=SC2094
|
|
exec {lazy_r}</proc/self/fd/$pipe {lazy_w}>/proc/self/fd/$pipe
|
|
exec {pipe}>&-
|
|
|
|
# TCP port for lazy migration
|
|
port=27277
|
|
|
|
__runc checkpoint \
|
|
--lazy-pages \
|
|
--page-server 0.0.0.0:${port} \
|
|
--status-fd ${lazy_w} \
|
|
--manage-cgroups-mode=ignore \
|
|
--work-path ./work-dir \
|
|
--image-path ./image-dir \
|
|
test_busybox &
|
|
cpt_pid=$!
|
|
|
|
# wait for lazy page server to be ready
|
|
out=$(timeout 2 dd if=/proc/self/fd/${lazy_r} bs=1 count=1 2>/dev/null | od)
|
|
exec {lazy_r}>&-
|
|
exec {lazy_w}>&-
|
|
# shellcheck disable=SC2116,SC2086
|
|
out=$(echo $out) # rm newlines
|
|
# expecting \0 which od prints as
|
|
[ "$out" = "0000000 000000 0000001" ]
|
|
|
|
# Check if inventory.img was written
|
|
[ -e image-dir/inventory.img ]
|
|
|
|
# Start CRIU in lazy-daemon mode
|
|
criu lazy-pages --page-server --address 127.0.0.1 --port ${port} -D image-dir &
|
|
lp_pid=$!
|
|
|
|
# Restore lazily from checkpoint.
|
|
#
|
|
# The restored container needs a different name and a different cgroup
|
|
# (and a different systemd unit name, in case systemd cgroup driver is
|
|
# used) as the checkpointed container is not yet destroyed. It is only
|
|
# destroyed at that point in time when the last page is lazily
|
|
# transferred to the destination.
|
|
#
|
|
# Killing the CRIU on the checkpoint side will let the container
|
|
# continue to run if the migration failed at some point.
|
|
runc_restore_with_pipes ./image-dir test_busybox_restore \
|
|
--lazy-pages \
|
|
--manage-cgroups-mode=ignore
|
|
|
|
wait $cpt_pid
|
|
|
|
wait $lp_pid
|
|
|
|
check_pipes
|
|
}
|
|
|
|
@test "checkpoint and restore in external network namespace" {
|
|
# Requires external network namespaces (criu >= 3.10).
|
|
requires criu_feature_external_net_ns
|
|
|
|
# create a temporary name for the test network namespace
|
|
tmp=$(mktemp)
|
|
rm -f "$tmp"
|
|
ns_name=$(basename "$tmp")
|
|
# create network namespace
|
|
ip netns add "$ns_name"
|
|
ns_path=$(ip netns add "$ns_name" 2>&1 | sed -e 's/.*"\(.*\)".*/\1/')
|
|
# shellcheck disable=SC2012
|
|
ns_inode=$(ls -iL "$ns_path" | awk '{ print $1 }')
|
|
|
|
# tell runc which network namespace to use
|
|
update_config '(.. | select(.type? == "network")) .path |= "'"$ns_path"'"'
|
|
|
|
runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox
|
|
[ "$status" -eq 0 ]
|
|
|
|
testcontainer test_busybox running
|
|
|
|
for _ in $(seq 2); do
|
|
# checkpoint the running container; this automatically tells CRIU to
|
|
# handle the network namespace defined in config.json as an external
|
|
runc checkpoint --work-path ./work-dir test_busybox
|
|
[ "$status" -eq 0 ]
|
|
|
|
testcontainer test_busybox checkpointed
|
|
|
|
# restore from checkpoint; this should restore the container into the existing network namespace
|
|
runc restore -d --work-path ./work-dir --console-socket "$CONSOLE_SOCKET" test_busybox
|
|
[ "$status" -eq 0 ]
|
|
|
|
testcontainer test_busybox running
|
|
|
|
# container should be running in same network namespace as before
|
|
pid=$(__runc state test_busybox | jq '.pid')
|
|
ns_inode_new=$(readlink /proc/"$pid"/ns/net | sed -e 's/.*\[\(.*\)\]/\1/')
|
|
echo "old network namespace inode $ns_inode"
|
|
echo "new network namespace inode $ns_inode_new"
|
|
[ "$ns_inode" -eq "$ns_inode_new" ]
|
|
done
|
|
ip netns del "$ns_name"
|
|
}
|
|
|
|
@test "checkpoint and restore with container specific CRIU config" {
|
|
tmp=$(mktemp /tmp/runc-criu-XXXXXX.conf)
|
|
# This is the file we write to /etc/criu/default.conf
|
|
tmplog1=$(mktemp /tmp/runc-criu-log-XXXXXX.log)
|
|
unlink "$tmplog1"
|
|
tmplog1=$(basename "$tmplog1")
|
|
# That is the actual configuration file to be used
|
|
tmplog2=$(mktemp /tmp/runc-criu-log-XXXXXX.log)
|
|
unlink "$tmplog2"
|
|
tmplog2=$(basename "$tmplog2")
|
|
# This adds the annotation 'org.criu.config' to set a container
|
|
# specific CRIU config file.
|
|
update_config '.annotations += {"org.criu.config": "'"$tmp"'"}'
|
|
|
|
# Tell CRIU to use another configuration file
|
|
mkdir -p /etc/criu
|
|
echo "log-file=$tmplog1" >/etc/criu/default.conf
|
|
# Make sure the RPC defined configuration file overwrites the previous
|
|
echo "log-file=$tmplog2" >"$tmp"
|
|
|
|
runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox
|
|
[ "$status" -eq 0 ]
|
|
|
|
testcontainer test_busybox running
|
|
|
|
runc checkpoint --work-path ./work-dir test_busybox
|
|
[ "$status" -eq 0 ]
|
|
run ! test -f ./work-dir/"$tmplog1"
|
|
test -f ./work-dir/"$tmplog2"
|
|
|
|
testcontainer test_busybox checkpointed
|
|
|
|
test -f ./work-dir/"$tmplog2" && unlink ./work-dir/"$tmplog2"
|
|
|
|
runc restore -d --work-path ./work-dir --console-socket "$CONSOLE_SOCKET" test_busybox
|
|
[ "$status" -eq 0 ]
|
|
run ! test -f ./work-dir/"$tmplog1"
|
|
test -f ./work-dir/"$tmplog2"
|
|
|
|
testcontainer test_busybox running
|
|
unlink "$tmp"
|
|
test -f ./work-dir/"$tmplog2" && unlink ./work-dir/"$tmplog2"
|
|
}
|
|
|
|
@test "checkpoint and restore with nested bind mounts" {
|
|
bind1=$(mktemp -d -p .)
|
|
bind2=$(mktemp -d -p .)
|
|
update_config ' .mounts += [{
|
|
type: "bind",
|
|
source: "'"$bind1"'",
|
|
destination: "/test",
|
|
options: ["rw", "bind"]
|
|
},
|
|
{
|
|
type: "bind",
|
|
source: "'"$bind2"'",
|
|
destination: "/test/for/nested",
|
|
options: ["rw", "bind"]
|
|
}]'
|
|
|
|
runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox
|
|
[ "$status" -eq 0 ]
|
|
|
|
testcontainer test_busybox running
|
|
|
|
runc checkpoint --work-path ./work-dir test_busybox
|
|
[ "$status" -eq 0 ]
|
|
|
|
testcontainer test_busybox checkpointed
|
|
|
|
# cleanup mountpoints created by runc during creation
|
|
# the mountpoints should be recreated during restore - that is the actual thing tested here
|
|
rm -rf "${bind1:?}"/*
|
|
|
|
runc restore -d --work-path ./work-dir --console-socket "$CONSOLE_SOCKET" test_busybox
|
|
[ "$status" -eq 0 ]
|
|
|
|
testcontainer test_busybox running
|
|
}
|
|
|
|
@test "checkpoint then restore into a different cgroup (via --manage-cgroups-mode ignore)" {
|
|
set_resources_limit
|
|
set_cgroups_path
|
|
runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox
|
|
[ "$status" -eq 0 ]
|
|
testcontainer test_busybox running
|
|
|
|
local orig_path
|
|
orig_path=$(get_cgroup_path "pids")
|
|
# Check that the cgroup exists.
|
|
test -d "$orig_path"
|
|
|
|
runc checkpoint --work-path ./work-dir --manage-cgroups-mode ignore test_busybox
|
|
[ "$status" -eq 0 ]
|
|
testcontainer test_busybox checkpointed
|
|
# Check that the cgroup is gone.
|
|
run ! test -d "$orig_path"
|
|
|
|
# Restore into a different cgroup.
|
|
set_cgroups_path # Changes the path.
|
|
runc restore -d --manage-cgroups-mode ignore --pid-file pid \
|
|
--work-path ./work-dir --console-socket "$CONSOLE_SOCKET" test_busybox
|
|
[ "$status" -eq 0 ]
|
|
testcontainer test_busybox running
|
|
|
|
# Check that the old cgroup path doesn't exist.
|
|
run ! test -d "$orig_path"
|
|
|
|
# Check that the new path exists.
|
|
local new_path
|
|
new_path=$(get_cgroup_path "pids")
|
|
test -d "$new_path"
|
|
|
|
# Check that container's init is in the new cgroup.
|
|
local pid
|
|
pid=$(cat "pid")
|
|
grep -q "${REL_CGROUPS_PATH}$" "/proc/$pid/cgroup"
|
|
}
|
|
|
|
@test "checkpoint/restore and exec" {
|
|
runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox
|
|
[ "$status" -eq 0 ]
|
|
|
|
testcontainer test_busybox running
|
|
|
|
local execed_pid=""
|
|
for _ in $(seq 2); do
|
|
runc checkpoint --work-path ./work-dir test_busybox
|
|
[ "$status" -eq 0 ]
|
|
|
|
testcontainer test_busybox checkpointed
|
|
|
|
runc restore -d --work-path ./work-dir --console-socket "$CONSOLE_SOCKET" test_busybox
|
|
[ "$status" -eq 0 ]
|
|
|
|
testcontainer test_busybox running
|
|
|
|
# verify that previously exec'd process is restored.
|
|
if [ -n "$execed_pid" ]; then
|
|
runc exec test_busybox ls -ld "/proc/$execed_pid"
|
|
[ "$status" -eq 0 ]
|
|
fi
|
|
|
|
# exec a new background process.
|
|
runc exec test_busybox sh -c 'sleep 1000 < /dev/null &> /dev/null & echo $!'
|
|
[ "$status" -eq 0 ]
|
|
execed_pid=$output
|
|
done
|
|
}
|