From 61eab0638e0c77d9f96a6c0e981f6f3c0b762c95 Mon Sep 17 00:00:00 2001 From: Artiom Chilaru Date: Wed, 12 Jun 2024 16:43:25 +0100 Subject: [PATCH 1/8] Adding MongoDb replica set support --- src/Testcontainers.MongoDb/MongoDbBuilder.cs | 23 +++++++++ .../MongoDbConfiguration.cs | 11 ++++- .../MongoDbContainerTest.cs | 49 ++++++++++++++++++- 3 files changed, 80 insertions(+), 3 deletions(-) diff --git a/src/Testcontainers.MongoDb/MongoDbBuilder.cs b/src/Testcontainers.MongoDb/MongoDbBuilder.cs index b4221b018..fe4d168e0 100644 --- a/src/Testcontainers.MongoDb/MongoDbBuilder.cs +++ b/src/Testcontainers.MongoDb/MongoDbBuilder.cs @@ -60,6 +60,13 @@ public MongoDbBuilder WithPassword(string password) .WithEnvironment("MONGO_INITDB_ROOT_PASSWORD", initDbRootPassword); } + public MongoDbBuilder WithReplicaSet(string replicaSetName = "rs0") + { + var initReplicaSetName = replicaSetName ?? "rs0"; + + return Merge(DockerResourceConfiguration, new MongoDbConfiguration(replicaSetName: initReplicaSetName)); + } + /// public override MongoDbContainer Build() { @@ -69,6 +76,22 @@ public override MongoDbContainer Build() // provided, the log message "Waiting for connections" appears twice. // If the user does not provide a custom waiting strategy, append the default MongoDb waiting strategy. var mongoDbBuilder = DockerResourceConfiguration.WaitStrategies.Count() > 1 ? this : WithWaitStrategy(Wait.ForUnixContainer().AddCustomWaitStrategy(new WaitUntil(DockerResourceConfiguration))); + + if (!string.IsNullOrEmpty(DockerResourceConfiguration.ReplicaSetName)) + { + mongoDbBuilder = mongoDbBuilder + .WithCommand(DockerResourceConfiguration.Command.Concat(["--replSet", DockerResourceConfiguration.ReplicaSetName, "--keyFile", "/tmp/keyfile"]).ToArray()) + .WithResourceMapping(Encoding.Default.GetBytes(""" + #!/bin/bash + openssl rand -base64 32 > "/tmp/keyfile" + chmod 600 /tmp/keyfile + chown 999:999 /tmp/keyfile + """.Replace("\r", "")), "/docker-entrypoint-initdb.d/01-init-keyfile.sh", UnixFileModes.OtherRead | UnixFileModes.OtherExecute) + .WithWaitStrategy(Wait.ForUnixContainer().UntilCommandIsCompleted( + "mongosh -u $MONGO_INITDB_ROOT_USERNAME -p $MONGO_INITDB_ROOT_PASSWORD --quiet --eval " + + $"\"try {{ rs.status().ok }} catch (e) {{ rs.initiate({{'_id':'{DockerResourceConfiguration.ReplicaSetName}', members: [{{'_id':1, 'host':'127.0.0.1:27017'}}]}}).ok }}\"")); + } + return new MongoDbContainer(mongoDbBuilder.DockerResourceConfiguration); } diff --git a/src/Testcontainers.MongoDb/MongoDbConfiguration.cs b/src/Testcontainers.MongoDb/MongoDbConfiguration.cs index b0f008ee6..632801726 100644 --- a/src/Testcontainers.MongoDb/MongoDbConfiguration.cs +++ b/src/Testcontainers.MongoDb/MongoDbConfiguration.cs @@ -11,10 +11,12 @@ public sealed class MongoDbConfiguration : ContainerConfiguration /// The MongoDb password. public MongoDbConfiguration( string username = null, - string password = null) + string password = null, + string replicaSetName = null) { Username = username; Password = password; + ReplicaSetName = replicaSetName; } /// @@ -57,6 +59,7 @@ public MongoDbConfiguration(MongoDbConfiguration oldValue, MongoDbConfiguration { Username = BuildConfiguration.Combine(oldValue.Username, newValue.Username); Password = BuildConfiguration.Combine(oldValue.Password, newValue.Password); + ReplicaSetName = BuildConfiguration.Combine(oldValue.ReplicaSetName, newValue.ReplicaSetName); } /// @@ -68,4 +71,10 @@ public MongoDbConfiguration(MongoDbConfiguration oldValue, MongoDbConfiguration /// Gets the MongoDb password. /// public string Password { get; } + + /// + /// Name of the replica set. If specified, the container will be started as a single node replica set. + /// + /// rs0 + public string ReplicaSetName { get; } } \ No newline at end of file diff --git a/tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs b/tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs index 732ab150f..8bbbbb3be 100644 --- a/tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs +++ b/tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs @@ -3,10 +3,12 @@ namespace Testcontainers.MongoDb; public abstract class MongoDbContainerTest : IAsyncLifetime { private readonly MongoDbContainer _mongoDbContainer; + private readonly bool _isReplicaSet; - private MongoDbContainerTest(MongoDbContainer mongoDbContainer) + private MongoDbContainerTest(MongoDbContainer mongoDbContainer, bool isReplicaSet = false) { _mongoDbContainer = mongoDbContainer; + _isReplicaSet = isReplicaSet; } public Task InitializeAsync() @@ -49,6 +51,31 @@ public async Task ExecScriptReturnsSuccessful() Assert.Empty(execResult.Stderr); } + [Fact] + [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))] + public async Task ReplicaSetStatus() + { + // Given + const string scriptContent = "rs.status().ok;"; + + // When + var execResult = await _mongoDbContainer.ExecScriptAsync(scriptContent) + .ConfigureAwait(true); + + // Then + if (_isReplicaSet) + { + Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr); + Assert.Empty(execResult.Stderr); + Assert.Equal("true\n", execResult.Stdout); + } + else + { + Assert.Equal(1L, execResult.ExitCode); + Assert.Equal("MongoServerError: not running with --replSet\n", execResult.Stderr); + } + } + [UsedImplicitly] public sealed class MongoDbDefaultConfiguration : MongoDbContainerTest { @@ -80,7 +107,25 @@ public MongoDbV5Configuration() public sealed class MongoDbV4Configuration : MongoDbContainerTest { public MongoDbV4Configuration() - : base(new MongoDbBuilder().WithImage("mongo:4.4").Build()) + : base(new MongoDbBuilder().WithImage("mongo:4.4").Build(), isReplicaSet: true) // Replica set status returns ok in MongoDB 4.4 without initialising it + { + } + } + + [UsedImplicitly] + public sealed class MongoDbReplicaSetDefaultConfiguration : MongoDbContainerTest + { + public MongoDbReplicaSetDefaultConfiguration() + : base(new MongoDbBuilder().WithReplicaSet().Build(), isReplicaSet: true) + { + } + } + + [UsedImplicitly] + public sealed class MongoDbNamedReplicaSetConfiguration : MongoDbContainerTest + { + public MongoDbNamedReplicaSetConfiguration() + : base(new MongoDbBuilder().WithReplicaSet("rscustom").Build(), isReplicaSet: true) { } } From e1a9a797b99a6d3940b9fc85a779cbf264bd5831 Mon Sep 17 00:00:00 2001 From: Artiom Chilaru Date: Wed, 12 Jun 2024 16:52:30 +0100 Subject: [PATCH 2/8] Adding replica set name to the connection string --- src/Testcontainers.MongoDb/MongoDbBuilder.cs | 2 +- src/Testcontainers.MongoDb/MongoDbContainer.cs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Testcontainers.MongoDb/MongoDbBuilder.cs b/src/Testcontainers.MongoDb/MongoDbBuilder.cs index fe4d168e0..fdd1ecd5d 100644 --- a/src/Testcontainers.MongoDb/MongoDbBuilder.cs +++ b/src/Testcontainers.MongoDb/MongoDbBuilder.cs @@ -80,7 +80,7 @@ public override MongoDbContainer Build() if (!string.IsNullOrEmpty(DockerResourceConfiguration.ReplicaSetName)) { mongoDbBuilder = mongoDbBuilder - .WithCommand(DockerResourceConfiguration.Command.Concat(["--replSet", DockerResourceConfiguration.ReplicaSetName, "--keyFile", "/tmp/keyfile"]).ToArray()) + .WithCommand(DockerResourceConfiguration.Command.Concat(["--replSet", DockerResourceConfiguration.ReplicaSetName, "--keyFile", "/tmp/keyfile", "--bind_ip_all"]).ToArray()) .WithResourceMapping(Encoding.Default.GetBytes(""" #!/bin/bash openssl rand -base64 32 > "/tmp/keyfile" diff --git a/src/Testcontainers.MongoDb/MongoDbContainer.cs b/src/Testcontainers.MongoDb/MongoDbContainer.cs index 25e3531b4..eabf2b181 100644 --- a/src/Testcontainers.MongoDb/MongoDbContainer.cs +++ b/src/Testcontainers.MongoDb/MongoDbContainer.cs @@ -26,6 +26,10 @@ public string GetConnectionString() var endpoint = new UriBuilder("mongodb", Hostname, GetMappedPublicPort(MongoDbBuilder.MongoDbPort)); endpoint.UserName = Uri.EscapeDataString(_configuration.Username); endpoint.Password = Uri.EscapeDataString(_configuration.Password); + if (!string.IsNullOrEmpty(_configuration.ReplicaSetName)) + { + endpoint.Query = $"replicaSet={_configuration.ReplicaSetName}"; + } return endpoint.ToString(); } From f629962a4f74fbe611f64bda1fce19fd4de0cf59 Mon Sep 17 00:00:00 2001 From: Artiom Chilaru Date: Wed, 12 Jun 2024 17:24:00 +0100 Subject: [PATCH 3/8] Remove the replica set name from the connection string --- src/Testcontainers.MongoDb/MongoDbContainer.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Testcontainers.MongoDb/MongoDbContainer.cs b/src/Testcontainers.MongoDb/MongoDbContainer.cs index eabf2b181..25e3531b4 100644 --- a/src/Testcontainers.MongoDb/MongoDbContainer.cs +++ b/src/Testcontainers.MongoDb/MongoDbContainer.cs @@ -26,10 +26,6 @@ public string GetConnectionString() var endpoint = new UriBuilder("mongodb", Hostname, GetMappedPublicPort(MongoDbBuilder.MongoDbPort)); endpoint.UserName = Uri.EscapeDataString(_configuration.Username); endpoint.Password = Uri.EscapeDataString(_configuration.Password); - if (!string.IsNullOrEmpty(_configuration.ReplicaSetName)) - { - endpoint.Query = $"replicaSet={_configuration.ReplicaSetName}"; - } return endpoint.ToString(); } From fe2887e13882c1d44fb8638d91ac5a5d593ff0c1 Mon Sep 17 00:00:00 2001 From: Artiom Chilaru Date: Mon, 17 Jun 2024 09:22:28 +0100 Subject: [PATCH 4/8] Docs page update --- docs/modules/mongodb.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/modules/mongodb.md b/docs/modules/mongodb.md index 478d0d63e..6adf4fea4 100644 --- a/docs/modules/mongodb.md +++ b/docs/modules/mongodb.md @@ -45,3 +45,12 @@ public sealed class MongoDbContainerTest : IAsyncLifetime ``` To execute the tests, use the command `dotnet test` from a terminal. + +## MongoDb Replica Set + +By default, MongoDB runs as a standalone instance. If your tests require a MongoDB replica set, use the code below which will initialise it as a single node replica set: + +```csharp + private readonly MongoDbContainer _mongoDbContainer = + new MongoDbBuilder().WithReplicaSet().Build(); +``` From a33964aabbd39a13583338ff5334938c397b8fd7 Mon Sep 17 00:00:00 2001 From: Andre Hofmeister <9199345+HofmeisterAn@users.noreply.github.com> Date: Fri, 16 Aug 2024 15:59:04 +0200 Subject: [PATCH 5/8] chore: Propose changes --- src/Testcontainers.MongoDb/MongoDbBuilder.cs | 86 +++++++++++++++----- 1 file changed, 67 insertions(+), 19 deletions(-) diff --git a/src/Testcontainers.MongoDb/MongoDbBuilder.cs b/src/Testcontainers.MongoDb/MongoDbBuilder.cs index fdd1ecd5d..3699f3920 100644 --- a/src/Testcontainers.MongoDb/MongoDbBuilder.cs +++ b/src/Testcontainers.MongoDb/MongoDbBuilder.cs @@ -12,6 +12,10 @@ public sealed class MongoDbBuilder : ContainerBuilder /// Initializes a new instance of the class. /// @@ -60,11 +64,14 @@ public MongoDbBuilder WithPassword(string password) .WithEnvironment("MONGO_INITDB_ROOT_PASSWORD", initDbRootPassword); } + /// + /// + /// + /// + /// public MongoDbBuilder WithReplicaSet(string replicaSetName = "rs0") { - var initReplicaSetName = replicaSetName ?? "rs0"; - - return Merge(DockerResourceConfiguration, new MongoDbConfiguration(replicaSetName: initReplicaSetName)); + return Merge(DockerResourceConfiguration, new MongoDbConfiguration(replicaSetName: replicaSetName)); } /// @@ -72,26 +79,37 @@ public override MongoDbContainer Build() { Validate(); - // The wait strategy relies on the configuration of MongoDb. If credentials are - // provided, the log message "Waiting for connections" appears twice. - // If the user does not provide a custom waiting strategy, append the default MongoDb waiting strategy. - var mongoDbBuilder = DockerResourceConfiguration.WaitStrategies.Count() > 1 ? this : WithWaitStrategy(Wait.ForUnixContainer().AddCustomWaitStrategy(new WaitUntil(DockerResourceConfiguration))); + IWaitUntil waitUntil; - if (!string.IsNullOrEmpty(DockerResourceConfiguration.ReplicaSetName)) + MongoDbBuilder mongoDbBuilder; + + if (string.IsNullOrEmpty(DockerResourceConfiguration.ReplicaSetName)) { - mongoDbBuilder = mongoDbBuilder - .WithCommand(DockerResourceConfiguration.Command.Concat(["--replSet", DockerResourceConfiguration.ReplicaSetName, "--keyFile", "/tmp/keyfile", "--bind_ip_all"]).ToArray()) - .WithResourceMapping(Encoding.Default.GetBytes(""" - #!/bin/bash - openssl rand -base64 32 > "/tmp/keyfile" - chmod 600 /tmp/keyfile - chown 999:999 /tmp/keyfile - """.Replace("\r", "")), "/docker-entrypoint-initdb.d/01-init-keyfile.sh", UnixFileModes.OtherRead | UnixFileModes.OtherExecute) - .WithWaitStrategy(Wait.ForUnixContainer().UntilCommandIsCompleted( - "mongosh -u $MONGO_INITDB_ROOT_USERNAME -p $MONGO_INITDB_ROOT_PASSWORD --quiet --eval " + - $"\"try {{ rs.status().ok }} catch (e) {{ rs.initiate({{'_id':'{DockerResourceConfiguration.ReplicaSetName}', members: [{{'_id':1, 'host':'127.0.0.1:27017'}}]}}).ok }}\"")); + // The wait strategy relies on the configuration of MongoDb. If credentials are + // provided, the log message "Waiting for connections" appears twice. + waitUntil = new WaitUntil(DockerResourceConfiguration); + mongoDbBuilder = this; + } + else + { + var initKeyFileScript = new StringWriter(); + initKeyFileScript.NewLine = "\n"; + initKeyFileScript.WriteLine("#!/bin/bash"); + initKeyFileScript.WriteLine("openssl rand -base64 32 > \"" + KeyFilePath + "\""); + initKeyFileScript.WriteLine("chmod 600 \"" + KeyFilePath + "\""); + + waitUntil = new WaitInitiateReplicaSet(DockerResourceConfiguration); + mongoDbBuilder = this + .WithCommand("--replSet") + .WithCommand(DockerResourceConfiguration.ReplicaSetName) + .WithCommand("--keyFile") + .WithCommand(KeyFilePath) + .WithCommand("--bind_ip_all") + .WithResourceMapping(Encoding.Default.GetBytes(initKeyFileScript.ToString()), InitKeyFileScriptFilePath, Unix.FileMode755); } + // If the user does not provide a custom waiting strategy, append the default MongoDb waiting strategy. + mongoDbBuilder = DockerResourceConfiguration.WaitStrategies.Count() > 1 ? mongoDbBuilder : mongoDbBuilder.WithWaitStrategy(Wait.ForUnixContainer().AddCustomWaitStrategy(waitUntil)); return new MongoDbContainer(mongoDbBuilder.DockerResourceConfiguration); } @@ -168,4 +186,34 @@ public async Task UntilAsync(IContainer container) .Count(line => line.Contains("Waiting for connections"))); } } + + /// + private sealed class WaitInitiateReplicaSet : IWaitUntil + { + private readonly string _scriptContent; + + /// + /// Initializes a new instance of the class. + /// + /// The container configuration. + public WaitInitiateReplicaSet(MongoDbConfiguration configuration) + { + _scriptContent = $"try{{rs.status().ok}}catch(e){{rs.initiate({{'_id':'{configuration.ReplicaSetName}',members:[{{'_id':1,'host':'127.0.0.1:27017'}}]}}).ok}}"; + } + + /// + public Task UntilAsync(IContainer container) + { + return UntilAsync(container as MongoDbContainer); + } + + /// + private async Task UntilAsync(MongoDbContainer container) + { + var execResult = await container.ExecScriptAsync(_scriptContent) + .ConfigureAwait(false); + + return 0L.Equals(execResult.ExitCode); + } + } } \ No newline at end of file From 5525c143440d9ef31f8c4ad920bcc5494c1d4f9f Mon Sep 17 00:00:00 2001 From: Artiom Chilaru Date: Fri, 23 Aug 2024 17:28:45 +0100 Subject: [PATCH 6/8] Reshuffling the Mongo replica init --- src/Testcontainers.MongoDb/MongoDbBuilder.cs | 54 +++++++++----------- 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/src/Testcontainers.MongoDb/MongoDbBuilder.cs b/src/Testcontainers.MongoDb/MongoDbBuilder.cs index 3699f3920..25b5ed957 100644 --- a/src/Testcontainers.MongoDb/MongoDbBuilder.cs +++ b/src/Testcontainers.MongoDb/MongoDbBuilder.cs @@ -65,13 +65,24 @@ public MongoDbBuilder WithPassword(string password) } /// - /// + /// Initialise the MongoDb container as a single node a replica set. /// - /// - /// + /// The replica set name. + /// A configured instance of . public MongoDbBuilder WithReplicaSet(string replicaSetName = "rs0") { - return Merge(DockerResourceConfiguration, new MongoDbConfiguration(replicaSetName: replicaSetName)); + var initKeyFileScript = new StringWriter(); + initKeyFileScript.NewLine = "\n"; + initKeyFileScript.WriteLine("#!/bin/bash"); + initKeyFileScript.WriteLine("openssl rand -base64 32 > \"" + KeyFilePath + "\""); + initKeyFileScript.WriteLine("chmod 600 \"" + KeyFilePath + "\""); + + return Merge(DockerResourceConfiguration, new MongoDbConfiguration(replicaSetName: replicaSetName)) + .WithCommand( + "--replSet", replicaSetName, + "--keyFile", KeyFilePath, + "--bind_ip_all") + .WithResourceMapping(Encoding.Default.GetBytes(initKeyFileScript.ToString()), InitKeyFileScriptFilePath, Unix.FileMode755); } /// @@ -79,37 +90,18 @@ public override MongoDbContainer Build() { Validate(); - IWaitUntil waitUntil; - - MongoDbBuilder mongoDbBuilder; + // The wait strategy relies on the configuration of MongoDb. If credentials are + // provided, the log message "Waiting for connections" appears twice. + // If the user does not provide a custom waiting strategy, append the default MongoDb waiting strategy. + var mongoDbBuilder = DockerResourceConfiguration.WaitStrategies.Count() > 1 ? this : WithWaitStrategy(Wait.ForUnixContainer().AddCustomWaitStrategy(new WaitUntil(DockerResourceConfiguration))); - if (string.IsNullOrEmpty(DockerResourceConfiguration.ReplicaSetName)) + if (!string.IsNullOrEmpty(DockerResourceConfiguration.ReplicaSetName)) { - // The wait strategy relies on the configuration of MongoDb. If credentials are - // provided, the log message "Waiting for connections" appears twice. - waitUntil = new WaitUntil(DockerResourceConfiguration); - mongoDbBuilder = this; - } - else - { - var initKeyFileScript = new StringWriter(); - initKeyFileScript.NewLine = "\n"; - initKeyFileScript.WriteLine("#!/bin/bash"); - initKeyFileScript.WriteLine("openssl rand -base64 32 > \"" + KeyFilePath + "\""); - initKeyFileScript.WriteLine("chmod 600 \"" + KeyFilePath + "\""); - - waitUntil = new WaitInitiateReplicaSet(DockerResourceConfiguration); - mongoDbBuilder = this - .WithCommand("--replSet") - .WithCommand(DockerResourceConfiguration.ReplicaSetName) - .WithCommand("--keyFile") - .WithCommand(KeyFilePath) - .WithCommand("--bind_ip_all") - .WithResourceMapping(Encoding.Default.GetBytes(initKeyFileScript.ToString()), InitKeyFileScriptFilePath, Unix.FileMode755); + // If the user has specified a replica set name, we need to initiate the replica set. + var replicaInitStrategy = new WaitInitiateReplicaSet(DockerResourceConfiguration); + mongoDbBuilder = mongoDbBuilder.WithWaitStrategy(Wait.ForUnixContainer().AddCustomWaitStrategy(replicaInitStrategy)); } - // If the user does not provide a custom waiting strategy, append the default MongoDb waiting strategy. - mongoDbBuilder = DockerResourceConfiguration.WaitStrategies.Count() > 1 ? mongoDbBuilder : mongoDbBuilder.WithWaitStrategy(Wait.ForUnixContainer().AddCustomWaitStrategy(waitUntil)); return new MongoDbContainer(mongoDbBuilder.DockerResourceConfiguration); } From 5d7f5fe286272664ef51a8f354a3e01bf6b5b9f2 Mon Sep 17 00:00:00 2001 From: Andre Hofmeister <9199345+HofmeisterAn@users.noreply.github.com> Date: Wed, 28 Aug 2024 19:22:00 +0200 Subject: [PATCH 7/8] chore: Apply minor alignments --- docs/modules/mongodb.md | 5 +-- src/Testcontainers.MongoDb/MongoDbBuilder.cs | 38 +++++++++---------- .../MongoDbConfiguration.cs | 7 +++- src/Testcontainers/Images/DockerImage.cs | 2 +- .../MongoDbContainerTest.cs | 16 ++++---- 5 files changed, 35 insertions(+), 33 deletions(-) diff --git a/docs/modules/mongodb.md b/docs/modules/mongodb.md index 6adf4fea4..7ebbdc6b1 100644 --- a/docs/modules/mongodb.md +++ b/docs/modules/mongodb.md @@ -48,9 +48,8 @@ To execute the tests, use the command `dotnet test` from a terminal. ## MongoDb Replica Set -By default, MongoDB runs as a standalone instance. If your tests require a MongoDB replica set, use the code below which will initialise it as a single node replica set: +By default, MongoDB runs as a standalone instance. If your tests require a MongoDB replica set, use the code below which will initialize it as a single-node replica set: ```csharp - private readonly MongoDbContainer _mongoDbContainer = - new MongoDbBuilder().WithReplicaSet().Build(); +MongoDbContainer _mongoDbContainer = new MongoDbBuilder().WithReplicaSet().Build(); ``` diff --git a/src/Testcontainers.MongoDb/MongoDbBuilder.cs b/src/Testcontainers.MongoDb/MongoDbBuilder.cs index 25b5ed957..1a806c7a8 100644 --- a/src/Testcontainers.MongoDb/MongoDbBuilder.cs +++ b/src/Testcontainers.MongoDb/MongoDbBuilder.cs @@ -14,7 +14,7 @@ public sealed class MongoDbBuilder : ContainerBuilder /// Initializes a new instance of the class. @@ -65,7 +65,7 @@ public MongoDbBuilder WithPassword(string password) } /// - /// Initialise the MongoDb container as a single node a replica set. + /// Initialize MongoDB as a single-node replica set. /// /// The replica set name. /// A configured instance of . @@ -74,14 +74,11 @@ public MongoDbBuilder WithReplicaSet(string replicaSetName = "rs0") var initKeyFileScript = new StringWriter(); initKeyFileScript.NewLine = "\n"; initKeyFileScript.WriteLine("#!/bin/bash"); - initKeyFileScript.WriteLine("openssl rand -base64 32 > \"" + KeyFilePath + "\""); - initKeyFileScript.WriteLine("chmod 600 \"" + KeyFilePath + "\""); + initKeyFileScript.WriteLine("openssl rand -base64 32 > \"" + KeyFileFilePath + "\""); + initKeyFileScript.WriteLine("chmod 600 \"" + KeyFileFilePath + "\""); return Merge(DockerResourceConfiguration, new MongoDbConfiguration(replicaSetName: replicaSetName)) - .WithCommand( - "--replSet", replicaSetName, - "--keyFile", KeyFilePath, - "--bind_ip_all") + .WithCommand("--replSet", replicaSetName, "--keyFile", KeyFileFilePath, "--bind_ip_all") .WithResourceMapping(Encoding.Default.GetBytes(initKeyFileScript.ToString()), InitKeyFileScriptFilePath, Unix.FileMode755); } @@ -90,18 +87,21 @@ public override MongoDbContainer Build() { Validate(); - // The wait strategy relies on the configuration of MongoDb. If credentials are - // provided, the log message "Waiting for connections" appears twice. - // If the user does not provide a custom waiting strategy, append the default MongoDb waiting strategy. - var mongoDbBuilder = DockerResourceConfiguration.WaitStrategies.Count() > 1 ? this : WithWaitStrategy(Wait.ForUnixContainer().AddCustomWaitStrategy(new WaitUntil(DockerResourceConfiguration))); + IWaitUntil waitUntil; - if (!string.IsNullOrEmpty(DockerResourceConfiguration.ReplicaSetName)) + if (string.IsNullOrEmpty(DockerResourceConfiguration.ReplicaSetName)) + { + // The wait strategy relies on the configuration of MongoDb. If credentials are + // provided, the log message "Waiting for connections" appears twice. + waitUntil = new WaitIndicateReadiness(DockerResourceConfiguration); + } + else { - // If the user has specified a replica set name, we need to initiate the replica set. - var replicaInitStrategy = new WaitInitiateReplicaSet(DockerResourceConfiguration); - mongoDbBuilder = mongoDbBuilder.WithWaitStrategy(Wait.ForUnixContainer().AddCustomWaitStrategy(replicaInitStrategy)); + waitUntil = new WaitInitiateReplicaSet(DockerResourceConfiguration); } + // If the user does not provide a custom waiting strategy, append the default MongoDb waiting strategy. + var mongoDbBuilder = DockerResourceConfiguration.WaitStrategies.Count() > 1 ? this : WithWaitStrategy(Wait.ForUnixContainer().AddCustomWaitStrategy(waitUntil)); return new MongoDbContainer(mongoDbBuilder.DockerResourceConfiguration); } @@ -151,17 +151,17 @@ protected override MongoDbBuilder Merge(MongoDbConfiguration oldValue, MongoDbCo } /// - private sealed class WaitUntil : IWaitUntil + private sealed class WaitIndicateReadiness : IWaitUntil { private static readonly string[] LineEndings = { "\r\n", "\n" }; private readonly int _count; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The container configuration. - public WaitUntil(MongoDbConfiguration configuration) + public WaitIndicateReadiness(MongoDbConfiguration configuration) { _count = string.IsNullOrEmpty(configuration.Username) && string.IsNullOrEmpty(configuration.Password) ? 1 : 2; } diff --git a/src/Testcontainers.MongoDb/MongoDbConfiguration.cs b/src/Testcontainers.MongoDb/MongoDbConfiguration.cs index 632801726..b069b2959 100644 --- a/src/Testcontainers.MongoDb/MongoDbConfiguration.cs +++ b/src/Testcontainers.MongoDb/MongoDbConfiguration.cs @@ -9,6 +9,7 @@ public sealed class MongoDbConfiguration : ContainerConfiguration /// /// The MongoDb username. /// The MongoDb password. + /// The replica set name. public MongoDbConfiguration( string username = null, string password = null, @@ -73,8 +74,10 @@ public MongoDbConfiguration(MongoDbConfiguration oldValue, MongoDbConfiguration public string Password { get; } /// - /// Name of the replica set. If specified, the container will be started as a single node replica set. + /// Gets the replica set name. /// - /// rs0 + /// + /// If specified, the container will be started as a single-node replica set. + /// public string ReplicaSetName { get; } } \ No newline at end of file diff --git a/src/Testcontainers/Images/DockerImage.cs b/src/Testcontainers/Images/DockerImage.cs index ef7341c44..8347b3fbe 100644 --- a/src/Testcontainers/Images/DockerImage.cs +++ b/src/Testcontainers/Images/DockerImage.cs @@ -126,7 +126,7 @@ public bool MatchVersion(Predicate predicate) /// public bool MatchVersion(Predicate predicate) { - var versionMatch = Regex.Match(Tag, @"^(\d+)(\.\d+)?(\.\d+)?", RegexOptions.None, TimeSpan.FromSeconds(1)); + var versionMatch = Regex.Match(Tag, "^(\\d+)(\\.\\d+)?(\\.\\d+)?", RegexOptions.None, TimeSpan.FromSeconds(1)); if (!versionMatch.Success) { diff --git a/tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs b/tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs index 8bbbbb3be..fd60860b5 100644 --- a/tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs +++ b/tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs @@ -3,12 +3,13 @@ namespace Testcontainers.MongoDb; public abstract class MongoDbContainerTest : IAsyncLifetime { private readonly MongoDbContainer _mongoDbContainer; - private readonly bool _isReplicaSet; - private MongoDbContainerTest(MongoDbContainer mongoDbContainer, bool isReplicaSet = false) + private readonly bool _replicaSetEnabled; + + private MongoDbContainerTest(MongoDbContainer mongoDbContainer, bool replicaSetEnabled = false) { _mongoDbContainer = mongoDbContainer; - _isReplicaSet = isReplicaSet; + _replicaSetEnabled = replicaSetEnabled; } public Task InitializeAsync() @@ -63,11 +64,10 @@ public async Task ReplicaSetStatus() .ConfigureAwait(true); // Then - if (_isReplicaSet) + if (_replicaSetEnabled) { Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr); Assert.Empty(execResult.Stderr); - Assert.Equal("true\n", execResult.Stdout); } else { @@ -107,7 +107,7 @@ public MongoDbV5Configuration() public sealed class MongoDbV4Configuration : MongoDbContainerTest { public MongoDbV4Configuration() - : base(new MongoDbBuilder().WithImage("mongo:4.4").Build(), isReplicaSet: true) // Replica set status returns ok in MongoDB 4.4 without initialising it + : base(new MongoDbBuilder().WithImage("mongo:4.4").Build(), true) { } } @@ -116,7 +116,7 @@ public MongoDbV4Configuration() public sealed class MongoDbReplicaSetDefaultConfiguration : MongoDbContainerTest { public MongoDbReplicaSetDefaultConfiguration() - : base(new MongoDbBuilder().WithReplicaSet().Build(), isReplicaSet: true) + : base(new MongoDbBuilder().WithReplicaSet().Build(), true /* Replica set status returns "ok" in MongoDB 4.4 without initialization. */) { } } @@ -125,7 +125,7 @@ public MongoDbReplicaSetDefaultConfiguration() public sealed class MongoDbNamedReplicaSetConfiguration : MongoDbContainerTest { public MongoDbNamedReplicaSetConfiguration() - : base(new MongoDbBuilder().WithReplicaSet("rscustom").Build(), isReplicaSet: true) + : base(new MongoDbBuilder().WithReplicaSet("rs1").Build(), true) { } } From 213f3c4b5471c32d0df94be88e30546585949681 Mon Sep 17 00:00:00 2001 From: Andre Hofmeister <9199345+HofmeisterAn@users.noreply.github.com> Date: Wed, 28 Aug 2024 19:25:57 +0200 Subject: [PATCH 8/8] fix: Morve comment to right test class --- tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs b/tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs index fd60860b5..81cfa2e46 100644 --- a/tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs +++ b/tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs @@ -107,7 +107,7 @@ public MongoDbV5Configuration() public sealed class MongoDbV4Configuration : MongoDbContainerTest { public MongoDbV4Configuration() - : base(new MongoDbBuilder().WithImage("mongo:4.4").Build(), true) + : base(new MongoDbBuilder().WithImage("mongo:4.4").Build(), true /* Replica set status returns "ok" in MongoDB 4.4 without initialization. */) { } } @@ -116,7 +116,7 @@ public MongoDbV4Configuration() public sealed class MongoDbReplicaSetDefaultConfiguration : MongoDbContainerTest { public MongoDbReplicaSetDefaultConfiguration() - : base(new MongoDbBuilder().WithReplicaSet().Build(), true /* Replica set status returns "ok" in MongoDB 4.4 without initialization. */) + : base(new MongoDbBuilder().WithReplicaSet().Build(), true) { } }