#!/usr/bin/env bats -*- bats -*- # # test podman checkpoint. Similar in many ways to our pause tests. # load helpers load helpers.network CHECKED_ROOTLESS= function setup() { # None of these tests work rootless.... if is_rootless; then # ...however, is that a genuine cast-in-stone limitation, or one # that can some day be fixed? If one day some PR removes that # restriction, fail loudly here, so the developer can enable tests. if [[ -n "$CHECKED_ROOTLESS" ]]; then run_podman '?' container checkpoint -l is "$output" "Error: checkpointing a container requires root" \ "Confirming that rootless checkpoint doesn't work. If that changed, please reexamine this test file!" CHECKED_ROOTLESS=y fi skip "checkpoint does not work rootless" fi # As of 2024-05, crun on Debian is not built with criu support: # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1008249 runtime=$(podman_runtime) run $runtime checkpoint --help if [[ $status -ne 0 ]]; then skip "runtime $runtime does not support checkpoint/restore" fi basic_setup } # bats test_tags=ci:parallel @test "podman checkpoint - basic test" { run_podman run -d $IMAGE sh -c 'while :;do cat /proc/uptime; sleep 0.1;done' local cid="$output" # Wait for container to start emitting output wait_for_output '[1-9]\+' $cid # Checkpoint, and confirm via inspect run_podman container checkpoint $cid # FIXME: remove the `.*` prefix after fix packaged for https://github.com/checkpoint-restore/criu/pull/1706 is "$output" ".*$cid" "podman container checkpoint" run_podman container inspect \ --format '{{.State.Status}}:{{.State.Running}}:{{.State.Paused}}:{{.State.Checkpointed}}' $cid is "$output" "exited:false:false:true" "State. Status:Running:Pause:Checkpointed" # Plan A was to do something similar to 080-pause.bats: sleep for long # enough to cause a gap in the timestamps in the log. But checkpoint # doesn't seem to work like that: upon restore, even if we sleep a long # time, the newly-started container seems to pick back up close to # where it left off. (Maybe it's something about /proc/uptime?) # Anyway, scratch Plan A. Plan B is simply to make sure that the # restarted container spits something out. run_podman logs $cid local nlines_before="${#lines[*]}" # Restart immediately and confirm state run_podman container restore $cid is "$output" "$cid" "podman container restore" # Note that upon restore, .Checkpointed reverts to false (#12117) run_podman container inspect \ --format '{{.State.Status}}:{{.State.Running}}:{{.State.Paused}}:{{.State.Checkpointed}}' $cid is "$output" "running:true:false:false" \ "State. Status:Running:Pause:Checkpointed" # Re-fetch logs, and ensure that they continue growing. # Allow a short while for container process to actually restart. local retries=10 while [[ $retries -gt 0 ]]; do run_podman logs $cid local nlines_after="${#lines[*]}" if [[ $nlines_after -gt $nlines_before ]]; then break fi sleep 0.1 retries=$((retries - 1)) done assert "$retries" -gt 0 \ "Container failed to output new lines after first restore" # Same thing again: test for https://github.com/containers/crun/issues/756 # in which, after second checkpoint/restore, we lose logs run_podman container checkpoint $cid run_podman container logs $cid nlines_before="${#lines[*]}" run_podman container restore $cid # Same as above, confirm that we get new output retries=10 while [[ $retries -gt 0 ]]; do run_podman logs $cid local nlines_after="${#lines[*]}" if [[ $nlines_after -gt $nlines_before ]]; then break fi sleep 0.1 retries=$((retries - 1)) done assert "$retries" -gt 0 \ "stdout went away after second restore (crun issue 756)" run_podman rm -t 0 -f $cid } # CANNOT BE PARALLELIZED: checkpoint -a @test "podman checkpoint/restore print IDs or raw input" { # checkpoint/restore -a must print the IDs run_podman run -d $IMAGE top ctrID="$output" run_podman container checkpoint -a is "$output" "$ctrID" run_podman container restore -a is "$output" "$ctrID" # checkpoint/restore $input must print $input cname=c-$(safename) run_podman run -d --name $cname $IMAGE top run_podman container checkpoint $cname is "$output" $cname run_podman container restore $cname is "$output" $cname run_podman rm -t 0 -f $ctrID $cname } # bats test_tags=ci:parallel @test "podman checkpoint --export, with volumes" { skip "FIXME: #26289 - Rawhide only issue, skip for now" skip_if_remote "Test uses --root/--runroot, which are N/A over remote" local p_opts="$(podman_isolation_opts ${PODMAN_TMPDIR}) --events-backend file" # prefetch image to avoid registry pulls because this is using a # unique root which does not have the image already present. # _PODMAN_TEST_OPTS is used to overwrite the podman options to # make the function aware of the custom --root. _PODMAN_TEST_OPTS="$p_opts --storage-driver $(podman_storage_driver)" _prefetch $IMAGE # Create a volume, find unused network port, and create a webserv container volname=v-$(safename) run_podman $p_opts volume create $volname local cname=c-$(safename) local host_port=$(random_free_port) local server=http://127.0.0.1:$host_port run_podman $p_opts run -d --name $cname --volume $volname:/myvol \ -p $host_port:80 \ -w /myvol \ $IMAGE sh -c "/bin/busybox-extras httpd -p 80;echo $cname >cname;echo READY;while :;do cat /proc/uptime >mydate.tmp;mv -f mydate.tmp mydate;sleep 0.1;done" local cid="$output" _PODMAN_TEST_OPTS="$p_opts" wait_for_ready $cid # Confirm that container responds run curl --max-time 3 -s $server/cname is "$output" "$cname" "curl $server/cname" run curl --max-time 3 -s $server/mydate local date_oldroot="$output" # Checkpoint... run_podman $p_opts container checkpoint \ --ignore-rootfs \ --export=$PODMAN_TMPDIR/$cname.tar.gz \ $cname # ...confirm that port is now closed run curl --max-time 1 -s $server/mydate is "$status" "7" "cannot connect to port $host_port while container is down" # ...now restore it to our regular root run_podman container restore --import=$PODMAN_TMPDIR/$cname.tar.gz is "$output" "$cid" # Inspect (on regular root). Note that, unlike the basic test above, # .State.Checkpointed here is *false*. run_podman container inspect \ --format '{{.State.Status}}:{{.State.Running}}:{{.State.Paused}}:{{.State.Checkpointed}}' $cname is "$output" "running:true:false:false" "State. Status:Running:Pause:Checkpointed" # Pause a moment to let the restarted container update the timestamp file sleep .3 run curl --max-time 3 -s $server/mydate local date_newroot="$output" assert "$date_newroot" != "$date_oldroot" \ "Restored container did not update the timestamp file" run_podman exec $cid cat /myvol/cname is "$output" "$cname" "volume transferred fine" run_podman rm -t 0 -f $cid run_podman volume rm -f $volname } # FIXME: test --leave-running # bats test_tags=ci:parallel @test "podman checkpoint --file-locks" { action='flock test.lock sh -c "while [ -e /wait ];do sleep 0.5;done;for i in 1 2 3;do echo \$i;sleep 0.5;done"' run_podman run -d $IMAGE sh -c "touch /wait; touch test.lock; echo READY; $action & $action & wait" local cid="$output" # Wait for container to start emitting output wait_for_ready $cid # Checkpoint, and confirm via inspect run_podman container checkpoint --file-locks $cid is "$output" "$cid" "podman container checkpoint" run_podman container inspect \ --format '{{.State.Status}}:{{.State.Running}}:{{.State.Paused}}:{{.State.Checkpointed}}' $cid is "$output" "exited:false:false:true" "State. Status:Running:Pause:Checkpointed" # Restart immediately and confirm state run_podman container restore --file-locks $cid is "$output" "$cid" "podman container restore" # Signal the container to continue; this is where the 1-2-3s will come from # The '-d' is because container exit is racy: the exec process itself # could get caught and killed by cleanup, causing this step to exit 137 run_podman exec -d $cid rm /wait # Wait for the container to stop run_podman wait $cid run_podman logs $cid trim=$(sed -z -e 's/[\r\n]\+//g' <<<"$output") is "$trim" "READY123123" "File lock restored" run_podman rm $cid } # bats test_tags=ci:parallel @test "podman checkpoint/restore ip and mac handling" { # Refer to https://github.com/containers/podman/issues/16666#issuecomment-1337860545 # for the correct behavior, this should cover all cases listed there. local netname="net-$(safename)" local subnet="$(random_rfc1918_subnet)" run_podman network create --subnet "$subnet.0/24" $netname run_podman run -d --network $netname $IMAGE top cid="$output" # get current ip and mac run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").IPAddress}}" ip1="$output" run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").MacAddress}}" mac1="$output" # There is a weird flake, where the hosts content changed after restore and I don't know why. # Because we start from a /etc/hosts base on the host print that. echo "hosts file on the host" cat /etc/hosts run_podman exec $cid cat /etc/hosts /etc/resolv.conf pre_hosts_resolv_conf_output="$output" run_podman container checkpoint $cid is "$output" "$cid" run_podman container restore $cid is "$output" "$cid" # now get mac and ip after restore they should be the same run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").IPAddress}}" ip2="$output" run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").MacAddress}}" mac2="$output" # Make sure hosts and resolv.conf are the same after restore (#22901) run_podman exec $cid cat /etc/hosts /etc/resolv.conf assert "$output" == "$pre_hosts_resolv_conf_output" "hosts/resolv.conf must be the same after checkpoint" assert "$ip2" == "$ip1" "ip after restore should match" assert "$mac2" == "$mac1" "mac after restore should match" # restart the container we should get a new ip/mac because they are not static run_podman restart $cid run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").IPAddress}}" ip3="$output" run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").MacAddress}}" mac3="$output" # the ip/mac should be different this time assert "$ip3" != "$ip1" "ip after restart should be different" assert "$mac3" != "$mac1" "mac after restart should be different" # restore with --ignore-static-ip/mac run_podman container checkpoint $cid is "$output" "$cid" run_podman container restore --ignore-static-ip --ignore-static-mac $cid is "$output" "$cid" run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").IPAddress}}" ip4="$output" run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").MacAddress}}" mac4="$output" # the ip/mac should be different this time assert "$ip4" != "$ip3" "ip after restore --ignore-static-ip should be different" assert "$mac4" != "$mac3" "mac after restore --ignore-static-mac should be different" local archive=$PODMAN_TMPDIR/checkpoint.tar.gz # now checkpoint and export the container run_podman container checkpoint --export "$archive" $cid is "$output" "$cid" # remove container run_podman rm -t 0 -f $cid # restore it without new name should keep the ip/mac, we also get a new container id run_podman container restore --import "$archive" cid="$output" run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").IPAddress}}" ip5="$output" run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").MacAddress}}" mac5="$output" assert "$ip5" == "$ip4" "ip after restore --import should match" assert "$mac5" == "$mac4" "mac after restore --import should match" run_podman rm -t 0 -f $cid # now restore it again but with --name this time, it should not keep the # mac and ip to allow restoring the same container with different names # at the same time newname="newc-$(safename)" run_podman container restore --import "$archive" --name $newname cid="$output" run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").IPAddress}}" ip6="$output" run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").MacAddress}}" mac6="$output" assert "$ip6" != "$ip5" "ip after restore --import --name should be different" assert "$mac6" != "$mac5" "mac after restore --import --name should be different" run_podman rm -t 0 -f $cid # now create a container with a static mac and ip local static_ip="$subnet.2" local static_mac="92:d0:c6:0a:29:38" run_podman run -d --network "$netname:ip=$static_ip,mac=$static_mac" $IMAGE top cid="$output" run_podman container checkpoint $cid is "$output" "$cid" run_podman container restore --ignore-static-ip --ignore-static-mac $cid is "$output" "$cid" run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").IPAddress}}" ip7="$output" run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").MacAddress}}" mac7="$output" assert "$ip7" != "$static_ip" "static ip after restore --ignore-static-ip should be different" assert "$mac7" != "$static_mac" "static mac after restore --ignore-static-mac should be different" # restart the container to make sure the change is actually persistent in the config and not just set for restore run_podman restart $cid run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").IPAddress}}" ip8="$output" run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").MacAddress}}" mac8="$output" assert "$ip8" != "$static_ip" "static ip after restore --ignore-static-ip and restart should be different" assert "$mac8" != "$static_mac" "static mac after restore --ignore-static-mac and restart should be different" assert "$ip8" != "$ip7" "static ip after restore --ignore-static-ip and restart should be different" assert "$mac8" != "$ip" "static mac after restore --ignore-static-mac and restart should be different" run_podman rm -t 0 -f $cid # now create container again and try the same again with --export and --import run_podman run -d --network "$netname:ip=$static_ip,mac=$static_mac" $IMAGE top cid="$output" run_podman container checkpoint --export "$archive" $cid is "$output" "$cid" # remove container run_podman rm -t 0 -f $cid # restore normal should keep static ip run_podman container restore --import "$archive" cid="$output" run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").IPAddress}}" ip9="$output" run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").MacAddress}}" mac9="$output" assert "$ip9" == "$static_ip" "static ip after restore --import should match" assert "$mac9" == "$static_mac" "static mac after restore --import should match" # restart the container to make sure the change is actually persistent in the config and not just set for restore run_podman restart $cid run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").IPAddress}}" ip10="$output" run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").MacAddress}}" mac10="$output" assert "$ip10" == "$static_ip" "static ip after restore --import and restart should match" assert "$mac10" == "$static_mac" "static mac after restore --import and restart should match" run_podman rm -t 0 -f $cid # restore normal without keeping static ip/mac run_podman container restore --ignore-static-ip --ignore-static-mac --import "$archive" cid="$output" run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").IPAddress}}" ip11="$output" run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").MacAddress}}" mac11="$output" assert "$ip11" != "$static_ip" "static ip after restore --import --ignore-static-ip should be different" assert "$mac11" != "$static_mac" "static mac after restore --import --ignore-static-mac should be different" # restart the container to make sure the change is actually persistent in the config and not just set for restore run_podman restart $cid run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").IPAddress}}" ip12="$output" run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").MacAddress}}" mac12="$output" assert "$ip12" != "$static_ip" "static ip after restore --import --ignore-static-ip and restart should be different" assert "$mac12" != "$static_mac" "static mac after restore --ignore-static-mac and restart should be different" assert "$ip12" != "$ip11" "static ip after restore --import --ignore-static-ip and restart should be different" assert "$mac12" != "$ip11" "static mac after restore --ignore-static-mac and restart should be different" run_podman rm -t 0 -f $cid run_podman network rm $netname } # rhbz#2177611 : podman breaks checkpoint/restore # CANNOT BE PARALLELIZED: --latest @test "podman checkpoint/restore the latest container" { skip_if_remote "podman-remote does not support --latest option" # checkpoint/restore -l must print the IDs run_podman run -d $IMAGE top ctrID="$output" run_podman container checkpoint --latest is "$output" "$ctrID" run_podman container inspect \ --format '{{.State.Status}}:{{.State.Running}}:{{.State.Paused}}:{{.State.Checkpointed}}' $ctrID is "$output" "exited:false:false:true" "State. Status:Running:Pause:Checkpointed" run_podman container restore -l is "$output" "$ctrID" run_podman container inspect \ --format '{{.State.Status}}:{{.State.Running}}:{{.State.Paused}}:{{.State.Checkpointed}}' $ctrID is "$output" "running:true:false:false" "State. Status:Running:Pause:Checkpointed" run_podman rm -t 0 -f $ctrID } # vim: filetype=sh