Skip to content

Commit 88b2982

Browse files
committed
fix: Throw an exception if the container has exited while checking for readiness
Fixes #1445
1 parent 2a09374 commit 88b2982

File tree

3 files changed

+61
-0
lines changed

3 files changed

+61
-0
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
using JetBrains.Annotations;
3+
4+
namespace DotNet.Testcontainers.Configurations
5+
{
6+
/// <summary>
7+
/// Represents an exception that is thrown when a Docker container fails to start.
8+
/// </summary>
9+
[PublicAPI]
10+
public sealed class ContainerException : Exception
11+
{
12+
/// <summary>
13+
/// Initializes a new instance of the <see cref="ContainerException" /> class.
14+
/// </summary>
15+
/// <param name="message">The message that describes the error.</param>
16+
public ContainerException(string message)
17+
: base(message)
18+
{
19+
}
20+
}
21+
}

src/Testcontainers/Containers/DockerContainer.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ namespace DotNet.Testcontainers.Containers
55
using System.Globalization;
66
using System.IO;
77
using System.Linq;
8+
using System.Text;
89
using System.Threading;
910
using System.Threading.Tasks;
1011
using Docker.DotNet;
@@ -635,6 +636,9 @@ private async Task<bool> CheckReadinessAsync(WaitStrategy waitStrategy, Cancella
635636
_container = await _client.Container.ByIdAsync(_container.ID, ct)
636637
.ConfigureAwait(false);
637638

639+
await ThrowIfExitedAsync(ct)
640+
.ConfigureAwait(false);
641+
638642
return await waitStrategy.UntilAsync(this, ct)
639643
.ConfigureAwait(false);
640644
}
@@ -660,6 +664,39 @@ await WaitStrategy.WaitUntilAsync(() => CheckReadinessAsync(waitStrategy, ct), w
660664
return true;
661665
}
662666

667+
private async Task ThrowIfExitedAsync(CancellationToken ct = default)
668+
{
669+
if (State == TestcontainersStates.Exited)
670+
{
671+
var message = new StringBuilder($"The {Image.FullName} container has exited.");
672+
try
673+
{
674+
var (stdout, stderr) = await GetLogsAsync(ct: ct)
675+
.ConfigureAwait(false);
676+
677+
stdout = stdout.Trim();
678+
stderr = stderr.Trim();
679+
680+
message.AppendLine();
681+
682+
if (stderr.Length > 0)
683+
{
684+
message.AppendLine().AppendLine($"=== stderr of {Name.TrimStart('/')} ({Id}) ===").AppendLine(stderr);
685+
}
686+
687+
if (stdout.Length > 0)
688+
{
689+
message.AppendLine().AppendLine($"=== stdout of {Name.TrimStart('/')} ({Id}) ===").AppendLine(stdout);
690+
}
691+
}
692+
catch
693+
{
694+
message.AppendLine(" Please look at the container logs.");
695+
}
696+
throw new ContainerException(message.ToString());
697+
}
698+
}
699+
663700
private sealed class WaitUntilPortBindingsMapped : WaitStrategy
664701
{
665702
private readonly DockerContainer _parent;

tests/Testcontainers.Platform.Linux.Tests/DependsOnTest.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ public async ValueTask InitializeAsync()
1919
{
2020
var childContainer1 = new ContainerBuilder()
2121
.WithImage(CommonImages.Alpine)
22+
.WithEntrypoint(CommonCommands.SleepInfinity)
2223
.WithLabel(_labelKey, _labelValue)
2324
.Build();
2425

2526
var childContainer2 = new ContainerBuilder()
2627
.WithImage(CommonImages.Alpine)
28+
.WithEntrypoint(CommonCommands.SleepInfinity)
2729
.WithLabel(_labelKey, _labelValue)
2830
.Build();
2931

@@ -41,6 +43,7 @@ public async ValueTask InitializeAsync()
4143
.DependsOn(network)
4244
.DependsOn(volume, "/workdir")
4345
.WithImage(CommonImages.Alpine)
46+
.WithEntrypoint(CommonCommands.SleepInfinity)
4447
.WithLabel(_labelKey, _labelValue)
4548
.Build();
4649

0 commit comments

Comments
 (0)