Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Restore output consumer support #959

Merged
merged 5 commits into from
Jul 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/api/create_docker_container.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ Assert.Equal(MagicNumber, magicNumber);
| `WithNetworkAliases` | Assigns a network-scoped aliases to the container e.g. `--network-alias "alias"`. |
| `WithExtraHost` | Adds a custom host-to-IP mapping to the container's `/etc/hosts` respectively `%WINDIR%\\system32\\drivers\\etc\\hosts` e.g. `--add-host "host.testcontainers.internal:172.17.0.2"`. |
| `WithPrivileged` | Sets the `--privileged` flag. |
| `WithOutputConsumer` | Redirects `stdout` and `stderr` to capture the container output. |
| `WithWaitStrategy` | Sets the wait strategy to complete the container start and indicates when it is ready. |
| `WithStartupCallback` | Sets the startup callback to invoke after the container start. |
| `WithCreateParameterModifier` | Allows low level modifications of the Docker container create parameter. |
Expand Down
1 change: 0 additions & 1 deletion src/Testcontainers/Builders/ContainerBuilder`3.cs
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,6 @@ public TBuilderEntity WithPrivileged(bool privileged)
}

/// <inheritdoc />
[Obsolete("It is no longer necessary to assign an output consumer to read the container's log messages.\nUse IContainer.GetLogsAsync(DateTime, DateTime, bool, CancellationToken) instead.")]
public TBuilderEntity WithOutputConsumer(IOutputConsumer outputConsumer)
{
return Clone(new ContainerConfiguration(outputConsumer: outputConsumer));
Expand Down
9 changes: 9 additions & 0 deletions src/Testcontainers/Clients/ITestcontainersClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,15 @@ internal interface ITestcontainersClient
/// <returns>Task that completes when the container has been removed.</returns>
Task RemoveAsync(string id, CancellationToken ct = default);

/// <summary>
/// Attaches to the container and copies the output to the <see cref="IOutputConsumer" />.
/// </summary>
/// <param name="id">The container id.</param>
/// <param name="outputConsumer">The stdout and stderr consumer.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>Task that completes when the container's stdout and stderr has been copied to the consumer.</returns>
Task AttachAsync(string id, IOutputConsumer outputConsumer, CancellationToken ct = default);

/// <summary>
/// Executes a command in the container.
/// </summary>
Expand Down
6 changes: 6 additions & 0 deletions src/Testcontainers/Clients/TestcontainersClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,12 @@ await Container.RemoveAsync(id, ct)
}
}

/// <inheritdoc />
public Task AttachAsync(string id, IOutputConsumer outputConsumer, CancellationToken ct = default)
{
return Container.AttachAsync(id, outputConsumer, ct);
}

/// <inheritdoc />
public Task<ExecResult> ExecAsync(string id, IList<string> command, CancellationToken ct = default)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ namespace DotNet.Testcontainers.Configurations
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using JetBrains.Annotations;

Expand Down Expand Up @@ -74,16 +73,6 @@ public interface IWaitForContainerOS
[PublicAPI]
IWaitForContainerOS UntilMessageIsLogged(Regex pattern);

/// <summary>
/// Waits until the message is logged in the steam.
/// </summary>
/// <param name="stream">The stream to be searched.</param>
/// <param name="message">The message to be checked.</param>
/// <returns>A configured instance of <see cref="IWaitForContainerOS" />.</returns>
[PublicAPI]
[Obsolete("It is no longer necessary to assign an output consumer to read the container's log messages.\nUse IWaitForContainerOS.UntilMessageIsLogged(string) or IWaitForContainerOS.UntilMessageIsLogged(Regex) instead.")]
IWaitForContainerOS UntilMessageIsLogged(Stream stream, string message);

/// <summary>
/// Waits until the operation is completed successfully.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ namespace DotNet.Testcontainers.Configurations
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;

/// <inheritdoc cref="IWaitForContainerOS" />
Expand Down Expand Up @@ -52,12 +51,6 @@ public IWaitForContainerOS UntilMessageIsLogged(Regex pattern)
return AddCustomWaitStrategy(new UntilMessageIsLogged(pattern));
}

/// <inheritdoc />
public virtual IWaitForContainerOS UntilMessageIsLogged(Stream stream, string message)
{
return AddCustomWaitStrategy(new UntilMessageIsLogged(message));
}

/// <inheritdoc />
public virtual IWaitForContainerOS UntilOperationIsSucceeded(Func<bool> operation, int maxCallCount)
{
Expand Down
3 changes: 3 additions & 0 deletions src/Testcontainers/Containers/DockerContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,9 @@ async Task<bool> CheckWaitStrategyAsync(IWaitUntil wait)
.ConfigureAwait(false);
}

await _client.AttachAsync(_container.ID, _configuration.OutputConsumer, ct)
.ConfigureAwait(false);

await _client.StartAsync(_container.ID, ct)
.ConfigureAwait(false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ public async Task HttpWaitStrategyUsesCustomHttpClientHandler()
var cookieContainer = new CookieContainer();
cookieContainer.Add(new Cookie("Key1", "Value1", "/", _container.Hostname));

var httpWaitStrategy = new HttpWaitStrategy().UsingHttpMessageHandler(new HttpClientHandler
{
CookieContainer = cookieContainer
});
using var httpMessageHandler = new HttpClientHandler();
httpMessageHandler.CookieContainer = cookieContainer;

var httpWaitStrategy = new HttpWaitStrategy().UsingHttpMessageHandler(httpMessageHandler);

// When
var succeeded = await httpWaitStrategy.UntilAsync(_container)
Expand All @@ -115,7 +115,9 @@ await Task.Delay(TimeSpan.FromSeconds(1))
public async Task HttpWaitStrategyCanReuseCustomHttpClientHandler()
{
// Given
var httpWaitStrategy = new HttpWaitStrategy().UsingHttpMessageHandler(new HttpClientHandler());
using var httpMessageHandler = new HttpClientHandler();

var httpWaitStrategy = new HttpWaitStrategy().UsingHttpMessageHandler(httpMessageHandler);

// When
await httpWaitStrategy.UntilAsync(_container)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace DotNet.Testcontainers.Tests.Unit
{
using System;
using System.Globalization;
using System.IO;
using System.Net;
using System.Net.Sockets;
Expand Down Expand Up @@ -279,6 +280,42 @@ public async Task HostnameShouldMatchDockerGatewayAddress(string expectedHostnam
Assert.Equal(expectedHostname, container.Hostname);
}

[Fact]
public async Task OutputConsumer()
{
// Given
using var consumer = Consume.RedirectStdoutAndStderrToStream(new MemoryStream(), new MemoryStream());

var unixTimeInMilliseconds = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString(CultureInfo.InvariantCulture);

// When
await using var container = new ContainerBuilder()
.WithImage(CommonImages.Alpine)
.WithEntrypoint("/bin/sh", "-c")
.WithCommand($"printf \"%s\" \"{unixTimeInMilliseconds}\" | tee /dev/stderr")
.WithOutputConsumer(consumer)
.WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged(unixTimeInMilliseconds))
.Build();

await container.StartAsync()
.ConfigureAwait(false);

consumer.Stdout.Seek(0, SeekOrigin.Begin);
consumer.Stderr.Seek(0, SeekOrigin.Begin);

using var stdoutReader = new StreamReader(consumer.Stdout, leaveOpen: true);
var stdout = await stdoutReader.ReadToEndAsync()
.ConfigureAwait(false);

using var stderrReader = new StreamReader(consumer.Stderr, leaveOpen: true);
var stderr = await stderrReader.ReadToEndAsync()
.ConfigureAwait(false);

// Then
Assert.Equal(unixTimeInMilliseconds, stdout);
Assert.Equal(unixTimeInMilliseconds, stderr);
}

[Fact]
public async Task WaitStrategy()
{
Expand Down