From 5ea3404e08700fe7393e5d0d9bfd494f8a908258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vlastimil=20Kalu=C5=BEa?= Date: Wed, 3 Aug 2022 13:34:41 +0200 Subject: [PATCH] feat(#370): Used env variables from new custom configurations --- .../MTlsEndpointAuthenticationProvider.cs | 25 +++-- .../TlsEndpointAuthenticationProvider.cs | 23 ++-- .../Configurations/CustomConfiguration.cs | 63 +++++++++-- .../EnvironmentConfiguration.cs | 45 +++++++- .../Configurations/ICustomConfiguration.cs | 34 +++++- .../PropertiesFileConfiguration.cs | 29 ++++- .../Configurations/CustomConfigurationTest.cs | 106 ++++++++++++++++-- 7 files changed, 269 insertions(+), 56 deletions(-) diff --git a/src/Testcontainers/Builders/MTlsEndpointAuthenticationProvider.cs b/src/Testcontainers/Builders/MTlsEndpointAuthenticationProvider.cs index 49a6edd0f..7a0766a1f 100644 --- a/src/Testcontainers/Builders/MTlsEndpointAuthenticationProvider.cs +++ b/src/Testcontainers/Builders/MTlsEndpointAuthenticationProvider.cs @@ -27,8 +27,7 @@ internal sealed class MTlsEndpointAuthenticationProvider : DockerEndpointAuthent private static readonly Regex PemData = new Regex("-----BEGIN (.*)-----(.*)-----END (.*)-----", RegexOptions.Multiline); private readonly Uri dockerEngine; - private readonly bool dockerTlsVerifyEnabled; - private readonly string dockerCaCertFile; + private readonly bool? dockerTlsVerifyEnabled; private readonly string dockerClientCertFile; private readonly string dockerClientKeyFile; private readonly Lazy caCertificate; @@ -36,24 +35,26 @@ internal sealed class MTlsEndpointAuthenticationProvider : DockerEndpointAuthent public MTlsEndpointAuthenticationProvider() { - var dockerHostValue = Environment.GetEnvironmentVariable("DOCKER_HOST"); - var dockerTlsVerifyValue = Environment.GetEnvironmentVariable("DOCKER_TLS_VERIFY"); - var dockerCertPathValue = Environment.GetEnvironmentVariable("DOCKER_CERT_PATH"); - var dockerCertPath = dockerCertPathValue ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), DefaultCertPath); - - this.dockerEngine = Uri.TryCreate(dockerHostValue, UriKind.RelativeOrAbsolute, out var dockerHost) ? dockerHost : DefaultTlsDockerEndpoint; - this.dockerTlsVerifyEnabled = int.TryParse(dockerTlsVerifyValue, out var dockerTlsVerify) && dockerTlsVerify == 1; - this.dockerCaCertFile = Path.Combine(dockerCertPath, DefaultCaCertFileName); + ICustomConfiguration propertiesFileConfiguration = new PropertiesFileConfiguration(); + ICustomConfiguration environmentConfiguration = new EnvironmentConfiguration(); + + var dockerCertPath = propertiesFileConfiguration.GetDockerCertPath() + ?? environmentConfiguration.GetDockerCertPath() + ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), DefaultCertPath); + var dockerCaCertFile = Path.Combine(dockerCertPath, DefaultCaCertFileName); + + this.dockerEngine = propertiesFileConfiguration.GetDockerHost() ?? environmentConfiguration.GetDockerHost() ?? DefaultTlsDockerEndpoint; + this.dockerTlsVerifyEnabled = propertiesFileConfiguration.GetDockerTlsVerify() ?? environmentConfiguration.GetDockerTlsVerify(); this.dockerClientCertFile = Path.Combine(dockerCertPath, DefaultClientCertFileName); this.dockerClientKeyFile = Path.Combine(dockerCertPath, DefaultClientKeyFileName); - this.caCertificate = new Lazy(() => new MSX509.X509Certificate2(this.dockerCaCertFile)); + this.caCertificate = new Lazy(() => new MSX509.X509Certificate2(dockerCaCertFile)); this.clientCertificate = new Lazy(this.GetClientCertificate); } /// public override bool IsApplicable() { - return this.dockerTlsVerifyEnabled && File.Exists(this.dockerClientCertFile) && File.Exists(this.dockerClientKeyFile); + return this.dockerTlsVerifyEnabled.HasValue && this.dockerTlsVerifyEnabled.Value && File.Exists(this.dockerClientCertFile) && File.Exists(this.dockerClientKeyFile); } /// diff --git a/src/Testcontainers/Builders/TlsEndpointAuthenticationProvider.cs b/src/Testcontainers/Builders/TlsEndpointAuthenticationProvider.cs index 6b8fabbed..47005972f 100644 --- a/src/Testcontainers/Builders/TlsEndpointAuthenticationProvider.cs +++ b/src/Testcontainers/Builders/TlsEndpointAuthenticationProvider.cs @@ -16,27 +16,28 @@ internal sealed class TlsEndpointAuthenticationProvider : DockerEndpointAuthenti private static readonly Uri DefaultTlsDockerEndpoint = new Uri("tcp://localhost:2376"); private readonly Uri dockerEngine; - private readonly bool dockerTlsEnabled; - private readonly string dockerCaCertFile; + private readonly bool? dockerTlsEnabled; private readonly Lazy caCertificate; public TlsEndpointAuthenticationProvider() { - var dockerHostValue = Environment.GetEnvironmentVariable("DOCKER_HOST"); - var dockerTlsValue = Environment.GetEnvironmentVariable("DOCKER_TLS"); - var dockerCertPathValue = Environment.GetEnvironmentVariable("DOCKER_CERT_PATH"); - var dockerCertPath = dockerCertPathValue ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), DefaultCertPath); + ICustomConfiguration propertiesFileConfiguration = new PropertiesFileConfiguration(); + ICustomConfiguration environmentConfiguration = new EnvironmentConfiguration(); - this.dockerEngine = Uri.TryCreate(dockerHostValue, UriKind.RelativeOrAbsolute, out var dockerHost) ? dockerHost : DefaultTlsDockerEndpoint; - this.dockerTlsEnabled = int.TryParse(dockerTlsValue, out var dockerTls) && dockerTls == 1; - this.dockerCaCertFile = Path.Combine(dockerCertPath, DefaultCaCertFileName); - this.caCertificate = new Lazy(() => new X509Certificate2(this.dockerCaCertFile)); + var dockerCertPath = propertiesFileConfiguration.GetDockerCertPath() + ?? environmentConfiguration.GetDockerCertPath() + ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), DefaultCertPath); + var dockerCaCertFile = Path.Combine(dockerCertPath, DefaultCaCertFileName); + + this.dockerEngine = propertiesFileConfiguration.GetDockerHost() ?? environmentConfiguration.GetDockerHost() ?? DefaultTlsDockerEndpoint; + this.dockerTlsEnabled = propertiesFileConfiguration.GetDockerTls() ?? environmentConfiguration.GetDockerTls(); + this.caCertificate = new Lazy(() => new X509Certificate2(dockerCaCertFile)); } /// public override bool IsApplicable() { - return this.dockerTlsEnabled; + return this.dockerTlsEnabled.HasValue && this.dockerTlsEnabled.Value; } /// diff --git a/src/Testcontainers/Configurations/CustomConfiguration.cs b/src/Testcontainers/Configurations/CustomConfiguration.cs index 8c722a918..9eb7f649b 100644 --- a/src/Testcontainers/Configurations/CustomConfiguration.cs +++ b/src/Testcontainers/Configurations/CustomConfiguration.cs @@ -1,7 +1,8 @@ -namespace DotNet.Testcontainers.Configurations +namespace DotNet.Testcontainers.Configurations { using System; using System.Collections.Generic; + using System.Globalization; using System.Text.Json; using DotNet.Testcontainers.Images; @@ -14,6 +15,37 @@ protected CustomConfiguration(IReadOnlyDictionary properties) this.properties = properties; } + protected JsonDocument GetDockerAuthConfig(string propertyName) + { + _ = this.properties.TryGetValue(propertyName, out var propertyValue); + + if (string.IsNullOrEmpty(propertyValue)) + { + return null; + } + + try + { + return JsonDocument.Parse(propertyValue); + } + catch (Exception) + { + return null; + } + } + + protected string GetDockerCertPath(string propertyName) + { + _ = this.properties.TryGetValue(propertyName, out var propertyValue); + + if (string.IsNullOrWhiteSpace(propertyValue)) + { + return null; + } + + return propertyValue; + } + protected string GetDockerConfig(string propertyName) { _ = this.properties.TryGetValue(propertyName, out var propertyValue); @@ -25,23 +57,38 @@ protected Uri GetDockerHost(string propertyName) return this.properties.TryGetValue(propertyName, out var propertyValue) && Uri.TryCreate(propertyValue, UriKind.RelativeOrAbsolute, out var dockerHost) ? dockerHost : null; } - protected JsonDocument GetDockerAuthConfig(string propertyName) + protected bool? GetDockerTls(string propertyName) { - _ = this.properties.TryGetValue(propertyName, out var propertyValue); - - if (string.IsNullOrEmpty(propertyValue)) + if (!this.properties.TryGetValue(propertyName, out var propertyValue) || string.IsNullOrWhiteSpace(propertyValue)) { return null; } - try + propertyValue = propertyValue.Trim().ToLower(CultureInfo.InvariantCulture); + + if (bool.TryParse(propertyValue, out var dockerTls)) { - return JsonDocument.Parse(propertyValue); + return dockerTls; } - catch (Exception) + + return propertyValue == "1"; + } + + protected bool? GetDockerTlsVerify(string propertyName) + { + if (!this.properties.TryGetValue(propertyName, out var propertyValue) || string.IsNullOrWhiteSpace(propertyValue)) { return null; } + + propertyValue = propertyValue.Trim().ToLower(CultureInfo.InvariantCulture); + + if (bool.TryParse(propertyValue, out var dockerTlsVerify)) + { + return dockerTlsVerify; + } + + return propertyValue == "1"; } protected bool GetRyukDisabled(string propertyName) diff --git a/src/Testcontainers/Configurations/EnvironmentConfiguration.cs b/src/Testcontainers/Configurations/EnvironmentConfiguration.cs index d99b55926..12c80d1df 100644 --- a/src/Testcontainers/Configurations/EnvironmentConfiguration.cs +++ b/src/Testcontainers/Configurations/EnvironmentConfiguration.cs @@ -1,4 +1,4 @@ -namespace DotNet.Testcontainers.Configurations +namespace DotNet.Testcontainers.Configurations { using System; using System.Linq; @@ -10,11 +10,17 @@ /// internal sealed class EnvironmentConfiguration : CustomConfiguration, ICustomConfiguration { + private const string DockerAuthConfig = "DOCKER_AUTH_CONFIG"; + + private const string DockerCertPath = "DOCKER_CERT_PATH"; + private const string DockerConfig = "DOCKER_CONFIG"; private const string DockerHost = "DOCKER_HOST"; - private const string DockerAuthConfig = "DOCKER_AUTH_CONFIG"; + private const string DockerTls = "DOCKER_TLS"; + + private const string DockerTlsVerify = "DOCKER_TLS_VERIFY"; private const string RyukDisabled = "TESTCONTAINERS_RYUK_DISABLED"; @@ -30,7 +36,18 @@ static EnvironmentConfiguration() /// Initializes a new instance of the class. /// public EnvironmentConfiguration() - : base(new[] { DockerConfig, DockerHost, DockerAuthConfig, RyukDisabled, RyukContainerImage, HubImageNamePrefix } + : base(new[] + { + DockerAuthConfig, + DockerCertPath, + DockerConfig, + DockerHost, + DockerTls, + DockerTlsVerify, + RyukDisabled, + RyukContainerImage, + HubImageNamePrefix, + } .ToDictionary(key => key, Environment.GetEnvironmentVariable)) { } @@ -41,6 +58,18 @@ public EnvironmentConfiguration() public static ICustomConfiguration Instance { get; } = new EnvironmentConfiguration(); + /// + public JsonDocument GetDockerAuthConfig() + { + return this.GetDockerAuthConfig(DockerAuthConfig); + } + + /// + public string GetDockerCertPath() + { + return this.GetDockerCertPath(DockerCertPath); + } + /// public string GetDockerConfig() { @@ -54,9 +83,15 @@ public Uri GetDockerHost() } /// - public JsonDocument GetDockerAuthConfig() + public bool? GetDockerTls() { - return this.GetDockerAuthConfig(DockerAuthConfig); + return this.GetDockerTls(DockerTls); + } + + /// + public bool? GetDockerTlsVerify() + { + return this.GetDockerTlsVerify(DockerTlsVerify); } /// diff --git a/src/Testcontainers/Configurations/ICustomConfiguration.cs b/src/Testcontainers/Configurations/ICustomConfiguration.cs index 9256cb884..ef93fcb85 100644 --- a/src/Testcontainers/Configurations/ICustomConfiguration.cs +++ b/src/Testcontainers/Configurations/ICustomConfiguration.cs @@ -1,4 +1,4 @@ -namespace DotNet.Testcontainers.Configurations +namespace DotNet.Testcontainers.Configurations { using System; using System.Text.Json; @@ -10,6 +10,22 @@ /// internal interface ICustomConfiguration { + /// + /// Gets the Docker registry authentication custom configuration. + /// + /// The Docker authentication custom configuration. + /// https://docs.gitlab.com/ee/ci/docker/using_docker_images.html#access-an-image-from-a-private-container-registry. + [CanBeNull] + JsonDocument GetDockerAuthConfig(); + + /// + /// Gets the Docker location of your authentication keys and host certificate. + /// + /// The Docker location of your authentication keys and host certificate. + /// https://www.testcontainers.org/features/configuration/#customizing-docker-host-detection. + [CanBeNull] + string GetDockerCertPath(); + /// /// Gets the Docker config custom configuration. /// @@ -27,12 +43,18 @@ internal interface ICustomConfiguration Uri GetDockerHost(); /// - /// Gets the Docker registry authentication custom configuration. + /// Gets the Docker uses TLS. /// - /// The Docker authentication custom configuration. - /// https://docs.gitlab.com/ee/ci/docker/using_docker_images.html#access-an-image-from-a-private-container-registry. - [CanBeNull] - JsonDocument GetDockerAuthConfig(); + /// The Docker uses TLS. + /// https://www.testcontainers.org/features/configuration/#customizing-docker-host-detection. + bool? GetDockerTls(); + + /// + /// Gets the Docker uses TLS and verifies the remote. + /// + /// The Docker uses TLS and verifies the remote. + /// https://www.testcontainers.org/features/configuration/#customizing-docker-host-detection. + bool? GetDockerTlsVerify(); /// /// Gets the Ryuk disabled custom configuration. diff --git a/src/Testcontainers/Configurations/PropertiesFileConfiguration.cs b/src/Testcontainers/Configurations/PropertiesFileConfiguration.cs index 1548425f9..f4fed505c 100644 --- a/src/Testcontainers/Configurations/PropertiesFileConfiguration.cs +++ b/src/Testcontainers/Configurations/PropertiesFileConfiguration.cs @@ -1,4 +1,4 @@ -namespace DotNet.Testcontainers.Configurations +namespace DotNet.Testcontainers.Configurations { using System; using System.IO; @@ -56,6 +56,20 @@ public PropertiesFileConfiguration(params string[] lines) public static ICustomConfiguration Instance { get; } = new PropertiesFileConfiguration(); + /// + public JsonDocument GetDockerAuthConfig() + { + const string propertyName = "docker.auth.config"; + return this.GetDockerAuthConfig(propertyName); + } + + /// + public string GetDockerCertPath() + { + const string propertyName = "docker.cert.path"; + return this.GetDockerCertPath(propertyName); + } + /// public string GetDockerConfig() { @@ -71,10 +85,17 @@ public Uri GetDockerHost() } /// - public JsonDocument GetDockerAuthConfig() + public bool? GetDockerTls() { - const string propertyName = "docker.auth.config"; - return this.GetDockerAuthConfig(propertyName); + const string propertyName = "docker.tls"; + return this.GetDockerTls(propertyName); + } + + /// + public bool? GetDockerTlsVerify() + { + const string propertyName = "docker.tls.verify"; + return this.GetDockerTlsVerify(propertyName); } /// diff --git a/tests/Testcontainers.Tests/Unit/Configurations/CustomConfigurationTest.cs b/tests/Testcontainers.Tests/Unit/Configurations/CustomConfigurationTest.cs index f4fd8f020..1730f1126 100644 --- a/tests/Testcontainers.Tests/Unit/Configurations/CustomConfigurationTest.cs +++ b/tests/Testcontainers.Tests/Unit/Configurations/CustomConfigurationTest.cs @@ -1,4 +1,4 @@ -namespace DotNet.Testcontainers.Tests.Unit +namespace DotNet.Testcontainers.Tests.Unit { using System; using System.Collections.Generic; @@ -15,14 +15,43 @@ public sealed class EnvironmentConfigurationTest : IDisposable static EnvironmentConfigurationTest() { + EnvironmentVariables.Add("DOCKER_AUTH_CONFIG"); + EnvironmentVariables.Add("DOCKER_CERT_PATH"); EnvironmentVariables.Add("DOCKER_CONFIG"); EnvironmentVariables.Add("DOCKER_HOST"); - EnvironmentVariables.Add("DOCKER_AUTH_CONFIG"); + EnvironmentVariables.Add("DOCKER_TLS"); + EnvironmentVariables.Add("DOCKER_TLS_VERIFY"); EnvironmentVariables.Add("TESTCONTAINERS_RYUK_DISABLED"); EnvironmentVariables.Add("TESTCONTAINERS_RYUK_CONTAINER_IMAGE"); EnvironmentVariables.Add("TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX"); } + [Theory] + [InlineData("", "", null)] + [InlineData("DOCKER_AUTH_CONFIG", "", null)] + [InlineData("DOCKER_AUTH_CONFIG", "{jsonReaderException}", null)] + [InlineData("DOCKER_AUTH_CONFIG", "{}", "{}")] + [InlineData("DOCKER_AUTH_CONFIG", "{\"auths\":null}", "{\"auths\":null}")] + [InlineData("DOCKER_AUTH_CONFIG", "{\"auths\":{}}", "{\"auths\":{}}")] + [InlineData("DOCKER_AUTH_CONFIG", "{\"auths\":{\"ghcr.io\":{}}}", "{\"auths\":{\"ghcr.io\":{}}}")] + public void GetDockerAuthConfigCustomConfiguration(string propertyName, string propertyValue, string expected) + { + SetEnvironmentVariable(propertyName, propertyValue); + ICustomConfiguration customConfiguration = new EnvironmentConfiguration(); + Assert.Equal(expected, customConfiguration.GetDockerAuthConfig()?.RootElement.ToString()); + } + + [Theory] + [InlineData("", "", null)] + [InlineData("DOCKER_CERT_PATH", "", null)] + [InlineData("DOCKER_CERT_PATH", "/home/docker/.docker/certs", "/home/docker/.docker/certs")] + public void GetDockerCertPathCustomConfiguration(string propertyName, string propertyValue, string expected) + { + SetEnvironmentVariable(propertyName, propertyValue); + ICustomConfiguration customConfiguration = new EnvironmentConfiguration(); + Assert.Equal(expected, customConfiguration.GetDockerCertPath()); + } + [Theory] [InlineData("", "", null)] [InlineData("DOCKER_CONFIG", "", null)] @@ -47,17 +76,34 @@ public void GetDockerHostCustomConfiguration(string propertyName, string propert [Theory] [InlineData("", "", null)] - [InlineData("DOCKER_AUTH_CONFIG", "", null)] - [InlineData("DOCKER_AUTH_CONFIG", "{jsonReaderException}", null)] - [InlineData("DOCKER_AUTH_CONFIG", "{}", "{}")] - [InlineData("DOCKER_AUTH_CONFIG", "{\"auths\":null}", "{\"auths\":null}")] - [InlineData("DOCKER_AUTH_CONFIG", "{\"auths\":{}}", "{\"auths\":{}}")] - [InlineData("DOCKER_AUTH_CONFIG", "{\"auths\":{\"ghcr.io\":{}}}", "{\"auths\":{\"ghcr.io\":{}}}")] - public void GetDockerAuthConfigCustomConfiguration(string propertyName, string propertyValue, string expected) + [InlineData("DOCKER_TLS", "", null)] + [InlineData("DOCKER_TLS", "0", false)] + [InlineData("DOCKER_TLS", "false", false)] + [InlineData("DOCKER_TLS", "FALSE", false)] + [InlineData("DOCKER_TLS", "1", true)] + [InlineData("DOCKER_TLS", "TRUE", true)] + [InlineData("DOCKER_TLS", "true", true)] + public void GetDockerTlsCustomConfiguration(string propertyName, string propertyValue, bool? expected) { SetEnvironmentVariable(propertyName, propertyValue); ICustomConfiguration customConfiguration = new EnvironmentConfiguration(); - Assert.Equal(expected, customConfiguration.GetDockerAuthConfig()?.RootElement.ToString()); + Assert.Equal(expected, customConfiguration.GetDockerTls()); + } + + [Theory] + [InlineData("", "", null)] + [InlineData("DOCKER_TLS_VERIFY", "", null)] + [InlineData("DOCKER_TLS_VERIFY", "0", false)] + [InlineData("DOCKER_TLS_VERIFY", "false", false)] + [InlineData("DOCKER_TLS_VERIFY", "FALSE", false)] + [InlineData("DOCKER_TLS_VERIFY", "1", true)] + [InlineData("DOCKER_TLS_VERIFY", "TRUE", true)] + [InlineData("DOCKER_TLS_VERIFY", "true", true)] + public void GetDockerTlsVerifyCustomConfiguration(string propertyName, string propertyValue, bool? expected) + { + SetEnvironmentVariable(propertyName, propertyValue); + ICustomConfiguration customConfiguration = new EnvironmentConfiguration(); + Assert.Equal(expected, customConfiguration.GetDockerTlsVerify()); } [Theory] @@ -147,6 +193,46 @@ public void GetDockerAuthConfigCustomConfiguration(string configuration, string Assert.Equal(expected, customConfiguration.GetDockerAuthConfig()?.RootElement.ToString()); } + [Theory] + [InlineData("", null)] + [InlineData("docker.tls=", null)] + [InlineData("docker.tls=0", false)] + [InlineData("docker.tls=false", false)] + [InlineData("docker.tls=FALSE", false)] + [InlineData("docker.tls=1", true)] + [InlineData("docker.tls=TRUE", true)] + [InlineData("docker.tls=true", true)] + public void GetDockerTlsCustomConfiguration(string configuration, bool? expected) + { + ICustomConfiguration customConfiguration = new PropertiesFileConfiguration(new[] { configuration }); + Assert.Equal(expected, customConfiguration.GetDockerTls()); + } + + [Theory] + [InlineData("", null)] + [InlineData("docker.tls.verify=", null)] + [InlineData("docker.tls.verify=0", false)] + [InlineData("docker.tls.verify=false", false)] + [InlineData("docker.tls.verify=FALSE", false)] + [InlineData("docker.tls.verify=1", true)] + [InlineData("docker.tls.verify=TRUE", true)] + [InlineData("docker.tls.verify=true", true)] + public void GetDockerTlsVerifyCustomConfiguration(string configuration, bool? expected) + { + ICustomConfiguration customConfiguration = new PropertiesFileConfiguration(new[] { configuration }); + Assert.Equal(expected, customConfiguration.GetDockerTlsVerify()); + } + + [Theory] + [InlineData("", null)] + [InlineData("docker.cert.path=", null)] + [InlineData("docker.cert.path=/home/docker/.docker/certs", "/home/docker/.docker/certs")] + public void GetDockerCertPathCustomConfiguration(string configuration, string expected) + { + ICustomConfiguration customConfiguration = new PropertiesFileConfiguration(new[] { configuration }); + Assert.Equal(expected, customConfiguration.GetDockerCertPath()); + } + [Theory] [InlineData("", false)] [InlineData("ryuk.disabled=", false)]