podman-5.4.0-8.el9
- Enable gating tests via TMT - Related: RHEL-80816 Signed-off-by: Lokesh Mandvekar <lsm5@fedoraproject.org>
This commit is contained in:
parent
bee13ddaf0
commit
2c2e94ad66
426
220-healthcheck.bats
Normal file
426
220-healthcheck.bats
Normal file
@ -0,0 +1,426 @@
|
||||
#!/usr/bin/env bats -*- bats -*-
|
||||
#
|
||||
# tests for podman healthcheck
|
||||
#
|
||||
#
|
||||
|
||||
load helpers
|
||||
load helpers.systemd
|
||||
|
||||
# bats file_tags=ci:parallel
|
||||
|
||||
# Helper function: run 'podman inspect' and check various given fields
|
||||
function _check_health {
|
||||
local ctrname="$1"
|
||||
local testname="$2"
|
||||
local tests="$3"
|
||||
local since="$4"
|
||||
local hc_status="$5"
|
||||
|
||||
# Loop-wait (up to a few seconds) for healthcheck event (#20342)
|
||||
# Allow a margin when running parallel, because of system load
|
||||
local timeout=5
|
||||
if [[ -n "$PARALLEL_JOBSLOT" ]]; then
|
||||
timeout=$((timeout + 3))
|
||||
fi
|
||||
|
||||
while :; do
|
||||
run_podman events --filter container=$ctrname --filter event=health_status \
|
||||
--since "$since" --stream=false --format "{{.HealthStatus}}"
|
||||
# Output may be empty or multiple lines.
|
||||
if [[ -n "$output" ]]; then
|
||||
if [[ "${lines[-1]}" = "$hc_status" ]]; then
|
||||
break
|
||||
fi
|
||||
fi
|
||||
|
||||
timeout=$((timeout - 1))
|
||||
if [[ $timeout -eq 0 ]]; then
|
||||
die "$testname - timed out waiting for '$hc_status' in podman events"
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Got the desired status. Now verify all the healthcheck fields
|
||||
run_podman inspect --format "{{json .State.Healthcheck}}" $ctrname
|
||||
|
||||
defer-assertion-failures
|
||||
parse_table "$tests" | while read field expect;do
|
||||
actual=$(jq ".$field" <<<"$output")
|
||||
is "$actual" "$expect" "$testname - .State.Healthcheck.$field"
|
||||
done
|
||||
immediate-assertion-failures
|
||||
}
|
||||
|
||||
@test "podman healthcheck" {
|
||||
local ctrname="c-h-$(safename)"
|
||||
run_podman run -d --name $ctrname \
|
||||
--health-cmd /home/podman/healthcheck \
|
||||
--health-interval 1s \
|
||||
--health-retries 3 \
|
||||
--health-on-failure=kill \
|
||||
--health-startup-cmd /home/podman/healthcheck \
|
||||
--health-startup-interval 1s \
|
||||
$IMAGE /home/podman/pause
|
||||
cid="$output"
|
||||
|
||||
run_podman inspect $ctrname --format "{{.Config.HealthcheckOnFailureAction}}"
|
||||
is "$output" "kill" "on-failure action is set to kill"
|
||||
|
||||
run_podman inspect $ctrname --format "{{.Config.StartupHealthCheck.Test}}"
|
||||
is "$output" "[CMD-SHELL /home/podman/healthcheck]" ".Config.StartupHealthCheck.Test"
|
||||
|
||||
current_time=$(date --iso-8601=ns)
|
||||
# We can't check for 'starting' because a 1-second interval is too
|
||||
# short; it could run healthcheck before we get to our first check.
|
||||
#
|
||||
# So, just force a healthcheck run, then confirm that it's running.
|
||||
run_podman healthcheck run $ctrname
|
||||
is "$output" "" "output from 'podman healthcheck run'"
|
||||
|
||||
_check_health $ctrname "All healthy" "
|
||||
Status | \"healthy\"
|
||||
FailingStreak | 0
|
||||
Log[-1].ExitCode | 0
|
||||
Log[-1].Output | \"Life is Good on stdout\\\nLife is Good on stderr\\\n\"
|
||||
" "$current_time" "healthy"
|
||||
|
||||
current_time=$(date --iso-8601=ns)
|
||||
# Force a failure
|
||||
run_podman exec $ctrname touch /uh-oh
|
||||
|
||||
_check_health $ctrname "First failure" "
|
||||
Status | \"healthy\"
|
||||
FailingStreak | [123]
|
||||
Log[-1].ExitCode | 1
|
||||
Log[-1].Output | \"Uh-oh on stdout!\\\nUh-oh on stderr!\\\n\"
|
||||
" "$current_time" "healthy"
|
||||
|
||||
# Check that we now we do have valid podman units with this
|
||||
# name so that the leak check below does not turn into a NOP without noticing.
|
||||
run -0 systemctl list-units
|
||||
cidmatch=$(grep "$cid" <<<"$output")
|
||||
echo "$cidmatch"
|
||||
assert "$cidmatch" =~ " $cid-[0-9a-f]+\.timer *.*/podman healthcheck run $cid" \
|
||||
"Healthcheck systemd unit exists"
|
||||
|
||||
current_time=$(date --iso-8601=ns)
|
||||
# After three successive failures, container should no longer be healthy
|
||||
_check_health $ctrname "Four or more failures" "
|
||||
Status | \"unhealthy\"
|
||||
FailingStreak | [3456]
|
||||
Log[-1].ExitCode | 1
|
||||
Log[-1].Output | \"Uh-oh on stdout!\\\nUh-oh on stderr!\\\n\"
|
||||
" "$current_time" "unhealthy"
|
||||
|
||||
# now the on-failure should kick in and kill the container
|
||||
run_podman wait $ctrname
|
||||
|
||||
# Clean up
|
||||
run_podman rm -t 0 -f $ctrname
|
||||
|
||||
# Important check for https://github.com/containers/podman/issues/22884
|
||||
# We never should leak the unit files, healthcheck uses the cid in name so just grep that.
|
||||
# (Ignore .scope units, those are conmon and can linger for 5 minutes)
|
||||
# (Ignore .mount, too. They are created/removed by systemd based on the actual real mounts
|
||||
# on the host and that is async and might be slow enough in CI to cause failures.)
|
||||
run -0 systemctl list-units --quiet "*$cid*"
|
||||
except_scope_mount=$(grep -vF ".scope " <<<"$output" | { grep -vF ".mount" || true; } )
|
||||
assert "$except_scope_mount" == "" "Healthcheck systemd unit cleanup: no units leaked"
|
||||
}
|
||||
|
||||
@test "podman healthcheck - restart cleans up old state" {
|
||||
ctr="c-h-$(safename)"
|
||||
|
||||
run_podman run -d --name $ctr \
|
||||
--health-cmd /home/podman/healthcheck \
|
||||
--health-retries=3 \
|
||||
--health-interval=disable \
|
||||
$IMAGE /home/podman/pause
|
||||
|
||||
run_podman container inspect $ctr --format "{{.State.Healthcheck.FailingStreak}}"
|
||||
is "$output" "0" "Failing streak of fresh container should be 0"
|
||||
|
||||
# Get the healthcheck to fail
|
||||
run_podman exec $ctr touch /uh-oh-only-once
|
||||
run_podman 1 healthcheck run $ctr
|
||||
is "$output" "unhealthy" "output from 'podman healthcheck run'"
|
||||
run_podman container inspect $ctr --format "{{.State.Healthcheck.FailingStreak}}"
|
||||
is "$output" "1" "Failing streak after one failed healthcheck should be 1"
|
||||
|
||||
run_podman container restart $ctr
|
||||
run_podman container inspect $ctr --format "{{.State.Healthcheck.FailingStreak}}"
|
||||
is "$output" "0" "Failing streak of restarted container should be 0 again"
|
||||
|
||||
run_podman rm -f -t0 $ctr
|
||||
}
|
||||
|
||||
@test "podman wait --condition={healthy,unhealthy}" {
|
||||
ctr="c-h-$(safename)"
|
||||
|
||||
wait_file="$PODMAN_TMPDIR/$(random_string).wait_for_me"
|
||||
|
||||
for condition in healthy unhealthy;do
|
||||
rm -f $wait_file
|
||||
run_podman run -d --name $ctr \
|
||||
--health-cmd /home/podman/healthcheck \
|
||||
--health-retries=1 \
|
||||
--health-interval=disable \
|
||||
$IMAGE /home/podman/pause
|
||||
if [[ $condition == "unhealthy" ]];then
|
||||
# create the uh-oh file to let the health check fail
|
||||
run_podman exec $ctr touch /uh-oh
|
||||
fi
|
||||
|
||||
# Wait for the container in the background and create the $wait_file to
|
||||
# signal the specified wait condition was met.
|
||||
(timeout --foreground -v --kill=5 10 $PODMAN wait --condition=$condition $ctr && touch $wait_file) &
|
||||
|
||||
# Sleep 1 second to make sure above commands are running
|
||||
sleep 1
|
||||
if [[ -f $wait_file ]]; then
|
||||
die "the wait file should only be created after the container turned healthy"
|
||||
fi
|
||||
|
||||
if [[ $condition == "healthy" ]];then
|
||||
run_podman healthcheck run $ctr
|
||||
else
|
||||
run_podman 1 healthcheck run $ctr
|
||||
fi
|
||||
wait_for_file $wait_file
|
||||
run_podman rm -f -t0 $ctr
|
||||
done
|
||||
}
|
||||
|
||||
@test "podman healthcheck --health-on-failure" {
|
||||
run_podman 125 create --health-on-failure=kill $IMAGE
|
||||
is "$output" "Error: cannot set on-failure action to kill without a health check"
|
||||
|
||||
ctr="c-h-$(safename)"
|
||||
|
||||
for policy in none kill restart stop;do
|
||||
uhoh=/uh-oh
|
||||
if [[ $policy != "none" ]];then
|
||||
# only fail the first run
|
||||
uhoh=/uh-oh-only-once
|
||||
fi
|
||||
|
||||
# Run healthcheck image.
|
||||
run_podman run -d --name $ctr \
|
||||
--health-cmd /home/podman/healthcheck \
|
||||
--health-retries=1 \
|
||||
--health-on-failure=$policy \
|
||||
--health-interval=disable \
|
||||
$IMAGE /home/podman/pause
|
||||
|
||||
# healthcheck should succeed
|
||||
run_podman healthcheck run $ctr
|
||||
|
||||
# Now cause the healthcheck to fail
|
||||
run_podman exec $ctr touch $uhoh
|
||||
|
||||
# healthcheck should now fail, with exit status 1 and 'unhealthy' output
|
||||
run_podman 1 healthcheck run $ctr
|
||||
is "$output" "unhealthy" "output from 'podman healthcheck run' (policy: $policy)"
|
||||
|
||||
if [[ $policy == "restart" ]];then
|
||||
# Make sure the container transitions back to running
|
||||
run_podman wait --condition=running $ctr
|
||||
run_podman inspect $ctr --format "{{.RestartCount}}"
|
||||
assert "${#lines[@]}" != 0 "Container has been restarted at least once"
|
||||
run_podman container inspect $ctr --format "{{.State.Healthcheck.FailingStreak}}"
|
||||
is "$output" "0" "Failing streak of restarted container should be 0 again"
|
||||
run_podman healthcheck run $ctr
|
||||
elif [[ $policy == "none" ]];then
|
||||
run_podman inspect $ctr --format "{{.State.Status}} {{.Config.HealthcheckOnFailureAction}}"
|
||||
# Container is still running and health check still broken
|
||||
is "$output" "running $policy" "container continued running"
|
||||
run_podman 1 healthcheck run $ctr
|
||||
is "$output" "unhealthy" "output from 'podman healthcheck run' (policy: $policy)"
|
||||
else
|
||||
run_podman inspect $ctr --format "{{.State.Status}} {{.Config.HealthcheckOnFailureAction}}"
|
||||
# kill and stop yield the container into a non-running state
|
||||
is "$output" ".* $policy" "container was stopped/killed (policy: $policy)"
|
||||
assert "$output" != "running $policy"
|
||||
# also make sure that it's not stuck in the stopping state
|
||||
assert "$output" != "stopping $policy"
|
||||
fi
|
||||
|
||||
run_podman rm -f -t0 $ctr
|
||||
done
|
||||
}
|
||||
|
||||
@test "podman healthcheck --health-on-failure with interval" {
|
||||
ctr="c-h-$(safename)"
|
||||
|
||||
for policy in stop kill restart ;do
|
||||
t0=$(date --iso-8601=seconds)
|
||||
run_podman run -d --name $ctr \
|
||||
--health-cmd /bin/false \
|
||||
--health-retries=1 \
|
||||
--health-on-failure=$policy \
|
||||
--health-interval=1s \
|
||||
$IMAGE top
|
||||
|
||||
if [[ $policy == "restart" ]];then
|
||||
# Sleeping for 2 seconds makes the test much faster than using
|
||||
# podman-wait which would compete with the container getting
|
||||
# restarted.
|
||||
sleep 2
|
||||
# Make sure the container transitions back to running
|
||||
run_podman wait --condition=running $ctr
|
||||
run_podman inspect $ctr --format "{{.RestartCount}}"
|
||||
assert "${#lines[@]}" != 0 "Container has been restarted at least once"
|
||||
else
|
||||
# kill and stop yield the container into a non-running state
|
||||
run_podman wait $ctr
|
||||
run_podman inspect $ctr --format "{{.State.Status}} {{.Config.HealthcheckOnFailureAction}}"
|
||||
is "$output" ".* $policy" "container was stopped/killed (policy: $policy)"
|
||||
assert "$output" != "running $policy"
|
||||
# also make sure that it's not stuck in the stopping state
|
||||
assert "$output" != "stopping $policy"
|
||||
fi
|
||||
|
||||
run_podman rm -f -t0 $ctr
|
||||
done
|
||||
}
|
||||
|
||||
function _create_container_with_health_log_settings {
|
||||
local ctrname="$1"
|
||||
local msg="$2"
|
||||
local format="$3"
|
||||
local flag="$4"
|
||||
local expect="$5"
|
||||
local expect_msg="$6"
|
||||
|
||||
run_podman run -d --name $ctrname \
|
||||
--health-cmd "echo $msg" \
|
||||
$flag \
|
||||
$IMAGE /home/podman/pause
|
||||
cid="$output"
|
||||
|
||||
run_podman inspect $ctrname --format $format
|
||||
is "$output" "$expect" "$expect_msg"
|
||||
|
||||
output=$cid
|
||||
}
|
||||
|
||||
function _check_health_log {
|
||||
local ctrname="$1"
|
||||
local expect_msg="$2"
|
||||
local comparison=$3
|
||||
local expect_count="$4"
|
||||
|
||||
run_podman inspect $ctrname --format "{{.State.Health.Log}}"
|
||||
count=$(grep -co "$expect_msg" <<< "$output")
|
||||
assert "$count" $comparison $expect_count "Number of matching health log messages"
|
||||
}
|
||||
|
||||
@test "podman healthcheck --health-max-log-count values" {
|
||||
# flag | expected value | op | log count
|
||||
test="
|
||||
| 5 | -eq | 5
|
||||
--health-max-log-count 0 | 0 | -ge | 11
|
||||
--health-max-log-count=0 | 0 | -ge | 11
|
||||
--health-max-log-count 10 | 10 | -eq | 10
|
||||
--health-max-log-count=10 | 10 | -eq | 10
|
||||
"
|
||||
|
||||
while read flag value op logs_count ; do
|
||||
local msg="healthmsg-$(random_string)"
|
||||
local ctrname="c-h-$(safename)"
|
||||
_create_container_with_health_log_settings $ctrname $msg "{{.Config.HealthMaxLogCount}}" $flag $value "HealthMaxLogCount"
|
||||
|
||||
for i in $(seq 1 $((logs_count + 5)));
|
||||
do
|
||||
run_podman healthcheck run $ctrname
|
||||
is "$output" "" "unexpected output from podman healthcheck run (pass $i)"
|
||||
done
|
||||
|
||||
_check_health_log $ctrname $msg $op $logs_count
|
||||
|
||||
run_podman rm -t 0 -f $ctrname
|
||||
done < <(parse_table "$tests")
|
||||
}
|
||||
|
||||
@test "podman healthcheck --health-max-log-size values" {
|
||||
local s=$(printf "healthmsg-%1000s")
|
||||
local long_msg=${s// /$(random_string)}
|
||||
|
||||
# flag | expected value | exp_msg
|
||||
test="
|
||||
| 500 | ${long_msg:0:500}}]\$
|
||||
--health-max-log-size 0 | 0 | $long_msg}]\$
|
||||
--health-max-log-size=0 | 0 | $long_msg}]\$
|
||||
--health-max-log-size 10 | 10 | ${long_msg:0:10}}]\$
|
||||
--health-max-log-size=10 | 10 | ${long_msg:0:10}}]\$
|
||||
"
|
||||
|
||||
while read flag value exp_msg ; do
|
||||
local ctrname="c-h-$(safename)"
|
||||
_create_container_with_health_log_settings $ctrname $long_msg "{{.Config.HealthMaxLogSize}}" $flag $value "HealthMaxLogSize"
|
||||
|
||||
run_podman healthcheck run $ctrname
|
||||
is "$output" "" "output from 'podman healthcheck run'"
|
||||
|
||||
_check_health_log $ctrname $exp_msg -eq 1
|
||||
|
||||
run_podman rm -t 0 -f $ctrname
|
||||
done < <(parse_table "$tests")
|
||||
}
|
||||
|
||||
@test "podman healthcheck --health-log-destination file" {
|
||||
local TMP_DIR_HEALTHCHECK="$PODMAN_TMPDIR/healthcheck"
|
||||
mkdir $TMP_DIR_HEALTHCHECK
|
||||
local ctrname="c-h-$(safename)"
|
||||
local msg="healthmsg-$(random_string)"
|
||||
_create_container_with_health_log_settings $ctrname $msg "{{.Config.HealthLogDestination}}" "--health-log-destination $TMP_DIR_HEALTHCHECK" "$TMP_DIR_HEALTHCHECK" "HealthLogDestination"
|
||||
cid="$output"
|
||||
|
||||
run_podman healthcheck run $ctrname
|
||||
is "$output" "" "output from 'podman healthcheck run'"
|
||||
|
||||
healthcheck_log_path="${TMP_DIR_HEALTHCHECK}/${cid}-healthcheck.log"
|
||||
# The healthcheck is triggered by the podman when the container is started, but its execution depends on systemd.
|
||||
# And since `run_podman healthcheck run` is also run manually, it will result in two runs.
|
||||
count=$(grep -co "$msg" $healthcheck_log_path)
|
||||
assert "$count" -ge 1 "Number of matching health log messages"
|
||||
|
||||
run_podman rm -t 0 -f $ctrname
|
||||
}
|
||||
|
||||
|
||||
@test "podman healthcheck --health-log-destination journal" {
|
||||
skip_if_remote "We cannot read journalctl over remote."
|
||||
|
||||
# We can't use journald on RHEL as rootless, either: rhbz#1895105
|
||||
skip_if_journald_unavailable
|
||||
|
||||
# FIXME: The rootless user belongs to systemd-journal, but this still fails
|
||||
if is_rhel_or_centos; then
|
||||
skip_if_rootless
|
||||
fi
|
||||
|
||||
local ctrname="c-h-$(safename)"
|
||||
local msg="healthmsg-$(random_string)"
|
||||
_create_container_with_health_log_settings $ctrname $msg "{{.Config.HealthLogDestination}}" "--health-log-destination events_logger" "events_logger" "HealthLogDestination"
|
||||
cid="$output"
|
||||
|
||||
run_podman healthcheck run $ctrname
|
||||
is "$output" "" "output from 'podman healthcheck run'"
|
||||
|
||||
cmd="journalctl --output cat --output-fields=PODMAN_HEALTH_LOG PODMAN_ID=$cid"
|
||||
echo "$_LOG_PROMPT $cmd"
|
||||
run $cmd
|
||||
echo "$output"
|
||||
assert "$status" -eq 0 "exit status of journalctl"
|
||||
|
||||
# The healthcheck is triggered by the podman when the container is started, but its execution depends on systemd.
|
||||
# And since `run_podman healthcheck run` is also run manually, it will result in two runs.
|
||||
count=$(grep -co "$msg" <<< "$output")
|
||||
assert "$count" -ge 1 "Number of matching health log messages"
|
||||
|
||||
run_podman rm -t 0 -f $ctrname
|
||||
}
|
||||
|
||||
# vim: filetype=sh
|
1754
252-quadlet.bats
Normal file
1754
252-quadlet.bats
Normal file
File diff suppressed because it is too large
Load Diff
701
255-auto-update.bats
Normal file
701
255-auto-update.bats
Normal file
@ -0,0 +1,701 @@
|
||||
#!/usr/bin/env bats -*- bats -*-
|
||||
#
|
||||
# Tests for automatically update images for containerized services
|
||||
#
|
||||
|
||||
load helpers
|
||||
load helpers.network
|
||||
load helpers.registry
|
||||
load helpers.systemd
|
||||
|
||||
export SNAME_FILE
|
||||
|
||||
function setup() {
|
||||
skip_if_remote "systemd tests are meaningless over remote"
|
||||
basic_setup
|
||||
|
||||
SNAME_FILE=${PODMAN_TMPDIR}/services
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
if [[ -e $SNAME_FILE ]]; then
|
||||
while read line; do
|
||||
if [[ "$line" =~ "podman-auto-update" ]]; then
|
||||
echo "Stop timer: $line.timer"
|
||||
systemctl stop $line.timer
|
||||
systemctl disable $line.timer
|
||||
else
|
||||
systemctl stop $line
|
||||
fi
|
||||
rm -f $UNIT_DIR/$line.{service,timer}
|
||||
done < $SNAME_FILE
|
||||
|
||||
rm -f $SNAME_FILE
|
||||
fi
|
||||
SNAME_FILE=
|
||||
|
||||
run_podman rmi -f \
|
||||
quay.io/libpod/alpine:latest \
|
||||
quay.io/libpod/busybox:latest \
|
||||
quay.io/libpod/localtest:latest \
|
||||
quay.io/libpod/autoupdatebroken:latest \
|
||||
quay.io/libpod/test:latest
|
||||
|
||||
# The rollback tests may leave some dangling images behind, so let's prune
|
||||
# them to leave a clean state.
|
||||
run_podman image prune -f
|
||||
basic_teardown
|
||||
}
|
||||
|
||||
# This functions is used for handle the basic step in auto-update related
|
||||
# tests. Including following steps:
|
||||
# 1. Generate a random container name and echo it to output.
|
||||
# 2. Tag the fake image before test
|
||||
# 3. Start a container with io.containers.autoupdate
|
||||
# 4. Generate the service file from the container
|
||||
# 5. Remove the origin container
|
||||
# 6. Start the container from service
|
||||
# 7. Use this fully-qualified image instead of 2)
|
||||
function generate_service() {
|
||||
local target_img_basename=$1
|
||||
local autoupdate=$2
|
||||
local command=$3
|
||||
local extraArgs=$4
|
||||
local noTag=$5
|
||||
local requires=$6
|
||||
|
||||
# Unless specified, set a default command.
|
||||
if [[ -z "$command" ]]; then
|
||||
command="top -d 120"
|
||||
fi
|
||||
|
||||
# Container name. Include the autoupdate type, to make debugging easier.
|
||||
# IMPORTANT: variable 'cname' is passed (out of scope) up to caller!
|
||||
cname=c_${autoupdate//\'/}_$(random_string)
|
||||
target_img="quay.io/libpod/$target_img_basename:latest"
|
||||
if [[ -n "$7" ]]; then
|
||||
target_img="$7"
|
||||
fi
|
||||
|
||||
if [[ -z "$noTag" ]]; then
|
||||
run_podman tag $IMAGE $target_img
|
||||
fi
|
||||
|
||||
if [[ -n "$autoupdate" ]]; then
|
||||
label="--label io.containers.autoupdate=$autoupdate"
|
||||
else
|
||||
label=""
|
||||
fi
|
||||
|
||||
if [[ -n "$requires" ]]; then
|
||||
requires="--requires=$requires"
|
||||
fi
|
||||
|
||||
run_podman create $extraArgs --name $cname $label $target_img $command
|
||||
|
||||
(cd $UNIT_DIR; run_podman generate systemd --new --files --name $requires $cname)
|
||||
echo "container-$cname" >> $SNAME_FILE
|
||||
run_podman rm -t 0 -f $cname
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl_start container-$cname
|
||||
systemctl status container-$cname
|
||||
|
||||
# Original image ID.
|
||||
# IMPORTANT: variable 'ori_image' is passed (out of scope) up to caller!
|
||||
run_podman inspect --format "{{.Image}}" $cname
|
||||
ori_image=$output
|
||||
}
|
||||
|
||||
function _wait_service_ready() {
|
||||
local sname=$1
|
||||
|
||||
local timeout=6
|
||||
while [[ $timeout -gt 1 ]]; do
|
||||
if systemctl -q is-active $sname; then
|
||||
return
|
||||
fi
|
||||
sleep 1
|
||||
let timeout=$timeout-1
|
||||
done
|
||||
|
||||
# Print service status as debug information before failed the case
|
||||
systemctl status $sname
|
||||
die "Timed out waiting for $sname to start"
|
||||
}
|
||||
|
||||
# Wait for container to update, as confirmed by its image ID changing
|
||||
function _confirm_update() {
|
||||
local cname=$1
|
||||
local old_iid=$2
|
||||
|
||||
# Image has already been pulled, so this shouldn't take too long
|
||||
local timeout=10
|
||||
while [[ $timeout -gt 0 ]]; do
|
||||
run_podman '?' inspect --format "{{.Image}}" $cname
|
||||
if [[ $status != 0 ]]; then
|
||||
if [[ $output =~ (no such object|does not exist in database): ]]; then
|
||||
# this is ok, it just means the container is being restarted
|
||||
:
|
||||
else
|
||||
die "podman inspect $cname failed unexpectedly"
|
||||
fi
|
||||
elif [[ $output != $old_iid ]]; then
|
||||
return
|
||||
fi
|
||||
sleep 1
|
||||
timeout=$((timeout - 1))
|
||||
done
|
||||
|
||||
die "Timed out waiting for $cname to update; old IID=$old_iid"
|
||||
}
|
||||
|
||||
@test "podman auto-update - validate input" {
|
||||
# Fully-qualified image reference is required
|
||||
run_podman create --label io.containers.autoupdate=registry $IMAGE
|
||||
run_podman rm -f "$output"
|
||||
|
||||
# Short name does not work
|
||||
shortname="shortname:latest"
|
||||
run_podman image tag $IMAGE $shortname
|
||||
run_podman 125 create --label io.containers.autoupdate=registry $shortname
|
||||
is "$output" "Error: short name: auto updates require fully-qualified image reference: \"$shortname\""
|
||||
|
||||
# Requires docker (or no) transport
|
||||
archive=$PODMAN_TMPDIR/archive.tar
|
||||
run_podman save -o $archive $IMAGE
|
||||
run_podman 125 create --label io.containers.autoupdate=registry docker-archive:$archive
|
||||
is "$output" ".*Error: auto updates require the docker image transport but image is of transport \"docker-archive\""
|
||||
|
||||
run_podman rmi $shortname
|
||||
}
|
||||
|
||||
# This test can fail in dev. environment because of SELinux.
|
||||
# quick fix: chcon -t container_runtime_exec_t ./bin/podman
|
||||
@test "podman auto-update - label io.containers.autoupdate=image" {
|
||||
since=$(date --iso-8601=seconds)
|
||||
run_podman auto-update
|
||||
is "$output" ""
|
||||
run_podman events --filter type=system --since $since --stream=false
|
||||
is "$output" ""
|
||||
|
||||
# Generate two units. The first "parent" to be auto updated, the second
|
||||
# "child" depends on/requires the "parent" and is expected to get restarted
|
||||
# as well on auto updates (regression test for #18926).
|
||||
generate_service alpine image
|
||||
ctr_parent=$cname
|
||||
_wait_service_ready container-$ctr_parent.service
|
||||
|
||||
generate_service alpine image "" "" "" "container-$ctr_parent.service"
|
||||
ctr_child=$cname
|
||||
_wait_service_ready container-$ctr_child.service
|
||||
run_podman container inspect --format "{{.ID}}" $ctr_child
|
||||
old_child_id=$output
|
||||
|
||||
since=$(date --iso-8601=seconds)
|
||||
run_podman auto-update --dry-run --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}"
|
||||
is "$output" ".*container-$ctr_parent.service,quay.io/libpod/alpine:latest,pending,registry.*" "Image update is pending."
|
||||
run_podman events --filter type=system --since $since --stream=false
|
||||
is "$output" ".* system auto-update"
|
||||
|
||||
since=$(date --iso-8601=seconds)
|
||||
run_podman auto-update --rollback=false --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}"
|
||||
is "$output" "Trying to pull.*" "Image is updated."
|
||||
is "$output" ".*container-$ctr_parent.service,quay.io/libpod/alpine:latest,true,registry.*" "Image is updated."
|
||||
run_podman events --filter type=system --since $since --stream=false
|
||||
is "$output" ".* system auto-update"
|
||||
|
||||
# Confirm that the update was successful and that the child container/unit
|
||||
# has been restarted as well.
|
||||
_confirm_update $ctr_parent $ori_image
|
||||
run_podman container inspect --format "{{.ID}}" $ctr_child
|
||||
assert "$output" != "$old_child_id" \
|
||||
"child container/unit has not been restarted during update"
|
||||
run_podman container inspect --format "{{.ID}}" $ctr_child
|
||||
run_podman container inspect --format "{{.State.Status}}" $ctr_child
|
||||
is "$output" "running" "child container is in running state"
|
||||
}
|
||||
|
||||
@test "podman auto-update - label io.containers.autoupdate=image with rollback" {
|
||||
# FIXME: this test should exercise the authfile label to have a regression
|
||||
# test for #11171.
|
||||
|
||||
# Note: the autoupdatebroken image is empty on purpose so it cannot be
|
||||
# executed and force a rollback. The rollback test for the local policy
|
||||
# is exercising the case where the container doesn't send a ready message.
|
||||
image=quay.io/libpod/autoupdatebroken
|
||||
|
||||
run_podman tag $IMAGE $image
|
||||
generate_service autoupdatebroken image
|
||||
|
||||
_wait_service_ready container-$cname.service
|
||||
run_podman auto-update --dry-run --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}"
|
||||
is "$output" ".*container-$cname.service,$image:latest,pending,registry.*" "Image update is pending."
|
||||
|
||||
run_podman container inspect --format "{{.Image}}" $cname
|
||||
oldID="$output"
|
||||
|
||||
run_podman inspect --format "{{.ID}}" $cname
|
||||
containerID="$output"
|
||||
|
||||
run_podman auto-update --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}"
|
||||
is "$output" "Trying to pull.*" "Image is updated."
|
||||
is "$output" ".*container-$cname.service,$image:latest,rolled back,registry.*" "Image has been rolled back."
|
||||
|
||||
run_podman container inspect --format "{{.Image}}" $cname
|
||||
is "$output" "$oldID" "container rolled back to previous image"
|
||||
|
||||
run_podman container inspect --format "{{.ID}}" $cname
|
||||
assert "$output" != "$containerID" \
|
||||
"container has not been restarted during rollback"
|
||||
}
|
||||
|
||||
@test "podman auto-update - label io.containers.autoupdate=disabled" {
|
||||
generate_service alpine disabled
|
||||
|
||||
_wait_service_ready container-$cname.service
|
||||
run_podman auto-update
|
||||
is "$output" "" "Image is not updated when autoupdate=disabled."
|
||||
|
||||
run_podman inspect --format "{{.Image}}" $cname
|
||||
is "$output" "$ori_image" "Image ID should not change"
|
||||
}
|
||||
|
||||
@test "podman auto-update - label io.containers.autoupdate=fakevalue" {
|
||||
fakevalue=fake_$(random_string)
|
||||
generate_service alpine $fakevalue
|
||||
|
||||
_wait_service_ready container-$cname.service
|
||||
run_podman 125 auto-update
|
||||
is "$output" ".*invalid auto-update policy.*" "invalid policy setup"
|
||||
|
||||
run_podman inspect --format "{{.Image}}" $cname
|
||||
is "$output" "$ori_image" "Image ID should not change"
|
||||
}
|
||||
|
||||
@test "podman auto-update - label io.containers.autoupdate=local" {
|
||||
generate_service localtest local
|
||||
_wait_service_ready container-$cname.service
|
||||
|
||||
image=quay.io/libpod/localtest:latest
|
||||
run_podman commit --change CMD=/bin/bash $cname $image
|
||||
run_podman image inspect --format "{{.ID}}" $image
|
||||
|
||||
run_podman auto-update --dry-run --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}"
|
||||
is "$output" ".*container-$cname.service,quay.io/libpod/localtest:latest,pending,local.*" "Image update is pending."
|
||||
|
||||
run_podman auto-update --rollback=false --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}"
|
||||
is "$output" ".*container-$cname.service,quay.io/libpod/localtest:latest,true,local.*" "Image is updated."
|
||||
|
||||
_confirm_update $cname $ori_image
|
||||
}
|
||||
|
||||
# This test can fail in dev. environment because of SELinux.
|
||||
# quick fix: chcon -t container_runtime_exec_t ./bin/podman
|
||||
@test "podman auto-update - label io.containers.autoupdate=local with rollback" {
|
||||
# sdnotify fails with runc 1.0.0-3-dev2 on Ubuntu. Let's just
|
||||
# assume that we work only with crun, nothing else.
|
||||
# [copied from 260-sdnotify.bats]
|
||||
runtime=$(podman_runtime)
|
||||
if [[ "$runtime" != "crun" ]]; then
|
||||
skip "this test only works with crun, not $runtime"
|
||||
fi
|
||||
|
||||
_prefetch $SYSTEMD_IMAGE
|
||||
|
||||
dockerfile1=$PODMAN_TMPDIR/Dockerfile.1
|
||||
cat >$dockerfile1 <<EOF
|
||||
FROM $SYSTEMD_IMAGE
|
||||
RUN echo -e "#!/bin/sh\n\
|
||||
printenv NOTIFY_SOCKET; echo READY; systemd-notify --ready;\n\
|
||||
trap 'echo Received SIGTERM, finishing; exit' SIGTERM; echo WAITING; while :; do sleep 0.1; done" \
|
||||
>> /runme
|
||||
RUN chmod +x /runme
|
||||
EOF
|
||||
|
||||
dockerfile2=$PODMAN_TMPDIR/Dockerfile.2
|
||||
cat >$dockerfile2 <<EOF
|
||||
FROM $SYSTEMD_IMAGE
|
||||
RUN echo -e "#!/bin/sh\n\
|
||||
exit 1" >> /runme
|
||||
RUN chmod +x /runme
|
||||
EOF
|
||||
image=test
|
||||
|
||||
# Generate a healthy image that will run correctly.
|
||||
run_podman build -t quay.io/libpod/$image -f $dockerfile1
|
||||
|
||||
generate_service $image local /runme --sdnotify=container noTag
|
||||
_wait_service_ready container-$cname.service
|
||||
|
||||
run_podman auto-update --dry-run --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}"
|
||||
is "$output" ".*container-$cname.service,quay.io/libpod/$image:latest,false,local.*" "No update available"
|
||||
|
||||
# Generate an unhealthy image that will fail.
|
||||
run_podman build -t quay.io/libpod/$image -f $dockerfile2
|
||||
run_podman image inspect --format "{{.ID}}" $image
|
||||
newID="$output"
|
||||
|
||||
run_podman auto-update --dry-run --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}"
|
||||
is "$output" ".*container-$cname.service,quay.io/libpod/$image:latest,pending,local.*" "Image updated is pending"
|
||||
|
||||
# Note: we rollback automatically by default.
|
||||
run_podman auto-update --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}"
|
||||
is "$output" ".*container-$cname.service,quay.io/libpod/$image:latest,rolled back,local.*" "Rolled back to old image"
|
||||
|
||||
# Make sure that new container is not using the new image ID anymore.
|
||||
_confirm_update $cname $newID
|
||||
}
|
||||
|
||||
@test "podman auto-update with multiple services" {
|
||||
# Preserve original image ID, to confirm that it changes (or not)
|
||||
run_podman inspect --format "{{.Id}}" $IMAGE
|
||||
local img_id="$output"
|
||||
|
||||
local cnames=()
|
||||
local -A expect_update
|
||||
local -A will_update=([image]=1 [registry]=1 [local]=1)
|
||||
|
||||
local fakevalue=fake_$(random_string)
|
||||
for auto_update in image registry "" disabled "''" $fakevalue local
|
||||
do
|
||||
local img_base="alpine"
|
||||
if [[ $auto_update == "registry" ]]; then
|
||||
img_base="busybox"
|
||||
elif [[ $auto_update == "local" ]]; then
|
||||
img_base="localtest"
|
||||
fi
|
||||
generate_service $img_base $auto_update
|
||||
cnames+=($cname)
|
||||
if [[ $auto_update == "local" ]]; then
|
||||
local_cname=$cname
|
||||
fi
|
||||
|
||||
if [[ -n "$auto_update" && -n "${will_update[$auto_update]}" ]]; then
|
||||
expect_update[$cname]=1
|
||||
fi
|
||||
done
|
||||
|
||||
# Make sure all services are ready.
|
||||
for cname in "${cnames[@]}"; do
|
||||
_wait_service_ready container-$cname.service
|
||||
done
|
||||
run_podman commit --change CMD=/bin/bash $local_cname quay.io/libpod/localtest:latest
|
||||
# Exit code is expected, due to invalid 'fakevalue'
|
||||
run_podman 125 auto-update --rollback=false
|
||||
update_log=$output
|
||||
is "$update_log" ".*invalid auto-update policy.*" "invalid policy setup"
|
||||
is "$update_log" ".*Error: invalid auto-update policy.*" "invalid policy setup"
|
||||
|
||||
local n_updated=$(grep -c 'Trying to pull' <<<"$update_log")
|
||||
is "$n_updated" "2" "Number of images updated from registry."
|
||||
|
||||
for cname in "${!expect_update[@]}"; do
|
||||
is "$update_log" ".*$cname.*" "container with auto-update policy image updated"
|
||||
# Just because podman says it fetched, doesn't mean it actually updated
|
||||
_confirm_update $cname $img_id
|
||||
done
|
||||
|
||||
# Final confirmation that all image IDs have/haven't changed
|
||||
for cname in "${cnames[@]}"; do
|
||||
run_podman inspect --format "{{.Image}}" $cname
|
||||
if [[ -n "${expect_update[$cname]}" ]]; then
|
||||
assert "$output" != "$img_id" "$cname: image ID did not change"
|
||||
else
|
||||
assert "$output" = "$img_id" "Image ID should not be changed."
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
@test "podman auto-update using systemd" {
|
||||
# FIXME: The rootless user belongs to systemd-journal, but this still fails
|
||||
if is_rhel_or_centos; then
|
||||
skip_if_rootless
|
||||
fi
|
||||
|
||||
generate_service alpine image
|
||||
|
||||
cat >$UNIT_DIR/podman-auto-update-$cname.timer <<EOF
|
||||
[Unit]
|
||||
Description=Podman auto-update testing timer
|
||||
|
||||
[Timer]
|
||||
OnActiveSec=0s
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
EOF
|
||||
cat >$UNIT_DIR/podman-auto-update-$cname.service <<EOF
|
||||
[Unit]
|
||||
Description=Podman auto-update testing service
|
||||
Documentation=man:podman-auto-update(1)
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=$PODMAN auto-update
|
||||
Environment="http_proxy=${http_proxy}"
|
||||
Environment="HTTP_PROXY=${HTTP_PROXY}"
|
||||
Environment="https_proxy=${https_proxy}"
|
||||
Environment="HTTPS_PROXY=${HTTPS_PROXY}"
|
||||
Environment="no_proxy=${no_proxy}"
|
||||
Environment="NO_PROXY=${NO_PROXY}"
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
EOF
|
||||
|
||||
echo "podman-auto-update-$cname" >> $SNAME_FILE
|
||||
systemctl enable --now podman-auto-update-$cname.timer
|
||||
systemctl list-timers --all
|
||||
|
||||
# systemd <245 displays 'Started Podman auto-update ...'
|
||||
# systemd 245 - <250 displays 'Finished Podman auto-update ...'
|
||||
# systemd 250 - ???? displays 'Finished <unit name> - Podman auto-...'
|
||||
local expect='(Started|Finished.*) Podman auto-update testing service'
|
||||
local failed_start=failed
|
||||
local count=0
|
||||
while [ $count -lt 120 ]; do
|
||||
run journalctl -n 15 -u podman-auto-update-$cname.service
|
||||
if [[ "$output" =~ $expect ]]; then
|
||||
failed_start=
|
||||
break
|
||||
fi
|
||||
((count+=1))
|
||||
sleep 1
|
||||
done
|
||||
|
||||
if [[ -n "$failed_start" ]]; then
|
||||
echo "journalctl output:"
|
||||
sed -e 's/^/ /' <<<"$output"
|
||||
die "Did not find expected string '$expect' in journalctl output for $cname"
|
||||
fi
|
||||
|
||||
_confirm_update $cname $ori_image
|
||||
}
|
||||
|
||||
@test "podman-kube@.service template with rollback" {
|
||||
# sdnotify fails with runc 1.0.0-3-dev2 on Ubuntu. Let's just
|
||||
# assume that we work only with crun, nothing else.
|
||||
# [copied from 260-sdnotify.bats]
|
||||
runtime=$(podman_runtime)
|
||||
if [[ "$runtime" != "crun" ]]; then
|
||||
skip "this test only works with crun, not $runtime"
|
||||
fi
|
||||
|
||||
_prefetch $SYSTEMD_IMAGE
|
||||
install_kube_template
|
||||
|
||||
dockerfile1=$PODMAN_TMPDIR/Dockerfile.1
|
||||
cat >$dockerfile1 <<EOF
|
||||
FROM $SYSTEMD_IMAGE
|
||||
RUN echo -e "#!/bin/sh\n\
|
||||
printenv NOTIFY_SOCKET; echo READY; systemd-notify --ready;\n\
|
||||
trap 'echo Received SIGTERM, finishing; exit' SIGTERM; echo WAITING; while :; do sleep 0.1; done" \
|
||||
>> /runme
|
||||
RUN chmod +x /runme
|
||||
EOF
|
||||
|
||||
dockerfile2=$PODMAN_TMPDIR/Dockerfile.2
|
||||
cat >$dockerfile2 <<EOF
|
||||
FROM $SYSTEMD_IMAGE
|
||||
RUN echo -e "#!/bin/sh\n\
|
||||
exit 1" >> /runme
|
||||
RUN chmod +x /runme
|
||||
EOF
|
||||
local_image=localhost/image:$(random_string 10)
|
||||
|
||||
# Generate a healthy image that will run correctly.
|
||||
run_podman build -t $local_image -f $dockerfile1
|
||||
run_podman image inspect --format "{{.ID}}" $local_image
|
||||
oldID="$output"
|
||||
|
||||
# Create the YAMl file
|
||||
yaml_source="$PODMAN_TMPDIR/test.yaml"
|
||||
cat >$yaml_source <<EOF
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
annotations:
|
||||
io.containers.autoupdate: "registry"
|
||||
io.containers.autoupdate/b: "local"
|
||||
io.containers.sdnotify/b: "container"
|
||||
labels:
|
||||
app: test
|
||||
name: test_pod
|
||||
spec:
|
||||
containers:
|
||||
- command:
|
||||
- top
|
||||
image: $IMAGE
|
||||
name: a
|
||||
- command:
|
||||
- /runme
|
||||
image: $local_image
|
||||
name: b
|
||||
EOF
|
||||
|
||||
# Dispatch the YAML file
|
||||
service_name="podman-kube@$(systemd-escape $yaml_source).service"
|
||||
systemctl_start $service_name
|
||||
systemctl is-active $service_name
|
||||
|
||||
# Make sure the containers are properly configured
|
||||
run_podman auto-update --dry-run --format "{{.Unit}},{{.Container}},{{.Image}},{{.Updated}},{{.Policy}}"
|
||||
is "$output" ".*$service_name,.* (test_pod-a),$IMAGE,false,registry.*" "global auto-update policy gets applied"
|
||||
is "$output" ".*$service_name,.* (test_pod-b),$local_image,false,local.*" "container-specified auto-update policy gets applied"
|
||||
|
||||
# Generate a broken image that will fail.
|
||||
run_podman build -t $local_image -f $dockerfile2
|
||||
run_podman image inspect --format "{{.ID}}" $local_image
|
||||
newID="$output"
|
||||
|
||||
assert "$oldID" != "$newID" "broken image really is a new one"
|
||||
|
||||
# Make sure container b sees the new image
|
||||
run_podman auto-update --dry-run --format "{{.Unit}},{{.Container}},{{.Image}},{{.Updated}},{{.Policy}}"
|
||||
is "$output" ".*$service_name,.* (test_pod-a),$IMAGE,false,registry.*" "global auto-update policy gets applied"
|
||||
is "$output" ".*$service_name,.* (test_pod-b),$local_image,pending,local.*" "container b sees the new image"
|
||||
|
||||
# Now update and check for the rollback
|
||||
run_podman auto-update --format "{{.Unit}},{{.Container}},{{.Image}},{{.Updated}},{{.Policy}}"
|
||||
is "$output" ".*$service_name,.* (test_pod-a),$IMAGE,rolled back,registry.*" "container a was rolled back as the update of b failed"
|
||||
is "$output" ".*$service_name,.* (test_pod-b),$local_image,rolled back,local.*" "container b was rolled back as its update has failed"
|
||||
|
||||
# Clean up
|
||||
systemctl stop $service_name
|
||||
run_podman rmi -f $(pause_image) $local_image $newID $oldID
|
||||
run_podman network rm podman-default-kube-network
|
||||
rm -f $UNIT_DIR/$unit_name
|
||||
}
|
||||
|
||||
@test "podman auto-update - pod" {
|
||||
dockerfile=$PODMAN_TMPDIR/Dockerfile
|
||||
cat >$dockerfile <<EOF
|
||||
FROM $IMAGE
|
||||
RUN touch /123
|
||||
EOF
|
||||
|
||||
podname=$(random_string)
|
||||
ctrname=$(random_string)
|
||||
podunit="$UNIT_DIR/pod-$podname.service.*"
|
||||
ctrunit="$UNIT_DIR/container-$ctrname.service.*"
|
||||
local_image=localhost/image:$(random_string 10)
|
||||
|
||||
run_podman tag $IMAGE $local_image
|
||||
|
||||
run_podman pod create --name=$podname
|
||||
run_podman create --label "io.containers.autoupdate=local" --pod=$podname --name=$ctrname $local_image top
|
||||
|
||||
# cd into the unit dir to generate the two files.
|
||||
pushd "$UNIT_DIR"
|
||||
run_podman generate systemd --name --new --files $podname
|
||||
is "$output" ".*$podunit.*"
|
||||
is "$output" ".*$ctrunit.*"
|
||||
popd
|
||||
|
||||
systemctl daemon-reload
|
||||
|
||||
systemctl_start pod-$podname.service
|
||||
_wait_service_ready container-$ctrname.service
|
||||
|
||||
run_podman pod inspect --format "{{.State}}" $podname
|
||||
is "$output" "Running" "pod is in running state"
|
||||
run_podman container inspect --format "{{.State.Status}}" $ctrname
|
||||
is "$output" "running" "container is in running state"
|
||||
|
||||
run_podman pod inspect --format "{{.ID}}" $podname
|
||||
podid="$output"
|
||||
run_podman container inspect --format "{{.ID}}" $ctrname
|
||||
ctrid="$output"
|
||||
|
||||
# Note that the pod's unit is listed below, not the one of the container.
|
||||
run_podman auto-update --dry-run --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}"
|
||||
is "$output" ".*pod-$podname.service,$local_image,false,local.*" "No update available"
|
||||
|
||||
run_podman build -t $local_image -f $dockerfile
|
||||
|
||||
run_podman auto-update --dry-run --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}"
|
||||
is "$output" ".*pod-$podname.service,$local_image,pending,local.*" "Image updated is pending"
|
||||
|
||||
run_podman auto-update --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}"
|
||||
is "$output" ".*pod-$podname.service,$local_image,true,local.*" "Service has been restarted"
|
||||
_wait_service_ready container-$ctrname.service
|
||||
|
||||
run_podman pod inspect --format "{{.ID}}" $podname
|
||||
assert "$output" != "$podid" "pod has been recreated"
|
||||
run_podman container inspect --format "{{.ID}}" $ctrname
|
||||
assert "$output" != "$ctrid" "container has been recreated"
|
||||
|
||||
run systemctl stop pod-$podname.service
|
||||
assert $status -eq 0 "Error stopping pod systemd unit: $output"
|
||||
|
||||
run_podman pod rm -f $podname
|
||||
run_podman rmi $local_image $(pause_image)
|
||||
rm -f $podunit $ctrunit
|
||||
systemctl daemon-reload
|
||||
}
|
||||
|
||||
@test "podman-auto-update --authfile" {
|
||||
# Test the three supported ways of using authfiles with auto updates
|
||||
# 1) Passed via --authfile CLI flag
|
||||
# 2) Passed via the REGISTRY_AUTH_FILE env variable
|
||||
# 3) Via a label at container creation where 1) and 2) will be ignored
|
||||
|
||||
registry=localhost:${PODMAN_LOGIN_REGISTRY_PORT}
|
||||
image_on_local_registry=$registry/name:tag
|
||||
authfile=$PODMAN_TMPDIR/authfile.json
|
||||
|
||||
# First, start the registry and populate the authfile that we can use for the test.
|
||||
start_registry
|
||||
run_podman login --authfile=$authfile \
|
||||
--tls-verify=false \
|
||||
--username ${PODMAN_LOGIN_USER} \
|
||||
--password ${PODMAN_LOGIN_PASS} \
|
||||
$registry
|
||||
|
||||
# Push the image to the registry and pull it down again to make sure we
|
||||
# have the identical digest in the local storage
|
||||
run_podman push --tls-verify=false --creds "${PODMAN_LOGIN_USER}:${PODMAN_LOGIN_PASS}" $IMAGE $image_on_local_registry
|
||||
run_podman pull --tls-verify=false --creds "${PODMAN_LOGIN_USER}:${PODMAN_LOGIN_PASS}" $image_on_local_registry
|
||||
|
||||
# Generate a systemd service with the "registry" auto-update policy running
|
||||
# "top" inside the image we just pushed to the local registry.
|
||||
generate_service "" registry top "" "" "" $image_on_local_registry
|
||||
ctr=$cname
|
||||
_wait_service_ready container-$ctr.service
|
||||
|
||||
run_podman 125 auto-update
|
||||
is "$output" \
|
||||
".*Error: checking image updates for container .*: x509: .*"
|
||||
|
||||
run_podman 125 auto-update --tls-verify=false
|
||||
is "$output" \
|
||||
".*Error: checking image updates for container .*: authentication required"
|
||||
|
||||
# Test 1)
|
||||
run_podman auto-update --authfile=$authfile --tls-verify=false --dry-run --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}"
|
||||
is "$output" "container-$ctr.service,$image_on_local_registry,false,registry" "auto-update works with authfile"
|
||||
|
||||
# Test 2)
|
||||
REGISTRY_AUTH_FILE=$authfile run_podman auto-update --tls-verify=false --dry-run --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}"
|
||||
is "$output" "container-$ctr.service,$image_on_local_registry,false,registry" "auto-update works with env var"
|
||||
systemctl stop container-$ctr.service
|
||||
run_podman rm -f -t0 --ignore $ctr
|
||||
|
||||
# Create a container with the auth-file label
|
||||
generate_service "" registry top "--label io.containers.autoupdate.authfile=$authfile" "" "" $image_on_local_registry
|
||||
ctr=$cname
|
||||
_wait_service_ready container-$ctr.service
|
||||
|
||||
# Test 3)
|
||||
# Also make sure that the label takes precedence over the CLI flag.
|
||||
run_podman auto-update --authfile=/dev/null --tls-verify=false --dry-run --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}"
|
||||
is "$output" "container-$ctr.service,$image_on_local_registry,false,registry" "auto-update works with authfile container label"
|
||||
run_podman rm -f -t0 --ignore $ctr
|
||||
run_podman rmi $image_on_local_registry
|
||||
}
|
||||
|
||||
# vim: filetype=sh
|
451
520-checkpoint.bats
Normal file
451
520-checkpoint.bats
Normal file
@ -0,0 +1,451 @@
|
||||
#!/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() {
|
||||
skip_if_rhel_or_centos "Checkpoint tests are very unstable and that's a problem on RHEL/CentOS Stream"
|
||||
|
||||
# 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_if_remote "Test uses --root/--runroot, which are N/A over remote"
|
||||
|
||||
# To avoid network pull, copy $IMAGE straight to temp root
|
||||
local p_opts="$(podman_isolation_opts ${PODMAN_TMPDIR}) --events-backend file"
|
||||
run_podman save -o $PODMAN_TMPDIR/image.tar $IMAGE
|
||||
run_podman $p_opts load -i $PODMAN_TMPDIR/image.tar
|
||||
|
||||
# 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"
|
||||
|
||||
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
|
1375
helpers.bash
Normal file
1375
helpers.bash
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,8 +0,0 @@
|
||||
summary: Check basic functionality
|
||||
discover:
|
||||
how: fmf
|
||||
execute:
|
||||
how: tmt
|
||||
prepare:
|
||||
how: feature
|
||||
epel: enabled
|
47
plans/system.fmf
Normal file
47
plans/system.fmf
Normal file
@ -0,0 +1,47 @@
|
||||
discover:
|
||||
how: fmf
|
||||
|
||||
execute:
|
||||
how: tmt
|
||||
|
||||
prepare:
|
||||
- how: shell
|
||||
script: modprobe null_blk nr_devices=1
|
||||
order: 5
|
||||
- how: shell
|
||||
script: |
|
||||
BATS_VERSION=1.12.0
|
||||
curl -L https://github.com/bats-core/bats-core/archive/refs/tags/v"$BATS_VERSION".tar.gz | tar -xz
|
||||
cd bats-core-"$BATS_VERSION"
|
||||
./install.sh /usr
|
||||
order: 10
|
||||
|
||||
provision:
|
||||
how: artemis
|
||||
hardware:
|
||||
memory: ">= 16 GB"
|
||||
cpu:
|
||||
cores: ">= 4"
|
||||
threads: ">=8"
|
||||
disk:
|
||||
- size: ">= 512 GB"
|
||||
|
||||
/root-local:
|
||||
summary: Local root system tests
|
||||
discover+:
|
||||
filter: 'tag:local & tag:root & tag:system'
|
||||
|
||||
/rootless-local:
|
||||
summary: Local rootless system tests
|
||||
discover+:
|
||||
filter: 'tag:local & tag:rootless & tag:system'
|
||||
|
||||
/root-remote:
|
||||
summary: Remote root system tests
|
||||
discover+:
|
||||
filter: 'tag:remote & tag:root & tag:system'
|
||||
|
||||
/rootless-remote:
|
||||
summary: Remote rootless system tests
|
||||
discover+:
|
||||
filter: 'tag:remote & tag:rootless & tag:system'
|
20
plans/tmt.fmf
Normal file
20
plans/tmt.fmf
Normal file
@ -0,0 +1,20 @@
|
||||
summary: Run tmt container provision test (downstream only)
|
||||
|
||||
discover:
|
||||
how: fmf
|
||||
filter: 'tag:downstream'
|
||||
|
||||
execute:
|
||||
how: tmt
|
||||
|
||||
prepare:
|
||||
- when: distro == centos-stream-9
|
||||
how: shell
|
||||
script: |
|
||||
dnf -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-$(rpm --eval '%{?rhel}').noarch.rpm
|
||||
dnf -y config-manager --set-enabled epel
|
||||
order: 10
|
||||
|
||||
adjust+:
|
||||
- enabled: false
|
||||
when: revdeps == yes or distro == rhel-10 or distro == centos-stream-10
|
26
podman.spec
26
podman.spec
@ -14,7 +14,7 @@ GO111MODULE=off go build -buildmode pie -compiler gc -tags="rpm_crashtraceback $
|
||||
Epoch: 5
|
||||
Name: podman
|
||||
Version: 5.4.0
|
||||
Release: 7%{?dist}
|
||||
Release: 8%{?dist}
|
||||
Summary: Manage Pods, Containers and Container Images
|
||||
License: Apache-2.0 AND BSD-2-Clause AND BSD-3-Clause AND ISC AND MIT AND MPL-2.0
|
||||
URL: https://%{name}.io/
|
||||
@ -27,6 +27,13 @@ Source1: https://github.com/openSUSE/catatonit/archive/v%{cataver}.tar.gz
|
||||
#Source2: https://github.com/containers/dnsname/archive/v%%{dnsnamever}.tar.gz
|
||||
Source2: https://github.com/containers/dnsname/archive/%{commit_dnsname}/dnsname-%{shortcommit_dnsname}.tar.gz
|
||||
# https://fedoraproject.org/wiki/PackagingDrafts/Go#Go_Language_Architectures
|
||||
# NOTE: Delete these sources after
|
||||
# https://github.com/containers/podman/pull/26183 merges
|
||||
Source3: 220-healthcheck.bats
|
||||
Source4: 252-quadlet.bats
|
||||
Source5: 255-auto-update.bats
|
||||
Source6: 520-checkpoint.bats
|
||||
Source7: helpers.bash
|
||||
ExclusiveArch: %{go_arches}
|
||||
Provides: %{name}-manpages = %{epoch}:%{version}-%{release}
|
||||
Obsoletes: %{name}-manpages < %{epoch}:%{version}-%{release}
|
||||
@ -127,7 +134,8 @@ file. Each CNI network will have its own dnsmasq instance.
|
||||
%package tests
|
||||
Summary: Tests for %{name}
|
||||
Requires: %{name} = %{epoch}:%{version}-%{release}
|
||||
#Requires: bats (which RHEL8 doesn't have. If it ever does, un-comment this)
|
||||
# Fetch bats rpm if you can, else install any way available
|
||||
Recommends: bats
|
||||
Requires: nmap-ncat
|
||||
Requires: httpd-tools
|
||||
Requires: jq
|
||||
@ -137,11 +145,13 @@ Requires: openssl
|
||||
Requires: buildah
|
||||
Requires: gnupg
|
||||
Requires: git-daemon
|
||||
Recommends: slirp4netns
|
||||
|
||||
%description tests
|
||||
%{summary}
|
||||
|
||||
This package contains system tests for %{name}
|
||||
This package contains system tests for %{name}. Only used for gating tests. End
|
||||
user / customer use cases not supported.
|
||||
|
||||
%prep
|
||||
%if 0%{?branch:1}
|
||||
@ -157,6 +167,12 @@ sed -i '$d' configure.ac
|
||||
popd
|
||||
tar fx %{SOURCE2}
|
||||
|
||||
cp %{SOURCE3} test/system/.
|
||||
cp %{SOURCE4} test/system/.
|
||||
cp %{SOURCE5} test/system/.
|
||||
cp %{SOURCE6} test/system/.
|
||||
cp %{SOURCE7} test/system/.
|
||||
|
||||
# cgroups-v1 is supported on rhel9
|
||||
%if 0%{?rhel} == 9
|
||||
sed -i '/DELETE ON RHEL9/,/DELETE ON RHEL9/d' libpod/runtime.go
|
||||
@ -370,6 +386,10 @@ fi
|
||||
%{_datadir}/%{name}/test
|
||||
|
||||
%changelog
|
||||
* Thu May 08 2025 Lokesh Mandvekar <lsm5@redhat.com> - 5:5.4.0-8
|
||||
- Enable gating tests via TMT
|
||||
- Related: RHEL-80816
|
||||
|
||||
* Fri Apr 25 2025 Jindrich Novy <jnovy@redhat.com> - 5:5.4.0-7
|
||||
- update to the latest content of https://github.com/containers/podman/tree/v5.4-rhel
|
||||
(https://github.com/containers/podman/commit/0ee1d49)
|
||||
|
49
test/tmt/system.fmf
Normal file
49
test/tmt/system.fmf
Normal file
@ -0,0 +1,49 @@
|
||||
require:
|
||||
- podman-tests
|
||||
- psmisc
|
||||
- slirp4netns
|
||||
|
||||
environment:
|
||||
# PODMAN_TESTING and CI_DESIRED_NETWORK envvars are set in system.sh
|
||||
PODMAN: /usr/bin/podman
|
||||
QUADLET: /usr/libexec/podman/quadlet
|
||||
ROOTLESS_USER: "fedora"
|
||||
adjust+:
|
||||
- when: distro == centos-stream
|
||||
environment+:
|
||||
ROOTLESS_USER: "ec2-user"
|
||||
- when: distro == rhel
|
||||
environment+:
|
||||
ROOTLESS_USER: "cloud-user"
|
||||
|
||||
/local-root:
|
||||
tag: [ local, root, system ]
|
||||
summary: local rootful test
|
||||
test: bash ./system.sh
|
||||
duration: 60m
|
||||
|
||||
/local-rootless:
|
||||
tag: [ local, rootless, system ]
|
||||
summary: rootless test
|
||||
test: bash ./system.sh rootless
|
||||
duration: 60m
|
||||
|
||||
/remote-root:
|
||||
tag: [ remote, root, system ]
|
||||
summary: remote rootful test
|
||||
test: bash ./system.sh
|
||||
duration: 60m
|
||||
environment+:
|
||||
PODMAN: /usr/bin/podman-remote
|
||||
require+:
|
||||
- podman-remote
|
||||
|
||||
/remote-rootless:
|
||||
tag: [ remote, rootless, system ]
|
||||
summary: remote rootless test
|
||||
test: bash ./system.sh rootless
|
||||
duration: 60m
|
||||
environment+:
|
||||
PODMAN: /usr/bin/podman-remote
|
||||
require+:
|
||||
- podman-remote
|
44
test/tmt/system.sh
Normal file
44
test/tmt/system.sh
Normal file
@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -exo pipefail
|
||||
|
||||
uname -r
|
||||
|
||||
loginctl enable-linger "$ROOTLESS_USER"
|
||||
|
||||
rpm -q \
|
||||
aardvark-dns \
|
||||
buildah \
|
||||
conmon \
|
||||
container-selinux \
|
||||
containers-common \
|
||||
criu \
|
||||
crun \
|
||||
netavark \
|
||||
passt \
|
||||
podman \
|
||||
podman-tests \
|
||||
skopeo \
|
||||
slirp4netns \
|
||||
systemd
|
||||
|
||||
export system_service_cmd="/usr/bin/podman system service --timeout=0 &"
|
||||
export test_cmd="whoami && cd /usr/share/podman/test/system && PATH=$PATH:/usr/libexec/podman CI_DESIRED_NETWORK=netavark PODMAN_TESTING=/usr/bin/podman-testing bats ."
|
||||
|
||||
if [[ -z $1 ]]; then
|
||||
if [[ $PODMAN == "/usr/bin/podman-remote" ]]; then
|
||||
eval "$system_service_cmd"
|
||||
fi
|
||||
eval "$test_cmd"
|
||||
elif [[ $1 == "rootless" ]]; then
|
||||
if [[ $PODMAN == "/usr/bin/podman-remote" ]]; then
|
||||
su - "$ROOTLESS_USER" -c "eval $system_service_cmd"
|
||||
fi
|
||||
su - "$ROOTLESS_USER" -c "eval $test_cmd"
|
||||
fi
|
||||
|
||||
# Kill all podman processes for remote tests
|
||||
if [[ $PODMAN == "/usr/bin/podman-remote" ]]; then
|
||||
killall -q podman
|
||||
fi
|
||||
exit 0
|
@ -1,4 +1,9 @@
|
||||
summary: Make sure that container provision works
|
||||
enabled: false
|
||||
adjust:
|
||||
enabled: true
|
||||
when: initiator != packit && distro != rhel
|
||||
summary: Make sure that TMT container provision works
|
||||
tag: [downstream]
|
||||
require:
|
||||
- tmt+provision-container
|
||||
test:
|
@ -1,2 +0,0 @@
|
||||
require:
|
||||
- podman
|
@ -1,12 +0,0 @@
|
||||
---
|
||||
# Sigh; RHEL8 doesn't have BATS
|
||||
- name: bats | fetch and unpack tarball
|
||||
unarchive:
|
||||
src: https://github.com/bats-core/bats-core/archive/v1.9.0.tar.gz
|
||||
dest: /root
|
||||
remote_src: true
|
||||
|
||||
- name: bats | install
|
||||
command: ./install.sh /usr/local
|
||||
args:
|
||||
chdir: /root/bats-core-1.9.0
|
@ -1,7 +0,0 @@
|
||||
---
|
||||
- name: create rootless user
|
||||
user:
|
||||
name: testuser
|
||||
shell: /bin/bash
|
||||
- name: enable linger
|
||||
command: loginctl enable-linger testuser
|
@ -1,72 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Run bats tests for a given $TEST_PACKAGE, e.g. buildah, podman
|
||||
#
|
||||
# This is invoked by the 'run_bats_tests' role; we assume that
|
||||
# the package foo has a foo-tests subpackage which provides the
|
||||
# directory /usr/share/foo/test/system, containing one or more .bats
|
||||
# test files.
|
||||
#
|
||||
|
||||
export PATH=/usr/local/bin:/usr/sbin:/usr/bin
|
||||
|
||||
FULL_LOG=/tmp/test.debug.log
|
||||
BATS_LOG=/tmp/test.bats.log
|
||||
rm -f $FULL_LOG $BATS_LOG
|
||||
touch $FULL_LOG $BATS_LOG
|
||||
|
||||
exec &> $FULL_LOG
|
||||
|
||||
# Log program versions
|
||||
echo "Packages:"
|
||||
rpm -q ${TEST_PACKAGE} ${TEST_PACKAGE}-tests
|
||||
|
||||
echo "------------------------------"
|
||||
printenv | sort
|
||||
|
||||
testdir=/usr/share/${TEST_PACKAGE}/test/system
|
||||
|
||||
if ! cd $testdir; then
|
||||
echo "FAIL ${TEST_NAME} : cd $testdir" >> /tmp/test.log
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -e /tmp/helper.sh ]; then
|
||||
echo "------------------------------"
|
||||
echo ". /tmp/helper.sh"
|
||||
. /tmp/helper.sh
|
||||
fi
|
||||
|
||||
if [ "$(type -t setup)" = "function" ]; then
|
||||
echo "------------------------------"
|
||||
echo "\$ setup"
|
||||
setup
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "FAIL ${TEST_NAME} : setup" >> /tmp/test.log
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "------------------------------"
|
||||
echo "\$ bats ."
|
||||
bats . &> $BATS_LOG
|
||||
rc=$?
|
||||
|
||||
echo "------------------------------"
|
||||
echo "bats completed with status $rc"
|
||||
|
||||
status=PASS
|
||||
if [ $rc -ne 0 ]; then
|
||||
status=FAIL
|
||||
fi
|
||||
|
||||
echo "${status} ${TEST_NAME}" >> /tmp/test.log
|
||||
|
||||
if [ "$(type -t teardown)" = "function" ]; then
|
||||
echo "------------------------------"
|
||||
echo "\$ teardown"
|
||||
teardown
|
||||
fi
|
||||
|
||||
# FIXME: for CI purposes, always exit 0. This allows subsequent tests.
|
||||
exit 0
|
@ -1,37 +0,0 @@
|
||||
---
|
||||
# Create empty results file, world-writable
|
||||
- name: initialize test.log file
|
||||
copy: dest=/tmp/test.log content='' force=yes mode=0666
|
||||
|
||||
- name: execute tests
|
||||
include: run_one_test.yml
|
||||
with_items: "{{ tests }}"
|
||||
loop_control:
|
||||
loop_var: test
|
||||
|
||||
- name: pull test.log results
|
||||
fetch:
|
||||
src: "/tmp/test.log"
|
||||
dest: "{{ artifacts }}/test.log"
|
||||
flat: yes
|
||||
|
||||
# Copied from standard-test-basic
|
||||
- name: check results
|
||||
shell: grep "^FAIL" /tmp/test.log
|
||||
register: test_fails
|
||||
# Never fail at this step. Just store result of tests.
|
||||
failed_when: False
|
||||
|
||||
- name: preserve results
|
||||
set_fact:
|
||||
role_result_failed: "{{ (test_fails.stdout|d|length > 0) or (test_fails.stderr|d|length > 0) }}"
|
||||
role_result_msg: "{{ test_fails.stdout|d('tests failed.') }}"
|
||||
|
||||
- name: display results
|
||||
vars:
|
||||
msg: |
|
||||
Tests failed: {{ role_result_failed|d('Undefined') }}
|
||||
Tests msg: {{ role_result_msg|d('None') }}
|
||||
debug:
|
||||
msg: "{{ msg.split('\n') }}"
|
||||
failed_when: "role_result_failed|bool"
|
@ -1,52 +0,0 @@
|
||||
---
|
||||
- name: "{{ test.name }} | install test packages"
|
||||
dnf: name="{{ test.package }}-tests" state=installed
|
||||
|
||||
- name: "{{ test.name }} | define helper variables"
|
||||
set_fact:
|
||||
test_name_oneword: "{{ test.name | replace(' ','-') }}"
|
||||
|
||||
# UGH. This is necessary because our caller sets some environment variables
|
||||
# and we need to set a few more based on other caller variables; then we
|
||||
# need to combine the two dicts when running the test. This seems to be
|
||||
# the only way to do it in ansible.
|
||||
- name: "{{ test.name }} | define local environment"
|
||||
set_fact:
|
||||
local_environment:
|
||||
TEST_NAME: "{{ test.name }}"
|
||||
TEST_PACKAGE: "{{ test.package }}"
|
||||
TEST_ENV: "{{ test.environment }}"
|
||||
|
||||
- name: "{{ test.name }} | setup/teardown helper | see if exists"
|
||||
local_action: stat path={{ role_path }}/files/helper.{{ test_name_oneword }}.sh
|
||||
register: helper
|
||||
|
||||
- name: "{{ test.name }} | setup/teardown helper | install"
|
||||
copy: src=helper.{{ test_name_oneword }}.sh dest=/tmp/helper.sh
|
||||
when: helper.stat.exists
|
||||
|
||||
- name: "{{ test.name }} | run test"
|
||||
script: ./run_bats_tests.sh
|
||||
args:
|
||||
chdir: /usr/share/{{ test.package }}/test/system
|
||||
become: "{{ true if test.become is defined else false }}"
|
||||
become_user: testuser
|
||||
environment: "{{ local_environment | combine(test.environment) }}"
|
||||
|
||||
- name: "{{ test.name }} | pull logs"
|
||||
fetch:
|
||||
src: "/tmp/test.{{ item }}.log"
|
||||
dest: "{{ artifacts }}/test.{{ test_name_oneword }}.{{ item }}.log"
|
||||
flat: yes
|
||||
with_items:
|
||||
- bats
|
||||
- debug
|
||||
|
||||
- name: "{{ test.name }} | remove remote logs and helpers"
|
||||
file:
|
||||
dest=/tmp/{{ item }}
|
||||
state=absent
|
||||
with_items:
|
||||
- test.bats.log
|
||||
- test.debug.log
|
||||
- helper.sh
|
@ -1,2 +0,0 @@
|
||||
summary: Execute a simple command
|
||||
test: podman run -t --rm fedora cat /etc/os-release | grep 'Fedora Linux'
|
@ -1,49 +0,0 @@
|
||||
---
|
||||
- hosts: localhost
|
||||
tags: classic
|
||||
vars:
|
||||
- artifacts: ./artifacts
|
||||
roles:
|
||||
- role: bats_installed
|
||||
- role: rootless_user
|
||||
- role: run_bats_tests
|
||||
tests:
|
||||
- name: podman root netavark
|
||||
package: podman
|
||||
environment:
|
||||
CI_DESIRED_NETWORK: netavark
|
||||
PODMAN: /usr/bin/podman
|
||||
QUADLET: /usr/libexec/podman/quadlet
|
||||
PODMAN_TESTING: /usr/bin/podman-testing
|
||||
|
||||
- name: podman rootless netavark
|
||||
package: podman
|
||||
environment:
|
||||
CI_DESIRED_NETWORK: netavark
|
||||
PODMAN: /usr/bin/podman
|
||||
QUADLET: /usr/libexec/podman/quadlet
|
||||
PODMAN_TESTING: /usr/bin/podman-testing
|
||||
become: true
|
||||
|
||||
- name: podman root cni
|
||||
package: podman
|
||||
environment:
|
||||
CI_DESIRED_NETWORK: cni
|
||||
PODMAN: /usr/bin/podman
|
||||
QUADLET: /usr/libexec/podman/quadlet
|
||||
PODMAN_TESTING: /usr/bin/podman-testing
|
||||
|
||||
- name: podman rootless cni
|
||||
package: podman
|
||||
environment:
|
||||
CI_DESIRED_NETWORK: cni
|
||||
PODMAN: /usr/bin/podman
|
||||
QUADLET: /usr/libexec/podman/quadlet
|
||||
PODMAN_TESTING: /usr/bin/podman-testing
|
||||
become: true
|
||||
|
||||
#- name: podman-remote root
|
||||
#package: podman
|
||||
#environment:
|
||||
# PODMAN: /usr/bin/podman-remote
|
||||
# QUADLET: /usr/libexec/podman/quadlet
|
Loading…
Reference in New Issue
Block a user