Skip to content

Commit

Permalink
feat: Invoke create parameter modifier for image, network, volume bui…
Browse files Browse the repository at this point in the history
…ld or create (#746)
  • Loading branch information
HofmeisterAn authored Jan 30, 2023
1 parent 3eec988 commit e3ecf20
Show file tree
Hide file tree
Showing 10 changed files with 139 additions and 48 deletions.
4 changes: 2 additions & 2 deletions src/Testcontainers/Builders/ImageFromDockerfileBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
/// </code>
/// </example>
[PublicAPI]
public class ImageFromDockerfileBuilder : AbstractBuilder<ImageFromDockerfileBuilder, IFutureDockerImage, ImagesCreateParameters, IImageFromDockerfileConfiguration>, IImageFromDockerfileBuilder<ImageFromDockerfileBuilder>
public class ImageFromDockerfileBuilder : AbstractBuilder<ImageFromDockerfileBuilder, IFutureDockerImage, ImageBuildParameters, IImageFromDockerfileConfiguration>, IImageFromDockerfileBuilder<ImageFromDockerfileBuilder>
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageFromDockerfileBuilder" /> class.
Expand Down Expand Up @@ -112,7 +112,7 @@ protected sealed override ImageFromDockerfileBuilder Init()
}

/// <inheritdoc />
protected override ImageFromDockerfileBuilder Clone(IResourceConfiguration<ImagesCreateParameters> resourceConfiguration)
protected override ImageFromDockerfileBuilder Clone(IResourceConfiguration<ImageBuildParameters> resourceConfiguration)
{
return this.Merge(this.DockerResourceConfiguration, new ImageFromDockerfileConfiguration(resourceConfiguration));
}
Expand Down
10 changes: 9 additions & 1 deletion src/Testcontainers/Clients/DockerImageOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,19 @@ await this.DeleteAsync(image, ct)
var buildParameters = new ImageBuildParameters
{
Dockerfile = configuration.Dockerfile,
Tags = new[] { image.FullName },
Tags = new List<string> { image.FullName },
BuildArgs = configuration.BuildArguments?.ToDictionary(item => item.Key, item => item.Value),
Labels = configuration.Labels?.ToDictionary(item => item.Key, item => item.Value),
};

if (configuration.ParameterModifiers != null)
{
foreach (var parameterModifier in configuration.ParameterModifiers)
{
parameterModifier(buildParameters);
}
}

var dockerfileArchiveFilePath = await dockerfileArchive.Tar(ct)
.ConfigureAwait(false);

Expand Down
8 changes: 8 additions & 0 deletions src/Testcontainers/Clients/DockerNetworkOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ public async Task<string> CreateAsync(INetworkConfiguration configuration, Cance
Labels = configuration.Labels?.ToDictionary(item => item.Key, item => item.Value),
};

if (configuration.ParameterModifiers != null)
{
foreach (var parameterModifier in configuration.ParameterModifiers)
{
parameterModifier(createParameters);
}
}

var createNetworkResponse = await this.Docker.Networks.CreateNetworkAsync(createParameters, ct)
.ConfigureAwait(false);

Expand Down
8 changes: 8 additions & 0 deletions src/Testcontainers/Clients/DockerVolumeOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ public async Task<string> CreateAsync(IVolumeConfiguration configuration, Cancel
Labels = configuration.Labels?.ToDictionary(item => item.Key, item => item.Value),
};

if (configuration.ParameterModifiers != null)
{
foreach (var parameterModifier in configuration.ParameterModifiers)
{
parameterModifier(createParameters);
}
}

var createVolumeResponse = await this.Docker.Volumes.CreateAsync(createParameters, ct)
.ConfigureAwait(false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
/// An image configuration.
/// </summary>
[PublicAPI]
public interface IImageFromDockerfileConfiguration : IResourceConfiguration<ImagesCreateParameters>
public interface IImageFromDockerfileConfiguration : IResourceConfiguration<ImageBuildParameters>
{
/// <summary>
/// Gets a value indicating whether Testcontainers removes an existing image or not.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

/// <inheritdoc cref="IImageFromDockerfileConfiguration" />
[PublicAPI]
internal sealed class ImageFromDockerfileConfiguration : ResourceConfiguration<ImagesCreateParameters>, IImageFromDockerfileConfiguration
internal sealed class ImageFromDockerfileConfiguration : ResourceConfiguration<ImageBuildParameters>, IImageFromDockerfileConfiguration
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageFromDockerfileConfiguration" /> class.
Expand Down Expand Up @@ -36,7 +36,7 @@ public ImageFromDockerfileConfiguration(
/// Initializes a new instance of the <see cref="ImageFromDockerfileConfiguration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public ImageFromDockerfileConfiguration(IResourceConfiguration<ImagesCreateParameters> resourceConfiguration)
public ImageFromDockerfileConfiguration(IResourceConfiguration<ImageBuildParameters> resourceConfiguration)
: base(resourceConfiguration)
{
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public async Task QueryContainerInformationOfCreatedContainer()
Assert.NotEmpty(testcontainer.Name);
Assert.NotEmpty(testcontainer.IpAddress);
Assert.NotEmpty(testcontainer.MacAddress);
Assert.NotEmpty(testcontainer.Hostname);
}
}

Expand All @@ -82,10 +83,13 @@ public async Task QueryContainerInformationOfNotCreatedContainer()
// Then
await using (ITestcontainersContainer testcontainer = testcontainersBuilder.Build())
{
Assert.Throws<InvalidOperationException>(() => testcontainer.Id);
Assert.Throws<InvalidOperationException>(() => testcontainer.Name);
Assert.Throws<InvalidOperationException>(() => testcontainer.IpAddress);
Assert.Throws<InvalidOperationException>(() => testcontainer.MacAddress);
Assert.Throws<InvalidOperationException>(() => testcontainer.GetMappedPublicPort(0));
Assert.Equal(TestcontainersStates.Undefined, testcontainer.State);
Assert.Equal(TestcontainersHealthStatus.Undefined, testcontainer.Health);
}
}

Expand Down
20 changes: 13 additions & 7 deletions tests/Testcontainers.Tests/Unit/Images/ImageFromDockerfileTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,16 @@ public async Task ThrowsDockerfileDirectoryDoesNotExist()
public async Task BuildsDockerImage()
{
// Given
IImage tag1 = new DockerImage("localhost/testcontainers", Guid.NewGuid().ToString("D"), string.Empty);

IImage tag2 = new DockerImage("localhost/testcontainers", Guid.NewGuid().ToString("D"), string.Empty);

var imageFromDockerfileBuilder = new ImageFromDockerfileBuilder()
.WithName("alpine:custom")
.WithName(tag1)
.WithDockerfile("Dockerfile")
.WithDockerfileDirectory("Assets")
.WithDeleteIfExists(true)
.WithCreateParameterModifier(parameterModifier => parameterModifier.Tags.Add(tag2.FullName))
.Build();

// When
Expand All @@ -99,12 +104,13 @@ await imageFromDockerfileBuilder.CreateAsync()
.ConfigureAwait(false);

// Then
Assert.True(DockerCli.ResourceExists(DockerCli.DockerResource.Image, imageFromDockerfileBuilder.FullName));
Assert.Null(Record.Exception(() => imageFromDockerfileBuilder.Repository));
Assert.Null(Record.Exception(() => imageFromDockerfileBuilder.Name));
Assert.Null(Record.Exception(() => imageFromDockerfileBuilder.Tag));
Assert.Null(Record.Exception(() => imageFromDockerfileBuilder.FullName));
Assert.Null(Record.Exception(() => imageFromDockerfileBuilder.GetHostname()));
Assert.True(DockerCli.ResourceExists(DockerCli.DockerResource.Image, tag1.FullName));
Assert.True(DockerCli.ResourceExists(DockerCli.DockerResource.Image, tag2.FullName));
Assert.NotNull(imageFromDockerfileBuilder.Repository);
Assert.NotNull(imageFromDockerfileBuilder.Name);
Assert.NotNull(imageFromDockerfileBuilder.Tag);
Assert.NotNull(imageFromDockerfileBuilder.FullName);
Assert.Null(imageFromDockerfileBuilder.GetHostname());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,21 @@ namespace DotNet.Testcontainers.Tests.Unit
using DotNet.Testcontainers.Builders;
using DotNet.Testcontainers.Clients;
using DotNet.Testcontainers.Configurations;
using DotNet.Testcontainers.Containers;
using DotNet.Testcontainers.Networks;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;

public sealed class TestcontainerNetworkBuilderTest : IClassFixture<TestcontainerNetworkBuilderTest.DockerNetwork>
{
private static readonly string NetworkName = Guid.NewGuid().ToString("D");

private static readonly KeyValuePair<string, string> Option = new KeyValuePair<string, string>("com.docker.network.driver.mtu", "1350");

private static readonly KeyValuePair<string, string> Label = new KeyValuePair<string, string>(TestcontainersClient.TestcontainersLabel + ".network.test", Guid.NewGuid().ToString("D"));

private static readonly KeyValuePair<string, string> Option = new KeyValuePair<string, string>("com.docker.network.driver.mtu", "1350");
private static readonly KeyValuePair<string, string> ParameterModifier = new KeyValuePair<string, string>(TestcontainersClient.TestcontainersLabel + ".parameter.modifier", Guid.NewGuid().ToString("D"));

private readonly IDockerNetwork network;

Expand Down Expand Up @@ -50,42 +54,41 @@ public void GetNameReturnsNetworkName()
}

[Fact]
public async Task CreateNetworkAssignsLabels()
public async Task CreateNetworkAssignsOptions()
{
// Given
using var dockerClientConfiguration = TestcontainersSettings.OS.DockerEndpointAuthConfig.GetDockerClientConfiguration();
using var dockerClient = dockerClientConfiguration.CreateClient();
IDockerNetworkOperations networkOperations = new DockerNetworkOperations(ResourceReaper.DefaultSessionId, TestcontainersSettings.OS.DockerEndpointAuthConfig, NullLogger.Instance);

// When
var networkResponse = await dockerClient.Networks.InspectNetworkAsync(this.network.Id)
var networkResponse = await networkOperations.ByNameAsync(this.network.Name)
.ConfigureAwait(false);

// Then
Assert.Equal(Label.Value, Assert.Contains(Label.Key, networkResponse.Labels));
Assert.Equal(Option.Value, Assert.Contains(Option.Key, networkResponse.Options));
}

[Fact]
public async Task CreateNetworkAssignsOptions()
public async Task CreateNetworkAssignsLabels()
{
// Given
using var dockerClientConfiguration = TestcontainersSettings.OS.DockerEndpointAuthConfig.GetDockerClientConfiguration();
using var dockerClient = dockerClientConfiguration.CreateClient();
IDockerNetworkOperations networkOperations = new DockerNetworkOperations(ResourceReaper.DefaultSessionId, TestcontainersSettings.OS.DockerEndpointAuthConfig, NullLogger.Instance);

// When
var networkResponse = await dockerClient.Networks.InspectNetworkAsync(this.network.Id)
var networkResponse = await networkOperations.ByNameAsync(this.network.Name)
.ConfigureAwait(false);

// Then
Assert.Equal(Option.Value, Assert.Contains(Option.Key, networkResponse.Options));
Assert.Equal(Label.Value, Assert.Contains(Label.Key, networkResponse.Labels));
Assert.Equal(ParameterModifier.Value, Assert.Contains(ParameterModifier.Key, networkResponse.Labels));
}

[UsedImplicitly]
public sealed class DockerNetwork : IDockerNetwork, IAsyncLifetime
{
private readonly IDockerNetwork network = new TestcontainersNetworkBuilder()
.WithName(NetworkName)
.WithLabel(Label.Key, Label.Value)
.WithOption(Option.Key, Option.Value)
.WithLabel(Label.Key, Label.Value)
.WithCreateParameterModifier(parameterModifier => parameterModifier.Labels.Add(ParameterModifier.Key, ParameterModifier.Value))
.Build();

public string Id
Expand Down
Original file line number Diff line number Diff line change
@@ -1,46 +1,100 @@
namespace DotNet.Testcontainers.Tests.Unit
{
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using DotNet.Testcontainers.Builders;
using DotNet.Testcontainers.Clients;
using DotNet.Testcontainers.Configurations;
using DotNet.Testcontainers.Containers;
using DotNet.Testcontainers.Volumes;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;

public sealed class TestcontainersVolumeBuilderTest
public sealed class TestcontainersVolumeBuilderTest : IClassFixture<TestcontainersVolumeBuilderTest.DockerVolume>
{
private static readonly string VolumeName = Guid.NewGuid().ToString("D");

private static readonly KeyValuePair<string, string> Label = new KeyValuePair<string, string>(TestcontainersClient.TestcontainersLabel + ".volume.test", Guid.NewGuid().ToString("D"));

private static readonly KeyValuePair<string, string> ParameterModifier = new KeyValuePair<string, string>(TestcontainersClient.TestcontainersLabel + ".parameter.modifier", Guid.NewGuid().ToString("D"));

private readonly IDockerVolume volume;

public TestcontainersVolumeBuilderTest(DockerVolume volume)
{
this.volume = volume;
}

[Fact]
public async Task ShouldCreateVolume()
public void GetIdOrNameThrowsInvalidOperationException()
{
// Given
var volumeName = Guid.NewGuid().ToString();
var noSuchVolume = new TestcontainersVolumeBuilder()
.WithName(VolumeName)
.Build();

var volumeLabel = Guid.NewGuid().ToString();
Assert.Throws<InvalidOperationException>(() => noSuchVolume.Name);
}

[Fact]
public void GetNameReturnsVolumeName()
{
Assert.Equal(VolumeName, this.volume.Name);
}

[Fact]
public async Task CreateVolumeAssignsLabels()
{
// Given
IDockerVolumeOperations volumeOperations = new DockerVolumeOperations(ResourceReaper.DefaultSessionId, TestcontainersSettings.OS.DockerEndpointAuthConfig, NullLogger.Instance);

// When
var volume = new TestcontainersVolumeBuilder()
.WithName(volumeName)
.WithLabel("label", volumeLabel)
var volumeResponse = await volumeOperations.ByNameAsync(this.volume.Name)
.ConfigureAwait(false);

// Then
Assert.Equal(Label.Value, Assert.Contains(Label.Key, volumeResponse.Labels));
Assert.Equal(ParameterModifier.Value, Assert.Contains(ParameterModifier.Key, volumeResponse.Labels));
}

[UsedImplicitly]
public sealed class DockerVolume : IDockerVolume, IAsyncLifetime
{
private readonly IDockerVolume volume = new TestcontainersVolumeBuilder()
.WithName(VolumeName)
.WithLabel(Label.Key, Label.Value)
.WithCreateParameterModifier(parameterModifier => parameterModifier.Labels.Add(ParameterModifier.Key, ParameterModifier.Value))
.Build();

await volume.CreateAsync();
public string Name
{
get
{
return this.volume.Name;
}
}

// Then
try
public Task InitializeAsync()
{
Assert.Equal(volumeName, volume.Name);
return this.CreateAsync();
}
finally

public Task DisposeAsync()
{
await volume.DeleteAsync();
return this.DeleteAsync();
}
}

[Fact]
public void ShouldThrowInvalidOperationException()
{
Assert.Throws<InvalidOperationException>(() => new TestcontainersVolumeBuilder()
.WithName(Guid.Empty.ToString())
.Build()
.Name);
public Task CreateAsync(CancellationToken ct = default)
{
return this.volume.CreateAsync(ct);
}

public Task DeleteAsync(CancellationToken ct = default)
{
return this.volume.DeleteAsync(ct);
}
}
}
}

0 comments on commit e3ecf20

Please sign in to comment.