From 78a5428f9593d445256a3b837cface7199a842ec Mon Sep 17 00:00:00 2001 From: Andre Hofmeister Date: Fri, 31 May 2019 21:54:21 +0200 Subject: [PATCH] Added Dockerfile support, closes #57. --- README.md | 5 +- .../Assets/Dockerfile.tar | Bin 323 -> 0 bytes .../DotNet.Testcontainers.Tests.csproj | 6 +- .../Unit/ImageFromDockerfileTest.cs | 2 +- .../Clients/TestcontainersClient.cs | 56 +++++++++++++++--- .../Builder/IImageFromDockerfileBuilder.cs | 2 +- .../Builder/ImageFromDockerfileBuilder.cs | 4 +- .../ImageFromDockerfileConfiguration.cs | 4 +- .../DotNet.Testcontainers.csproj | 2 + 9 files changed, 61 insertions(+), 20 deletions(-) delete mode 100644 src/DotNet.Testcontainers.Tests/Assets/Dockerfile.tar diff --git a/README.md b/README.md index bc9c9e7eb..abbed9359 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ .NET Testcontainers is a library to support tests with throwaway instances of Docker containers for all compatible .NET Standard versions. The library is built on top of the .NET Docker remote API and provides a lightweight implementation to support your test environment in all circumstances. -Choose from existing pre-configured configurations and start containers within a second, to support and run your tests. +Choose from existing pre-configured configurations and start containers within a second, to support and run your tests. Or create your own Testcontainers using Dockerfiles. ## Supported commands @@ -20,8 +20,11 @@ Choose from existing pre-configured configurations and start containers within a - `WithExposedPort` exposes a port inside the container e. g. `--expose=80`. - `WithPortBinding` publishes a container port to the host e. g. `-p, --publish 80:80`. - `WithMount` mounts a volume into the container e. g. `-v, --volume .:/tmp`. +- `WithCleanUp` removes the stopped container automatically. - `WithOutputConsumer` redirects `stdout` and `stderr` to capture the Testcontainer output. - `WithWaitStrategy` sets the wait strategy to complete the Testcontainer start and indicates when it is ready. +- `WithDockerfileDirectory` builds a Docker image based on a Dockerfile (`ImageFromDockerfileBuilder`). +- `WithDeleteIfExists` removes the Docker image before it is rebuilt (`ImageFromDockerfileBuilder`). ## Pre-configured containers diff --git a/src/DotNet.Testcontainers.Tests/Assets/Dockerfile.tar b/src/DotNet.Testcontainers.Tests/Assets/Dockerfile.tar deleted file mode 100644 index bcb1f0689b678999469417e7934fedbd1d9cbf45..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 323 zcmV-J0lfYniwFQ}OX^$z1MSnzO2a@92k1i^@kN=v2f!Gj=6 zvcX`!NVZqspwFO>;B)y3UfgX<#Zpv|DB}MI!(^A)Wi~M8o_TXzgmd8*iY5i)w0#<1V<+wlJO*$DnxbAJ`b|PAdf0>Hvdbe8ryd3x7!B*0000000000 V00000008*+UI7P`Ql9`Q003&+pSu76 diff --git a/src/DotNet.Testcontainers.Tests/DotNet.Testcontainers.Tests.csproj b/src/DotNet.Testcontainers.Tests/DotNet.Testcontainers.Tests.csproj index d239c2ff0..74628fd9f 100644 --- a/src/DotNet.Testcontainers.Tests/DotNet.Testcontainers.Tests.csproj +++ b/src/DotNet.Testcontainers.Tests/DotNet.Testcontainers.Tests.csproj @@ -1,6 +1,7 @@ netcoreapp2.0 + latest false @@ -27,10 +28,5 @@ PreserveNewest - - - PreserveNewest - - diff --git a/src/DotNet.Testcontainers.Tests/Unit/ImageFromDockerfileTest.cs b/src/DotNet.Testcontainers.Tests/Unit/ImageFromDockerfileTest.cs index 4c8398daf..46cd30487 100644 --- a/src/DotNet.Testcontainers.Tests/Unit/ImageFromDockerfileTest.cs +++ b/src/DotNet.Testcontainers.Tests/Unit/ImageFromDockerfileTest.cs @@ -31,7 +31,7 @@ public async Task SimpleDockerfile() var imageFromDockerfile = await new ImageFromDockerfileBuilder() .WithName("alpine:custom") .WithDockerfileDirectory("Assets") - .WithDeleteIfExits(false) + .WithDeleteIfExists(false) .Build(); Assert.NotEmpty(imageFromDockerfile); diff --git a/src/DotNet.Testcontainers/Clients/TestcontainersClient.cs b/src/DotNet.Testcontainers/Clients/TestcontainersClient.cs index 93438526c..d6b0c9b93 100644 --- a/src/DotNet.Testcontainers/Clients/TestcontainersClient.cs +++ b/src/DotNet.Testcontainers/Clients/TestcontainersClient.cs @@ -1,6 +1,7 @@ namespace DotNet.Testcontainers.Clients { using System; + using System.Diagnostics; using System.IO; using System.Linq; using System.Threading; @@ -10,6 +11,8 @@ namespace DotNet.Testcontainers.Clients using DotNet.Testcontainers.Core.Mapper; using DotNet.Testcontainers.Core.Models; using DotNet.Testcontainers.Diagnostics; + using ICSharpCode.SharpZipLib.Tar; + using ICSharpCode.SharpZipLib.Zip; internal class TestcontainersClient : DockerApiClient, ITestcontainersClient { @@ -85,25 +88,26 @@ await WaitStrategy.WaitWhile(async () => public async Task BuildAsync(ImageFromDockerfileConfiguration config) { - var dockerfileDirectoryInfo = new DirectoryInfo(config.DockerfileDirectory); + var dockerfileDirectory = new DirectoryInfo(config.DockerfileDirectory); - if (!dockerfileDirectoryInfo.Exists) + if (!dockerfileDirectory.Exists) { - throw new ArgumentException("Directory does not exist."); + throw new ArgumentException($"Directory '{dockerfileDirectory.FullName}' does not exist."); } - if (!dockerfileDirectoryInfo.GetFiles().Any(file => "Dockerfile".Equals(file.Name))) + if (!dockerfileDirectory.GetFiles().Any(file => "Dockerfile".Equals(file.Name))) { - throw new ArgumentException("Dockerfile does not exist."); + throw new ArgumentException($"Dockerfile does not exist in '{dockerfileDirectory.FullName}'."); } - if (config.DeleteIfExits) + if (config.DeleteIfExists) { await Docker.Images.DeleteImageAsync(config.Image, new ImageDeleteParameters { Force = true }); } - // TODO: Create temp archive (tar) of Dockerfile base directory. - using (var stream = new FileStream($"{config.DockerfileDirectory}/Dockerfile.tar", FileMode.Open)) + var dockerfileArchive = CreateDockerfileArchive(dockerfileDirectory.FullName); + + using (var stream = new FileStream(dockerfileArchive, FileMode.Open)) { await Docker.Images.BuildImageFromDockerfileAsync(stream, new ImageBuildParameters { Dockerfile = "Dockerfile", Tags = new[] { config.Image } }); } @@ -167,5 +171,41 @@ public async Task RunAsync(TestcontainersConfiguration config) return (await Docker.Containers.CreateContainerAsync(createParameters)).ID; } + + private static string CreateDockerfileArchive(string dockerfileRootDirectory) + { + var dockerfileArchiveFile = $"{Path.GetTempPath()}/Dockerfile.tar"; + + using (var dockerfileArchiveStream = File.Create(dockerfileArchiveFile)) + { + using (var dockerfileArchive = TarArchive.CreateOutputTarArchive(dockerfileArchiveStream)) + { + dockerfileArchive.RootPath = dockerfileRootDirectory; + + void Tar(string baseDirectory) + { + void WriteEntry(string entry) + { + var tarEntry = TarEntry.CreateEntryFromFile(entry); + tarEntry.Name = entry.Replace(dockerfileRootDirectory, string.Empty); + dockerfileArchive.WriteEntry(tarEntry, File.Exists(entry)); + } + + if (!dockerfileRootDirectory.Equals(baseDirectory)) + { + WriteEntry(baseDirectory); + } + + Directory.GetFiles(baseDirectory).ToList().ForEach(WriteEntry); + + Directory.GetDirectories(baseDirectory).ToList().ForEach(Tar); + } + + Tar(dockerfileRootDirectory); + } + } + + return dockerfileArchiveFile; + } } } diff --git a/src/DotNet.Testcontainers/Core/Builder/IImageFromDockerfileBuilder.cs b/src/DotNet.Testcontainers/Core/Builder/IImageFromDockerfileBuilder.cs index cd31d8d8c..017a665af 100644 --- a/src/DotNet.Testcontainers/Core/Builder/IImageFromDockerfileBuilder.cs +++ b/src/DotNet.Testcontainers/Core/Builder/IImageFromDockerfileBuilder.cs @@ -11,7 +11,7 @@ public interface IImageFromDockerfileBuilder IImageFromDockerfileBuilder WithDockerfileDirectory(string dockerfileDirectory); - IImageFromDockerfileBuilder WithDeleteIfExits(bool deleteIfExits); + IImageFromDockerfileBuilder WithDeleteIfExists(bool deleteIfExists); Task Build(); } diff --git a/src/DotNet.Testcontainers/Core/Builder/ImageFromDockerfileBuilder.cs b/src/DotNet.Testcontainers/Core/Builder/ImageFromDockerfileBuilder.cs index 55a8c94eb..dae6566cd 100644 --- a/src/DotNet.Testcontainers/Core/Builder/ImageFromDockerfileBuilder.cs +++ b/src/DotNet.Testcontainers/Core/Builder/ImageFromDockerfileBuilder.cs @@ -26,9 +26,9 @@ public IImageFromDockerfileBuilder WithDockerfileDirectory(string dockerfileDire return this; } - public IImageFromDockerfileBuilder WithDeleteIfExits(bool deleteIfExits) + public IImageFromDockerfileBuilder WithDeleteIfExists(bool deleteIfExists) { - this.configuration.DeleteIfExits = deleteIfExits; + this.configuration.DeleteIfExists = deleteIfExists; return this; } diff --git a/src/DotNet.Testcontainers/Core/Models/ImageFromDockerfileConfiguration.cs b/src/DotNet.Testcontainers/Core/Models/ImageFromDockerfileConfiguration.cs index d7c89cda1..cc2fa9a25 100644 --- a/src/DotNet.Testcontainers/Core/Models/ImageFromDockerfileConfiguration.cs +++ b/src/DotNet.Testcontainers/Core/Models/ImageFromDockerfileConfiguration.cs @@ -4,10 +4,10 @@ namespace DotNet.Testcontainers.Core.Models internal class ImageFromDockerfileConfiguration { - public string Image { get; set; } = Guid.NewGuid().ToString("n"); + public string Image { get; set; } = Guid.NewGuid().ToString("n").Substring(0, 12); public string DockerfileDirectory { get; set; } = "."; - public bool DeleteIfExits { get; set; } = true; + public bool DeleteIfExists { get; set; } = true; } } diff --git a/src/DotNet.Testcontainers/DotNet.Testcontainers.csproj b/src/DotNet.Testcontainers/DotNet.Testcontainers.csproj index cb4c8a172..17ab0e950 100644 --- a/src/DotNet.Testcontainers/DotNet.Testcontainers.csproj +++ b/src/DotNet.Testcontainers/DotNet.Testcontainers.csproj @@ -1,10 +1,12 @@ netstandard2.0 + latest +