Skip to content

Commit

Permalink
[testcontainers#242] #IMPLEMENT 'assemblyName: DotNet.Testcontainers;…
Browse files Browse the repository at this point in the history
… function: ResourceReaper'

{Add ResourceReaper.}
  • Loading branch information
PSanetra committed Dec 21, 2021
1 parent feebebc commit 7c18985
Show file tree
Hide file tree
Showing 51 changed files with 763 additions and 188 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
namespace DotNet.Testcontainers.Builders
{
using System;
using System.Threading.Tasks;
using Configurations;
using DotNet.Testcontainers.Images;
using JetBrains.Annotations;

Expand Down Expand Up @@ -50,10 +52,27 @@ public interface IImageFromDockerfileBuilder
IImageFromDockerfileBuilder WithDeleteIfExists(bool deleteIfExists);

/// <summary>
/// Builds the instance of <see cref="IImageFromDockerfileBuilder" /> with the given configuration.
/// Add a label to the resulting image.
/// </summary>
/// <param name="labelName">Name of the label.</param>
/// <param name="value">Value of the label.</param>
/// <returns>A configured instance of <see cref="IImageFromDockerfileBuilder" />.</returns>
[PublicAPI]
IImageFromDockerfileBuilder WithLabel(string labelName, string value);

/// <summary>
/// Sets the resource reaper session id for this image.
/// The <see cref="ResourceReaper"/> will make sure to delete the image after the tests have finished if it was not deleted explicitly.
/// </summary>
/// <param name="resourceReaperSessionId">The session id of the <see cref="ResourceReaper"/> instance.</param>
[PublicAPI]
IImageFromDockerfileBuilder WithResourceReaperSessionId(Guid? resourceReaperSessionId);

/// <summary>
/// Builds the instance of <see cref="IImageFromDockerfileBuilder" /> with the given configuration.
/// </summary>
/// <returns>FullName of the created image.</returns>
[PublicAPI]
Task<string> Build();
}
}
25 changes: 25 additions & 0 deletions src/DotNet.Testcontainers/Builders/ITestcontainersBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,22 @@ public interface ITestcontainersBuilder<out TDockerContainer>
[PublicAPI]
ITestcontainersBuilder<TDockerContainer> WithCleanUp(bool cleanUp);

/// <summary>
/// If true, the Testcontainer is removed automatically by the Docker daemon when stopped.
/// </summary>
/// <param name="autoRemove">True, TestcontainerTestcontainer is removed automatically by the Docker daemon when stopped.</param>
/// <returns>A configured instance of <see cref="ITestcontainersBuilder{TDockerContainer}" />.</returns>
[PublicAPI]
ITestcontainersBuilder<TDockerContainer> WithAutoRemove(bool autoRemove);

/// <summary>
/// If true, the Testcontainer is removed automatically by the Docker daemon when stopped.
/// </summary>
/// <param name="autoRemove">True, TestcontainerTestcontainer is removed automatically by the Docker daemon when stopped.</param>
/// <returns>A configured instance of <see cref="ITestcontainersBuilder{TDockerContainer}" />.</returns>
[PublicAPI]
ITestcontainersBuilder<TDockerContainer> WithPrivileged(bool privileged);

/// <summary>
/// Sets the Docker API endpoint.
/// </summary>
Expand Down Expand Up @@ -301,6 +317,15 @@ public interface ITestcontainersBuilder<out TDockerContainer>
[PublicAPI]
ITestcontainersBuilder<TDockerContainer> WithStartupCallback(Func<IRunningDockerContainer, CancellationToken, Task> startupCallback);

/// <summary>
/// Sets the resource reaper session id for this container.
/// The <see cref="ResourceReaper"/> will make sure to delete the container after the tests have finished if it was not deleted explicitly.
/// </summary>
/// <param name="resourceReaperSessionId">The session id of the <see cref="ResourceReaper"/> instance.</param>
/// <returns>A configured instance of <see cref="ITestcontainersBuilder{TDockerContainer}" />.</returns>
[PublicAPI]
ITestcontainersBuilder<TDockerContainer> WithResourceReaperSessionId(Guid? resourceReaperSessionId);

/// <summary>
/// Builds the instance of <see cref="ITestcontainersContainer" /> with the given configuration.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace DotNet.Testcontainers.Builders
{
using System;
using DotNet.Testcontainers.Configurations;
using DotNet.Testcontainers.Network;
using JetBrains.Annotations;
Expand Down Expand Up @@ -42,6 +43,15 @@ public interface ITestcontainersNetworkBuilder
[PublicAPI]
ITestcontainersNetworkBuilder WithLabel(string name, string value);

/// <summary>
/// Sets the resource reaper session id for this network.
/// The <see cref="ResourceReaper"/> will make sure to delete the network after the tests have finished if it was not deleted explicitly.
/// </summary>
/// <param name="resourceReaperSessionId">The session id of the <see cref="ResourceReaper"/> instance.</param>
/// <returns>A configured instance of <see cref="ITestcontainersNetworkBuilder" />.</returns>
[PublicAPI]
ITestcontainersNetworkBuilder WithResourceReaperSessionId(Guid? resourceReaperSessionId);

/// <summary>
/// Builds the instance of <see cref="IDockerNetwork" /> with the given configuration.
/// </summary>
Expand Down
10 changes: 10 additions & 0 deletions src/DotNet.Testcontainers/Builders/ITestcontainersVolumeBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
namespace DotNet.Testcontainers.Builders
{
using System;
using DotNet.Testcontainers.Configurations;
using DotNet.Testcontainers.Volumes;
using JetBrains.Annotations;

Expand Down Expand Up @@ -33,6 +35,14 @@ public interface ITestcontainersVolumeBuilder
[PublicAPI]
ITestcontainersVolumeBuilder WithLabel(string name, string value);

/// <summary>
/// Sets the resource reaper session id for this volume.
/// The <see cref="ResourceReaper"/> will make sure to delete the volume after the tests have finished if it was not deleted explicitly.
/// </summary>
/// <param name="resourceReaperSessionId">The session id of the <see cref="ResourceReaper"/> instance.</param>
/// <returns>A configured instance of <see cref="ITestcontainersVolumeBuilder" />.</returns>
ITestcontainersVolumeBuilder WithResourceReaperSessionId(Guid? resourceReaperSessionId);

/// <summary>
/// Builds the instance of <see cref="IDockerVolume" /> with the given configuration.
/// </summary>
Expand Down
35 changes: 31 additions & 4 deletions src/DotNet.Testcontainers/Builders/ImageFromDockerfileBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
namespace DotNet.Testcontainers.Builders
{
using System;
using System.Linq;
using System.Threading.Tasks;
using DotNet.Testcontainers.Clients;
using DotNet.Testcontainers.Configurations;
using DotNet.Testcontainers.Configurations.Images;
using DotNet.Testcontainers.Images;
using JetBrains.Annotations;

Expand Down Expand Up @@ -39,28 +42,52 @@ public IImageFromDockerfileBuilder WithName(string name)
public IImageFromDockerfileBuilder WithName(IDockerImage name)
{
return new ImageFromDockerfileBuilder(
new ImageFromDockerfileConfiguration(name, this.configuration.Dockerfile, this.configuration.DockerfileDirectory, this.configuration.DeleteIfExists));
new ImageFromDockerfileConfiguration(name, this.configuration.Dockerfile, this.configuration.DockerfileDirectory, this.configuration.DeleteIfExists, this.configuration.Labels));
}

/// <inheritdoc />
public IImageFromDockerfileBuilder WithDockerfile(string dockerfile)
{
return new ImageFromDockerfileBuilder(
new ImageFromDockerfileConfiguration(this.configuration.Image, dockerfile, this.configuration.DockerfileDirectory, this.configuration.DeleteIfExists));
new ImageFromDockerfileConfiguration(this.configuration.Image, dockerfile, this.configuration.DockerfileDirectory, this.configuration.DeleteIfExists, this.configuration.Labels));
}

/// <inheritdoc />
public IImageFromDockerfileBuilder WithDockerfileDirectory(string dockerfileDirectory)
{
return new ImageFromDockerfileBuilder(
new ImageFromDockerfileConfiguration(this.configuration.Image, this.configuration.Dockerfile, dockerfileDirectory, this.configuration.DeleteIfExists));
new ImageFromDockerfileConfiguration(this.configuration.Image, this.configuration.Dockerfile, dockerfileDirectory, this.configuration.DeleteIfExists, this.configuration.Labels));
}

/// <inheritdoc />
public IImageFromDockerfileBuilder WithDeleteIfExists(bool deleteIfExists)
{
return new ImageFromDockerfileBuilder(
new ImageFromDockerfileConfiguration(this.configuration.Image, this.configuration.Dockerfile, this.configuration.DockerfileDirectory, deleteIfExists));
new ImageFromDockerfileConfiguration(this.configuration.Image, this.configuration.Dockerfile, this.configuration.DockerfileDirectory, deleteIfExists, this.configuration.Labels));
}

/// <inheritdoc />
public IImageFromDockerfileBuilder WithLabel(string labelName, string value)
{
var tmpLabels = this.configuration.Labels.ToDictionary(kv => kv.Key, kv => kv.Value);

if (tmpLabels.ContainsKey(labelName))
{
tmpLabels[labelName] = value;
}
else
{
tmpLabels.Add(labelName, value);
}

return new ImageFromDockerfileBuilder(
new ImageFromDockerfileConfiguration(this.configuration.Image, this.configuration.Dockerfile, this.configuration.DockerfileDirectory, this.configuration.DeleteIfExists, tmpLabels));
}

/// <inheritdoc />
public IImageFromDockerfileBuilder WithResourceReaperSessionId(Guid? resourceReaperSessionId)
{
return this.WithLabel(ResourceReaper.ResourceReaperSessionLabel, resourceReaperSessionId?.ToString("D"));
}

/// <inheritdoc />
Expand Down
45 changes: 39 additions & 6 deletions src/DotNet.Testcontainers/Builders/TestcontainersBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ namespace DotNet.Testcontainers.Builders
using System.Threading.Tasks;
using DotNet.Testcontainers.Clients;
using DotNet.Testcontainers.Configurations;
using DotNet.Testcontainers.Configurations.Containers;
using DotNet.Testcontainers.Configurations.Images;
using DotNet.Testcontainers.Containers;
using DotNet.Testcontainers.Images;
using DotNet.Testcontainers.Network;
Expand Down Expand Up @@ -235,8 +237,29 @@ public ITestcontainersBuilder<TDockerContainer> WithNetwork(IDockerNetwork docke
/// <inheritdoc />
public ITestcontainersBuilder<TDockerContainer> WithCleanUp(bool cleanUp)
{
return Build(this, Apply(cleanUp: cleanUp))
.WithLabel(TestcontainersClient.TestcontainersCleanUpLabel, cleanUp.ToString());
if (cleanUp)
{
if (string.IsNullOrWhiteSpace(this.configuration.Labels[ResourceReaper.ResourceReaperSessionLabel]))
{
return this.WithResourceReaperSessionId(ResourceReaper.DefaultSessionId);
}

return this;
}

return this.WithResourceReaperSessionId(null);
}

/// <inheritdoc />
public ITestcontainersBuilder<TDockerContainer> WithAutoRemove(bool autoRemove)
{
return Build(this, Apply(autoRemove: autoRemove));
}

/// <inheritdoc />
public ITestcontainersBuilder<TDockerContainer> WithPrivileged(bool privileged)
{
return Build(this, Apply(privileged: privileged));
}

/// <inheritdoc />
Expand Down Expand Up @@ -269,6 +292,12 @@ public ITestcontainersBuilder<TDockerContainer> WithStartupCallback(Func<IRunnin
return Build(this, Apply(startupCallback: startupCallback));
}

/// <inheritdoc />
public ITestcontainersBuilder<TDockerContainer> WithResourceReaperSessionId(Guid? resourceReaperSessionId)
{
return this.WithLabel(ResourceReaper.ResourceReaperSessionLabel, resourceReaperSessionId?.ToString("D"));
}

/// <inheritdoc />
public TDockerContainer Build()
{
Expand Down Expand Up @@ -305,7 +334,8 @@ public TDockerContainer Build()
IOutputConsumer outputConsumer = null,
IEnumerable<IWaitUntil> waitStrategies = null,
Func<ITestcontainersContainer, CancellationToken, Task> startupCallback = null,
bool cleanUp = true)
bool? autoRemove = null,
bool? privileged = null)
{
return new TestcontainersConfiguration(
endpoint,
Expand All @@ -325,7 +355,8 @@ public TDockerContainer Build()
outputConsumer,
waitStrategies,
startupCallback,
cleanUp);
autoRemove,
privileged);
}

#pragma warning restore S107
Expand All @@ -335,7 +366,8 @@ public TDockerContainer Build()
ITestcontainersConfiguration next,
Action<TDockerContainer> moduleConfiguration = null)
{
var cleanUp = next.CleanUp && previous.configuration.CleanUp;
var autoRemove = next.AutoRemove ?? previous.configuration.AutoRemove;
var privileged = next.Privileged ?? previous.configuration.Privileged;
var endpoint = BuildConfiguration.Combine(next.Endpoint, previous.configuration.Endpoint);
var image = BuildConfiguration.Combine(next.Image, previous.configuration.Image);
var name = BuildConfiguration.Combine(next.Name, previous.configuration.Name);
Expand Down Expand Up @@ -373,7 +405,8 @@ public TDockerContainer Build()
outputConsumer,
waitStrategies,
startupCallback,
cleanUp);
autoRemove,
privileged);

return new TestcontainersBuilder<TDockerContainer>(mergedConfiguration, moduleConfiguration ?? previous.moduleConfiguration);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace DotNet.Testcontainers.Builders
{
using DotNet.Testcontainers.Containers.Modules.Misc;

/// <summary>
/// This class applies the extended Testcontainer configurations for the resource reaper.
/// </summary>
public static class TestcontainersBuilderResourceReaperExtension
{
public static ITestcontainersBuilder<T> WithResourceReaper<T>(this ITestcontainersBuilder<T> builder, ResourceReaperContainerConfiguration configuration)
where T : ResourceReaperContainer
{
return builder
.WithName(configuration.Name)
.WithImage(configuration.Image)
.WithAutoRemove(true)
.WithCleanUp(false)
.WithPortBinding(configuration.Port, configuration.DefaultPort)
.WithExposedPort(configuration.DefaultPort)
.WithWaitStrategy(configuration.WaitStrategy)
.WithMount(configuration.HostDockerSocketPath, "/var/run/docker.sock");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ public ITestcontainersNetworkBuilder WithLabel(string name, string value)
return Build(this, Apply(labels: labels));
}

/// <inheritdoc />
public ITestcontainersNetworkBuilder WithResourceReaperSessionId(Guid? resourceReaperSessionId)
{
return this.WithLabel(ResourceReaper.ResourceReaperSessionLabel, resourceReaperSessionId?.ToString("D"));
}

/// <inheritdoc />
public IDockerNetwork Build()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ public ITestcontainersVolumeBuilder WithLabel(string name, string value)
return Build(this, Apply(labels: labels));
}

/// <inheritdoc />
public ITestcontainersVolumeBuilder WithResourceReaperSessionId(Guid? resourceReaperSessionId)
{
return this.WithLabel(ResourceReaper.ResourceReaperSessionLabel, resourceReaperSessionId?.ToString("D"));
}

/// <inheritdoc />
public IDockerVolume Build()
{
Expand Down
3 changes: 2 additions & 1 deletion src/DotNet.Testcontainers/Clients/DefaultLabels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ namespace DotNet.Testcontainers.Clients
{
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Configurations;

internal sealed class DefaultLabels : ReadOnlyDictionary<string, string>
{
private DefaultLabels()
: base(new Dictionary<string, string>
{
{ TestcontainersClient.TestcontainersLabel, bool.TrueString },
{ TestcontainersClient.TestcontainersCleanUpLabel, bool.TrueString },
{ ResourceReaper.ResourceReaperSessionLabel, ResourceReaper.DefaultSessionId.ToString("D") }
})
{
}
Expand Down
10 changes: 3 additions & 7 deletions src/DotNet.Testcontainers/Clients/DockerContainerOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace DotNet.Testcontainers.Clients
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Configurations.Containers;
using Docker.DotNet.Models;
using DotNet.Testcontainers.Configurations;
using DotNet.Testcontainers.Containers;
Expand All @@ -27,13 +28,6 @@ public async Task<IEnumerable<ContainerListResponse>> GetAllAsync(CancellationTo
.ConfigureAwait(false)).ToArray();
}

public async Task<IEnumerable<ContainerListResponse>> GetOrphanedObjects(CancellationToken ct = default)
{
var filters = new FilterByProperty { { "label", $"{TestcontainersClient.TestcontainersCleanUpLabel}={bool.TrueString}" }, { "status", "exited" } };
return (await this.Docker.Containers.ListContainersAsync(new ContainersListParameters { All = true, Filters = filters }, ct)
.ConfigureAwait(false)).ToArray();
}

public Task<ContainerListResponse> ByIdAsync(string id, CancellationToken ct = default)
{
return this.ByPropertyAsync("id", id, ct);
Expand Down Expand Up @@ -151,6 +145,8 @@ public async Task<string> RunAsync(ITestcontainersConfiguration configuration, C

var hostConfig = new HostConfig
{
AutoRemove = configuration.AutoRemove ?? false,
Privileged = configuration.Privileged ?? false,
PortBindings = converter.PortBindings,
Mounts = converter.Mounts,
};
Expand Down
Loading

0 comments on commit 7c18985

Please sign in to comment.