Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Pull dependent images from private registries while building an image #951

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
54 changes: 41 additions & 13 deletions src/Testcontainers/Clients/TestcontainersClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ namespace DotNet.Testcontainers.Clients
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Docker.DotNet;
using Docker.DotNet.Models;
using DotNet.Testcontainers.Builders;
using DotNet.Testcontainers.Configurations;
using DotNet.Testcontainers.Containers;
using DotNet.Testcontainers.Images;
using ICSharpCode.SharpZipLib.Tar;
using Microsoft.Extensions.Logging;

Expand All @@ -28,6 +30,8 @@ internal sealed class TestcontainersClient : ITestcontainersClient

private static readonly string OSRootDirectory = Path.GetPathRoot(Directory.GetCurrentDirectory());

private static readonly Regex FromLinePattern = new Regex("FROM (?<arg>--[^\\s]+\\s)*(?<image>[^\\s]+).*", RegexOptions.None, TimeSpan.FromSeconds(1));

private readonly DockerRegistryAuthenticationProvider _registryAuthenticationProvider;

/// <summary>
Expand Down Expand Up @@ -290,19 +294,7 @@ public async Task<string> RunAsync(IContainerConfiguration configuration, Cancel

if (configuration.ImagePullPolicy(cachedImage))
{
var dockerRegistryServerAddress = configuration.Image.GetHostname();

if (dockerRegistryServerAddress == null)
{
var info = await System.GetInfoAsync(ct)
.ConfigureAwait(false);

dockerRegistryServerAddress = info.IndexServerAddress;
}

var authConfig = _registryAuthenticationProvider.GetAuthConfig(dockerRegistryServerAddress);

await Image.CreateAsync(configuration.Image, authConfig, ct)
await PullImageAsync(configuration.Image, ct)
.ConfigureAwait(false);
}

Expand All @@ -327,9 +319,22 @@ await Task.WhenAll(configuration.ResourceMappings.Values.Select(resourceMapping
/// <inheritdoc />
public async Task<string> BuildAsync(IImageFromDockerfileConfiguration configuration, CancellationToken ct = default)
{
var dockerfileFilePath = Path.Combine(configuration.DockerfileDirectory, configuration.Dockerfile);

var cachedImage = await Image.ByNameAsync(configuration.Image.FullName, ct)
.ConfigureAwait(false);

if (File.Exists(dockerfileFilePath))
{
await Task.WhenAll(File.ReadAllLines(dockerfileFilePath)
.Select(line => FromLinePattern.Match(line))
.Where(match => match.Success)
.Select(match => match.Groups["image"])
.Select(group => group.Value)
.Select(image => new DockerImage(image))
benjaminangerer marked this conversation as resolved.
Show resolved Hide resolved
.Select(image => PullImageAsync(image, ct)));
}

if (configuration.ImageBuildPolicy(cachedImage))
{
_ = await Image.BuildAsync(configuration, ct)
Expand All @@ -338,5 +343,28 @@ public async Task<string> BuildAsync(IImageFromDockerfileConfiguration configura

return configuration.Image.FullName;
}

/// <summary>
/// Pulls an image from a registry.
/// </summary>
/// <param name="image">The image to pull.</param>
/// <param name="ct">Cancellation token.</param>
private async Task PullImageAsync(IImage image, CancellationToken ct = default)
{
var dockerRegistryServerAddress = image.GetHostname();

if (dockerRegistryServerAddress == null)
{
var info = await System.GetInfoAsync(ct)
.ConfigureAwait(false);

dockerRegistryServerAddress = info.IndexServerAddress;
}

var authConfig = _registryAuthenticationProvider.GetAuthConfig(dockerRegistryServerAddress);

await Image.CreateAsync(image, authConfig, ct)
.ConfigureAwait(false);
}
}
}
2 changes: 1 addition & 1 deletion src/Testcontainers/Images/IgnoreFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public IgnoreFile(IEnumerable<string> patterns, ILogger logger)
return new KeyValuePair<string, bool>(key, value);
})

// Compile and cache regular expression to increase the performance.
// Cache regular expression to increase the performance.
.Select(ignorePattern =>
{
var key = ignorePattern.Key;
Expand Down