From 6d613ed9af70cf9d91ffa850e0243148d69b6180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Mon, 14 Sep 2026 15:05:03 +0200 Subject: [PATCH 01/31] test: Tweak tomcat_wait_for_n_nodes to run faster when nodes are ready --- test/includes/common.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/includes/common.sh b/test/includes/common.sh index 1fcbd9bb..b1994522 100644 --- a/test/includes/common.sh +++ b/test/includes/common.sh @@ -182,7 +182,7 @@ tomcat_wait_for_n_nodes() { echo "$(date) httpd isn't running or something is VERY wrong" exit 1 fi - NBNODES=-1 + NBNODES=$(curl -s http://localhost:8090/mod_cluster_manager -m 20 | grep "Status: OK" | awk ' { print $3} ' | wc -l) i=0 while [ ${NBNODES} != ${nodes} ] do From 6f18a31dff1a9aa977568e526900712e8574f32e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Mon, 14 Sep 2026 16:43:36 +0200 Subject: [PATCH 02/31] test: Simplify the defaulting of test variables --- test/testsuite.sh | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/test/testsuite.sh b/test/testsuite.sh index 8ef515e9..181d43ce 100644 --- a/test/testsuite.sh +++ b/test/testsuite.sh @@ -4,34 +4,19 @@ # exits with 2 if httpd container build failed # exits with 3 if tomcat container build failed -# configuration of variables -# if you want tests to pass much faster, decrease these values -if [ -z ${FOREVER_PAUSE+x} ]; then - export FOREVER_PAUSE=3600 # sleep period length during which tomcats are run & stopped -fi -if [ -z ${TOMCAT_CYCLE_COUNT+x} ]; then - export TOMCAT_CYCLE_COUNT=100 # the number of repetitions of a test cycle -fi -if [ -z ${ITERATION_COUNT+x} ]; then - export ITERATION_COUNT=50 # the number of iteration of starting/stopping a tomcat -fi -if [ -z ${IMG+x} ]; then - export IMG=mod_proxy_cluster-testsuite-tomcat -fi -if [ -z ${HTTPD_IMG+x} ]; then - export HTTPD_IMG=mod_proxy_cluster-testsuite-httpd -fi - echo "Test parameters are:" -echo " FOREVER_PAUSE=$FOREVER_PAUSE" -echo " TOMCAT_CYCLE_COUNT=$TOMCAT_CYCLE_COUNT" -echo " ITERATION_COUNT=$ITERATION_COUNT" -echo " IMG=$IMG" -echo " HTTPD_IMG=$HTTPD_IMG" +echo " FOREVER_PAUSE=${FOREVER_PAUSE:=3600}" # sleep period length during which tomcats are run & stopped +echo " TOMCAT_CYCLE_COUNT=${TOMCAT_CYCLE_COUNT:=100}" # the number of repetitions of a test cycle +echo " ITERATION_COUNT=${ITERATION_COUNT:=50}" # the number of iteration of starting/stopping a tomcat +echo " IMG=${IMG:=mod_proxy_cluster-testsuite-tomcat}" # tomcat container image +echo " HTTPD_IMG=${HTTPD_IMG:=mod_proxy_cluster-testsuite-httpd}" # httpd with mod_proxy_cluster container image if [ ! -z ${MPC_CONF+x} ]; then echo " MPC_CONF=$MPC_CONF" fi echo " DEBUG=${DEBUG:-Off (undefined)}" +export FOREVER_PAUSE TOMCAT_CYCLE_COUNT ITERATION_COUNT IMG HTTPD_IMG + +>>>>>>> 76f3193 (fix) if [ ! -d logs ]; then mkdir logs From 24936b042df682fdb3872ad8c2ff5747d4979b54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Mon, 14 Sep 2026 16:48:05 +0200 Subject: [PATCH 03/31] test: Make mod_proxy_cluster/mod_proxy_balancer tests execution conditional --- test/testsuite.sh | 88 +++++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 42 deletions(-) diff --git a/test/testsuite.sh b/test/testsuite.sh index 181d43ce..1c8f2fec 100644 --- a/test/testsuite.sh +++ b/test/testsuite.sh @@ -14,10 +14,10 @@ if [ ! -z ${MPC_CONF+x} ]; then echo " MPC_CONF=$MPC_CONF" fi echo " DEBUG=${DEBUG:-Off (undefined)}" +echo " MOD_PROXY_CLUSTER_TESTS=${MOD_PROXY_CLUSTER_TESTS:=On}" +echo " MOD_PROXY_BALANCER_TESTS=${MOD_PROXY_BALANCER_TESTS:=On}" export FOREVER_PAUSE TOMCAT_CYCLE_COUNT ITERATION_COUNT IMG HTTPD_IMG ->>>>>>> 76f3193 (fix) - if [ ! -d logs ]; then mkdir logs fi @@ -47,48 +47,52 @@ echo " Done" res=0 -# IMG name might include specific version, we have to handle that -IMG_NOVER=$(echo $IMG | cut -d: -f1) +if is_enabled "$MOD_PROXY_CLUSTER_TESTS"; then + # IMG name might include specific version, we have to handle that + IMG_NOVER=$(echo $IMG | cut -d: -f1) -for tomcat_version in "9.0" "10.1" "11.0" -do - IMG="$IMG_NOVER:$tomcat_version" tomcat_create $tomcat_version > /dev/null 2>&1 || exit 3 - IMG="$IMG_NOVER:$tomcat_version" run_test basetests.sh "Basic tests with tomcat $tomcat_version" - res=$(expr $res + $?) -done -run_test hangingtests.sh "Hanging tests" -res=$(expr $res + $?) -run_test maintests.sh "Main tests" -res=$(expr $res + $?) -run_test websocket/basic.sh "Websocket tests" -res=$(expr $res + $?) -run_test usealias/testit.sh "UseAlias" -res=$(expr $res + $?) -run_test MODCLUSTER-640/testit.sh "MODCLUSTER-640" -res=$(expr $res + $?) -run_test MODCLUSTER-734/testit.sh "MODCLUSTER-734" -res=$(expr $res + $?) -run_test MODCLUSTER-736/testit.sh "MODCLUSTER-736" -res=$(expr $res + $?) -run_test MODCLUSTER-755/testit.sh "MODCLUSTER-755" -res=$(expr $res + $?) -run_test MODCLUSTER-785/testit.sh "MODCLUSTER-785" -res=$(expr $res + $?) -run_test MODCLUSTER-794/testit.sh "MODCLUSTER-794" -res=$(expr $res + $?) + for tomcat_version in "9.0" "10.1" "11.0" + do + IMG="$IMG_NOVER:$tomcat_version" tomcat_create $tomcat_version > /dev/null 2>&1 || exit 3 + IMG="$IMG_NOVER:$tomcat_version" run_test basetests.sh "Basic tests with tomcat $tomcat_version" + res=$(expr $res + $?) + done + run_test hangingtests.sh "Hanging tests" + res=$(expr $res + $?) + run_test maintests.sh "Main tests" + res=$(expr $res + $?) + run_test websocket/basic.sh "Websocket tests" + res=$(expr $res + $?) + run_test usealias/testit.sh "UseAlias" + res=$(expr $res + $?) + run_test MODCLUSTER-640/testit.sh "MODCLUSTER-640" + res=$(expr $res + $?) + run_test MODCLUSTER-734/testit.sh "MODCLUSTER-734" + res=$(expr $res + $?) + run_test MODCLUSTER-736/testit.sh "MODCLUSTER-736" + res=$(expr $res + $?) + run_test MODCLUSTER-755/testit.sh "MODCLUSTER-755" + res=$(expr $res + $?) + run_test MODCLUSTER-785/testit.sh "MODCLUSTER-785" + res=$(expr $res + $?) + run_test MODCLUSTER-794/testit.sh "MODCLUSTER-794" + res=$(expr $res + $?) +fi -MPC_CONF=httpd/mod_lbmethod_cluster.conf run_test basetests.sh "Basic tests with mod_proxy_balancer" -res=$(expr $res + $?) -MPC_CONF=MODCLUSTER-640/mod_lbmethod_cluster.conf run_test MODCLUSTER-640/testit.sh "MODCLUSTER-640 with mod_proxy_balancer" -res=$(expr $res + $?) -MPC_CONF=MODCLUSTER-734/mod_lbmethod_cluster.conf run_test MODCLUSTER-734/testit.sh "MODCLUSTER-734 with mod_proxy_balancer" -res=$(expr $res + $?) -MPC_CONF=httpd/mod_lbmethod_cluster.conf run_test MODCLUSTER-755/testit.sh "MODCLUSTER-755 with mod_proxy_balancer" -res=$(expr $res + $?) -MPC_CONF=MODCLUSTER-785/mod_lbmethod_cluster.conf run_test MODCLUSTER-785/testit.sh "MODCLUSTER-785 with mod_proxy_balancer" -res=$(expr $res + $?) -MPC_CONF=MODCLUSTER-794/mod_lbmethod_cluster.conf run_test MODCLUSTER-794/testit.sh "MODCLUSTER-794 with mod_proxy_balancer" -res=$(expr $res + $?) +if is_enabled "$MOD_PROXY_BALANCER_TESTS"; then + MPC_CONF=httpd/mod_lbmethod_cluster.conf run_test basetests.sh "Basic tests with mod_proxy_balancer" + res=$(expr $res + $?) + MPC_CONF=MODCLUSTER-640/mod_lbmethod_cluster.conf run_test MODCLUSTER-640/testit.sh "MODCLUSTER-640 with mod_proxy_balancer" + res=$(expr $res + $?) + MPC_CONF=MODCLUSTER-734/mod_lbmethod_cluster.conf run_test MODCLUSTER-734/testit.sh "MODCLUSTER-734 with mod_proxy_balancer" + res=$(expr $res + $?) + MPC_CONF=httpd/mod_lbmethod_cluster.conf run_test MODCLUSTER-755/testit.sh "MODCLUSTER-755 with mod_proxy_balancer" + res=$(expr $res + $?) + MPC_CONF=MODCLUSTER-785/mod_lbmethod_cluster.conf run_test MODCLUSTER-785/testit.sh "MODCLUSTER-785 with mod_proxy_balancer" + res=$(expr $res + $?) + MPC_CONF=MODCLUSTER-794/mod_lbmethod_cluster.conf run_test MODCLUSTER-794/testit.sh "MODCLUSTER-794 with mod_proxy_balancer" + res=$(expr $res + $?) +fi echo -n "Cleaning containers if any..." httpd_remove > /dev/null 2>&1 From aa8d386053dbf6744c99113255067f17d24ed6f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Mon, 14 Sep 2026 17:14:49 +0200 Subject: [PATCH 04/31] ci: Separate mod_proxy_cluster and mod_proxy_balancer tests into two jobs --- .github/workflows/ci.yml | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d981f5e4..85b562ca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,9 +17,7 @@ on: debug: description: "Test logs are generated when on" type: choice - options: - - "on" - - "off" + options: [ "on", "off" ] default: "on" forever-pause: @@ -37,6 +35,18 @@ on: type: number default: 2 + mod_proxy_cluster: + description: "mod_proxy_cluster tests are executed" + type: choice + options: [ "on", "off" ] + default: "on" + + balancer: + description: "mod_proxy_balancer tests are executed" + type: choice + options: [ "on", "off" ] + default: "on" + jobs: cmake-fedora-latest: @@ -298,6 +308,7 @@ jobs: fail-fast: false matrix: os: [ ubuntu-latest, ubuntu-24.04-arm ] + suite: [ mod_proxy_cluster, mod_proxy_balancer ] env: IMG: local-tomcat HTTPD_IMG: local-httpd @@ -305,6 +316,8 @@ jobs: FOREVER_PAUSE: ${{ inputs.forever-pause || 100 }} ITERATION_COUNT: ${{ inputs.iteration-count || 2 }} TOMCAT_CYCLE_COUNT: ${{ inputs.tomcat-cycle-count || 2 }} + MOD_PROXY_CLUSTER_TESTS: ${{ matrix.suite == 'mod_proxy_cluster' && (inputs.mod_proxy_cluster || 'on') || 'off' }} + MOD_PROXY_BALANCER_TESTS: ${{ matrix.suite == 'mod_proxy_balancer' && (inputs.balancer || 'on') || 'off' }} steps: - name: Checkout uses: actions/checkout@v6 @@ -338,7 +351,7 @@ jobs: uses: actions/upload-artifact@v6 if: ${{ env.DEBUG == 'on' }} with: - name: Test logs ${{ matrix.os }} + name: Test logs ${{ matrix.os }} ${{ matrix.suite }} path: | test/logs/* retention-days: 7 From 40fe84e6c984e2b9fc8b699171b4cd890eb04f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Tue, 15 Sep 2026 09:11:13 +0200 Subject: [PATCH 05/31] test: Run Base mod_proxy_balancer tests with multiple tomcat versions too --- test/testsuite.sh | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/test/testsuite.sh b/test/testsuite.sh index 1c8f2fec..7c9bd09a 100644 --- a/test/testsuite.sh +++ b/test/testsuite.sh @@ -37,6 +37,14 @@ else httpd_create > /dev/null 2>&1 || exit 2 tomcat_create > /dev/null 2>&1 || exit 3 fi +# create all main tomcat versions for Base tests +## IMG name might include specific version, we have to handle that +IMG_NOVER=$(echo $IMG | cut -d: -f1) +for tomcat_version in "9.0" "10.1" "11.0" +do + IMG="$IMG_NOVER:$tomcat_version" tomcat_create $tomcat_version > /dev/null 2>&1 || exit 3 +done + echo " Done" # clean everything at first @@ -48,12 +56,8 @@ echo " Done" res=0 if is_enabled "$MOD_PROXY_CLUSTER_TESTS"; then - # IMG name might include specific version, we have to handle that - IMG_NOVER=$(echo $IMG | cut -d: -f1) - for tomcat_version in "9.0" "10.1" "11.0" do - IMG="$IMG_NOVER:$tomcat_version" tomcat_create $tomcat_version > /dev/null 2>&1 || exit 3 IMG="$IMG_NOVER:$tomcat_version" run_test basetests.sh "Basic tests with tomcat $tomcat_version" res=$(expr $res + $?) done @@ -80,13 +84,16 @@ if is_enabled "$MOD_PROXY_CLUSTER_TESTS"; then fi if is_enabled "$MOD_PROXY_BALANCER_TESTS"; then - MPC_CONF=httpd/mod_lbmethod_cluster.conf run_test basetests.sh "Basic tests with mod_proxy_balancer" - res=$(expr $res + $?) + for tomcat_version in "9.0" "10.1" "11.0" + do + IMG="$IMG_NOVER:$tomcat_version" MPC_CONF=httpd/mod_lbmethod_cluster.conf run_test basetests.sh "Basic tests with mod_proxy_balancer and tomcat $tomcat_version" + res=$(expr $res + $?) + done MPC_CONF=MODCLUSTER-640/mod_lbmethod_cluster.conf run_test MODCLUSTER-640/testit.sh "MODCLUSTER-640 with mod_proxy_balancer" res=$(expr $res + $?) MPC_CONF=MODCLUSTER-734/mod_lbmethod_cluster.conf run_test MODCLUSTER-734/testit.sh "MODCLUSTER-734 with mod_proxy_balancer" res=$(expr $res + $?) - MPC_CONF=httpd/mod_lbmethod_cluster.conf run_test MODCLUSTER-755/testit.sh "MODCLUSTER-755 with mod_proxy_balancer" + MPC_CONF=httpd/mod_lbmethod_cluster.conf run_test MODCLUSTER-755/testit.sh "MODCLUSTER-755 with mod_proxy_balancer" res=$(expr $res + $?) MPC_CONF=MODCLUSTER-785/mod_lbmethod_cluster.conf run_test MODCLUSTER-785/testit.sh "MODCLUSTER-785 with mod_proxy_balancer" res=$(expr $res + $?) From 7c5e86cad100c293dccb34b0d8bf96232f797015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Tue, 15 Sep 2026 10:05:37 +0200 Subject: [PATCH 06/31] test: Make run_test format wider --- test/includes/common.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/includes/common.sh b/test/includes/common.sh index b1994522..4f73c072 100644 --- a/test/includes/common.sh +++ b/test/includes/common.sh @@ -8,9 +8,9 @@ MPC_NAME=${MPC_NAME:-httpd-mod_proxy_cluster} run_test() { local ret=0 if [ ! -z "$2" ]; then - printf "Running %-42s ..." "$2" + printf "Running %-64s ..." "$2" else - printf "Running %-42s ..." "$1" + printf "Running %-64s ..." "$1" fi if is_enabled "$DEBUG"; then sh $1 > "logs/${2:-$1}.log" 2>&1 From 36d80faa0e3235da14eefeeab2cb4c98ceab30f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Mon, 14 Sep 2026 16:48:05 +0200 Subject: [PATCH 07/31] test: Make mod_proxy_cluster/mod_proxy_balancer tests execution conditional --- test/testsuite.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/test/testsuite.sh b/test/testsuite.sh index 7c9bd09a..9bda3242 100644 --- a/test/testsuite.sh +++ b/test/testsuite.sh @@ -18,6 +18,7 @@ echo " MOD_PROXY_CLUSTER_TESTS=${MOD_PROXY_CLUSTER_TESTS:=On}" echo " MOD_PROXY_BALANCER_TESTS=${MOD_PROXY_BALANCER_TESTS:=On}" export FOREVER_PAUSE TOMCAT_CYCLE_COUNT ITERATION_COUNT IMG HTTPD_IMG + if [ ! -d logs ]; then mkdir logs fi From e1b5960699972272841d504c5bc4eb56061e2129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Wed, 30 Sep 2026 17:48:29 +0200 Subject: [PATCH 08/31] test: Separate container creation, allow to skip it --- test/includes/common.sh | 18 ++++++++++++++++++ test/testsuite.sh | 38 +++++++++++++++++++------------------- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/test/includes/common.sh b/test/includes/common.sh index 4f73c072..63a83c10 100644 --- a/test/includes/common.sh +++ b/test/includes/common.sh @@ -47,6 +47,24 @@ is_enabled() { return 0 } +test_create_all_containers() { + if is_enabled "$DEBUG"; then + httpd_create || exit 2 + tomcat_create || exit 3 + else + httpd_create > /dev/null 2>&1 || exit 2 + tomcat_create > /dev/null 2>&1 || exit 3 + fi + # create all main tomcat versions for Base tests + ## IMG name might include specific version, we have to handle that + IMG_NOVER=$(echo $IMG | cut -d: -f1) + for tomcat_version in "9.0" "10.1" "11.0" + do + IMG="$IMG_NOVER:$tomcat_version" tomcat_create $tomcat_version > /dev/null 2>&1 || exit 3 + done +} + + ##################################################### ### H T T P D H E L P E R F U N C T I O N S ### ##################################################### diff --git a/test/testsuite.sh b/test/testsuite.sh index 9bda3242..24b0b951 100644 --- a/test/testsuite.sh +++ b/test/testsuite.sh @@ -16,6 +16,7 @@ fi echo " DEBUG=${DEBUG:-Off (undefined)}" echo " MOD_PROXY_CLUSTER_TESTS=${MOD_PROXY_CLUSTER_TESTS:=On}" echo " MOD_PROXY_BALANCER_TESTS=${MOD_PROXY_BALANCER_TESTS:=On}" +echo " SKIP_CONTAINER_CREATION=${SKIP_CONTAINER_CREATION:=Off}" export FOREVER_PAUSE TOMCAT_CYCLE_COUNT ITERATION_COUNT IMG HTTPD_IMG @@ -25,28 +26,27 @@ fi . includes/common.sh -if [ ! -d tomcat/target ]; then - echo "Missing dependencies. Please run setup-dependencies.sh and then try again" - exit 4 -fi +IMG_NOVER=$(echo $IMG | cut -d: -f1) -echo -n "Creating docker containers..." -if is_enabled "$DEBUG"; then - httpd_create || exit 2 - tomcat_create || exit 3 +if is_enabled "$SKIP_CONTAINER_CREATION"; then + # let's just check all the containers are present + for tomcat_image in "$IMG_NOVER:latest" "$IMG_NOVER:9.0" "$IMG_NOVER:10.1" "$IMG_NOVER:11.0" + do + if ! docker image inspect $tomcat_image > /dev/null 2>&1; then + echo "tomcat image $tomcat_image is missing" + exit 3 + fi + done + if ! docker image inspect $HTTPD_IMG > /dev/null 2>&1; then + echo "httpd image $HTTPD_IMG is missing" + exit 2 + fi else - httpd_create > /dev/null 2>&1 || exit 2 - tomcat_create > /dev/null 2>&1 || exit 3 + # create all containers + echo "Creating docker containers..." + test_create_all_containers + echo "Done" fi -# create all main tomcat versions for Base tests -## IMG name might include specific version, we have to handle that -IMG_NOVER=$(echo $IMG | cut -d: -f1) -for tomcat_version in "9.0" "10.1" "11.0" -do - IMG="$IMG_NOVER:$tomcat_version" tomcat_create $tomcat_version > /dev/null 2>&1 || exit 3 -done - -echo " Done" # clean everything at first echo -n "Cleaning possibly running containers..." From 75f4fab416846ecac2c76bd1a1fb4a282d75ccd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Wed, 30 Sep 2026 18:17:13 +0200 Subject: [PATCH 09/31] ci: Rework the CI, build dependencies once then share Also have one job to gather and evaluate tests as needed --- .github/workflows/ci.yml | 118 +++++++++++++++++++++++++++++++++------ 1 file changed, 101 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 85b562ca..1b6a2dfb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -115,17 +115,17 @@ jobs: } > Containerfile podman build . - clang-format-style-check: + clang-style-checks: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6 with: path: mod_proxy_cluster - - name: Install clang-format + - name: Install clang-format and clang-tidy run: | sudo apt-get update - sudo apt-get install -y clang-format + sudo apt-get install -y clang-format clang-tidy apache2-dev - name: Check styles run: | code=0 @@ -142,18 +142,6 @@ jobs: rm diff.txt || true # we don't fail if the file does not exist done; exit $code - - clang-tidy-naming-check: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - path: mod_proxy_cluster - - name: Install clang-tidy - run: | - sudo apt-get update - sudo apt-get install -y clang-tidy apache2-dev - name: Check naming conventions run: | code=0 @@ -171,6 +159,7 @@ jobs: done exit $code + make-httpd-maintainer-mode: runs-on: ubuntu-latest strategy: @@ -302,8 +291,61 @@ jobs: path: C:\Apache24\logs\ retention-days: 7 + # Both testsuites run against this one image, so that they exercise the very same + # binaries and the coverage they gather can be merged (see the merged-coverage job). + build-test-containers: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest, ubuntu-24.04-arm ] + env: + IMG: local-tomcat + HTTPD_IMG: local-httpd + steps: + - name: Checkout + uses: actions/checkout@v6 + - name: Set up JDK 17 + uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: 17 + - name: Setup dependencies + run: | + sudo apt-get update -y + sudo apt-get install -y podman-docker maven git curl iproute2 wcstools zstd + sudo touch /etc/containers/nodocker + cd test + sh setup-dependencies.sh + - name: Build the test images + run: | + cd test + . includes/common.sh + test_create_all_containers + for img in $(docker image ls --format "{{.Repository}}:{{.Tag}}" | sed 's|^localhost/||'); do + if [[ "$img" != "$IMG"* ]] && [[ "$img" != "$HTTPD_IMG"* ]]; then continue; fi + echo "Saving $img image" + docker save $img | zstd -T0 -o /tmp/$(echo $img | sed 's|:|-|').tar.zst + done + - name: Preserve the test images + uses: actions/upload-artifact@v6 + with: + name: test-containers-${{ matrix.os }} + path: /tmp/*.tar.zst + retention-days: 1 + # the tests themselves need these, they don't run setup-dependencies.sh + - name: Preserve the test dependencies + uses: actions/upload-artifact@v6 + with: + name: test-dependencies-${{ matrix.os }} + path: | + test/websocket/websocket-hello-0.0.1.war + test/includes/target/test-1.0.jar + retention-days: 1 + tests: runs-on: ${{ matrix.os }} + needs: build-test-containers strategy: fail-fast: false matrix: @@ -318,6 +360,7 @@ jobs: TOMCAT_CYCLE_COUNT: ${{ inputs.tomcat-cycle-count || 2 }} MOD_PROXY_CLUSTER_TESTS: ${{ matrix.suite == 'mod_proxy_cluster' && (inputs.mod_proxy_cluster || 'on') || 'off' }} MOD_PROXY_BALANCER_TESTS: ${{ matrix.suite == 'mod_proxy_balancer' && (inputs.balancer || 'on') || 'off' }} + SKIP_CONTAINER_CREATION: 'on' steps: - name: Checkout uses: actions/checkout@v6 @@ -329,7 +372,7 @@ jobs: - name: Setup dependencies run: | sudo apt-get update -y - sudo apt-get install -y podman-docker maven git curl iproute2 wcstools + sudo apt-get install -y podman-docker maven git curl iproute2 wcstools zstd sudo touch /etc/containers/nodocker cd test sh setup-dependencies.sh @@ -343,13 +386,30 @@ jobs: [engine] events_logger = "file" EOF + - name: Get the test images built + uses: actions/download-artifact@v7 + with: + name: test-containers-${{ matrix.os }} + path: /tmp + - name: Load the test images + run: | + cd /tmp + for img in $(ls *.tar.zst); do + echo "Loading $img image" + zstd -dc $img | docker load + done + - name: Get the test dependencies built + uses: actions/download-artifact@v7 + with: + name: test-dependencies-${{ matrix.os }} + path: test - name: Run testsuite run: | cd test sh testsuite.sh - name: Preserve test logs uses: actions/upload-artifact@v6 - if: ${{ env.DEBUG == 'on' }} + if: ${{ !cancelled() && env.DEBUG == 'on' }} with: name: Test logs ${{ matrix.os }} ${{ matrix.suite }} path: | @@ -416,3 +476,27 @@ jobs: name: Doxygen docs path: doxygen-out/html/* retention-days: 30 + + gather-results: + runs-on: ubuntu-latest + # make-with-httpd-trunk is in there transitively through perl tests + needs: [ tests, perl-tests, clang-style-checks, cmake-fedora-latest, make-httpd-maintainer-mode, make-fedora-latest, doxygen, cmake-windows-latest ] + steps: + - name: Get all the test logs + uses: actions/download-artifact@v7 + with: + pattern: '*logs*' + # now delete the obtained archives and make a new one containing all the logs + - name: Delete all the logs we got + uses: geekyeggo/delete-artifact@v6 + with: + name: '*logs*' + failOnError: false + - name: Bundle the logs together + uses: actions/upload-artifact@v6 + if: always() + with: + name: Test logs + path: '*logs*' + retention-days: 7 + From 18b9f6f95b765875ad8db6a1d563f9cc266decdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Thu, 1 Oct 2026 09:49:25 +0200 Subject: [PATCH 10/31] ci: Drop the setup-dependencies call from tests --- .github/workflows/ci.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1b6a2dfb..1362cc90 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -364,18 +364,13 @@ jobs: steps: - name: Checkout uses: actions/checkout@v6 - - name: Set up JDK 17 - uses: actions/setup-java@v5 - with: - distribution: temurin - java-version: 17 - name: Setup dependencies run: | sudo apt-get update -y - sudo apt-get install -y podman-docker maven git curl iproute2 wcstools zstd + sudo apt-get install -y podman-docker git curl iproute2 wcstools zstd sudo touch /etc/containers/nodocker - cd test - sh setup-dependencies.sh + # create the network (usually done by setup-dependencies.sh + docker network create mod_proxy_cluster_testsuite_net > /dev/null 2>&1 || true - name: Configure Podman logging driver run: | mkdir -p ~/.config/containers From b70a1d8b4a53475726b709f27a82ea5df2743074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Thu, 1 Oct 2026 09:50:30 +0200 Subject: [PATCH 11/31] test: Run setup-dependencies.sh only when containers are created --- test/testsuite.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/testsuite.sh b/test/testsuite.sh index 24b0b951..28349f4d 100644 --- a/test/testsuite.sh +++ b/test/testsuite.sh @@ -43,6 +43,11 @@ if is_enabled "$SKIP_CONTAINER_CREATION"; then fi else # create all containers + if [ ! -d tomcat/target ]; then + echo "Missing dependencies. Please run setup-dependencies.sh and then try again" + exit 4 + fi + echo "Creating docker containers..." test_create_all_containers echo "Done" From 6a8abf0037942777a8b7388ed8e88af333dd3c8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Thu, 1 Oct 2026 18:35:50 +0200 Subject: [PATCH 12/31] ci: Separate Hanging tests and MODCLUSTER-736 from the rest --- .github/workflows/ci.yml | 97 +++++++++++++++++++++++++++++++++++----- 1 file changed, 85 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1362cc90..13ca7d2b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -343,6 +343,76 @@ jobs: test/includes/target/test-1.0.jar retention-days: 1 + test-extra: + name: test-extra (${{ matrix.os }}, ${{ matrix.test.name }}) + runs-on: ${{ matrix.os }} + needs: build-test-containers + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest, ubuntu-24.04-arm ] + # these tests take a lot of time, that's why we keep them separate + test: + - name: MODCLUSTER-736 + script: MODCLUSTER-736/testit.sh + + - name: Hanging tests + script: hangingtests.sh + env: + IMG: local-tomcat + HTTPD_IMG: local-httpd + DEBUG: ${{ inputs.debug || 'on' }} + FOREVER_PAUSE: ${{ inputs.forever-pause || 100 }} + ITERATION_COUNT: ${{ inputs.iteration-count || 2 }} + TOMCAT_CYCLE_COUNT: ${{ inputs.tomcat-cycle-count || 2 }} + SKIP_CONTAINER_CREATION: 'on' + steps: + - name: Checkout + uses: actions/checkout@v6 + - name: Setup dependencies + run: | + sudo apt-get update -y + sudo apt-get install -y podman-docker git curl iproute2 wcstools zstd + sudo touch /etc/containers/nodocker + # create the network (usually done by setup-dependencies.sh + docker network create mod_proxy_cluster_testsuite_net > /dev/null 2>&1 || true + - name: Configure Podman logging driver + run: | + mkdir -p ~/.config/containers + cat < ~/.config/containers/containers.conf + [containers] + log_driver = "k8s-file" + + [engine] + events_logger = "file" + EOF + - name: Get the test images built + uses: actions/download-artifact@v7 + with: + name: test-containers-${{ matrix.os }} + path: /tmp + - name: Load the test images + run: | + cd /tmp + for img in $(ls *.tar.zst); do + echo "Loading $img image" + zstd -dc $img | docker load + done + - name: Run ${{ matrix.test.name }} + run: | + cd test + mkdir logs + . includes/common.sh + run_test "${{ matrix.test.script }}" "${{ matrix.test.name }}" + - name: Preserve test logs + uses: actions/upload-artifact@v6 + if: ${{ !cancelled() && env.DEBUG == 'on' }} + with: + name: ${{ matrix.test.name }} logs ${{ matrix.os }} + path: | + test/logs/* + retention-days: 7 + tests: runs-on: ${{ matrix.os }} needs: build-test-containers @@ -398,6 +468,11 @@ jobs: with: name: test-dependencies-${{ matrix.os }} path: test + - name: Disable MODCLUSTER-736 and Hanging tests + run: | + cd test + sed -i '/MODCLUSTER-736/s|^|#|' testsuite.sh + sed -i '/Hanging tests/s|^|#|' testsuite.sh - name: Run testsuite run: | cd test @@ -475,23 +550,21 @@ jobs: gather-results: runs-on: ubuntu-latest # make-with-httpd-trunk is in there transitively through perl tests - needs: [ tests, perl-tests, clang-style-checks, cmake-fedora-latest, make-httpd-maintainer-mode, make-fedora-latest, doxygen, cmake-windows-latest ] + needs: [ tests, perl-tests, clang-style-checks, cmake-fedora-latest, make-httpd-maintainer-mode, make-fedora-latest, doxygen, cmake-windows-latest, test-extra ] steps: - - name: Get all the test logs - uses: actions/download-artifact@v7 - with: - pattern: '*logs*' - # now delete the obtained archives and make a new one containing all the logs - - name: Delete all the logs we got + - name: Delete all the containers/dependencies we no longer need uses: geekyeggo/delete-artifact@v6 with: - name: '*logs*' + name: | + test-containers* + test-dependencies* failOnError: false - - name: Bundle the logs together - uses: actions/upload-artifact@v6 - if: always() + - name: Merge all the logs + uses: actions/upload-artifact/merge@v6 with: name: Test logs - path: '*logs*' + pattern: '*logs*' + separate-directories: true + delete-merged: true retention-days: 7 From 42017a4cd723374d1d78dd5d99bf1bc0e27e252f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Fri, 2 Oct 2026 09:03:27 +0200 Subject: [PATCH 13/31] test: Use $((...)) instead of expr `expr` returns 1 (a failure) when the result is 0 which may lead to some failures even though nothing bad happenned (see testsuite.sh) --- test/MODCLUSTER-736/testit.sh | 10 +++++----- test/MODCLUSTER-755/testit.sh | 6 +++--- test/MODCLUSTER-785/testit.sh | 4 ++-- test/basetests.sh | 2 +- test/includes/common.sh | 14 +++++++------- test/maintests.sh | 6 +++--- test/testsuite.sh | 34 +++++++++++++++++----------------- 7 files changed, 38 insertions(+), 38 deletions(-) diff --git a/test/MODCLUSTER-736/testit.sh b/test/MODCLUSTER-736/testit.sh index d2248669..f25bb854 100644 --- a/test/MODCLUSTER-736/testit.sh +++ b/test/MODCLUSTER-736/testit.sh @@ -25,7 +25,7 @@ runtomcatbatch() { MPC_NAME=MODCLUSTER-736 tomcat_start $i done - tomcat_count=$(expr 3 + 11 - $t) + tomcat_count=$(( 3 + 11 - $t )) tomcat_wait_for_n_nodes $tomcat_count || exit 1 for i in $(seq $t 10); do @@ -94,7 +94,7 @@ singlecycle() { echo "Timeout: tomcat$1 is not ready" exit 1 fi - i=$(expr $i + 1) + i=$(( $i + 1 )) sleep 1 done echo "Testing(0) tomcat$1 started" @@ -112,7 +112,7 @@ singlecycle() { echo "Timeout: webapp on tomcat$1 is not ready after 300 seconds" exit 1 fi - i=$(expr $i + 1) + i=$(( $i + 1 )) sleep 1 done echo "Testing(1) tomcat$1" @@ -132,7 +132,7 @@ singlecycle() { echo "Timeout: webapp is still present on tomcat$1 after 300 seconds" exit 1 fi - i=$(expr $i + 1) + i=$(( $i + 1 )) sleep 1 done tomcat_remove $1 || exit 1 @@ -210,7 +210,7 @@ runmodcluster736() { runmodcluster736=0 while true do - runmodcluster736=$(expr $runmodcluster736 + 1) + runmodcluster736=$(( $runmodcluster736 + 1 )) if [ $runmodcluster736 -gt 2 ]; then echo "Looks OK, runmodcluster736 stopping!" break diff --git a/test/MODCLUSTER-755/testit.sh b/test/MODCLUSTER-755/testit.sh index 9fe7c726..f9aae7cc 100755 --- a/test/MODCLUSTER-755/testit.sh +++ b/test/MODCLUSTER-755/testit.sh @@ -33,7 +33,7 @@ if [ "x$USE_MULTI_APP" = "xtrue" ]; then echo "The webapp are going to be 1-9000/2-9000 until count (1-9499/2-9499)" fi -for i in $(seq 9000 $(expr 9000 + $NODE_COUNT - 1)) +for i in $(seq 9000 $(( 9000 + $NODE_COUNT - 1 ))) do curl $HTTPD -H "User-Agent: ClusterListener/1.0" -X CONFIG --data "JVMRoute=appserver$i&Host=127.0.0.1&Maxattempts=1&Port=$i&StickySessionForce=No&Timeout=20&Type=ajp&ping=20" curl $HTTPD -H "User-Agent: ClusterListener/1.0" -X STATUS --data "JVMRoute=appserver$i&Load=100" @@ -51,7 +51,7 @@ done i=0 while [ true ] do - for i in $(seq 9000 $(expr 9000 + $NODE_COUNT - 1)) + for i in $(seq 9000 $(( 9000 + $NODE_COUNT - 1 ))) do curl $HTTPD -H "User-Agent: ClusterListener/1.0" -X STATUS --data "JVMRoute=appserver$i&Load=100" if [ $? -ne 0 ]; then @@ -60,7 +60,7 @@ do fi done sleep 10 - i=$(expr $i + 1) + i=$(( $i + 1 )) if [ $i -gt 100 ]; then break fi diff --git a/test/MODCLUSTER-785/testit.sh b/test/MODCLUSTER-785/testit.sh index f6dbf214..9871b2b2 100755 --- a/test/MODCLUSTER-785/testit.sh +++ b/test/MODCLUSTER-785/testit.sh @@ -67,7 +67,7 @@ do if [ ${http_code} = 200 ]; then break fi - i=$(expr $i + 1) + i=$(( $i + 1 )) if [ $i -gt 60 ]; then break fi @@ -92,7 +92,7 @@ do echo "MODCLUSTER-785 Failed! return 503" exit 1 fi - i=$(expr $i + 1) + i=$(( $i + 1 )) if [ $i -gt 60 ]; then break fi diff --git a/test/basetests.sh b/test/basetests.sh index 3ea5e1fc..fdd36154 100644 --- a/test/basetests.sh +++ b/test/basetests.sh @@ -45,7 +45,7 @@ do tomcat_shutdown 1 tomcat_wait_for_n_nodes 0 || exit 1 tomcat_remove 1 - iter=$(expr $iter + 1) + iter=$(( $iter + 1 )) done tomcat_all_remove diff --git a/test/includes/common.sh b/test/includes/common.sh index 63a83c10..9258289b 100644 --- a/test/includes/common.sh +++ b/test/includes/common.sh @@ -111,7 +111,7 @@ httpd_wait_until_ready() { curl -m 20 localhost:8090 > /dev/null 2>&1 while [ $? != 0 ]; do - i=$(expr $i + 1) + i=$(( $i + 1 )) if [ $i -gt 20 ]; then echo "$(date) Failed to run httpd container" exit 1; @@ -169,8 +169,8 @@ tomcat_start() { exit 1 fi - local DEFAULT_OFFSET=$(expr $1 - 1) - local shutport=$(expr ${SHUTDOWN_PORT:-8005} + $DEFAULT_OFFSET) + local DEFAULT_OFFSET=$(( $1 - 1 )) + local shutport=$(( ${SHUTDOWN_PORT:-8005} + $DEFAULT_OFFSET )) echo "$(date) Starting tomcat$1" nohup docker run --network=mod_proxy_cluster_testsuite_net \ @@ -207,7 +207,7 @@ tomcat_wait_for_n_nodes() { NBNODES=$(curl -s http://localhost:8090/mod_cluster_manager -m 20 | grep "Status: OK" | awk ' { print $3} ' | wc -l) sleep 10 echo "$(date) Waiting for $nodes node to be ready (nodes ready: $NBNODES)" - i=$(expr $i + 1) + i=$(( $i + 1 )) if [ $i -gt 60 ]; then echo "($date) Timeout! There are not $nodes nodes but $NBNODES instead" exit 1 @@ -290,7 +290,7 @@ tomcat_shutdown() { fi echo "$(date) shutting down tomcat$1" - echo "SHUTDOWN" | nc localhost $(expr ${SHUTDOWN_PORT:-8005} + $1 - 1) + echo "SHUTDOWN" | nc localhost $(( ${SHUTDOWN_PORT:-8005} + $1 - 1 )) } # Remove the docker image tomcat$1 @@ -370,7 +370,7 @@ tomcat_all_run_ab() { while true do tomcat_run_ab $tc || exit 1 - tc=$(expr $tc + 1) + tc=$(( $tc + 1 )) if [ $tc -gt $1 ]; then echo "$(date) abtomcats: Done!" break @@ -394,7 +394,7 @@ tomcat_all_test_app() { while true do tomcat_test_app $tc || exit 1 - tc=$(expr $tc + 1) + tc=$(( $tc + 1 )) if [ $tc -gt $1 ]; then echo "$(date) tomcat_tests $tc Done!" break diff --git a/test/maintests.sh b/test/maintests.sh index b909bbd4..fd67da8f 100644 --- a/test/maintests.sh +++ b/test/maintests.sh @@ -60,7 +60,7 @@ while [ "${NODE}" = "${NEWNODE}" ] do NEWCO=$(curl -v http://localhost:8090/testapp/test.jsp -m 20 -o /dev/null 2>&1 | grep Set-Cookie | awk '{ print $3 } ' | sed 's:;::') NEWNODE=$(echo ${NEWCO} | awk -F = '{ print $2 }' | awk -F . '{ print $2 }') - i=$(expr $i + 1) + i=$(( $i + 1 )) if [ $i -gt 40 ]; then echo "Can't find the 2 webapps" exit 1 @@ -90,7 +90,7 @@ echotestlabel "sticky: stopping one node and doing requests..." NODE=$(echo ${NEWCO} | awk -F = '{ print $2 }' | awk -F . '{ print $2 }') echo $NODE PORT=$(curl http://localhost:8090/mod_cluster_manager -m 20 | grep Node | grep $NODE | sed 's:)::' | awk -F : '{ print $3 } ') -NUMBER=$(expr ${PORT} - 8080 + 1) +NUMBER=$(( ${PORT} - 8080 + 1 )) CODE="200" i=0 while [ "$CODE" = "200" ] @@ -106,7 +106,7 @@ do # We're going to kill instead of removal because kill only tomcat proc (keeps the container in DNS) tomcat_kill $NUMBER fi - i=$(expr $i + 1) + i=$(( $i + 1 )) done if [ ${CODE} != "200" ]; then echo "Something was wrong... got: ${CODE}" diff --git a/test/testsuite.sh b/test/testsuite.sh index 28349f4d..6fa45de6 100644 --- a/test/testsuite.sh +++ b/test/testsuite.sh @@ -65,46 +65,46 @@ if is_enabled "$MOD_PROXY_CLUSTER_TESTS"; then for tomcat_version in "9.0" "10.1" "11.0" do IMG="$IMG_NOVER:$tomcat_version" run_test basetests.sh "Basic tests with tomcat $tomcat_version" - res=$(expr $res + $?) + res=$(( $res + $? )) done run_test hangingtests.sh "Hanging tests" - res=$(expr $res + $?) + res=$(( $res + $? )) run_test maintests.sh "Main tests" - res=$(expr $res + $?) + res=$(( $res + $? )) run_test websocket/basic.sh "Websocket tests" - res=$(expr $res + $?) + res=$(( $res + $? )) run_test usealias/testit.sh "UseAlias" - res=$(expr $res + $?) + res=$(( $res + $? )) run_test MODCLUSTER-640/testit.sh "MODCLUSTER-640" - res=$(expr $res + $?) + res=$(( $res + $? )) run_test MODCLUSTER-734/testit.sh "MODCLUSTER-734" - res=$(expr $res + $?) + res=$(( $res + $? )) run_test MODCLUSTER-736/testit.sh "MODCLUSTER-736" - res=$(expr $res + $?) + res=$(( $res + $? )) run_test MODCLUSTER-755/testit.sh "MODCLUSTER-755" - res=$(expr $res + $?) + res=$(( $res + $? )) run_test MODCLUSTER-785/testit.sh "MODCLUSTER-785" - res=$(expr $res + $?) + res=$(( $res + $? )) run_test MODCLUSTER-794/testit.sh "MODCLUSTER-794" - res=$(expr $res + $?) + res=$(( $res + $? )) fi if is_enabled "$MOD_PROXY_BALANCER_TESTS"; then for tomcat_version in "9.0" "10.1" "11.0" do IMG="$IMG_NOVER:$tomcat_version" MPC_CONF=httpd/mod_lbmethod_cluster.conf run_test basetests.sh "Basic tests with mod_proxy_balancer and tomcat $tomcat_version" - res=$(expr $res + $?) + res=$(( $res + $? )) done MPC_CONF=MODCLUSTER-640/mod_lbmethod_cluster.conf run_test MODCLUSTER-640/testit.sh "MODCLUSTER-640 with mod_proxy_balancer" - res=$(expr $res + $?) + res=$(( $res + $? )) MPC_CONF=MODCLUSTER-734/mod_lbmethod_cluster.conf run_test MODCLUSTER-734/testit.sh "MODCLUSTER-734 with mod_proxy_balancer" - res=$(expr $res + $?) + res=$(( $res + $? )) MPC_CONF=httpd/mod_lbmethod_cluster.conf run_test MODCLUSTER-755/testit.sh "MODCLUSTER-755 with mod_proxy_balancer" - res=$(expr $res + $?) + res=$(( $res + $? )) MPC_CONF=MODCLUSTER-785/mod_lbmethod_cluster.conf run_test MODCLUSTER-785/testit.sh "MODCLUSTER-785 with mod_proxy_balancer" - res=$(expr $res + $?) + res=$(( $res + $? )) MPC_CONF=MODCLUSTER-794/mod_lbmethod_cluster.conf run_test MODCLUSTER-794/testit.sh "MODCLUSTER-794 with mod_proxy_balancer" - res=$(expr $res + $?) + res=$(( $res + $? )) fi echo -n "Cleaning containers if any..." From faedf72c031857bb12e746b9a524a377e721d64e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Fri, 2 Oct 2026 10:56:40 +0200 Subject: [PATCH 14/31] ci: Move the env vars to the top level --- .github/workflows/ci.yml | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 13ca7d2b..efe13b62 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,6 +47,15 @@ on: options: [ "on", "off" ] default: "on" +env: + IMG: local-tomcat + HTTPD_IMG: local-httpd + DEBUG: ${{ inputs.debug || 'on' }} + FOREVER_PAUSE: ${{ inputs.forever-pause || 100 }} + ITERATION_COUNT: ${{ inputs.iteration-count || 2 }} + TOMCAT_CYCLE_COUNT: ${{ inputs.tomcat-cycle-count || 2 }} + SKIP_CONTAINER_CREATION: 'on' + jobs: cmake-fedora-latest: @@ -299,9 +308,6 @@ jobs: fail-fast: false matrix: os: [ ubuntu-latest, ubuntu-24.04-arm ] - env: - IMG: local-tomcat - HTTPD_IMG: local-httpd steps: - name: Checkout uses: actions/checkout@v6 @@ -358,14 +364,6 @@ jobs: - name: Hanging tests script: hangingtests.sh - env: - IMG: local-tomcat - HTTPD_IMG: local-httpd - DEBUG: ${{ inputs.debug || 'on' }} - FOREVER_PAUSE: ${{ inputs.forever-pause || 100 }} - ITERATION_COUNT: ${{ inputs.iteration-count || 2 }} - TOMCAT_CYCLE_COUNT: ${{ inputs.tomcat-cycle-count || 2 }} - SKIP_CONTAINER_CREATION: 'on' steps: - name: Checkout uses: actions/checkout@v6 @@ -422,15 +420,8 @@ jobs: os: [ ubuntu-latest, ubuntu-24.04-arm ] suite: [ mod_proxy_cluster, mod_proxy_balancer ] env: - IMG: local-tomcat - HTTPD_IMG: local-httpd - DEBUG: ${{ inputs.debug || 'on' }} - FOREVER_PAUSE: ${{ inputs.forever-pause || 100 }} - ITERATION_COUNT: ${{ inputs.iteration-count || 2 }} - TOMCAT_CYCLE_COUNT: ${{ inputs.tomcat-cycle-count || 2 }} MOD_PROXY_CLUSTER_TESTS: ${{ matrix.suite == 'mod_proxy_cluster' && (inputs.mod_proxy_cluster || 'on') || 'off' }} MOD_PROXY_BALANCER_TESTS: ${{ matrix.suite == 'mod_proxy_balancer' && (inputs.balancer || 'on') || 'off' }} - SKIP_CONTAINER_CREATION: 'on' steps: - name: Checkout uses: actions/checkout@v6 From c38f31f120f2d0cd187611a97896b320a73b99ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Fri, 2 Oct 2026 11:03:17 +0200 Subject: [PATCH 15/31] test: Fix run_test when the test fails in shell with -e (GH Actions) --- test/includes/common.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/includes/common.sh b/test/includes/common.sh index 9258289b..34e9226c 100644 --- a/test/includes/common.sh +++ b/test/includes/common.sh @@ -13,11 +13,11 @@ run_test() { printf "Running %-64s ..." "$1" fi if is_enabled "$DEBUG"; then - sh $1 > "logs/${2:-$1}.log" 2>&1 + sh $1 > "logs/${2:-$1}.log" 2>&1 || ret=$? else - sh $1 > /dev/null 2>&1 + sh $1 > /dev/null 2>&1 || ret=$? fi - if [ $? = 0 ]; then + if [ $ret = 0 ]; then echo " OK" else echo " NOK" From 007d0ce054df5e8ef9b0f0fa8efd3dc836928704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Fri, 2 Oct 2026 12:12:44 +0200 Subject: [PATCH 16/31] ci: Gather only the logs on Windows --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index efe13b62..41493646 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -297,7 +297,7 @@ jobs: if: always() with: name: Windows logs for ${{ matrix.os }} - path: C:\Apache24\logs\ + path: C:\Apache24\logs\*log retention-days: 7 # Both testsuites run against this one image, so that they exercise the very same From f817d6903cfade11319c6f27ffcfef651d4bbb29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Fri, 2 Oct 2026 15:30:33 +0200 Subject: [PATCH 17/31] test: Simplify MODCLUSTER-736, drop redundant/dead code --- test/MODCLUSTER-736/testit.sh | 113 ++++++---------------------------- 1 file changed, 20 insertions(+), 93 deletions(-) diff --git a/test/MODCLUSTER-736/testit.sh b/test/MODCLUSTER-736/testit.sh index f25bb854..964c37cd 100644 --- a/test/MODCLUSTER-736/testit.sh +++ b/test/MODCLUSTER-736/testit.sh @@ -26,33 +26,21 @@ runtomcatbatch() { done tomcat_count=$(( 3 + 11 - $t )) - tomcat_wait_for_n_nodes $tomcat_count || exit 1 + tomcat_wait_for_n_nodes $tomcat_count for i in $(seq $t 10); do - tomcat_start_webapp $i || exit 1 + tomcat_start_webapp $i done # test the tomcats sleep 20 tomcat_all_test_app $tomcat_count - if [ $? -ne 0 ]; then - echo "runtomcatbatch tomcat_all_test_app $tomcat_count FAILED!" - exit 1 - fi # "load test" 9 of them tomcat_all_run_ab $tomcat_count - if [ $? -ne 0 ]; then - echo "runtomcatbatch tomcat_all_run_ab $tomcat_count FAILED!" - exit 1 - fi # retest tomcat_all_test_app $tomcat_count - if [ $? -ne 0 ]; then - echo "runtomcatbatch tomcat_all_test_app $tomcat_count FAILED!" - exit 1 - fi # stop the tomcats for i in $(seq $t 10); @@ -61,10 +49,6 @@ runtomcatbatch() { done tomcat_wait_for_n_nodes 3 - if [ $? -ne 0 ]; then - echo "runtomcatbatch tomcat_wait_for_n_nodes 3 FAILED!" - exit 1 - fi # remove the tomcats for i in $(seq $t 10); @@ -79,7 +63,7 @@ runtomcatbatch() { singlecycle() { echo "singlecycle: Testing tomcat$1" R=$1 - MPC_NAME=MODCLUSTER-736 tomcat_start $1 || exit 1 + MPC_NAME=MODCLUSTER-736 tomcat_start $1 # Wait for it to start echo "Testing(0) tomcat$1 waiting..." @@ -98,7 +82,7 @@ singlecycle() { sleep 1 done echo "Testing(0) tomcat$1 started" - tomcat_start_webapp $1 || exit 1 + tomcat_start_webapp $1 echo "Testing(0) tomcat$1 with webapp" i=0 while true @@ -116,10 +100,10 @@ singlecycle() { sleep 1 done echo "Testing(1) tomcat$1" - tomcat_test_app $1 || exit 1 + tomcat_test_app $1 echo "Testing(2) tomcat$1" - tomcat_test_app $1 || exit 1 - tomcat_run_ab $1 || exit 1 + tomcat_test_app $1 + tomcat_run_ab $1 echo "Testing(3) tomcat$1" tomcat_shutdown $1 || exit 1 while true @@ -143,7 +127,7 @@ singlecycle() { looptomcatforever() { while true do - singlecycle $1 || exit 1 + singlecycle $1 done } @@ -177,14 +161,16 @@ forevertomcat() { tomcat_remove_by_name tomcat14 tomcat_remove_by_name tomcat15 tomcat_remove_by_name tomcat16 - sleep 10 + # the nodes have to be gone from the manager, otherwise the following + # tomcat_wait_for_n_nodes calls would be satisfied by these leftovers + tomcat_wait_for_n_nodes 0 } # Start and stop successively (one after another) $1 tomcats cyclestomcats() { for i in $(seq 1 $1); do echo -n "$i/$1: " - singlecycle $i || exit 1 + singlecycle $i done echo "Looks OK, Done!" } @@ -196,15 +182,15 @@ runmodcluster736() { MPC_NAME=MODCLUSTER-736 tomcat_start 2 MPC_NAME=MODCLUSTER-736 tomcat_start 3 MPC_NAME=MODCLUSTER-736 tomcat_start 4 - tomcat_wait_for_n_nodes 3 || exit 1 + tomcat_wait_for_n_nodes 3 # check them - tomcat_start_webapp 2 || exit 1 - tomcat_start_webapp 3 || exit 1 - tomcat_start_webapp 4 || exit 1 + tomcat_start_webapp 2 + tomcat_start_webapp 3 + tomcat_start_webapp 4 sleep 20 - tomcat_test_app 2 || exit 1 - tomcat_test_app 3 || exit 1 - tomcat_test_app 4 || exit 1 + tomcat_test_app 2 + tomcat_test_app 3 + tomcat_test_app 4 # start a bunch of tomcats, test, shutdown, remove and try in a loop. runmodcluster736=0 @@ -217,56 +203,23 @@ runmodcluster736() { fi # cycle the tomcats runtomcatbatch - - if [ $? -ne 0 ]; then - echo "runtomcatbatch: runmodcluster736 Failed!" - exit 1 - fi tomcat_shutdown 2 tomcat_wait_for_n_nodes 2 - if [ $? -ne 0 ]; then - echo "tomcat_wait_for_n_nodes 2: runmodcluster736 Failed!" - exit 1 - fi tomcat_remove 2 MPC_NAME=MODCLUSTER-736 tomcat_start 5 tomcat_wait_for_n_nodes 3 - if [ $? -ne 0 ]; then - echo "tomcat_wait_for_n_nodes 3: runmodcluster736 Failed!" - exit 1 - fi tomcat_start_webapp 5 - if [ $? -ne 0 ]; then - echo "tomcat_start_webapp 5: runmodcluster736 Failed!" - exit 1 - fi sleep 20 tomcat_test_app 5 - if [ $? -ne 0 ]; then - echo "tomcat_test_app 5: runmodcluster736 Failed!" - exit 1 - fi # we have 5 3 4 in shared memory # read 2 MPC_NAME=MODCLUSTER-736 tomcat_start 2 tomcat_wait_for_n_nodes 4 - if [ $? -ne 0 ]; then - echo "tomcat_wait_for_n_nodes 4: runmodcluster736 Failed!" - exit 1 - fi tomcat_start_webapp 2 - if [ $? -ne 0 ]; then - echo "tomcat_start_webapp 2: runmodcluster736 Failed!" - exit 1 - fi sleep 20 tomcat_test_app 2 - if [ $? -ne 0 ]; then - echo "tomcat_test_app 2: runmodcluster736 Failed!" - exit 1 - fi sleep 20 @@ -275,29 +228,11 @@ runmodcluster736() { tomcat_shutdown 5 tomcat_wait_for_n_nodes 3 - if [ $? -ne 0 ]; then - echo "tomcat_wait_for_n_nodes 3: runmodcluster736 Failed!" - exit 1 - fi tomcat_remove 5 tomcat_test_app 2 - if [ $? -ne 0 ]; then - echo "tomcat_test_app 2: runmodcluster736 Failed!" - exit 1 - fi - tomcat_test_app 3 - if [ $? -ne 0 ]; then - echo "tomcat_test_app 3: runmodcluster736 Failed!" - exit 1 - fi - tomcat_test_app 4 - if [ $? -ne 0 ]; then - echo "tomcat_test_app 4: runmodcluster736 Failed!" - exit 1 - fi echo "runmodcluster736 loop: $runmodcluster736 DONE" done @@ -305,7 +240,7 @@ runmodcluster736() { tomcat_shutdown 4 tomcat_shutdown 3 tomcat_shutdown 2 - tomcat_wait_for_n_nodes 0 || exit 1 + tomcat_wait_for_n_nodes 0 tomcat_remove 2 tomcat_remove 3 tomcat_remove 4 @@ -314,16 +249,8 @@ runmodcluster736() { # MODCLUSTER-736 echo "Testing MODCLUSTER-736" cyclestomcats ${TOMCAT_CYCLE_COUNT:-10} -if [ $? -ne 0 ]; then - echo "MODCLUSTER-736 cyclestomcats 100 FAILED!" - exit 1 -fi echo "cycletomcats DONE" forevertomcat -if [ $? -ne 0 ]; then - echo "MODCLUSTER-736 forevertomcat FAILED!" - exit 1 -fi echo "forevertomcat DONE" runmodcluster736 if [ $? -ne 0 ]; then From ed2352e40db9dc3c9b879a5eb45691cee9e28db4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Tue, 6 Oct 2026 14:18:24 +0200 Subject: [PATCH 18/31] ci: Tweak the ignored workaround We will require only the gather-results job succeeding, so we can make things a little bit simpler. --- .github/workflows/ci-ignored-workaround.yml | 34 +-------------------- 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/.github/workflows/ci-ignored-workaround.yml b/.github/workflows/ci-ignored-workaround.yml index 4f35e429..509b73c0 100644 --- a/.github/workflows/ci-ignored-workaround.yml +++ b/.github/workflows/ci-ignored-workaround.yml @@ -12,42 +12,10 @@ on: - 'LICENSE' jobs: - cmake-fedora-latest: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - compiler: [ gcc, clang ] - steps: - - name: Skip - run: echo "Skipping the job" - - clang-format-style-check: - runs-on: ubuntu-latest - steps: - - name: Skip - run: echo "Skipping the job" - clang-tidy-naming-check: + gather-results: runs-on: ubuntu-latest steps: - name: Skip run: echo "Skipping the job" - make-fedora-latest: - runs-on: ubuntu-latest - steps: - - name: Skip - run: echo "Skipping the job" - - tests: - runs-on: ubuntu-latest - steps: - - name: Skip - run: echo "Skipping the job" - - perl-tests: - runs-on: ubuntu-latest - steps: - - name: Skip - run: echo "Skipping the job" From e62afd44a6861437e72332e2bf3ebdd906e2319c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Tue, 6 Oct 2026 17:42:38 +0200 Subject: [PATCH 19/31] ci: Minor tweaks, drop ls, use apt-get everywhere --- .github/workflows/ci.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41493646..ca912628 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,7 +59,6 @@ env: jobs: cmake-fedora-latest: - name: cmake-fedora-latest runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -100,7 +99,7 @@ jobs: path: mod_proxy_cluster - name: Setup Podman run: | - sudo apt update + sudo apt-get update sudo apt-get -y install podman - name: Create container and build run: | @@ -218,12 +217,10 @@ jobs: path: httpd/srclib/apr - name: Install necessary packages run: | - ls sudo apt-get update sudo apt-get install gcc cmake make libtool libtool-bin python3 autoconf libxml2-dev libpcre2-dev -y - name: Build httpd run: | - ls export "APACHE_DIR=/usr/local/apache2/" cd httpd ./buildconf @@ -236,7 +233,6 @@ jobs: env: CFLAGS: "-Wall -Werror" run: | - ls cd mod_proxy_cluster/native for module in advertise/ mod_proxy_cluster/ balancers/ mod_manager/; do \ cd $module; \ From a3b59c6b73288f92120f2aaf4de825b69e4e608f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Tue, 6 Oct 2026 17:43:27 +0200 Subject: [PATCH 20/31] ci: Fix typo wstunned vs wstunnel for trunk httpd --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ca912628..b75dbf83 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -225,7 +225,7 @@ jobs: cd httpd ./buildconf ./configure --prefix=/usr/local/apache2 --with-included-apr --enable-proxy-ajp --enable-maintainer-mode \ - --enable-so --enable-proxy --enable-proxy-http --enable-proxy-wstunned --enable-proxy-hcheck \ + --enable-so --enable-proxy --enable-proxy-http --enable-proxy-wstunnel --enable-proxy-hcheck \ --with-port=8090 sudo make sudo make install From c4af7f689993678964e9123330cb4fa2b5c25828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Tue, 6 Oct 2026 18:26:25 +0200 Subject: [PATCH 21/31] ci: Make sure that any job failure causes gather-results to fail --- .github/workflows/ci.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b75dbf83..02c482d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -536,9 +536,22 @@ jobs: gather-results: runs-on: ubuntu-latest + if: always() # make-with-httpd-trunk is in there transitively through perl tests needs: [ tests, perl-tests, clang-style-checks, cmake-fedora-latest, make-httpd-maintainer-mode, make-fedora-latest, doxygen, cmake-windows-latest, test-extra ] steps: + # Check that all the needs jobs were successful + - name: Check the results of all the jobs + if: always() + run: | + results="${{ join(needs.*.result, ' ') }}" + echo "Job results: $results" + for result in $results; do + if [ "$result" != "success" ]; then + echo "At least one job did not succeed" + exit 1 + fi + done - name: Delete all the containers/dependencies we no longer need uses: geekyeggo/delete-artifact@v6 with: From d29a565875e075e7c89dc2d40e99be79efe89765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Thu, 27 Aug 2026 11:18:08 +0200 Subject: [PATCH 22/31] test: Use ServerName directive at the top level --- test/httpd/mod_proxy_cluster.conf | 2 +- test/usealias/mod_proxy_cluster.conf | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/test/httpd/mod_proxy_cluster.conf b/test/httpd/mod_proxy_cluster.conf index acb3d388..3b7d4cd3 100644 --- a/test/httpd/mod_proxy_cluster.conf +++ b/test/httpd/mod_proxy_cluster.conf @@ -11,9 +11,9 @@ ProxyPreserveHost On Listen 8090 ManagerBalancerName mycluster WSUpgradeHeader websocket +ServerName httpd-mod_proxy_cluster - ServerName httpd-mod_proxy_cluster EnableMCMPReceive # For podman, this gets changed to IP in httpd/run.sh diff --git a/test/usealias/mod_proxy_cluster.conf b/test/usealias/mod_proxy_cluster.conf index 3c8110bf..2dc95cdf 100644 --- a/test/usealias/mod_proxy_cluster.conf +++ b/test/usealias/mod_proxy_cluster.conf @@ -9,13 +9,12 @@ LoadModule proxy_cluster_module modules/mod_proxy_cluster.so UseAlias On ProxyPreserveHost On - Listen 8090 ManagerBalancerName mycluster WSUpgradeHeader websocket +ServerName httpd-mod_proxy_cluster - ServerName httpd-mod_proxy_cluster EnableMCMPReceive # For podman, this gets changed to IP in httpd/run.sh From d70790166c58e630ae48748644e7829e10294b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Thu, 27 Aug 2026 11:21:35 +0200 Subject: [PATCH 23/31] test: Add CODE_COVERAGE flag for code coverage generation, use CMake for modules compilation --- test/httpd/Containerfile | 21 ++++++++------------- test/httpd/run.sh | 10 +++++++++- test/includes/common.sh | 23 +++++++++++++++++++++-- test/testsuite.sh | 25 ++++++++++++++++++++++++- 4 files changed, 62 insertions(+), 17 deletions(-) diff --git a/test/httpd/Containerfile b/test/httpd/Containerfile index 31d117e6..dfcf4e0b 100644 --- a/test/httpd/Containerfile +++ b/test/httpd/Containerfile @@ -1,8 +1,9 @@ FROM fedora:44 AS builder ARG HTTPD_SOURCES="https://dlcdn.apache.org/httpd/httpd-2.4.69.tar.gz" +ARG ENABLE_COVERAGE="" -RUN dnf install gcc apr-devel apr-util-devel openssl-devel pcre-devel redhat-rpm-config autoconf wcstools -y +RUN dnf install gcc apr-devel apr-util-devel openssl-devel pcre-devel redhat-rpm-config wcstools cmake -y ENV HTTPD=${HTTPD_SOURCES} @@ -29,17 +30,10 @@ RUN sed -i 's/\(Listen 80\)/#\1/' /usr/local/apache2/conf/httpd.conf # httpd is installed in /usr/local/apache2/bin/ # build and install mod_proxy_cluster *.so files. WORKDIR /native -RUN for m in advertise mod_proxy_cluster balancers mod_manager; \ - do \ - cd $m; \ - echo "Building $m"; \ - ./buildconf; \ - ./configure --with-apxs=/usr/local/apache2/bin/apxs; \ - make clean; \ - make || exit 1; \ - cp *.so /usr/local/apache2/modules; \ - cd $OLDPWD; \ - done; + +RUN mkdir build || true + +RUN cd build && rm -rf * && cmake .. -DAPACHE_INCLUDE_DIR=/usr/local/apache2/include -DENABLE_COVERAGE=${ENABLE_COVERAGE:-OFF} && make && for f in modules/*.so; do ln -s "$PWD/$f" /usr/local/apache2/$f; done; RUN rm -rf /test/httpd/mod_proxy_cluster @@ -47,9 +41,10 @@ FROM fedora:44 ENV CONF=httpd/mod_proxy_cluster.conf -RUN dnf install pcre apr-util wcstools -y +RUN dnf install pcre apr-util wcstools gcc gcovr lcov -y COPY --from=builder /usr/local/apache2 /usr/local/apache2 +COPY --from=builder /native /native COPY --from=builder /test /test diff --git a/test/httpd/run.sh b/test/httpd/run.sh index 0f92a4d7..519d6203 100755 --- a/test/httpd/run.sh +++ b/test/httpd/run.sh @@ -24,7 +24,15 @@ if [ ! -z "$MPC_NAME" ]; then sed -i "s/ServerName httpd-mod_proxy_cluster/ServerName ${MPC_NAME}/g" /usr/local/apache2/conf/$FILECONF fi -# start apache httpd server in foreground +# start apache httpd server echo "Starting httpd..." +if [ "$ENABLE_COVERAGE" = "1" ]; then + # Let the unprivileged worker children create/merge .gcda files in the + # root-owned build tree: 777 for directory traversal + file creation, and + # umask 0 so gcov creates the .gcda files world-writable (0666). umask is + # inherited across fork(), so every worker gets it. + chmod -R 777 /native + umask 0 +fi /usr/local/apache2/bin/apachectl start tail -f /usr/local/apache2/logs/error_log diff --git a/test/includes/common.sh b/test/includes/common.sh index 34e9226c..a2579b70 100644 --- a/test/includes/common.sh +++ b/test/includes/common.sh @@ -23,12 +23,28 @@ run_test() { echo " NOK" ret=1 fi + + local httpd_cont=$(docker ps -a | grep $HTTPD_IMG | cut -f 1 -d' ') # preserve httpd's logs too if DEBUG if is_enabled "$DEBUG"; then - local httpd_cont=$(docker ps -a | grep $HTTPD_IMG | cut -f 1 -d' ') docker logs $httpd_cont > "logs/${2:-$1}-httpd.log" 2>&1 docker cp ${httpd_cont}:/usr/local/apache2/logs/access_log "logs/${2:-$1}-httpd_access.log" 2> /dev/null || true fi + + if is_enabled "$CODE_COVERAGE"; then + f=$(echo ${2:-$1} | sed 's/ /-/g') + + docker exec ${httpd_cont} mkdir -p /coverage + docker exec ${httpd_cont} sh -c "/usr/local/apache2/bin/apachectl stop" + sleep 2 # wait for the coverage dump, just to be sure + docker exec ${httpd_cont} sh -c "cd /native && gcovr --gcov-ignore-parse-errors=negative_hits.warn_once_per_file --json /coverage/coverage-$f.json > /coverage/coverage-$f.log 2>&1" + docker exec ${httpd_cont} sh -c "lcov --capture --directory /native/build --ignore-errors gcov,negative --exclude '/usr/local/*' --output-file /coverage/coverage-$f.info > /coverage/coverage-lcov-$f.log 2>&1" + + for cf in $(docker exec ${httpd_cont} ls /coverage/); do + docker cp ${httpd_cont}:/coverage/$cf $PWD/coverage/$cf > /dev/null + done + fi + # Clean all after run httpd_remove > /dev/null 2>&1 tomcat_all_remove > /dev/null 2>&1 @@ -84,7 +100,9 @@ httpd_create() { done cp -r ../native ../test /tmp/mod_proxy_cluster/ mv /tmp/mod_proxy_cluster httpd/ - docker build -t $HTTPD_IMG -f httpd/Containerfile httpd/ + + docker build -t $HTTPD_IMG ${CODE_COVERAGE:+--build-arg ENABLE_COVERAGE=ON} \ + -f httpd/Containerfile httpd/ } # Build and run httpd container @@ -101,6 +119,7 @@ httpd_start() { --ulimit nofile=65536:65536 --name ${MPC_NAME:-httpd-mod_proxy_cluster} \ -e MPC_NAME=${MPC_NAME:-httpd-mod_proxy_cluster} \ -e CONF=${MPC_CONF:-httpd/mod_proxy_cluster.conf} \ + ${CODE_COVERAGE:+-e ENABLE_COVERAGE=1} \ $HTTPD_IMG httpd_wait_until_ready diff --git a/test/testsuite.sh b/test/testsuite.sh index 6fa45de6..63f4985b 100644 --- a/test/testsuite.sh +++ b/test/testsuite.sh @@ -24,6 +24,13 @@ if [ ! -d logs ]; then mkdir logs fi +if [ $CODE_COVERAGE ]; then + if [ ! -d coverage ]; then + mkdir coverage + fi + rm -f coverage/* +fi + . includes/common.sh IMG_NOVER=$(echo $IMG | cut -d: -f1) @@ -110,7 +117,7 @@ fi echo -n "Cleaning containers if any..." httpd_remove > /dev/null 2>&1 tomcat_all_remove > /dev/null 2>&1 -echo " Done" +echo " Done" if [ $res -eq 0 ]; then echo "Tests finished successfully!" @@ -119,4 +126,20 @@ else res=1 fi +if [ $CODE_COVERAGE ]; then + echo "Generating test coverage..." + MPC_CONF=httpd/mod_lbmethod_cluster.conf httpd_start > /dev/null 2>&1 + docker exec $MPC_NAME mkdir -p /coverage + + for f in coverage/*.json coverage/*.info; do + [ -e "$f" ] && docker cp $f $MPC_NAME:/coverage/ > /dev/null + done + + docker exec $MPC_NAME sh -c 'cd /native && gcovr --gcov-ignore-parse-errors=negative_hits.warn_once_per_file --add-tracefile "/coverage/coverage-*.json" --txt /coverage/test-coverage.txt --html-details /coverage/test-coverage.html > /coverage/test-coverage.log 2>&1' + docker exec $MPC_NAME sh -c 'cd /coverage && mkdir -p lcov && genhtml --ignore-errors negative,empty *.info --output-directory lcov > /coverage/lcov/test-coverage-lcov.log 2>&1' + docker cp $MPC_NAME:/coverage/ . > /dev/null + + httpd_remove > /dev/null 2>&1 +fi + exit $res From c0689f5d72368dcfa350b7ad7a11097f52fa8541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Thu, 27 Aug 2026 11:22:17 +0200 Subject: [PATCH 24/31] Add code coverage support to CMakeLists.txt --- native/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/native/CMakeLists.txt b/native/CMakeLists.txt index c2fe9168..2c3ce4f2 100644 --- a/native/CMakeLists.txt +++ b/native/CMakeLists.txt @@ -17,6 +17,12 @@ ELSE() SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -p -O1 -std=gnu99 -fPIC -Wall -Wextra") ENDIF() +OPTION(ENABLE_COVERAGE "Enable code coverage instrumentation" OFF) +IF(ENABLE_COVERAGE) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage -O0 -g -fprofile-update=atomic") + SET(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} --coverage") +ENDIF() + SET(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/modules) SET(PROJECT_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include) SET(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH}) From bd36a25c765b4fc1f1144c1942f062150dcba2b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Thu, 27 Aug 2026 11:22:41 +0200 Subject: [PATCH 25/31] Add coverage files to gitignore --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 1dfff7a0..574e5b8b 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,12 @@ test/httpd/mod_proxy_cluster # Log files **/*.log +# Coverage files +test/coverage/ +*.gcno +*.gcda +*.gcov + # build files *.slo *.so From ddc9d15557541a2784fd67faaccb88635435806f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Thu, 27 Aug 2026 11:23:12 +0200 Subject: [PATCH 26/31] ci: Enable code coverage by default, preserve its output --- .github/workflows/ci.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 02c482d1..9f055ebd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,14 @@ on: options: [ "on", "off" ] default: "on" + code-coverage: + description: "Test code coverage is gathered when on" + type: choice + options: + - "on" + - "off" + default: "on" + forever-pause: description: "Length of a period during which tomcats are run and stopped (MODCLUSTER-736 test)" type: number @@ -55,6 +63,7 @@ env: ITERATION_COUNT: ${{ inputs.iteration-count || 2 }} TOMCAT_CYCLE_COUNT: ${{ inputs.tomcat-cycle-count || 2 }} SKIP_CONTAINER_CREATION: 'on' + CODE_COVERAGE: ${{ inputs.code-coverage || 'on' }} jobs: @@ -472,6 +481,17 @@ jobs: path: | test/logs/* retention-days: 7 + - name: Print the coverage summary + if: ${{ env.CODE_COVERAGE == 'on' }} + run: cat test/coverage/test-coverage.txt + # Preserve coverage data if defined + - name: Preserve coverage files + if: ${{ env.CODE_COVERAGE == 'on' }} + uses: actions/upload-artifact@v6 + with: + name: Coverage ${{ matrix.os }} + path: test/coverage/* + retention-days: 7 perl-tests: runs-on: ubuntu-latest From f178e3043e5dddb69a59da9a1a3fad6593a6e4d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Tue, 6 Oct 2026 11:36:05 +0200 Subject: [PATCH 27/31] test: Create a script for coverage generation, move it to native/, use it --- native/scripts/coverage.sh | 94 ++++++++++++++++++++++++++++++++++++++ test/httpd/Containerfile | 13 ++++-- test/includes/common.sh | 19 +++++--- test/testsuite.sh | 13 +++--- 4 files changed, 121 insertions(+), 18 deletions(-) create mode 100755 native/scripts/coverage.sh diff --git a/native/scripts/coverage.sh b/native/scripts/coverage.sh new file mode 100755 index 00000000..a8372fb3 --- /dev/null +++ b/native/scripts/coverage.sh @@ -0,0 +1,94 @@ +#!/usr/bin/sh +# Collects code coverage of the modules built with -DENABLE_COVERAGE=ON. +# +# coverage.sh capture gather the counters dumped so far +# coverage.sh report merge the gathered tracefiles into reports +# +# "capture" produces the coverage-.json and coverage-.info tracefiles, +# "report" merges every tracefile within the output directory into test-coverage.txt, +# test-coverage.html and lcov/index.html. +# +# The httpd instance under test has to be stopped before capturing, otherwise gcov +# has not dumped its counters yet and there is nothing to collect. +# +# The location of the sources is derived from the location of this script, override +# it with the NATIVE variable if needed. +# +# exits with 0 if the data were collected +# exits with 1 if there is nothing to collect +# exits with 2 when used incorrectly +# exits with 3 if one of the coverage tools failed + +NATIVE=${NATIVE:-$(cd -- "$(dirname -- "$0")/.." && pwd)} + +# gcovr is always given --root so that the paths within the tracefiles stay relative +# to native/. That keeps tracefiles captured from different runs (and even on +# different machines) mergeable. Mind that gcovr reads its own tracefiles only when +# they were written by the very same version, so all the jobs have to agree on one. +GCOVR="gcovr --gcov-ignore-parse-errors=negative_hits.warn_once_per_file --root $NATIVE" + +# Prints the given message and gives up +fail() { + echo "$1" + exit 3 +} + +capture() { + name=$1 + out=$2 + + if [ -z "$(find $NATIVE -name '*.gcda' 2> /dev/null)" ]; then + echo "No coverage data found in $NATIVE, is httpd stopped?" + return 1 + fi + + mkdir -p $out + $GCOVR --json $out/coverage-$name.json > $out/coverage-$name.log 2>&1 \ + || fail "gcovr failed to capture $name, see $out/coverage-$name.log" + # lcov is confined to our sources the same way gcovr is by --root, otherwise the + # httpd headers end up in the report as well (wherever they happen to live) + lcov --capture --directory $NATIVE/build --ignore-errors gcov,negative \ + --include "$NATIVE/*" --output-file $out/coverage-$name.info \ + > $out/coverage-lcov-$name.log 2>&1 \ + || fail "lcov failed to capture $name, see $out/coverage-lcov-$name.log" +} + +report() { + out=$1 + + if [ -z "$(ls $out/coverage-*.json 2> /dev/null)" ]; then + echo "No tracefiles to report on within $out." + return 1 + fi + + mkdir -p $out/lcov + # the glob is quoted on purpose, it is gcovr who expands it + $GCOVR --add-tracefile "$out/coverage-*.json" \ + --txt $out/test-coverage.txt --html-details $out/test-coverage.html \ + > $out/test-coverage.log 2>&1 \ + || fail "gcovr failed to merge the tracefiles, see $out/test-coverage.log" + # unlike gcovr, lcov records absolute paths, so they are pointed back at these + # sources; that is a no-op for the tracefiles captured here and it is what makes + # the ones captured elsewhere merge (and render) instead of piling up side by side + sed -i "s|^SF:.*/native/|SF:$NATIVE/|" $out/coverage-*.info + genhtml --ignore-errors negative,empty $out/coverage-*.info \ + --output-directory $out/lcov > $out/lcov/test-coverage-lcov.log 2>&1 \ + || fail "genhtml failed, see $out/lcov/test-coverage-lcov.log" +} + +usage() { + echo "usage: $0 capture " + echo " $0 report " + exit 2 +} + +case "$1" in +capture) + if [ -z "$2" ] || [ -z "$3" ]; then usage; fi + capture "$2" "$3";; +report) + if [ -z "$2" ]; then usage; fi + report "$2";; +*) + usage;; +esac diff --git a/test/httpd/Containerfile b/test/httpd/Containerfile index dfcf4e0b..cc6c5ccd 100644 --- a/test/httpd/Containerfile +++ b/test/httpd/Containerfile @@ -7,10 +7,6 @@ RUN dnf install gcc apr-devel apr-util-devel openssl-devel pcre-devel redhat-rpm ENV HTTPD=${HTTPD_SOURCES} -# make sure you have copy of the local repository at place -# (our function "httpd_create" takes care of that) -COPY mod_proxy_cluster / - ADD ${HTTPD} . RUN mkdir /httpd && tar xvf $(filename $HTTPD) --strip 1 -C /httpd @@ -27,6 +23,13 @@ RUN make install # we don't need any other listeners except the ones we define in our config files RUN sed -i 's/\(Listen 80\)/#\1/' /usr/local/apache2/conf/httpd.conf +# the sources come in only now so that a change in them does not invalidate the layers +# above, the httpd build in particular +# +# make sure you have copy of the local repository at place +# (our function "httpd_create" takes care of that) +COPY mod_proxy_cluster / + # httpd is installed in /usr/local/apache2/bin/ # build and install mod_proxy_cluster *.so files. WORKDIR /native @@ -41,7 +44,7 @@ FROM fedora:44 ENV CONF=httpd/mod_proxy_cluster.conf -RUN dnf install pcre apr-util wcstools gcc gcovr lcov -y +RUN dnf install pcre apr-util wcstools gcc lcov gcovr -y COPY --from=builder /usr/local/apache2 /usr/local/apache2 COPY --from=builder /native /native diff --git a/test/includes/common.sh b/test/includes/common.sh index a2579b70..f1a86b25 100644 --- a/test/includes/common.sh +++ b/test/includes/common.sh @@ -34,11 +34,9 @@ run_test() { if is_enabled "$CODE_COVERAGE"; then f=$(echo ${2:-$1} | sed 's/ /-/g') - docker exec ${httpd_cont} mkdir -p /coverage docker exec ${httpd_cont} sh -c "/usr/local/apache2/bin/apachectl stop" sleep 2 # wait for the coverage dump, just to be sure - docker exec ${httpd_cont} sh -c "cd /native && gcovr --gcov-ignore-parse-errors=negative_hits.warn_once_per_file --json /coverage/coverage-$f.json > /coverage/coverage-$f.log 2>&1" - docker exec ${httpd_cont} sh -c "lcov --capture --directory /native/build --ignore-errors gcov,negative --exclude '/usr/local/*' --output-file /coverage/coverage-$f.info > /coverage/coverage-lcov-$f.log 2>&1" + docker exec ${httpd_cont} /native/scripts/coverage.sh capture "$f" /coverage for cf in $(docker exec ${httpd_cont} ls /coverage/); do docker cp ${httpd_cont}:/coverage/$cf $PWD/coverage/$cf > /dev/null @@ -101,8 +99,12 @@ httpd_create() { cp -r ../native ../test /tmp/mod_proxy_cluster/ mv /tmp/mod_proxy_cluster httpd/ - docker build -t $HTTPD_IMG ${CODE_COVERAGE:+--build-arg ENABLE_COVERAGE=ON} \ - -f httpd/Containerfile httpd/ + local coverage_arg="" + if is_enabled "$CODE_COVERAGE"; then + coverage_arg="--build-arg ENABLE_COVERAGE=ON" + fi + + docker build -t $HTTPD_IMG $coverage_arg -f httpd/Containerfile httpd/ } # Build and run httpd container @@ -115,11 +117,16 @@ httpd_start() { echo " NAME: ${MPC_NAME:-httpd-mod_proxy_cluster}" echo "You can config those with envars MPC_SOURCES, MPC_BRANCH, MPC_CONF, MPC_NAME respectively" fi + local coverage_env="" + if is_enabled "$CODE_COVERAGE"; then + coverage_env="-e ENABLE_COVERAGE=1" + fi + docker run -d --network=mod_proxy_cluster_testsuite_net -p 8090:8090 \ --ulimit nofile=65536:65536 --name ${MPC_NAME:-httpd-mod_proxy_cluster} \ -e MPC_NAME=${MPC_NAME:-httpd-mod_proxy_cluster} \ -e CONF=${MPC_CONF:-httpd/mod_proxy_cluster.conf} \ - ${CODE_COVERAGE:+-e ENABLE_COVERAGE=1} \ + $coverage_env \ $HTTPD_IMG httpd_wait_until_ready diff --git a/test/testsuite.sh b/test/testsuite.sh index 63f4985b..e2ba92e9 100644 --- a/test/testsuite.sh +++ b/test/testsuite.sh @@ -24,15 +24,15 @@ if [ ! -d logs ]; then mkdir logs fi -if [ $CODE_COVERAGE ]; then +. includes/common.sh + +if is_enabled "$CODE_COVERAGE"; then if [ ! -d coverage ]; then mkdir coverage fi rm -f coverage/* fi -. includes/common.sh - IMG_NOVER=$(echo $IMG | cut -d: -f1) if is_enabled "$SKIP_CONTAINER_CREATION"; then @@ -126,17 +126,16 @@ else res=1 fi -if [ $CODE_COVERAGE ]; then +if is_enabled "$CODE_COVERAGE"; then echo "Generating test coverage..." - MPC_CONF=httpd/mod_lbmethod_cluster.conf httpd_start > /dev/null 2>&1 + httpd_start > /dev/null 2>&1 docker exec $MPC_NAME mkdir -p /coverage for f in coverage/*.json coverage/*.info; do [ -e "$f" ] && docker cp $f $MPC_NAME:/coverage/ > /dev/null done - docker exec $MPC_NAME sh -c 'cd /native && gcovr --gcov-ignore-parse-errors=negative_hits.warn_once_per_file --add-tracefile "/coverage/coverage-*.json" --txt /coverage/test-coverage.txt --html-details /coverage/test-coverage.html > /coverage/test-coverage.log 2>&1' - docker exec $MPC_NAME sh -c 'cd /coverage && mkdir -p lcov && genhtml --ignore-errors negative,empty *.info --output-directory lcov > /coverage/lcov/test-coverage-lcov.log 2>&1' + docker exec $MPC_NAME /native/scripts/coverage.sh report /coverage docker cp $MPC_NAME:/coverage/ . > /dev/null httpd_remove > /dev/null 2>&1 From 601a41f47c46036f7e786550c2d797041e8410a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Tue, 6 Oct 2026 11:40:07 +0200 Subject: [PATCH 28/31] test-perl: Enable using container image from /test for perl as well --- test-perl/Containerfile | 32 ++++++++++++++++++++++++++++++++ test-perl/run.sh | 32 ++++++++++++++++++++++++++++++++ test-perl/t/conf/extra.conf.in | 4 ++++ 3 files changed, 68 insertions(+) create mode 100644 test-perl/Containerfile create mode 100755 test-perl/run.sh diff --git a/test-perl/Containerfile b/test-perl/Containerfile new file mode 100644 index 00000000..ad984b8a --- /dev/null +++ b/test-perl/Containerfile @@ -0,0 +1,32 @@ +# Runs the Apache::Test testsuite against the very same image the container testsuite +# uses. Building on top of it is what makes the code coverage of the two mergeable: +# the modules are the ones compiled there, so the compiler, the httpd headers and the +# paths recorded in the coverage data all agree. +# +# Pass the image to build upon with --build-arg HTTPD_IMG=. + +ARG HTTPD_IMG=mod_proxy_cluster-testsuite-httpd + +FROM ${HTTPD_IMG} + +RUN dnf install make perl perl-App-cpanminus perl-ExtUtils-MakeMaker \ + perl-libwww-perl perl-LWP-Protocol-https perl-HTTP-Message \ + perl-Crypt-SSLeay -y + +# Apache::Test is not packaged by Fedora, so this one has to come from CPAN +# (LWP::Protocol::https above is what lets cpanm reach it) +RUN cpanm --force Apache::Test Apache::TestMM + +# Apache::Test refuses to spawn the httpd children as root, so everything it touches +# is handed over to a plain user. That includes the build tree, because the .gcda +# files are written next to the objects the modules were compiled from. +RUN useradd --create-home tester && mkdir /coverage \ + && chown -R tester:tester /native /coverage + +COPY --chown=tester:tester . /test-perl + +USER tester + +WORKDIR /test-perl + +CMD /test-perl/run.sh diff --git a/test-perl/run.sh b/test-perl/run.sh new file mode 100755 index 00000000..f38d5bf4 --- /dev/null +++ b/test-perl/run.sh @@ -0,0 +1,32 @@ +#!/usr/bin/sh +# Entrypoint of the perl testsuite image. +# It uses the contaniner image from test/ testsuite. +# Runs it and gathers the code coverage when the underlying container has ENABLE_COVERAGE set to 1 +# +# The test log ends up in t/logs/test-perl.log and the coverage data in /coverage. +# +# exits with 0 if every test passed + +HTTPD=/usr/local/apache2/bin/httpd + +# Apache::Test puts the ServerRoot at t/, so every module the generated config loads +# has to be found in t/modules -- the httpd ones included. Ours are symlinked among +# them by the image, cp resolves that for us. +mkdir -p t/modules +cp /usr/local/apache2/modules/*.so t/modules/ || exit 1 + +perl Makefile.PL -httpd $HTTPD || exit 1 +make || exit 1 + +t/TEST -httpd $HTTPD 2>&1 | tee test-perl.log +mv test-perl.log t/logs/test-perl.log +grep -q "Result: PASS" t/logs/test-perl.log +res=$? + +# t/TEST stops httpd on its own, so the counters are already dumped by now +if [ "$ENABLE_COVERAGE" = "1" ]; then + /native/scripts/coverage.sh capture perl-tests /coverage \ + && /native/scripts/coverage.sh report /coverage +fi + +exit $res diff --git a/test-perl/t/conf/extra.conf.in b/test-perl/t/conf/extra.conf.in index b2d403c5..3b0ca8fe 100644 --- a/test-perl/t/conf/extra.conf.in +++ b/test-perl/t/conf/extra.conf.in @@ -1,3 +1,7 @@ + + LoadModule watchdog_module modules/mod_watchdog.so + + LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_http_module modules/mod_proxy_http.so LoadModule proxy_hcheck_module modules/mod_proxy_hcheck.so From f3acd563c16c7f0bcd2d0fa7d0fa2e16404e97cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Tue, 6 Oct 2026 11:40:33 +0200 Subject: [PATCH 29/31] Add test-perl/coverage among ignored paths --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 574e5b8b..cd8da86e 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ test/httpd/mod_proxy_cluster # Coverage files test/coverage/ +test-perl/coverage/ *.gcno *.gcda *.gcov From 5926411292abfd3fc606ecef5ce19eb2cd35b19e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Tue, 6 Oct 2026 11:48:21 +0200 Subject: [PATCH 30/31] ci: Gather coverage from perl tests as well --- .github/workflows/ci.yml | 139 ++++++++++++++++++++++++++++----------- 1 file changed, 102 insertions(+), 37 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9f055ebd..90f5bc5e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -252,12 +252,6 @@ jobs: sudo cp *.so /usr/local/apache2/modules; \ cd ..; \ done; - - name: Preserve built Apache for perl-test job - uses: actions/upload-artifact@v6 - with: - name: apache-trunk - path: /usr/local/apache2 - retention-days: 0 cmake-windows-latest: runs-on: ${{ matrix.os }} @@ -377,7 +371,7 @@ jobs: sudo apt-get update -y sudo apt-get install -y podman-docker git curl iproute2 wcstools zstd sudo touch /etc/containers/nodocker - # create the network (usually done by setup-dependencies.sh + # create the network (usually done by setup-dependencies.sh) docker network create mod_proxy_cluster_testsuite_net > /dev/null 2>&1 || true - name: Configure Podman logging driver run: | @@ -404,7 +398,7 @@ jobs: - name: Run ${{ matrix.test.name }} run: | cd test - mkdir logs + mkdir logs coverage . includes/common.sh run_test "${{ matrix.test.script }}" "${{ matrix.test.name }}" - name: Preserve test logs @@ -415,6 +409,15 @@ jobs: path: | test/logs/* retention-days: 7 + # Preserve coverage data if defined + - name: Preserve coverage files + if: ${{ !cancelled() && env.CODE_COVERAGE == 'on' && matrix.os == 'ubuntu-latest' }} + uses: actions/upload-artifact@v6 + with: + name: ${{ matrix.test.name }} coverage ${{ matrix.os }} + path: test/coverage/* + retention-days: 7 + tests: runs-on: ${{ matrix.os }} @@ -435,7 +438,7 @@ jobs: sudo apt-get update -y sudo apt-get install -y podman-docker git curl iproute2 wcstools zstd sudo touch /etc/containers/nodocker - # create the network (usually done by setup-dependencies.sh + # create the network (usually done by setup-dependencies.sh) docker network create mod_proxy_cluster_testsuite_net > /dev/null 2>&1 || true - name: Configure Podman logging driver run: | @@ -482,51 +485,52 @@ jobs: test/logs/* retention-days: 7 - name: Print the coverage summary - if: ${{ env.CODE_COVERAGE == 'on' }} + if: ${{ !cancelled() && env.CODE_COVERAGE == 'on' }} run: cat test/coverage/test-coverage.txt # Preserve coverage data if defined - name: Preserve coverage files - if: ${{ env.CODE_COVERAGE == 'on' }} + if: ${{ !cancelled() && env.CODE_COVERAGE == 'on' && matrix.os == 'ubuntu-latest' }} uses: actions/upload-artifact@v6 with: - name: Coverage ${{ matrix.os }} + name: Test coverage ${{ matrix.os }} ${{ matrix.suite }} path: test/coverage/* retention-days: 7 perl-tests: runs-on: ubuntu-latest - needs: make-with-httpd-trunk + needs: build-test-containers + env: + PERL_IMG: local-perl-tests steps: - name: Checkout uses: actions/checkout@v6 - name: Setup dependencies run: | sudo apt-get update - sudo apt-get remove apache2* - sudo apt-get install -y gcc make perl cpanminus libcrypt-ssleay-perl - - name: Get Apache built in the previous job + sudo apt-get install -y podman-docker zstd + sudo touch /etc/containers/nodocker + - name: Get the test images built uses: actions/download-artifact@v7 with: - name: apache-trunk - path: apache2 - - name: Put httpd on the right path - run: | - mkdir test-perl/t/modules/ - cp apache2/modules/{mod_proxy_cluster.so,mod_manager.so,mod_advertise.so,mod_lbmethod_cluster.so} test-perl/t/modules/ - sudo mv apache2/ /usr/local/apache2/ - sudo chmod -R 755 /usr/local/apache2/ - - name: Install perl modules + name: test-containers-ubuntu-latest + path: /tmp + - name: Load the test images run: | - sudo cpanm --force Apache::Test Apache::TestMM HTTP::Request LWP::UserAgent + cd /tmp + for img in $(ls *.tar.zst); do + echo "Loading $img image" + zstd -dc $img | docker load + done + - name: Build the perl testsuite image + run: docker build -t $PERL_IMG --build-arg HTTPD_IMG=$HTTPD_IMG -f test-perl/Containerfile test-perl/ - name: Run testsuite run: | - cd test-perl - perl Makefile.PL -httpd /usr/local/apache2/bin/httpd - make - t/TEST -httpd /usr/local/apache2/bin/httpd 2>&1 | tee test-perl.log - mv test-perl.log t/logs/test-perl.log - grep "Result: PASS" -q t/logs/test-perl.log - exit $? + if [ "$CODE_COVERAGE" = "on" ]; then coverage_env="-e ENABLE_COVERAGE=1"; fi + docker run --name perl-tests $coverage_env $PERL_IMG + # now get the logs and coverage + mkdir -p test-perl/t/logs test-perl/coverage + docker cp perl-tests:/test-perl/t/logs/. test-perl/t/logs/ || true + docker cp perl-tests:/coverage/. test-perl/coverage/ || true - name: Preserve test logs uses: actions/upload-artifact@v6 if: always() @@ -535,6 +539,16 @@ jobs: path: | test-perl/t/logs/* retention-days: 7 + - name: Print the coverage summary + if: ${{ !cancelled() && env.CODE_COVERAGE == 'on' }} + run: cat test-perl/coverage/test-coverage.txt + - name: Preserve coverage files + if: ${{ !cancelled() && env.CODE_COVERAGE == 'on' }} + uses: actions/upload-artifact@v6 + with: + name: Perl tests coverage + path: test-perl/coverage/* + retention-days: 7 doxygen: runs-on: ubuntu-latest @@ -557,8 +571,17 @@ jobs: gather-results: runs-on: ubuntu-latest if: always() - # make-with-httpd-trunk is in there transitively through perl tests - needs: [ tests, perl-tests, clang-style-checks, cmake-fedora-latest, make-httpd-maintainer-mode, make-fedora-latest, doxygen, cmake-windows-latest, test-extra ] + needs: + - tests + - perl-tests + - test-extra + - clang-style-checks + - cmake-fedora-latest + - cmake-windows-latest + - make-httpd-maintainer-mode + - make-fedora-latest + - make-with-httpd-trunk + - doxygen steps: # Check that all the needs jobs were successful - name: Check the results of all the jobs @@ -572,13 +595,55 @@ jobs: exit 1 fi done - - name: Delete all the containers/dependencies we no longer need + - name: Setup dependencies + run: | + sudo apt-get update + sudo apt-get install -y podman-docker zstd + sudo touch /etc/containers/nodocker + - name: Get the httpd/mod_proxy_cluster for merging the coverage reports + if: ${{ env.CODE_COVERAGE == 'on' }} + uses: actions/download-artifact@v8 + with: + name: test-containers-ubuntu-latest + - name: Load the test images + if: ${{ env.CODE_COVERAGE == 'on' }} + run: | + for img in $(ls *.tar.zst); do + echo "Loading $img image" + zstd -dc $img | docker load + done + - name: Get all the coverage reports + if: ${{ env.CODE_COVERAGE == 'on' }} + uses: actions/download-artifact@v7 + with: + pattern: '*coverage*' + path: coverage + merge-multiple: true + - name: Get all the coverage files + if: ${{ env.CODE_COVERAGE == 'on' }} + run: | + rm -rf coverage/lcov coverage/test-coverage.* + docker run --rm -v $PWD/coverage:/coverage $HTTPD_IMG \ + /native/scripts/coverage.sh report /coverage + - name: Print the full coverage summary + if: ${{ env.CODE_COVERAGE == 'on' }} + run: cat coverage/test-coverage.txt + - name: Delete all the artifacts we no longer need uses: geekyeggo/delete-artifact@v6 with: name: | test-containers* test-dependencies* + *coverage* failOnError: false + # First delete the individual coverage archives, then create a final report + - name: Preserve coverage files + if: ${{ env.CODE_COVERAGE == 'on' }} + uses: actions/upload-artifact@v6 + with: + name: Test coverage + path: coverage/* + retention-days: 14 - name: Merge all the logs uses: actions/upload-artifact/merge@v6 with: @@ -586,5 +651,5 @@ jobs: pattern: '*logs*' separate-directories: true delete-merged: true - retention-days: 7 + retention-days: 14 From 4309b940a6e4ecb41dc4fb166ea62c3c966d4d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Chlup?= Date: Tue, 6 Oct 2026 18:21:56 +0200 Subject: [PATCH 31/31] ci: Add a custom prepare action to simplify ci.yml --- .../prepare-test-environment/action.yml | 50 ++++++++ .github/workflows/ci.yml | 109 +++--------------- 2 files changed, 68 insertions(+), 91 deletions(-) create mode 100644 .github/actions/prepare-test-environment/action.yml diff --git a/.github/actions/prepare-test-environment/action.yml b/.github/actions/prepare-test-environment/action.yml new file mode 100644 index 00000000..96ca9676 --- /dev/null +++ b/.github/actions/prepare-test-environment/action.yml @@ -0,0 +1,50 @@ +name: Prepare the test environment +description: Install and setup dependencies and load the container images built by build-test-containers + +inputs: + os: + description: Runner OS, selects which test-containers artifact to download + default: ubuntu-latest + extra-packages: + description: Additional apt packages the testsuite needs + default: '' + +runs: + using: composite + steps: + - name: Setup dependencies + shell: bash + run: | + sudo apt-get update -y + sudo apt-get install -y podman-docker zstd ${{ inputs.extra-packages }} + sudo touch /etc/containers/nodocker + + - name: Create the testsuite network + shell: bash + run: docker network create mod_proxy_cluster_testsuite_net > /dev/null 2>&1 || true + + - name: Configure Podman logging driver + shell: bash + run: | + mkdir -p ~/.config/containers + cat < ~/.config/containers/containers.conf + [containers] + log_driver = "k8s-file" + + [engine] + events_logger = "file" + EOF + + - name: Get the test images built + uses: actions/download-artifact@v8 + with: + name: test-containers-${{ inputs.os }} + path: /tmp + + - name: Load the test images + shell: bash + run: | + for img in /tmp/*.tar.zst; do + echo "Loading $img image" + zstd -dc "$img" | docker load + done diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 90f5bc5e..8b83a728 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -366,35 +366,11 @@ jobs: steps: - name: Checkout uses: actions/checkout@v6 - - name: Setup dependencies - run: | - sudo apt-get update -y - sudo apt-get install -y podman-docker git curl iproute2 wcstools zstd - sudo touch /etc/containers/nodocker - # create the network (usually done by setup-dependencies.sh) - docker network create mod_proxy_cluster_testsuite_net > /dev/null 2>&1 || true - - name: Configure Podman logging driver - run: | - mkdir -p ~/.config/containers - cat < ~/.config/containers/containers.conf - [containers] - log_driver = "k8s-file" - - [engine] - events_logger = "file" - EOF - - name: Get the test images built - uses: actions/download-artifact@v7 + - name: Setup podman and load the test images + uses: ./.github/actions/prepare-test-environment with: - name: test-containers-${{ matrix.os }} - path: /tmp - - name: Load the test images - run: | - cd /tmp - for img in $(ls *.tar.zst); do - echo "Loading $img image" - zstd -dc $img | docker load - done + os: ${{ matrix.os }} + extra-packages: git curl iproute2 wcstools - name: Run ${{ matrix.test.name }} run: | cd test @@ -433,37 +409,13 @@ jobs: steps: - name: Checkout uses: actions/checkout@v6 - - name: Setup dependencies - run: | - sudo apt-get update -y - sudo apt-get install -y podman-docker git curl iproute2 wcstools zstd - sudo touch /etc/containers/nodocker - # create the network (usually done by setup-dependencies.sh) - docker network create mod_proxy_cluster_testsuite_net > /dev/null 2>&1 || true - - name: Configure Podman logging driver - run: | - mkdir -p ~/.config/containers - cat < ~/.config/containers/containers.conf - [containers] - log_driver = "k8s-file" - - [engine] - events_logger = "file" - EOF - - name: Get the test images built - uses: actions/download-artifact@v7 + - name: Setup podman and load the test images + uses: ./.github/actions/prepare-test-environment with: - name: test-containers-${{ matrix.os }} - path: /tmp - - name: Load the test images - run: | - cd /tmp - for img in $(ls *.tar.zst); do - echo "Loading $img image" - zstd -dc $img | docker load - done + os: ${{ matrix.os }} + extra-packages: git curl iproute2 wcstools - name: Get the test dependencies built - uses: actions/download-artifact@v7 + uses: actions/download-artifact@v8 with: name: test-dependencies-${{ matrix.os }} path: test @@ -504,23 +456,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v6 - - name: Setup dependencies - run: | - sudo apt-get update - sudo apt-get install -y podman-docker zstd - sudo touch /etc/containers/nodocker - - name: Get the test images built - uses: actions/download-artifact@v7 - with: - name: test-containers-ubuntu-latest - path: /tmp - - name: Load the test images - run: | - cd /tmp - for img in $(ls *.tar.zst); do - echo "Loading $img image" - zstd -dc $img | docker load - done + - name: Setup podman and load the test images + uses: ./.github/actions/prepare-test-environment - name: Build the perl testsuite image run: docker build -t $PERL_IMG --build-arg HTTPD_IMG=$HTTPD_IMG -f test-perl/Containerfile test-perl/ - name: Run testsuite @@ -595,26 +532,16 @@ jobs: exit 1 fi done - - name: Setup dependencies - run: | - sudo apt-get update - sudo apt-get install -y podman-docker zstd - sudo touch /etc/containers/nodocker - - name: Get the httpd/mod_proxy_cluster for merging the coverage reports - if: ${{ env.CODE_COVERAGE == 'on' }} - uses: actions/download-artifact@v8 - with: - name: test-containers-ubuntu-latest - - name: Load the test images + # the checkout is only here so that the local action below can be used + - name: Checkout + uses: actions/checkout@v6 + # we need the httpd/mod_proxy_cluster image to merge the coverage reports + - name: Setup podman and load the test images if: ${{ env.CODE_COVERAGE == 'on' }} - run: | - for img in $(ls *.tar.zst); do - echo "Loading $img image" - zstd -dc $img | docker load - done + uses: ./.github/actions/prepare-test-environment - name: Get all the coverage reports if: ${{ env.CODE_COVERAGE == 'on' }} - uses: actions/download-artifact@v7 + uses: actions/download-artifact@v8 with: pattern: '*coverage*' path: coverage