From ec0915ce7c55c2ad263b60ba2b1c0a4ff7bec25d Mon Sep 17 00:00:00 2001 From: Sebastien Le Digabel Date: Mon, 2 Aug 2021 10:11:48 +0100 Subject: [PATCH] Adding some unit testing for entrypoint.sh The unit tests are simulating a run for entrypoint. It creates some dummy config.sh and runsvc.sh and makes sure the logic behind entrypoint.sh is correct. Unfortunately the entrypoint.sh contains some sections that are not mockable so I had to put some logic in there too. Testing includes for now: - the normal scenario - the normal non-ephemeral scenario - the configuration failure scenario Also tested the entrypoint.sh on a real runner, still works as expected. --- runner/entrypoint.sh | 46 +++++++----- test/entrypoint/entrypoint_unittest.sh | 37 ++++++++++ test/entrypoint/logging.sh | 18 +++++ .../should_retry_configuring/config.sh | 28 +++++++ .../should_retry_configuring/runsvc.sh | 31 ++++++++ .../should_retry_configuring/test.sh | 74 +++++++++++++++++++ .../should_work_non_ephemeral/config.sh | 28 +++++++ .../should_work_non_ephemeral/runsvc.sh | 31 ++++++++ .../should_work_non_ephemeral/test.sh | 72 ++++++++++++++++++ .../entrypoint/should_work_normally/config.sh | 28 +++++++ .../entrypoint/should_work_normally/runsvc.sh | 31 ++++++++ test/entrypoint/should_work_normally/test.sh | 71 ++++++++++++++++++ 12 files changed, 477 insertions(+), 18 deletions(-) create mode 100755 test/entrypoint/entrypoint_unittest.sh create mode 100755 test/entrypoint/logging.sh create mode 100755 test/entrypoint/should_retry_configuring/config.sh create mode 100755 test/entrypoint/should_retry_configuring/runsvc.sh create mode 100755 test/entrypoint/should_retry_configuring/test.sh create mode 100755 test/entrypoint/should_work_non_ephemeral/config.sh create mode 100755 test/entrypoint/should_work_non_ephemeral/runsvc.sh create mode 100755 test/entrypoint/should_work_non_ephemeral/test.sh create mode 100755 test/entrypoint/should_work_normally/config.sh create mode 100755 test/entrypoint/should_work_normally/runsvc.sh create mode 100755 test/entrypoint/should_work_normally/test.sh diff --git a/runner/entrypoint.sh b/runner/entrypoint.sh index e48966d9..14a14e1d 100755 --- a/runner/entrypoint.sh +++ b/runner/entrypoint.sh @@ -1,20 +1,22 @@ #!/bin/bash +RUNNER_HOME=${RUNNER_HOME:-/runner} + LIGHTGREEN="\e[0;32m" LIGHTRED="\e[0;31m" WHITE="\e[0;97m" RESET="\e[0m" log(){ - echo -e "${WHITE}${@}${RESET}" 1>&2 + printf "${WHITE}${@}${RESET}\n" 1>&2 } success(){ - echo -e "${LIGHTGREEN}${@}${RESET}" 1>&2 + printf "${LIGHTGREEN}${@}${RESET}\n" 1>&2 } error(){ - echo -e "${LIGHTRED}${@}${RESET}" 1>&2 + printf "${LIGHTRED}${@}${RESET}\n" 1>&2 } if [ ! -z "${STARTUP_DELAY_IN_SECONDS}" ]; then @@ -22,6 +24,7 @@ if [ ! -z "${STARTUP_DELAY_IN_SECONDS}" ]; then sleep ${STARTUP_DELAY_IN_SECONDS} fi + if [ -z "${GITHUB_URL}" ]; then log "Working with public GitHub" GITHUB_URL="https://github.com/" @@ -61,15 +64,19 @@ if [ -z "${RUNNER_REPO}" ] && [ -n "${RUNNER_GROUP}" ];then fi # Hack due to https://github.com/actions-runner-controller/actions-runner-controller/issues/252#issuecomment-758338483 -if [ ! -d /runner ]; then - error "/runner should be an emptyDir mount. Please fix the pod spec." +if [ ! -d ${RUNNER_HOME} ]; then + error "${RUNNER_HOME} should be an emptyDir mount. Please fix the pod spec." exit 1 fi -sudo chown -R runner:docker /runner -cp -r /runnertmp/* /runner/ +# if this is not a testing environment +if [ -z "$UNITTEST" ]; then + sudo chown -R runner:docker ${RUNNER_HOME} + mv /runnertmp/* ${RUNNER_HOME}/ +fi -cd /runner +cd ${RUNNER_HOME} +# past that point, it's all relative pathes from /runner config_args=() if [ "${RUNNER_FEATURE_FLAG_EPHEMERAL:-}" == "true" -a "${RUNNER_EPHEMERAL}" != "false" ]; then @@ -88,16 +95,17 @@ while [[ ${retries_left} -gt 0 ]]; do --labels "${RUNNER_LABELS}" \ --work "${RUNNER_WORKDIR}" "${config_args[@]}" - if [ -f /runner/.runner ]; then + if [ -f .runner ]; then success "Runner successfully configured." break fi + error "Configuration failed. Retrying" retries_left=$((retries_left - 1)) sleep 1 done -if [ ! -f /runner/.runner ]; then +if [ ! -f .runner ]; then # we couldn't configure and register the runner; no point continuing error "Configuration failed!" exit 2 @@ -130,15 +138,17 @@ if [ -n "${RUNNER_REGISTRATION_ONLY}" ]; then exit 0 fi -mkdir ./externals -# Hack due to the DinD volumes -mv ./externalstmp/* ./externals/ +if [ -z "$UNITTEST" ]; then + mkdir ./externals + # Hack due to the DinD volumes + mv ./externalstmp/* ./externals/ -for f in runsvc.sh RunnerService.js; do - diff {bin,patched}/${f} || : - sudo mv bin/${f}{,.bak} - sudo mv {patched,bin}/${f} -done + for f in runsvc.sh RunnerService.js; do + diff {bin,patched}/${f} || : + sudo mv bin/${f}{,.bak} + sudo mv {patched,bin}/${f} + done +fi args=() if [ "${RUNNER_FEATURE_FLAG_EPHEMERAL:-}" != "true" -a "${RUNNER_EPHEMERAL}" != "false" ]; then diff --git a/test/entrypoint/entrypoint_unittest.sh b/test/entrypoint/entrypoint_unittest.sh new file mode 100755 index 00000000..28e24fe9 --- /dev/null +++ b/test/entrypoint/entrypoint_unittest.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +source logging.sh + +for unittest in ./should*; do + log "**********************************" + log " UNIT TEST: ${unittest}" + log "**********************************" + log "" + + cd ${unittest} + ./test.sh + ret_code=$? + cd .. + + log "" + log "" + if [ "${ret_code}" = "0" ]; then + success "Completed: unit test ${unittest}" + else + error "Completed: unit test ${unittest} with errors" + failed="true" + fi +done + +if [ -n "${failed:-}" ]; then + error "" + error "*************************************" + error "All unit tests completed, with errors" + error "*************************************" + exit 1 +else + success "" + success "***************************************" + success "All unit tests completed with no errors" + success "***************************************" +fi diff --git a/test/entrypoint/logging.sh b/test/entrypoint/logging.sh new file mode 100755 index 00000000..3d13bb9b --- /dev/null +++ b/test/entrypoint/logging.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +export LIGHTGREEN='\e[0;32m' +export LIGHTRED='\e[0;31m' +export WHITE='\e[0;97m' +export RESET='\e[0m' + +log(){ + printf "${WHITE}$@${RESET}\n" 2>&1 +} + +success(){ + printf "${LIGHTGREEN}$@${RESET}\n" 2>&1 +} + +error(){ + printf "${LIGHTRED}$@${RESET}\n" 2>&1 +} diff --git a/test/entrypoint/should_retry_configuring/config.sh b/test/entrypoint/should_retry_configuring/config.sh new file mode 100755 index 00000000..49db04b8 --- /dev/null +++ b/test/entrypoint/should_retry_configuring/config.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +set -euo pipefail + +export LIGHTGREEN='\e[0;32m' +export LIGHTRED='\e[0;31m' +export WHITE='\e[0;97m' +export RESET='\e[0m' + +log(){ + printf "\t${WHITE}$@${RESET}\n" 2>&1 +} + +success(){ + printf "\t${LIGHTGREEN}$@${RESET}\n" 2>&1 +} + +error(){ + printf "\t${LIGHTRED}$@${RESET}\n" 2>&1 + exit 1 +} + +success "I'm pretending the configuration is not successful" +# increasing a counter to measure how many times we restarted +count=`cat counter 2>/dev/null|| echo "0"` +count=$((count + 1)) +echo ${count} > counter + diff --git a/test/entrypoint/should_retry_configuring/runsvc.sh b/test/entrypoint/should_retry_configuring/runsvc.sh new file mode 100755 index 00000000..a46a801b --- /dev/null +++ b/test/entrypoint/should_retry_configuring/runsvc.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +set -euo pipefail + +export LIGHTGREEN='\e[0;32m' +export LIGHTRED='\e[0;31m' +export WHITE='\e[0;97m' +export RESET='\e[0m' + +log(){ + printf "\t${WHITE}$@${RESET}\n" 2>&1 +} + +success(){ + printf "\t${LIGHTGREEN}$@${RESET}\n" 2>&1 +} + +error(){ + printf "\t${LIGHTRED}$@${RESET}\n" 2>&1 + exit 1 +} + +success "" +success "Running the service..." +# SHOULD NOT HAPPEN +# creating a file to show this script has run +touch runsvc_ran +success "...successful" +success "" + + diff --git a/test/entrypoint/should_retry_configuring/test.sh b/test/entrypoint/should_retry_configuring/test.sh new file mode 100755 index 00000000..feb66e91 --- /dev/null +++ b/test/entrypoint/should_retry_configuring/test.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +# UNITTEST: retry config +# Will simulate a configuration failure and expects: +# - the configuration step to be run 10 times +# - the entrypoint script to exit with error code 2 +# - the runsvc.sh script to never run. + +source ../logging.sh + +entrypoint_log() { + while read I; do + printf "\tentrypoint.sh: $I\n" + done +} + +log "Setting up the test" +export UNITTEST=true +export RUNNER_HOME=localhome +export RUNNER_NAME="example_runner_name" +export RUNNER_REPO="myorg/myrepo" +export RUNNER_TOKEN="xxxxxxxxxxxxx" + +mkdir -p ${RUNNER_HOME}/bin +# add up the config.sh and runsvc.sh +ln -s ../config.sh ${RUNNER_HOME}/config.sh +ln -s ../../runsvc.sh ${RUNNER_HOME}/bin/runsvc.sh + +cleanup() { + rm -rf ${RUNNER_HOME} + unset UNITTEST + unset RUNNERHOME + unset RUNNER_NAME + unset RUNNER_REPO + unset RUNNER_TOKEN +} + +trap cleanup SIGINT SIGTERM SIGQUIT EXIT + +log "Running the entrypoint" +log "" + +../../../runner/entrypoint.sh 2> >(entrypoint_log) + +if [ "$?" != "2" ]; then + error "=========================================" + error "Configuration should have thrown an error" + exit 1 +fi +success "Entrypoint didn't complete successfully" +success "" + +log "Checking the counter, should have 10 iterations" +count=`cat ${RUNNER_HOME}/counter || "notfound"` +if [ "${count}" != "10" ]; then + error "=============================================" + error "The retry loop should have done 10 iterations" + exit 1 +fi +success "Retry loop went up to 10" +success + +log "Checking that runsvc never ran" +if [ -f ${RUNNER_HOME}/runsvc_ran ]; then + error "=================================================================" + error "runsvc was invoked, entrypoint.sh should have failed before that." + exit 1 +fi + +success "runsvc.sh never ran" +success +success "===========================" +success "Test completed successfully" +exit 0 diff --git a/test/entrypoint/should_work_non_ephemeral/config.sh b/test/entrypoint/should_work_non_ephemeral/config.sh new file mode 100755 index 00000000..200874d6 --- /dev/null +++ b/test/entrypoint/should_work_non_ephemeral/config.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +export LIGHTGREEN='\e[0;32m' +export LIGHTRED='\e[0;31m' +export WHITE='\e[0;97m' +export RESET='\e[0m' + +log(){ + printf "\t${WHITE}$@${RESET}\n" 2>&1 +} + +success(){ + printf "\t${LIGHTGREEN}$@${RESET}\n" 2>&1 +} + +error(){ + printf "\t${LIGHTRED}$@${RESET}\n" 2>&1 +} + +success "I'm configured normally" +touch .runner +success "created a dummy config file" +success +# adding a counter to see how many times we've gone through a configuration step +count=`cat counter 2>/dev/null|| echo "0"` +count=$((count + 1)) +echo ${count} > counter + diff --git a/test/entrypoint/should_work_non_ephemeral/runsvc.sh b/test/entrypoint/should_work_non_ephemeral/runsvc.sh new file mode 100755 index 00000000..fa766433 --- /dev/null +++ b/test/entrypoint/should_work_non_ephemeral/runsvc.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +set -euo pipefail + +export LIGHTGREEN='\e[0;32m' +export LIGHTRED='\e[0;31m' +export WHITE='\e[0;97m' +export RESET='\e[0m' + +log(){ + printf "\t${WHITE}$@${RESET}\n" 2>&1 +} + +success(){ + printf "\t${LIGHTGREEN}$@${RESET}\n" 2>&1 +} + +error(){ + printf "\t${LIGHTRED}$@${RESET}\n" 2>&1 + exit 1 +} + +success "" +success "Running the service..." +# test if --once is present as a parameter +echo "$*" | grep -q 'once' && error "Should not include --once in the parameters" +success "...successful" +touch runsvc_ran +success "" + + diff --git a/test/entrypoint/should_work_non_ephemeral/test.sh b/test/entrypoint/should_work_non_ephemeral/test.sh new file mode 100755 index 00000000..a983c877 --- /dev/null +++ b/test/entrypoint/should_work_non_ephemeral/test.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +# UNITTEST: should work as non ephemeral +# Will simulate a scenario where ephemeral=false. expects: +# - the configuration step to be run exactly once +# - the entrypoint script to exit with no error +# - the runsvc.sh script to run without the --once flag + +source ../logging.sh + +entrypoint_log() { + while read I; do + printf "\tentrypoint.sh: $I\n" + done +} + +log "Setting up the test" +export UNITTEST=true +export RUNNER_HOME=localhome +export RUNNER_NAME="example_runner_name" +export RUNNER_REPO="myorg/myrepo" +export RUNNER_TOKEN="xxxxxxxxxxxxx" +export RUNNER_EPHEMERAL=false + +mkdir -p ${RUNNER_HOME}/bin +# add up the config.sh and runsvc.sh +ln -s ../config.sh ${RUNNER_HOME}/config.sh +ln -s ../../runsvc.sh ${RUNNER_HOME}/bin/runsvc.sh + +cleanup() { + rm -rf ${RUNNER_HOME} + unset UNITTEST + unset RUNNERHOME + unset RUNNER_NAME + unset RUNNER_REPO + unset RUNNER_TOKEN + unset RUNNER_EPHEMERAL +} + +trap cleanup SIGINT SIGTERM SIGQUIT EXIT + +log "Running the entrypoint" +log "" + +../../../runner/entrypoint.sh 2> >(entrypoint_log) + +if [ "$?" != "0" ]; then + error "===========================================" + error "Entrypoint script did not exit successfully" + exit 1 +fi + +log "Testing if we went through the configuration step only once" +count=`cat ${RUNNER_HOME}/counter || echo "not_found"` +if [ ${count} != "1" ]; then + error "===============================================" + error "The configuration step was not run exactly once" + exit 1 +fi + +success "The configuration ran ${count} time(s)" + +log "Testing if runsvc ran" +if [ ! -f "${RUNNER_HOME}/runsvc_ran" ]; then + error "==============================" + error "The runner service has not run" + exit 1 +fi +success "The service ran" +success "" +success "===========================" +success "Test completed successfully" diff --git a/test/entrypoint/should_work_normally/config.sh b/test/entrypoint/should_work_normally/config.sh new file mode 100755 index 00000000..f1e7a7bf --- /dev/null +++ b/test/entrypoint/should_work_normally/config.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +export LIGHTGREEN='\e[0;32m' +export LIGHTRED='\e[0;31m' +export WHITE='\e[0;97m' +export RESET='\e[0m' + +log(){ + printf "\t${WHITE}$@${RESET}\n" 2>&1 +} + +success(){ + printf "\t${LIGHTGREEN}$@${RESET}\n" 2>&1 +} + +error(){ + printf "\t${LIGHTRED}$@${RESET}\n" 2>&1 +} + +success "I'm configured normally" +touch .runner +success "created a dummy config file" +success +# Adding a counter to see how many times we've gone through the configuration step +count=`cat counter 2>/dev/null|| echo "0"` +count=$((count + 1)) +echo ${count} > counter + diff --git a/test/entrypoint/should_work_normally/runsvc.sh b/test/entrypoint/should_work_normally/runsvc.sh new file mode 100755 index 00000000..8eb268fe --- /dev/null +++ b/test/entrypoint/should_work_normally/runsvc.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +set -euo pipefail + +export LIGHTGREEN='\e[0;32m' +export LIGHTRED='\e[0;31m' +export WHITE='\e[0;97m' +export RESET='\e[0m' + +log(){ + printf "\t${WHITE}$@${RESET}\n" 2>&1 +} + +success(){ + printf "\t${LIGHTGREEN}$@${RESET}\n" 2>&1 +} + +error(){ + printf "\t${LIGHTRED}$@${RESET}\n" 2>&1 + exit 1 +} + +success "" +success "Running the service..." +# test if --once is present as a parameter +echo "$*" | grep -q 'once' || error "Should include --once in the parameters"j +success "...successful" +touch runsvc_ran +success "" + + diff --git a/test/entrypoint/should_work_normally/test.sh b/test/entrypoint/should_work_normally/test.sh new file mode 100755 index 00000000..5b7bfb3f --- /dev/null +++ b/test/entrypoint/should_work_normally/test.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +# UNITTEST: should work normally +# Will simulate a normal execution scenario. expects: +# - the configuration step to be run exactly once +# - the entrypoint script to exit with no error +# - the runsvc.sh script to run with the --once flag activated. + +source ../logging.sh + +entrypoint_log() { + while read I; do + printf "\tentrypoint.sh: $I\n" + done +} + +log "Setting up the test" +export UNITTEST=true +export RUNNER_HOME=localhome +export RUNNER_NAME="example_runner_name" +export RUNNER_REPO="myorg/myrepo" +export RUNNER_TOKEN="xxxxxxxxxxxxx" + +mkdir -p ${RUNNER_HOME}/bin +# add up the config.sh and runsvc.sh +ln -s ../config.sh ${RUNNER_HOME}/config.sh +ln -s ../../runsvc.sh ${RUNNER_HOME}/bin/runsvc.sh + +cleanup() { + rm -rf ${RUNNER_HOME} + unset UNITTEST + unset RUNNERHOME + unset RUNNER_NAME + unset RUNNER_REPO + unset RUNNER_TOKEN +} + +trap cleanup SIGINT SIGTERM SIGQUIT EXIT + +log "Running the entrypoint" +log "" + +../../../runner/entrypoint.sh 2> >(entrypoint_log) + +if [ "$?" != "0" ]; then + error "==========================" + error "Test completed with errors" + exit 1 +fi + +log "Testing if the configuration step was run only once" +count=`cat ${RUNNER_HOME}/counter || echo "not_found"` +if [ ${count} != "1" ]; then + error "===============================================" + error "The configuration step was not run exactly once" + exit 1 +fi + +success "The configuration ran ${count} time(s)" + +log "Testing if runsvc ran" +if [ ! -f "${RUNNER_HOME}/runsvc_ran" ]; then + error "==============================" + error "The runner service has not run" + exit 1 +fi + +success "The service ran" +success "" +success "===========================" +success "Test completed successfully"