diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 6c49e0825..13babbd08 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -63,9 +63,11 @@ jobs: test-results publish: + if: ${{ contains(fromJson('["develop", "master"]'), github.ref_name) }} + needs: build - environment: ${{ contains(fromJson('["develop", "master"]'), github.ref_name) && 'production' || 'development' }} + environment: production runs-on: ubuntu-20.04 diff --git a/src/Testcontainers/Builders/DockerCredentialProcess.cs b/src/Testcontainers/Builders/DockerCredentialProcess.cs index 81404c315..49d4459d3 100644 --- a/src/Testcontainers/Builders/DockerCredentialProcess.cs +++ b/src/Testcontainers/Builders/DockerCredentialProcess.cs @@ -19,14 +19,16 @@ public static string Get(string credentialProviderName, string hostname) try { - if (!dockerCredentialProcess.Start()) + if (dockerCredentialProcess.Start()) { - return null; + dockerCredentialProcess.StandardInput.WriteLine(hostname); + dockerCredentialProcess.StandardInput.Close(); + return dockerCredentialProcess.StandardOutput.ReadToEnd().Trim(); + } + else + { + throw new InvalidOperationException("Docker not found."); } - - dockerCredentialProcess.StandardInput.WriteLine(hostname); - dockerCredentialProcess.StandardInput.Close(); - return dockerCredentialProcess.StandardOutput.ReadToEnd().Trim(); } catch (Exception) { diff --git a/src/Testcontainers/Clients/DockerContainerOperations.cs b/src/Testcontainers/Clients/DockerContainerOperations.cs index ed82c4398..bbd3d44f1 100644 --- a/src/Testcontainers/Clients/DockerContainerOperations.cs +++ b/src/Testcontainers/Clients/DockerContainerOperations.cs @@ -97,6 +97,11 @@ public async Task GetArchiveFromContainerAsync(string id, string path, C public async Task AttachAsync(string id, IOutputConsumer outputConsumer, CancellationToken ct = default) { + if (!outputConsumer.Enabled) + { + return; + } + this.logger.AttachToDockerContainer(id, outputConsumer.GetType()); var attachParameters = new ContainerAttachParameters diff --git a/src/Testcontainers/Configurations/OutputConsumers/IOutputConsumer.cs b/src/Testcontainers/Configurations/OutputConsumers/IOutputConsumer.cs index 295273036..1deb5722a 100644 --- a/src/Testcontainers/Configurations/OutputConsumers/IOutputConsumer.cs +++ b/src/Testcontainers/Configurations/OutputConsumers/IOutputConsumer.cs @@ -8,6 +8,11 @@ namespace DotNet.Testcontainers.Configurations /// public interface IOutputConsumer : IDisposable { + /// + /// Gets a value indicating whether the is enabled or not. + /// + bool Enabled { get; } + /// /// Gets the stream that receives stdout. /// diff --git a/src/Testcontainers/Configurations/OutputConsumers/RedirectStdoutAndStderrToNull.cs b/src/Testcontainers/Configurations/OutputConsumers/RedirectStdoutAndStderrToNull.cs index ea0981b04..9d4e09982 100644 --- a/src/Testcontainers/Configurations/OutputConsumers/RedirectStdoutAndStderrToNull.cs +++ b/src/Testcontainers/Configurations/OutputConsumers/RedirectStdoutAndStderrToNull.cs @@ -10,10 +10,14 @@ internal sealed class RedirectStdoutAndStderrToNull : IOutputConsumer /// public RedirectStdoutAndStderrToNull() { + this.Enabled = false; this.Stdout = Stream.Null; this.Stderr = Stream.Null; } + /// + public bool Enabled { get; } + /// public Stream Stdout { get; } diff --git a/src/Testcontainers/Configurations/OutputConsumers/RedirectStdoutAndStderrToStream.cs b/src/Testcontainers/Configurations/OutputConsumers/RedirectStdoutAndStderrToStream.cs index ddbde271f..0b1f6ae94 100644 --- a/src/Testcontainers/Configurations/OutputConsumers/RedirectStdoutAndStderrToStream.cs +++ b/src/Testcontainers/Configurations/OutputConsumers/RedirectStdoutAndStderrToStream.cs @@ -25,10 +25,16 @@ public RedirectStdoutAndStderrToStream() /// The stderr stream. public RedirectStdoutAndStderrToStream(Stream stdout, Stream stderr) { - this.stdout = new StreamWriter(stdout) { AutoFlush = true }; - this.stderr = new StreamWriter(stderr) { AutoFlush = true }; + this.Enabled = stdout.CanWrite || stderr.CanWrite; + this.stdout = new StreamWriter(stdout); + this.stdout.AutoFlush = true; + this.stderr = new StreamWriter(stderr); + this.stderr.AutoFlush = true; } + /// + public bool Enabled { get; } + /// public Stream Stdout => this.stdout.BaseStream; diff --git a/src/Testcontainers/Containers/ResourceReaper.cs b/src/Testcontainers/Containers/ResourceReaper.cs index a36bf882c..5e039d8bd 100644 --- a/src/Testcontainers/Containers/ResourceReaper.cs +++ b/src/Testcontainers/Containers/ResourceReaper.cs @@ -197,10 +197,10 @@ public async ValueTask DisposeAsync() this.disposed = true; - this.maintainConnectionCts.Cancel(); - try { + this.maintainConnectionCts.Cancel(); + // Close connection before disposing ResourceReaper. await this.maintainConnectionTask .ConfigureAwait(false); @@ -209,8 +209,10 @@ await this.maintainConnectionTask { // Ignore } - - this.maintainConnectionCts.Dispose(); + finally + { + this.maintainConnectionCts.Dispose(); + } if (this.resourceReaperContainer != null) { diff --git a/src/Testcontainers/Containers/TestcontainersContainer.cs b/src/Testcontainers/Containers/TestcontainersContainer.cs index 484e38d89..cfb38045f 100644 --- a/src/Testcontainers/Containers/TestcontainersContainer.cs +++ b/src/Testcontainers/Containers/TestcontainersContainer.cs @@ -286,11 +286,10 @@ private async Task Create(CancellationToken ct = default) private async Task Start(string id, CancellationToken ct = default) { - var attachTask = this.client.AttachAsync(id, this.configuration.OutputConsumer, ct); - - var startTask = this.client.StartAsync(id, ct); + await this.client.AttachAsync(id, this.configuration.OutputConsumer, ct) + .ConfigureAwait(false); - await Task.WhenAll(attachTask, startTask) + await this.client.StartAsync(id, ct) .ConfigureAwait(false); this.container = await this.client.GetContainer(id, ct) diff --git a/tests/Testcontainers.Tests/SkipOnLinuxEngineAttribute.cs b/tests/Testcontainers.Tests/SkipOnLinuxEngineAttribute.cs index 834d1eda5..695cf9751 100644 --- a/tests/Testcontainers.Tests/SkipOnLinuxEngineAttribute.cs +++ b/tests/Testcontainers.Tests/SkipOnLinuxEngineAttribute.cs @@ -24,15 +24,24 @@ private static bool GetIsLinuxEngineEnabled() dockerProcessStartInfo.RedirectStandardOutput = true; dockerProcessStartInfo.UseShellExecute = false; - using (var dockerProcess = Process.Start(dockerProcessStartInfo)) + var dockerProcess = new Process(); + dockerProcess.StartInfo = dockerProcessStartInfo; + + try { - if (dockerProcess == null) + if (dockerProcess.Start()) + { + var stdout = dockerProcess.StandardOutput.ReadToEnd(); + return stdout.Contains("linux", StringComparison.OrdinalIgnoreCase); + } + else { throw new InvalidOperationException("Docker not found."); } - - var stdout = dockerProcess.StandardOutput.ReadToEnd(); - return stdout.Contains("linux", StringComparison.OrdinalIgnoreCase); + } + finally + { + dockerProcess.Dispose(); } } } diff --git a/tests/Testcontainers.Tests/Unit/Docker.cs b/tests/Testcontainers.Tests/Unit/Docker.cs index c5f3a6813..da6049006 100644 --- a/tests/Testcontainers.Tests/Unit/Docker.cs +++ b/tests/Testcontainers.Tests/Unit/Docker.cs @@ -1,30 +1,45 @@ namespace DotNet.Testcontainers.Tests.Unit { + using System; using System.Diagnostics; public static class Docker { public static bool Exists(DockerResource dockerResource, string name) { + DataReceivedEventHandler ignoreReceivedData = (_, _) => { }; + var dockerProcessStartInfo = new ProcessStartInfo(); dockerProcessStartInfo.FileName = "docker"; dockerProcessStartInfo.Arguments = $"inspect --type={dockerResource.Type} {name}"; - dockerProcessStartInfo.RedirectStandardOutput = true; dockerProcessStartInfo.RedirectStandardError = true; + dockerProcessStartInfo.RedirectStandardOutput = true; dockerProcessStartInfo.UseShellExecute = false; - using (var dockerProcess = Process.Start(dockerProcessStartInfo)) + var dockerProcess = new Process(); + dockerProcess.StartInfo = dockerProcessStartInfo; + dockerProcess.ErrorDataReceived += ignoreReceivedData; + dockerProcess.OutputDataReceived += ignoreReceivedData; + + try { - if (dockerProcess == null) + if (dockerProcess.Start()) { - return false; + dockerProcess.BeginErrorReadLine(); + dockerProcess.BeginOutputReadLine(); + dockerProcess.WaitForExit(); + return 0.Equals(dockerProcess.ExitCode); } - - _ = dockerProcess.StandardOutput.ReadToEnd(); - _ = dockerProcess.StandardError.ReadToEnd(); - - dockerProcess.WaitForExit(); - return 0.Equals(dockerProcess.ExitCode); + else + { + throw new InvalidOperationException("Docker not found."); + } + } + finally + { + dockerProcess.ErrorDataReceived -= ignoreReceivedData; + dockerProcess.OutputDataReceived -= ignoreReceivedData; + dockerProcess.Dispose(); } } }