Skip to content

Commit 579d530

Browse files
committed
Throw the new DockerUnavailableException when Docker is not available
(because it is either not running or misconfigured) The `ArgumentException` was not the appropriate exception to throw since the root of the problem is not a bad argument but the state of the system which is wrong.
1 parent 4c24aab commit 579d530

File tree

4 files changed

+53
-19
lines changed

4 files changed

+53
-19
lines changed

src/Testcontainers/Builders/AbstractBuilder`4.cs

+9-3
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,15 @@ protected virtual void Validate()
141141
_ = Guard.Argument(DockerResourceConfiguration.Logger, nameof(IResourceConfiguration<TCreateResourceEntity>.Logger))
142142
.NotNull();
143143

144-
const string containerRuntimeNotFound = "Docker is either not running or misconfigured. Please ensure that Docker is running and that the endpoint is properly configured. You can customize your configuration using either the environment variables or the ~/.testcontainers.properties file. For more information, visit:\nhttps://dotnet.testcontainers.org/custom_configuration/";
145-
_ = Guard.Argument(DockerResourceConfiguration.DockerEndpointAuthConfig, nameof(IResourceConfiguration<TCreateResourceEntity>.DockerEndpointAuthConfig))
146-
.ThrowIf(argument => argument.Value == null, argument => new ArgumentException(containerRuntimeNotFound, argument.Name));
144+
if (DockerResourceConfiguration.DockerEndpointAuthConfig == null)
145+
{
146+
var message = TestcontainersSettings.UnavailableEndpoints.Count == 0
147+
? "Docker is either not running or misconfigured. Please ensure that Docker is running and that the endpoint is properly configured."
148+
: $"Docker is either not running or misconfigured. Please ensure that Docker is available at {string.Join(" or ", TestcontainersSettings.UnavailableEndpoints)}";
149+
150+
throw new DockerUnavailableException(message + "\nYou can customize your configuration using either the environment variables or the ~/.testcontainers.properties file. " +
151+
"For more information, visit:\nhttps://dotnet.testcontainers.org/custom_configuration/");
152+
}
147153

148154
const string reuseNotSupported = "Reuse cannot be used in conjunction with WithCleanUp(true).";
149155
_ = Guard.Argument(DockerResourceConfiguration, nameof(IResourceConfiguration<TCreateResourceEntity>.Reuse))

src/Testcontainers/Builders/DockerEndpointAuthenticationProvider.cs

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ namespace DotNet.Testcontainers.Builders
33
using System;
44
using System.Threading;
55
using System.Threading.Tasks;
6+
using JetBrains.Annotations;
67
using DotNet.Testcontainers.Configurations;
78
using DotNet.Testcontainers.Containers;
89

@@ -11,6 +12,9 @@ internal class DockerEndpointAuthenticationProvider : IDockerEndpointAuthenticat
1112
{
1213
private static readonly TaskFactory TaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
1314

15+
[CanBeNull]
16+
public Uri UnavailableEndpoint;
17+
1418
/// <inheritdoc />
1519
public virtual bool IsApplicable()
1620
{
@@ -42,6 +46,7 @@ await dockerClient.System.PingAsync()
4246
}
4347
catch (Exception)
4448
{
49+
UnavailableEndpoint = dockerClientConfiguration.EndpointBaseUri;
4550
return false;
4651
}
4752
}

src/Testcontainers/Configurations/TestcontainersSettings.cs

+19-16
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,30 @@ namespace DotNet.Testcontainers.Configurations
1818
public static class TestcontainersSettings
1919
{
2020
[CanBeNull]
21-
private static readonly IDockerEndpointAuthenticationProvider DockerEndpointAuthProvider
22-
= new IDockerEndpointAuthenticationProvider[]
23-
{
24-
new TestcontainersEndpointAuthenticationProvider(),
25-
new MTlsEndpointAuthenticationProvider(),
26-
new TlsEndpointAuthenticationProvider(),
27-
new EnvironmentEndpointAuthenticationProvider(),
28-
new NpipeEndpointAuthenticationProvider(),
29-
new UnixEndpointAuthenticationProvider(),
30-
new DockerDesktopEndpointAuthenticationProvider(),
31-
new RootlessUnixEndpointAuthenticationProvider(),
32-
}
33-
.Where(authProvider => authProvider.IsApplicable())
34-
.FirstOrDefault(authProvider => authProvider.IsAvailable());
21+
private static readonly IDockerEndpointAuthenticationProvider DockerEndpointAuthProvider;
3522

3623
[CanBeNull]
37-
private static readonly IDockerEndpointAuthenticationConfiguration DockerEndpointAuthConfig
38-
= DockerEndpointAuthProvider?.GetAuthConfig();
24+
private static readonly IDockerEndpointAuthenticationConfiguration DockerEndpointAuthConfig;
25+
26+
internal static readonly IReadOnlyList<Uri> UnavailableEndpoints;
3927

4028
static TestcontainersSettings()
4129
{
30+
var providers = new IDockerEndpointAuthenticationProvider[]
31+
{
32+
new TestcontainersEndpointAuthenticationProvider(),
33+
new MTlsEndpointAuthenticationProvider(),
34+
new TlsEndpointAuthenticationProvider(),
35+
new EnvironmentEndpointAuthenticationProvider(),
36+
new NpipeEndpointAuthenticationProvider(),
37+
new UnixEndpointAuthenticationProvider(),
38+
new DockerDesktopEndpointAuthenticationProvider(),
39+
new RootlessUnixEndpointAuthenticationProvider(),
40+
};
41+
42+
DockerEndpointAuthProvider = providers.Where(authProvider => authProvider.IsApplicable()).FirstOrDefault(authProvider => authProvider.IsAvailable());
43+
DockerEndpointAuthConfig = DockerEndpointAuthProvider?.GetAuthConfig();
44+
UnavailableEndpoints = providers.OfType<DockerEndpointAuthenticationProvider>().Select(e => e.UnavailableEndpoint).Where(e => e != null).Distinct().ToList();
4245
}
4346

4447
/// <summary>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace DotNet.Testcontainers
2+
{
3+
using System;
4+
using JetBrains.Annotations;
5+
6+
/// <summary>
7+
/// The exception that is thrown when Docker is not available (because it is either not running or misconfigured).
8+
/// </summary>
9+
[PublicAPI]
10+
public sealed class DockerUnavailableException : Exception
11+
{
12+
/// <summary>
13+
/// Initializes a new instance of the <see cref="DockerUnavailableException"/> class, using the provided message.
14+
/// </summary>
15+
/// <param name="message">The error message that explains the reason for the exception.</param>
16+
public DockerUnavailableException(string message) : base(message)
17+
{
18+
}
19+
}
20+
}

0 commit comments

Comments
 (0)