Skip to content

Commit 136a966

Browse files
feat: Add Apache Cassandra module (#1367)
Co-authored-by: Andre Hofmeister <[email protected]>
1 parent 4c24aab commit 136a966

File tree

14 files changed

+306
-0
lines changed

14 files changed

+306
-0
lines changed

.github/workflows/cicd.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ jobs:
4242
{ name: "Testcontainers.Azurite", runs-on: "ubuntu-22.04" },
4343
{ name: "Testcontainers.BigQuery", runs-on: "ubuntu-22.04" },
4444
{ name: "Testcontainers.Bigtable", runs-on: "ubuntu-22.04" },
45+
{ name: "Testcontainers.Cassandra", runs-on: "ubuntu-22.04" },
4546
{ name: "Testcontainers.ClickHouse", runs-on: "ubuntu-22.04" },
4647
{ name: "Testcontainers.CockroachDb", runs-on: "ubuntu-22.04" },
4748
{ name: "Testcontainers.Consul", runs-on: "ubuntu-22.04" },

Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
<PackageVersion Include="Azure.Messaging.ServiceBus" Version="7.18.2"/>
3737
<PackageVersion Include="Azure.Storage.Blobs" Version="12.17.0"/>
3838
<PackageVersion Include="Azure.Storage.Queues" Version="12.15.0"/>
39+
<PackageVersion Include="CassandraCSharpDriver" Version="3.22.0"/>
3940
<PackageVersion Include="ClickHouse.Client" Version="7.9.1"/>
4041
<PackageVersion Include="Confluent.Kafka" Version="2.8.0"/>
4142
<PackageVersion Include="Confluent.SchemaRegistry.Serdes.Json" Version="2.8.0"/>

Testcontainers.sln

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.BigQuery", "
2323
EndProject
2424
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Bigtable", "src\Testcontainers.Bigtable\Testcontainers.Bigtable.csproj", "{302EC1E0-AE75-4E99-A6BF-524F35338BC8}"
2525
EndProject
26+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Cassandra", "src\Testcontainers.Cassandra\Testcontainers.Cassandra.csproj", "{8495D757-5FD7-491C-B941-9D43B3DCF3C0}"
27+
EndProject
2628
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.ClickHouse", "src\Testcontainers.ClickHouse\Testcontainers.ClickHouse.csproj", "{B061A78E-536E-4CA1-8401-234D5FBFBAB7}"
2729
EndProject
2830
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.CockroachDb", "src\Testcontainers.CockroachDb\Testcontainers.CockroachDb.csproj", "{8D9871C6-5A39-4F0B-A15A-E87D34F3EA73}"
@@ -123,6 +125,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.BigQuery.Tes
123125
EndProject
124126
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Bigtable.Tests", "tests\Testcontainers.Bigtable.Tests\Testcontainers.Bigtable.Tests.csproj", "{2E7B92E3-8526-4706-90F3-00F0F5C47C37}"
125127
EndProject
128+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Cassandra.Tests", "tests\Testcontainers.Cassandra.Tests\Testcontainers.Cassandra.Tests.csproj", "{C6A2B99E-BFD5-4510-83D7-A8844142F27D}"
129+
EndProject
126130
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.ClickHouse.Tests", "tests\Testcontainers.ClickHouse.Tests\Testcontainers.ClickHouse.Tests.csproj", "{9D0A0B32-4921-400C-99CB-8650677E3E44}"
127131
EndProject
128132
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.CockroachDb.Tests", "tests\Testcontainers.CockroachDb.Tests\Testcontainers.CockroachDb.Tests.csproj", "{685E6D9A-B05E-41D9-A08E-5F3CA7733F7D}"
@@ -258,6 +262,10 @@ Global
258262
{302EC1E0-AE75-4E99-A6BF-524F35338BC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
259263
{302EC1E0-AE75-4E99-A6BF-524F35338BC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
260264
{302EC1E0-AE75-4E99-A6BF-524F35338BC8}.Release|Any CPU.Build.0 = Release|Any CPU
265+
{8495D757-5FD7-491C-B941-9D43B3DCF3C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
266+
{8495D757-5FD7-491C-B941-9D43B3DCF3C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
267+
{8495D757-5FD7-491C-B941-9D43B3DCF3C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
268+
{8495D757-5FD7-491C-B941-9D43B3DCF3C0}.Release|Any CPU.Build.0 = Release|Any CPU
261269
{B061A78E-536E-4CA1-8401-234D5FBFBAB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
262270
{B061A78E-536E-4CA1-8401-234D5FBFBAB7}.Debug|Any CPU.Build.0 = Debug|Any CPU
263271
{B061A78E-536E-4CA1-8401-234D5FBFBAB7}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -458,6 +466,10 @@ Global
458466
{2E7B92E3-8526-4706-90F3-00F0F5C47C37}.Debug|Any CPU.Build.0 = Debug|Any CPU
459467
{2E7B92E3-8526-4706-90F3-00F0F5C47C37}.Release|Any CPU.ActiveCfg = Release|Any CPU
460468
{2E7B92E3-8526-4706-90F3-00F0F5C47C37}.Release|Any CPU.Build.0 = Release|Any CPU
469+
{C6A2B99E-BFD5-4510-83D7-A8844142F27D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
470+
{C6A2B99E-BFD5-4510-83D7-A8844142F27D}.Debug|Any CPU.Build.0 = Debug|Any CPU
471+
{C6A2B99E-BFD5-4510-83D7-A8844142F27D}.Release|Any CPU.ActiveCfg = Release|Any CPU
472+
{C6A2B99E-BFD5-4510-83D7-A8844142F27D}.Release|Any CPU.Build.0 = Release|Any CPU
461473
{9D0A0B32-4921-400C-99CB-8650677E3E44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
462474
{9D0A0B32-4921-400C-99CB-8650677E3E44}.Debug|Any CPU.Build.0 = Debug|Any CPU
463475
{9D0A0B32-4921-400C-99CB-8650677E3E44}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -677,6 +689,7 @@ Global
677689
{3F2E254F-C203-43FD-A078-DC3E2CBC0F9F} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
678690
{A9FF9C7F-BBA0-4B44-90B7-48A60F9E00F3} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
679691
{302EC1E0-AE75-4E99-A6BF-524F35338BC8} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
692+
{8495D757-5FD7-491C-B941-9D43B3DCF3C0} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
680693
{B061A78E-536E-4CA1-8401-234D5FBFBAB7} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
681694
{8D9871C6-5A39-4F0B-A15A-E87D34F3EA73} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
682695
{51ED33B9-B688-401E-85F2-329D3C935BD1} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
@@ -727,6 +740,7 @@ Global
727740
{B272FDDE-5E01-425D-B9E1-10FF883DDAAA} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
728741
{03E60673-078A-4508-99AD-8537CE6F78F1} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
729742
{2E7B92E3-8526-4706-90F3-00F0F5C47C37} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
743+
{C6A2B99E-BFD5-4510-83D7-A8844142F27D} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
730744
{9D0A0B32-4921-400C-99CB-8650677E3E44} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
731745
{685E6D9A-B05E-41D9-A08E-5F3CA7733F7D} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
732746
{2478673C-B063-469D-ABD1-0C3E0A25541B} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}

docs/modules/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ await moduleNameContainer.StartAsync();
2929
| Azurite | `mcr.microsoft.com/azure-storage/azurite:3.24.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.Azurite) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Azurite) |
3030
| BigQuery | `ghcr.io/goccy/bigquery-emulator:0.4` | [NuGet](https://www.nuget.org/packages/Testcontainers.BigQuery) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.BigQuery) |
3131
| Bigtable | `gcr.io/google.com/cloudsdktool/google-cloud-cli:446.0.1-emulators` | [NuGet](https://www.nuget.org/packages/Testcontainers.Bigtable) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Bigtable) |
32+
| Cassandra | `library/cassandra:5.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.Cassandra) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Cassandra) |
3233
| ClickHouse | `clickhouse/clickhouse-server:23.6-alpine` | [NuGet](https://www.nuget.org/packages/Testcontainers.ClickHouse) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.ClickHouse) |
3334
| CockroachDB | `cockroachdb:23.1.13` | [NuGet](https://www.nuget.org/packages/Testcontainers.CockroachDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.CockroachDb) |
3435
| Consul | `consul:1.15` | [NuGet](https://www.nuget.org/packages/Testcontainers.Consul) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Consul) |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
root = true
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
namespace Testcontainers.Cassandra;
2+
3+
/// <inheritdoc cref="ContainerBuilder{TBuilderEntity, TContainerEntity, TConfigurationEntity}" />
4+
[PublicAPI]
5+
public sealed class CassandraBuilder : ContainerBuilder<CassandraBuilder, CassandraContainer, CassandraConfiguration>
6+
{
7+
public const string CassandraImage = "cassandra:5.0";
8+
9+
public const ushort CqlPort = 9042;
10+
11+
public const string DefaultDatacenterName = "dc1";
12+
13+
/// <summary>
14+
/// Initializes a new instance of the <see cref="CassandraBuilder" /> class.
15+
/// </summary>
16+
public CassandraBuilder()
17+
: this(new CassandraConfiguration())
18+
{
19+
DockerResourceConfiguration = Init().DockerResourceConfiguration;
20+
}
21+
22+
/// <summary>
23+
/// Initializes a new instance of the <see cref="CassandraBuilder" /> class.
24+
/// </summary>
25+
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
26+
private CassandraBuilder(CassandraConfiguration resourceConfiguration)
27+
: base(resourceConfiguration)
28+
{
29+
DockerResourceConfiguration = resourceConfiguration;
30+
}
31+
32+
/// <inheritdoc />
33+
protected override CassandraConfiguration DockerResourceConfiguration { get; }
34+
35+
/// <inheritdoc />
36+
public override CassandraContainer Build()
37+
{
38+
Validate();
39+
return new CassandraContainer(DockerResourceConfiguration);
40+
}
41+
42+
/// <inheritdoc />
43+
protected override CassandraBuilder Init()
44+
{
45+
return base.Init()
46+
.WithImage(CassandraImage)
47+
.WithPortBinding(CqlPort, true)
48+
.WithEnvironment("JVM_OPTS", "-Dcassandra.skip_wait_for_gossip_to_settle=0 -Dcassandra.initial_token=0")
49+
.WithEnvironment("HEAP_NEWSIZE", "128M")
50+
.WithEnvironment("MAX_HEAP_SIZE", "1024M")
51+
.WithEnvironment("CASSANDRA_SNITCH", "GossipingPropertyFileSnitch")
52+
.WithEnvironment("CASSANDRA_ENDPOINT_SNITCH", "GossipingPropertyFileSnitch")
53+
.WithEnvironment("CASSANDRA_DC", DefaultDatacenterName)
54+
.WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged("Startup complete"));
55+
}
56+
57+
/// <inheritdoc />
58+
protected override CassandraBuilder Clone(IResourceConfiguration<CreateContainerParameters> resourceConfiguration)
59+
{
60+
return Merge(DockerResourceConfiguration, new CassandraConfiguration(resourceConfiguration));
61+
}
62+
63+
/// <inheritdoc />
64+
protected override CassandraBuilder Clone(IContainerConfiguration resourceConfiguration)
65+
{
66+
return Merge(DockerResourceConfiguration, new CassandraConfiguration(resourceConfiguration));
67+
}
68+
69+
/// <inheritdoc />
70+
protected override CassandraBuilder Merge(CassandraConfiguration oldValue, CassandraConfiguration newValue)
71+
{
72+
return new CassandraBuilder(new CassandraConfiguration(oldValue, newValue));
73+
}
74+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
namespace Testcontainers.Cassandra;
2+
3+
/// <inheritdoc cref="ContainerConfiguration" />
4+
[PublicAPI]
5+
public sealed class CassandraConfiguration : ContainerConfiguration
6+
{
7+
/// <summary>
8+
/// Initializes a new instance of the <see cref="CassandraConfiguration" /> class.
9+
/// </summary>
10+
public CassandraConfiguration()
11+
{
12+
}
13+
14+
/// <summary>
15+
/// Initializes a new instance of the <see cref="CassandraConfiguration" /> class.
16+
/// </summary>
17+
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
18+
public CassandraConfiguration(IResourceConfiguration<CreateContainerParameters> resourceConfiguration)
19+
: base(resourceConfiguration)
20+
{
21+
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
22+
}
23+
24+
/// <summary>
25+
/// Initializes a new instance of the <see cref="CassandraConfiguration" /> class.
26+
/// </summary>
27+
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
28+
public CassandraConfiguration(IContainerConfiguration resourceConfiguration)
29+
: base(resourceConfiguration)
30+
{
31+
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
32+
}
33+
34+
/// <summary>
35+
/// Initializes a new instance of the <see cref="CassandraConfiguration" /> class.
36+
/// </summary>
37+
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
38+
public CassandraConfiguration(CassandraConfiguration resourceConfiguration)
39+
: this(new CassandraConfiguration(), resourceConfiguration)
40+
{
41+
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
42+
}
43+
44+
/// <summary>
45+
/// Initializes a new instance of the <see cref="CassandraConfiguration" /> class.
46+
/// </summary>
47+
/// <param name="oldValue">The old Docker resource configuration.</param>
48+
/// <param name="newValue">The new Docker resource configuration.</param>
49+
public CassandraConfiguration(CassandraConfiguration oldValue, CassandraConfiguration newValue)
50+
: base(oldValue, newValue)
51+
{
52+
}
53+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
namespace Testcontainers.Cassandra;
2+
3+
/// <inheritdoc cref="DockerContainer" />
4+
[PublicAPI]
5+
public sealed class CassandraContainer : DockerContainer, IDatabaseContainer
6+
{
7+
/// <inheritdoc cref="DockerContainer" />
8+
public CassandraContainer(CassandraConfiguration configuration)
9+
: base(configuration)
10+
{
11+
}
12+
13+
/// <summary>
14+
/// Gets the Cassandra connection string.
15+
/// </summary>
16+
/// <returns>The Cassandra connection string.</returns>
17+
public string GetConnectionString()
18+
{
19+
var properties = new Dictionary<string, string>();
20+
properties.Add("Contact Points", Hostname);
21+
properties.Add("Port", GetMappedPublicPort(CassandraBuilder.CqlPort).ToString());
22+
return string.Join(";", properties.Select(property => string.Join("=", property.Key, property.Value)));
23+
}
24+
25+
/// <summary>
26+
/// Executes the CQL script in the Cassandra container.
27+
/// </summary>
28+
/// <param name="scriptContent">The content of the CQL script to execute.</param>
29+
/// <param name="ct">Cancellation token.</param>
30+
/// <returns>Task that completes when the CQL script has been executed.</returns>
31+
public async Task<ExecResult> ExecScriptAsync(string scriptContent, CancellationToken ct = default)
32+
{
33+
var scriptFilePath = string.Join("/", string.Empty, "tmp", Guid.NewGuid().ToString("D"), Path.GetRandomFileName());
34+
35+
await CopyAsync(Encoding.Default.GetBytes(scriptContent), scriptFilePath, Unix.FileMode644, ct)
36+
.ConfigureAwait(false);
37+
38+
return await ExecAsync(new[] { "cqlsh", "--file", scriptFilePath }, ct)
39+
.ConfigureAwait(false);
40+
}
41+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFrameworks>net8.0;net9.0;netstandard2.0;netstandard2.1</TargetFrameworks>
4+
<LangVersion>latest</LangVersion>
5+
</PropertyGroup>
6+
<ItemGroup>
7+
<PackageReference Include="JetBrains.Annotations" VersionOverride="2023.3.0" PrivateAssets="All"/>
8+
</ItemGroup>
9+
<ItemGroup>
10+
<ProjectReference Include="../Testcontainers/Testcontainers.csproj"/>
11+
</ItemGroup>
12+
</Project>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
global using System;
2+
global using System.IO;
3+
global using System.Text;
4+
global using System.Linq;
5+
global using System.Threading;
6+
global using System.Threading.Tasks;
7+
global using System.Collections.Generic;
8+
global using Docker.DotNet.Models;
9+
global using DotNet.Testcontainers.Builders;
10+
global using DotNet.Testcontainers.Configurations;
11+
global using DotNet.Testcontainers.Containers;
12+
global using JetBrains.Annotations;

0 commit comments

Comments
 (0)