diff --git a/src/Testcontainers/Builders/Base64Provider.cs b/src/Testcontainers/Builders/Base64Provider.cs
index de0671e57..465477792 100644
--- a/src/Testcontainers/Builders/Base64Provider.cs
+++ b/src/Testcontainers/Builders/Base64Provider.cs
@@ -39,15 +39,40 @@ public Base64Provider(JsonElement jsonElement, ILogger logger)
}
///
- /// Gets a predicate that determines whether a contains a Docker registry key.
+ /// Determines whether the specified JSON property contains a Docker registry
+ /// that matches the given registry host.
///
- public static Func HasDockerRegistryKey { get; }
- = (property, hostname) => property.Name.Equals(hostname, StringComparison.OrdinalIgnoreCase) || property.Name.EndsWith("://" + hostname, StringComparison.OrdinalIgnoreCase);
+ /// The JSON property to check.
+ /// The registry host to match against.
+ /// true if the property contains a matching Docker registry; otherwise, false.
+ public static bool HasDockerRegistryName(JsonProperty property, string registryHost)
+ {
+ var propertyName = property.Name;
+
+ if (propertyName.Equals(registryHost, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+
+ if (propertyName.EndsWith("://" + registryHost, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+
+ if (TryGetHost(propertyName, out var propertyNameNormalized) && TryGetHost(registryHost, out var registryHostNormalized))
+ {
+ return string.Equals(propertyNameNormalized, registryHostNormalized, StringComparison.OrdinalIgnoreCase);
+ }
+ else
+ {
+ return false;
+ }
+ }
///
public bool IsApplicable(string hostname)
{
- return !JsonValueKind.Undefined.Equals(_rootElement.ValueKind) && !JsonValueKind.Null.Equals(_rootElement.ValueKind) && _rootElement.EnumerateObject().Any(property => HasDockerRegistryKey(property, hostname));
+ return !JsonValueKind.Undefined.Equals(_rootElement.ValueKind) && !JsonValueKind.Null.Equals(_rootElement.ValueKind) && _rootElement.EnumerateObject().Any(property => HasDockerRegistryName(property, hostname));
}
///
@@ -60,7 +85,7 @@ public IDockerRegistryAuthenticationConfiguration GetAuthConfig(string hostname)
return null;
}
- var authProperty = _rootElement.EnumerateObject().LastOrDefault(property => HasDockerRegistryKey(property, hostname));
+ var authProperty = _rootElement.EnumerateObject().LastOrDefault(property => HasDockerRegistryName(property, hostname));
if (JsonValueKind.Undefined.Equals(authProperty.Value.ValueKind))
{
@@ -120,5 +145,27 @@ public IDockerRegistryAuthenticationConfiguration GetAuthConfig(string hostname)
_logger.DockerRegistryCredentialFound(hostname);
return new DockerRegistryAuthenticationConfiguration(authProperty.Name, credential[0], credential[1]);
}
+
+ ///
+ /// Tries to extract the host from the specified value.
+ ///
+ /// The string to extract the host from.
+ /// The extracted host if successful; otherwise, the original string.
+ /// true if the host was successfully extracted; otherwise, false.
+ private static bool TryGetHost(string value, out string host)
+ {
+ var uriToParse = value.Contains("://") ? value : "dummy://" + value;
+
+ if (Uri.TryCreate(uriToParse, UriKind.Absolute, out var uri))
+ {
+ host = uri.Port == -1 || uri.IsDefaultPort ? uri.Host : uri.Host + ":" + uri.Port;
+ return true;
+ }
+ else
+ {
+ host = value;
+ return false;
+ }
+ }
}
}
diff --git a/src/Testcontainers/Builders/CredsHelperProvider.cs b/src/Testcontainers/Builders/CredsHelperProvider.cs
index 4e2ab9387..6072f53a1 100644
--- a/src/Testcontainers/Builders/CredsHelperProvider.cs
+++ b/src/Testcontainers/Builders/CredsHelperProvider.cs
@@ -39,7 +39,7 @@ public CredsHelperProvider(JsonElement jsonElement, ILogger logger)
///
public bool IsApplicable(string hostname)
{
- return !JsonValueKind.Undefined.Equals(_rootElement.ValueKind) && !JsonValueKind.Null.Equals(_rootElement.ValueKind) && _rootElement.EnumerateObject().Any(property => Base64Provider.HasDockerRegistryKey(property, hostname));
+ return !JsonValueKind.Undefined.Equals(_rootElement.ValueKind) && !JsonValueKind.Null.Equals(_rootElement.ValueKind) && _rootElement.EnumerateObject().Any(property => Base64Provider.HasDockerRegistryName(property, hostname));
}
///
@@ -52,7 +52,7 @@ public IDockerRegistryAuthenticationConfiguration GetAuthConfig(string hostname)
return null;
}
- var registryEndpointProperty = _rootElement.EnumerateObject().LastOrDefault(property => Base64Provider.HasDockerRegistryKey(property, hostname));
+ var registryEndpointProperty = _rootElement.EnumerateObject().LastOrDefault(property => Base64Provider.HasDockerRegistryName(property, hostname));
if (!JsonValueKind.String.Equals(registryEndpointProperty.Value.ValueKind))
{
diff --git a/tests/Testcontainers.Tests/Unit/Configurations/DockerRegistryAuthenticationProviderTest.cs b/tests/Testcontainers.Tests/Unit/Configurations/DockerRegistryAuthenticationProviderTest.cs
index 8be2f65b3..855d190c0 100644
--- a/tests/Testcontainers.Tests/Unit/Configurations/DockerRegistryAuthenticationProviderTest.cs
+++ b/tests/Testcontainers.Tests/Unit/Configurations/DockerRegistryAuthenticationProviderTest.cs
@@ -66,9 +66,29 @@ public sealed class Base64ProviderTest
private readonly WarnLogger _warnLogger = new WarnLogger();
[Theory]
- [InlineData("{\"auths\":{\"ghcr.io\":{}}}")]
- [InlineData("{\"auths\":{\"://ghcr.io\":{}}}")]
- public void ResolvePartialDockerRegistry(string jsonDocument)
+ [InlineData("{\"auths\":{\"ghcr.io\":{}}}", "ghcr.io", true)]
+ [InlineData("{\"auths\":{\"ghcr.io\":{}}}", "ghcr", false)]
+ [InlineData("{\"auths\":{\"http://ghcr.io\":{}}}", "ghcr.io", true)]
+ [InlineData("{\"auths\":{\"https://ghcr.io\":{}}}", "ghcr.io", true)]
+ [InlineData("{\"auths\":{\"registry.example.com:5000\":{}}}", "registry.example.com:5000", true)]
+ [InlineData("{\"auths\":{\"localhost:5000\":{}}}", "localhost:5000", true)]
+ [InlineData("{\"auths\":{\"registry.example.com:5000\":{}}}", "registry.example.com", false)]
+ [InlineData("{\"auths\":{\"localhost:5000\":{}}}", "localhost", false)]
+ [InlineData("{\"auths\":{\"https://registry.example.com:5000\":{}}}", "registry.example.com:5000", true)]
+ [InlineData("{\"auths\":{\"http://localhost:8080\":{}}}", "localhost:8080", true)]
+ [InlineData("{\"auths\":{\"docker.io\":{}}}", "docker.io", true)]
+ [InlineData("{\"auths\":{\"docker.io\":{}}}", "index.docker.io", false)]
+ [InlineData("{\"auths\":{\"index.docker.io\":{}}}", "docker.io", false)]
+ [InlineData("{\"auths\":{\"https://index.docker.io/v1/\":{}}}", "index.docker.io", true)]
+ [InlineData("{\"auths\":{\"registry.k8s.io\":{}}}", "registry.k8s.io", true)]
+ [InlineData("{\"auths\":{\"gcr.io\":{}}}", "gcr.io", true)]
+ [InlineData("{\"auths\":{\"us-docker.pkg.dev\":{}}}", "us-docker.pkg.dev", true)]
+ [InlineData("{\"auths\":{\"quay.io\":{}}}", "quay.io", true)]
+ [InlineData("{\"auths\":{\"localhost\":{}}}", "localhost", true)]
+ [InlineData("{\"auths\":{\"127.0.0.1:5000\":{}}}", "127.0.0.1:5000", true)]
+ [InlineData("{\"auths\":{\"[::1]:5000\":{}}}", "[::1]:5000", true)]
+ [InlineData("{\"auths\":{\"https://registry.example.com/v2\":{}}}", "registry.example.com", true)]
+ public void ResolvePartialDockerRegistry(string jsonDocument, string hostname, bool expectedResult)
{
// Given
var jsonElement = JsonDocument.Parse(jsonDocument).RootElement;
@@ -77,8 +97,7 @@ public void ResolvePartialDockerRegistry(string jsonDocument)
var authenticationProvider = new Base64Provider(jsonElement, NullLogger.Instance);
// Then
- Assert.False(authenticationProvider.IsApplicable("ghcr"));
- Assert.True(authenticationProvider.IsApplicable("ghcr.io"));
+ Assert.Equal(expectedResult, authenticationProvider.IsApplicable(hostname));
}
[Theory]