From 375e4940f138725b41a8a1b75a7ceec82fe426cc Mon Sep 17 00:00:00 2001 From: Chet Husk Date: Wed, 24 Jul 2024 16:26:00 +0000 Subject: [PATCH] Merged PR 41305: Added new set of credential env variables to be used separately for pull and... Added new set of credential env variables to be used separately for pull and push operations. Old set of variables is used for fallback. ---- #### AI description (iteration 1) #### PR Classification New feature: Added support for separate credential environment variables for different registry modes (push, pull, pull from output). #### PR Summary This pull request introduces new environment variables for Docker credentials based on registry modes and updates the relevant classes and tests to support this feature. - `AuthHandshakeMessageHandler.cs`: Added `GetDockerCredentialsFromEnvironment` method to fetch credentials based on registry mode. - `Registry.cs`: Introduced `RegistryMode` enum and updated constructors to handle different registry modes. - `DefaultRegistryAPI.cs`: Updated to use registry mode when creating HTTP clients. - `ContainerHelpers.cs`: Added new constants for push and pull registry credentials. - Added unit tests in `AuthHandshakeMessageHandlerTests.cs` to verify the new credential fetching logic. --- .../AuthHandshakeMessageHandler.cs | 72 ++++++++++++++++--- .../ContainerBuilder.cs | 3 +- .../ContainerHelpers.cs | 6 ++ .../DestinationImageReference.cs | 5 +- .../Registry/DefaultRegistryAPI.cs | 8 +-- .../Registry/Registry.cs | 49 +++++++++++-- .../Tasks/CreateNewImage.cs | 3 +- .../CreateNewImageTests.cs | 2 +- .../DockerRegistryManager.cs | 4 +- .../DockerRegistryTests.cs | 6 +- .../EndToEndTests.cs | 8 +-- .../RegistryTests.cs | 2 +- .../AuthHandshakeMessageHandlerTests.cs | 38 ++++++++++ .../RegistryTests.cs | 6 +- 14 files changed, 179 insertions(+), 33 deletions(-) create mode 100644 src/Tests/Microsoft.NET.Build.Containers.UnitTests/AuthHandshakeMessageHandlerTests.cs diff --git a/src/Containers/Microsoft.NET.Build.Containers/AuthHandshakeMessageHandler.cs b/src/Containers/Microsoft.NET.Build.Containers/AuthHandshakeMessageHandler.cs index 172f968baa30..8e4bc29b7656 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/AuthHandshakeMessageHandler.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/AuthHandshakeMessageHandler.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Concurrent; +using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Net; @@ -38,12 +39,14 @@ private sealed record AuthInfo(string Realm, string? Service, string? Scope); private readonly string _registryName; private readonly ILogger _logger; + private readonly RegistryMode _registryMode; private static ConcurrentDictionary _authenticationHeaders = new(); - public AuthHandshakeMessageHandler(string registryName, HttpMessageHandler innerHandler, ILogger logger) : base(innerHandler) + public AuthHandshakeMessageHandler(string registryName, HttpMessageHandler innerHandler, ILogger logger, RegistryMode mode) : base(innerHandler) { _registryName = registryName; _logger = logger; + _registryMode = mode; } /// @@ -156,14 +159,10 @@ public DateTimeOffset ResolvedExpiration /// private async Task<(AuthenticationHeaderValue, DateTimeOffset)?> GetAuthenticationAsync(string registry, string scheme, AuthInfo? bearerAuthInfo, CancellationToken cancellationToken) { - // Allow overrides for auth via environment variables - string? credU = Environment.GetEnvironmentVariable(ContainerHelpers.HostObjectUser) ?? Environment.GetEnvironmentVariable(ContainerHelpers.HostObjectUserLegacy); - string? credP = Environment.GetEnvironmentVariable(ContainerHelpers.HostObjectPass) ?? Environment.GetEnvironmentVariable(ContainerHelpers.HostObjectPassLegacy); - - // fetch creds for the host + DockerCredentials? privateRepoCreds; - - if (!string.IsNullOrEmpty(credU) && !string.IsNullOrEmpty(credP)) + // Allow overrides for auth via environment variables + if (GetDockerCredentialsFromEnvironment(_registryMode) is (string credU, string credP)) { privateRepoCreds = new DockerCredentials(credU, credP); } @@ -196,6 +195,63 @@ public DateTimeOffset ResolvedExpiration } } + internal static (string credU, string credP)? TryGetCredentialsFromEnvVars(string unameVar, string passwordVar) + { + var credU = Environment.GetEnvironmentVariable(unameVar); + var credP = Environment.GetEnvironmentVariable(passwordVar); + if (!string.IsNullOrEmpty(credU) && !string.IsNullOrEmpty(credP)) + { + return (credU, credP); + } + else + { + return null; + } + } + + /// + /// Gets docker credentials from the environment variables based on registry mode. + /// + internal static (string credU, string credP)? GetDockerCredentialsFromEnvironment(RegistryMode mode) + { + if (mode == RegistryMode.Push) + { + if (TryGetCredentialsFromEnvVars(ContainerHelpers.PushHostObjectUser, ContainerHelpers.PushHostObjectPass) is (string, string) pushCreds) + { + return pushCreds; + } + + if (TryGetCredentialsFromEnvVars(ContainerHelpers.HostObjectUser, ContainerHelpers.HostObjectPass) is (string, string) genericCreds) + { + return genericCreds; + } + + return TryGetCredentialsFromEnvVars(ContainerHelpers.HostObjectUserLegacy, ContainerHelpers.HostObjectPassLegacy); + } + else if (mode == RegistryMode.Pull) + { + return TryGetCredentialsFromEnvVars(ContainerHelpers.PullHostObjectUser, ContainerHelpers.PullHostObjectPass); + } + else if (mode == RegistryMode.PullFromOutput) + { + if (TryGetCredentialsFromEnvVars(ContainerHelpers.PullHostObjectUser, ContainerHelpers.PullHostObjectPass) is (string, string) pullCreds) + { + return pullCreds; + } + + if (TryGetCredentialsFromEnvVars(ContainerHelpers.HostObjectUser, ContainerHelpers.HostObjectPass) is (string, string) genericCreds) + { + return genericCreds; + } + + return TryGetCredentialsFromEnvVars(ContainerHelpers.HostObjectUserLegacy, ContainerHelpers.HostObjectPassLegacy); + } + else + { + throw new InvalidEnumArgumentException(nameof(mode), (int)mode, typeof(RegistryMode)); + } + } + /// /// Implements the Docker OAuth2 Authentication flow as documented at . /// ContainerizeAsync( logger.LogTrace("Trace logging: enabled."); bool isLocalPull = string.IsNullOrEmpty(baseRegistry); - Registry? sourceRegistry = isLocalPull ? null : new Registry(baseRegistry, logger); + RegistryMode sourceRegistryMode = baseRegistry.Equals(outputRegistry, StringComparison.InvariantCultureIgnoreCase) ? RegistryMode.PullFromOutput : RegistryMode.Pull; + Registry? sourceRegistry = isLocalPull ? null : new Registry(baseRegistry, logger, sourceRegistryMode); SourceImageReference sourceImageReference = new(sourceRegistry, baseImageName, baseImageTag); DestinationImageReference destinationImageReference = DestinationImageReference.CreateFromSettings( diff --git a/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs b/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs index 137755cdeb3d..3a1ff7b8d004 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs @@ -22,6 +22,12 @@ public static class ContainerHelpers internal const string HostObjectPass = "DOTNET_CONTAINER_REGISTRY_PWORD"; internal const string HostObjectPassLegacy = "SDK_CONTAINER_REGISTRY_PWORD"; + internal const string PushHostObjectUser = "DOTNET_CONTAINER_PUSH_REGISTRY_UNAME"; + internal const string PushHostObjectPass = "DOTNET_CONTAINER_PUSH_REGISTRY_PWORD"; + + internal const string PullHostObjectUser = "DOTNET_CONTAINER_PULL_REGISTRY_UNAME"; + internal const string PullHostObjectPass = "DOTNET_CONTAINER_PULL_REGISTRY_PWORD"; + internal const string DockerRegistryAlias = "docker.io"; /// diff --git a/src/Containers/Microsoft.NET.Build.Containers/DestinationImageReference.cs b/src/Containers/Microsoft.NET.Build.Containers/DestinationImageReference.cs index dc9c99a9fb97..a6c92d93dd80 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/DestinationImageReference.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/DestinationImageReference.cs @@ -75,7 +75,10 @@ public static DestinationImageReference CreateFromSettings( } else if (!string.IsNullOrEmpty(outputRegistry)) { - destinationImageReference = new DestinationImageReference(new Registry(outputRegistry, loggerFactory.CreateLogger()), repository, imageTags); + destinationImageReference = new DestinationImageReference( + new Registry(outputRegistry, loggerFactory.CreateLogger(), RegistryMode.Push), + repository, + imageTags); } else { diff --git a/src/Containers/Microsoft.NET.Build.Containers/Registry/DefaultRegistryAPI.cs b/src/Containers/Microsoft.NET.Build.Containers/Registry/DefaultRegistryAPI.cs index 71b937233fe4..dff5a8921cc2 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Registry/DefaultRegistryAPI.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Registry/DefaultRegistryAPI.cs @@ -22,11 +22,11 @@ internal class DefaultRegistryAPI : IRegistryAPI // Making this a round 30 for convenience. private static TimeSpan LongRequestTimeout = TimeSpan.FromMinutes(30); - internal DefaultRegistryAPI(string registryName, Uri baseUri, bool isInsecureRegistry, ILogger logger) + internal DefaultRegistryAPI(string registryName, Uri baseUri, bool isInsecureRegistry, ILogger logger, RegistryMode mode) { _baseUri = baseUri; _logger = logger; - _client = CreateClient(registryName, baseUri, isInsecureRegistry, logger); + _client = CreateClient(registryName, baseUri, logger, isInsecureRegistry, mode); Manifest = new DefaultManifestOperations(_baseUri, registryName, _client, _logger); Blob = new DefaultBlobOperations(_baseUri, registryName, _client, _logger); } @@ -35,11 +35,11 @@ internal DefaultRegistryAPI(string registryName, Uri baseUri, bool isInsecureReg public IManifestOperations Manifest { get; } - private static HttpClient CreateClient(string registryName, Uri baseUri, bool isInsecureRegistry, ILogger logger) + private static HttpClient CreateClient(string registryName, Uri baseUri, ILogger logger, bool isInsecureRegistry, RegistryMode mode) { HttpMessageHandler innerHandler = CreateHttpHandler(baseUri, isInsecureRegistry, logger); - HttpMessageHandler clientHandler = new AuthHandshakeMessageHandler(registryName, innerHandler, logger); + HttpMessageHandler clientHandler = new AuthHandshakeMessageHandler(registryName, innerHandler, logger, mode); if (baseUri.IsAmazonECRRegistry()) { diff --git a/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs b/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs index 387a21008f11..253ea2a2dc3a 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Registry/Registry.cs @@ -53,6 +53,13 @@ public RidGraphManifestPicker(string runtimeIdentifierGraphPath) } +internal enum RegistryMode +{ + Push, + Pull, + PullFromOutput +} + internal sealed class Registry { private const string DockerHubRegistry1 = "registry-1.docker.io"; @@ -70,11 +77,24 @@ internal sealed class Registry /// public string RegistryName { get; } - internal Registry(string registryName, ILogger logger, IRegistryAPI? registryAPI = null, RegistrySettings? settings = null) : + internal Registry(string registryName, ILogger logger, IRegistryAPI registryAPI, RegistrySettings? settings = null) : this(new Uri($"https://{registryName}"), logger, registryAPI, settings) { } - internal Registry(Uri baseUri, ILogger logger, IRegistryAPI? registryAPI = null, RegistrySettings? settings = null) + internal Registry(string registryName, ILogger logger, RegistryMode mode, RegistrySettings? settings = null) : + this(new Uri($"https://{registryName}"), logger, new RegistryApiFactory(mode), settings) + { } + + + internal Registry(Uri baseUri, ILogger logger, IRegistryAPI registryAPI, RegistrySettings? settings = null) : + this(baseUri, logger, new RegistryApiFactory(registryAPI), settings) + { } + + internal Registry(Uri baseUri, ILogger logger, RegistryMode mode, RegistrySettings? settings = null) : + this(baseUri, logger, new RegistryApiFactory(mode), settings) + { } + + private Registry(Uri baseUri, ILogger logger, RegistryApiFactory factory, RegistrySettings? settings = null) { RegistryName = DeriveRegistryName(baseUri); @@ -87,7 +107,7 @@ internal Registry(Uri baseUri, ILogger logger, IRegistryAPI? registryAPI = null, _logger = logger; _settings = settings ?? new RegistrySettings(RegistryName); - _registryAPI = registryAPI ?? new DefaultRegistryAPI(RegistryName, BaseUri, _settings.IsInsecure, logger); + _registryAPI = factory.Create(RegistryName, BaseUri, logger, _settings.IsInsecure); } private static string DeriveRegistryName(Uri baseUri) @@ -95,7 +115,7 @@ private static string DeriveRegistryName(Uri baseUri) var port = baseUri.Port == -1 ? string.Empty : $":{baseUri.Port}"; if (baseUri.OriginalString.EndsWith(port, ignoreCase: true, culture: null)) { - // the port was part of the original assignment, so it's ok to consider it part of the 'name + // the port was part of the original assignment, so it's ok to consider it part of the 'name' return baseUri.GetComponents(UriComponents.HostAndPort, UriFormat.Unescaped); } else @@ -507,4 +527,25 @@ private async Task PushAsync(BuiltImage builtImage, SourceImageReference source, _logger.LogInformation(Strings.Registry_ManifestUploaded, RegistryName); } } + + private readonly ref struct RegistryApiFactory + { + private readonly IRegistryAPI? _registryApi; + private readonly RegistryMode? _mode; + + public RegistryApiFactory(IRegistryAPI registryApi) + { + _registryApi = registryApi; + } + + public RegistryApiFactory(RegistryMode mode) + { + _mode = mode; + } + + public IRegistryAPI Create(string registryName, Uri baseUri, ILogger logger, bool isInsecureRegistry) + { + return _registryApi ?? new DefaultRegistryAPI(registryName, baseUri, isInsecureRegistry, logger, _mode!.Value); + } + } } diff --git a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs index 0bdd2eed555f..834bae6529f4 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs @@ -60,7 +60,8 @@ internal async Task ExecuteAsync(CancellationToken cancellationToken) return !Log.HasLoggedErrors; } - Registry? sourceRegistry = IsLocalPull ? null : new Registry(BaseRegistry, logger); + RegistryMode sourceRegistryMode = BaseRegistry.Equals(OutputRegistry, StringComparison.InvariantCultureIgnoreCase) ? RegistryMode.PullFromOutput : RegistryMode.Pull; + Registry? sourceRegistry = IsLocalPull ? null : new Registry(BaseRegistry, logger, sourceRegistryMode); SourceImageReference sourceImageReference = new(sourceRegistry, BaseImageName, BaseImageTag); DestinationImageReference destinationImageReference = DestinationImageReference.CreateFromSettings( diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateNewImageTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateNewImageTests.cs index e9fe7898dff5..cf25db4b0309 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateNewImageTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateNewImageTests.cs @@ -225,7 +225,7 @@ public async System.Threading.Tasks.Task CreateNewImage_RootlessBaseImage() var logger = loggerFactory.CreateLogger(nameof(CreateNewImage_RootlessBaseImage)); // Build a rootless base runtime image. - Registry registry = new Registry(DockerRegistryManager.LocalRegistry, logger); + Registry registry = new(DockerRegistryManager.LocalRegistry, logger, RegistryMode.Push); ImageBuilder imageBuilder = await registry.GetImageManifestAsync( DockerRegistryManager.RuntimeBaseImage, diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerRegistryManager.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerRegistryManager.cs index 22447bc5df00..253c44653a85 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerRegistryManager.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerRegistryManager.cs @@ -43,8 +43,8 @@ public static async Task StartAndPopulateDockerRegistry(ITestOutputHelper testOu int spawnRegistryDelay = 1000; //ms StringBuilder failureReasons = new(); - var pullRegistry = new Registry(BaseImageSource, logger); - var pushRegistry = new Registry(LocalRegistry, logger); + var pullRegistry = new Registry(BaseImageSource, logger, RegistryMode.Pull); + var pushRegistry = new Registry(LocalRegistry, logger, RegistryMode.Push); for (int spawnRegistryAttempt = 1; spawnRegistryAttempt <= spawnRegistryMaxRetry; spawnRegistryAttempt++) { diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerRegistryTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerRegistryTests.cs index 3529e5edec41..ba8840dc01c4 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerRegistryTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerRegistryTests.cs @@ -22,7 +22,7 @@ public async Task GetFromRegistry() { var loggerFactory = new TestLoggerFactory(_testOutput); var logger = loggerFactory.CreateLogger(nameof(GetFromRegistry)); - Registry registry = new Registry(DockerRegistryManager.LocalRegistry, logger); + Registry registry = new(DockerRegistryManager.LocalRegistry, logger, RegistryMode.Push); var ridgraphfile = ToolsetUtils.GetRuntimeGraphFilePath(); // Don't need rid graph for local registry image pulls - since we're only pushing single image manifests (not manifest lists) @@ -74,9 +74,9 @@ public async Task WriteToPrivateBasicRegistry() // login to that registry ContainerCli.LoginCommand(_testOutput, "--username", "testuser", "--password", "testpassword", registryName).Execute().Should().Pass(); // push an image to that registry using username/password - Registry localAuthed = new(new Uri($"https://{registryName}"), logger, settings: new(registryName) { ParallelUploadEnabled = false, ForceChunkedUpload = true }); + Registry localAuthed = new(new Uri($"https://{registryName}"), logger, RegistryMode.Push, settings: new() { ParallelUploadEnabled = false, ForceChunkedUpload = true }); var ridgraphfile = ToolsetUtils.GetRuntimeGraphFilePath(); - Registry mcr = new Registry(DockerRegistryManager.BaseImageSource, logger); + Registry mcr = new(DockerRegistryManager.BaseImageSource, logger, RegistryMode.Pull); var sourceImage = new SourceImageReference(mcr, DockerRegistryManager.RuntimeBaseImage, DockerRegistryManager.Net6ImageTag); var destinationImage = new DestinationImageReference(localAuthed, DockerRegistryManager.RuntimeBaseImage, new[] { DockerRegistryManager.Net6ImageTag }); diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs index 9dc7db0b6586..d78b88cc211b 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs @@ -46,7 +46,7 @@ public async Task ApiEndToEndWithRegistryPushAndPull() // Build the image - Registry registry = new Registry(DockerRegistryManager.LocalRegistry, logger); + Registry registry = new(DockerRegistryManager.LocalRegistry, logger, RegistryMode.Push); ImageBuilder imageBuilder = await registry.GetImageManifestAsync( DockerRegistryManager.RuntimeBaseImage, @@ -93,7 +93,7 @@ public async Task ApiEndToEndWithLocalLoad() // Build the image - Registry registry = new Registry(DockerRegistryManager.LocalRegistry, logger); + Registry registry = new(DockerRegistryManager.LocalRegistry, logger, RegistryMode.Push); ImageBuilder imageBuilder = await registry.GetImageManifestAsync( DockerRegistryManager.RuntimeBaseImage, @@ -134,7 +134,7 @@ public async Task ApiEndToEndWithArchiveWritingAndLoad() // Build the image - Registry registry = new Registry(DockerRegistryManager.LocalRegistry, logger); + Registry registry = new(DockerRegistryManager.LocalRegistry, logger, RegistryMode.Push); ImageBuilder imageBuilder = await registry.GetImageManifestAsync( DockerRegistryManager.RuntimeBaseImage, @@ -555,7 +555,7 @@ public async Task CanPackageForAllSupportedContainerRIDs(string dockerPlatform, string publishDirectory = BuildLocalApp(tfm: ToolsetInfo.CurrentTargetFramework, rid: rid); // Build the image - Registry registry = new(DockerRegistryManager.BaseImageSource, logger); + Registry registry = new(DockerRegistryManager.BaseImageSource, logger, RegistryMode.Push); var isWin = rid.StartsWith("win"); ImageBuilder? imageBuilder = await registry.GetImageManifestAsync( DockerRegistryManager.RuntimeBaseImage, diff --git a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/RegistryTests.cs b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/RegistryTests.cs index 0f58b21e98ec..814892354356 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/RegistryTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/RegistryTests.cs @@ -39,7 +39,7 @@ public async Task CanReadManifestFromRegistry(string fullyQualifiedContainerName containerTag ??= "latest"; ILogger logger = _loggerFactory.CreateLogger(nameof(CanReadManifestFromRegistry)); - Registry registry = new Registry(containerRegistry, logger); + Registry registry = new(containerRegistry, logger, RegistryMode.Pull); var ridgraphfile = ToolsetUtils.GetRuntimeGraphFilePath(); diff --git a/src/Tests/Microsoft.NET.Build.Containers.UnitTests/AuthHandshakeMessageHandlerTests.cs b/src/Tests/Microsoft.NET.Build.Containers.UnitTests/AuthHandshakeMessageHandlerTests.cs new file mode 100644 index 000000000000..9e17b99e56bc --- /dev/null +++ b/src/Tests/Microsoft.NET.Build.Containers.UnitTests/AuthHandshakeMessageHandlerTests.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.NET.Build.Containers.UnitTests +{ + public class AuthHandshakeMessageHandlerTests + { + [Theory] + [InlineData("SDK_CONTAINER_REGISTRY_UNAME", "SDK_CONTAINER_REGISTRY_PWORD", (int)RegistryMode.Push)] + [InlineData("DOTNET_CONTAINER_PUSH_REGISTRY_UNAME", "DOTNET_CONTAINER_PUSH_REGISTRY_PWORD", (int)RegistryMode.Push)] + [InlineData("DOTNET_CONTAINER_PULL_REGISTRY_UNAME", "DOTNET_CONTAINER_PULL_REGISTRY_PWORD", (int)RegistryMode.Pull)] + [InlineData("DOTNET_CONTAINER_PULL_REGISTRY_UNAME", "DOTNET_CONTAINER_PULL_REGISTRY_PWORD", (int)RegistryMode.PullFromOutput)] + [InlineData("SDK_CONTAINER_REGISTRY_UNAME", "SDK_CONTAINER_REGISTRY_PWORD", (int)RegistryMode.PullFromOutput)] + public void GetDockerCredentialsFromEnvironment_ReturnsCorrectValues(string unameVarName, string pwordVarName, int mode) + { + string? originalUnameValue = Environment.GetEnvironmentVariable(unameVarName); + string? originalPwordValue = Environment.GetEnvironmentVariable(pwordVarName); + + Environment.SetEnvironmentVariable(unameVarName, "uname"); + Environment.SetEnvironmentVariable(pwordVarName, "pword"); + + if (AuthHandshakeMessageHandler.GetDockerCredentialsFromEnvironment((RegistryMode)mode) is (string credU, string credP)) + { + Assert.Equal("uname", credU); + Assert.Equal("pword", credP); + } + else + { + Assert.Fail("Should have parsed credentials from environment"); + } + + + // restore env variable values + Environment.SetEnvironmentVariable(unameVarName, originalUnameValue); + Environment.SetEnvironmentVariable(pwordVarName, originalPwordValue); + } + } +} diff --git a/src/Tests/Microsoft.NET.Build.Containers.UnitTests/RegistryTests.cs b/src/Tests/Microsoft.NET.Build.Containers.UnitTests/RegistryTests.cs index d01aab77d77b..51fad494918f 100644 --- a/src/Tests/Microsoft.NET.Build.Containers.UnitTests/RegistryTests.cs +++ b/src/Tests/Microsoft.NET.Build.Containers.UnitTests/RegistryTests.cs @@ -35,7 +35,7 @@ public void Dispose() public void CheckIfGoogleArtifactRegistry(string registryName, bool isECR) { ILogger logger = _loggerFactory.CreateLogger(nameof(CheckIfGoogleArtifactRegistry)); - Registry registry = new Registry(registryName, logger); + Registry registry = new(registryName, logger, RegistryMode.Push); Assert.Equal(isECR, registry.IsGoogleArtifactRegistry); } @@ -43,7 +43,7 @@ public void CheckIfGoogleArtifactRegistry(string registryName, bool isECR) public void DockerIoAlias() { ILogger logger = _loggerFactory.CreateLogger(nameof(DockerIoAlias)); - Registry registry = new Registry("docker.io", logger); + Registry registry = new("docker.io", logger, RegistryMode.Push); Assert.True(registry.IsDockerHub); Assert.Equal("docker.io", registry.RegistryName); Assert.Equal("registry-1.docker.io", registry.BaseUri.Host); @@ -456,7 +456,7 @@ public async Task InsecureRegistry(bool isInsecureRegistry, bool serverIsHttps, { IsInsecure = isInsecureRegistry }; - Registry registry = new(registryUri, logger, settings: settings); + Registry registry = new(registryUri, logger, RegistryMode.Pull, settings: settings); // Make a request. Task getManifest = registry.GetImageManifestAsync(repositoryName: "dotnet/runtime", reference: "latest", runtimeIdentifier: "linux-x64", manifestPicker: null!, cancellationToken: default!);