@@ -5,13 +5,15 @@ namespace DotNet.Testcontainers.Clients
5
5
using System . IO ;
6
6
using System . Linq ;
7
7
using System . Text ;
8
+ using System . Text . RegularExpressions ;
8
9
using System . Threading ;
9
10
using System . Threading . Tasks ;
10
11
using Docker . DotNet ;
11
12
using Docker . DotNet . Models ;
12
13
using DotNet . Testcontainers . Builders ;
13
14
using DotNet . Testcontainers . Configurations ;
14
15
using DotNet . Testcontainers . Containers ;
16
+ using DotNet . Testcontainers . Images ;
15
17
using ICSharpCode . SharpZipLib . Tar ;
16
18
using Microsoft . Extensions . Logging ;
17
19
@@ -28,6 +30,8 @@ internal sealed class TestcontainersClient : ITestcontainersClient
28
30
29
31
private static readonly string OSRootDirectory = Path . GetPathRoot ( Directory . GetCurrentDirectory ( ) ) ;
30
32
33
+ private static readonly Regex FromLinePattern = new Regex ( "FROM (?<arg>--[^\\ s]+\\ s)*(?<image>[^\\ s]+).*" , RegexOptions . None , TimeSpan . FromSeconds ( 1 ) ) ;
34
+
31
35
private readonly DockerRegistryAuthenticationProvider _registryAuthenticationProvider ;
32
36
33
37
/// <summary>
@@ -290,19 +294,7 @@ public async Task<string> RunAsync(IContainerConfiguration configuration, Cancel
290
294
291
295
if ( configuration . ImagePullPolicy ( cachedImage ) )
292
296
{
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 )
306
298
. ConfigureAwait ( false ) ;
307
299
}
308
300
@@ -327,9 +319,22 @@ await Task.WhenAll(configuration.ResourceMappings.Values.Select(resourceMapping
327
319
/// <inheritdoc />
328
320
public async Task < string > BuildAsync ( IImageFromDockerfileConfiguration configuration , CancellationToken ct = default )
329
321
{
322
+ var dockerfileFilePath = Path . Combine ( configuration . DockerfileDirectory , configuration . Dockerfile ) ;
323
+
330
324
var cachedImage = await Image . ByNameAsync ( configuration . Image . FullName , ct )
331
325
. ConfigureAwait ( false ) ;
332
326
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
+
333
338
if ( configuration . ImageBuildPolicy ( cachedImage ) )
334
339
{
335
340
_ = await Image . BuildAsync ( configuration , ct )
@@ -338,5 +343,28 @@ public async Task<string> BuildAsync(IImageFromDockerfileConfiguration configura
338
343
339
344
return configuration . Image . FullName ;
340
345
}
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
+ }
341
369
}
342
370
}
0 commit comments