diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 26e9a3944..0f8cae249 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -23,12 +23,12 @@ jobs: uses: actions/checkout@v2 - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v2 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 diff --git a/src/Testcontainers/Builders/Base64Provider.cs b/src/Testcontainers/Builders/Base64Provider.cs index df1758cf9..7131a7add 100644 --- a/src/Testcontainers/Builders/Base64Provider.cs +++ b/src/Testcontainers/Builders/Base64Provider.cs @@ -8,7 +8,7 @@ using JetBrains.Annotations; using Microsoft.Extensions.Logging; - /// + /// internal sealed class Base64Provider : IDockerRegistryAuthenticationProvider { private readonly JsonElement rootElement; @@ -41,7 +41,11 @@ public Base64Provider(JsonElement jsonElement, ILogger logger) /// public bool IsApplicable(string hostname) { - return !default(JsonElement).Equals(this.rootElement) && this.rootElement.EnumerateObject().Any(); +#if NETSTANDARD2_1_OR_GREATER + return !default(JsonElement).Equals(this.rootElement) && !JsonValueKind.Null.Equals(this.rootElement.ValueKind) && this.rootElement.EnumerateObject().Any(property => property.Name.Contains(hostname, StringComparison.OrdinalIgnoreCase)); +#else + return !default(JsonElement).Equals(this.rootElement) && !JsonValueKind.Null.Equals(this.rootElement.ValueKind) && this.rootElement.EnumerateObject().Any(property => property.Name.IndexOf(hostname, StringComparison.OrdinalIgnoreCase) >= 0); +#endif } /// diff --git a/src/Testcontainers/Builders/CredsHelperProvider.cs b/src/Testcontainers/Builders/CredsHelperProvider.cs index 29ddbc6ce..2edf480f7 100644 --- a/src/Testcontainers/Builders/CredsHelperProvider.cs +++ b/src/Testcontainers/Builders/CredsHelperProvider.cs @@ -1,66 +1,109 @@ namespace DotNet.Testcontainers.Builders { + using System; + using System.Linq; using System.Text.Json; using DotNet.Testcontainers.Configurations; + using JetBrains.Annotations; using Microsoft.Extensions.Logging; - /// + /// internal sealed class CredsHelperProvider : IDockerRegistryAuthenticationProvider { - private const string TokenUsername = ""; - private readonly JsonElement dockerConfig; + private readonly JsonElement rootElement; + private readonly ILogger logger; - public CredsHelperProvider(JsonElement dockerConfig, ILogger logger) + /// + /// Initializes a new instance of the class. + /// + /// The JSON document that holds the Docker config credsHelper node. + /// The logger. + [PublicAPI] + public CredsHelperProvider(JsonDocument jsonDocument, ILogger logger) + : this(jsonDocument.RootElement, logger) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The JSON element that holds the Docker config credsHelper node. + /// The logger. + [PublicAPI] + public CredsHelperProvider(JsonElement jsonElement, ILogger logger) { - this.dockerConfig = dockerConfig; + this.rootElement = jsonElement.TryGetProperty("credHelpers", out var credHelpers) ? credHelpers : default; this.logger = logger; } /// public bool IsApplicable(string hostname) { - return this.FindCredHelperName(hostname) != null; +#if NETSTANDARD2_1_OR_GREATER + return !default(JsonElement).Equals(this.rootElement) && !JsonValueKind.Null.Equals(this.rootElement.ValueKind) && this.rootElement.EnumerateObject().Any(property => property.Name.Contains(hostname, StringComparison.OrdinalIgnoreCase)); +#else + return !default(JsonElement).Equals(this.rootElement) && !JsonValueKind.Null.Equals(this.rootElement.ValueKind) && this.rootElement.EnumerateObject().Any(property => property.Name.IndexOf(hostname, StringComparison.OrdinalIgnoreCase) >= 0); +#endif } /// public IDockerRegistryAuthenticationConfiguration GetAuthConfig(string hostname) { - this.logger.SearchingDockerRegistryCredential("CredsHelper"); - var credHelperName = this.FindCredHelperName(hostname); - if (credHelperName == null) + this.logger.SearchingDockerRegistryCredential("CredHelpers"); + + if (!this.IsApplicable(hostname)) + { + return null; + } + +#if NETSTANDARD2_1_OR_GREATER + var registryEndpointProperty = this.rootElement.EnumerateObject().LastOrDefault(property => property.Name.Contains(hostname, StringComparison.OrdinalIgnoreCase)); +#else + var registryEndpointProperty = this.rootElement.EnumerateObject().LastOrDefault(property => property.Name.IndexOf(hostname, StringComparison.OrdinalIgnoreCase) >= 0); +#endif + + if (!JsonValueKind.String.Equals(registryEndpointProperty.Value.ValueKind)) { return null; } - var credentialProviderOutput = ExternalProcessCredentialProvider.GetCredentialProviderOutput(credHelperName, hostname); - if (credentialProviderOutput == null) + if (string.IsNullOrEmpty(registryEndpointProperty.Value.GetString())) { return null; } - DockerRegistryAuthenticationConfiguration dockerRegistryAuthenticationConfiguration; + var credentialProviderOutput = DockerCredentialProcess.Get(registryEndpointProperty.Value.GetString(), hostname); + if (string.IsNullOrEmpty(credentialProviderOutput)) + { + return null; + } + + JsonElement credential; + try { - var credentialProviderOutputJson = JsonSerializer.Deserialize(credentialProviderOutput); - var username = credentialProviderOutputJson.GetProperty("Username").GetString(); - var secret = credentialProviderOutputJson.GetProperty("Secret").GetString(); - dockerRegistryAuthenticationConfiguration = username == TokenUsername ? new DockerRegistryAuthenticationConfiguration(hostname, identityToken: secret) : new DockerRegistryAuthenticationConfiguration(hostname, username, secret); + credential = JsonDocument.Parse(credentialProviderOutput).RootElement; } catch (JsonException) { return null; } + var username = credential.TryGetProperty("Username", out var usernameProperty) ? usernameProperty.GetString() : null; + + var password = credential.TryGetProperty("Secret", out var passwordProperty) ? passwordProperty.GetString() : null; + this.logger.DockerRegistryCredentialFound(hostname); - return dockerRegistryAuthenticationConfiguration; - } - private string FindCredHelperName(string hostname) - { - return this.dockerConfig.TryGetProperty("credHelpers", out var credHelpersProperty) && credHelpersProperty.ValueKind != JsonValueKind.Null && credHelpersProperty.TryGetProperty(hostname, out var credHelperProperty) - ? credHelperProperty.GetString() - : null; + if ("".Equals(username, StringComparison.OrdinalIgnoreCase)) + { + return new DockerRegistryAuthenticationConfiguration(hostname, null, null, password); + } + else + { + return new DockerRegistryAuthenticationConfiguration(hostname, username, password); + } } } } diff --git a/src/Testcontainers/Builders/CredsStoreProvider.cs b/src/Testcontainers/Builders/CredsStoreProvider.cs index d5eb42d28..a90bac2e6 100644 --- a/src/Testcontainers/Builders/CredsStoreProvider.cs +++ b/src/Testcontainers/Builders/CredsStoreProvider.cs @@ -6,7 +6,7 @@ using JetBrains.Annotations; using Microsoft.Extensions.Logging; - /// + /// internal sealed class CredsStoreProvider : IDockerRegistryAuthenticationProvider { private static readonly JsonSerializerOptions JsonSerializerOptions = new JsonSerializerOptions(); @@ -59,8 +59,8 @@ public IDockerRegistryAuthenticationConfiguration GetAuthConfig(string hostname) return null; } - var credentialProviderOutput = ExternalProcessCredentialProvider.GetCredentialProviderOutput(this.rootElement.GetString(), hostname); - if (credentialProviderOutput == null) + var credentialProviderOutput = DockerCredentialProcess.Get(this.rootElement.GetString(), hostname); + if (string.IsNullOrEmpty(credentialProviderOutput)) { return null; } diff --git a/src/Testcontainers/Builders/DockerCredentialProcess.cs b/src/Testcontainers/Builders/DockerCredentialProcess.cs new file mode 100644 index 000000000..81404c315 --- /dev/null +++ b/src/Testcontainers/Builders/DockerCredentialProcess.cs @@ -0,0 +1,41 @@ +namespace DotNet.Testcontainers.Builders +{ + using System; + using System.Diagnostics; + + internal static class DockerCredentialProcess + { + public static string Get(string credentialProviderName, string hostname) + { + var dockerCredentialStartInfo = new ProcessStartInfo(); + dockerCredentialStartInfo.FileName = "docker-credential-" + credentialProviderName; + dockerCredentialStartInfo.Arguments = "get"; + dockerCredentialStartInfo.RedirectStandardInput = true; + dockerCredentialStartInfo.RedirectStandardOutput = true; + dockerCredentialStartInfo.UseShellExecute = false; + + var dockerCredentialProcess = new Process(); + dockerCredentialProcess.StartInfo = dockerCredentialStartInfo; + + try + { + if (!dockerCredentialProcess.Start()) + { + return null; + } + + dockerCredentialProcess.StandardInput.WriteLine(hostname); + dockerCredentialProcess.StandardInput.Close(); + return dockerCredentialProcess.StandardOutput.ReadToEnd().Trim(); + } + catch (Exception) + { + return null; + } + finally + { + dockerCredentialProcess.Dispose(); + } + } + } +} diff --git a/src/Testcontainers/Builders/DockerEndpointAuthenticationProvider.cs b/src/Testcontainers/Builders/DockerEndpointAuthenticationProvider.cs index 3d75c0a53..040f182c6 100644 --- a/src/Testcontainers/Builders/DockerEndpointAuthenticationProvider.cs +++ b/src/Testcontainers/Builders/DockerEndpointAuthenticationProvider.cs @@ -3,7 +3,7 @@ using System.Linq; using DotNet.Testcontainers.Configurations; - /// + /// internal sealed class DockerEndpointAuthenticationProvider : IDockerEndpointAuthenticationProvider { /// diff --git a/src/Testcontainers/Builders/DockerRegistryAuthenticationProvider.cs b/src/Testcontainers/Builders/DockerRegistryAuthenticationProvider.cs index 347160151..4c1b5d069 100644 --- a/src/Testcontainers/Builders/DockerRegistryAuthenticationProvider.cs +++ b/src/Testcontainers/Builders/DockerRegistryAuthenticationProvider.cs @@ -9,7 +9,7 @@ using JetBrains.Annotations; using Microsoft.Extensions.Logging; - /// + /// internal sealed class DockerRegistryAuthenticationProvider : IDockerRegistryAuthenticationProvider { private const string DockerHub = "index.docker.io"; @@ -83,10 +83,9 @@ private IDockerRegistryAuthenticationConfiguration GetUncachedAuthConfig(string { authConfig = new IDockerRegistryAuthenticationProvider[] { - new CredsHelperProvider(dockerConfigDocument.RootElement, this.logger), - new CredsStoreProvider(dockerConfigDocument, this.logger), - new Base64Provider(dockerConfigDocument, this.logger), - }.AsParallel() + new CredsHelperProvider(dockerConfigDocument, this.logger), new CredsStoreProvider(dockerConfigDocument, this.logger), new Base64Provider(dockerConfigDocument, this.logger), + } + .AsParallel() .Select(authenticationProvider => authenticationProvider.GetAuthConfig(hostname)) .FirstOrDefault(authenticationProvider => authenticationProvider != null); } diff --git a/src/Testcontainers/Builders/EnvironmentEndpointAuthenticationProvider.cs b/src/Testcontainers/Builders/EnvironmentEndpointAuthenticationProvider.cs index 67f50e89a..0275f8346 100644 --- a/src/Testcontainers/Builders/EnvironmentEndpointAuthenticationProvider.cs +++ b/src/Testcontainers/Builders/EnvironmentEndpointAuthenticationProvider.cs @@ -3,7 +3,7 @@ using System; using DotNet.Testcontainers.Configurations; - /// + /// internal sealed class EnvironmentEndpointAuthenticationProvider : IDockerEndpointAuthenticationProvider { private readonly Uri dockerEngine; diff --git a/src/Testcontainers/Builders/IAuthenticationProvider.cs b/src/Testcontainers/Builders/IAuthenticationProvider.cs deleted file mode 100644 index e94b83199..000000000 --- a/src/Testcontainers/Builders/IAuthenticationProvider.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace DotNet.Testcontainers.Builders -{ - using JetBrains.Annotations; - - /// - /// A Docker authentication provider. - /// - /// Type of the authentication configuration. - [PublicAPI] - internal interface IAuthenticationProvider - { - /// - /// Is true when the authentication provider contains any credentials, otherwise false. - /// - /// True when the authentication provider contains any credentials, otherwise false. - [PublicAPI] - bool IsApplicable(); - - /// - /// Gets the Docker authentication configuration. - /// - /// The Docker hostname. - /// The Docker authentication configuration or null if no configuration matches the hostname. - [PublicAPI] - [CanBeNull] - TAuthenticationConfiguration GetAuthConfig(string hostname); - } -} diff --git a/src/Testcontainers/Builders/IDockerEndpointAuthenticationProvider.cs b/src/Testcontainers/Builders/IDockerEndpointAuthenticationProvider.cs new file mode 100644 index 000000000..44856c593 --- /dev/null +++ b/src/Testcontainers/Builders/IDockerEndpointAuthenticationProvider.cs @@ -0,0 +1,27 @@ +namespace DotNet.Testcontainers.Builders +{ + using DotNet.Testcontainers.Configurations; + using JetBrains.Annotations; + + /// + /// A Docker endpoint authentication provider. + /// + [PublicAPI] + internal interface IDockerEndpointAuthenticationProvider + { + /// + /// Is true when the authentication provider contains any Docker endpoint credentials, otherwise false. + /// + /// True when the authentication provider contains any Docker endpoint credentials, otherwise false. + [PublicAPI] + bool IsApplicable(); + + /// + /// Gets the Docker endpoint authentication configuration. + /// + /// The Docker endpoint authentication configuration or null if no configuration matches the hostname. + [PublicAPI] + [CanBeNull] + IDockerEndpointAuthenticationConfiguration GetAuthConfig(); + } +} diff --git a/src/Testcontainers/Builders/IDockerRegistryAuthenticationProvider.cs b/src/Testcontainers/Builders/IDockerRegistryAuthenticationProvider.cs new file mode 100644 index 000000000..119869de5 --- /dev/null +++ b/src/Testcontainers/Builders/IDockerRegistryAuthenticationProvider.cs @@ -0,0 +1,29 @@ +namespace DotNet.Testcontainers.Builders +{ + using DotNet.Testcontainers.Configurations; + using JetBrains.Annotations; + + /// + /// A Docker registry authentication provider. + /// + [PublicAPI] + internal interface IDockerRegistryAuthenticationProvider + { + /// + /// Is true when the authentication provider contains any Docker registry credentials, otherwise false. + /// + /// The Docker hostname. + /// True when the authentication provider contains any Docker registry credentials, otherwise false. + [PublicAPI] + bool IsApplicable(string hostname); + + /// + /// Gets the Docker registry authentication configuration. + /// + /// The Docker hostname. + /// The Docker registry authentication configuration or null if no configuration matches the hostname. + [PublicAPI] + [CanBeNull] + IDockerRegistryAuthenticationConfiguration GetAuthConfig(string hostname); + } +} diff --git a/src/Testcontainers/Builders/ITestcontainersBuilder.cs b/src/Testcontainers/Builders/ITestcontainersBuilder.cs index 81c6b5fe2..626cdc660 100644 --- a/src/Testcontainers/Builders/ITestcontainersBuilder.cs +++ b/src/Testcontainers/Builders/ITestcontainersBuilder.cs @@ -274,7 +274,7 @@ public interface ITestcontainersBuilder : IAbstractBuilder /// Password to authenticate. /// A configured instance of . [PublicAPI] - [Obsolete("Be aware we will replace this method in the future. Instead, we will use the local Docker credential store. Then, no additional configurations are necessary.")] + [Obsolete("Use the local Docker credential store.")] ITestcontainersBuilder WithRegistryAuthentication(string registryEndpoint, string username, string password); /// diff --git a/src/Testcontainers/Builders/NpipeEndpointAuthenticationProvider.cs b/src/Testcontainers/Builders/NpipeEndpointAuthenticationProvider.cs index 3a9fb23d3..0c04ba523 100644 --- a/src/Testcontainers/Builders/NpipeEndpointAuthenticationProvider.cs +++ b/src/Testcontainers/Builders/NpipeEndpointAuthenticationProvider.cs @@ -4,7 +4,7 @@ using System.Runtime.InteropServices; using DotNet.Testcontainers.Configurations; - /// + /// internal sealed class NpipeEndpointAuthenticationProvider : IDockerEndpointAuthenticationProvider { #pragma warning disable S1075 diff --git a/src/Testcontainers/Builders/TestcontainersBuilder.cs b/src/Testcontainers/Builders/TestcontainersBuilder.cs index 91a7b0329..6bdf0d5e6 100644 --- a/src/Testcontainers/Builders/TestcontainersBuilder.cs +++ b/src/Testcontainers/Builders/TestcontainersBuilder.cs @@ -19,7 +19,7 @@ namespace DotNet.Testcontainers.Builders /// /// /// - /// var builder = new builder<TestcontainersContainer>() + /// var builder = new TestcontainersBuilder<TestcontainersContainer>() /// .WithName("nginx") /// .WithImage("nginx") /// .WithEntrypoint("...") @@ -52,7 +52,7 @@ public TestcontainersBuilder() labels: DefaultLabels.Instance, outputConsumer: Consume.DoNotConsumeStdoutAndStderr(), waitStrategies: Wait.ForUnixContainer().Build(), - startupCallback: (testcontainers, ct) => Task.CompletedTask, + startupCallback: (_, ct) => Task.CompletedTask, autoRemove: false, privileged: false), _ => { }) diff --git a/src/Testcontainers/Builders/UnixEndpointAuthenticationProvider.cs b/src/Testcontainers/Builders/UnixEndpointAuthenticationProvider.cs index 4aee083c1..55b84aa52 100644 --- a/src/Testcontainers/Builders/UnixEndpointAuthenticationProvider.cs +++ b/src/Testcontainers/Builders/UnixEndpointAuthenticationProvider.cs @@ -4,7 +4,7 @@ using System.Runtime.InteropServices; using DotNet.Testcontainers.Configurations; - /// + /// internal sealed class UnixEndpointAuthenticationProvider : IDockerEndpointAuthenticationProvider { private static readonly Uri DockerEngine = new Uri("unix:/var/run/docker.sock"); diff --git a/src/Testcontainers/Configurations/Images/DockerRegistryAuthenticationConfiguration.cs b/src/Testcontainers/Configurations/Images/DockerRegistryAuthenticationConfiguration.cs index 2d71c6cda..71da468c4 100644 --- a/src/Testcontainers/Configurations/Images/DockerRegistryAuthenticationConfiguration.cs +++ b/src/Testcontainers/Configurations/Images/DockerRegistryAuthenticationConfiguration.cs @@ -1,9 +1,6 @@ namespace DotNet.Testcontainers.Configurations { /// - /// - /// In the future, we will replace this class. Instead, we will use the local Docker credentials. - /// internal readonly struct DockerRegistryAuthenticationConfiguration : IDockerRegistryAuthenticationConfiguration { /// @@ -12,6 +9,7 @@ namespace DotNet.Testcontainers.Configurations /// The Docker registry endpoint. /// The username. /// The password. + /// The identity token. public DockerRegistryAuthenticationConfiguration( string registryEndpoint = null, string username = null, diff --git a/src/Testcontainers/Logging.cs b/src/Testcontainers/Logging.cs index d5f58aab9..3f3258b28 100644 --- a/src/Testcontainers/Logging.cs +++ b/src/Testcontainers/Logging.cs @@ -66,10 +66,10 @@ private static readonly Action _CanNotGetResourceReape = LoggerMessage.Define(LogLevel.Debug, default, "Can not get resource reaper {Id} endpoint"); private static readonly Action _CanNotConnectToResourceReaper - = LoggerMessage.Define(LogLevel.Error, default, "Can not connect to resource reaper {Id} at {Endpoint}"); + = LoggerMessage.Define(LogLevel.Debug, default, "Can not connect to resource reaper {Id} at {Endpoint}"); private static readonly Action _LostConnectionToResourceReaper - = LoggerMessage.Define(LogLevel.Error, default, "Lost connection to resource reaper {Id} at {Endpoint}"); + = LoggerMessage.Define(LogLevel.Debug, default, "Lost connection to resource reaper {Id} at {Endpoint}"); private static readonly Action _DockerConfigFileNotFound = LoggerMessage.Define(LogLevel.Information, default, "Docker config {DockerConfigFilePath} not found"); @@ -176,13 +176,13 @@ public static void CanNotGetResourceReaperEndpoint(this ILogger logger, Guid id, public static void CanNotConnectToResourceReaper(this ILogger logger, Guid id, string host, ushort port, Exception e) { var endpoint = $"{host}:{port}"; - _CanNotConnectToResourceReaper(logger, id, endpoint, logger.IsEnabled(LogLevel.Debug) ? e : null); + _CanNotConnectToResourceReaper(logger, id, endpoint, e); } public static void LostConnectionToResourceReaper(this ILogger logger, Guid id, string host, ushort port, Exception e) { var endpoint = $"{host}:{port}"; - _LostConnectionToResourceReaper(logger, id, endpoint, logger.IsEnabled(LogLevel.Debug) ? e : null); + _LostConnectionToResourceReaper(logger, id, endpoint, e); } public static void DockerConfigFileNotFound(this ILogger logger, string dockerConfigFilePath) diff --git a/tests/Testcontainers.Tests/Assets/.dockerignore b/tests/Testcontainers.Tests/Assets/.dockerignore index 964a69458..4c9a422f7 100644 --- a/tests/Testcontainers.Tests/Assets/.dockerignore +++ b/tests/Testcontainers.Tests/Assets/.dockerignore @@ -1,2 +1,2 @@ -**/*.md credHelpers +**/*.md diff --git a/tests/DotNet.Testcontainers.Tests/Assets/credHelpers/docker-credential-password.bat b/tests/Testcontainers.Tests/Assets/credHelpers/docker-credential-password.bat similarity index 77% rename from tests/DotNet.Testcontainers.Tests/Assets/credHelpers/docker-credential-password.bat rename to tests/Testcontainers.Tests/Assets/credHelpers/docker-credential-password.bat index c0b760452..c0c9098a4 100644 --- a/tests/DotNet.Testcontainers.Tests/Assets/credHelpers/docker-credential-password.bat +++ b/tests/Testcontainers.Tests/Assets/credHelpers/docker-credential-password.bat @@ -1,2 +1,3 @@ @echo off +set /P hostname= echo {"Username":"username","Secret":"password"} diff --git a/tests/DotNet.Testcontainers.Tests/Assets/credHelpers/docker-credential-password.sh b/tests/Testcontainers.Tests/Assets/credHelpers/docker-credential-password.sh similarity index 92% rename from tests/DotNet.Testcontainers.Tests/Assets/credHelpers/docker-credential-password.sh rename to tests/Testcontainers.Tests/Assets/credHelpers/docker-credential-password.sh index d31ed7343..a48231371 100755 --- a/tests/DotNet.Testcontainers.Tests/Assets/credHelpers/docker-credential-password.sh +++ b/tests/Testcontainers.Tests/Assets/credHelpers/docker-credential-password.sh @@ -1,3 +1,3 @@ #!/bin/bash - +read echo '{"Username":"username","Secret":"password"}' diff --git a/tests/DotNet.Testcontainers.Tests/Assets/credHelpers/docker-credential-token.bat b/tests/Testcontainers.Tests/Assets/credHelpers/docker-credential-token.bat similarity index 78% rename from tests/DotNet.Testcontainers.Tests/Assets/credHelpers/docker-credential-token.bat rename to tests/Testcontainers.Tests/Assets/credHelpers/docker-credential-token.bat index f78dfea62..2ca98397c 100644 --- a/tests/DotNet.Testcontainers.Tests/Assets/credHelpers/docker-credential-token.bat +++ b/tests/Testcontainers.Tests/Assets/credHelpers/docker-credential-token.bat @@ -1,2 +1,3 @@ @echo off +set /P hostname= echo {"Username":"","Secret":"identitytoken"} diff --git a/tests/DotNet.Testcontainers.Tests/Assets/credHelpers/docker-credential-token.sh b/tests/Testcontainers.Tests/Assets/credHelpers/docker-credential-token.sh similarity index 93% rename from tests/DotNet.Testcontainers.Tests/Assets/credHelpers/docker-credential-token.sh rename to tests/Testcontainers.Tests/Assets/credHelpers/docker-credential-token.sh index ee361914d..3313d9743 100755 --- a/tests/DotNet.Testcontainers.Tests/Assets/credHelpers/docker-credential-token.sh +++ b/tests/Testcontainers.Tests/Assets/credHelpers/docker-credential-token.sh @@ -1,3 +1,3 @@ #!/bin/bash - +read echo '{"Username":"","Secret":"identitytoken"}' diff --git a/tests/Testcontainers.Tests/Unit/Configurations/DockerRegistryAuthenticationProviderTest.cs b/tests/Testcontainers.Tests/Unit/Configurations/DockerRegistryAuthenticationProviderTest.cs index 075b26381..1278bdd3c 100644 --- a/tests/Testcontainers.Tests/Unit/Configurations/DockerRegistryAuthenticationProviderTest.cs +++ b/tests/Testcontainers.Tests/Unit/Configurations/DockerRegistryAuthenticationProviderTest.cs @@ -2,6 +2,7 @@ { using System; using System.IO; + using System.Linq; using System.Runtime.InteropServices; using System.Text.Json; using DotNet.Testcontainers.Builders; @@ -62,8 +63,9 @@ public sealed class Base64ProviderTest { [Theory] [InlineData("{}", false)] + [InlineData("{\"auths\":null}", false)] [InlineData("{\"auths\":{}}", false)] - [InlineData("{\"auths\":{\"ghcr.io\":{}}}", true)] + [InlineData("{\"auths\":{\"ghcr.io\":{}}}", false)] [InlineData("{\"auths\":{\"" + DockerRegistry + "\":{}}}", true)] [InlineData("{\"auths\":{\"" + DockerRegistry + "\":{\"auth\":null}}}", true)] [InlineData("{\"auths\":{\"" + DockerRegistry + "\":{\"auth\":\"\"}}}", true)] @@ -125,6 +127,7 @@ public void ShouldGetNull(string jsonDocument, bool isApplicable) #pragma warning disable xUnit1004 [Fact(Skip = "The pipeline has no configured credential store (maybe we can use the Windows tests in the future).")] + #pragma warning restore xUnit1004 public void ShouldGetAuthConfig() { @@ -147,11 +150,23 @@ public void ShouldGetAuthConfig() public sealed class CredsHelperProviderTest { + static CredsHelperProviderTest() + { + Environment.SetEnvironmentVariable("PATH", string.Join(Path.PathSeparator, (Environment.GetEnvironmentVariable("PATH") ?? string.Empty) + .Split(Path.PathSeparator) + .Prepend(Path.Combine(Directory.GetCurrentDirectory(), "Assets", "credHelpers")) + .Distinct())); + } + [Theory] [InlineData("{}", false)] [InlineData("{\"credHelpers\":null}", false)] - [InlineData("{\"credHelpers\":{\"" + DockerRegistry + "\":null}}", false)] - [InlineData("{\"credHelpers\":{\"registry2\":\"script.bat\"}}", false)] + [InlineData("{\"credHelpers\":{}}", false)] + [InlineData("{\"credHelpers\":{\"ghcr.io\":{}}}", false)] + [InlineData("{\"credHelpers\":{\"" + DockerRegistry + "\":{}}}", true)] + [InlineData("{\"credHelpers\":{\"" + DockerRegistry + "\":null}}", true)] + [InlineData("{\"credHelpers\":{\"" + DockerRegistry + "\":\"\"}}", true)] + [InlineData("{\"credHelpers\":{\"" + DockerRegistry + "\":\"script.sh\"}}", true)] public void ShouldGetNull(string jsonDocument, bool isApplicable) { // Given @@ -171,15 +186,9 @@ public void ShouldGetNull(string jsonDocument, bool isApplicable) [InlineData("token", null, null, "identitytoken")] public void ShouldGetAuthConfig(string credHelperName, string expectedUsername, string expectedPassword, string expectedIdentityToken) { - var path = Environment.GetEnvironmentVariable("PATH") ?? string.Empty; - var pathSeparator = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ";" : ":"; - var credHelpersPath = Path.Combine(Environment.CurrentDirectory, "Assets", "credHelpers"); - Environment.SetEnvironmentVariable("PATH", string.Join(pathSeparator, credHelpersPath, path)); - var extension = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".bat" : ".sh"; - var credHelperScriptSuffix = $"{credHelperName}{extension}"; - // Given - var jsonDocument = "{\"credHelpers\":{\"" + DockerRegistry + "\":\"" + credHelperScriptSuffix + "\"}}"; + var credHelpersScriptName = Path.ChangeExtension(credHelperName, RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "bat" : "sh"); + var jsonDocument = "{\"credHelpers\":{\"" + DockerRegistry + "\":\"" + credHelpersScriptName + "\"}}"; var jsonElement = JsonDocument.Parse(jsonDocument).RootElement; // When