diff --git a/src/Testcontainers.MariaDb/MariaDbBuilder.cs b/src/Testcontainers.MariaDb/MariaDbBuilder.cs index cbc7c6da7..98c067bc2 100644 --- a/src/Testcontainers.MariaDb/MariaDbBuilder.cs +++ b/src/Testcontainers.MariaDb/MariaDbBuilder.cs @@ -89,7 +89,8 @@ protected override MariaDbBuilder Init() .WithPortBinding(MariaDbPort, true) .WithDatabase(DefaultDatabase) .WithUsername(DefaultUsername) - .WithPassword(DefaultPassword); + .WithPassword(DefaultPassword) + .WithStartupCallback((container, ct) => container.WriteConfigurationFileAsync(ct)); } /// @@ -135,7 +136,7 @@ private sealed class WaitUntil : IWaitUntil /// The container configuration. public WaitUntil(MariaDbConfiguration configuration) { - _command = new List { "mariadb", "--protocol=TCP", $"--port={MariaDbPort}", $"--user={configuration.Username}", $"--password={configuration.Password}", configuration.Database, "--wait", "--silent", "--execute=SELECT 1;" }; + _command = new List { "mariadb", configuration.Database, "--wait", "--silent", "--execute=SELECT 1;" }; } /// diff --git a/src/Testcontainers.MariaDb/MariaDbContainer.cs b/src/Testcontainers.MariaDb/MariaDbContainer.cs index fd3845fa3..a1bb69eb4 100644 --- a/src/Testcontainers.MariaDb/MariaDbContainer.cs +++ b/src/Testcontainers.MariaDb/MariaDbContainer.cs @@ -44,7 +44,25 @@ public async Task ExecScriptAsync(string scriptContent, Cancellation await CopyAsync(Encoding.Default.GetBytes(scriptContent), scriptFilePath, Unix.FileMode644, ct) .ConfigureAwait(false); - return await ExecAsync(new[] { "mariadb", "--protocol=TCP", $"--port={MariaDbBuilder.MariaDbPort}", $"--user={_configuration.Username}", $"--password={_configuration.Password}", _configuration.Database, $"--execute=source {scriptFilePath};" }, ct) + return await ExecAsync(new[] { "mariadb", _configuration.Database, $"--execute=source {scriptFilePath};" }, ct) .ConfigureAwait(false); } + + /// + /// Write an unobfuscated MariaDb configuration file that configures the client + /// login path. This prevents warnings in the + /// result about using a password on the command line. + /// + /// Cancellation token. + /// Task that completes when the configuration file has been executed. + internal Task WriteConfigurationFileAsync(CancellationToken ct = default) + { + var config = new StringWriter(); + config.NewLine = "\n"; + config.WriteLine("[client]"); + config.WriteLine("protocol=TCP"); + config.WriteLine($"user={_configuration.Username}"); + config.WriteLine($"password={_configuration.Password}"); + return CopyAsync(Encoding.Default.GetBytes(config.ToString()), "/etc/mysql/my.cnf", UnixFileModes.UserRead | UnixFileModes.UserWrite, ct); + } } \ No newline at end of file diff --git a/src/Testcontainers.MongoDb/MongoDbContainer.cs b/src/Testcontainers.MongoDb/MongoDbContainer.cs index b5eb44752..f3c4f5e35 100644 --- a/src/Testcontainers.MongoDb/MongoDbContainer.cs +++ b/src/Testcontainers.MongoDb/MongoDbContainer.cs @@ -42,7 +42,20 @@ public async Task ExecScriptAsync(string scriptContent, Cancellation await CopyAsync(Encoding.Default.GetBytes(scriptContent), scriptFilePath, Unix.FileMode644, ct) .ConfigureAwait(false); - return await ExecAsync(new MongoDbShellCommand($"load('{scriptFilePath}')", _configuration.Username, _configuration.Password), ct) + var whichMongoDbShell = await ExecAsync(new[] { "which", "mongosh" }, ct) + .ConfigureAwait(false); + + var command = new[] + { + whichMongoDbShell.ExitCode == 0 ? "mongosh" : "mongo", + "--username", _configuration.Username, + "--password", _configuration.Password, + "--quiet", + "--eval", + $"load('{scriptFilePath}')", + }; + + return await ExecAsync(command, ct) .ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/Testcontainers.MongoDb/MongoDbShellCommand.cs b/src/Testcontainers.MongoDb/MongoDbShellCommand.cs deleted file mode 100644 index 7c288c983..000000000 --- a/src/Testcontainers.MongoDb/MongoDbShellCommand.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace Testcontainers.MongoDb; - -/// -/// A MongoDb shell command that evaluates a JavaScript expression. -/// -internal sealed class MongoDbShellCommand : List -{ - private const string Format = "{0} --username '{1}' --password '{2}' --quiet --eval '{3}'"; - - private const string Sanitize = "'\"'\"'"; - - private readonly StringBuilder _mongoDbShellCommand = new StringBuilder(); - - /// - /// Initializes a new instance of the class. - /// - /// - /// The legacy mongo shell was deprecated in MongoDb 5.0 and removed in MongoDb 6.0: - /// https://www.mongodb.com/docs/mongodb-shell/#the-mdb-shell-versus-the-legacy-mongo-shell. - /// - /// The JavaScript expression. - /// The MongoDb username. - /// The MongoDb password. - public MongoDbShellCommand(string js, string username, string password) - { - var sanitizedJs = js.Replace("'", Sanitize); - var sanitizedUsername = username.Replace("'", Sanitize); - var sanitizedPassword = password.Replace("'", Sanitize); - _mongoDbShellCommand.AppendFormat(Format, "mongosh", sanitizedUsername, sanitizedPassword, sanitizedJs); - _mongoDbShellCommand.Append(" || "); - _mongoDbShellCommand.AppendFormat(Format, "mongo", sanitizedUsername, sanitizedPassword, sanitizedJs); - Add("/bin/sh"); - Add("-c"); - Add(_mongoDbShellCommand.ToString()); - } -} \ No newline at end of file diff --git a/src/Testcontainers.MongoDb/Usings.cs b/src/Testcontainers.MongoDb/Usings.cs index e2b02a51e..16243387a 100644 --- a/src/Testcontainers.MongoDb/Usings.cs +++ b/src/Testcontainers.MongoDb/Usings.cs @@ -1,5 +1,4 @@ global using System; -global using System.Collections.Generic; global using System.IO; global using System.Linq; global using System.Text; diff --git a/src/Testcontainers.MySql/MySqlBuilder.cs b/src/Testcontainers.MySql/MySqlBuilder.cs index ac756ec73..4ed177c30 100644 --- a/src/Testcontainers.MySql/MySqlBuilder.cs +++ b/src/Testcontainers.MySql/MySqlBuilder.cs @@ -89,7 +89,8 @@ protected override MySqlBuilder Init() .WithPortBinding(MySqlPort, true) .WithDatabase(DefaultDatabase) .WithUsername(DefaultUsername) - .WithPassword(DefaultPassword); + .WithPassword(DefaultPassword) + .WithStartupCallback((container, ct) => container.WriteConfigurationFileAsync(ct)); } /// @@ -135,7 +136,7 @@ private sealed class WaitUntil : IWaitUntil /// The container configuration. public WaitUntil(MySqlConfiguration configuration) { - _command = new List { "mysql", "--protocol=TCP", $"--port={MySqlPort}", $"--user={configuration.Username}", $"--password={configuration.Password}", configuration.Database, "--wait", "--silent", "--execute=SELECT 1;" }; + _command = new List { "mysql", configuration.Database, "--wait", "--silent", "--execute=SELECT 1;" }; } /// diff --git a/src/Testcontainers.MySql/MySqlContainer.cs b/src/Testcontainers.MySql/MySqlContainer.cs index 001b1fef6..d18ffa3db 100644 --- a/src/Testcontainers.MySql/MySqlContainer.cs +++ b/src/Testcontainers.MySql/MySqlContainer.cs @@ -44,7 +44,25 @@ public async Task ExecScriptAsync(string scriptContent, Cancellation await CopyAsync(Encoding.Default.GetBytes(scriptContent), scriptFilePath, Unix.FileMode644, ct) .ConfigureAwait(false); - return await ExecAsync(new[] { "mysql", "--protocol=TCP", $"--port={MySqlBuilder.MySqlPort}", $"--user={_configuration.Username}", $"--password={_configuration.Password}", _configuration.Database, $"--execute=source {scriptFilePath};" }, ct) + return await ExecAsync(new[] { "mysql", _configuration.Database, $"--execute=source {scriptFilePath};" }, ct) .ConfigureAwait(false); } + + /// + /// Write an unobfuscated MySql configuration file that configures the client + /// login path. This prevents warnings in the + /// result about using a password on the command line. + /// + /// Cancellation token. + /// Task that completes when the configuration file has been executed. + internal Task WriteConfigurationFileAsync(CancellationToken ct = default) + { + var config = new StringWriter(); + config.NewLine = "\n"; + config.WriteLine("[client]"); + config.WriteLine("protocol=TCP"); + config.WriteLine($"user={_configuration.Username}"); + config.WriteLine($"password={_configuration.Password}"); + return CopyAsync(Encoding.Default.GetBytes(config.ToString()), "/etc/mysql/my.cnf", UnixFileModes.UserRead | UnixFileModes.UserWrite, ct); + } } \ No newline at end of file diff --git a/src/Testcontainers/Builders/ContainerBuilder`3.cs b/src/Testcontainers/Builders/ContainerBuilder`3.cs index 9fed8de2d..a41a049b2 100644 --- a/src/Testcontainers/Builders/ContainerBuilder`3.cs +++ b/src/Testcontainers/Builders/ContainerBuilder`3.cs @@ -366,9 +366,9 @@ public TBuilderEntity WithWaitStrategy(IWaitForContainerOS waitStrategy) } /// - public TBuilderEntity WithStartupCallback(Func startupCallback) + public TBuilderEntity WithStartupCallback(Func startupCallback) { - return Clone(new ContainerConfiguration(startupCallback: startupCallback)); + return Clone(new ContainerConfiguration(startupCallback: (container, ct) => startupCallback((TContainerEntity)container, ct))); } /// diff --git a/src/Testcontainers/Builders/IContainerBuilder`2.cs b/src/Testcontainers/Builders/IContainerBuilder`2.cs index 6fade9c82..1475881f8 100644 --- a/src/Testcontainers/Builders/IContainerBuilder`2.cs +++ b/src/Testcontainers/Builders/IContainerBuilder`2.cs @@ -452,6 +452,6 @@ public interface IContainerBuilder : I /// The callback method to invoke. /// A configured instance of . [PublicAPI] - TBuilderEntity WithStartupCallback(Func startupCallback); + TBuilderEntity WithStartupCallback(Func startupCallback); } } diff --git a/src/Testcontainers/Clients/DockerApiClient.cs b/src/Testcontainers/Clients/DockerApiClient.cs index cf8f1d91a..826b18b58 100644 --- a/src/Testcontainers/Clients/DockerApiClient.cs +++ b/src/Testcontainers/Clients/DockerApiClient.cs @@ -113,7 +113,7 @@ await RuntimeInitialized.WaitAsync(ct) } catch(Exception e) { - Logger.LogError(e, "Failed to retrieve Docker container runtime information."); + Logger.LogError(e, "Failed to retrieve Docker container runtime information"); } finally { diff --git a/src/Testcontainers/Containers/ResourceReaperState.cs b/src/Testcontainers/Containers/ResourceReaperState.cs index f3ee7398a..c9503e850 100644 --- a/src/Testcontainers/Containers/ResourceReaperState.cs +++ b/src/Testcontainers/Containers/ResourceReaperState.cs @@ -4,6 +4,7 @@ namespace DotNet.Testcontainers.Containers using System.Threading; using DotNet.Testcontainers.Configurations; using DotNet.Testcontainers.Images; + using Microsoft.Extensions.Logging; /// /// Resource Reaper states. @@ -24,7 +25,7 @@ public enum ResourceReaperState /// maintains the TCP connection to Ryuk. /// /// - /// will complete now. + /// will complete now. /// MaintainingConnection, diff --git a/tests/Testcontainers.ClickHouse.Tests/ClickHouseContainerTest.cs b/tests/Testcontainers.ClickHouse.Tests/ClickHouseContainerTest.cs index fde12cad5..7d423065b 100644 --- a/tests/Testcontainers.ClickHouse.Tests/ClickHouseContainerTest.cs +++ b/tests/Testcontainers.ClickHouse.Tests/ClickHouseContainerTest.cs @@ -39,7 +39,8 @@ public async Task ExecScriptReturnsSuccessful() var execResult = await _clickHouseContainer.ExecScriptAsync(scriptContent) .ConfigureAwait(true); - // When + // Then Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr); + Assert.Empty(execResult.Stderr); } } \ No newline at end of file diff --git a/tests/Testcontainers.CockroachDb.Tests/CockroachDbContainerTest.cs b/tests/Testcontainers.CockroachDb.Tests/CockroachDbContainerTest.cs index 14ce462a4..9c3f37566 100644 --- a/tests/Testcontainers.CockroachDb.Tests/CockroachDbContainerTest.cs +++ b/tests/Testcontainers.CockroachDb.Tests/CockroachDbContainerTest.cs @@ -39,7 +39,8 @@ public async Task ExecScriptReturnsSuccessful() var execResult = await _cockroachDbContainer.ExecScriptAsync(scriptContent) .ConfigureAwait(true); - // When + // Then Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr); + Assert.Empty(execResult.Stderr); } } \ No newline at end of file diff --git a/tests/Testcontainers.FirebirdSql.Tests/FirebirdSqlContainerTest.cs b/tests/Testcontainers.FirebirdSql.Tests/FirebirdSqlContainerTest.cs index e6cf980f9..220b90619 100644 --- a/tests/Testcontainers.FirebirdSql.Tests/FirebirdSqlContainerTest.cs +++ b/tests/Testcontainers.FirebirdSql.Tests/FirebirdSqlContainerTest.cs @@ -44,7 +44,7 @@ public async Task ExecScriptReturnsSuccessful() var execResult = await _firebirdSqlContainer.ExecScriptAsync(scriptContent) .ConfigureAwait(true); - // When + // Then Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr); Assert.Empty(execResult.Stderr); } diff --git a/tests/Testcontainers.MariaDb.Tests/MariaDbContainerTest.cs b/tests/Testcontainers.MariaDb.Tests/MariaDbContainerTest.cs index 3dd575f11..acd8480c5 100644 --- a/tests/Testcontainers.MariaDb.Tests/MariaDbContainerTest.cs +++ b/tests/Testcontainers.MariaDb.Tests/MariaDbContainerTest.cs @@ -44,8 +44,9 @@ public async Task ExecScriptReturnsSuccessful() var execResult = await _mariaDbContainer.ExecScriptAsync(scriptContent) .ConfigureAwait(true); - // When + // Then Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr); + Assert.Empty(execResult.Stderr); } [UsedImplicitly] diff --git a/tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs b/tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs index 9c96aae1f..732ab150f 100644 --- a/tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs +++ b/tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs @@ -44,8 +44,9 @@ public async Task ExecScriptReturnsSuccessful() var execResult = await _mongoDbContainer.ExecScriptAsync(scriptContent) .ConfigureAwait(true); - // When + // Then Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr); + Assert.Empty(execResult.Stderr); } [UsedImplicitly] diff --git a/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs b/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs index 19c3c81e4..f7d18102f 100644 --- a/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs +++ b/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs @@ -39,7 +39,8 @@ public async Task ExecScriptReturnsSuccessful() var execResult = await _msSqlContainer.ExecScriptAsync(scriptContent) .ConfigureAwait(true); - // When + // Then Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr); + Assert.Empty(execResult.Stderr); } } \ No newline at end of file diff --git a/tests/Testcontainers.MySql.Tests/MySqlContainerTest.cs b/tests/Testcontainers.MySql.Tests/MySqlContainerTest.cs index ea8b958c6..11a8e71a9 100644 --- a/tests/Testcontainers.MySql.Tests/MySqlContainerTest.cs +++ b/tests/Testcontainers.MySql.Tests/MySqlContainerTest.cs @@ -44,8 +44,9 @@ public async Task ExecScriptReturnsSuccessful() var execResult = await _mySqlContainer.ExecScriptAsync(scriptContent) .ConfigureAwait(true); - // When + // Then Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr); + Assert.Empty(execResult.Stderr); } [UsedImplicitly] diff --git a/tests/Testcontainers.Oracle.Tests/OracleContainerTest.cs b/tests/Testcontainers.Oracle.Tests/OracleContainerTest.cs index 08e7755f9..5b6984dd0 100644 --- a/tests/Testcontainers.Oracle.Tests/OracleContainerTest.cs +++ b/tests/Testcontainers.Oracle.Tests/OracleContainerTest.cs @@ -39,7 +39,8 @@ public async Task ExecScriptReturnsSuccessful() var execResult = await _oracleContainer.ExecScriptAsync(scriptContent) .ConfigureAwait(true); - // When + // Then Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr); + Assert.Empty(execResult.Stderr); } } \ No newline at end of file diff --git a/tests/Testcontainers.PostgreSql.Tests/PostgreSqlContainerTest.cs b/tests/Testcontainers.PostgreSql.Tests/PostgreSqlContainerTest.cs index 6c0259839..a92461dbd 100644 --- a/tests/Testcontainers.PostgreSql.Tests/PostgreSqlContainerTest.cs +++ b/tests/Testcontainers.PostgreSql.Tests/PostgreSqlContainerTest.cs @@ -39,8 +39,9 @@ public async Task ExecScriptReturnsSuccessful() var execResult = await _postgreSqlContainer.ExecScriptAsync(scriptContent) .ConfigureAwait(true); - // When + // Then Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr); + Assert.Empty(execResult.Stderr); } public sealed class ReuseContainerTest : IClassFixture, IDisposable diff --git a/tests/Testcontainers.Redis.Tests/RedisContainerTest.cs b/tests/Testcontainers.Redis.Tests/RedisContainerTest.cs index 937dd769d..15d311e44 100644 --- a/tests/Testcontainers.Redis.Tests/RedisContainerTest.cs +++ b/tests/Testcontainers.Redis.Tests/RedisContainerTest.cs @@ -33,7 +33,9 @@ public async Task ExecScriptReturnsSuccessful() var execResult = await _redisContainer.ExecScriptAsync(scriptContent) .ConfigureAwait(true); - // When + // Then + Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr); Assert.True("Hello, scripting!\n".Equals(execResult.Stdout), execResult.Stdout); + Assert.Empty(execResult.Stderr); } } \ No newline at end of file