Skip to content

Commit

Permalink
[#NON] #CHANGE 'assemblyName: DotNet.Testcontainers; function: Docker…
Browse files Browse the repository at this point in the history
…ContainerOperations'

{Set AutoRemove; Do not remove necessary Testcontainers in unit tests.}
  • Loading branch information
HofmeisterAn committed Feb 15, 2021
1 parent 39a8e11 commit 464f147
Show file tree
Hide file tree
Showing 20 changed files with 79 additions and 53 deletions.
2 changes: 1 addition & 1 deletion .azuredevops/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
- powershell: Get-ChildItem -Path . -Include *.log -Recurse | % { Get-Content -Path $_.FullName }
displayName: Logs

- powershell: Get-ChildItem -Path 'test-coverage' -Filter *.xml | Rename-Item -NewName { $_.Name -Replace 'coverage.netcoreapp3.1', '${{ parameters.displayName }}'.ToLower() }
- powershell: Get-ChildItem -Path 'test-coverage' -Filter *.xml | Rename-Item -NewName { $_.Name -Replace 'coverage.net5.0', '${{ parameters.displayName }}'.ToLower() }
displayName: Rename Test And Coverage Results

- powershell: "@('test-results', 'test-coverage') | % { Copy-Item -Path $_ -Destination '$(Build.ArtifactStagingDirectory)' -Recurse }"
Expand Down
1 change: 0 additions & 1 deletion .azuredevops/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ jobs:
- powershell: |
echo "##vso[task.setvariable variable=JAVA_HOME]$(JAVA_HOME_11_X64)"
echo "##vso[task.setvariable variable=PATH]$(JAVA_HOME_11_X64)\bin;$(PATH)"
echo "##vso[task.setvariable variable=DOTNET_ROLL_FORWARD]Major"
./cake-tools/dotnet-cake --target=Restore-NuGet-Packages
displayName: Prepare
Expand Down
2 changes: 1 addition & 1 deletion .cake-tools/packages.config
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Cake" version="0.38.5" />
<package id="Cake" version="1.0.0" />
</packages>
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -347,9 +347,9 @@ healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/

# Cakebuild
tools/*
!tools/packages.config
# Cake Build
.cake-tools/*
!.cake-tools/packages.config

# Project
test-results/
Expand Down
4 changes: 2 additions & 2 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ variables:
MSBUILDDISABLENODEREUSE: true # https://github.com/dotnet/sdk/issues/9452
DECODE_PERCENTS: true # https://github.com/microsoft/azure-pipelines-agent/pull/3152
TZ: CET # https://stackoverflow.com/q/53510011
dotNetCoreVersion: 3.1.111
cakeVersion: 0.38.5
dotNetCoreVersion: 5.0.103
cakeVersion: 1.0.0

trigger:
- master
Expand Down
8 changes: 5 additions & 3 deletions build.cake
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#tool nuget:?package=MSBuild.SonarQube.Runner.Tool&version=4.8.0
#tool nuget:?package=dotnet-sonarscanner&version=5.0.4

#addin nuget:?package=Cake.Sonar&version=1.1.25

#addin nuget:?package=Cake.Git&version=0.22.0
#addin nuget:?package=Cake.Git&version=1.0.0

#load ".cake-scripts/parameters.cake"

Expand Down Expand Up @@ -106,6 +106,7 @@ Task("Sonar-Begin")
Login = param.SonarQubeCredentials.Token,
Organization = param.SonarQubeCredentials.Organization,
Branch = param.IsPullRequest ? null : param.Branch, // A pull request analysis can not have the branch analysis parameter 'sonar.branch.name'.
UseCoreClr = true,
Silent = true,
Version = param.Version.Substring(0, 5),
PullRequestProvider = "GitHub",
Expand All @@ -124,7 +125,8 @@ Task("Sonar-End")
{
SonarEnd(new SonarEndSettings
{
Login = param.SonarQubeCredentials.Token
Login = param.SonarQubeCredentials.Token,
UseCoreClr = true
});
});

Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "3.1.100",
"version": "5.0.100",
"rollForward": "latestPatch"
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.1</TargetFrameworks>
<TargetFrameworks>net5.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.msbuild" Version="3.0.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="Confluent.Kafka" Version="1.5.3" />
<PackageReference Include="CouchbaseNetClient" Version="3.0.7" />
<PackageReference Include="coverlet.msbuild" Version="2.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
<PackageReference Include="MyCouch" Version="7.4.0" />
<PackageReference Include="MySql.Data" Version="8.0.22" />
<PackageReference Include="Npgsql" Version="5.0.0" />
<PackageReference Include="Oracle.ManagedDataAccess.Core" Version="2.19.100" />
<PackageReference Include="RabbitMQ.Client" Version="6.2.1" />
<PackageReference Include="StackExchange.Redis" Version="2.2.4" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../DotNet.Testcontainers/DotNet.Testcontainers.csproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public class AlpineFixture : ModuleFixture<TestcontainersContainer>, IAsyncLifet
public AlpineFixture()
: base(new TestcontainersBuilder<TestcontainersContainer>()
.WithImage("alpine")
.WithCommand(KeepTestcontainersUpAndRunning.Command)
.Build())
{
}
Expand All @@ -18,6 +19,7 @@ public async Task InitializeAsync()
{
await this.Container.StartAsync()
.ConfigureAwait(false);

await this.Container.StopAsync()
.ConfigureAwait(false);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace DotNet.Testcontainers.Tests.Fixtures
{
public static class KeepTestcontainersUpAndRunning
{
public static string[] Command { get; } = { "/bin/sh", "-c", "tail -f /dev/null" };
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ public async Task Start()
{
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(0)))
{
var expectedExceptions = new[] { typeof(TaskCanceledException), typeof(OperationCanceledException), typeof(TimeoutException) };
// It depends on which part in the StartAsync gets canceled. Catch base exception.
// This test will not throw a TimeoutException. We do not cancel the wait strategy.
await Assert.ThrowsAnyAsync<OperationCanceledException>(() => this.alpineFixture.Container.StartAsync(cts.Token));
var exception = await Assert.ThrowsAnyAsync<SystemException>(() => this.alpineFixture.Container.StartAsync(cts.Token));
Assert.Contains(exception.GetType(), expectedExceptions);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ public async Task Hostname()
// When
var testcontainersBuilder = new TestcontainersBuilder<TestcontainersContainer>()
.WithImage("alpine")
.WithCleanUp(false)
.WithHostname(hostname)
.WithEntrypoint("/bin/sh", "-c", $"hostname | grep '{hostname}' &> /dev/null");

Expand All @@ -121,6 +122,7 @@ public async Task WorkingDirectory()
// Given
var testcontainersBuilder = new TestcontainersBuilder<TestcontainersContainer>()
.WithImage("alpine")
.WithCleanUp(false)
.WithCommand("/bin/sh", "-c", "test -d /tmp && exit $? || exit $?")
.WithWorkingDirectory("/tmp");

Expand All @@ -139,6 +141,7 @@ public async Task Entrypoint()
// Given
var testcontainersBuilder = new TestcontainersBuilder<TestcontainersContainer>()
.WithImage("alpine")
.WithCleanUp(false)
.WithEntrypoint("/bin/sh", "-c", "exit 255");

// When
Expand Down Expand Up @@ -318,7 +321,7 @@ public async Task OutputConsumer()
// When
var testcontainersBuilder = new TestcontainersBuilder<TestcontainersContainer>()
.WithImage("alpine")
.WithCommand("/bin/sh", "-c", $"sleep 1 && printf \"{unixTimeInMilliseconds}\" | tee /dev/stderr")
.WithCommand("/bin/sh", "-c", $"printf \"{unixTimeInMilliseconds}\" | tee /dev/stderr && tail -f /dev/null")
.WithOutputConsumer(consumer)
.WithWaitStrategy(Wait.ForUnixContainer()
.UntilMessageIsLogged(consumer.Stdout, unixTimeInMilliseconds)
Expand Down Expand Up @@ -368,7 +371,7 @@ public async Task ExecCommandInRunningContainer()
// Given
var testcontainersBuilder = new TestcontainersBuilder<TestcontainersContainer>()
.WithImage("alpine")
.WithCommand("/bin/sh", "-c", "tail -f /dev/null");
.WithCommand(KeepTestcontainersUpAndRunning.Command);

// When
// Then
Expand All @@ -389,7 +392,7 @@ public async Task CopyFileToRunningContainer()

var testcontainersBuilder = new TestcontainersBuilder<TestcontainersContainer>()
.WithImage("alpine")
.WithCommand("/bin/sh", "-c", "tail -f /dev/null");
.WithCommand(KeepTestcontainersUpAndRunning.Command);

// When
// Then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ public async Task<string> RunAsync(ITestcontainersConfiguration configuration, C

var hostConfig = new HostConfig
{
AutoRemove = configuration.CleanUp,
AutoRemove = configuration.CleanUp, // TODO: Should we keep this? If the Docker daemon remove containers we're no longer able to call e. g. `CleanUp(true)` + `GetExitCode()`.
PortBindings = converter.PortBindings,
Mounts = converter.Mounts,
};
Expand Down
11 changes: 9 additions & 2 deletions src/DotNet.Testcontainers/Clients/FilterByProperty.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
namespace DotNet.Testcontainers.Clients
{
using System.Collections.Concurrent;
using System.Collections.Generic;

internal sealed class FilterByProperty : Dictionary<string, IDictionary<string, bool>>
internal sealed class FilterByProperty : ConcurrentDictionary<string, IDictionary<string, bool>>
{
public FilterByProperty(string property, string value)
{
this.Add(property, new Dictionary<string, bool> { { value, true } });
this.Add(property, value);
}

public void Add(string property, string value)
{
var values = this.GetOrAdd(property, _ => new Dictionary<string, bool>());
values[value] = true;
}
}
}
2 changes: 1 addition & 1 deletion src/DotNet.Testcontainers/Clients/TestcontainersClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ public Task<string> BuildAsync(IImageFromDockerfileConfiguration configuration,
private void PurgeOrphanedContainers(object sender, EventArgs args)
{
var arguments = new PurgeOrphanedContainersArgs(this.endpoint, this.registryService.GetRegisteredContainers());
new Process { StartInfo = { FileName = "docker", Arguments = arguments.ToString() } }.Start();
_ = new Process { StartInfo = { FileName = "docker", Arguments = arguments.ToString() } }.Start();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ namespace DotNet.Testcontainers.Containers.Builders
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Threading;
Expand Down Expand Up @@ -42,10 +43,11 @@ public sealed class TestcontainersBuilder<TDockerContainer> : ITestcontainersBui
public TestcontainersBuilder() : this(
Apply(
authConfig: new AuthenticationConfiguration(),
labels: new DefaultLabels(),
outputConsumer: Consume.DoNotConsumeStdoutAndStderr(),
waitStrategies: Wait.ForUnixContainer().Build(),
startupCallback: (container, ct) => Task.CompletedTask),
testcontainer => { })
startupCallback: (_, _) => Task.CompletedTask),
_ => { })
{
}

Expand Down Expand Up @@ -355,5 +357,16 @@ private static IReadOnlyDictionary<T, T> Merge<T>(IReadOnlyDictionary<T, T> next
return next.Concat(previous.Where(item => !next.Keys.Contains(item.Key))).ToDictionary(item => item.Key, item => item.Value);
}
}

private sealed class DefaultLabels : ReadOnlyDictionary<string, string>
{
public DefaultLabels()
: base(new Dictionary<string, string>
{
{ "dotnet.testcontainers", "true"},
})
{
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace DotNet.Testcontainers.Containers.Configurations

/// <inheritdoc cref="IAuthenticationConfiguration" />
/// <remarks>In the future, we will replace this class. Instead, we will use the local Docker credentials.</remarks>
internal class AuthenticationConfiguration : IAuthenticationConfiguration
internal readonly struct AuthenticationConfiguration : IAuthenticationConfiguration
{
/// <summary>
/// Creates a <see cref="AuthenticationConfiguration" />.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ internal CouchbaseTestcontainer(ITestcontainersConfiguration configuration) : ba
{
}

// TODO: The hostname won't work on Azure, ip address won't work on macOS.
// TODO: The hostname won't work on Azure, ip address won't work on Windows or macOS?
public override string ConnectionString => $"couchbase://{this.IpAddress}";

/// <summary>
Expand All @@ -23,7 +23,7 @@ internal CouchbaseTestcontainer(ITestcontainersConfiguration configuration) : ba
/// <returns>A task that returns the couchbase-cli exit code when it is finished.</returns>
public Task<long> CreateBucket(string bucket, int memory = 128)
{
var createBucketCommand = $"{couchbaseCli} bucket-create -c localhost:8091 --username {this.Username} --password {this.Password} --bucket {bucket} --bucket-type couchbase --bucket-ramsize {memory} --enable-flush 1 --bucket-replica 0 --wait";
var createBucketCommand = $"{couchbaseCli} bucket-create -c 127.0.0.1:8091 --username {this.Username} --password {this.Password} --bucket {bucket} --bucket-type couchbase --bucket-ramsize {memory} --enable-flush 1 --bucket-replica 0 --wait";
return this.ExecAsync(new[] { "/bin/sh", "-c", createBucketCommand });
}

Expand All @@ -34,7 +34,7 @@ public Task<long> CreateBucket(string bucket, int memory = 128)
/// <returns>A task that returns the couchbase-cli exit code when it is finished.</returns>
public Task<long> FlushBucket(string bucket)
{
var flushBucketCommand = $"yes | {couchbaseCli} bucket-flush -c localhost:8091 --username {this.Username} --password {this.Password} --bucket {bucket}";
var flushBucketCommand = $"yes | {couchbaseCli} bucket-flush -c 127.0.0.1:8091 --username {this.Username} --password {this.Password} --bucket {bucket}";
return this.ExecAsync(new[] { "/bin/sh", "-c", flushBucketCommand });
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class TestcontainersContainer : IDockerContainer
{
private static readonly TestcontainersState[] ContainerHasBeenCreatedStates = { TestcontainersState.Created, TestcontainersState.Running, TestcontainersState.Exited };

private readonly SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);
private readonly SemaphoreSlim semaphoreSlim;

private readonly ITestcontainersClient client;

Expand All @@ -26,6 +26,7 @@ public class TestcontainersContainer : IDockerContainer

protected TestcontainersContainer(ITestcontainersConfiguration configuration)
{
this.semaphoreSlim = new SemaphoreSlim(1, 1);
this.client = new TestcontainersClient(configuration.Endpoint);
this.configuration = configuration;
}
Expand Down Expand Up @@ -219,11 +220,11 @@ private async Task<ContainerListResponse> Create(CancellationToken ct = default)

private async Task<ContainerListResponse> Start(string id, CancellationToken ct = default)
{
var startTask = this.client.StartAsync(id, ct);

var attachTask = this.client.AttachAsync(id, this.configuration.OutputConsumer, ct);

await Task.WhenAll(startTask, attachTask)
var startTask = this.client.StartAsync(id, ct);

await Task.WhenAll(attachTask, startTask)
.ConfigureAwait(false);

this.container = await this.client.GetContainer(id, ct)
Expand All @@ -232,9 +233,11 @@ await Task.WhenAll(startTask, attachTask)
await this.configuration.StartupCallback(this, ct)
.ConfigureAwait(false);

// Do not use a to small frequency. Especially with a lot of containers,
// we send many operations to the Docker endpoint. The endpoint may cancel operations.
foreach (var waitStrategy in this.configuration.WaitStrategies)
{
await WaitStrategy.WaitUntil(() => waitStrategy.Until(this.configuration.Endpoint, id), 100, ct: ct)
await WaitStrategy.WaitUntil(() => waitStrategy.Until(this.configuration.Endpoint, id), 500, ct: ct)
.ConfigureAwait(false);
}

Expand Down
Loading

0 comments on commit 464f147

Please sign in to comment.