Skip to content

Commit

Permalink
fix: Consider the stopped timestamp in the log message wait strategy …
Browse files Browse the repository at this point in the history
…while starting an existing container
  • Loading branch information
HofmeisterAn committed Feb 10, 2024
1 parent 1c80fe8 commit 727e35a
Show file tree
Hide file tree
Showing 13 changed files with 117 additions and 13 deletions.
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
<PackageVersion Include="coverlet.collector" Version="6.0.0"/>
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.6"/>
<PackageVersion Include="xunit" Version="2.6.5"/>
<PackageVersion Include="xunit" Version="2.6.6"/>
<!-- Third-party client dependencies to connect and interact with the containers: -->
<PackageVersion Include="Apache.NMS.ActiveMQ" Version="2.1.0"/>
<PackageVersion Include="ArangoDBNetStandard" Version="2.0.1"/>
Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers.Elasticsearch/ElasticsearchBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ private sealed class WaitUntil : IWaitUntil
/// <inheritdoc />
public async Task<bool> UntilAsync(IContainer container)
{
var (stdout, _) = await container.GetLogsAsync(timestampsEnabled: false)
var (stdout, _) = await container.GetLogsAsync(since: container.StoppedTime, timestampsEnabled: false)
.ConfigureAwait(false);

return Pattern.Any(stdout.Contains);
Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers.MongoDb/MongoDbBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ public WaitUntil(MongoDbConfiguration configuration)
/// <inheritdoc />
public async Task<bool> UntilAsync(IContainer container)
{
var (stdout, stderr) = await container.GetLogsAsync(timestampsEnabled: false)
var (stdout, stderr) = await container.GetLogsAsync(since: container.StoppedTime, timestampsEnabled: false)
.ConfigureAwait(false);

return _count.Equals(Array.Empty<string>()
Expand Down
13 changes: 7 additions & 6 deletions src/Testcontainers.PostgreSql/PostgreSqlBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,18 +123,19 @@ protected override PostgreSqlBuilder Merge(PostgreSqlConfiguration oldValue, Pos
/// <inheritdoc cref="IWaitUntil" />
private sealed class WaitUntil : IWaitUntil
{
private static readonly string[] LineEndings = ["\r\n", "\n"];
private const string IPv4Listening = "listening on IPv4";

private const string IPv6Listening = "listening on IPv6";

private const string DatabaseSystemReady = "database system is ready to accept connections";

/// <inheritdoc />
public async Task<bool> UntilAsync(IContainer container)
{
var (stdout, stderr) = await container.GetLogsAsync(timestampsEnabled: false)
var (_, stderr) = await container.GetLogsAsync(since: container.StoppedTime, timestampsEnabled: false)
.ConfigureAwait(false);

return 2.Equals(Array.Empty<string>()
.Concat(stdout.Split(LineEndings, StringSplitOptions.RemoveEmptyEntries))
.Concat(stderr.Split(LineEndings, StringSplitOptions.RemoveEmptyEntries))
.Count(line => line.Contains("database system is ready to accept connections")));
return new[] { IPv4Listening, IPv6Listening, DatabaseSystemReady }.All(stderr.Contains);
}
}
}
4 changes: 2 additions & 2 deletions src/Testcontainers/Clients/DockerContainerOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ public async Task<long> GetExitCodeAsync(string id, CancellationToken ct = defau
{
ShowStdout = true,
ShowStderr = true,
Since = Math.Max(0, since.TotalSeconds).ToString("0", CultureInfo.InvariantCulture),
Until = Math.Max(0, until.TotalSeconds).ToString("0", CultureInfo.InvariantCulture),
Since = Math.Max(0, Math.Floor(since.TotalSeconds)).ToString("0", CultureInfo.InvariantCulture),
Until = Math.Max(0, Math.Floor(until.TotalSeconds)).ToString("0", CultureInfo.InvariantCulture),
Timestamps = timestampsEnabled,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public UntilMessageIsLogged(Regex pattern)

public async Task<bool> UntilAsync(IContainer container)
{
var (stdout, stderr) = await container.GetLogsAsync(timestampsEnabled: false)
var (stdout, stderr) = await container.GetLogsAsync(since: container.StoppedTime, timestampsEnabled: false)
.ConfigureAwait(false);

return _pattern.IsMatch(stdout) || _pattern.IsMatch(stderr);
Expand Down
16 changes: 16 additions & 0 deletions src/Testcontainers/Containers/DockerContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ public DockerContainer(IContainerConfiguration configuration, ILogger logger)
/// <inheritdoc />
public ILogger Logger { get; }

/// <inheritdoc />
public DateTime CreatedTime { get; private set; }

/// <inheritdoc />
public DateTime StartedTime { get; private set; }

/// <inheritdoc />
public DateTime StoppedTime { get; private set; }

/// <inheritdoc />
public string Id
{
Expand Down Expand Up @@ -400,6 +409,7 @@ protected override async Task UnsafeCreateAsync(CancellationToken ct = default)
_container = await _client.Container.ByIdAsync(id, ct)
.ConfigureAwait(false);

CreatedTime = DateTime.UtcNow;
Created?.Invoke(this, EventArgs.Empty);
}

Expand All @@ -420,6 +430,10 @@ await _client.RemoveAsync(_container.ID, ct)
.ConfigureAwait(false);

_container = new ContainerInspectResponse();

CreatedTime = default;
StartedTime = default;
StoppedTime = default;
}

/// <summary>
Expand Down Expand Up @@ -476,6 +490,7 @@ await WaitStrategy.WaitUntilAsync(() => CheckWaitStrategyAsync(waitStrategy), Ti

Logger.CompleteReadinessCheck(_container.ID);

StartedTime = DateTime.UtcNow;
Started?.Invoke(this, EventArgs.Empty);
}

Expand Down Expand Up @@ -504,6 +519,7 @@ await _client.StopAsync(_container.ID, ct)
_container = await _client.Container.ByIdAsync(_container.ID, ct)
.ConfigureAwait(false);

StoppedTime = DateTime.UtcNow;
Stopped?.Invoke(this, EventArgs.Empty);
}

Expand Down
15 changes: 15 additions & 0 deletions src/Testcontainers/Containers/IContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,21 @@ public interface IContainer : IAsyncDisposable
[NotNull]
ILogger Logger { get; }

/// <summary>
/// Gets the created timestamp.
/// </summary>
DateTime CreatedTime { get; }

/// <summary>
/// Gets the started timestamp.
/// </summary>
DateTime StartedTime { get; }

/// <summary>
/// Gets the stopped timestamp.
/// </summary>
DateTime StoppedTime { get; }

/// <summary>
/// Gets the container id.
/// </summary>
Expand Down
23 changes: 23 additions & 0 deletions tests/Testcontainers.Commons/SharedContainerInstance.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace DotNet.Testcontainers.Commons;

[PublicAPI]
public abstract class SharedContainerInstance<TContainer> : IAsyncLifetime
where TContainer : IContainer
{
public SharedContainerInstance(TContainer container)
{
Container = container;
}

public TContainer Container { get; }

public Task InitializeAsync()
{
return Container.StartAsync();
}

public Task DisposeAsync()
{
return Container.DisposeAsync().AsTask();
}
}
2 changes: 2 additions & 0 deletions tests/Testcontainers.Commons/Testcontainers.Commons.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
<IsPackable>false</IsPackable>
<IsPublishable>false</IsPublishable>
<IsTestProject>false</IsTestProject>
<SignAssembly>true</SignAssembly>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" VersionOverride="2023.3.0"/>
<PackageReference Include="xunit"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../src/Testcontainers/Testcontainers.csproj"/>
Expand Down
5 changes: 4 additions & 1 deletion tests/Testcontainers.Commons/Usings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@
global using System.Diagnostics;
global using System.IO;
global using System.Text;
global using System.Threading.Tasks;
global using DotNet.Testcontainers.Containers;
global using DotNet.Testcontainers.Images;
global using JetBrains.Annotations;
global using JetBrains.Annotations;
global using Xunit;
41 changes: 41 additions & 0 deletions tests/Testcontainers.PostgreSql.Tests/PostgreSqlContainerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,45 @@ public async Task ExecScriptReturnsSuccessful()
// When
Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr);
}

public sealed class ReuseContainerTest : IClassFixture<SharedPostgreSqlInstance>, IDisposable
{
private readonly CancellationTokenSource _cts = new CancellationTokenSource(TimeSpan.FromMinutes(1));

private readonly SharedContainerInstance<PostgreSqlContainer> _fixture;

public ReuseContainerTest(SharedPostgreSqlInstance fixture)
{
_fixture = fixture;
}

public void Dispose()
{
_cts.Dispose();
}

[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(2)]
public async Task StopsAndStartsContainerSuccessful(int _)
{
await _fixture.Container.StopAsync(_cts.Token)
.ConfigureAwait(true);

await _fixture.Container.StartAsync(_cts.Token)
.ConfigureAwait(true);

Assert.False(_cts.IsCancellationRequested);
}
}

[UsedImplicitly]
public sealed class SharedPostgreSqlInstance : SharedContainerInstance<PostgreSqlContainer>
{
public SharedPostgreSqlInstance()
: base(new PostgreSqlBuilder().Build())
{
}
}
}
3 changes: 3 additions & 0 deletions tests/Testcontainers.PostgreSql.Tests/Usings.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
global using System;
global using System.Data;
global using System.Data.Common;
global using System.Threading;
global using System.Threading.Tasks;
global using DotNet.Testcontainers.Commons;
global using JetBrains.Annotations;
global using Npgsql;
global using Xunit;

0 comments on commit 727e35a

Please sign in to comment.