diff --git a/src/Testcontainers.MongoDb/MongoDbBuilder.cs b/src/Testcontainers.MongoDb/MongoDbBuilder.cs index 4c8ba30ed..b3000d5a6 100644 --- a/src/Testcontainers.MongoDb/MongoDbBuilder.cs +++ b/src/Testcontainers.MongoDb/MongoDbBuilder.cs @@ -130,7 +130,7 @@ public override MongoDbContainer Build() } else { - waitUntil = new WaitInitiateReplicaSet(); + waitUntil = new WaitReplicaSetPrimary(); } // If the user does not provide a custom waiting strategy, append the default MongoDb waiting strategy. @@ -198,7 +198,7 @@ private static async Task InitiateReplicaSetAsync(MongoDbContainer container, Mo return; } - var readiness = new WaitIndicateReadiness(configuration); + var readiness = new WaitReplicaSetEnabled(); // This is a simple workaround to use the default options, which can be configured // with custom configurations as needed. @@ -260,9 +260,9 @@ public async Task UntilAsync(IContainer container) } /// - private sealed class WaitInitiateReplicaSet : IWaitUntil + private abstract class WaitUntilScriptBase : IWaitUntil { - private const string ScriptContent = "var r=db.runCommand({hello:1}).isWritablePrimary;quit(r===true?0:1);"; + protected abstract string ScriptContent { get; } /// public Task UntilAsync(IContainer container) @@ -271,7 +271,7 @@ public Task UntilAsync(IContainer container) } /// - private static async Task UntilAsync(MongoDbContainer container) + private async Task UntilAsync(MongoDbContainer container) { var execResult = await container.ExecScriptAsync(ScriptContent) .ConfigureAwait(false); @@ -279,4 +279,20 @@ private static async Task UntilAsync(MongoDbContainer container) return 0L.Equals(execResult.ExitCode); } } + + /// + private sealed class WaitReplicaSetEnabled : WaitUntilScriptBase + { + // rs.status() only answers once the final mongod is serving. The official image + // forks a temporary mongod during first-time initialization to create the root + // user. That process is not started with --replSet, so it never reports + // NotYetInitialized. + protected override string ScriptContent => "try{rs.status();quit(0);}catch(e){quit(e.codeName===\"NotYetInitialized\"?0:1);}"; + } + + /// + private sealed class WaitReplicaSetPrimary : WaitUntilScriptBase + { + protected override string ScriptContent => "var r=db.runCommand({hello:1}).isWritablePrimary;quit(r===true?0:1);"; + } } \ No newline at end of file diff --git a/tests/Testcontainers.MongoDb.Tests/MongoDbReplicaSetReadinessTest.cs b/tests/Testcontainers.MongoDb.Tests/MongoDbReplicaSetReadinessTest.cs new file mode 100644 index 000000000..6f5db835f --- /dev/null +++ b/tests/Testcontainers.MongoDb.Tests/MongoDbReplicaSetReadinessTest.cs @@ -0,0 +1,40 @@ +namespace Testcontainers.MongoDb; + +public sealed class MongoDbReplicaSetReadinessTest : IAsyncLifetime +{ + private const string ReadinessMessagesScriptContent = "#!/bin/sh\necho 'Waiting for connections'\necho 'Waiting for connections'\n"; + + private const string ReadinessMessagesScriptFilePath = "/docker-entrypoint-initdb.d/00-readiness-messages.sh"; + + private readonly MongoDbContainer _mongoDbContainer = new MongoDbBuilder(TestSession.GetImageFromDockerfile()) + .WithReplicaSet() + .WithResourceMapping(Encoding.Default.GetBytes(ReadinessMessagesScriptContent), ReadinessMessagesScriptFilePath, fileMode: Unix.FileMode755) + .Build(); + + public async ValueTask InitializeAsync() + { + await _mongoDbContainer.StartAsync() + .ConfigureAwait(false); + } + + public ValueTask DisposeAsync() + { + return _mongoDbContainer.DisposeAsync(); + } + + [Fact] + [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))] + public async Task StartsWithAdditionalReadinessMessages() + { + // Given + const string scriptContent = "rs.status().ok;"; + + // When + var execResult = await _mongoDbContainer.ExecScriptAsync(scriptContent, TestContext.Current.CancellationToken) + .ConfigureAwait(true); + + // Then + Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr); + Assert.Empty(execResult.Stderr); + } +} \ No newline at end of file diff --git a/tests/Testcontainers.MongoDb.Tests/Usings.cs b/tests/Testcontainers.MongoDb.Tests/Usings.cs index d9d3fa9ac..26affceb1 100644 --- a/tests/Testcontainers.MongoDb.Tests/Usings.cs +++ b/tests/Testcontainers.MongoDb.Tests/Usings.cs @@ -1,6 +1,7 @@ global using System; global using System.Collections.Generic; global using System.Linq; +global using System.Text; global using System.Threading.Tasks; global using DotNet.Testcontainers.Commons; global using DotNet.Testcontainers.Configurations;