Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Db2 module #1237

Open
wants to merge 33 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
61bfd3d
Add Db2 module
kevin0x90 Aug 20, 2024
3a0bef9
Merge branch 'develop' into Db2Support
kevin0x90 Aug 20, 2024
5feb0d0
Add windows / linux switch for Db2 dependency
kevin0x90 Aug 20, 2024
5558efe
Adjust conditional references
kevin0x90 Aug 20, 2024
7444da8
Revert conditional Db2 packages to use OS variable
kevin0x90 Aug 20, 2024
780f617
Remove windows condition for non is specific db2 dependency
kevin0x90 Aug 20, 2024
2a690e6
Adjust platform specific Db2 package conditions
kevin0x90 Aug 21, 2024
7d05809
Add Db2 windows dependency condition
kevin0x90 Aug 21, 2024
e11fdb2
Add Db2 dependency conditionals to test project
kevin0x90 Aug 21, 2024
128b996
Run Db2 connection test only on windows due to issues with the ubuntu…
kevin0x90 Aug 21, 2024
3597532
Attempt to exclude Db2 connection test on ubuntu runner
kevin0x90 Aug 21, 2024
398976b
Update docker platform trait for windows only test
kevin0x90 Aug 21, 2024
8f219eb
Set environment variables for Db2 test to run on linux
kevin0x90 Aug 22, 2024
65c88d9
Add condition to only set db2 env variables for linux, fix variable s…
kevin0x90 Aug 22, 2024
fd98e17
Update cicd.yml
kevin0x90 Aug 22, 2024
827b5cf
Update cicd.yml
kevin0x90 Aug 22, 2024
0872bf9
Update cicd.yml
kevin0x90 Aug 22, 2024
974b869
Add Db2 environment variables debug
kevin0x90 Aug 23, 2024
2aee543
Remove command continuation for Db2 variables
kevin0x90 Aug 23, 2024
45694ab
Adjust Db2 env variables
kevin0x90 Aug 23, 2024
9f3ab65
Generate runsettings file to prepare environment variables required f…
kevin0x90 Aug 23, 2024
d7be1e7
Adjust Db2 linux runsetting generation to fill a template file instea…
kevin0x90 Aug 24, 2024
8682b88
Add documentation and adjust test case for connectionstring
kevin0x90 Aug 31, 2024
0ca7952
Merge branch 'develop' into Db2Support
kevin0x90 Aug 31, 2024
0f81656
Add Db2 module documentation to navigation
kevin0x90 Sep 1, 2024
9ed4196
Add in-memory mapping and do not archive logs, no autoconfig to reduc…
kevin0x90 Sep 2, 2024
cd3a803
Merge branch 'develop' into Db2Support
kevin0x90 Sep 2, 2024
b6eccbc
Merge branch 'develop' into Db2Support
kevin0x90 Sep 3, 2024
3fcf5e9
Merge branch 'develop' into Db2Support
kevin0x90 Sep 6, 2024
15f59ef
Use source includes for documentation
kevin0x90 Sep 9, 2024
21948f9
Merge branch 'develop' into Db2Support
kevin0x90 Sep 10, 2024
86e6479
Minor cleanup
kevin0x90 Sep 10, 2024
53b351f
Merge branch 'develop' into Db2Support
kevin0x90 Sep 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
<PackageVersion Include="Confluent.Kafka" Version="2.0.2"/>
<PackageVersion Include="Consul" Version="1.6.10.9"/>
<PackageVersion Include="CouchbaseNetClient" Version="3.4.3"/>
<PackageVersion Include="Net.IBM.Data.Db2" Version="8.0.0.200" Condition="$([MSBuild]::IsOSPlatform('Windows'))" />
<PackageVersion Include="Net.IBM.Data.Db2-lnx" Version="8.0.0.200" Condition="$([MSBuild]::IsOSPlatform('Linux'))" />
<PackageVersion Include="DotPulsar" Version="3.3.2"/>
<PackageVersion Include="Elastic.Clients.Elasticsearch" Version="8.0.5"/>
<PackageVersion Include="EventStore.Client.Grpc.Streams" Version="22.0.0"/>
Expand Down
209 changes: 113 additions & 96 deletions Testcontainers.sln

Large diffs are not rendered by default.

41 changes: 41 additions & 0 deletions docs/modules/db2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# IBM DB2

[IBM DB2](https://www.ibm.com/db2), is a relational database engine developed by IBM. The following example provides .NET developers with a starting point to use a IBM DB2 instance in the [xUnit][xunit] tests.

The following example (for windows) uses the following NuGet packages:

```console title="Install the NuGet dependencies"
dotnet add package Testcontainers.Db2
dotnet add package Net.IBM.Data.Db2
dotnet add package xunit
```

Please note: For linux there are currently some hurdles and the package Net.IBM.Data.Db2-lnx has to be used with the following environment variables being set:

- LD_LIBRARY_PATH
- PATH
- DB2_CLI_DRIVER_INSTALL_PATH

One way to achieve this within a test project is to extend the .csproj with a task that writes a .runsettings file. An example is given below:

=== "Example"
```xml
--8<-- "tests/Testcontainers.Db2.Tests/Testcontainers.Db2.Tests.csproj:RunSettingsGeneration"
```

IDEs and editors may also require the following packages to run tests: `xunit.runner.visualstudio` and `Microsoft.NET.Test.Sdk`.

Copy and paste the following code into a new `.cs` test file within an existing test project.

=== "Usage Example"
```csharp
--8<-- "tests/Testcontainers.Db2.Tests/Db2ContainerTest.cs:UseDb2Container"
```

To execute the tests, use the command `dotnet test` from a terminal.

## A Note To Developers

Once Testcontainers creates a server instance, developers may use the connection string with any of the popular data-access technologies found in the .NET Ecosystem. Some of these libraries include [Entity Framework Core](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore), [Dapper](https://www.nuget.org/packages/Dapper), and [NHibernate](https://www.nuget.org/packages/NHibernate). At which point, developers can execute database migrations and SQL scripts.

[xunit]: https://xunit.net/
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ nav:
- examples/aspnet.md
- Modules:
- modules/index.md
- modules/db2.md
- modules/elasticsearch.md
- modules/mongodb.md
- modules/mssql.md
Expand Down
1 change: 1 addition & 0 deletions src/Testcontainers.Db2/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root = true
205 changes: 205 additions & 0 deletions src/Testcontainers.Db2/Db2Builder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
using System.Linq;
using System.Threading.Tasks;

namespace Testcontainers.Db2;

/// <inheritdoc cref="ContainerBuilder{TBuilderEntity, TContainerEntity, TConfigurationEntity}" />
[PublicAPI]
public sealed class Db2Builder : ContainerBuilder<Db2Builder, Db2Container, Db2Configuration>
{
public const string Db2Image = "icr.io/db2_community/db2:latest";

public const ushort Db2Port = 50000;

public const string DefaultDatabase = "test";

public const string DefaultUsername = "db2inst1";

public const string DefaultPassword = "test123";

public const string DefaultLicenseAgreement = "accept";

public const string DefaultInMemoryDatabasePath = "/home/db2inst1/data";

/// <summary>
/// Initializes a new instance of the <see cref="Db2Builder" /> class.
/// </summary>
public Db2Builder()
: this(new Db2Configuration())
{
DockerResourceConfiguration = Init().DockerResourceConfiguration;
}

/// <summary>
/// Initializes a new instance of the <see cref="Db2Builder" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
private Db2Builder(Db2Configuration resourceConfiguration)
: base(resourceConfiguration)
{
DockerResourceConfiguration = resourceConfiguration;
}

/// <inheritdoc />
protected override Db2Configuration DockerResourceConfiguration { get; }

/// <summary>
/// Sets the Db2 database name.
/// </summary>
/// <param name="database">The Db2 database.</param>
/// <returns>A configured instance of <see cref="Db2Builder" />.</returns>
public Db2Builder WithDatabase(string database)
{
return Merge(DockerResourceConfiguration, new Db2Configuration(database: database))
.WithEnvironment("DBNAME", database);
}

/// <summary>
/// Sets the Db2 username.
/// </summary>
/// <param name="username">The Db2 username.</param>
/// <returns>A configured instance of <see cref="Db2Builder" />.</returns>
public Db2Builder WithUsername(string username)
{
return Merge(DockerResourceConfiguration, new Db2Configuration(username: username))
.WithEnvironment("DB2INSTANCE", username);
}

/// <summary>
/// Sets the Db2 password.
/// </summary>
/// <param name="password">The Db2 password.</param>
/// <returns>A configured instance of <see cref="Db2Builder" />.</returns>
public Db2Builder WithPassword(string password)
{
return Merge(DockerResourceConfiguration, new Db2Configuration(password: password))
.WithEnvironment("DB2INST1_PASSWORD", password);
}

/// <summary>
/// Sets the Db2 archive logs.
/// </summary>
/// <param name="archiveLogs">The Db2 archive logs setting.</param>
/// <returns>A configured instance of <see cref="Db2Builder" />.</returns>
public Db2Builder WithArchiveLogs(bool archiveLogs)
{
return Merge(DockerResourceConfiguration, new Db2Configuration(archiveLogs: archiveLogs))
.WithEnvironment("ARCHIVE_LOGS", archiveLogs.ToString());
}

/// <summary>
/// Sets the Db2 autoconfig setting.
/// </summary>
/// <param name="autoConfig">The Db2 autoconfig setting.</param>
/// <returns>A configured instance of <see cref="Db2Builder" />.</returns>
public Db2Builder WithAutoconfig(bool autoConfig)
{
return Merge(DockerResourceConfiguration, new Db2Configuration(autoConfig: autoConfig))
.WithEnvironment("AUTOCONFIG", autoConfig.ToString());
}

/// <summary>
/// Accepts the Db2 license agreement.
/// </summary>
/// <returns>A configured instance of <see cref="Db2Builder" />.</returns>
public Db2Builder WithLicenseAgreement()
{
return Merge(DockerResourceConfiguration, new Db2Configuration(licenseAgreement: DefaultLicenseAgreement))
.WithEnvironment("LICENSE", DefaultLicenseAgreement);
}

/// <summary>
/// Maps the database to memory.
/// </summary>
/// <returns>A configured instance of <see cref="Db2Builder" />.</returns>
public Db2Builder WithInMemoryDatabase()
{
return Merge(DockerResourceConfiguration, new Db2Configuration(licenseAgreement: DefaultInMemoryDatabasePath))
.WithTmpfsMount(DefaultInMemoryDatabasePath);
}

/// <inheritdoc />
public override Db2Container Build()
{
Validate();

// By default, the base builder waits until the container is running. However, for Db2, a more advanced waiting strategy is necessary
// If the user does not provide a custom waiting strategy, append the default Db2 waiting strategy.
var db2Builder = DockerResourceConfiguration.WaitStrategies.Count() > 1
? this
: WithWaitStrategy(Wait.ForUnixContainer()
.UntilMessageIsLogged("All databases are now active")
.UntilMessageIsLogged("Setup has completed.")
.AddCustomWaitStrategy(new WaitUntil(DockerResourceConfiguration))
);

return new Db2Container(db2Builder.DockerResourceConfiguration);
}

/// <inheritdoc />
protected override Db2Builder Init() => base.Init()
.WithImage(Db2Image)
.WithPortBinding(Db2Port, true)
.WithDatabase(DefaultDatabase)
.WithUsername(DefaultUsername)
.WithPassword(DefaultPassword)
.WithLicenseAgreement()
.WithArchiveLogs(false)
.WithAutoconfig(false)
.WithInMemoryDatabase()
.WithPrivileged(true);

/// <inheritdoc />
protected override void Validate()
{
base.Validate();

_ = Guard.Argument(DockerResourceConfiguration.Username, nameof(DockerResourceConfiguration.Username))
.NotNull()
.NotEmpty();

_ = Guard.Argument(DockerResourceConfiguration.Password, nameof(DockerResourceConfiguration.Password))
.NotNull()
.NotEmpty();
}

/// <inheritdoc />
protected override Db2Builder Clone(IResourceConfiguration<CreateContainerParameters> resourceConfiguration)
{
return Merge(DockerResourceConfiguration, new Db2Configuration(resourceConfiguration));
}

/// <inheritdoc />
protected override Db2Builder Clone(IContainerConfiguration resourceConfiguration)
{
return Merge(DockerResourceConfiguration, new Db2Configuration(resourceConfiguration));
}

/// <inheritdoc />
protected override Db2Builder Merge(Db2Configuration oldValue, Db2Configuration newValue)
{
return new Db2Builder(new Db2Configuration(oldValue, newValue));
}

/// <inheritdoc cref="IWaitUntil" />
private sealed class WaitUntil : IWaitUntil
{
/// <summary>
/// Initializes a new instance of the <see cref="WaitUntil" /> class.
/// </summary>
/// <param name="configuration">The container configuration.</param>
public WaitUntil(Db2Configuration configuration)
{
}

/// <inheritdoc />
public async Task<bool> UntilAsync(IContainer container)
{
var db2Container = (Db2Container)container;

var execResult = await db2Container.ExecScriptAsync("SELECT 1 FROM SYSIBM.SYSDUMMY1").ConfigureAwait(false);

return 0L.Equals(execResult.ExitCode);
}
}
}
117 changes: 117 additions & 0 deletions src/Testcontainers.Db2/Db2Configuration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
namespace Testcontainers.Db2;

/// <inheritdoc cref="ContainerConfiguration" />
[PublicAPI]
public sealed class Db2Configuration : ContainerConfiguration
{

/// <summary>
/// Initializes a new instance of the <see cref="Db2Configuration" /> class.
/// </summary>
/// <param name="database">The Db2 database.</param>
/// <param name="username">The Db2 username.</param>
/// <param name="password">The Db2 password.</param>
/// <param name="archiveLogs">The Db2 archive logs setting.</param>
/// <param name="autoConfig">The Db2 auto config setting.</param>
/// <param name="licenseAgreement">The Db2 license agreement.</param>
/// <param name="inMemoryDatabasePath">The Db2 database path to map into memory (tmpfs).</param>
public Db2Configuration(
string database = null,
string username = null,
string password = null,
bool archiveLogs = false,
bool autoConfig = false,
string licenseAgreement = null,
string inMemoryDatabasePath = null)
{
Database = database;
Username = username;
Password = password;
ArchiveLogs = archiveLogs;
AutoConfig = autoConfig;
LicenseAgreement = licenseAgreement;
InMemoryDatabasePath = inMemoryDatabasePath;
}

/// <summary>
/// Initializes a new instance of the <see cref="Db2Configuration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public Db2Configuration(IResourceConfiguration<CreateContainerParameters> resourceConfiguration)
: base(resourceConfiguration)
{
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
}

/// <summary>
/// Initializes a new instance of the <see cref="Db2Configuration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public Db2Configuration(IContainerConfiguration resourceConfiguration)
: base(resourceConfiguration)
{
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
}

/// <summary>
/// Initializes a new instance of the <see cref="Db2Configuration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public Db2Configuration(Db2Configuration resourceConfiguration)
: this(new Db2Configuration(), resourceConfiguration)
{
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
}

/// <summary>
/// Initializes a new instance of the <see cref="Db2Configuration" /> class.
/// </summary>
/// <param name="oldValue">The old Docker resource configuration.</param>
/// <param name="newValue">The new Docker resource configuration.</param>
public Db2Configuration(Db2Configuration oldValue, Db2Configuration newValue)
: base(oldValue, newValue)
{
Database = BuildConfiguration.Combine(oldValue.Database, newValue.Database);
Username = BuildConfiguration.Combine(oldValue.Username, newValue.Username);
Password = BuildConfiguration.Combine(oldValue.Password, newValue.Password);
ArchiveLogs = BuildConfiguration.Combine(oldValue.ArchiveLogs, newValue.ArchiveLogs);
AutoConfig = BuildConfiguration.Combine(oldValue.AutoConfig, newValue.AutoConfig);
LicenseAgreement = BuildConfiguration.Combine(oldValue.LicenseAgreement, newValue.LicenseAgreement);
InMemoryDatabasePath = BuildConfiguration.Combine(oldValue.InMemoryDatabasePath, newValue.InMemoryDatabasePath);
}

/// <summary>
/// Gets the Db2 database.
/// </summary>
public string Database { get; }

/// <summary>
/// Gets the Db2 username.
/// </summary>
public string Username { get; }

/// <summary>
/// Gets the Db2 password.
/// </summary>
public string Password { get; }

/// <summary>
/// Toggle for archivation of logs.
/// </summary>
public bool ArchiveLogs { get; }

/// <summary>
/// Toggle for database autoconfiguration.
/// </summary>
public bool AutoConfig { get; }

/// <summary>
/// License agreement value.
/// </summary>
public string LicenseAgreement { get; }

/// <summary>
/// Path to the database files that should be mapped into memory (tmpfs).
/// </summary>
public string InMemoryDatabasePath { get; }
}
Loading
Loading