Skip to content

Commit

Permalink
Added further container information, closes #21.
Browse files Browse the repository at this point in the history
  • Loading branch information
HofmeisterAn committed Feb 27, 2019
1 parent 44c8e71 commit 875573a
Show file tree
Hide file tree
Showing 11 changed files with 148 additions and 108 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=dotnet-testcontainers&metric=coverage)](https://sonarcloud.io/dashboard?id=dotnet-testcontainers)

# .NET Testcontainers
.NET Testcontainers is a library to support tests with throwaway instances of Docker containers. 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.
.NET Testcontainers is a library to support tests with throwaway instances of Docker containers for `netstandard2.0`, `net452` and `net462`. 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 [^1] and start containers within a second, to support and run your tests.

## Supported commands
- `WithImage` specifies an `IMAGE[:TAG]` to derive the container from.
- `WithCommand` specifies and overrides the `[COMMAND]` instruction provided from the Dockerfile.
- `WithEnvironment` set an environment variable in the container e. g.`-e "test=containers"`.
- `WithLabel` applies metadata to a container e. g.`-l, --label dotnet.testcontainers=awesome`.
- `WithEnvironment` set an environment variable in the container e. g. `-e "test=containers"`.
- `WithLabel` applies metadata to a container e. g. `-l, --label dotnet.testcontainers=awesome`.
- `WithExposedPort` exposes a port inside the container e. g. `--expose=80`.
- `WithPortBinding` publishes a container port to the host e. g. `-p 80:80`.
- `WithMount` mounts a volume into the container e. g. `-v, --volume .:/tmp`.
Expand Down
20 changes: 15 additions & 5 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,40 @@ jobs:
version: $(dotNetCoreVersion)

- powershell: |
# Disable dotnet cli telemetry
$env:DOTNET_CLI_TELEMETRY_OPTOUT=1
# Set environment variables
echo "##vso[task.setvariable variable=sonarcloudUrl;]$(sonarcloud.url)"
echo "##vso[task.setvariable variable=sonarcloudKey;]$(sonarcloud.key)"
echo "##vso[task.setvariable variable=sonarcloudToken;]$(sonarcloud.token)"
echo "##vso[task.setvariable variable=sonarcloudOrganization;]$(sonarcloud.organization)"
echo "##vso[task.setvariable variable=nuGetSource;]$(feed.source)"
echo "##vso[task.setvariable variable=nuGetApiKey;]$(feed.apikey)"
./build.sh --target=Restore-NuGet-Packages
# Set Docker configuration, until Unix containers are supported native
@{experimental=$true} | ConvertTo-Json -Compress | Out-File -FilePath C:\ProgramData\Docker\config\daemon.json
Get-Content C:\ProgramData\Docker\config\daemon.json
Restart-Service docker
docker pull --platform linux alpine:latest
docker pull --platform linux nginx:latest
# Prepare build
./build.ps1 --target=Restore-NuGet-Packages
displayName: 'Prepare'
- powershell: ./build.sh --target=Build
- powershell: ./build.ps1 --target=Build
displayName: 'Build'

- powershell: ./build.sh --target=Test
- powershell: ./build.ps1 --target=Test
displayName: 'Test'

- powershell: ./build.sh --target=Sonar
- powershell: ./build.ps1 --target=Sonar
displayName: 'Sonar'
env:
SONARQUBE_URL: $(sonarcloudUrl)
SONARQUBE_KEY: $(sonarcloudKey)
SONARQUBE_TOKEN: $(sonarcloudToken)
SONARQUBE_ORGANIZATION: $(sonarcloudOrganization)

- powershell: ./build.sh --target=Publish
- powershell: ./build.ps1 --target=Publish
displayName: 'Publish'
env:
NUGET_SOURCE: $(nuGetSource)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@
<PackageReference Include="coverlet.msbuild" Version="2.5.1" />
<PackageReference Include="LanguageExt.Core" Version="3.1.15" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="xunit" Version="2.4.1" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\xunit.runner.json" Link="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</AdditionalFiles>
<ProjectReference Include="..\DotNet.Testcontainers\DotNet.Testcontainers.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DotNet.Testcontainers\DotNet.Testcontainers.csproj" />
<None Include="..\xunit.runner.json">
<Link>xunit.runner.json</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<Import Project="$(MSBuildThisFileDirectory)../Shared.msbuild" />
</Project>
42 changes: 38 additions & 4 deletions src/DotNet.Testcontainers.Tests/TestcontainersTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,25 +37,59 @@ public class AccessDockerInformation
[Fact]
public void QueryNotExistingDockerImageById()
{
Assert.False(TestcontainersClient.Instance.ExistImageById(string.Empty));
Assert.False(MetaDataClientImages.Instance.ExistsWithId(string.Empty));
}

[Fact]
public void QueryNotExistingDockerContainerById()
{
Assert.False(TestcontainersClient.Instance.ExistContainerById(string.Empty));
Assert.False(MetaDataClientContainers.Instance.ExistsWithId(string.Empty));
}

[Fact]
public void QueryNotExistingDockerImageByName()
{
Assert.False(TestcontainersClient.Instance.ExistImageByName(string.Empty));
Assert.False(MetaDataClientImages.Instance.ExistsWithName(string.Empty));
}

[Fact]
public void QueryNotExistingDockerContainerByName()
{
Assert.False(TestcontainersClient.Instance.ExistContainerByName(string.Empty));
Assert.False(MetaDataClientContainers.Instance.ExistsWithName(string.Empty));
}

[Fact]
public void QueryContainerInformationOfRunningContainer()
{
// Given
// When
var testcontainersBuilder = new TestcontainersBuilder()
.WithImage("alpine");

// Then
using (var testcontainer = testcontainersBuilder.Build())
{
testcontainer.Start();

Assert.NotEmpty(testcontainer.Name);
Assert.NotEmpty(testcontainer.IPAddress);
Assert.NotEmpty(testcontainer.MacAddress);
}
}

[Fact]
public void QueryContainerInformationOfStoppedContainer()
{
// Given
// When
var testcontainersBuilder = new TestcontainersBuilder()
.WithImage("alpine");

// Then
using (var testcontainer = testcontainersBuilder.Build())
{
Assert.Throws<InvalidOperationException>(() => testcontainer.Name);
}
}
}

Expand Down
11 changes: 11 additions & 0 deletions src/DotNet.Testcontainers/Clients/DockerMetaDataClient.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace DotNet.Testcontainers.Clients
{
using System.Collections.Generic;
using static LanguageExt.Prelude;

internal abstract class DockerMetaDataClient<T> : DockerApiClient
{
Expand All @@ -11,5 +12,15 @@ internal abstract class DockerMetaDataClient<T> : DockerApiClient
internal abstract T ByName(string name);

internal abstract T ByProperty(string property, string value);

internal bool ExistsWithId(string id)
{
return notnull(this.ById(id));
}

internal bool ExistsWithName(string name)
{
return notnull(this.ByName(name));
}
}
}
17 changes: 0 additions & 17 deletions src/DotNet.Testcontainers/Clients/ITestcontainersClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,6 @@ namespace DotNet.Testcontainers.Clients

internal interface ITestcontainersClient
{
// Wrap image and container queries into proper response classes.
bool ExistImageById(string id);

bool ExistImageByName(string name);

bool ExistContainerById(string id);

bool ExistContainerByName(string name);

string FindImageNameById(string id);

string FindImageNameByName(string name);

string FindContainerNameById(string id);

string FindContainerNameByName(string name);

void Start(string id);

void Stop(string id);
Expand Down
76 changes: 13 additions & 63 deletions src/DotNet.Testcontainers/Clients/TestcontainersClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ namespace DotNet.Testcontainers.Clients
{
using System;
using System.Collections.Generic;
using System.Linq;
using Docker.DotNet.Models;
using DotNet.Testcontainers.Core.Mapper;
using DotNet.Testcontainers.Core.Mapper.Converters;
using DotNet.Testcontainers.Core.Models;
using DotNet.Testcontainers.Diagnostics;
using static LanguageExt.Prelude;

internal class TestcontainersClient : DockerApiClient, ITestcontainersClient
{
Expand All @@ -18,6 +16,8 @@ internal class TestcontainersClient : DockerApiClient, ITestcontainersClient

private static readonly GenericConverter GenericConverter = new GenericConverter(ConverterFactory);

private static readonly object IsImageAvailableLock = new object();

static TestcontainersClient()
{
ConverterFactory.Register<IReadOnlyCollection<string>, IList<string>>(
Expand Down Expand Up @@ -47,81 +47,25 @@ internal static ITestcontainersClient Instance
}
}

public bool ExistImageById(string id)
{
return Optional(id).Match(
Some: value => notnull(MetaDataClientImages.Instance.ById(value)),
None: () => false);
}

public bool ExistImageByName(string name)
{
return Optional(name).Match(
Some: value => notnull(MetaDataClientImages.Instance.ByName(value)),
None: () => false);
}

public bool ExistContainerById(string id)
{
return Optional(id).Match(
Some: value => notnull(MetaDataClientContainers.Instance.ById(value)),
None: () => false);
}

public bool ExistContainerByName(string name)
{
return Optional(name).Match(
Some: value => notnull(MetaDataClientContainers.Instance.ByName(value)),
None: () => false);
}

public string FindImageNameById(string id)
{
return Optional(id).Match(
Some: value => MetaDataClientImages.Instance.ById(value).RepoTags.FirstOrDefault(),
None: () => string.Empty);
}

public string FindImageNameByName(string name)
{
return Optional(name).Match(
Some: value => MetaDataClientImages.Instance.ByName(value).RepoTags.FirstOrDefault(),
None: () => string.Empty);
}

public string FindContainerNameById(string id)
{
return Optional(id).Match(
Some: value => MetaDataClientContainers.Instance.ById(value).Names.FirstOrDefault(),
None: () => string.Empty);
}

public string FindContainerNameByName(string name)
{
return Optional(name).Match(
Some: value => MetaDataClientContainers.Instance.ByName(value).Names.FirstOrDefault(),
None: () => string.Empty);
}

public void Start(string id)
{
if (this.ExistContainerById(id))
if (MetaDataClientContainers.Instance.ExistsWithId(id))
{
Docker.Containers.StartContainerAsync(id, new ContainerStartParameters { }).Wait();
}
}

public void Stop(string id)
{
if (this.ExistContainerById(id))
if (MetaDataClientContainers.Instance.ExistsWithId(id))
{
Docker.Containers.StopContainerAsync(id, new ContainerStopParameters { WaitBeforeKillSeconds = 15 }).Wait();
}
}

public void Remove(string id)
{
if (this.ExistContainerById(id))
if (MetaDataClientContainers.Instance.ExistsWithId(id))
{
Docker.Containers.RemoveContainerAsync(id, new ContainerRemoveParameters { Force = true }).Wait();
}
Expand All @@ -133,9 +77,15 @@ public string Run(TestcontainersConfiguration configuration)

var name = configuration.Container.Name;

if (!this.ExistImageByName(image))
Console.WriteLine(image);
Console.WriteLine(image);

lock (IsImageAvailableLock)
{
Docker.Images.CreateImageAsync(new ImagesCreateParameters { FromImage = image }, null, DebugProgress.Instance).Wait();
if (!MetaDataClientImages.Instance.ExistsWithName(image))
{
Docker.Images.CreateImageAsync(new ImagesCreateParameters { FromImage = image }, null, DebugProgress.Instance).Wait();
}
}

var cmd = GenericConverter.Convert<IReadOnlyCollection<string>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ public IDockerContainer Build()
configuration.Container.Command = this.command;
configuration.Container.Environments = this.environments;
configuration.Container.ExposedPorts = this.exposedPorts;
configuration.Container.Labels = this.labels;
configuration.Host.PortBindings = this.portBindings;
configuration.Host.Mounts = this.mounts;

Expand Down
8 changes: 8 additions & 0 deletions src/DotNet.Testcontainers/Core/Containers/IDockerContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ public interface IDockerContainer : IDisposable
/// <value>Returns the Docker container name if present or an empty string instead.</value>
string Name { get; }

/// <summary>Gets the Testcontainer ip address.</summary>
/// <value>Returns the Docker container ip address if present or an empty string instead.</value>
string IPAddress { get; }

/// <summary>Gets the Testcontainer mac address.</summary>
/// <value>Returns the Docker container mac address if present or an empty string instead.</value>
string MacAddress { get; }

/// <summary>
/// Starts the Testcontainer. If the image does not exist, it will be downloaded automatically. Non-existing containers are created at first start.
/// </summary>
Expand Down
Loading

0 comments on commit 875573a

Please sign in to comment.