Skip to content

Commit 6bc8cc6

Browse files
authored
feat: Add named pipe connection timeout custom configuration (#1480)
1 parent 63f67dc commit 6bc8cc6

File tree

10 files changed

+110
-28
lines changed

10 files changed

+110
-28
lines changed

Directory.Packages.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
<PackageVersion Include="Confluent.SchemaRegistry.Serdes.Json" Version="2.8.0"/>
5252
<PackageVersion Include="Confluent.SchemaRegistry" Version="2.8.0"/>
5353
<PackageVersion Include="Consul" Version="1.6.10.9"/>
54-
<PackageVersion Include="CouchbaseNetClient" Version="3.6.4"/>
54+
<PackageVersion Include="CouchbaseNetClient" Version="3.7.2"/>
5555
<PackageVersion Include="DotPulsar" Version="3.6.0"/>
5656
<PackageVersion Include="Elastic.Clients.Elasticsearch" Version="8.16.3"/>
5757
<PackageVersion Include="EventStore.Client.Grpc.Streams" Version="22.0.0"/>

docs/custom_configuration/index.md

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,25 @@
22

33
Testcontainers supports various configurations to set up your test environment. It automatically discovers the Docker environment and applies the configuration. You can set or override the default values either with the Testcontainers [properties file][properties-file-format] (`~/.testcontainers.properties`) or with environment variables. The following configurations are available:
44

5-
| Properties File | Environment Variable | Description | Default |
6-
|-----------------------------|--------------------------------------------|---------------------------------------------------------------------------------------------------------------------------|-----------------------------|
7-
| `docker.config` | `DOCKER_CONFIG` | The directory path that contains the Docker configuration (`config.json`) file. | `~/.docker/` |
8-
| `docker.host` | `DOCKER_HOST` | The Docker daemon socket to connect to. | - |
9-
| `docker.context` | `DOCKER_CONTEXT` | The Docker context to connect to. | - |
10-
| `docker.auth.config` | `DOCKER_AUTH_CONFIG` | The Docker configuration file content (GitLab: [Use statically-defined credentials][use-statically-defined-credentials]). | - |
11-
| `docker.cert.path` | `DOCKER_CERT_PATH` | The directory path that contains the client certificate (`{ca,cert,key}.pem`) files. | `~/.docker/` |
12-
| `docker.tls` | `DOCKER_TLS` | Enables TLS. | `false` |
13-
| `docker.tls.verify` | `DOCKER_TLS_VERIFY` | Enables TLS verify. | `false` |
14-
| `host.override` | `TESTCONTAINERS_HOST_OVERRIDE` | The host that exposes Docker's ports. | - |
15-
| `docker.socket.override` | `TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE` | The file path to the Docker daemon socket that is used by Ryuk (resource reaper). | `/var/run/docker.sock` |
16-
| `ryuk.disabled` | `TESTCONTAINERS_RYUK_DISABLED` | Disables Ryuk (resource reaper). | `false` |
17-
| `ryuk.container.privileged` | `TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED` | Runs Ryuk (resource reaper) in privileged mode. | `true` |
18-
| `ryuk.container.image` | `TESTCONTAINERS_RYUK_CONTAINER_IMAGE` | The Ryuk (resource reaper) Docker image. | `testcontainers/ryuk:0.5.1` |
19-
| `hub.image.name.prefix` | `TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX` | The name to use for substituting the Docker Hub registry part of the image name. | - |
20-
| `wait.strategy.retries` | `TESTCONTAINERS_WAIT_STRATEGY_RETRIES` | The wait strategy retry count. | `infinite` |
21-
| `wait.strategy.interval` | `TESTCONTAINERS_WAIT_STRATEGY_INTERVAL` | The wait strategy interval<sup>1</sup>. | `00:00:01` |
22-
| `wait.strategy.timeout` | `TESTCONTAINERS_WAIT_STRATEGY_TIMEOUT` | The wait strategy timeout<sup>1</sup>. | `01:00:00` |
5+
| Properties File | Environment Variable | Description | Default |
6+
|---------------------------------|------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------|------------------------------|
7+
| `docker.config` | `DOCKER_CONFIG` | The directory path that contains the Docker configuration (`config.json`) file. | `~/.docker/` |
8+
| `docker.host` | `DOCKER_HOST` | The Docker daemon socket to connect to. | - |
9+
| `docker.context` | `DOCKER_CONTEXT` | The Docker context to connect to. | - |
10+
| `docker.auth.config` | `DOCKER_AUTH_CONFIG` | The Docker configuration file content (GitLab: [Use statically-defined credentials][use-statically-defined-credentials]). | - |
11+
| `docker.cert.path` | `DOCKER_CERT_PATH` | The directory path that contains the client certificate (`{ca,cert,key}.pem`) files. | `~/.docker/` |
12+
| `docker.tls` | `DOCKER_TLS` | Enables TLS. | `false` |
13+
| `docker.tls.verify` | `DOCKER_TLS_VERIFY` | Enables TLS verify. | `false` |
14+
| `host.override` | `TESTCONTAINERS_HOST_OVERRIDE` | The host that exposes Docker's ports. | - |
15+
| `docker.socket.override` | `TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE` | The file path to the Docker daemon socket that is used by Ryuk (resource reaper). | `/var/run/docker.sock` |
16+
| `ryuk.disabled` | `TESTCONTAINERS_RYUK_DISABLED` | Disables Ryuk (resource reaper). | `false` |
17+
| `ryuk.container.privileged` | `TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED` | Runs Ryuk (resource reaper) in privileged mode. | `true` |
18+
| `ryuk.container.image` | `TESTCONTAINERS_RYUK_CONTAINER_IMAGE` | The Ryuk (resource reaper) Docker image. | `testcontainers/ryuk:0.12.0` |
19+
| `hub.image.name.prefix` | `TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX` | The name to use for substituting the Docker Hub registry part of the image name. | - |
20+
| `wait.strategy.retries` | `TESTCONTAINERS_WAIT_STRATEGY_RETRIES` | The wait strategy retry count. | `infinite` |
21+
| `wait.strategy.interval` | `TESTCONTAINERS_WAIT_STRATEGY_INTERVAL` | The wait strategy interval<sup>1</sup>. | `00:00:01` |
22+
| `wait.strategy.timeout` | `TESTCONTAINERS_WAIT_STRATEGY_TIMEOUT` | The wait strategy timeout<sup>1</sup>. | `01:00:00` |
23+
| `named.pipe.connection.timeout` | `TESTCONTAINERS_NAMED_PIPE_CONNECTION_TIMEOUT` | The named pipe connection timeout<sup>1</sup>. | `00:00:01` |
2324

2425
1) The value represent the string representation of a [TimeSpan](https://learn.microsoft.com/en-us/dotnet/api/system.timespan), for example, `00:00:01` for 1 second.
2526

src/Testcontainers/Builders/DockerDesktopEndpointAuthenticationProvider.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,5 +125,11 @@ public string GetHubImageNamePrefix()
125125
{
126126
return null;
127127
}
128+
129+
/// <inheritdoc />
130+
public TimeSpan? GetNamedPipeConnectionTimeout()
131+
{
132+
return null;
133+
}
128134
}
129135
}

src/Testcontainers/Builders/TestcontainersEndpointAuthenticationProvider.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,12 @@ public string GetHubImageNamePrefix()
153153
return _customConfiguration.GetWaitStrategyTimeout();
154154
}
155155

156+
/// <inheritdoc />
157+
public TimeSpan? GetNamedPipeConnectionTimeout()
158+
{
159+
return _customConfiguration.GetNamedPipeConnectionTimeout();
160+
}
161+
156162
private sealed class TestcontainersConfiguration : PropertiesFileConfiguration
157163
{
158164
public TestcontainersConfiguration()

src/Testcontainers/Configurations/AuthConfigs/DockerEndpointAuthenticationConfiguration.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,30 +10,40 @@ namespace DotNet.Testcontainers.Configurations
1010
[PublicAPI]
1111
public readonly struct DockerEndpointAuthenticationConfiguration : IDockerEndpointAuthenticationConfiguration
1212
{
13+
// Since the static `TestcontainersSettings` class holds the detected container
14+
// runtime information from the auto-discovery mechanism, we can't add a static
15+
// `NamedPipeConnectionTimeout` property to it because that would create a
16+
// circular dependency during discovery. To fix this, we either need to split the
17+
// class or stop exposing the `TestcontainersSettings` properties publicly.
18+
// Instead, we could rely only on custom configurations via environment variables
19+
// or the properties file.
20+
private static readonly TimeSpan NamedPipeConnectionTimeout = EnvironmentConfiguration.Instance.GetNamedPipeConnectionTimeout() ?? PropertiesFileConfiguration.Instance.GetNamedPipeConnectionTimeout() ?? TimeSpan.FromSeconds(1);
21+
1322
/// <summary>
1423
/// Initializes a new instance of the <see cref="DockerEndpointAuthenticationConfiguration" /> struct.
1524
/// </summary>
1625
/// <param name="endpoint">The Docker API endpoint.</param>
1726
/// <param name="credentials">The Docker API authentication credentials.</param>
1827
public DockerEndpointAuthenticationConfiguration(Uri endpoint, Credentials credentials = null)
1928
{
20-
Credentials = credentials;
2129
Endpoint = endpoint;
30+
Credentials = credentials;
2231
}
2332

2433
/// <inheritdoc />
25-
public Credentials Credentials { get; }
34+
public Uri Endpoint { get; }
2635

2736
/// <inheritdoc />
28-
public Uri Endpoint { get; }
37+
public Credentials Credentials { get; }
2938

3039
/// <inheritdoc />
3140
public DockerClientConfiguration GetDockerClientConfiguration(Guid sessionId = default)
3241
{
3342
var defaultHttpRequestHeaders = new Dictionary<string, string>();
3443
defaultHttpRequestHeaders.Add("User-Agent", "tc-dotnet/" + TestcontainersClient.Version);
3544
defaultHttpRequestHeaders.Add("x-tc-sid", sessionId.ToString("D"));
36-
return new DockerClientConfiguration(Endpoint, Credentials, defaultHttpRequestHeaders: defaultHttpRequestHeaders);
45+
46+
return new DockerClientConfiguration(Endpoint, Credentials, namedPipeConnectTimeout: NamedPipeConnectionTimeout, defaultHttpRequestHeaders: defaultHttpRequestHeaders);
3747
}
3848
}
3949
}

src/Testcontainers/Configurations/CustomConfiguration.cs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,17 @@ protected virtual string GetHubImageNamePrefix(string propertyName)
115115

116116
protected virtual TimeSpan? GetWaitStrategyInterval(string propertyName)
117117
{
118-
return _properties.TryGetValue(propertyName, out var propertyValue) && TimeSpan.TryParse(propertyValue, CultureInfo.InvariantCulture, out var result) && result > TimeSpan.Zero ? result : null;
118+
return GetPropertyValue<TimeSpan?>(propertyName);
119119
}
120120

121121
protected virtual TimeSpan? GetWaitStrategyTimeout(string propertyName)
122122
{
123-
return _properties.TryGetValue(propertyName, out var propertyValue) && TimeSpan.TryParse(propertyValue, CultureInfo.InvariantCulture, out var result) && result > TimeSpan.Zero ? result : null;
123+
return GetPropertyValue<TimeSpan?>(propertyName);
124+
}
125+
126+
protected virtual TimeSpan? GetNamedPipeConnectionTimeout(string propertyName)
127+
{
128+
return GetPropertyValue<TimeSpan?>(propertyName);
124129
}
125130

126131
private T GetPropertyValue<T>(string propertyName)
@@ -129,21 +134,27 @@ private T GetPropertyValue<T>(string propertyName)
129134

130135
var isNullable = type != typeof(T);
131136

137+
var hasValue = _properties.TryGetValue(propertyName, out var propertyValue);
138+
139+
if (typeof(TimeSpan) == type)
140+
{
141+
return (T)(object)(hasValue && TimeSpan.TryParse(propertyValue, CultureInfo.InvariantCulture, out var result) && result > TimeSpan.Zero ? result : null);
142+
}
143+
132144
switch (Type.GetTypeCode(type))
133145
{
134146
case TypeCode.Boolean:
135147
{
136-
return (T)(object)(_properties.TryGetValue(propertyName, out var propertyValue) && bool.TryParse(propertyValue, out var result) ? result : isNullable ? null : "1".Equals(propertyValue, StringComparison.Ordinal));
148+
return (T)(object)(hasValue && bool.TryParse(propertyValue, out var result) ? result : isNullable ? null : "1".Equals(propertyValue, StringComparison.Ordinal));
137149
}
138150

139151
case TypeCode.UInt16:
140152
{
141-
return (T)(object)(_properties.TryGetValue(propertyName, out var propertyValue) && ushort.TryParse(propertyValue, out var result) ? result : isNullable ? null : 0);
153+
return (T)(object)(hasValue && ushort.TryParse(propertyValue, out var result) ? result : isNullable ? null : 0);
142154
}
143155

144156
case TypeCode.String:
145157
{
146-
_ = _properties.TryGetValue(propertyName, out var propertyValue);
147158
return (T)(object)(string.IsNullOrEmpty(propertyValue) ? null : propertyValue);
148159
}
149160

src/Testcontainers/Configurations/EnvironmentConfiguration.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ internal class EnvironmentConfiguration : CustomConfiguration, ICustomConfigurat
4242

4343
private const string WaitStrategyTimeout = "TESTCONTAINERS_WAIT_STRATEGY_TIMEOUT";
4444

45+
private const string NamedPipeConnectionTimeout = "TESTCONTAINERS_NAMED_PIPE_CONNECTION_TIMEOUT";
46+
4547
static EnvironmentConfiguration()
4648
{
4749
}
@@ -68,6 +70,7 @@ public EnvironmentConfiguration()
6870
WaitStrategyRetries,
6971
WaitStrategyInterval,
7072
WaitStrategyTimeout,
73+
NamedPipeConnectionTimeout,
7174
}
7275
.ToDictionary(key => key, Environment.GetEnvironmentVariable))
7376
{
@@ -174,5 +177,11 @@ public string GetHubImageNamePrefix()
174177
{
175178
return GetWaitStrategyTimeout(WaitStrategyTimeout);
176179
}
180+
181+
/// <inheritdoc />
182+
public TimeSpan? GetNamedPipeConnectionTimeout()
183+
{
184+
return GetWaitStrategyTimeout(NamedPipeConnectionTimeout);
185+
}
177186
}
178187
}

src/Testcontainers/Configurations/ICustomConfiguration.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,5 +134,13 @@ internal interface ICustomConfiguration
134134
/// <remarks>https://dotnet.testcontainers.org/custom_configuration/.</remarks>
135135
[CanBeNull]
136136
TimeSpan? GetWaitStrategyTimeout();
137+
138+
/// <summary>
139+
/// Gets the named pipe connection timeout custom configuration.
140+
/// </summary>
141+
/// <returns>The named pipe connection timeout custom configuration.</returns>
142+
/// <remarks>https://dotnet.testcontainers.org/custom_configuration/.</remarks>
143+
[CanBeNull]
144+
TimeSpan? GetNamedPipeConnectionTimeout();
137145
}
138146
}

src/Testcontainers/Configurations/PropertiesFileConfiguration.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,5 +167,12 @@ public string GetHubImageNamePrefix()
167167
const string propertyName = "wait.strategy.timeout";
168168
return GetWaitStrategyTimeout(propertyName);
169169
}
170+
171+
/// <inheritdoc />
172+
public TimeSpan? GetNamedPipeConnectionTimeout()
173+
{
174+
const string propertyName = "named.pipe.connection.timeout";
175+
return GetWaitStrategyTimeout(propertyName);
176+
}
170177
}
171178
}

tests/Testcontainers.Tests/Unit/Configurations/CustomConfigurationTest.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ static EnvironmentConfigurationTest()
3131
EnvironmentVariables.Add("TESTCONTAINERS_WAIT_STRATEGY_RETRIES");
3232
EnvironmentVariables.Add("TESTCONTAINERS_WAIT_STRATEGY_INTERVAL");
3333
EnvironmentVariables.Add("TESTCONTAINERS_WAIT_STRATEGY_TIMEOUT");
34+
EnvironmentVariables.Add("TESTCONTAINERS_NAMED_PIPE_CONNECTION_TIMEOUT");
3435
}
3536

3637
[Theory]
@@ -227,6 +228,18 @@ public void GetWaitStrategyTimeoutCustomConfiguration(string propertyName, strin
227228
Assert.Equal(expected, customConfiguration.GetWaitStrategyTimeout()?.ToString());
228229
}
229230

231+
[Theory]
232+
[InlineData("", "", null)]
233+
[InlineData("TESTCONTAINERS_NAMED_PIPE_CONNECTION_TIMEOUT", "", null)]
234+
[InlineData("TESTCONTAINERS_NAMED_PIPE_CONNECTION_TIMEOUT", "-00:00:00.001", null)]
235+
[InlineData("TESTCONTAINERS_NAMED_PIPE_CONNECTION_TIMEOUT", "00:00:01", "00:00:01")]
236+
public void GetNamedPipeConnectionTimeoutCustomConfiguration(string propertyName, string propertyValue, string expected)
237+
{
238+
SetEnvironmentVariable(propertyName, propertyValue);
239+
ICustomConfiguration customConfiguration = new EnvironmentConfiguration();
240+
Assert.Equal(expected, customConfiguration.GetNamedPipeConnectionTimeout()?.ToString());
241+
}
242+
230243
public void Dispose()
231244
{
232245
foreach (var propertyName in EnvironmentVariables)
@@ -423,6 +436,17 @@ public void GetWaitStrategyTimeoutCustomConfiguration(string configuration, stri
423436
ICustomConfiguration customConfiguration = new PropertiesFileConfiguration(new[] { configuration });
424437
Assert.Equal(expected, customConfiguration.GetWaitStrategyTimeout()?.ToString());
425438
}
439+
440+
[Theory]
441+
[InlineData("", null)]
442+
[InlineData("named.pipe.connection.timeout=", null)]
443+
[InlineData("named.pipe.connection.timeout=-00:00:00.001", null)]
444+
[InlineData("named.pipe.connection.timeout=00:00:01", "00:00:01")]
445+
public void GetNamedPipeConnectionTimeoutCustomConfiguration(string configuration, string expected)
446+
{
447+
ICustomConfiguration customConfiguration = new PropertiesFileConfiguration(new[] { configuration });
448+
Assert.Equal(expected, customConfiguration.GetNamedPipeConnectionTimeout()?.ToString());
449+
}
426450
}
427451
}
428452
}

0 commit comments

Comments
 (0)