Skip to content

Commit

Permalink
Merge branch 'develop' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
HofmeisterAn authored Jan 14, 2025
2 parents 840e3a9 + 63422de commit fa508a6
Show file tree
Hide file tree
Showing 18 changed files with 273 additions and 15 deletions.
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<Product>Testcontainers</Product>
<Copyright>Copyright (c) 2019 - 2024 Andre Hofmeister and other authors</Copyright>
<Copyright>Copyright (c) 2019 - 2025 Andre Hofmeister and other authors</Copyright>
<Authors>Andre Hofmeister and contributors</Authors>
<Company>Andre Hofmeister</Company>
<Description>Testcontainers for .NET is a library to support tests with throwaway instances of Docker containers for all compatible .NET Standard versions.</Description>
Expand Down
7 changes: 4 additions & 3 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@
<PackageVersion Include="coverlet.collector" Version="6.0.2"/>
<PackageVersion Include="Dapper" Version="2.1.35"/>
<PackageVersion Include="ReflectionMagic" Version="5.0.1"/>
<PackageVersion Include="xunit.extensibility.execution" Version="2.9.0"/>
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2"/>
<PackageVersion Include="xunit.v3.extensibility.core" Version="0.2.0-pre.69"/>
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.0"/>
<PackageVersion Include="xunit" Version="2.9.2"/>
<!-- xUnit.net extensibility for Testcontainers.Xunit and Testcontainers.XunitV3 packages: -->
<PackageVersion Include="xunit.extensibility.execution" Version="2.9.0"/>
<PackageVersion Include="xunit.v3.extensibility.core" Version="1.0.0"/>
<!-- 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 LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2019 - 2024 Andre Hofmeister and other authors
Copyright (c) 2019 - 2025 Andre Hofmeister and other authors

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ See [LICENSE](https://github.com/testcontainers/testcontainers-dotnet/blob/main/

## Copyright

Copyright (c) 2019 - 2024 Andre Hofmeister and other authors.
Copyright (c) 2019 - 2025 Andre Hofmeister and other authors.

See [contributors][testcontainers-dotnet-contributors] for all contributors.

Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ See [LICENSE](https://raw.githubusercontent.com/testcontainers/testcontainers-do

## Copyright

Copyright (c) 2019 - 2024 Andre Hofmeister and other authors.
Copyright (c) 2019 - 2025 Andre Hofmeister and other authors.

See [contributors][testcontainers-dotnet-contributors] for all contributors.

Expand Down
5 changes: 1 addition & 4 deletions src/Testcontainers.Pulsar/PulsarBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public override PulsarContainer Build()
waitStrategy = waitStrategy.UntilMessageIsLogged("Function worker service started");
}

var pulsarBuilder = WithWaitStrategy(waitStrategy);
var pulsarBuilder = DockerResourceConfiguration.WaitStrategies.Count() > 1 ? this : WithWaitStrategy(waitStrategy);
return new PulsarContainer(pulsarBuilder.DockerResourceConfiguration);
}

Expand Down Expand Up @@ -156,9 +156,6 @@ public Task<bool> UntilAsync(IContainer container)
/// <inheritdoc cref="IWaitUntil.UntilAsync" />
private async Task<bool> UntilAsync(PulsarContainer container)
{
_ = Guard.Argument(container, nameof(container))
.NotNull();

if (_authenticationEnabled && _authToken == null)
{
try
Expand Down
2 changes: 0 additions & 2 deletions src/Testcontainers.XunitV3/Testcontainers.XunitV3.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
<PropertyGroup>
<TargetFrameworks>net8.0;net9.0;netstandard2.0;netstandard2.1</TargetFrameworks>
<LangVersion>latest</LangVersion>
<IsPackable>false</IsPackable>
<IsPublishable>false</IsPublishable>
<DefineConstants>$(DefineConstants);XUNIT_V3</DefineConstants>
</PropertyGroup>
<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ protected override X509Certificate2 GetClientCertificate()
return Polyfills.X509Certificate2.CreateFromPemFile(clientCertificateFilePath, clientCertificateKeyFilePath);
#elif NET9_0_OR_GREATER
var certificate = X509Certificate2.CreateFromPemFile(clientCertificateFilePath, clientCertificateKeyFilePath);
return OperatingSystem.IsWindows() ? X509CertificateLoader.LoadCertificate(certificate.Export(X509ContentType.Pfx)) : certificate;
return OperatingSystem.IsWindows() ? X509CertificateLoader.LoadPkcs12(certificate.Export(X509ContentType.Pfx), null) : certificate;
#elif NET6_0_OR_GREATER
var certificate = X509Certificate2.CreateFromPemFile(clientCertificateFilePath, clientCertificateKeyFilePath);
return OperatingSystem.IsWindows() ? new X509Certificate2(certificate.Export(X509ContentType.Pfx)) : certificate;
Expand Down
12 changes: 12 additions & 0 deletions src/Testcontainers/Clients/DockerContainerOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,18 @@ public Task StopAsync(string id, CancellationToken ct = default)
return DockerClient.Containers.StopContainerAsync(id, new ContainerStopParameters { WaitBeforeKillSeconds = 15 }, ct);
}

public Task PauseAsync(string id, CancellationToken ct = default)
{
Logger.PauseDockerContainer(id);
return DockerClient.Containers.PauseContainerAsync(id, ct);
}

public Task UnpauseAsync(string id, CancellationToken ct = default)
{
Logger.UnpauseDockerContainer(id);
return DockerClient.Containers.UnpauseContainerAsync(id, ct);
}

public Task RemoveAsync(string id, CancellationToken ct = default)
{
Logger.DeleteDockerContainer(id);
Expand Down
4 changes: 4 additions & 0 deletions src/Testcontainers/Clients/IDockerContainerOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ internal interface IDockerContainerOperations : IHasListOperations<ContainerList

Task StopAsync(string id, CancellationToken ct = default);

Task PauseAsync(string id, CancellationToken ct = default);

Task UnpauseAsync(string id, CancellationToken ct = default);

Task RemoveAsync(string id, CancellationToken ct = default);

Task ExtractArchiveToContainerAsync(string id, string path, TarOutputMemoryStream tarStream, CancellationToken ct = default);
Expand Down
16 changes: 16 additions & 0 deletions src/Testcontainers/Clients/ITestcontainersClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,22 @@ internal interface ITestcontainersClient
/// <returns>Task that completes when the container has been stopped.</returns>
Task StopAsync(string id, CancellationToken ct = default);

/// <summary>
/// Pauses the container.
/// </summary>
/// <param name="id">The container id.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>Task that completes when the container has been paused.</returns>
Task PauseAsync(string id, CancellationToken ct = default);

/// <summary>
/// Unpauses the container.
/// </summary>
/// <param name="id">The container id.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>Task that completes when the container has been unpaused.</returns>
Task UnpauseAsync(string id, CancellationToken ct = default);

/// <summary>
/// Removes the container.
/// </summary>
Expand Down
22 changes: 22 additions & 0 deletions src/Testcontainers/Clients/TestcontainersClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,28 @@ await Container.StopAsync(id, ct)
}
}

/// <inheritdoc />
public async Task PauseAsync(string id, CancellationToken ct = default)
{
if (await Container.ExistsWithIdAsync(id, ct)
.ConfigureAwait(false))
{
await Container.PauseAsync(id, ct)
.ConfigureAwait(false);
}
}

/// <inheritdoc />
public async Task UnpauseAsync(string id, CancellationToken ct = default)
{
if (await Container.ExistsWithIdAsync(id, ct)
.ConfigureAwait(false))
{
await Container.UnpauseAsync(id, ct)
.ConfigureAwait(false);
}
}

/// <inheritdoc />
public async Task RemoveAsync(string id, CancellationToken ct = default)
{
Expand Down
6 changes: 6 additions & 0 deletions src/Testcontainers/Configurations/Networks/NetworkDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ public readonly struct NetworkDriver
[PublicAPI]
public static readonly NetworkDriver Host = new NetworkDriver("host");

/// <summary>
/// Gets network driver nat.
/// </summary>
[PublicAPI]
public static readonly NetworkDriver Nat = new NetworkDriver("nat");

/// <summary>
/// Initializes a new instance of the <see cref="NetworkDriver" /> struct.
/// </summary>
Expand Down
98 changes: 97 additions & 1 deletion src/Testcontainers/Containers/DockerContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace DotNet.Testcontainers.Containers
[PublicAPI]
public class DockerContainer : Resource, IContainer
{
private const TestcontainersStates ContainerHasBeenCreatedStates = TestcontainersStates.Created | TestcontainersStates.Running | TestcontainersStates.Exited;
private const TestcontainersStates ContainerHasBeenCreatedStates = TestcontainersStates.Created | TestcontainersStates.Running | TestcontainersStates.Paused | TestcontainersStates.Exited;

private const TestcontainersHealthStatus ContainerHasHealthCheck = TestcontainersHealthStatus.Starting | TestcontainersHealthStatus.Healthy | TestcontainersHealthStatus.Unhealthy;

Expand Down Expand Up @@ -48,6 +48,12 @@ public DockerContainer(IContainerConfiguration configuration)
/// <inheritdoc />
public event EventHandler Stopping;

/// <inheritdoc />
public event EventHandler Pausing;

/// <inheritdoc />
public event EventHandler Unpausing;

/// <inheritdoc />
public event EventHandler Created;

Expand All @@ -57,6 +63,12 @@ public DockerContainer(IContainerConfiguration configuration)
/// <inheritdoc />
public event EventHandler Stopped;

/// <inheritdoc />
public event EventHandler Paused;

/// <inheritdoc />
public event EventHandler Unpaused;

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

Expand All @@ -66,6 +78,12 @@ public DockerContainer(IContainerConfiguration configuration)
/// <inheritdoc />
public DateTime StoppedTime { get; private set; }

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

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

/// <inheritdoc />
public ILogger Logger
{
Expand Down Expand Up @@ -294,6 +312,26 @@ await UnsafeStopAsync(ct)
.ConfigureAwait(false);
}

/// <inheritdoc />
public async Task PauseAsync(CancellationToken ct = default)
{
using var disposable = await AcquireLockAsync(ct)
.ConfigureAwait(false);

await UnsafePauseAsync(ct)
.ConfigureAwait(false);
}

/// <inheritdoc />
public async Task UnpauseAsync(CancellationToken ct = default)
{
using var disposable = await AcquireLockAsync(ct)
.ConfigureAwait(false);

await UnsafeUnpauseAsync(ct)
.ConfigureAwait(false);
}

/// <inheritdoc />
public Task CopyAsync(byte[] fileContent, string filePath, UnixFileModes fileMode = Unix.FileMode644, CancellationToken ct = default)
{
Expand Down Expand Up @@ -522,6 +560,64 @@ await _client.StopAsync(_container.ID, ct)
Stopped?.Invoke(this, EventArgs.Empty);
}

/// <summary>
/// Pauses the container.
/// </summary>
/// <remarks>
/// Only the public members <see cref="PauseAsync" /> and <see cref="UnpauseAsync" /> are thread-safe for now.
/// </remarks>
/// <param name="ct">Cancellation token.</param>
/// <returns>Task that completes when the container has been paused.</returns>
protected virtual async Task UnsafePauseAsync(CancellationToken ct = default)
{
ThrowIfLockNotAcquired();

if (!Exists())
{
return;
}

Pausing?.Invoke(this, EventArgs.Empty);

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

_container = await _client.Container.ByIdAsync(_container.ID, ct)
.ConfigureAwait(false);

PausedTime = DateTime.UtcNow;
Paused?.Invoke(this, EventArgs.Empty);
}

/// <summary>
/// Unpauses the container.
/// </summary>
/// <remarks>
/// Only the public members <see cref="PauseAsync" /> and <see cref="UnpauseAsync" /> are thread-safe for now.
/// </remarks>
/// <param name="ct">Cancellation token.</param>
/// <returns>Task that completes when the container has been unpaused.</returns>
protected virtual async Task UnsafeUnpauseAsync(CancellationToken ct = default)
{
ThrowIfLockNotAcquired();

if (!Exists())
{
return;
}

Unpausing?.Invoke(this, EventArgs.Empty);

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

_container = await _client.Container.ByIdAsync(_container.ID, ct)
.ConfigureAwait(false);

UnpausedTime = DateTime.UtcNow;
Unpaused?.Invoke(this, EventArgs.Empty);
}

/// <inheritdoc />
protected override bool Exists()
{
Expand Down
Loading

0 comments on commit fa508a6

Please sign in to comment.