From 8aee376aa98018d07d270264920161113dfd8490 Mon Sep 17 00:00:00 2001
From: Andre Hofmeister <9199345+HofmeisterAn@users.noreply.github.com>
Date: Wed, 21 Feb 2024 20:17:12 +0100
Subject: [PATCH] feat: Add remote container registry identity token support
(#1124)
---
Directory.Packages.props | 8 +++----
examples/Flyway/Directory.Packages.props | 4 ++--
.../WeatherForecast/Directory.Packages.props | 4 ++--
global.json | 2 +-
.../PostgreSqlBuilder.cs | 21 ++++++++++---------
src/Testcontainers/Builders/Base64Provider.cs | 11 ++++++++++
...ockerRegistryAuthenticationProviderTest.cs | 17 +++++++++------
7 files changed, 42 insertions(+), 25 deletions(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 3a9e4dbd2..6eeb39533 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -4,7 +4,7 @@
true
-
+
@@ -15,8 +15,8 @@
-
-
+
+
@@ -59,4 +59,4 @@
-
\ No newline at end of file
+
diff --git a/examples/Flyway/Directory.Packages.props b/examples/Flyway/Directory.Packages.props
index 6fee13d9c..8549bb998 100644
--- a/examples/Flyway/Directory.Packages.props
+++ b/examples/Flyway/Directory.Packages.props
@@ -7,8 +7,8 @@
-
-
+
+
diff --git a/examples/WeatherForecast/Directory.Packages.props b/examples/WeatherForecast/Directory.Packages.props
index edbae9614..a281383a5 100644
--- a/examples/WeatherForecast/Directory.Packages.props
+++ b/examples/WeatherForecast/Directory.Packages.props
@@ -11,8 +11,8 @@
-
-
+
+
diff --git a/global.json b/global.json
index b75deba61..d031a7632 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "8.0.100",
+ "version": "8.0.200",
"rollForward": "latestPatch"
}
}
diff --git a/src/Testcontainers.PostgreSql/PostgreSqlBuilder.cs b/src/Testcontainers.PostgreSql/PostgreSqlBuilder.cs
index 0cbb00e9c..c7a0334ac 100644
--- a/src/Testcontainers.PostgreSql/PostgreSqlBuilder.cs
+++ b/src/Testcontainers.PostgreSql/PostgreSqlBuilder.cs
@@ -126,7 +126,7 @@ protected override PostgreSqlBuilder Merge(PostgreSqlConfiguration oldValue, Pos
///
private sealed class WaitUntil : IWaitUntil
{
- private readonly string[] _command;
+ private readonly IList _command;
///
/// Initializes a new instance of the class.
@@ -134,18 +134,19 @@ private sealed class WaitUntil : IWaitUntil
/// The container configuration.
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 { "pg_isready", "--host", "localhost", "--dbname", configuration.Database, "--username", configuration.Username };
}
///
- /// Test whether the database is ready to accept connections or not with the pg_isready command.
+ /// Checks whether the database is ready and accepts connections or not.
///
- /// if the database is ready to accept connections; if the database is not yet ready.
+ ///
+ /// The wait strategy uses pg_isready to check the connection status of PostgreSql.
+ ///
+ /// The starting container instance.
+ /// Task that completes and returns true when the database is ready and accepts connections, otherwise false.
+ /// Thrown when the PostgreSql image does not contain pg_isready.
public async Task UntilAsync(IContainer container)
{
var execResult = await container.ExecAsync(_command)
@@ -153,7 +154,7 @@ public async Task UntilAsync(IContainer container)
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);
diff --git a/src/Testcontainers/Builders/Base64Provider.cs b/src/Testcontainers/Builders/Base64Provider.cs
index 7998aa5e8..29ed7b142 100644
--- a/src/Testcontainers/Builders/Base64Provider.cs
+++ b/src/Testcontainers/Builders/Base64Provider.cs
@@ -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;
diff --git a/tests/Testcontainers.Tests/Unit/Configurations/DockerRegistryAuthenticationProviderTest.cs b/tests/Testcontainers.Tests/Unit/Configurations/DockerRegistryAuthenticationProviderTest.cs
index 27beda932..728a083b1 100644
--- a/tests/Testcontainers.Tests/Unit/Configurations/DockerRegistryAuthenticationProviderTest.cs
+++ b/tests/Testcontainers.Tests/Unit/Configurations/DockerRegistryAuthenticationProviderTest.cs
@@ -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
@@ -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
@@ -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);
}
}
@@ -259,7 +264,7 @@ public void Dispose()
private sealed class WarnLogger : ILogger
{
- private readonly List> _logMessages = new List>();
+ private readonly IList> _logMessages = new List>();
public IEnumerable> LogMessages => _logMessages;