diff --git a/.github/workflows/composites/download-docker-images/action.yaml b/.github/workflows/composites/download-docker-images/action.yaml index 0a26162e..03f76e00 100644 --- a/.github/workflows/composites/download-docker-images/action.yaml +++ b/.github/workflows/composites/download-docker-images/action.yaml @@ -6,6 +6,7 @@ description: download docker images runs: using: "composite" steps: + - uses: actions/cache@v3 with: path: /tmp/docker/images diff --git a/.github/workflows/composites/matrix-bounds-on-test-times-cache-hit/action.yaml b/.github/workflows/composites/matrix-bounds-on-test-times-cache-hit/action.yaml new file mode 100644 index 00000000..8be1192c --- /dev/null +++ b/.github/workflows/composites/matrix-bounds-on-test-times-cache-hit/action.yaml @@ -0,0 +1,71 @@ +########################################### find test times ####################################### +# when cache of test times is present, compute matrix steps +# we do this because the number of steps required in a matrix is not stable +# it is computed as sum_of_times_of_all_tests / max_time_of_a_single_test +# For example if there is no test times cache, we will be using 32 steps in the matrix +# on the other hand if cache is present, we might need less steps: + +# sum_of_times_of_all_tests = 8000 seconds +# max_time_of_a_single_test = 400 +# we would require 20 steps in the matrix, as having 32 would make no sense +# because one step will run around 400 seconds and others will wait for this one to finish +# as they finish earlier + +name: matrix bounds on test times cache hit +description: matrix bounds on test times cache hit +runs: + using: "composite" + + steps: + + - name: compute matrix steps + shell: bash + run: | + + sum_of_all_tests=$(awk -F' ' '{sum+=$2;} END{print sum;}' /tmp/sorted.txt) + sum_of_all_tests_as_int=$(printf "%.0f\n" "$sum_of_all_tests") + echo "sum of all tests : $sum_of_all_tests_as_int" + + max_test_time=$(tail -1 /tmp/sorted.txt | awk '{print $2}') + max_test_time_as_int=$(printf "%.0f\n" "$max_test_time") + echo "max test time : $max_test_time_as_int" + + number_of_instances=$(( $sum_of_all_tests_as_int / $max_test_time_as_int )) + + number_of_instances_as_array=() + number_of_instances_as_array+='[' + number_of_instances_as_array+=$number_of_instances + number_of_instances_as_array+=']' + + average_time_per_instance=$(( sum_of_all_tests_as_int / $number_of_instances )) + echo "average time per instance $average_time_per_instance" + + matrix_array=() + matrix_array+='[' + for ((i=0; i<=${number_of_instances}; i++)); do + if (( $i == $(( $number_of_instances )) )); then + matrix_array+=$i + else + matrix_array+=$i, + fi + done + + matrix_array+=']' + + echo "number of instances : $number_of_instances_as_array" + echo "average time per instance : $average_time_per_instance" + echo "matrix_array : $matrix_array" + echo "********************************************************************************************************" + + number_of_instances_json=$(jq -r -c . <<< $number_of_instances_as_array) + echo "number_of_instances_json : $number_of_instances_json" + + matrix_array_json=$(jq -r -c . <<< $matrix_array) + echo "matrix_array_json : $matrix_array_json" + + echo "TEST_TIMES_CACHE_PRESENT=true" >> $GITHUB_ENV + echo "NUMBER_OF_MATRIX_INSTANCES=$(echo $number_of_instances_json)" >> $GITHUB_ENV + echo "MATRIX_ARRAY=$(echo $matrix_array_json)" >> $GITHUB_ENV + echo "AVERAGE_TIME_PER_INSTANCE=$(echo $average_time_per_instance)" >> $GITHUB_ENV + + diff --git a/.github/workflows/composites/pre-test-actions/action.yaml b/.github/workflows/composites/pre-test-actions/action.yaml new file mode 100644 index 00000000..f6fd058b --- /dev/null +++ b/.github/workflows/composites/pre-test-actions/action.yaml @@ -0,0 +1,41 @@ +# common steps before running actula tests for both +# when a cache of test times is presen and when it is not present + +name: pre-test-actions +description: pre-test-actions +runs: + using: "composite" + + steps: + + - name: set env variables + uses: ./.github/workflows/composites/env-variables + + - name: setup project jdk-17 + uses: ./.github/workflows/composites/setup-jdk17 + if: env.BASE_BRANCH == 'main' + + - name: setup project jdk-8 + uses: ./.github/workflows/composites/setup-jdk1.8 + if: env.BASE_BRANCH == '2.1.x' + + - name: cache local maven repository + uses: ./.github/workflows/composites/cache + + - name: download docker images + uses: ./.github/workflows/composites/download-docker-images + if: env.BASE_BRANCH != '2.1.x' + + - name: load docker images into local repo + uses: ./.github/workflows/composites/load-docker-images + if: env.BASE_BRANCH != '2.1.x' + + - name: download tests + uses: actions/download-artifact@v3 + with: + name: tests.txt + path: /tmp + + + + diff --git a/.github/workflows/composites/run-and-save-test-times-when-cache-missing/action.yaml b/.github/workflows/composites/run-and-save-test-times-when-cache-missing/action.yaml new file mode 100644 index 00000000..39e79ebd --- /dev/null +++ b/.github/workflows/composites/run-and-save-test-times-when-cache-missing/action.yaml @@ -0,0 +1,97 @@ +########################################### find test times ####################################### +# 1. find files that look like this: 'org.springframework.xxx.txt', these files are generated +# by surefire-plugin when tests run. +# 2. extract the time it took to run a certain test (from the above .txt file) +# 3. create a file that contains test times from this current matrix + +name: run and save test times when cache missing +description: run and save test times when cache missing +runs: + using: "composite" + + steps: + + - name: run and save test times + shell: bash + run: | + + # take only likes that look like : + # 'spring.cloud.k8s.test.to.run -> org.springframework.cloud.kubernetes.fabric8.discovery.ServicePortSecureResolverTest' + PLAIN_TEST_CLASSNAMES=($(cat /tmp/tests.txt | grep -o 'spring.cloud.k8s.test.to.run -> org.*' | awk '{print $3}')) + IFS=$'\n' + SORTED_TEST_CLASSNAMES=( $(sort <<< "${PLAIN_TEST_CLASSNAMES[*]}") ) + unset IFS + + start_from_name=start_from_${CURRENT_INDEX} + how_many_name=how_many_${CURRENT_INDEX} + + start_from=${!start_from_name} + how_many=${!how_many_name} + + echo "${SORTED_TEST_CLASSNAMES[@]}" + echo "$start_from_name : ${start_from}" + echo "$how_many_name : ${how_many}" + + sliced_array=(${SORTED_TEST_CLASSNAMES[@]:$start_from:$how_many}) + + TEST_ARG=$(echo ${sliced_array[@]} | sed 's/ /,/g') + echo "$TEST_ARG" + + if [[ $baseBranch == "2.1.x" ]]; then + + ./mvnw -s .settings.xml -pl '-:kubernetes-leader-election-example' -pl '-:kubernetes-hello-world-example' \ + -pl '-:kubernetes-reload-example' -pl '-:kubernetes-loadbalancer-example' \ + -pl '-:spring-cloud-kubernetes-configserver' -pl '-:spring-cloud-kubernetes-configuration-watcher' \ + -pl '-:spring-cloud-kubernetes-discoveryserver' \ + -DtestsToRun=${TEST_ARG[@]} \ + -e clean install \ + -U -P sonar -nsu --batch-mode \ + -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn \ + -Dhttp.keepAlive=false \ + -Dmaven.wagon.http.pool=false \ + -Dmaven.wagon.http.retryHandler.class=standard \ + -Dmaven.wagon.http.retryHandler.count=3 \ + -Dskip.build.image=true + + else + + version=$(java -version) + echo "version of java: $version" + + ./mvnw -s .settings.xml \ + -DtestsToRun=${TEST_ARG[@]} \ + -e clean install \ + -U -P sonar -nsu --batch-mode \ + -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn \ + -Dhttp.keepAlive=false \ + -Dmaven.wagon.http.pool=false \ + -Dmaven.wagon.http.retryHandler.class=standard \ + -Dmaven.wagon.http.retryHandler.count=3 \ + -Dskip.build.image=true + fi + + touch /tmp/test_times_${{ env.CURRENT_INDEX }}.txt + + for i in "${sliced_array[@]}"; do + filename="${i}.txt" + echo "searching for filename: ${filename}" + file=$(find . -name "${filename}") + echo "found file: ${file}" + result=$(cat "${file}" | grep 'elapsed' | awk '{print $12, $13}') + + echo "run test: ${i} in : ${result}" >> /tmp/test_times_${{ env.CURRENT_INDEX }}.txt + done + + - name: show individual test times + shell: bash + if: env.BASE_BRANCH != '2.1.x' + run: cat /tmp/test_times_${{ env.CURRENT_INDEX }}.txt + + - name: upload individual tests + if: env.BASE_BRANCH != '2.1.x' + uses: actions/upload-artifact@v3 + with: + name: test_times_${{ env.CURRENT_INDEX }}.txt + path: /tmp/test_times_${{ env.CURRENT_INDEX }}.txt + + diff --git a/.github/workflows/composites/run-and-save-test-times-when-cache-present/action.yaml b/.github/workflows/composites/run-and-save-test-times-when-cache-present/action.yaml new file mode 100644 index 00000000..13414b29 --- /dev/null +++ b/.github/workflows/composites/run-and-save-test-times-when-cache-present/action.yaml @@ -0,0 +1,213 @@ +name: run and save test times when cache present +description: run and save test times when cache present +runs: + using: "composite" + + steps: + - name: restore test times cache + uses: actions/cache/restore@v3 + with: + path: /tmp/sorted.txt + key: ${{ runner.os }}-spring-cloud-kubernetes-existing-test-times-cache-${{ github.run_id }} + restore-keys: ${{ runner.os }}-spring-cloud-kubernetes-existing-test-times-cache- + + - name: show cached test times + shell: bash + run: cat /tmp/sorted.txt + + - name: split tests into known times and un-known times + shell: bash + run: | + ############################################################################################################ + ############################################################################################################ + ############################################################################################################ + + # 1. Get all existing tests and place them in PLAIN_TEST_CLASSNAMES + # 2. Get all test times from the existing cache : /tmp/sorted.txt + # 3. Split tests from PLAIN_TEST_CLASSNAMES into two files depending if we already know their running times + # or not : tests-without-times.txt and tests-with-times.txt + + ############################################################################################################ + ############################################################################################################ + ############################################################################################################ + PLAIN_TEST_CLASSNAMES=($(cat /tmp/tests.txt | grep -o 'spring.cloud.k8s.test.to.run -> org.*' | awk '{print $3}')) + + echo "${PLAIN_TEST_CLASSNAMES[@]}" + + temp_dir=$(mktemp -d) + + for test in "${PLAIN_TEST_CLASSNAMES[@]}"; do + + find_test_in_sorted=$(grep "$test " /tmp/sorted.txt || true) + if [[ -z "$find_test_in_sorted" ]]; then + echo $test >> $temp_dir/tests-without-times.txt + else + echo $find_test_in_sorted >> $temp_dir/tests-with-times.txt + fi + + done + + echo "tests with times:" + cat $temp_dir/tests-with-times.txt + + sort -t' ' -nk2 $temp_dir/tests-with-times.txt >> $temp_dir/tests-with-times-sorted.txt + + ############################################################################################################ + ############################################################################################################ + ############################################################################################################ + # For each number_of_instances, iterate from bottom to top ('for ((j=$number_of_lines_in_file; j>0; j--))') + # (starting from the test that has the max time). Easier to understand is via an example. + # Suppose our /tmp/sorted.txt looks like this (very artificial): + # testA 1 sec + # testB 2 sec + # testC 3 sec + + # average_time_per_instance = 3.1 sec + # number_of_lines_in_file = 3 + + # we start iterating from bottom to top and first get 'testC' that has a running time of 3 sec + # since 3 sec < 3.1 sec ('if [[ $next_sum -lt $average_time_per_instance ]];') + # this test needs to be taken in 'tests_to_take_in_current_iteration', thus: + # tests_to_take_in_current_iteration=testC; also next_sum becomes 3sec + # we then drop this line from /tmp/sorted.txt because we have already processed it + # ('sed -i "" "${j}d" $temp_dir/tests-with-times-sorted.txt') + # we also decrement j, since we removed one line from the file + + # we then take testB, add its time to next_sum, thus next_sum = 5 sec, but now the time is NOT < 3.1 sec + # This means we do not take testB in current iteration and skip it. + + # Same goes for testA, next_sum = 4 sec, and it is NOT < 3.1 sec + # There are no more lines in file to iterate, thus tests_to_take_in_current_iteration=testC + + # we repeat the process again and now tests_to_take_in_current_iteration=testB,testA because their cumulative + # sum will be 3 sec and it's < 3.1 sec. + + sum_of_all_tests=$(awk -F' ' '{sum+=$2;} END{print sum;}' $temp_dir/tests-with-times-sorted.txt) + sum_of_all_tests_as_int=$(printf "%.0f\n" "$sum_of_all_tests") + echo "sum of all tests : $sum_of_all_tests_as_int" + + max_test_time=$(tail -1 $temp_dir/tests-with-times-sorted.txt | awk '{print $2}') + max_test_time_as_int=$(printf "%.0f\n" "$max_test_time") + echo "max test time : $max_test_time_as_int" + + number_of_instances=$(( $sum_of_all_tests_as_int / $max_test_time_as_int )) + echo "number of instances $number_of_instances" + + average_time_per_instance=$(( sum_of_all_tests_as_int / number_of_instances )) + echo "average time per instance $average_time_per_instance" + + number_of_lines_in_file=$(grep -c ^ $temp_dir/tests-with-times-sorted.txt) + echo "number of lines in fine : $number_of_lines_in_file" + tests_to_run_in_current_index='' + + for ((i=0; i<=${number_of_instances}; i++)) ; do + + sum=0 + tests_to_take_in_current_iteration='' + for ((j=$number_of_lines_in_file; j>0; j--)) ; do + + current_line_in_file=$(awk "NR == ${j}" $temp_dir/tests-with-times-sorted.txt) + current_test_time=$(echo $current_line_in_file | awk '{print $2}') + current_test_time=$(printf "%.0f\n" "$current_test_time") + current_test_name=$(echo $current_line_in_file | awk '{print $1}') + next_sum=$(( $sum + $current_test_time )) + + if [[ $next_sum -lt $average_time_per_instance ]]; then + + sum=$(( $sum + $current_test_time )) + + if [[ -z $tests_to_take_in_current_iteration ]]; then + tests_to_take_in_current_iteration="$current_test_name" + else + tests_to_take_in_current_iteration="$tests_to_take_in_current_iteration,$current_test_name" + fi + + sed -i "${j}d" $temp_dir/tests-with-times-sorted.txt + number_of_lines_in_file=$(( $number_of_lines_in_file-1 )) + continue + fi + + done + + if [[ $i = ${CURRENT_INDEX} ]]; then + echo "current index : ${CURRENT_INDEX}" + tests_to_run_in_current_index=$tests_to_take_in_current_iteration + echo "time of tests in current index : $sum" + fi + + done + + # here we compute tests that are "new", these are tests that our cache is not aware of + # for example these can come from a new PR where new tests are added + # This last step can have already existing tests to run, so we need to be careful to append + # new tests here. + + if [[ ${CURRENT_INDEX} = ${NUMBER_OF_JOBS} ]]; then + echo "last index spotted" + + if [ ! -f $temp_dir/tests-without-times.txt ]; then + echo "no tests outside cache found" + + if [ -z "$tests_to_run_in_current_index" ]; then + echo "no tests to run in the last index" + tests_to_run_in_current_index='none' + fi + + else + TESTS_WITHOUT_TIMES=($(cat $temp_dir/tests-without-times.txt)) + + for test in "${TESTS_WITHOUT_TIMES[@]}"; do + if [[ -z "$tests_to_run_in_current_index" ]]; then + tests_to_run_in_current_index="$test" + else + tests_to_run_in_current_index="$tests_to_run_in_current_index,$test" + fi + done + + fi + fi + + echo "will run tests : $tests_to_run_in_current_index" + + ./mvnw -s .settings.xml \ + -DtestsToRun=${tests_to_run_in_current_index} \ + -e clean install \ + -U -P sonar -nsu --batch-mode \ + -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn \ + -Dhttp.keepAlive=false \ + -Dmaven.wagon.http.pool=false \ + -Dmaven.wagon.http.retryHandler.class=standard \ + -Dmaven.wagon.http.retryHandler.count=3 \ + -Dskip.build.image=true + + touch /tmp/test_times_${{ env.CURRENT_INDEX }}.txt + + # if there are no tests to run in the last index, don't parse anything + if [ $tests_to_run_in_current_index != "none" ]; then + IFS=',' read -r -a sliced_array <<< "$tests_to_run_in_current_index" + unset IFS + + for i in "${sliced_array[@]}"; do + filename="${i}.txt" + echo "searching for filename: ${filename}" + file=$(find . -name "${filename}") + echo "found file: ${file}" + result=$(cat "${file}" | grep 'elapsed' | awk '{print $12, $13}') + + echo "run test: ${i} in : ${result}" >> /tmp/test_times_${{ env.CURRENT_INDEX }}.txt + done + fi + + + + - name: show individual test times + shell: bash + if: env.BASE_BRANCH != '2.1.x' + run: cat /tmp/test_times_${{ env.CURRENT_INDEX }}.txt + + - name: upload individual tests + if: env.BASE_BRANCH != '2.1.x' + uses: actions/upload-artifact@v3 + with: + name: test_times_${{ env.CURRENT_INDEX }}.txt + path: /tmp/test_times_${{ env.CURRENT_INDEX }}.txt diff --git a/.github/workflows/composites/test-times/action.yaml b/.github/workflows/composites/test-times/action.yaml new file mode 100644 index 00000000..97f9a320 --- /dev/null +++ b/.github/workflows/composites/test-times/action.yaml @@ -0,0 +1,58 @@ +# this stage sort all tests by their timings. For that we rely on the surefire-plugin reports +# that have the name as .txt and inside it there is a field that holds the time it took +# We get a hold of that field via awk '{print $12, $13} in the previous step. +# That previous step stores only tests for one matrix step, which we uploaded. +# In the steps below we download all artifacts (thus all test times), put them into a single file +# called /tmp/times-of-all-tests.txt, then sort this file, thus get a histogram of all test names +# and the time it took to run them. + +name: test-times +description: test-times +runs: + using: "composite" + + steps: + + # we omit the name, as such all artifacts are downloaded + - name: download all artifacts + if: env.BASE_BRANCH != '2.1.x' + uses: actions/download-artifact@v3 + with: + path: /tmp/all-artifacts + + - name: show all artifacts + if: env.BASE_BRANCH != '2.1.x' + shell: bash + run: ls -l /tmp/all-artifacts + + - name: merge all artifacts into a single file and sort by time + if: env.BASE_BRANCH != '2.1.x' + shell: bash + run: | + arr=($(find /tmp/all-artifacts/ -type f -name "test_times*")) + + for i in "${arr[@]}"; do + echo "parsing -> ${i}" + cat "${i}" | awk '{print $3" "$6}' >> /tmp/times-of-all-tests.txt + done + + sort -t' ' -nk2 /tmp/times-of-all-tests.txt >> /tmp/sorted.txt + + cat /tmp/sorted.txt + + - name: show all tests in a sorted manner + if: env.BASE_BRANCH != '2.1.x' + shell: bash + run: cat /tmp/sorted.txt + + # save with the current run_id, but restore it without it. This means two things: + # 1) if we re-run, cache will be available + # 2) if there is a new run, we restore as '${{ runner.os }}-spring-cloud-kubernetes-existing-test-times-cache-' + # meaning there could be many of them already present and this is not an exact match + # github in this case will pick up the latest one, exactly what we want. + - name: save test times in cache + uses: actions/cache/save@v3 + if: env.BASE_BRANCH != '2.1.x' + with: + path: /tmp/sorted.txt + key: ${{ runner.os }}-spring-cloud-kubernetes-existing-test-times-cache-${{ github.run_id }} diff --git a/.github/workflows/maven.yaml b/.github/workflows/maven.yaml index 1b1aecfc..b9fd2b50 100644 --- a/.github/workflows/maven.yaml +++ b/.github/workflows/maven.yaml @@ -10,8 +10,19 @@ jobs: build: runs-on: ubuntu-latest env: - TESTCONTAINERS_REUSE_ENABLE: false + # this might get set to true if there is an existing cache of test times + # this happens in 'matrix-bounds-on-test-times-cache-hit' + TEST_TIMES_CACHE_PRESENT: false SEGMENT_DOWNLOAD_TIMEOUT_MINS: 30 + + # this job ('build') outputs a value from step 'test_times_cache_present_init', that has the name of: + # 'test_times_cache_present'. This can later be used by other jobs. For example, we use this one + # to skip the job responsible for running the tests, if a previous cache that has the test times is not present. + # Same for other two variables 'number_of_matrix_instances' and 'matrix_array'. + outputs: + test_times_cache_present: ${{ steps.test_times_cache_present_init.outputs.test_times_cache_present }} + number_of_matrix_instances: ${{ steps.test_times_cache_present_init.outputs.number_of_matrix_instances }} + matrix_array: ${{ steps.test_times_cache_present_init.outputs.matrix_array }} steps: - name: checkout project @@ -35,9 +46,52 @@ jobs: - name: cache local maven repository uses: ./.github/workflows/composites/cache + - name: Show caches + uses: actions/github-script@v6 + with: + script: | + const caches = await github.rest.actions.getActionsCacheList({ + owner: context.repo.owner, + repo: context.repo.repo, + }) + for (const cache of caches.data.actions_caches) { + console.log(cache) + } + - name: maven build with dry-run for tests uses: ./.github/workflows/composites/maven-build-with-dry-run-for-tests + - name: restore test times cache if it exists + id: restore_test_times_cache + if: env.BASE_BRANCH != '2.1.x' + uses: actions/cache/restore@v3 + with: + path: /tmp/sorted.txt + key: ${{ runner.os }}-spring-cloud-kubernetes-existing-test-times-cache-${{ github.run_id }} + restore-keys: ${{ runner.os }}-spring-cloud-kubernetes-existing-test-times-cache- + + - name: check test times cache exists + id: check_files + uses: andstor/file-existence-action@v2 + with: + files: /tmp/sorted.txt + + - name: show existing cache of test times + if: steps.check_files.outputs.files_exists == 'true' + shell: bash + run: cat /tmp/sorted.txt + + - name: compute matrix related fields when cache is present + if: steps.check_files.outputs.files_exists == 'true' + uses: ./.github/workflows/composites/matrix-bounds-on-test-times-cache-hit + + - name: matrix related variables when cache is present + id: test_times_cache_present_init + run: | + echo "test_times_cache_present=${{ env.TEST_TIMES_CACHE_PRESENT }}" >> $GITHUB_OUTPUT + echo "number_of_matrix_instances=${{ env.NUMBER_OF_MATRIX_INSTANCES }}" >> $GITHUB_OUTPUT + echo "matrix_array=${{ env.MATRIX_ARRAY }}" >> $GITHUB_OUTPUT + - name: build controllers project uses: ./.github/workflows/composites/build-controllers-project if: env.BASE_BRANCH != '2.1.x' @@ -62,24 +116,22 @@ jobs: uses: ./.github/workflows/composites/upload-docker-images if: env.BASE_BRANCH != '2.1.x' - test: + test_when_cache_present: needs: [ build ] runs-on: ubuntu-latest - timeout-minutes: 60 env: SEGMENT_DOWNLOAD_TIMEOUT_MINS: 30 + # only run this one if there is a previous cache of test times + if: needs.build.outputs.test_times_cache_present == 'true' strategy: fail-fast: true matrix: - current_index: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31] - number_of_jobs: [32] + current_index: [ "${{ fromJSON(needs.build.outputs.matrix_array) }}" ] + number_of_jobs: [ "${{ fromJSON(needs.build.outputs.number_of_matrix_instances) }}" ] steps: - - name: testcontainers reuse support - run: echo "testcontainers.reuse.enable=true" > ~/.testcontainers.properties - - name: checkout project uses: actions/checkout@v2 @@ -97,22 +149,55 @@ jobs: uses: ./.github/workflows/composites/setup-jdk1.8 if: env.BASE_BRANCH == '2.1.x' - - name: cache local maven repository - uses: ./.github/workflows/composites/cache + - name: pre-test-actions + uses: ./.github/workflows/composites/pre-test-actions - - name: download docker images - uses: ./.github/workflows/composites/download-docker-images - if: env.BASE_BRANCH != '2.1.x' + - name: testcontainers reuse support + shell: bash + run: echo "testcontainers.reuse.enable=true" > ~/.testcontainers.properties - - name: load docker images into local repo - uses: ./.github/workflows/composites/load-docker-images - if: env.BASE_BRANCH != '2.1.x' + - name: run and save test times when cache is present + uses: ./.github/workflows/composites/run-and-save-test-times-when-cache-present + env: + CURRENT_INDEX: ${{ matrix.current_index }} + NUMBER_OF_JOBS: ${{ matrix.number_of_jobs }} - - name: download tests - uses: actions/download-artifact@v3 - with: - name: tests.txt - path: /tmp + test_when_cache_missing: + needs: [ build ] + runs-on: ubuntu-latest + env: + SEGMENT_DOWNLOAD_TIMEOUT_MINS: 30 + timeout-minutes: 60 + # only run this one if there is no previous cache of test times + if: needs.build.outputs.test_times_cache_present == 'false' + + strategy: + fail-fast: true + matrix: + current_index: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31] + number_of_jobs: [32] + + steps: + + - name: checkout project + uses: actions/checkout@v2 + + - name: clean space + uses: ./.github/workflows/composites/clean-space + + - name: set env variables + uses: ./.github/workflows/composites/env-variables + + - name: setup project jdk-17 + uses: ./.github/workflows/composites/setup-jdk17 + if: env.BASE_BRANCH == 'main' || env.BASE_BRANCH == '3.0.x' + + - name: setup project jdk-8 + uses: ./.github/workflows/composites/setup-jdk1.8 + if: env.BASE_BRANCH == '2.1.x' + + - name: pre-test-actions + uses: ./.github/workflows/composites/pre-test-actions - name: compute single step test bounds uses: ./.github/workflows/composites/test-bounds @@ -120,56 +205,38 @@ jobs: CURRENT_INDEX: ${{ matrix.current_index }} NUMBER_OF_JOBS: ${{ matrix.number_of_jobs }} - - name: run tests + - name: testcontainers reuse support + shell: bash + run: echo "testcontainers.reuse.enable=true" > ~/.testcontainers.properties + + - name: run and save individual test times env: CURRENT_INDEX: ${{ matrix.current_index }} - run: | - - PLAIN_TEST_CLASSNAMES=($(cat /tmp/tests.txt | grep -o 'spring.cloud.k8s.test.to.run -> org.*' | awk '{print $3}')) - IFS=$'\n' - SORTED_TEST_CLASSNAMES=( $(sort <<< "${PLAIN_TEST_CLASSNAMES[*]}") ) - unset IFS - - start_from_name=start_from_${CURRENT_INDEX} - how_many_name=how_many_${CURRENT_INDEX} - - start_from=${!start_from_name} - how_many=${!how_many_name} - - echo "${SORTED_TEST_CLASSNAMES[@]}" - echo "$start_from_name : ${start_from}" - echo "$how_many_name : ${how_many}" - - sliced_array=(${SORTED_TEST_CLASSNAMES[@]:$start_from:$how_many}) + if: env.BASE_BRANCH != '2.1.x' + uses: ./.github/workflows/composites/run-and-save-test-times-when-cache-missing - TEST_ARG=$(echo ${sliced_array[@]} | sed 's/ /,/g') - echo "$TEST_ARG" - - if [[ $baseBranch == "2.1.x" ]]; then - - ./mvnw -s .settings.xml -pl '-:kubernetes-leader-election-example' -pl '-:kubernetes-hello-world-example' \ - -pl '-:kubernetes-reload-example' -pl '-:kubernetes-loadbalancer-example' \ - -pl '-:spring-cloud-kubernetes-configserver' -pl '-:spring-cloud-kubernetes-configuration-watcher' \ - -pl '-:spring-cloud-kubernetes-discoveryserver' \ - -DtestsToRun=${TEST_ARG[@]} \ - -e clean install \ - -U -P sonar -nsu --batch-mode \ - -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn \ - -Dhttp.keepAlive=false \ - -Dmaven.wagon.http.pool=false \ - -Dmaven.wagon.http.retryHandler.class=standard \ - -Dmaven.wagon.http.retryHandler.count=3 \ - -Dskip.build.image=true - - else - ./mvnw -s .settings.xml \ - -DtestsToRun=${TEST_ARG[@]} \ - -e clean install \ - -U -P sonar -nsu --batch-mode \ - -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn \ - -Dhttp.keepAlive=false \ - -Dmaven.wagon.http.pool=false \ - -Dmaven.wagon.http.retryHandler.class=standard \ - -Dmaven.wagon.http.retryHandler.count=3 \ - -Dskip.build.image=true - fi + save_test_times_when_cache_missing: + runs-on: ubuntu-latest + needs: [build, test_when_cache_missing ] + + steps: + + - name: checkout project + uses: actions/checkout@v2 + + - name: compute and save running time of tests + if: env.BASE_BRANCH != '2.1.x' + uses: ./.github/workflows/composites/test-times + + save_test_times_when_cache_present: + runs-on: ubuntu-latest + needs: [ build, test_when_cache_present ] + + steps: + + - name: checkout project + uses: actions/checkout@v2 + + - name: compute and save running time of tests + if: env.BASE_BRANCH != '2.1.x' + uses: ./.github/workflows/composites/test-times diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/java/org/springframework/cloud/kubernetes/client/config/it/ConfigMapAndSecretIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/java/org/springframework/cloud/kubernetes/client/config/it/ConfigMapAndSecretIT.java index 6cd1c099..1137969a 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/java/org/springframework/cloud/kubernetes/client/config/it/ConfigMapAndSecretIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/java/org/springframework/cloud/kubernetes/client/config/it/ConfigMapAndSecretIT.java @@ -115,7 +115,7 @@ class ConfigMapAndSecretIT { WebClient.Builder builder = builder(); WebClient propertyClient = builder.baseUrl(PROPERTY_URL).build(); - await().timeout(Duration.ofSeconds(60)).pollInterval(Duration.ofSeconds(2)).until(() -> propertyClient + await().timeout(Duration.ofSeconds(120)).pollInterval(Duration.ofSeconds(2)).until(() -> propertyClient .method(HttpMethod.GET).retrieve().bodyToMono(String.class).block().equals("from-config-map")); WebClient secretClient = builder.baseUrl(SECRET_URL).build(); diff --git a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/tests/commons/junit_extension/DisabledTestsCondition.java b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/tests/commons/junit_extension/DisabledTestsCondition.java index 10f84ea8..50a1de99 100644 --- a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/tests/commons/junit_extension/DisabledTestsCondition.java +++ b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/tests/commons/junit_extension/DisabledTestsCondition.java @@ -40,7 +40,8 @@ public class DisabledTestsCondition implements ExecutionCondition { @Override public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext extensionContext) { if ("true".equals(System.getProperty("spring.cloud.k8s.skip.tests"))) { - System.out.println("spring.cloud.k8s.test.to.run -> " + extensionContext.getRequiredTestClass().getName()); + System.out.println("\nspring.cloud.k8s.test.to.run -> " + + extensionContext.getRequiredTestClass().getName() + " \n"); return ConditionEvaluationResult.disabled(""); } else {