#!/usr/bin/env bats -*- bats -*- # # Tests generated configurations for systemd. # # bats file_tags=ci:parallel load helpers load helpers.network load helpers.registry load helpers.systemd UNIT_FILES=() function start_time() { sleep_to_next_second # Ensure we're on a new second with no previous logging STARTED_TIME=$(date "+%F %R:%S") # Start time for new log time } function setup() { skip_if_remote "quadlet tests are meaningless over remote" skip_if_rootless_cgroupsv1 "Can't use --cgroups=split w/ CGv1 (issue 17456, wontfix)" skip_if_journald_unavailable "Needed for RHEL. FIXME: we might be able to re-enable a subset of tests." test -x "$QUADLET" || die "Cannot run quadlet tests without executable \$QUADLET ($QUADLET)" start_time basic_setup } function teardown() { for UNIT_FILE in ${UNIT_FILES[@]}; do if [[ -e "$UNIT_FILE" ]]; then local service=$(basename "$UNIT_FILE") run systemctl stop "$service" if [ $status -ne 0 ]; then echo "# WARNING: systemctl stop failed in teardown: $output" >&3 fi run systemctl reset-failed "$service" rm -f "$UNIT_FILE" fi done systemctl daemon-reload basic_teardown } # Converts the quadlet file and installs the result it in $UNIT_DIR function run_quadlet() { local sourcefile="$1" local service=$(quadlet_to_service_name "$sourcefile") # quadlet always works on an entire directory, so copy the file # to transform to the given or newly created tmpdir local quadlet_tmpdir="$2" if [ -z "$quadlet_tmpdir" ]; then quadlet_tmpdir=$(mktemp -d --tmpdir=$PODMAN_TMPDIR quadlet.XXXXXX) fi cp $sourcefile $quadlet_tmpdir/ echo "$_LOG_PROMPT $QUADLET $_DASHUSER $UNIT_DIR" QUADLET_UNIT_DIRS="$quadlet_tmpdir" run \ timeout --foreground -v --kill=10 $PODMAN_TIMEOUT \ $QUADLET $_DASHUSER $UNIT_DIR echo "$output" assert $status -eq 0 "Failed to convert quadlet file: $sourcefile" is "$output" "" "quadlet should report no errors" run cat $UNIT_DIR/$service assert $status -eq 0 "Could not cat $UNIT_DIR/$service" echo "$output" local content="$output" # Ensure this is teared down UNIT_FILES+=("$UNIT_DIR/$service") QUADLET_SERVICE_NAME="$service" QUADLET_SERVICE_CONTENT="$content" QUADLET_SYSLOG_ID="$(basename $service .service)" QUADLET_CONTAINER_NAME="systemd-$QUADLET_SYSLOG_ID" } function service_setup() { local service="$1" local option="$2" systemctl daemon-reload local startargs="" local statusexit=0 local activestate="active" # If option wait, start and wait for service to exist if [ "$option" == "wait" ]; then startargs="--wait" statusexit=3 local activestate="inactive" fi systemctl_start $startargs "$service" # FIXME FIXME FIXME: this is racy with short-lived containers! echo "$_LOG_PROMPT systemctl status $service" run systemctl status "$service" echo "$output" assert $status -eq $statusexit "systemctl status $service" echo "$_LOG_PROMPT systemctl show --value --property=ActiveState $service" run systemctl show --value --property=ActiveState "$service" echo "$output" assert $status -eq 0 "systemctl show $service" is "$output" $activestate } # Helper to stop a systemd service running a container function service_cleanup() { local service="$1" local expected_state="$2" run systemctl stop "$service" assert $status -eq 0 "Error stopping systemd unit $service: $output" # Regression test for #11304: confirm that unit stops into correct state if [[ -n "$expected_state" ]]; then run systemctl show --property=ActiveState "$service" assert "$output" = "ActiveState=$expected_state" \ "state of service $service after systemctl stop" fi # reset-failed necessary to clean up stray systemd cruft run systemctl reset-failed "$service" rm -f "$UNIT_DIR/$service" systemctl daemon-reload } function create_secret() { local secret_name=$(random_string) local secret_file=$PODMAN_TMPDIR/secret_$(random_string) local secret=$(random_string) echo $secret > $secret_file run_podman secret create $secret_name $secret_file SECRET_NAME=$secret_name SECRET=$secret } function remove_secret() { local secret_name="$1" run_podman secret rm $secret_name } function wait_for_journal() { local step=1 local count=10 local expect_str= while [ "$#" -gt 0 ]; do case "$1" in -s|--step) step="$2" shift 2 ;; -c|--count) count="$2" shift 2 ;; *) expect_str="$1" shift 1 ;; esac done while [ "$count" -gt 0 ]; do run journalctl "--since=$STARTED_TIME" --unit="$QUADLET_SERVICE_NAME" if [[ "$output" =~ "$expect_str" ]]; then return fi sleep "$step" count=$(( count - 1 )) done die "Timed out waiting for '$expect_str' in journalctl output" } @test "quadlet - basic" { # Network=none is to work around a Pasta bug, can be removed once a patched Pasta is available. # Ref https://github.com/containers/podman/pull/21563#issuecomment-1965145324 local quadlet_file=$PODMAN_TMPDIR/basic_$(safename).container cat > $quadlet_file < $dir1/$quadlet_file < $dir2/$quadlet_file < $quadlet_file < $quadlet_file <&2; top -d 10" EOF run_quadlet "$quadlet_file" service_setup $QUADLET_SERVICE_NAME # Ensure we can access with the custom container name run_podman container inspect --format "{{.State.Status}}" customcontainername is "$output" "running" "container should be started by systemd and hence be running" wait_for_journal "Started $QUADLET_SERVICE_NAME" run journalctl "--since=$STARTED_TIME" --unit="$QUADLET_SERVICE_NAME" assert "$output" =~ "$token_out" "Output can be found with journalctl" assert "$output" =~ "$token_err" "Error can be found with journalctl" assert "$output" =~ "Starting $QUADLET_SERVICE_NAME" "Status information can be found with journalctl" # log priority 3 in journalctl is err. This is documented in syslog(3) run journalctl "--since=$STARTED_TIME" --priority=3 --unit="$QUADLET_SERVICE_NAME" assert "$output" =~ "$token_err" "Error can be found with journalctl --priority=3" assert "$output" !~ "$token_out" "Output can not be found with journalctl --priority=3" service_cleanup $QUADLET_SERVICE_NAME failed } @test "quadlet - labels" { local quadlet_file=$PODMAN_TMPDIR/labels_$(safename).container cat > $quadlet_file < $quadlet_file < $quadlet_file < $quadlet_vol_file < $quadlet_file < $quadlet_vol_file < $quadlet_file < $quadlet_file < $quadlet_network_file < $quadlet_container_file < $quadlet_network_file < $quadlet_file < $quadlet_network_file < $quadlet_file <$yaml_source < $quadlet_file < $quadlet_network_file <$yaml_source < $quadlet_file < $quadlet_file < $quadlet_file < $quadlet_file < $quadlet_file < $quadlet_file <$unitfile <$percent_t_file" Type=oneshot EOF systemctl daemon-reload systemctl_start --wait $service percent_t=$(< $percent_t_file) # Clean up. Don't bother to systemctl-reload, service_setup does that below. rm -f $unitfile # Sanity check: just make sure it's not "/" assert "${#percent_t}" -ge 4 "sanity check: length of %T ($percent_t)" # Step 2: Make a subdirectory in %T, and in there, a scratch file local tmp_path=$(mktemp -d --tmpdir=${percent_t} quadlet.volume.XXXXXX) local tmp_subdir=$(basename $tmp_path) local file_name="f$(random_string 10).txt" local file_content="data_$(random_string 15)" echo $file_content > $tmp_path/$file_name local quadlet_file=$PODMAN_TMPDIR/basic_$(safename).container cat > $quadlet_file < $quadlet_file < $quadlet_file < $yaml_file < $quadlet_file <$yaml_source < $quadlet_file < /test/test.txt" is $(cat $PODMAN_TMPDIR/$local_path/test.txt) "hello" service_cleanup $QUADLET_SERVICE_NAME inactive } # https://github.com/containers/podman/issues/20667 @test "quadlet kube - start error" { local port=$(random_free_port) # Create the YAMl file pod_name="p-$(safename)" container_name="c-$(safename)" yaml_source="$PODMAN_TMPDIR/start_err$(safename).yaml" cat >$yaml_source < $quadlet_file <$yaml_source < $quadlet_file < $quadlet_image_file < $quadlet_volume_file < $quadlet_container_file < $quadlet_kube_volume_yaml_file < $quadlet_kube_volume_unit_file < $quadlet_kube_pod_yaml_file < $quadlet_kube_pod_unit_file < $quadlet_kube_pod_yaml_file < $quadlet_kube_pod_unit_file < $quadlet_image_file < $quadlet_volume_file < $quadlet_container_file < $quadlet_pod_file < $quadlet_container_file <$container_file_path << EOF FROM $untagged_image EOF # Create the YAMl file pod_name="p-$(safename)" container_name="c-$(safename)" yaml_source="$yaml_dir/build_$(safename).yaml" cat >$yaml_source < $quadlet_file <>"${f}" echo "${content}" >>"${f}" done < <(parse_table "${dropin_files}") # Create the base quadlet file quadlet_base="${PODMAN_TMPDIR}/${quadlet_file}" cat > "${quadlet_base}" <$container_file_path << EOF FROM $IMAGE EOF local image_tag=quay.io/i-$(safename):$(random_string) local quadlet_file=$PODMAN_TMPDIR/pull_$(safename).build cat >$quadlet_file << EOF [Build] ImageTag=$image_tag File=$container_file_path Pull=never EOF run_quadlet "$quadlet_file" service_setup $QUADLET_SERVICE_NAME "wait" run_podman rmi -i $image_tag } # vim: filetype=sh