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 Lowkey Vault module #1344

Open
wants to merge 25 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
78a4981
add support for lowkey vault
Xor-el Jan 16, 2025
a8b7b5b
simplify lowkey vault builder and configuration
Xor-el Jan 17, 2025
a703363
add lowkey vault to ci/cd workflow
Xor-el Jan 17, 2025
34ec22d
update Azure.Identity nuget package
Xor-el Jan 17, 2025
c3e7124
reorder lowkey vault in ci/cd yml
Xor-el Jan 17, 2025
0a2dc1c
fix lowkey vault ordering in sln
Xor-el Jan 17, 2025
707633c
add support for dynamic host port mapping
Xor-el Jan 18, 2025
9269620
code updates
Xor-el Jan 19, 2025
7990cdc
fix formatting in Directory.Packages.props
Xor-el Jan 20, 2025
9d47508
add support for lowkey vault
Xor-el Jan 16, 2025
01d18ec
simplify lowkey vault builder and configuration
Xor-el Jan 17, 2025
78a12ba
add lowkey vault to ci/cd workflow
Xor-el Jan 17, 2025
18286be
update Azure.Identity nuget package
Xor-el Jan 17, 2025
139b0d9
reorder lowkey vault in ci/cd yml
Xor-el Jan 17, 2025
6868076
fix lowkey vault ordering in sln
Xor-el Jan 17, 2025
814de99
add support for dynamic host port mapping
Xor-el Jan 18, 2025
1c55d3e
code updates
Xor-el Jan 19, 2025
d27c20f
fix formatting in Directory.Packages.props
Xor-el Jan 20, 2025
123f685
Merge branch 'feature/add-lowkey-vault-support' of https://github.com…
Xor-el Feb 1, 2025
8098d50
prefer using HttpStatusCode
Xor-el Feb 1, 2025
a2227bf
Trigger CI
Xor-el Feb 2, 2025
b92aca1
Merge branch 'develop' into feature/add-lowkey-vault-support
Xor-el Feb 18, 2025
c008181
Merge branch 'develop' into feature/add-lowkey-vault-support
Xor-el Mar 14, 2025
ecb080a
Merge branch 'develop' into feature/add-lowkey-vault-support
HofmeisterAn Mar 22, 2025
db8ffe2
simplify API surface leaving only essential ones.
Xor-el Mar 22, 2025
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
1 change: 1 addition & 0 deletions .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ jobs:
{ name: "Testcontainers.Keycloak", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.Kusto", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.LocalStack", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.LowkeyVault", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.MariaDb", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.Milvus", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.Minio", runs-on: "ubuntu-22.04" },
Expand Down
3 changes: 3 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,8 @@
<PackageVersion Include="RavenDB.Client" Version="5.4.100"/>
<PackageVersion Include="Selenium.WebDriver" Version="4.8.1"/>
<PackageVersion Include="StackExchange.Redis" Version="2.6.90"/>
<PackageVersion Include="Azure.Identity" Version="1.13.2" />
<PackageVersion Include="Azure.Security.KeyVault.Certificates" Version="4.7.0" />
<PackageVersion Include="Azure.Security.KeyVault.Secrets" Version="4.7.0" />
</ItemGroup>
</Project>
20 changes: 17 additions & 3 deletions Testcontainers.sln
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Kusto", "src
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.LocalStack", "src\Testcontainers.LocalStack\Testcontainers.LocalStack.csproj", "{3792268A-EF08-4569-8118-991E08FD61C4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.LowkeyVault", "src\Testcontainers.LowkeyVault\Testcontainers.LowkeyVault.csproj", "{436486CE-E855-43DA-A2C7-9832E98BD86E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.MariaDb", "src\Testcontainers.MariaDb\Testcontainers.MariaDb.csproj", "{4B204EB3-C478-422E-9B6F-62DF3871291A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Milvus", "src\Testcontainers.Milvus\Testcontainers.Milvus.csproj", "{B024E315-831F-429D-92AA-44B839AC10F4}"
Expand Down Expand Up @@ -175,6 +177,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Kusto.Tests"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.LocalStack.Tests", "tests\Testcontainers.LocalStack.Tests\Testcontainers.LocalStack.Tests.csproj", "{728CBE16-1D52-4F84-AF01-7229E6013512}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.LowkeyVault.Tests", "tests\Testcontainers.LowkeyVault.Tests\Testcontainers.LowkeyVault.Tests.csproj", "{CB4F241B-EB79-49D5-A45F-050BEE2191B8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.MariaDb.Tests", "tests\Testcontainers.MariaDb.Tests\Testcontainers.MariaDb.Tests.csproj", "{7F0AE083-9DB8-4BD4-91F7-C199DCC7301D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Milvus.Tests", "tests\Testcontainers.Milvus.Tests\Testcontainers.Milvus.Tests.csproj", "{5247DF94-32F3-4ED6-AE71-6AB4F4078E6D}"
Expand Down Expand Up @@ -242,9 +246,6 @@ Global
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5365F780-0E6C-41F0-B1B9-7DC34368F80C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5365F780-0E6C-41F0-B1B9-7DC34368F80C}.Debug|Any CPU.Build.0 = Debug|Any CPU
Expand Down Expand Up @@ -442,6 +443,10 @@ Global
{64A87DE5-29B0-4A54-9E74-560484D8C7C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{64A87DE5-29B0-4A54-9E74-560484D8C7C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{64A87DE5-29B0-4A54-9E74-560484D8C7C0}.Release|Any CPU.Build.0 = Release|Any CPU
{436486CE-E855-43DA-A2C7-9832E98BD86E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{436486CE-E855-43DA-A2C7-9832E98BD86E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{436486CE-E855-43DA-A2C7-9832E98BD86E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{436486CE-E855-43DA-A2C7-9832E98BD86E}.Release|Any CPU.Build.0 = Release|Any CPU
{380BB29B-F556-404D-B13B-CA250599C565}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{380BB29B-F556-404D-B13B-CA250599C565}.Debug|Any CPU.Build.0 = Debug|Any CPU
{380BB29B-F556-404D-B13B-CA250599C565}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -690,11 +695,18 @@ Global
{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}.Release|Any CPU.Build.0 = Release|Any CPU
{CB4F241B-EB79-49D5-A45F-050BEE2191B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CB4F241B-EB79-49D5-A45F-050BEE2191B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CB4F241B-EB79-49D5-A45F-050BEE2191B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CB4F241B-EB79-49D5-A45F-050BEE2191B8}.Release|Any CPU.Build.0 = Release|Any CPU
{E901DF14-6F05-4FC2-825A-3055FAD33561}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E901DF14-6F05-4FC2-825A-3055FAD33561}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E901DF14-6F05-4FC2-825A-3055FAD33561}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E901DF14-6F05-4FC2-825A-3055FAD33561}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{5365F780-0E6C-41F0-B1B9-7DC34368F80C} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{AB9C1563-07C7-4685-BACD-BB1FF64B3611} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
Expand Down Expand Up @@ -745,6 +757,7 @@ Global
{7D5C6816-0DD2-4E13-A585-033B5D3C80D5} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{68F8600D-24E9-4E03-9E25-5F6EB338EAC1} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{64A87DE5-29B0-4A54-9E74-560484D8C7C0} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{436486CE-E855-43DA-A2C7-9832E98BD86E} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{380BB29B-F556-404D-B13B-CA250599C565} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{84911C93-C2A9-46E9-AE5E-D567306589E5} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{EC76857B-A3B8-4B7A-A1B0-8D867A4D1733} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
Expand Down Expand Up @@ -807,6 +820,7 @@ Global
{27CDB869-A150-4593-958F-6F26E5391E7C} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{DDB41BC8-5826-4D97-9C5F-001151E3FFD6} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{CB4F241B-EB79-49D5-A45F-050BEE2191B8} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{E901DF14-6F05-4FC2-825A-3055FAD33561} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
EndGlobalSection
EndGlobal
1 change: 1 addition & 0 deletions docs/modules/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ await moduleNameContainer.StartAsync();
| Keycloak | `quay.io/keycloak/keycloak:21.1` | [NuGet](https://www.nuget.org/packages/Testcontainers.Keycloak) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Keycloak) |
| Kusto emulator | `mcr.microsoft.com/azuredataexplorer/kustainer-linux:latest` | [NuGet](https://www.nuget.org/packages/Testcontainers.Kusto) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Kusto) |
| LocalStack | `localstack/localstack:2.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.LocalStack) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.LocalStack) |
| Lowkey Vault | `nagyesta/lowkey-vault:2.7.1-ubi9-minimal` | [NuGet](https://www.nuget.org/packages/Testcontainers.LowkeyVault) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.LowkeyVault) |
| MariaDB | `mariadb:10.10` | [NuGet](https://www.nuget.org/packages/Testcontainers.MariaDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.MariaDb) |
| Milvus | `milvusdb/milvus:v2.3.10` | [NuGet](https://www.nuget.org/packages/Testcontainers.Milvus) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Milvus) |
| MinIO | `minio/minio:RELEASE.2023-01-31T02-24-19Z` | [NuGet](https://www.nuget.org/packages/Testcontainers.Minio) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Minio) |
Expand Down
1 change: 1 addition & 0 deletions src/Testcontainers.LowkeyVault/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root = true
156 changes: 156 additions & 0 deletions src/Testcontainers.LowkeyVault/LowkeyVaultBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
namespace Testcontainers.LowkeyVault;

/// <inheritdoc cref="ContainerBuilder{TBuilderEntity, TContainerEntity, TConfigurationEntity}" />
[PublicAPI]
public sealed class LowkeyVaultBuilder : ContainerBuilder<LowkeyVaultBuilder, LowkeyVaultContainer, LowkeyVaultConfiguration>
{
public const string LowkeyVaultImage = "nagyesta/lowkey-vault:2.7.1-ubi9-minimal";

public const ushort LowkeyVaultPort = 8443;

public const ushort LowkeyVaultTokenPort = 8080;

public const string TokenEndPointPath = "/metadata/identity/oauth2/token";

private const string LowKeyVaultEnvVarKey = "LOWKEY_ARGS";

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

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

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


/// <summary>
/// Sets Additional Arguments.
/// </summary>
/// <param name="debug">The additional arguments.</param>
/// <returns>A configured instance of <see cref="LowkeyVaultBuilder" />.</returns>
public LowkeyVaultBuilder WithAdditionalArguments(List<string> additionalArguments)
{
return Merge(DockerResourceConfiguration, new LowkeyVaultConfiguration(additionalArguments: additionalArguments))
.WithEnvironment(LowKeyVaultEnvVarKey, AddOrAppend(string.Join(" ", additionalArguments)));
}

/// <inheritdoc />
public override LowkeyVaultContainer Build()
{
Validate();
return new LowkeyVaultContainer(DockerResourceConfiguration);
}

/// <inheritdoc />
protected override LowkeyVaultBuilder Init()
{
return base.Init()
.WithImage(LowkeyVaultImage)
.WithPortBinding(LowkeyVaultPort, true)
.WithPortBinding(LowkeyVaultTokenPort, true)
.WithAdditionalArguments([$"--LOWKEY_VAULT_RELAXED_PORTS={true}"])
.WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged("(?s).*Started LowkeyVaultApp.*$"));
}

/// <inheritdoc />
protected override void Validate()
{
base.Validate();
_ = Guard.Argument(DockerResourceConfiguration.AdditionalArguments, nameof(DockerResourceConfiguration.AdditionalArguments)).NotNull();
}

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

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

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

private string AddOrAppend(string value)
{
return DockerResourceConfiguration.Environments.TryGetValue(LowKeyVaultEnvVarKey, out var existingValue)
? MergeEnv(existingValue, value)
: value;
}

/// <summary>
/// Merges two input strings by treating strings starting with `--` as keys
/// and strings after `=` as their corresponding values. Updates the original
/// string with values from the new string if keys overlap.
/// </summary>
/// <param name="originalString">The original input string containing key-value pairs.</param>
/// <param name="newString">The new input string containing updated key-value pairs.</param>
/// <returns>The merged string with updated key-value pairs.</returns>
private static string MergeEnv(string originalString, string newString)
{
var originalDict = ParseToDictionary(originalString);
var newDict = ParseToDictionary(newString);

foreach (var kvp in newDict)
originalDict[kvp.Key] = kvp.Value;

return string.Join(" ", originalDict.Select(kvp => $"{kvp.Key}={kvp.Value}"));

/// <summary>
/// Parses an input string into a dictionary where keys are the parts starting with `--`
/// and values are extracted from the segment after `=`. Handles multi-word values.
/// </summary>
/// <param name="input">The input string to parse.</param>
/// <returns>A dictionary representation of the key-value pairs in the input string.</returns>
static Dictionary<string, string> ParseToDictionary(string input)
{
var dictionary = new Dictionary<string, string>();
var parts = input.Split([' '], StringSplitOptions.RemoveEmptyEntries);
string currentKey = null;

foreach (var part in parts)
{
if (part.StartsWith("--"))
{
var equalIndex = part.IndexOf('=');

if (equalIndex > 0)
{
dictionary[part.Substring(0, equalIndex)] = part.Substring(equalIndex + 1);
}
else
{
currentKey = part;
}
}
else if (currentKey != null)
{
dictionary[currentKey] = part;
currentKey = null;
}
}

return dictionary;
}
}
}
61 changes: 61 additions & 0 deletions src/Testcontainers.LowkeyVault/LowkeyVaultConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
namespace Testcontainers.LowkeyVault;

/// <inheritdoc cref="ContainerConfiguration" />
[PublicAPI]
public sealed class LowkeyVaultConfiguration : ContainerConfiguration
{
/// <summary>
/// Initializes a new instance of the <see cref="LowkeyVaultConfiguration" /> class.
/// </summary>
/// <param name="additionalArguments">The additional arguments to be passed via environment variables to the container.</param>
public LowkeyVaultConfiguration(List<string> additionalArguments = null)
{
AdditionalArguments = additionalArguments;
}

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

/// <summary>
/// Initializes a new instance of the <see cref="LowkeyVaultConfiguration" /> class.
/// </summary>
/// <param name="oldValue">The old Docker resource configuration.</param>
/// <param name="newValue">The new Docker resource configuration.</param>
public LowkeyVaultConfiguration(LowkeyVaultConfiguration oldValue, LowkeyVaultConfiguration newValue)
: base(oldValue, newValue)
{
AdditionalArguments = BuildConfiguration.Combine(oldValue.AdditionalArguments, newValue.AdditionalArguments);
}

/// <summary>
/// Gets Additional Arguments.
/// </summary>
internal List<string> AdditionalArguments { get; } = [];
}
Loading