diff --git a/src/Testcontainers/Builders/ContainerBuilder`3.cs b/src/Testcontainers/Builders/ContainerBuilder`3.cs index f8b3b0859..0ca8b2933 100644 --- a/src/Testcontainers/Builders/ContainerBuilder`3.cs +++ b/src/Testcontainers/Builders/ContainerBuilder`3.cs @@ -255,12 +255,6 @@ public TBuilderEntity WithTmpfsMount(string destination, AccessMode accessMode) return WithMount(new TmpfsMount(destination, accessMode)); } - /// - public TBuilderEntity WithNetwork(string id, string name) - { - return WithNetwork(name); - } - /// public TBuilderEntity WithNetwork(string name) { diff --git a/src/Testcontainers/Clients/DockerContainerOperations.cs b/src/Testcontainers/Clients/DockerContainerOperations.cs index a9e783844..72121bee0 100644 --- a/src/Testcontainers/Clients/DockerContainerOperations.cs +++ b/src/Testcontainers/Clients/DockerContainerOperations.cs @@ -102,13 +102,13 @@ public Task RemoveAsync(string id, CancellationToken ct = default) public Task ExtractArchiveToContainerAsync(string id, string path, Stream tarStream, CancellationToken ct = default) { - _logger.ExtractArchiveToDockerContainer(id, path); + _logger.CopyArchiveToDockerContainer(id, path); return Docker.Containers.ExtractArchiveToContainerAsync(id, new ContainerPathStatParameters { Path = path, AllowOverwriteDirWithFile = false }, tarStream, ct); } public async Task GetArchiveFromContainerAsync(string id, string path, CancellationToken ct = default) { - _logger.GetArchiveFromDockerContainer(id, path); + _logger.ReadArchiveFromDockerContainer(id, path); var tarResponse = await Docker.Containers.GetArchiveFromContainerAsync(id, new GetArchiveFromContainerParameters { Path = path }, false, ct) .ConfigureAwait(false); diff --git a/src/Testcontainers/Clients/TraceProgress.cs b/src/Testcontainers/Clients/TraceProgress.cs index 95264ae18..f03aa11c7 100644 --- a/src/Testcontainers/Clients/TraceProgress.cs +++ b/src/Testcontainers/Clients/TraceProgress.cs @@ -19,17 +19,17 @@ public void Report(JSONMessage value) if (!string.IsNullOrWhiteSpace(value.Status)) { - _logger.LogTrace(value.Status); + _logger.LogDebug(value.Status); } if (!string.IsNullOrWhiteSpace(value.Stream)) { - _logger.LogTrace(value.Stream); + _logger.LogDebug(value.Stream); } if (!string.IsNullOrWhiteSpace(value.ProgressMessage)) { - _logger.LogTrace(value.ProgressMessage); + _logger.LogDebug(value.ProgressMessage); } if (!string.IsNullOrWhiteSpace(value.ErrorMessage)) diff --git a/src/Testcontainers/Configurations/TestcontainersSettings.cs b/src/Testcontainers/Configurations/TestcontainersSettings.cs index 595807dfe..4cae25bc1 100644 --- a/src/Testcontainers/Configurations/TestcontainersSettings.cs +++ b/src/Testcontainers/Configurations/TestcontainersSettings.cs @@ -1,6 +1,7 @@ namespace DotNet.Testcontainers.Configurations { using System; + using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Runtime.InteropServices; @@ -167,7 +168,7 @@ static TestcontainersSettings() /// [NotNull] public static ILogger Logger { get; set; } - = new Logger(); + = ConsoleLogger.Instance; /// /// Gets or sets the host operating system. @@ -184,12 +185,16 @@ public static WaitHandle SettingsInitialized => ManualResetEvent.WaitHandle; /// - public static async Task ExposeHostPortsAsync(params ushort[] ports) + public static Task ExposeHostPortsAsync(ushort port, CancellationToken ct = default) + => ExposeHostPortsAsync(new[] { port }, ct); + + /// + public static async Task ExposeHostPortsAsync(IEnumerable ports, CancellationToken ct = default) { - await PortForwardingContainer.Instance.StartAsync() + await PortForwardingContainer.Instance.StartAsync(ct) .ConfigureAwait(false); - await PortForwardingContainer.Instance.ExposeHostPortsAsync(ports) + await PortForwardingContainer.Instance.ExposeHostPortsAsync(ports, ct) .ConfigureAwait(false); } } diff --git a/src/Testcontainers/Containers/DockerContainer.cs b/src/Testcontainers/Containers/DockerContainer.cs index 01e037529..69dbd2526 100644 --- a/src/Testcontainers/Containers/DockerContainer.cs +++ b/src/Testcontainers/Containers/DockerContainer.cs @@ -440,12 +440,16 @@ await WaitStrategy.WaitUntilAsync(CheckPortBindingsAsync, TimeSpan.FromSeconds(1 await _configuration.StartupCallback(this, ct) .ConfigureAwait(false); + Logger.StartReadinessCheck(_container.ID); + foreach (var waitStrategy in _configuration.WaitStrategies) { await WaitStrategy.WaitUntilAsync(() => CheckWaitStrategyAsync(waitStrategy), TimeSpan.FromSeconds(1), Timeout.InfiniteTimeSpan, ct) .ConfigureAwait(false); } + Logger.CompleteReadinessCheck(_container.ID); + Started?.Invoke(this, EventArgs.Empty); } diff --git a/src/Testcontainers/Containers/PortForwarding.cs b/src/Testcontainers/Containers/PortForwarding.cs index 07450e0b8..2ebec57f2 100644 --- a/src/Testcontainers/Containers/PortForwarding.cs +++ b/src/Testcontainers/Containers/PortForwarding.cs @@ -1,7 +1,9 @@ namespace DotNet.Testcontainers.Containers { + using System.Collections.Generic; using System.Linq; using System.Net; + using System.Threading; using System.Threading.Tasks; using Docker.DotNet.Models; using DotNet.Testcontainers.Builders; @@ -41,8 +43,9 @@ private PortForwardingContainer(PortForwardingConfiguration configuration, ILogg /// Exposes the host ports using SSH port forwarding. /// /// The host ports to forward. + /// Cancellation token. /// A task that completes when the host ports are forwarded. - public Task ExposeHostPortsAsync(params ushort[] ports) + public Task ExposeHostPortsAsync(IEnumerable ports, CancellationToken ct = default) { var sshClient = new SshClient(Hostname, GetMappedPublicPort(PortForwardingBuilder.SshdPort), _configuration.Username, _configuration.Password); sshClient.Connect(); diff --git a/src/Testcontainers/Logger.cs b/src/Testcontainers/Logger.cs index 0fea6e8b8..757a61e9a 100644 --- a/src/Testcontainers/Logger.cs +++ b/src/Testcontainers/Logger.cs @@ -2,9 +2,8 @@ namespace DotNet.Testcontainers { using System; using System.Diagnostics; - using System.Globalization; - using System.IO; using System.Runtime.InteropServices; + using JetBrains.Annotations; using Microsoft.Extensions.Logging; /// @@ -55,11 +54,14 @@ namespace DotNet.Testcontainers /// } /// /// - internal sealed class Logger : ILogger, IDisposable + [PublicAPI] + public sealed class ConsoleLogger : ILogger, IDisposable { private readonly Stopwatch _stopwatch = Stopwatch.StartNew(); - public Logger() + private LogLevel _minLogLevel = LogLevel.Information; + + private ConsoleLogger() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !Console.IsOutputRedirected && !Console.IsErrorRedirected) { @@ -67,42 +69,50 @@ public Logger() } } + /// + /// Gets the instance. + /// + public static ConsoleLogger Instance { get; } + = new ConsoleLogger(); + + /// + /// Gets a value indicating whether the debug log level is enabled or not. + /// + public bool DebugLogLevelEnabled + { + get + { + return LogLevel.Debug.Equals(_minLogLevel); + } + + set + { + _minLogLevel = value ? LogLevel.Debug : LogLevel.Information; + } + } + + /// public void Dispose() { - // The default logger does not support scopes. We return itself as IDisposable implementation. + // The default console logger does not support scopes. We return itself as IDisposable implementation. } + /// public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { - TextWriter console; - - switch (logLevel) + if (IsEnabled(logLevel)) { - case LogLevel.Information: - console = Console.Out; - break; - case LogLevel.Warning: - console = Console.Out; - break; - case LogLevel.Error: - console = Console.Error; - break; - case LogLevel.Critical: - console = Console.Error; - break; - default: - return; + Console.Out.WriteLine("[testcontainers.org {0:hh\\:mm\\:ss\\.ff}] {1}", _stopwatch.Elapsed, formatter.Invoke(state, exception)); } - - var message = string.Format(CultureInfo.CurrentCulture, "[testcontainers.org {0:hh\\:mm\\:ss\\.ff}] {1}", _stopwatch.Elapsed, formatter.Invoke(state, exception)); - console.WriteLine(message); } + /// public bool IsEnabled(LogLevel logLevel) { - return true; + return logLevel >= _minLogLevel; } + /// public IDisposable BeginScope(TState state) { return this; diff --git a/src/Testcontainers/Logging.cs b/src/Testcontainers/Logging.cs index 9e675d861..12318b354 100644 --- a/src/Testcontainers/Logging.cs +++ b/src/Testcontainers/Logging.cs @@ -25,10 +25,16 @@ private static readonly Action _StopDockerContainer private static readonly Action _DeleteDockerContainer = LoggerMessage.Define(LogLevel.Information, default, "Delete Docker container {Id}"); - private static readonly Action _ExtractArchiveToDockerContainer - = LoggerMessage.Define(LogLevel.Information, default, "Copy tar archive to \"{Path}\" at Docker container {Id}"); + private static readonly Action _StartReadinessCheck + = LoggerMessage.Define(LogLevel.Information, default, "Wait for Docker container {Id} to complete readiness checks"); - private static readonly Action _GetArchiveFromDockerContainer + private static readonly Action _CompleteReadinessCheck + = LoggerMessage.Define(LogLevel.Information, default, "Docker container {Id} ready"); + + private static readonly Action _CopyArchiveToDockerContainer + = LoggerMessage.Define(LogLevel.Information, default, "Copy tar archive to \"{Path}\" to Docker container {Id}"); + + private static readonly Action _ReadArchiveFromDockerContainer = LoggerMessage.Define(LogLevel.Information, default, "Read \"{Path}\" from Docker container {Id}"); private static readonly Action _AttachToDockerContainer @@ -91,47 +97,57 @@ public static void IgnorePatternAdded(this ILogger logger, Regex ignorePattern) public static void DockerContainerCreated(this ILogger logger, string id) { - _DockerContainerCreated(logger, id, null); + _DockerContainerCreated(logger, TruncId(id), null); } public static void StartDockerContainer(this ILogger logger, string id) { - _StartDockerContainer(logger, id, null); + _StartDockerContainer(logger, TruncId(id), null); } public static void StopDockerContainer(this ILogger logger, string id) { - _StopDockerContainer(logger, id, null); + _StopDockerContainer(logger, TruncId(id), null); } public static void DeleteDockerContainer(this ILogger logger, string id) { - _DeleteDockerContainer(logger, id, null); + _DeleteDockerContainer(logger, TruncId(id), null); + } + + public static void StartReadinessCheck(this ILogger logger, string id) + { + _StartReadinessCheck(logger, TruncId(id), null); } - public static void ExtractArchiveToDockerContainer(this ILogger logger, string id, string path) + public static void CompleteReadinessCheck(this ILogger logger, string id) { - _ExtractArchiveToDockerContainer(logger, path, id, null); + _CompleteReadinessCheck(logger, TruncId(id), null); } - public static void GetArchiveFromDockerContainer(this ILogger logger, string id, string path) + public static void CopyArchiveToDockerContainer(this ILogger logger, string id, string path) { - _GetArchiveFromDockerContainer(logger, path, id, null); + _CopyArchiveToDockerContainer(logger, path, TruncId(id), null); + } + + public static void ReadArchiveFromDockerContainer(this ILogger logger, string id, string path) + { + _ReadArchiveFromDockerContainer(logger, path, TruncId(id), null); } public static void AttachToDockerContainer(this ILogger logger, string id, Type type) { - _AttachToDockerContainer(logger, type, id, null); + _AttachToDockerContainer(logger, type, TruncId(id), null); } public static void ConnectToDockerNetwork(this ILogger logger, string networkId, string containerId) { - _ConnectToDockerNetwork(logger, containerId, networkId, null); + _ConnectToDockerNetwork(logger, TruncId(containerId), TruncId(networkId), null); } public static void ExecuteCommandInDockerContainer(this ILogger logger, string id, IEnumerable command) { - _ExecuteCommandInDockerContainer(logger, string.Join(" ", command), id, null); + _ExecuteCommandInDockerContainer(logger, string.Join(" ", command), TruncId(id), null); } public static void DockerImageCreated(this ILogger logger, IImage image) @@ -151,12 +167,12 @@ public static void DeleteDockerImage(this ILogger logger, IImage image) public static void DockerNetworkCreated(this ILogger logger, string id) { - _DockerNetworkCreated(logger, id, null); + _DockerNetworkCreated(logger, TruncId(id), null); } public static void DeleteDockerNetwork(this ILogger logger, string id) { - _DeleteDockerNetwork(logger, id, null); + _DeleteDockerNetwork(logger, TruncId(id), null); } public static void DockerVolumeCreated(this ILogger logger, string name) @@ -205,5 +221,10 @@ public static void DockerRegistryCredentialFound(this ILogger logger, string doc { _DockerRegistryCredentialFound(logger, dockerRegistry, null); } + + private static string TruncId(string id) + { + return id.Substring(0, Math.Min(12, id.Length)); + } } }