Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ public interface IImageFromDockerfileBuilder<out TBuilderEntity>
[PublicAPI]
TBuilderEntity WithDockerfileDirectory(CommonDirectoryPath commonDirectoryPath, string dockerfileDirectory);

/// <summary>
/// Sets the target build stage for the Docker image, allowing partial builds for
/// multi-stage Dockerfiles.
/// </summary>
/// <param name="target">The target build stage to use for the image build.</param>
/// <returns>A configured instance of <typeparamref name="TBuilderEntity" />.</returns>
[PublicAPI]
TBuilderEntity WithTarget(string target);

/// <summary>
/// Sets the image build policy.
/// </summary>
Expand Down
6 changes: 6 additions & 0 deletions src/Testcontainers/Builders/ImageFromDockerfileBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ public ImageFromDockerfileBuilder WithDockerfileDirectory(CommonDirectoryPath co
return Merge(DockerResourceConfiguration, new ImageFromDockerfileConfiguration(dockerfileDirectory: dockerfileDirectoryPath));
}

/// <inheritdoc />
public ImageFromDockerfileBuilder WithTarget(string target)
{
return Merge(DockerResourceConfiguration, new ImageFromDockerfileConfiguration(target: target));
}

/// <inheritdoc />
public ImageFromDockerfileBuilder WithImageBuildPolicy(Func<ImageInspectResponse, bool> imageBuildPolicy)
{
Expand Down
1 change: 1 addition & 0 deletions src/Testcontainers/Clients/DockerImageOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ await DeleteAsync(image, ct)
var buildParameters = new ImageBuildParameters
{
Dockerfile = configuration.Dockerfile,
Target = configuration.Target,
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),
Expand Down
8 changes: 4 additions & 4 deletions src/Testcontainers/Clients/TraceProgress.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,22 @@ public void Report(JSONMessage value)

if (!string.IsNullOrWhiteSpace(value.Status))
{
_logger.LogDebug(value.Status);
_logger.LogDebug(value.Status.TrimEnd());
}

if (!string.IsNullOrWhiteSpace(value.Stream))
{
_logger.LogDebug(value.Stream);
_logger.LogDebug(value.Stream.TrimEnd());
}

if (!string.IsNullOrWhiteSpace(value.ProgressMessage))
{
_logger.LogDebug(value.ProgressMessage);
_logger.LogDebug(value.ProgressMessage.TrimEnd());
}

if (!string.IsNullOrWhiteSpace(value.ErrorMessage))
{
_logger.LogError(value.ErrorMessage);
_logger.LogError(value.ErrorMessage.TrimEnd());
}

#pragma warning restore CA1848, CA2254
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ public interface IImageFromDockerfileConfiguration : IResourceConfiguration<Imag
/// </summary>
string DockerfileDirectory { get; }

/// <summary>
/// Gets the target.
/// </summary>
string Target { get; }

/// <summary>
/// Gets the image.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,23 @@ internal sealed class ImageFromDockerfileConfiguration : ResourceConfiguration<I
/// </summary>
/// <param name="dockerfile">The Dockerfile.</param>
/// <param name="dockerfileDirectory">The Dockerfile directory.</param>
/// <param name="target">The target.</param>
/// <param name="image">The image.</param>
/// <param name="imageBuildPolicy">The image build policy.</param>
/// <param name="buildArguments">A list of build arguments.</param>
/// <param name="deleteIfExists">A value indicating whether Testcontainers removes an existing image or not.</param>
public ImageFromDockerfileConfiguration(
string dockerfile = null,
string dockerfileDirectory = null,
string target = null,
IImage image = null,
Func<ImageInspectResponse, bool> imageBuildPolicy = null,
IReadOnlyDictionary<string, string> buildArguments = null,
bool? deleteIfExists = null)
{
Dockerfile = dockerfile;
DockerfileDirectory = dockerfileDirectory;
Target = target;
Image = image;
ImageBuildPolicy = imageBuildPolicy;
BuildArguments = buildArguments;
Expand Down Expand Up @@ -65,6 +68,7 @@ public ImageFromDockerfileConfiguration(IImageFromDockerfileConfiguration oldVal
{
Dockerfile = BuildConfiguration.Combine(oldValue.Dockerfile, newValue.Dockerfile);
DockerfileDirectory = BuildConfiguration.Combine(oldValue.DockerfileDirectory, newValue.DockerfileDirectory);
Target = BuildConfiguration.Combine(oldValue.Target, newValue.Target);
Image = BuildConfiguration.Combine(oldValue.Image, newValue.Image);
ImageBuildPolicy = BuildConfiguration.Combine(oldValue.ImageBuildPolicy, newValue.ImageBuildPolicy);
BuildArguments = BuildConfiguration.Combine(oldValue.BuildArguments, newValue.BuildArguments);
Expand All @@ -83,6 +87,10 @@ public ImageFromDockerfileConfiguration(IImageFromDockerfileConfiguration oldVal
[JsonIgnore]
public string DockerfileDirectory { get; }

/// <inheritdoc />
[JsonIgnore]
public string Target { get; }

/// <inheritdoc />
[JsonIgnore]
public IImage Image { get; }
Expand Down
1 change: 1 addition & 0 deletions tests/Testcontainers.Tests/Assets/.dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ credsStore
healthWaitStrategy
pullBaseImages
scratch
target
**/*.md
3 changes: 3 additions & 0 deletions tests/Testcontainers.Tests/Assets/target/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM scratch AS base
FROM base AS build
FROM build AS final
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace DotNet.Testcontainers.Tests.Unit
using DotNet.Testcontainers.Commons;
using DotNet.Testcontainers.Images;
using ICSharpCode.SharpZipLib.Tar;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;

Expand Down Expand Up @@ -168,5 +169,42 @@ await imageFromDockerfileBuilder.CreateAsync(TestContext.Current.CancellationTok
Assert.NotNull(imageFromDockerfileBuilder.FullName);
Assert.Null(imageFromDockerfileBuilder.GetHostname());
}

[Fact]
public async Task BuildTargetBuildsUpToExpectedTarget()
{
// Given
var logger = new TestLogger();

var imageFromDockerfileBuilder = new ImageFromDockerfileBuilder()
.WithDockerfileDirectory("Assets/target")
.WithTarget("build")
.WithLogger(logger)
.Build();

// When
await imageFromDockerfileBuilder.CreateAsync(TestContext.Current.CancellationToken)
.ConfigureAwait(true);

// Then
Assert.Contains(logger.Logs, line => line.Contains("FROM scratch AS base"));
Assert.Contains(logger.Logs, line => line.Contains("FROM base AS build"));
Assert.DoesNotContain(logger.Logs, line => line.Contains("FROM build AS final"));
}

private sealed class TestLogger : ILogger
{
public IList<string> Logs { get; }
= new List<string>();

public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
=> Logs.Add(formatter(state, exception));

public bool IsEnabled(LogLevel logLevel)
=> true;

public IDisposable BeginScope<TState>(TState state) where TState : notnull
=> null;
}
}
}