Skip to content

Commit ad14e24

Browse files
feat: Pull dependent images from private registries while building an image (#951)
Co-authored-by: Andre Hofmeister <9199345+HofmeisterAn@users.noreply.github.com>
1 parent 762feda commit ad14e24

File tree

2 files changed

+42
-14
lines changed

2 files changed

+42
-14
lines changed

src/Testcontainers/Clients/TestcontainersClient.cs

+41-13
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ namespace DotNet.Testcontainers.Clients
55
using System.IO;
66
using System.Linq;
77
using System.Text;
8+
using System.Text.RegularExpressions;
89
using System.Threading;
910
using System.Threading.Tasks;
1011
using Docker.DotNet;
1112
using Docker.DotNet.Models;
1213
using DotNet.Testcontainers.Builders;
1314
using DotNet.Testcontainers.Configurations;
1415
using DotNet.Testcontainers.Containers;
16+
using DotNet.Testcontainers.Images;
1517
using ICSharpCode.SharpZipLib.Tar;
1618
using Microsoft.Extensions.Logging;
1719

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

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

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

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

291295
if (configuration.ImagePullPolicy(cachedImage))
292296
{
293-
var dockerRegistryServerAddress = configuration.Image.GetHostname();
294-
295-
if (dockerRegistryServerAddress == null)
296-
{
297-
var info = await System.GetInfoAsync(ct)
298-
.ConfigureAwait(false);
299-
300-
dockerRegistryServerAddress = info.IndexServerAddress;
301-
}
302-
303-
var authConfig = _registryAuthenticationProvider.GetAuthConfig(dockerRegistryServerAddress);
304-
305-
await Image.CreateAsync(configuration.Image, authConfig, ct)
297+
await PullImageAsync(configuration.Image, ct)
306298
.ConfigureAwait(false);
307299
}
308300

@@ -327,9 +319,22 @@ await Task.WhenAll(configuration.ResourceMappings.Values.Select(resourceMapping
327319
/// <inheritdoc />
328320
public async Task<string> BuildAsync(IImageFromDockerfileConfiguration configuration, CancellationToken ct = default)
329321
{
322+
var dockerfileFilePath = Path.Combine(configuration.DockerfileDirectory, configuration.Dockerfile);
323+
330324
var cachedImage = await Image.ByNameAsync(configuration.Image.FullName, ct)
331325
.ConfigureAwait(false);
332326

327+
if (File.Exists(dockerfileFilePath))
328+
{
329+
await Task.WhenAll(File.ReadAllLines(dockerfileFilePath)
330+
.Select(line => FromLinePattern.Match(line))
331+
.Where(match => match.Success)
332+
.Select(match => match.Groups["image"])
333+
.Select(group => group.Value)
334+
.Select(image => new DockerImage(image))
335+
.Select(image => PullImageAsync(image, ct)));
336+
}
337+
333338
if (configuration.ImageBuildPolicy(cachedImage))
334339
{
335340
_ = await Image.BuildAsync(configuration, ct)
@@ -338,5 +343,28 @@ public async Task<string> BuildAsync(IImageFromDockerfileConfiguration configura
338343

339344
return configuration.Image.FullName;
340345
}
346+
347+
/// <summary>
348+
/// Pulls an image from a registry.
349+
/// </summary>
350+
/// <param name="image">The image to pull.</param>
351+
/// <param name="ct">Cancellation token.</param>
352+
private async Task PullImageAsync(IImage image, CancellationToken ct = default)
353+
{
354+
var dockerRegistryServerAddress = image.GetHostname();
355+
356+
if (dockerRegistryServerAddress == null)
357+
{
358+
var info = await System.GetInfoAsync(ct)
359+
.ConfigureAwait(false);
360+
361+
dockerRegistryServerAddress = info.IndexServerAddress;
362+
}
363+
364+
var authConfig = _registryAuthenticationProvider.GetAuthConfig(dockerRegistryServerAddress);
365+
366+
await Image.CreateAsync(image, authConfig, ct)
367+
.ConfigureAwait(false);
368+
}
341369
}
342370
}

src/Testcontainers/Images/IgnoreFile.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public IgnoreFile(IEnumerable<string> patterns, ILogger logger)
9696
return new KeyValuePair<string, bool>(key, value);
9797
})
9898

99-
// Compile and cache regular expression to increase the performance.
99+
// Cache regular expression to increase the performance.
100100
.Select(ignorePattern =>
101101
{
102102
var key = ignorePattern.Key;

0 commit comments

Comments
 (0)