diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..4eb25a4dd --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,68 @@ +name: CI + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] + +jobs: + test: + runs-on: ubuntu-24.04 + strategy: + matrix: + scylla-version: [ENTERPRISE-RELEASE, OSS-RELEASE] + fail-fast: false + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Set up JDK 8 + uses: actions/setup-java@v4 + with: + java-version: 8 + distribution: 'temurin' + + - name: Setup Python 3 + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Setup environment + run: | + pip3 install https://github.com/scylladb/scylla-ccm/archive/5392dd68748ee5e71f7fbad346667038437d2123.zip + CCM_PATH=$(which ccm) + sudo mkdir -p /usr/local/bin + sudo ln -sf ${CCM_PATH} /usr/local/bin/ccm + + - name: Install get-version CLI + run: | + git clone https://github.com/scylladb-actions/get-version.git + cd get-version + go mod tidy + go build -o get-version + + - name: Get scylla version + id: scylla-version + run: | + cd get-version + if [[ "${{ matrix.scylla-version }}" == "ENTERPRISE-RELEASE" ]]; then + echo "SCYLLA_VERSION=$(./get-version --source dockerhub-imagetag --repo scylladb/scylla-enterprise -filters "2024.1.LAST" | tr -d '\"')" >> $GITHUB_ENV + elif [[ "${{ matrix.scylla-version }}" == "OSS-RELEASE" ]]; then + echo "SCYLLA_VERSION=$(./get-version --source dockerhub-imagetag --repo scylladb/scylla -filters "6.1.LAST" | tr -d '\"')" >> $GITHUB_ENV + elif echo "${{ matrix.scylla-version }}" | grep -P '^[0-9\.]+'; then # If you want to run specific version do just that + echo "SCYLLA_VERSION=${{ matrix.scylla-version }}" >> $GITHUB_ENV + else + echo "Unknown scylla version name `${{ matrix.scylla-version }}`" + exit 1 + fi + + - name: Run integration tests on Scylla + run: SCYLLA_EXT_OPTS="--smp 2 --memory 4G" SIMULACRON_PATH=../../../../../ci/simulacron-standalone-0.12.0.jar dotnet test src/Cassandra.IntegrationTests/Cassandra.IntegrationTests.csproj -f net8 -l "console;verbosity=detailed" --filter "(FullyQualifiedName!~ClientWarningsTests & FullyQualifiedName!~CustomPayloadTests & FullyQualifiedName!~Connect_With_Ssl_Test & FullyQualifiedName!~Should_UpdateHosts_When_HostIpChanges & FullyQualifiedName!~Should_UseNewHostInQueryPlans_When_HostIsDecommissionedAndJoinsAgain & FullyQualifiedName!~Should_RemoveNodeMetricsAndDisposeMetricsContext_When_HostIsRemoved & FullyQualifiedName!~Virtual_Keyspaces_Are_Included & FullyQualifiedName!~Virtual_Table_Metadata_Test & FullyQualifiedName!~SessionAuthenticationTests & FullyQualifiedName!~TypeSerializersTests & FullyQualifiedName!~Custom_MetadataTest & FullyQualifiedName!~LinqWhere_WithVectors & FullyQualifiedName!~SimpleStatement_With_No_Compact_Enabled_Should_Reveal_Non_Schema_Columns & FullyQualifiedName!~SimpleStatement_With_No_Compact_Disabled_Should_Not_Reveal_Non_Schema_Columns & FullyQualifiedName!~ColumnClusteringOrderReversedTest & FullyQualifiedName!~GetMaterializedView_Should_Refresh_View_Metadata_Via_Events & FullyQualifiedName!~MaterializedView_Base_Table_Column_Addition & FullyQualifiedName!~MultipleSecondaryIndexTest & FullyQualifiedName!~RaiseErrorOnInvalidMultipleSecondaryIndexTest & FullyQualifiedName!~TableMetadataAllTypesTest & FullyQualifiedName!~TableMetadataClusteringOrderTest & FullyQualifiedName!~TableMetadataCollectionsSecondaryIndexTest & FullyQualifiedName!~TableMetadataCompositePartitionKeyTest & FullyQualifiedName!~TupleMetadataTest & FullyQualifiedName!~Udt_Case_Sensitive_Metadata_Test & FullyQualifiedName!~UdtMetadataTest & FullyQualifiedName!~Should_Retrieve_Table_Metadata & FullyQualifiedName!~CreateTable_With_Frozen_Key & FullyQualifiedName!~CreateTable_With_Frozen_Udt & FullyQualifiedName!~CreateTable_With_Frozen_Value & FullyQualifiedName!~Should_AllMetricsHaveValidValues_When_AllNodesAreUp & FullyQualifiedName!~SimpleStatement_Dictionary_Parameters_CaseInsensitivity_ExcessOfParams & FullyQualifiedName!~SimpleStatement_Dictionary_Parameters_CaseInsensitivity_NoOverload & FullyQualifiedName!~TokenAware_TransientReplication_NoHopsAndOnlyFullReplicas & FullyQualifiedName!~GetFunction_Should_Return_Most_Up_To_Date_Metadata_Via_Events & FullyQualifiedName!~LargeDataTests & FullyQualifiedName!~MetadataTests & FullyQualifiedName!~MultiThreadingTests & FullyQualifiedName!~PoolTests & FullyQualifiedName!~PrepareLongTests & FullyQualifiedName!~SpeculativeExecutionLongTests & FullyQualifiedName!~StressTests & FullyQualifiedName!~TransitionalAuthenticationTests & FullyQualifiedName!~ProxyAuthenticationTests & FullyQualifiedName!~SessionDseAuthenticationTests & FullyQualifiedName!~CloudIntegrationTests & FullyQualifiedName!~CoreGraphTests & FullyQualifiedName!~GraphTests & FullyQualifiedName!~InsightsIntegrationTests & FullyQualifiedName!~DateRangeTests & FullyQualifiedName!~FoundBugTests & FullyQualifiedName!~GeometryTests & FullyQualifiedName!~LoadBalancingPolicyTests & FullyQualifiedName!~ConsistencyTests & FullyQualifiedName!~LoadBalancingPolicyTests & FullyQualifiedName!~ReconnectionPolicyTests & FullyQualifiedName!~RetryPolicyTests)" diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 80b00230a..000000000 --- a/.travis.yml +++ /dev/null @@ -1,37 +0,0 @@ -language: csharp -sudo: required -mono: none -dist: focal - -branches: - except: - - 1.0 - - 2.0 - - 2.1 - - dse_3.1 - -matrix: - include: - - dotnet: 8.0.200 - env: TARGETF="net8" BUILD_EXAMPLES="1" BuildCoreOnly=True CASSANDRA_NETCORE_RUNTIME="8" RunCodeAnalyzers="True" - - dotnet: 6.0.419 - env: TARGETF="net6" BUILD_EXAMPLES="0" BuildCoreOnly=True CASSANDRA_NETCORE_RUNTIME="6" RunCodeAnalyzers="True" - - dotnet: 7.0.406 - env: TARGETF="net7" BUILD_EXAMPLES="0" BuildCoreOnly=True CASSANDRA_NETCORE_RUNTIME="7" RunCodeAnalyzers="True" - -script: - - dotnet --info - - | - if [ "${BUILD_EXAMPLES}" == "1" ] - then - dotnet restore examples - dotnet build --no-restore examples/examples.sln -c Release - fi - - dotnet restore src - - # work around dotnet sdk concurrency issue - - dotnet build src/Cassandra.Tests/Cassandra.Tests.csproj --no-restore -c Release || true - - sleep 5 - - - dotnet build src/Cassandra.Tests/Cassandra.Tests.csproj --no-restore -c Release || true - - dotnet test src/Cassandra.Tests/Cassandra.Tests.csproj --no-restore --no-build -v n -c Release -f $TARGETF \ No newline at end of file diff --git a/ci/simulacron-standalone-0.12.0.jar b/ci/simulacron-standalone-0.12.0.jar new file mode 100644 index 000000000..2b9791e34 Binary files /dev/null and b/ci/simulacron-standalone-0.12.0.jar differ diff --git a/src/Cassandra.IntegrationTests/Core/ClusterTests.cs b/src/Cassandra.IntegrationTests/Core/ClusterTests.cs index e972fb34c..656a7a952 100644 --- a/src/Cassandra.IntegrationTests/Core/ClusterTests.cs +++ b/src/Cassandra.IntegrationTests/Core/ClusterTests.cs @@ -190,7 +190,7 @@ await TestGlobals.ConnectAndDispose(cluster, false, session => [Category(TestCategory.RealClusterLong)] public async Task Should_Remove_Decommissioned_Node() { - const int numberOfNodes = 2; + const int numberOfNodes = 3; _realCluster = TestClusterManager.CreateNew(numberOfNodes); var cluster = ClusterBuilder().AddContactPoint(_realCluster.InitialContactPoint).Build(); @@ -210,10 +210,10 @@ await TestGlobals.ConnectAndDispose(cluster, false, session => string decommisionedNode = null; TestHelper.RetryAssert(() => { - decommisionedNode = _realCluster.ClusterIpPrefix + 2; + decommisionedNode = _realCluster.ClusterIpPrefix + 3; Assert.False(TestUtils.IsNodeReachable(IPAddress.Parse(decommisionedNode))); //New node should be part of the metadata - Assert.AreEqual(1, cluster.AllHosts().Count); + Assert.AreEqual(2, cluster.AllHosts().Count); }, 100, 100); var queried = false; for (var i = 0; i < 10; i++) diff --git a/src/Cassandra.IntegrationTests/Core/ParameterizedStatementsTests.cs b/src/Cassandra.IntegrationTests/Core/ParameterizedStatementsTests.cs index 665c43e1b..bf767ddd4 100644 --- a/src/Cassandra.IntegrationTests/Core/ParameterizedStatementsTests.cs +++ b/src/Cassandra.IntegrationTests/Core/ParameterizedStatementsTests.cs @@ -50,9 +50,7 @@ protected override string[] SetupQueries } }; - // COMPACT STORAGE is not supported by DSE 6.0 / C* 4.0. - if (TestClusterManager.CheckCassandraVersion(true, new Version(4, 0), Comparison.LessThan) || - (TestClusterManager.IsDse && TestClusterManager.CheckDseVersion(new Version(6, 0), Comparison.LessThan))) + if (!TestClusterManager.IsScylla && TestClusterManager.CheckCassandraVersion(false, new Version(4, 0), Comparison.LessThan)) { setupQueries.Add($"CREATE TABLE {TableCompactStorage} (key blob PRIMARY KEY, bar int, baz uuid)" + $" WITH COMPACT STORAGE"); @@ -169,7 +167,7 @@ public void DateTimeOffset_Insert_Select_Test() public void DateTime_Insert_Select_Test() { InsertSelectTest(new DateTime(2010, 4, 29, 19, 01, 02, 300, DateTimeKind.Utc), "timestamp_sample"); - InsertSelectTest(new DateTime(2005, 8, 5, 21, 01, 02, 300, DateTimeKind.Utc), + InsertSelectTest(new DateTime(2005, 8, 5, 21, 01, 02, 300, DateTimeKind.Utc), "timestamp_sample"); InsertSelectTest(null, "timestamp_sample"); } @@ -311,7 +309,7 @@ public void SimpleStatementNamedValuesNotSpecified() var insertQuery = string.Format("INSERT INTO {0} (float_sample, text_sample, bigint_sample, id) VALUES (:MY_float, :my_TexT, :my_BIGint, :id)", AllTypesTableName); Assert.Throws(() => Session.Execute( - new SimpleStatement(insertQuery, + new SimpleStatement(insertQuery, new {id = Guid.NewGuid(), my_bigint = 1L }))); } @@ -342,7 +340,7 @@ public void SimpleStatementTinyIntTests() var values = new sbyte[] { sbyte.MinValue, -4, -3, 0, 1, 2, 126, sbyte.MaxValue }; foreach (var v in values) { - var insert = new SimpleStatement("INSERT INTO tbl_tinyint_param (id, v, m) VALUES (?, ?, ?)", + var insert = new SimpleStatement("INSERT INTO tbl_tinyint_param (id, v, m) VALUES (?, ?, ?)", Convert.ToInt32(v), v, new SortedDictionary { { v, v.ToString()} }); var select = new SimpleStatement("SELECT * FROM tbl_tinyint_param WHERE id = ?", Convert.ToInt32(v)); Session.Execute(insert); @@ -358,7 +356,7 @@ public void SimpleStatementTinyIntTests() public void SimpleStatementDateTests() { Session.Execute("CREATE TABLE tbl_date_param (id int PRIMARY KEY, v date, m map)"); - var values = new[] { + var values = new[] { new LocalDate(2010, 4, 29), new LocalDate(0, 3, 12), new LocalDate(-10, 2, 4), @@ -413,7 +411,7 @@ public void SimpleStatementTimeTests() /// /// Testing the usage of dictionary for named parameters. - /// + /// /// @since 2.1.0 /// @jira_ticket CSHARP-406 /// @expected_result Replace the named parameters according to keys in dictionary @@ -440,7 +438,7 @@ public void SimpleStatement_Dictionary_Parameters_CaseInsensitivity() /// /// Testing the usage of dictionary for named parameters, in such a case that the dictionary has more than one equal key (with different capital letters). - /// + /// /// @since 2.1.0 /// @jira_ticket CSHARP-406 /// @expected_result The statement will use the first key in dictionary that match unregarding the case sensitivity @@ -469,7 +467,7 @@ public void SimpleStatement_Dictionary_Parameters_CaseInsensitivity_NoOverload() /// /// Testing missing parameter in dictionary for named parameters. - /// + /// /// @throws InvalidQueryException /// /// @since 2.1.0 @@ -492,7 +490,7 @@ public void SimpleStatement_Dictionary_Parameters_CaseInsensitivity_MissingParam /// /// Testing the usage of dictionary for named parameters, in such a case that the dictionary has more keys than named parameters in statement. - /// + /// /// @since 2.1.0 /// @jira_ticket CSHARP-406 /// @expected_result The statement will ignore the excess of parameters @@ -549,7 +547,7 @@ public void SimpleStatement_With_Keyspace_Defined_On_Lower_Protocol_Versions() [TestCassandraVersion(3, 11)] public void SimpleStatement_With_No_Compact_Enabled_Should_Reveal_Non_Schema_Columns() { - if (TestClusterManager.CheckCassandraVersion(true, new Version(4, 0), Comparison.GreaterThanOrEqualsTo) || + if (TestClusterManager.CheckCassandraVersion(true, new Version(4, 0), Comparison.GreaterThanOrEqualsTo) || (TestClusterManager.IsDse && TestClusterManager.CheckDseVersion(new Version(6, 0), Comparison.GreaterThanOrEqualsTo)) || TestClusterManager.IsHcd) { @@ -572,7 +570,7 @@ public void SimpleStatement_With_No_Compact_Enabled_Should_Reveal_Non_Schema_Col [TestCassandraVersion(3, 11)] public void SimpleStatement_With_No_Compact_Disabled_Should_Not_Reveal_Non_Schema_Columns() { - if (TestClusterManager.CheckCassandraVersion(true, new Version(4, 0), Comparison.GreaterThanOrEqualsTo) || + if (TestClusterManager.CheckCassandraVersion(true, new Version(4, 0), Comparison.GreaterThanOrEqualsTo) || (TestClusterManager.IsDse && TestClusterManager.CheckDseVersion(new Version(6, 0), Comparison.GreaterThanOrEqualsTo)) || TestClusterManager.IsHcd) { diff --git a/src/Cassandra.IntegrationTests/Core/PreparedStatementsTests.cs b/src/Cassandra.IntegrationTests/Core/PreparedStatementsTests.cs index ae4762c7f..5d03c2e5d 100644 --- a/src/Cassandra.IntegrationTests/Core/PreparedStatementsTests.cs +++ b/src/Cassandra.IntegrationTests/Core/PreparedStatementsTests.cs @@ -34,6 +34,35 @@ public class PreparedStatementsTests : SharedClusterTest { private readonly string _tableName = "tbl" + Guid.NewGuid().ToString("N").ToLower(); private const string AllTypesTableName = "all_types_table_prepared"; + private readonly List _privateClusterInstances = new List(); + + protected override ICluster GetNewTemporaryCluster(Action build = null) + { + var builder = ClusterBuilder() + .AddContactPoint(TestCluster.InitialContactPoint) + .WithSocketOptions(new SocketOptions().SetConnectTimeoutMillis(30000).SetReadTimeoutMillis(22000)); + build?.Invoke(builder); + var cluster = builder.Build(); + _privateClusterInstances.Add(cluster); + return cluster; + } + + public override void TearDown() + { + foreach (var c in _privateClusterInstances) + { + try + { + c.Dispose(); + } + catch + { + // ignored + } + } + _privateClusterInstances.Clear(); + base.TearDown(); + } public PreparedStatementsTests() : base(3) { @@ -163,8 +192,8 @@ public void PreparedStatement_With_Changing_Schema() { byte[] originalResultMetadataId = null; // Use 2 different clusters as the prepared statement cache should be different - using (var cluster1 = ClusterBuilder().AddContactPoint(TestClusterManager.InitialContactPoint).Build()) - using (var cluster2 = ClusterBuilder().AddContactPoint(TestClusterManager.InitialContactPoint).Build()) + using (var cluster1 = GetNewTemporaryCluster()) + using (var cluster2 = GetNewTemporaryCluster()) { var session1 = cluster1.Connect(); var session2 = cluster2.Connect(); @@ -906,13 +935,12 @@ public void Session_Prepare_With_Keyspace_Defined_On_Previuos_Cassandra_Versions private void TestKeyspaceInPrepareNotSupported(bool specifyProtocol) { - var builder = ClusterBuilder().AddContactPoint(TestClusterManager.InitialContactPoint); - if (specifyProtocol) - { - builder.WithMaxProtocolVersion(ProtocolVersion.V4); - } - - using (var cluster = builder.Build()) + using (var cluster = GetNewTemporaryCluster(builder => { + if (specifyProtocol) + { + builder.WithMaxProtocolVersion(ProtocolVersion.V4); + } + })) { var session = cluster.Connect(KeyspaceName); @@ -1114,7 +1142,7 @@ public void BatchStatement_With_Keyspace_Defined_On_Protocol_Greater_Than_4() [TestBothServersVersion(4, 0, 5,1, Comparison.LessThan)] public void BatchStatement_With_Keyspace_Defined_On_Lower_Protocol_Versions() { - using (var cluster = ClusterBuilder().AddContactPoint(TestClusterManager.InitialContactPoint).Build()) + using (var cluster = GetNewTemporaryCluster()) { var session = cluster.Connect("system"); var query = new SimpleStatement( @@ -1150,10 +1178,8 @@ public void Should_FailFast_When_PreparedStatementIdChangesOnReprepare() } var tableName = TestUtils.GetUniqueTableName(); - using (var cluster = - ClusterBuilder() - .AddContactPoint(TestClusterManager.InitialContactPoint) - .WithQueryTimeout(500000).Build()) + using (var cluster = + GetNewTemporaryCluster(builder => builder.WithQueryTimeout(500000))) { var session = cluster.Connect(); session.Execute($"CREATE TABLE {KeyspaceName}.{tableName} (a int PRIMARY KEY, b int, c int)"); diff --git a/src/Cassandra.IntegrationTests/Core/SchemaAgreementTests.cs b/src/Cassandra.IntegrationTests/Core/SchemaAgreementTests.cs index 6e4e736ff..ec89a1133 100644 --- a/src/Cassandra.IntegrationTests/Core/SchemaAgreementTests.cs +++ b/src/Cassandra.IntegrationTests/Core/SchemaAgreementTests.cs @@ -29,7 +29,7 @@ public class SchemaAgreementTests : SharedClusterTest { private volatile bool _paused = false; - public SchemaAgreementTests() : base(2, false) + public SchemaAgreementTests() : base(3, false) { } diff --git a/src/Cassandra.IntegrationTests/Core/UdfTests.cs b/src/Cassandra.IntegrationTests/Core/UdfTests.cs index 3dc772933..867a812e7 100644 --- a/src/Cassandra.IntegrationTests/Core/UdfTests.cs +++ b/src/Cassandra.IntegrationTests/Core/UdfTests.cs @@ -50,24 +50,27 @@ public void TestFixtureSetup() { return; } - - if (TestClusterManager.IsHcd) + _testCluster = TestClusterManager.GetTestCluster(1, 0, false, DefaultMaxClusterCreateRetries, false, false); + var userDefinedFunctionsConfig = "enable_user_defined_functions: true"; + if (TestClusterManager.CheckCassandraVersion(true, Version.Parse("5.0"), Comparison.GreaterThanOrEqualsTo)) { - Assert.Ignore("Skipping UDF tests on HCD due to DSP-24606. See CSHARP-1020."); - return; + userDefinedFunctionsConfig = "user_defined_functions_enabled: true"; } - _testCluster = TestClusterManager.GetTestCluster(1, 0, false, DefaultMaxClusterCreateRetries, false, false); - _testCluster.UpdateConfig("enable_user_defined_functions:true"); + var experimentalFeaturesConfig = TestClusterManager.IsScylla + ? "experimental_features:[udf]" + : null; + _testCluster.UpdateConfig(userDefinedFunctionsConfig, experimentalFeaturesConfig); _testCluster.Start(1); using (var cluster = ClusterBuilder().AddContactPoint(_testCluster.InitialContactPoint).Build()) { + var udfLanguage = TestClusterManager.IsScylla ? "lua" : "java"; var session = cluster.Connect(); var queries = new List { "CREATE KEYSPACE ks_udf WITH replication = {'class': 'SimpleStrategy', 'replication_factor' : 1}", - "CREATE FUNCTION ks_udf.return_one() RETURNS NULL ON NULL INPUT RETURNS int LANGUAGE java AS 'return 1;'", - "CREATE FUNCTION ks_udf.plus(s int, v int) RETURNS NULL ON NULL INPUT RETURNS int LANGUAGE java AS 'return s+v;'", - "CREATE FUNCTION ks_udf.plus(s bigint, v bigint) RETURNS NULL ON NULL INPUT RETURNS bigint LANGUAGE java AS 'return s+v;'", + $"CREATE FUNCTION ks_udf.return_one() RETURNS NULL ON NULL INPUT RETURNS int LANGUAGE {udfLanguage} AS 'return 1;'", + $"CREATE FUNCTION ks_udf.plus(s int, v int) RETURNS NULL ON NULL INPUT RETURNS int LANGUAGE {udfLanguage} AS 'return s+v;'", + $"CREATE FUNCTION ks_udf.plus(s bigint, v bigint) RETURNS NULL ON NULL INPUT RETURNS bigint LANGUAGE {udfLanguage} AS 'return s+v;'", "CREATE AGGREGATE ks_udf.sum(int) SFUNC plus STYPE int INITCOND 1", "CREATE AGGREGATE ks_udf.sum(bigint) SFUNC plus STYPE bigint INITCOND 2" }; @@ -75,16 +78,16 @@ public void TestFixtureSetup() if (TestClusterManager.CheckDseVersion(new Version(6, 0), Comparison.GreaterThanOrEqualsTo)) { queries.Add("CREATE FUNCTION ks_udf.deterministic(dividend int, divisor int) " + - "CALLED ON NULL INPUT RETURNS int DETERMINISTIC LANGUAGE java AS " + + $"CALLED ON NULL INPUT RETURNS int DETERMINISTIC LANGUAGE {udfLanguage} AS " + "'return dividend / divisor;'"); queries.Add("CREATE FUNCTION ks_udf.monotonic(dividend int, divisor int) " + - "CALLED ON NULL INPUT RETURNS int MONOTONIC LANGUAGE java AS " + + $"CALLED ON NULL INPUT RETURNS int MONOTONIC LANGUAGE {udfLanguage} AS " + "'return dividend / divisor;'"); queries.Add("CREATE FUNCTION ks_udf.md(dividend int, divisor int) " + - "CALLED ON NULL INPUT RETURNS int DETERMINISTIC MONOTONIC LANGUAGE java AS " + + $"CALLED ON NULL INPUT RETURNS int DETERMINISTIC MONOTONIC LANGUAGE {udfLanguage} AS " + "'return dividend / divisor;'"); queries.Add("CREATE FUNCTION ks_udf.monotonic_on(dividend int, divisor int) " + - "CALLED ON NULL INPUT RETURNS int MONOTONIC ON dividend LANGUAGE java AS " + + $"CALLED ON NULL INPUT RETURNS int MONOTONIC ON dividend LANGUAGE {udfLanguage} AS " + "'return dividend / divisor;'"); queries.Add("CREATE AGGREGATE ks_udf.deta(int) SFUNC plus STYPE int INITCOND 0 DETERMINISTIC;"); } @@ -137,7 +140,7 @@ public void GetFunction_Should_Retrieve_Metadata_Of_Cql_Function(bool metadataSy Assert.AreEqual(ColumnTypeCode.Int, func.ArgumentTypes[0].TypeCode); Assert.AreEqual(ColumnTypeCode.Int, func.ArgumentTypes[1].TypeCode); Assert.AreEqual("return s+v;", func.Body); - Assert.AreEqual("java", func.Language); + Assert.AreEqual(TestClusterManager.IsScylla ? "lua" : "java", func.Language); Assert.AreEqual(ColumnTypeCode.Int, func.ReturnType.TypeCode); Assert.AreEqual(false, func.CalledOnNullInput); } @@ -155,7 +158,7 @@ public void GetFunction_Should_Retrieve_Metadata_Of_Cql_Function_Without_Paramet Assert.AreEqual(0, func.ArgumentTypes.Length); Assert.AreEqual(0, func.Signature.Length); Assert.AreEqual("return 1;", func.Body); - Assert.AreEqual("java", func.Language); + Assert.AreEqual(TestClusterManager.IsScylla ? "lua" : "java", func.Language); Assert.AreEqual(ColumnTypeCode.Int, func.ReturnType.TypeCode); Assert.AreEqual(false, func.CalledOnNullInput); Assert.False(func.Monotonic); @@ -207,10 +210,11 @@ public void GetFunction_Should_Return_Most_Up_To_Date_Metadata_Via_Events(bool m var session = cluster.Connect("ks_udf"); var cluster2 = GetCluster(metadataSync); var session2 = cluster.Connect("ks_udf"); - session.Execute("CREATE OR REPLACE FUNCTION stringify(i int) RETURNS NULL ON NULL INPUT RETURNS text LANGUAGE java AS 'return Integer.toString(i);'"); + var udfLanguage = TestClusterManager.IsScylla ? "lua" : "java"; + session.Execute($"CREATE OR REPLACE FUNCTION stringify(i int) RETURNS NULL ON NULL INPUT RETURNS text LANGUAGE {udfLanguage} AS 'return Integer.toString(i);'"); cluster2.RefreshSchema("ks_udf"); Task.Delay(500).GetAwaiter().GetResult(); // wait for events to be processed - var _ = cluster2.Metadata.KeyspacesSnapshot // cache + var _ = cluster2.Metadata.KeyspacesSnapshot // cache .Single(kvp => kvp.Key == "ks_udf") .Value .GetFunction("stringify", new[] { "int" }); @@ -219,7 +223,7 @@ public void GetFunction_Should_Return_Most_Up_To_Date_Metadata_Via_Events(bool m var func = cluster.Metadata.GetFunction("ks_udf", "stringify", new[] { "int" }); Assert.NotNull(func); Assert.AreEqual("return Integer.toString(i);", func.Body); - session.Execute("CREATE OR REPLACE FUNCTION stringify(i int) RETURNS NULL ON NULL INPUT RETURNS text LANGUAGE java AS 'return Integer.toString(i) + \"hello\";'"); + session.Execute($"CREATE OR REPLACE FUNCTION stringify(i int) RETURNS NULL ON NULL INPUT RETURNS text LANGUAGE {udfLanguage} AS 'return Integer.toString(i) + \"hello\";'"); if (metadataSync) { TestHelper.RetryAssert(() => diff --git a/src/Cassandra.IntegrationTests/Mapping/Tests/InsertTests.cs b/src/Cassandra.IntegrationTests/Mapping/Tests/InsertTests.cs index 7cecd2750..c69279284 100644 --- a/src/Cassandra.IntegrationTests/Mapping/Tests/InsertTests.cs +++ b/src/Cassandra.IntegrationTests/Mapping/Tests/InsertTests.cs @@ -351,11 +351,13 @@ public void Insert_TableNameLowerCase_PartitionKeyCamelCase() // Attempt to select from Camel Case partition key string cqlCamelCasePartitionKey = "SELECT * from " + typeof (lowercaseclassnamepkcamelcase).Name + " where \"SomePartitionKey\" = 'doesntmatter'"; var ex = Assert.Throws(() => _session.Execute(cqlCamelCasePartitionKey)); - var expectedErrMsg = "Undefined name SomePartitionKey in where clause"; + var expectedMessageCassandra = "Undefined name SomePartitionKey in where clause"; + var expectedMessageScylla = "Unrecognized name SomePartitionKey"; if (TestClusterManager.CheckCassandraVersion(false, Version.Parse("3.10"), Comparison.GreaterThanOrEqualsTo)) { - expectedErrMsg = "Undefined column name \"SomePartitionKey\""; + expectedMessageCassandra = "Undefined column name \"SomePartitionKey\""; } + var expectedErrMsg = TestClusterManager.IsScylla ? expectedMessageScylla : expectedMessageCassandra; StringAssert.Contains(expectedErrMsg, ex.Message); // Validate that select on lower case key does not fail @@ -379,11 +381,13 @@ public void Insert_MislabledClusteringKey() // Validate expected exception var ex = Assert.Throws(() => cqlClient.Insert(pocoWithCustomAttributes)); - var expectedMessage = "Unknown identifier someotherstring"; + var expectedMessageCassandra = "Unknown identifier someotherstring"; + var expectedMessageScylla = "Unknown identifier someotherstring"; if (TestClusterManager.CheckCassandraVersion(false, Version.Parse("3.10"), Comparison.GreaterThanOrEqualsTo)) { - expectedMessage = "Undefined column name someotherstring"; + expectedMessageCassandra = "Undefined column name someotherstring"; } + var expectedMessage = TestClusterManager.IsScylla ? expectedMessageScylla : expectedMessageCassandra; StringAssert.Contains(expectedMessage, ex.Message); } diff --git a/src/Cassandra.IntegrationTests/TestBase/TestCassandraVersion.cs b/src/Cassandra.IntegrationTests/TestBase/TestCassandraVersion.cs index 46db8b158..71db1d443 100644 --- a/src/Cassandra.IntegrationTests/TestBase/TestCassandraVersion.cs +++ b/src/Cassandra.IntegrationTests/TestBase/TestCassandraVersion.cs @@ -35,7 +35,7 @@ public class TestCassandraVersion : NUnitAttribute, IApplyToTest public Comparison Comparison { get; set; } private bool IsOssRequired { get; set; } - + /// /// Creates an instance of an attribute that filters the test to execute according to the current Cassandra version /// being used. @@ -73,7 +73,7 @@ public TestCassandraVersion(int major, int minor, int build, Comparison comparis Comparison = comparison; IsOssRequired = isOssRequired; } - + protected virtual bool IsDseRequired() { return false; @@ -88,7 +88,7 @@ public void ApplyToTest(NUnit.Framework.Internal.Test test) test.Properties.Set("_SKIPREASON", message); } } - + public bool Applies(out string msg) { var expectedVersion = new Version(Major, Minor, Build); @@ -97,11 +97,23 @@ public bool Applies(out string msg) public static bool VersionMatch(Version expectedVersion, bool requiresDse, bool requiresOss, Comparison comparison, out string message) { + if (TestClusterManager.IsScylla && requiresDse) + { + message = "Test designed to run with DSE (executing Scylla)"; + return false; + } + + if (TestClusterManager.IsScylla && requiresOss) + { + message = "Test designed to run with OSS Cassandra (executing Scylla)"; + return true; + } + if (TestClusterManager.IsDse && requiresOss) { - message = string.Format("Test designed to run with OSS {0} v{1} (executing DSE {2})", - TestCassandraVersion.GetComparisonText(comparison), - expectedVersion, + message = string.Format("Test designed to run with OSS {0} v{1} (executing DSE {2})", + TestCassandraVersion.GetComparisonText(comparison), + expectedVersion, TestClusterManager.DseVersion); return false; } @@ -144,7 +156,7 @@ public static bool VersionMatch(Version expectedVersion, Version executingVersio executingVersion = AdaptVersion(executingVersion); var comparisonResult = (Comparison)executingVersion.CompareTo(expectedVersion); - + if (comparisonResult >= Comparison.Equal && comparison == Comparison.GreaterThanOrEqualsTo) { return true; @@ -220,24 +232,24 @@ public TestDseVersion(int major, int minor, int build, Comparison comparison = C public class TestBothServersVersion : TestDseVersion { public TestBothServersVersion( - int cassandraVersionMajor, - int cassandraVersionMinor, - int dseVersionMajor, - int dseVersionMinor, - Comparison comparison = Comparison.GreaterThanOrEqualsTo) + int cassandraVersionMajor, + int cassandraVersionMinor, + int dseVersionMajor, + int dseVersionMinor, + Comparison comparison = Comparison.GreaterThanOrEqualsTo) : this( - TestClusterManager.IsDse - ? new Version(dseVersionMajor, dseVersionMinor) - : new Version(cassandraVersionMajor, cassandraVersionMinor), + TestClusterManager.IsDse + ? new Version(dseVersionMajor, dseVersionMinor) + : new Version(cassandraVersionMajor, cassandraVersionMinor), comparison) { } - private TestBothServersVersion(Version version, Comparison comparison) + private TestBothServersVersion(Version version, Comparison comparison) : base(version.Major, version.Minor, comparison) { } - + protected override bool IsDseRequired() { return TestClusterManager.IsDse; diff --git a/src/Cassandra.IntegrationTests/TestClusterManagement/CcmBridge.cs b/src/Cassandra.IntegrationTests/TestClusterManagement/CcmBridge.cs index 446659267..b8475c126 100644 --- a/src/Cassandra.IntegrationTests/TestClusterManagement/CcmBridge.cs +++ b/src/Cassandra.IntegrationTests/TestClusterManagement/CcmBridge.cs @@ -31,18 +31,21 @@ public class CcmBridge : IDisposable public DirectoryInfo CcmDir { get; private set; } public string Name { get; private set; } public string Version { get; private set; } - public string IpPrefix { get; private set; } + public string ScyllaVersion { get; private set; } + public string IdPrefix { get; private set; } + public string IpPrefix => $"127.0.{IdPrefix}."; public ICcmProcessExecuter CcmProcessExecuter { get; set; } private readonly string _dseInstallPath; - public CcmBridge(string name, string ipPrefix, string dsePath, string version, ICcmProcessExecuter executor) + public CcmBridge(string name, string idPrefix, string dsePath, string version, string scyllaVersion, ICcmProcessExecuter executor) { Name = name; - IpPrefix = ipPrefix; + IdPrefix = idPrefix; CcmDir = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())); CcmProcessExecuter = executor; _dseInstallPath = dsePath; Version = version; + ScyllaVersion = scyllaVersion; } public void Dispose() @@ -66,7 +69,11 @@ public void Create(bool useSsl) sslParams = "--ssl " + sslPath; } - if (string.IsNullOrEmpty(_dseInstallPath)) + if (!string.IsNullOrEmpty(ScyllaVersion)) + { + ExecuteCcm($"create {Name} --scylla -v release:{ScyllaVersion} {sslParams}"); + } + else if (string.IsNullOrEmpty(_dseInstallPath)) { if (TestClusterManager.IsDse) { @@ -153,7 +160,7 @@ public ProcessOutput Start(int n, string additionalArgs = null, string[] jvmArgs { runAsRoot = "--root"; } - + var jvmArgsParameters = new List { "start", @@ -273,10 +280,15 @@ public ProcessOutput BootstrapNode(int n, string dc, bool start = true) if (TestClusterManager.IsDse) { cmd += " --dse"; - }else if (TestClusterManager.CurrentBackendType == TestClusterManager.BackendType.Hcd) + } + else if (TestClusterManager.CurrentBackendType == TestClusterManager.BackendType.Hcd) { cmd += " --hcd"; } + else if (TestClusterManager.IsScylla) + { + cmd += " --scylla"; + } var output = ExecuteCcm(string.Format(cmd, n, IpPrefix, n, 7000 + 100 * n, dc != null ? "-d " + dc : null)); @@ -332,7 +344,7 @@ public void UpdateConfig(int nodeId, params string[] yamlChanges) var joinedChanges = string.Join(" ", yamlChanges.Select(s => $"\"{s}\"")); ExecuteCcm($"node{nodeId} updateconf {joinedChanges}"); } - + private static void FixYaml(string[] yamlToFix) { // in-place fix @@ -367,8 +379,8 @@ private static void FixYaml(string[] yamlToFix) } } } - - + + public void SetNodeWorkloads(int nodeId, string[] workloads) { if (!TestClusterManager.IsDse) diff --git a/src/Cassandra.IntegrationTests/TestClusterManagement/CcmCluster.cs b/src/Cassandra.IntegrationTests/TestClusterManagement/CcmCluster.cs index b15e095c5..73baf9749 100644 --- a/src/Cassandra.IntegrationTests/TestClusterManagement/CcmCluster.cs +++ b/src/Cassandra.IntegrationTests/TestClusterManagement/CcmCluster.cs @@ -25,33 +25,37 @@ public class CcmCluster : ITestCluster { public string Name { get; set; } public string Version { get; set; } + public string ScyllaVersion { get; set; } public Builder Builder { get; set; } public Cluster Cluster { get; set; } public ISession Session { get; set; } public string InitialContactPoint { get; set; } public string ClusterIpPrefix { get; set; } + public string IdPrefix { get; private set; } public string DsePath { get; set; } public string DefaultKeyspace { get; set; } private readonly ICcmProcessExecuter _executor; private CcmBridge _ccm; private int _nodeLength; - public CcmCluster(string name, string clusterIpPrefix, string dsePath, ICcmProcessExecuter executor, string defaultKeyspace, string version) + public CcmCluster(string name, string idPrefix, string dsePath, ICcmProcessExecuter executor, string defaultKeyspace, string version, string scyllaVersion = null) { _executor = executor; Name = name; DefaultKeyspace = defaultKeyspace; - ClusterIpPrefix = clusterIpPrefix; - DsePath = dsePath; + IdPrefix = idPrefix; + ClusterIpPrefix = $"127.0.{IdPrefix}."; InitialContactPoint = ClusterIpPrefix + "1"; + DsePath = dsePath; Version = version; + ScyllaVersion = scyllaVersion; } public void Create(int nodeLength, TestClusterOptions options = null) { _nodeLength = nodeLength; options = options ?? TestClusterOptions.Default; - _ccm = new CcmBridge(Name, ClusterIpPrefix, DsePath, Version, _executor); + _ccm = new CcmBridge(Name, IdPrefix, DsePath, Version, ScyllaVersion, _executor); _ccm.Create(options.UseSsl); _ccm.Populate(nodeLength, options.Dc2NodeLength, options.UseVNodes); _ccm.UpdateConfig(options.CassandraYaml); diff --git a/src/Cassandra.IntegrationTests/TestClusterManagement/TestClusterManager.cs b/src/Cassandra.IntegrationTests/TestClusterManagement/TestClusterManager.cs index 08502d525..e467b0304 100644 --- a/src/Cassandra.IntegrationTests/TestClusterManagement/TestClusterManager.cs +++ b/src/Cassandra.IntegrationTests/TestClusterManagement/TestClusterManager.cs @@ -28,7 +28,12 @@ public static class TestClusterManager { public const string DefaultKeyspaceName = "test_cluster_keyspace"; private static ICcmProcessExecuter _executor; - + private static int _idPrefixCounter = 0; + private static string GetUniqueIdPrefix() + { + return (_idPrefixCounter++).ToString(); + } + private static readonly Version Version2Dot0 = new Version(2, 0); private static readonly Version Version2Dot1 = new Version(2, 1); private static readonly Version Version3Dot0 = new Version(3, 0); @@ -104,7 +109,7 @@ public enum BackendType Dse, Cassandra } - + /// /// "hcd", "dse", or "cassandra" (default), based on CCM_DISTRIBUTION /// if there's env var DSE_VERSION, ignore CCM_DISTRIBUTION @@ -168,6 +173,16 @@ public static string CassandraVersionString } } + public static string ScyllaVersionString + { + get { return Environment.GetEnvironmentVariable("SCYLLA_VERSION"); } + } + + public static bool IsScylla + { + get { return !string.IsNullOrEmpty(ScyllaVersionString); } + } + public static bool IsDse { get { return CurrentBackendType == BackendType.Dse; } @@ -177,7 +192,7 @@ public static bool IsHcd { get { return CurrentBackendType == BackendType.Hcd; } } - + public static Version DseVersion { get @@ -201,7 +216,7 @@ public static bool ShouldEnableBetaProtocolVersion() public static bool SupportsDecommissionForcefully() { - return TestClusterManager.CheckDseVersion(new Version(5, 1), Comparison.GreaterThanOrEqualsTo) + return TestClusterManager.CheckDseVersion(new Version(5, 1), Comparison.GreaterThanOrEqualsTo) || TestClusterManager.CheckCassandraVersion(true, new Version(4, 0), Comparison.GreaterThanOrEqualsTo); } @@ -256,9 +271,9 @@ public static ICcmProcessExecuter Executor var remoteDseServerPassword = Environment.GetEnvironmentVariable("DSE_SERVER_PWD") ?? "vagrant"; var remoteDseServerPort = int.Parse(Environment.GetEnvironmentVariable("DSE_SERVER_PORT") ?? "2222"); var remoteDseServerUserPrivateKey = Environment.GetEnvironmentVariable("DSE_SERVER_PRIVATE_KEY"); - TestClusterManager._executor = + TestClusterManager._executor = new RemoteCcmProcessExecuter( - remoteDseServer, remoteDseServerUser, remoteDseServerPassword, + remoteDseServer, remoteDseServerUser, remoteDseServerPassword, remoteDseServerPort, remoteDseServerUserPrivateKey); } else if (TestClusterManager.CcmUseWsl) @@ -273,18 +288,19 @@ public static ICcmProcessExecuter Executor return TestClusterManager._executor; } } - + private static ITestCluster CreateNewNoRetry(int nodeLength, TestClusterOptions options, bool startCluster) { TryRemove(); options = options ?? new TestClusterOptions(); var testCluster = new CcmCluster( TestUtils.GetTestClusterNameBasedOnRandomString(), - IpPrefix, + GetUniqueIdPrefix(), DsePath, Executor, DefaultKeyspaceName, - IsDse ? DseVersionString : CassandraVersionString); + IsDse ? DseVersionString : CassandraVersionString, + ScyllaVersionString); testCluster.Create(nodeLength, options); if (startCluster) { @@ -391,7 +407,7 @@ public static void TryRemove() { if (Diagnostics.CassandraTraceSwitch.Level == TraceLevel.Verbose) { - Trace.TraceError("ccm test cluster could not be removed: {0}", ex); + Trace.TraceError("ccm test cluster could not be removed: {0}", ex); } } }