Skip to content

Commit

Permalink
feat: Add remote container registry identity token support (#1124)
Browse files Browse the repository at this point in the history
  • Loading branch information
HofmeisterAn authored Feb 21, 2024
1 parent 2766fec commit 8aee376
Show file tree
Hide file tree
Showing 7 changed files with 42 additions and 25 deletions.
8 changes: 4 additions & 4 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="BouncyCastle.Cryptography" Version="2.2.1"/>
<PackageVersion Include="BouncyCastle.Cryptography" Version="2.3.0"/>
<PackageVersion Include="Docker.DotNet.X509" Version="3.125.15"/>
<PackageVersion Include="Docker.DotNet" Version="3.125.15"/>
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0"/>
Expand All @@ -15,8 +15,8 @@
<!-- Unit and integration test dependencies: -->
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
<PackageVersion Include="coverlet.collector" Version="6.0.0"/>
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.6"/>
<PackageVersion Include="xunit" Version="2.6.6"/>
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.7"/>
<PackageVersion Include="xunit" Version="2.7.0"/>
<!-- Third-party client dependencies to connect and interact with the containers: -->
<PackageVersion Include="Apache.NMS.ActiveMQ" Version="2.1.0"/>
<PackageVersion Include="ArangoDBNetStandard" Version="2.0.1"/>
Expand Down Expand Up @@ -59,4 +59,4 @@
<PackageVersion Include="Selenium.WebDriver" Version="4.8.1"/>
<PackageVersion Include="StackExchange.Redis" Version="2.6.90"/>
</ItemGroup>
</Project>
</Project>
4 changes: 2 additions & 2 deletions examples/Flyway/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
<!-- Unit and integration test dependencies: -->
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
<PackageVersion Include="Testcontainers.PostgreSql" Version="3.7.0"/>
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.6"/>
<PackageVersion Include="xunit" Version="2.6.5"/>
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.7"/>
<PackageVersion Include="xunit" Version="2.7.0"/>
<!-- Third-party client dependencies to connect and interact with the containers: -->
<PackageVersion Include="Npgsql" Version="6.0.10"/>
</ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions examples/WeatherForecast/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.1"/>
<PackageVersion Include="Testcontainers.SqlEdge" Version="3.7.0"/>
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.6"/>
<PackageVersion Include="xunit" Version="2.6.5"/>
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.7"/>
<PackageVersion Include="xunit" Version="2.7.0"/>
<!-- Third-party client dependencies to connect and interact with the containers: -->
<PackageVersion Include="Selenium.WebDriver.ChromeDriver" Version="106.0.5249.6100"/>
<PackageVersion Include="Selenium.WebDriver" Version="4.9.1"/>
Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "8.0.100",
"version": "8.0.200",
"rollForward": "latestPatch"
}
}
21 changes: 11 additions & 10 deletions src/Testcontainers.PostgreSql/PostgreSqlBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,34 +126,35 @@ protected override PostgreSqlBuilder Merge(PostgreSqlConfiguration oldValue, Pos
/// <inheritdoc cref="IWaitUntil" />
private sealed class WaitUntil : IWaitUntil
{
private readonly string[] _command;
private readonly IList<string> _command;

/// <summary>
/// Initializes a new instance of the <see cref="WaitUntil" /> class.
/// </summary>
/// <param name="configuration">The container configuration.</param>
public WaitUntil(PostgreSqlConfiguration configuration)
{
_command = new[] {
"pg_isready",
"--host", "localhost", // Explicitly specify localhost in order to be ready only after the initdb scripts have run and the server is listening over TCP/IP
"--dbname", configuration.Database,
"--username", configuration.Username,
};
// Explicitly specify the host to ensure readiness only after the initdb scripts have executed, and the server is listening on TCP/IP.
_command = new List<string> { "pg_isready", "--host", "localhost", "--dbname", configuration.Database, "--username", configuration.Username };
}

/// <summary>
/// Test whether the database is ready to accept connections or not with the <a href="https://www.postgresql.org/docs/current/app-pg-isready.html">pg_isready</a> command.
/// Checks whether the database is ready and accepts connections or not.
/// </summary>
/// <returns><see langword="true"/> if the database is ready to accept connections; <see langword="false"/> if the database is not yet ready.</returns>
/// <remarks>
/// The wait strategy uses <a href="https://www.postgresql.org/docs/current/app-pg-isready.html">pg_isready</a> to check the connection status of PostgreSql.
/// </remarks>
/// <param name="container">The starting container instance.</param>
/// <returns>Task that completes and returns true when the database is ready and accepts connections, otherwise false.</returns>
/// <exception cref="NotSupportedException">Thrown when the PostgreSql image does not contain <c>pg_isready</c>.</exception>
public async Task<bool> UntilAsync(IContainer container)
{
var execResult = await container.ExecAsync(_command)
.ConfigureAwait(false);

if (execResult.Stderr.Contains("pg_isready was not found"))
{
throw new NotSupportedException($"The {container.Image.FullName} image is not supported. Please use postgres:9.3 onwards.");
throw new NotSupportedException($"The '{container.Image.FullName}' image does not contain: pg_isready. Please use 'postgres:9.3' onwards.");
}

return 0L.Equals(execResult.ExitCode);
Expand Down
11 changes: 11 additions & 0 deletions src/Testcontainers/Builders/Base64Provider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,17 @@ public IDockerRegistryAuthenticationConfiguration GetAuthConfig(string hostname)
return null;
}

if (authProperty.Value.TryGetProperty("identitytoken", out var identityToken) && JsonValueKind.String.Equals(identityToken.ValueKind))
{
var identityTokenValue = identityToken.GetString();

if (!string.IsNullOrEmpty(identityTokenValue))
{
_logger.DockerRegistryCredentialFound(hostname);
return new DockerRegistryAuthenticationConfiguration(authProperty.Name, null, null, identityTokenValue);
}
}

if (!authProperty.Value.TryGetProperty("auth", out var auth))
{
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ public void ResolvePartialDockerRegistry(string jsonDocument)
[InlineData("{\"auths\":{\"" + DockerRegistry + "\":{\"auth\":{}}}}", true, "The \"auth\" property value kind for https://index.docker.io/v1/ is invalid: Object")]
[InlineData("{\"auths\":{\"" + DockerRegistry + "\":{\"auth\":\"Not_Base64_encoded\"}}}", true, "The \"auth\" property value for https://index.docker.io/v1/ is not a valid Base64 string")]
[InlineData("{\"auths\":{\"" + DockerRegistry + "\":{\"auth\":\"dXNlcm5hbWU=\"}}}", true, "The \"auth\" property value for https://index.docker.io/v1/ should contain one colon separating the username and the password (basic authentication)")]
[InlineData("{\"auths\":{\"" + DockerRegistry + "\":{\"identitytoken\":null}}}", true, null)]
[InlineData("{\"auths\":{\"" + DockerRegistry + "\":{\"identitytoken\":\"\"}}}", true, null)]
[InlineData("{\"auths\":{\"" + DockerRegistry + "\":{\"identitytoken\":{}}}}", true, null)]
public void ShouldGetNull(string jsonDocument, bool isApplicable, string logMessage)
{
// Given
Expand All @@ -116,11 +119,12 @@ public void ShouldGetNull(string jsonDocument, bool isApplicable, string logMess
}
}

[Fact]
public void ShouldGetAuthConfig()
[Theory]
[InlineData("{\"auths\":{\"" + DockerRegistry + "\":{\"auth\":\"dXNlcm5hbWU6cGFzc3dvcmQ=\"}}}", "username", "password", null)]
[InlineData("{\"auths\":{\"" + DockerRegistry + "\":{\"identitytoken\":\"identitytoken\"}}}", null, null, "identitytoken")]
public void ShouldGetAuthConfig(string jsonDocument, string expectedUsername, string expectedPassword, string expectedIdentityToken)
{
// Given
const string jsonDocument = "{\"auths\":{\"" + DockerRegistry + "\":{\"auth\":\"dXNlcm5hbWU6cGFzc3dvcmQ=\"}}}";
var jsonElement = JsonDocument.Parse(jsonDocument).RootElement;

// When
Expand All @@ -131,8 +135,9 @@ public void ShouldGetAuthConfig()
Assert.True(authenticationProvider.IsApplicable(DockerRegistry));
Assert.NotNull(authConfig);
Assert.Equal(DockerRegistry, authConfig.RegistryEndpoint);
Assert.Equal("username", authConfig.Username);
Assert.Equal("password", authConfig.Password);
Assert.Equal(expectedUsername, authConfig.Username);
Assert.Equal(expectedPassword, authConfig.Password);
Assert.Equal(expectedIdentityToken, authConfig.IdentityToken);
}
}

Expand Down Expand Up @@ -259,7 +264,7 @@ public void Dispose()

private sealed class WarnLogger : ILogger
{
private readonly List<Tuple<LogLevel, string>> _logMessages = new List<Tuple<LogLevel, string>>();
private readonly IList<Tuple<LogLevel, string>> _logMessages = new List<Tuple<LogLevel, string>>();

public IEnumerable<Tuple<LogLevel, string>> LogMessages => _logMessages;

Expand Down

0 comments on commit 8aee376

Please sign in to comment.