From 7ce896023b465fe44991608b65e766ef41bdf4ca Mon Sep 17 00:00:00 2001 From: Lukasz Antoniak Date: Thu, 13 Nov 2025 14:20:17 +0100 Subject: [PATCH] CASSANALYTICS-106: Setup CI Pipeline with GitHub Actions --- .github/workflows/test.yaml | 211 ++++++++++++++++++ .../distributed/impl/CassandraCluster.java | 28 +++ .../apache/cassandra/testing/TestUtils.java | 12 + ...lkWriteDownInstanceMultipleTokensTest.java | 12 + ...ulkWriteDownSidecarMultipleTokensTest.java | 12 + ...ndraAnalyticsSimpleMultipleTokensTest.java | 12 + .../analytics/RandomPartitionerTest.java | 12 + .../analytics/data/ClearSnapshotTest.java | 6 + ...eS3CompatModeSimpleMultipleTokensTest.java | 12 + 9 files changed, 317 insertions(+) create mode 100644 .github/workflows/test.yaml diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 000000000..d5235fd80 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,211 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: Test + +on: + push: + branches: [ "trunk" ] + pull_request: + branches: [ "trunk" ] + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +jobs: + build: + name: Compile and build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup JDK + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: 11 + - run: | + sudo apt-get update + + if [ -f /etc/ssl/certs/java/cacerts/cacerts ]; then + sudo mv /etc/ssl/certs/java/cacerts/ /etc/ssl/certs/java/cacerts-old + sudo mv /etc/ssl/certs/java/cacerts-old/cacerts /etc/ssl/certs/java/ + sudo rmdir /etc/ssl/certs/java/cacerts-old + fi + + apt-get download ant ant-optional + sudo dpkg --force-all -i ant*.deb + rm ant*.deb + + sudo bash -c 'for i in {2..20}; do echo 127.0.0.${i} localhost${i} >> /etc/hosts; done' + + for i in {2..20} + do + sudo ip addr add "127.0.0.${i}" dev lo + sudo route add -host "127.0.0.${i}" dev lo; + done + + CASSANDRA_USE_JDK11=true ./scripts/build-dependencies.sh + + ./gradlew codeCheckTasks + - name: Cache Maven repository + uses: actions/cache@v4 + with: + path: ~/.m2 + key: maven-repo-${{ github.sha }} + - name: Cache workspace + id: cache-build-save + uses: actions/cache/save@v4 + with: + path: ${{ github.workspace }} + key: build-${{ github.sha }} + + unit-test: + name: Unit test + needs: build + runs-on: ubuntu-latest + strategy: + matrix: + scala: [ '2.12', '2.13' ] + fail-fast: false + steps: + - name: Setup JDK + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: 11 + - run: | + sudo bash -c 'for i in {2..20}; do echo 127.0.0.${i} localhost${i} >> /etc/hosts; done' + for i in {2..20} + do + sudo ip addr add "127.0.0.${i}" dev lo + sudo route add -host "127.0.0.${i}" dev lo; + done + - name: Cache Maven repository + uses: actions/cache@v4 + with: + path: ~/.m2 + key: maven-repo-${{ github.sha }} + - name: Cache workspace + id: cache-build-restore + uses: actions/cache/restore@v4 + with: + path: ${{ github.workspace }} + key: build-${{ github.sha }} + - run: | + export SPARK_VERSION="3" + export SCALA_VERSION="${{ matrix.scala }}" + export JDK_VERSION="11" + export INTEGRATION_MAX_PARALLEL_FORKS=1 + export INTEGRATION_MAX_HEAP_SIZE="1500M" + export CASSANDRA_USE_JDK11=true + + ./gradlew --stacktrace clean assemble check -x cassandra-analytics-integration-tests:test + + integration-test: + name: Integration test + needs: build + runs-on: ubuntu-latest + strategy: + matrix: + scala: [ '2.12', '2.13' ] + cassandra: [ '4.0.17', '4.1.4', '5.0.5' ] + job_index: [ 0, 1, 2, 3, 4 ] + job_total: [ 5 ] + exclude: + - scala: "2.12" + cassandra: "5.0.5" + - scala: "2.12" + cassandra: "4.1.4" + - scala: "2.13" + cassandra: "4.0.17" + fail-fast: false + steps: + - name: Setup JDK + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: 11 + - run: | + sudo bash -c 'for i in {2..20}; do echo 127.0.0.${i} localhost${i} >> /etc/hosts; done' + for i in {2..20} + do + sudo ip addr add "127.0.0.${i}" dev lo + sudo route add -host "127.0.0.${i}" dev lo; + done + - name: Cache Maven repository + uses: actions/cache@v4 + with: + path: ~/.m2 + key: maven-repo-${{ github.sha }} + - name: Cache workspace + id: cache-build-restore + uses: actions/cache/restore@v4 + with: + path: ${{ github.workspace }} + key: build-${{ github.sha }} + - run: | + export SPARK_VERSION="3" + export SCALA_VERSION="${{ matrix.scala }}" + export JDK_VERSION="11" + export INTEGRATION_MAX_PARALLEL_FORKS=1 + export INTEGRATION_MAX_HEAP_SIZE="4096M" + export CASSANDRA_USE_JDK11=true + + export DTEST_JAR="dtest-${{ matrix.cassandra }}.jar" + export CASSANDRA_VERSION=$(echo ${{ matrix.cassandra }} | cut -d'.' -f 1,2) + + ./gradlew --stacktrace clean assemble + + cd cassandra-analytics-integration-tests/src/test/java + CLASSNAMES=$(find . -name '*Test.java' | sort | cut -c 3- | sed 's@/@.@g' | sed 's/.\{5\}$//' | awk 'NR % ${{ matrix.job_total }} == ${{ matrix.job_index }}') + cd ../../../.. + + EXIT_STATUS=0 + # Execution of "gradle test --test $TEST_NAME" returns non-zero exit code when commend did not run any test + # (e.g. when all tests are ignored). Currently there is no option to change Gradle behaviour. + # Workaround the issue by explicitly skipping test classes that cannot be executed on Cassandra 4.0. + C40_EXCLUSIONS=("org.apache.cassandra.analytics.BulkWriteDownInstanceMultipleTokensTest" "org.apache.cassandra.analytics.BulkWriteDownSidecarMultipleTokensTest" "org.apache.cassandra.analytics.CassandraAnalyticsSimpleMultipleTokensTest" "org.apache.cassandra.analytics.RandomPartitionerTest" "org.apache.cassandra.analytics.testcontainer.BulkWriteS3CompatModeSimpleMultipleTokensTest" "org.apache.cassandra.analytics.data.ClearSnapshotTest") + for TEST_NAME in $CLASSNAMES; do + SKIP="false" + for C40_EXCLUSION in "${C40_EXCLUSIONS[@]}"; + do + if [[ "$CASSANDRA_VERSION" == "4.0" && "$TEST_NAME" == ${C40_EXCLUSION} ]]; then + SKIP="true" + break + fi + done + + test_id=$(date +%H%M%S) + mkdir -p test-reports/$test_id + + if [ $SKIP == "false" ]; then + echo Executing test $TEST_NAME + ./gradlew --stacktrace cassandra-analytics-integration-tests:test --tests $TEST_NAME --no-daemon || EXIT_STATUS=$?; + mv build/test-reports/* test-reports/$test_id + else + echo "Skipping test $TEST_NAME" + fi + done; + + tar czf integration-tests.tar.gz test-reports/* + + exit $EXIT_STATUS + - name: Publish test results + uses: actions/upload-artifact@v4 + if: (!cancelled()) + with: + name: integration-tests-${{ matrix.scala }}-${{ matrix.cassandra }}-${{ matrix.job_index }} + path: integration-tests.tar.gz \ No newline at end of file diff --git a/cassandra-analytics-integration-framework/src/main/java/org/apache/cassandra/distributed/impl/CassandraCluster.java b/cassandra-analytics-integration-framework/src/main/java/org/apache/cassandra/distributed/impl/CassandraCluster.java index ab8524dc4..dfbd996c9 100644 --- a/cassandra-analytics-integration-framework/src/main/java/org/apache/cassandra/distributed/impl/CassandraCluster.java +++ b/cassandra-analytics-integration-framework/src/main/java/org/apache/cassandra/distributed/impl/CassandraCluster.java @@ -20,6 +20,8 @@ package org.apache.cassandra.distributed.impl; import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.net.InetSocketAddress; import java.util.Arrays; import java.util.Iterator; @@ -94,6 +96,11 @@ public AbstractCluster initializeCluster(String versionString, int originalNodeCount = nodesPerDc * dcCount; int finalNodeCount = dcCount * (nodesPerDc + newNodesPerDc); + if (requestedVersion.version.isLowerThan("4.1")) + { + updateCassandra40DTestSharedClasses(); + } + UpgradeableCluster.Builder clusterBuilder = UpgradeableCluster.build(originalNodeCount); clusterBuilder.withVersion(requestedVersion) .withDynamicPortAllocation(configuration.dynamicPortAllocation) // to allow parallel test runs @@ -152,6 +159,27 @@ public AbstractCluster initializeCluster(String versionString, return (AbstractCluster) cluster; } + /** + * CASSANDRA-16931 has not been backported to Cassandra 4.0, so we cannot + * configure shared classes. Applying a workaround with reflection. + */ + private static void updateCassandra40DTestSharedClasses() + { + try + { + Field field = AbstractCluster.class.getDeclaredField("SHARED_PREDICATE"); + field.setAccessible(true); + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); + field.set(null, ((Predicate) field.get(null)).or(EXTRA)); + } + catch (Exception e) + { + throw new IllegalStateException("Failed to adjust Cassandra 4.0 DTest shared classes", e); + } + } + // IClusterExtension methods @Override diff --git a/cassandra-analytics-integration-framework/src/main/java/org/apache/cassandra/testing/TestUtils.java b/cassandra-analytics-integration-framework/src/main/java/org/apache/cassandra/testing/TestUtils.java index e98b5f32b..a233d377f 100644 --- a/cassandra-analytics-integration-framework/src/main/java/org/apache/cassandra/testing/TestUtils.java +++ b/cassandra-analytics-integration-framework/src/main/java/org/apache/cassandra/testing/TestUtils.java @@ -24,6 +24,8 @@ import com.google.common.collect.ImmutableMap; +import com.vdurmont.semver4j.Semver; +import org.apache.cassandra.distributed.shared.Versions; import org.apache.cassandra.sidecar.testing.QualifiedName; /** @@ -130,4 +132,14 @@ public static void configureDefaultDTestJarProperties() System.setProperty("cassandra.minimum_replication_factor", "1"); } + + /** + * Utility method to know Cassandra cluster version before it is being initialized. + */ + public static Semver getDTestClusterVersion() + { + Versions versions = Versions.find(); + TestVersion testVersion = TestVersionSupplier.testVersions().findFirst().orElseThrow(); + return versions.getLatest(new Semver(testVersion.version(), Semver.SemverType.LOOSE)).version; + } } diff --git a/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/BulkWriteDownInstanceMultipleTokensTest.java b/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/BulkWriteDownInstanceMultipleTokensTest.java index 3351d546f..fcd3de664 100644 --- a/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/BulkWriteDownInstanceMultipleTokensTest.java +++ b/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/BulkWriteDownInstanceMultipleTokensTest.java @@ -18,7 +18,11 @@ package org.apache.cassandra.analytics; +import com.vdurmont.semver4j.Semver; import org.apache.cassandra.testing.ClusterBuilderConfiguration; +import org.apache.cassandra.testing.TestUtils; + +import static org.assertj.core.api.Assumptions.assumeThat; public class BulkWriteDownInstanceMultipleTokensTest extends BulkWriteDownInstanceTest { @@ -28,4 +32,12 @@ protected ClusterBuilderConfiguration testClusterConfiguration() return super.testClusterConfiguration() .tokenCount(4); } + + @Override + protected void beforeClusterProvisioning() + { + assumeThat(TestUtils.getDTestClusterVersion().isGreaterThanOrEqualTo(new Semver("4.1", Semver.SemverType.LOOSE))) + .describedAs("Cassandra 4.0 DTest does not support v-nodes") + .isTrue(); + } } diff --git a/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/BulkWriteDownSidecarMultipleTokensTest.java b/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/BulkWriteDownSidecarMultipleTokensTest.java index 57e98920d..645fc0762 100644 --- a/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/BulkWriteDownSidecarMultipleTokensTest.java +++ b/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/BulkWriteDownSidecarMultipleTokensTest.java @@ -18,7 +18,11 @@ package org.apache.cassandra.analytics; +import com.vdurmont.semver4j.Semver; import org.apache.cassandra.testing.ClusterBuilderConfiguration; +import org.apache.cassandra.testing.TestUtils; + +import static org.assertj.core.api.Assumptions.assumeThat; public class BulkWriteDownSidecarMultipleTokensTest extends BulkWriteDownSidecarTest { @@ -28,4 +32,12 @@ protected ClusterBuilderConfiguration testClusterConfiguration() return super.testClusterConfiguration() .tokenCount(4); } + + @Override + protected void beforeClusterProvisioning() + { + assumeThat(TestUtils.getDTestClusterVersion().isGreaterThanOrEqualTo(new Semver("4.1", Semver.SemverType.LOOSE))) + .describedAs("Cassandra 4.0 DTest does not support v-nodes") + .isTrue(); + } } diff --git a/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/CassandraAnalyticsSimpleMultipleTokensTest.java b/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/CassandraAnalyticsSimpleMultipleTokensTest.java index b5e9cf781..efd5b3adb 100644 --- a/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/CassandraAnalyticsSimpleMultipleTokensTest.java +++ b/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/CassandraAnalyticsSimpleMultipleTokensTest.java @@ -18,7 +18,11 @@ package org.apache.cassandra.analytics; +import com.vdurmont.semver4j.Semver; import org.apache.cassandra.testing.ClusterBuilderConfiguration; +import org.apache.cassandra.testing.TestUtils; + +import static org.assertj.core.api.Assumptions.assumeThat; public class CassandraAnalyticsSimpleMultipleTokensTest extends CassandraAnalyticsSimpleTest { @@ -28,4 +32,12 @@ protected ClusterBuilderConfiguration testClusterConfiguration() return super.testClusterConfiguration() .tokenCount(4); } + + @Override + protected void beforeClusterProvisioning() + { + assumeThat(TestUtils.getDTestClusterVersion().isGreaterThanOrEqualTo(new Semver("4.1", Semver.SemverType.LOOSE))) + .describedAs("Cassandra 4.0 DTest does not support v-nodes") + .isTrue(); + } } diff --git a/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/RandomPartitionerTest.java b/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/RandomPartitionerTest.java index 6f7dfa064..9705568f9 100644 --- a/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/RandomPartitionerTest.java +++ b/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/RandomPartitionerTest.java @@ -19,7 +19,11 @@ package org.apache.cassandra.analytics; +import com.vdurmont.semver4j.Semver; import org.apache.cassandra.testing.ClusterBuilderConfiguration; +import org.apache.cassandra.testing.TestUtils; + +import static org.assertj.core.api.Assumptions.assumeThat; /** * Runs test from {@link CassandraAnalyticsSimpleTest} with the {@code RandomPartitioner}. @@ -37,4 +41,12 @@ protected ClusterBuilderConfiguration testClusterConfiguration() { return super.testClusterConfiguration().partitioner("org.apache.cassandra.dht.RandomPartitioner"); } + + @Override + protected void beforeClusterProvisioning() + { + // Cassandra 4.0 DTest uses TokenSupplier#token(int) method, which fails for big integer token. + assumeThat(TestUtils.getDTestClusterVersion() + .isGreaterThanOrEqualTo(new Semver("4.1", Semver.SemverType.LOOSE))).isTrue(); + } } diff --git a/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/data/ClearSnapshotTest.java b/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/data/ClearSnapshotTest.java index 261502a95..ebb89a5f7 100644 --- a/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/data/ClearSnapshotTest.java +++ b/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/data/ClearSnapshotTest.java @@ -31,16 +31,19 @@ import org.junit.jupiter.api.Test; +import com.vdurmont.semver4j.Semver; import org.apache.cassandra.analytics.SharedClusterSparkIntegrationTestBase; import org.apache.cassandra.distributed.api.ConsistencyLevel; import org.apache.cassandra.distributed.shared.Uninterruptibles; import org.apache.cassandra.sidecar.testing.QualifiedName; +import org.apache.cassandra.testing.TestUtils; import org.apache.spark.sql.DataFrameReader; import org.apache.spark.sql.Row; import static org.apache.cassandra.testing.TestUtils.DC1_RF1; import static org.apache.cassandra.testing.TestUtils.TEST_KEYSPACE; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assumptions.assumeThat; class ClearSnapshotTest extends SharedClusterSparkIntegrationTestBase { @@ -117,6 +120,9 @@ private List findChildFile(Path path, String target) @Override protected void beforeClusterProvisioning() { + assumeThat(TestUtils.getDTestClusterVersion().isGreaterThanOrEqualTo(new Semver("4.1", Semver.SemverType.LOOSE))) + .describedAs("TTL support for snapshot was added to Cassandra 4.1 as part of CASSANDRA-16789") + .isTrue(); System.setProperty("cassandra.snapshot.ttl_cleanup_initial_delay_seconds", "0"); System.setProperty("cassandra.snapshot.ttl_cleanup_period_seconds", "1"); System.setProperty("cassandra.snapshot.min_allowed_ttl_seconds", "5"); diff --git a/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/testcontainer/BulkWriteS3CompatModeSimpleMultipleTokensTest.java b/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/testcontainer/BulkWriteS3CompatModeSimpleMultipleTokensTest.java index c75ba488f..8a0f4fdf9 100644 --- a/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/testcontainer/BulkWriteS3CompatModeSimpleMultipleTokensTest.java +++ b/cassandra-analytics-integration-tests/src/test/java/org/apache/cassandra/analytics/testcontainer/BulkWriteS3CompatModeSimpleMultipleTokensTest.java @@ -18,7 +18,11 @@ package org.apache.cassandra.analytics.testcontainer; +import com.vdurmont.semver4j.Semver; import org.apache.cassandra.testing.ClusterBuilderConfiguration; +import org.apache.cassandra.testing.TestUtils; + +import static org.assertj.core.api.Assumptions.assumeThat; public class BulkWriteS3CompatModeSimpleMultipleTokensTest extends BulkWriteS3CompatModeSimpleTest { @@ -28,4 +32,12 @@ protected ClusterBuilderConfiguration testClusterConfiguration() return super.testClusterConfiguration() .tokenCount(4); } + + @Override + protected void beforeClusterProvisioning() + { + assumeThat(TestUtils.getDTestClusterVersion().isGreaterThanOrEqualTo(new Semver("4.1", Semver.SemverType.LOOSE))) + .describedAs("Cassandra 4.0 DTest does not support v-nodes") + .isTrue(); + } }