Skip to content

Commit

Permalink
[#163] #EXTEND 'assemblyName: DotNet.Testcontainers; function: IDocke…
Browse files Browse the repository at this point in the history
…rContainer'

{Add ExecAsync to execute shell commands in running containers.}
  • Loading branch information
HofmeisterAn committed Nov 15, 2019
1 parent f3498a2 commit fc9e656
Show file tree
Hide file tree
Showing 14 changed files with 83 additions and 58 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ The pre-configured Testcontainers below are supported. Further examples can be f

- CouchDB (couchdb:2.3.1)
- MsSql (server:2017-CU14-ubuntu)
- MySql (mysql:8.0.15)
- PostgreSql (postgres:11.2)
- Redis (redis:5.0.5)
- RabbitMQ (rabbitmq:3.7.15)
- MySql (mysql:8.0.18)
- PostgreSql (postgres:11.5)
- Redis (redis:5.0.6)
- RabbitMQ (rabbitmq:3.7.21)

## Examples

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.msbuild" Version="2.7.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="MyCouch" Version="7.1.0" />
<PackageReference Include="MySql.Data" Version="8.0.18" />
<PackageReference Include="Npgsql" Version="4.1.1" />
<PackageReference Include="Npgsql" Version="4.1.2" />
<PackageReference Include="RabbitMQ.Client" Version="5.1.2" />
<PackageReference Include="StackExchange.Redis" Version="2.0.601" />
<PackageReference Include="System.Data.SqlClient" Version="4.7.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,23 @@ public async Task WaitStrategy()
await testcontainer.StartAsync();
}
}

[Fact]
public async Task ExecCommandInRunningContainer()
{
// Given
var testcontainersBuilder = new TestcontainersBuilder<TestcontainersContainer>()
.WithImage("alpine")
.WithCommand("/bin/ash", "-c", "tail -f /dev/null");

// When
// Then
using (var testcontainer = testcontainersBuilder.Build())
{
await testcontainer.StartAsync();
Assert.Equal(255, await testcontainer.ExecAsync(new [] { "/bin/ash", "-c", "exit 255" }));
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,33 +35,27 @@ public Task<bool> While(string id)
public class Timeout : IWaitUntil, IWaitWhile
{
[Fact]
public async Task UntilAfter1ms()
public async Task UntilAfter1Ms()
{
await Assert.ThrowsAsync<TimeoutException>(async () =>
{
await WaitStrategy.WaitUntil(() => this.Until(string.Empty), timeout: 1);
});
await Assert.ThrowsAsync<TimeoutException>(() => WaitStrategy.WaitUntil(() => this.Until(string.Empty), timeout: 1));
}

[Fact]
public async Task WhileAfter1ms()
public async Task WhileAfter1Ms()
{
await Assert.ThrowsAsync<TimeoutException>(async () =>
{
await WaitStrategy.WaitWhile(() => this.While(string.Empty), timeout: 1);
});
await Assert.ThrowsAsync<TimeoutException>(() => WaitStrategy.WaitWhile(() => this.While(string.Empty), timeout: 1));
}

public async Task<bool> Until(string id)
{
await Task.Delay(TimeSpan.FromSeconds(1));
return true;
return false;
}

public async Task<bool> While(string id)
{
await Task.Delay(TimeSpan.FromSeconds(1));
return false;
return true;
}
}

Expand All @@ -70,19 +64,13 @@ public class Rethrow : IWaitUntil, IWaitWhile
[Fact]
public async Task RethrowUntil()
{
await Assert.ThrowsAsync<NotImplementedException>(async () =>
{
await WaitStrategy.WaitUntil(() => this.Until(string.Empty));
});
await Assert.ThrowsAsync<NotImplementedException>(() => WaitStrategy.WaitUntil(() => this.Until(string.Empty)));
}

[Fact]
public async Task RethrowWhile()
{
await Assert.ThrowsAsync<NotImplementedException>(async () =>
{
await WaitStrategy.WaitWhile(() => this.While(string.Empty));
});
await Assert.ThrowsAsync<NotImplementedException>(() => WaitStrategy.WaitWhile(() => this.While(string.Empty)));
}

public Task<bool> Until(string id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace DotNet.Testcontainers.Tests.Unit.Containers
using Testcontainers.Containers.WaitStrategies;
using Xunit;

public class WaitUntilOperationSucceedsTest
public class WaitUntilOperationSucceededTest
{
[Theory]
[InlineData( 1, 1)]
Expand All @@ -16,16 +16,21 @@ public class WaitUntilOperationSucceedsTest
[InlineData( 0, 0)]
public async Task UntilMaxRepeats(int maxCallCount, int expectedCallsCount)
{
// Given
var callCounter = 0;

// When
await Assert.ThrowsAsync<TimeoutException>(async () =>
{
var wait = new WaitUntilOperationSucceeds(() =>
var wait = new WaitUntilOperationSucceeded(() =>
{
callCounter += 1;
return false;
}, maxCallCount);
await WaitStrategy.WaitUntil(() => wait.Until(string.Empty));
});

// Then
Assert.Equal(expectedCallsCount, callCounter);
}

Expand All @@ -40,9 +45,14 @@ await Assert.ThrowsAsync<TimeoutException>(async () =>
[InlineData(10, 7)]
public async Task UntilSomeRepeats(int maxCallCount, int expectedCallsCount)
{
// Given
var callCounter = 0;
var wait = new WaitUntilOperationSucceeds(() => ++callCounter >= expectedCallsCount, maxCallCount);

// When
var wait = new WaitUntilOperationSucceeded(() => ++callCounter >= expectedCallsCount, maxCallCount);
await WaitStrategy.WaitUntil(() => wait.Until(string.Empty));

// Then
Assert.Equal(expectedCallsCount, callCounter);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace DotNet.Testcontainers.Containers.Builders
using DotNet.Testcontainers.Containers.WaitStrategies;
using DotNet.Testcontainers.Images;

public interface ITestcontainersBuilder<T>
public interface ITestcontainersBuilder<out T>
{
ITestcontainersBuilder<T> ConfigureContainer(Action<T> moduleConfiguration);

Expand Down Expand Up @@ -131,7 +131,7 @@ public interface ITestcontainersBuilder<T>
/// <summary>
/// Sets the output consumer to capture the Testcontainer stdout and stderr messages.
/// </summary>
/// <param name="outputConsumer">Output consumer to capture stdout and strerr.</param>
/// <param name="outputConsumer">Output consumer to capture stdout and stderr.</param>
/// <returns>A configured instance of <see cref="ITestcontainersBuilder{T}"/>.</returns>
ITestcontainersBuilder<T> WithOutputConsumer(IOutputConsumer outputConsumer);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace DotNet.Testcontainers.Containers.Configurations.Databases

public sealed class MySqlTestcontainerConfiguration : TestcontainerDatabaseConfiguration
{
public MySqlTestcontainerConfiguration() : base("mysql:8.0.15", 3306)
public MySqlTestcontainerConfiguration() : base("mysql:8.0.18", 3306)
{
this.Environments["MYSQL_ALLOW_EMPTY_PASSWORD"] = "yes";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace DotNet.Testcontainers.Containers.Configurations.Databases

public sealed class PostgreSqlTestcontainerConfiguration : TestcontainerDatabaseConfiguration
{
public PostgreSqlTestcontainerConfiguration() : base("postgres:11.2", 5432)
public PostgreSqlTestcontainerConfiguration() : base("postgres:11.5", 5432)
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace DotNet.Testcontainers.Containers.Configurations.Databases

public sealed class RedisTestcontainerConfiguration : TestcontainerDatabaseConfiguration
{
public RedisTestcontainerConfiguration() : base("redis:5.0.5", 6379)
public RedisTestcontainerConfiguration() : base("redis:5.0.6", 6379)
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace DotNet.Testcontainers.Containers.Configurations.MessageBrokers

public sealed class RabbitMqTestcontainerConfiguration : TestcontainerMessageBrokerConfiguration
{
public RabbitMqTestcontainerConfiguration() : base("rabbitmq:3.7.15", 5672)
public RabbitMqTestcontainerConfiguration() : base("rabbitmq:3.7.21", 5672)
{
}

Expand Down
10 changes: 10 additions & 0 deletions src/DotNet.Testcontainers/Containers/IDockerContainer.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
namespace DotNet.Testcontainers.Containers
{
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

public interface IDockerContainer : IDisposable
Expand Down Expand Up @@ -52,5 +54,13 @@ public interface IDockerContainer : IDisposable
/// </summary>
/// <returns>A task that represents the asynchronous stop operation of a Testcontainer.</returns>
Task StopAsync();

/// <summary>
/// Executes a command in the running Testcontainer.
/// </summary>
/// <param name="command">Shell command.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>Task that completes when the shell command has been executed.</returns>
Task<long> ExecAsync(IList<string> command, CancellationToken ct = default);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace DotNet.Testcontainers.Containers.Modules
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -52,11 +53,7 @@ public string Name
{
get
{
if (this.container == null)
{
throw new InvalidOperationException(ContainerIsNotRunning);
}

this.ThrowIfContainerIsNotRunning();
return this.container.Names.FirstOrDefault() ?? string.Empty;
}
}
Expand All @@ -65,11 +62,7 @@ public string IpAddress
{
get
{
if (this.container == null)
{
throw new InvalidOperationException(ContainerIsNotRunning);
}

this.ThrowIfContainerIsNotRunning();
var ipAddress = this.container.NetworkSettings.Networks.FirstOrDefault();
return ipAddress.Value?.IPAddress ?? string.Empty;
}
Expand All @@ -79,11 +72,7 @@ public string MacAddress
{
get
{
if (this.container == null)
{
throw new InvalidOperationException(ContainerIsNotRunning);
}

this.ThrowIfContainerIsNotRunning();
var macAddress = this.container.NetworkSettings.Networks.FirstOrDefault();
return macAddress.Value?.MacAddress ?? string.Empty;
}
Expand All @@ -98,11 +87,7 @@ public ushort GetMappedPublicPort(int privatePort)

public ushort GetMappedPublicPort(string privatePort)
{
if (this.container == null)
{
throw new InvalidOperationException(ContainerIsNotRunning);
}

this.ThrowIfContainerIsNotRunning();
var mappedPort = this.container.Ports.FirstOrDefault(port => $"{port.PrivatePort}".Equals(privatePort));
return mappedPort?.PublicPort ?? ushort.MinValue;
}
Expand All @@ -126,6 +111,12 @@ public async Task StopAsync()
await this.Stop();
}

public Task<long> ExecAsync(IList<string> command, CancellationToken ct = default)
{
this.ThrowIfContainerIsNotRunning();
return TestcontainersClient.Instance.ExecAsync(this.Id, command, ct);
}

public void Dispose()
{
this.Dispose(true);
Expand All @@ -145,6 +136,14 @@ protected virtual void Dispose(bool disposing)
this.disposed = true;
}

private void ThrowIfContainerIsNotRunning()
{
if (this.container == null)
{
throw new InvalidOperationException(ContainerIsNotRunning);
}
}

private async Task Create()
{
if (!this.HasId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ namespace DotNet.Testcontainers.Containers.WaitStrategies
using System;
using System.Threading.Tasks;

internal class WaitUntilOperationSucceeds : WaitUntilContainerIsRunning
internal class WaitUntilOperationSucceeded : WaitUntilContainerIsRunning
{
private readonly int maxCallCount;

private readonly Func<bool> operation;

private int tryCount = 0;

public WaitUntilOperationSucceeds(Func<bool> operation, int maxCallCount = 4)
public WaitUntilOperationSucceeded(Func<bool> operation, int maxCallCount = 4)
{
this.maxCallCount = maxCallCount;
this.operation = operation;
Expand Down
2 changes: 1 addition & 1 deletion src/DotNet.Testcontainers/DotNet.Testcontainers.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<PackageReference Include="Docker.DotNet" Version="3.125.2" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
<PackageReference Include="SharpZipLib" Version="1.2.0" />
</ItemGroup>
<Import Project="$(MSBuildThisFileDirectory)../Shared.msbuild" />
Expand Down

0 comments on commit fc9e656

Please sign in to comment.