From 0412f64ac6ad030d5a068f1176ecadcbdd77d41f Mon Sep 17 00:00:00 2001 From: Andrei Stepanov Date: Apr 27 2020 19:02:58 +0000 Subject: Basic&beakerlib take into account external termination. Signed-off-by: Andrei Stepanov --- diff --git a/roles/standard-test-basic/files/run-basic-test b/roles/standard-test-basic/files/run-basic-test index 8f456ac..b49f895 100755 --- a/roles/standard-test-basic/files/run-basic-test +++ b/roles/standard-test-basic/files/run-basic-test @@ -109,24 +109,42 @@ debug "Timeout: $STR_TIMEOUT" # Starting from this point and bellow any fail is considered as a test fail. Exit code == 0. clean_exit() { - rc=$?; + rc=$? + # WARNING! At this place ansible closes all FD for STDIN STDERR. + # echo "something" > ANY will not work, and will fail + # With the above do next relax: + set +efu + # Also any output to old tee-STDERR/STDOUT will terminate clean_exit() trap - SIGINT SIGTERM SIGABRT EXIT # clear the trap - echo "Run test '$STR_TEST_NAME': done. Test's exit code: $rc" + echo "Run test '$STR_TEST_NAME': done. Test's exit code: $rc" >&4 + if [[ $terminated_outside -eq 1 ]]; then + echo "The test was terminated outside." >&4 + echo "Mark current test as ERROR." >&4 + rc=77 + fi # Exit code == 0, no matter of the test result. # Close tee pipes - for pid in $(ps -o pid --no-headers --ppid $$); do - if [ -n "$(ps -p $pid -o pid=)" ]; then - kill -s HUP $pid + for pid in $(ps -o pid --no-headers --ppid $$ 2>/dev/null); do + if [ -n "$(ps -p $pid -o pid= 2>/dev/null)" ]; then + kill -s HUP $pid > /dev/null 2>&1 fi done + # At this place STDIN/STDOUT(tee) are closed. + # Can work original STDOUT/STDERR &3 and &4. + # Depends how this command was invoked. local status="FAIL" # Return non-zero when test command not found if [[ $rc -eq 127 ]]; then - echo "$STR_TEST_NAME (problem with test execution)" >&2 + echo "$STR_TEST_NAME (problem with test execution)" >&4 + status="ERROR" + elif [[ $rc -eq 77 ]]; then + # Test was terminated outside. + # For example: ansible-playbook was terminated + echo "$STR_TEST_NAME (test was terminated outside)" >&4 status="ERROR" elif [[ $rc -eq 124 ]]; then # test case timed out - echo "$STR_TEST_NAME (test aborted due to timeout)" >&2 + echo "$STR_TEST_NAME (test aborted due to timeout)" >&4 status="ERROR" elif [[ $rc -eq 0 ]]; then status="PASS" @@ -158,22 +176,36 @@ clean_exit() { cp -f $STR_WORKDIR/$file $STR_ARTIFACTS_DIR/$STR_TEST_NAME || true set -f done + # If killed outside, return code will not be 0, no matter what. exit 0 } trap clean_exit SIGINT SIGTERM SIGABRT EXIT +terminated_outside=1 +rc=0 export PATH="$PATH:$STR_WORKDIR" mkdir -p "$STR_ARTIFACTS_DIR" # add str_ prefix to test logs logfile="$STR_ARTIFACTS_DIR/$(echo "str_$STR_TEST_NAME" | sed -e 's/\//-/g').log" logfile="$(realpath "$logfile")" -exec > >(tee -a "$logfile") 2>&1 +# Save real STDIN/STDERR to 3 and 4 +exec 3>&1 4>&2 > >(tee -a "$logfile") 2>&1 cd "$STR_WORKDIR" # Purpose to spawn new bash is to ignore -efu setting for current shell # Test command: run-basic-test -w wodir -c 'false; echo 123; echo 333 >&2; touch "123 123"; exit 43' -t my_test -a logs -v if [ -f "$STR_CMD" ]; then chmod 0775 "$STR_CMD" fi -timeout --foreground "$STR_TIMEOUT" bash -c "$STR_CMD" +timeout --foreground "$STR_TIMEOUT" bash -c "$STR_CMD" || rc=$? +# `terminated_outside` epxlanation: +# This is for a case when STR is driven by STR-pipeline. +# The STR-pipeline has code: `timeout 4h ansible-playbook`. +# `ansible-playbook` is terminated by `timeout` command. +# Active ansible-task (this scipt) is terminated with HUP signal. +# In this case `clean_exit()` is called with: +# Signal: HUP +# Rerurn code: 0 (the `timeout` command will be terminated itself ==> no return code from test command) +# If terminated_outside==1 mark unfinished script as failed. +terminated_outside=0 # Explicit return code -exit $? +exit $rc diff --git a/roles/standard-test-beakerlib/files/run-beakerlib-test b/roles/standard-test-beakerlib/files/run-beakerlib-test index 30d6680..c733502 100755 --- a/roles/standard-test-beakerlib/files/run-beakerlib-test +++ b/roles/standard-test-beakerlib/files/run-beakerlib-test @@ -93,15 +93,28 @@ STR_BKR_TEST_DASHED=$(echo "$STR_BKR_TEST" | sed -e 's/\//-/g') # Starting from this point and bellow any fail is considered as a test fail. Exit code == 0. clean_exit() { - rc=$?; + rc=$? + # WARNING! At this place ansible closes all FD for STDIN STDERR. + # echo "something" > ANY will not work, and will fail + # With the above, relax: + set +efu + # Also any output to old tee-STDERR/STDOUT will terminate clean_exit() trap - SIGINT SIGTERM SIGABRT EXIT # clear the trap - echo "Run test $STR_BKR_TEST: done." + echo "Run test '$STR_BKR_NAME': done. Test's exit code: $rc" >&4 + if [[ $terminated_outside -eq 1 ]]; then + echo "The test was terminated outside." >&4 + echo "Mark current test as ERROR." >&4 + rc=77 + fi # Close tee pipes - for pid in $(ps -o pid --no-headers --ppid $$); do - if [ -n "$(ps -p $pid -o pid=)" ]; then - kill -s HUP $pid + for pid in $(ps -o pid --no-headers --ppid $$ 2>/dev/null); do + if [ -n "$(ps -p $pid -o pid= 2>/dev/null)" ]; then + kill -s HUP $pid > /dev/null 2>&1 fi done + # At this place STDIN/STDOUT(tee) are closed. + # Can work original STDOUT/STDERR &3 and &4. + # Depends how this command was invoked. # Check test result status local log_file_name="$STR_BKR_TEST_DASHED.log" local log_file_path="$STR_ARTIFACTS_DIR/$log_file_name" @@ -110,7 +123,12 @@ clean_exit() { status="ERROR" elif [[ $rc -eq 124 ]]; then # test case timed out - echo "$STR_BKR_TEST (test aborted due to timeout)" >&2 + echo "$STR_BKR_TEST (test aborted due to timeout)" >&4 + status="ERROR" + elif [[ $rc -eq 77 ]]; then + # Test was terminated outside. + # For example: ansible-playbook was terminated + echo "$STR_TEST_NAME (test was terminated outside)" >&4 status="ERROR" elif grep -q "RESULT: WARN" "$log_file_path"; then status="ERROR" @@ -143,9 +161,12 @@ clean_exit() { fi done # Exit code == 0, no matter of the test result. + # If killed outside, return code will not be 0, no matter what. exit 0 } trap clean_exit SIGINT SIGTERM SIGABRT EXIT +terminated_outside=1 +rc=0 # For beakerlib-libraries export PATH="$PATH:$STR_WORKDIR" @@ -154,6 +175,7 @@ logfile_stdout="$STR_ARTIFACTS_DIR/$STR_BKR_TEST_DASHED.log" logfile_stderr="$STR_ARTIFACTS_DIR/$STR_BKR_TEST_DASHED-err.log" # OUTPUTFILE has influence on beakerlib-libraries output export OUTPUTFILE="$(realpath "$logfile_stdout")" +# Save real STDIN/STDERR to 3 and 4 exec 3>&1 4>&2 1> >(tee -a "$logfile_stdout" >&3) 2> >(tee -a "$logfile_stderr" >&4) mkdir -p "$STR_WORKDIR" cd "$STR_WORKDIR" @@ -161,14 +183,16 @@ cd "$STR_WORKDIR" if ! [ -d "$STR_BKR_TEST" ] && ! [ -f "$STR_BKR_TEST" ]; then # Next string goes to .log file echo "FAIL test $STR_BKR_TEST does not appear to be a file or directory" + terminated_outside=0 exit 127 fi if [ -f "$STR_BKR_TEST" ]; then debug "Running test from file: $STR_BKR_TEST" cd $(dirname "$STR_BKR_TEST") - timeout --foreground "$STR_TIMEOUT" /bin/sh -e ./$(basename "$STR_BKR_TEST") - exit + timeout --foreground "$STR_TIMEOUT" /bin/sh -e ./$(basename "$STR_BKR_TEST") || rc=$? + terminated_outside=0 + exit $rc fi if [ -d "$STR_BKR_TEST" ]; then @@ -178,12 +202,13 @@ if [ -d "$STR_BKR_TEST" ]; then get-test-deps -i . if [ -f "Makefile" ] && command -p -v "make" >"/dev/null" 2>&1; then debug "Running test from Makefile" - timeout --foreground "$STR_TIMEOUT" make run + timeout --foreground "$STR_TIMEOUT" make run || rc=$? elif [ -f "runtest.sh" ]; then debug "Running test from runtest.sh" - timeout --foreground "$STR_TIMEOUT" /bin/sh -e ./runtest.sh + timeout --foreground "$STR_TIMEOUT" /bin/sh -e ./runtest.sh || rc=$? else echo "FAIL test $STR_BKR_TEST do not know how to run test" fi - exit + terminated_outside=0 + exit $rc fi diff --git a/tests/test-basic-stdout-stderr/expected_output.txt b/tests/test-basic-stdout-stderr/expected_output.txt index 0cb05d6..1d385da 100644 --- a/tests/test-basic-stdout-stderr/expected_output.txt +++ b/tests/test-basic-stdout-stderr/expected_output.txt @@ -4,4 +4,3 @@ error2 pass2 pass3 error3 -Run test 'test-basic-stdout-stderr': done. Test's exit code: 0