-
-
Notifications
You must be signed in to change notification settings - Fork 339
feat: Add OpenSearch module #1395
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 10 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
3bb6905
add OpenSearch container and tests
digital88 5b40540
add missing line to matrix in cicd.yaml
digital88 34fece7
rebased and updated
digital88 63361cd
rm from workflows (temp)
digital88 fae757f
added and fixed tests
digital88 e43f8e3
add docs
digital88 034f97e
Update cicd.yml add OpenSearch
digital88 af8aded
Merge branch 'develop' into opensearch
HofmeisterAn 1a8591c
Merge branch 'develop' into opensearch
HofmeisterAn 01e9623
fix: Configure wait strategy in Build()
HofmeisterAn a587a6b
password fallback for legacy images
digital88 65a5dd8
Merge branch 'develop' into opensearch
digital88 725d613
chore: Align tests
HofmeisterAn 1a4e120
chore: Move selectors to local variable
HofmeisterAn File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| # OpenSearch | ||
|
|
||
| [OpenSearch](https://opensearch.org/) is an open-source, enterprise-grade search and observability suite that brings order to unstructured data at scale. | ||
|
|
||
| Add the following dependency to your project file: | ||
|
|
||
| ```shell title="NuGet" | ||
| dotnet add package Testcontainers.OpenSearch | ||
| ``` | ||
|
|
||
| You can start an OpenSearch container instance from any .NET application. To create and start a container instance with the default configuration, use the module-specific builder as shown below: | ||
|
|
||
| === "Start an OpenSearch container" | ||
| ```csharp | ||
| var openSearchContainer = new OpenSearchBuilder().Build(); | ||
| await openSearchContainer.StartAsync(); | ||
| ``` | ||
|
|
||
| This example uses xUnit.net's `IAsyncLifetime` interface to manage the lifecycle of the container. The container is started in the `InitializeAsync` method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the `DisposeAsync` method. | ||
|
|
||
| === "Base test class" | ||
| ```csharp | ||
| --8<-- "tests/Testcontainers.OpenSearch.Tests/OpenSearchContainerTest.cs:BaseClass" | ||
| } | ||
| ``` | ||
| === "SslBasicAuth" | ||
| ```csharp | ||
| --8<-- "tests/Testcontainers.OpenSearch.Tests/OpenSearchContainerTest.cs:SslBasicAuth" | ||
| ``` | ||
| === "InsecureNoAuth" | ||
| ```csharp | ||
| --8<-- "tests/Testcontainers.OpenSearch.Tests/OpenSearchContainerTest.cs:InsecureNoAuth" | ||
| ``` | ||
|
|
||
| How to check that client established connection: | ||
| === "PingExample" | ||
| ```csharp | ||
| --8<-- "tests/Testcontainers.OpenSearch.Tests/OpenSearchContainerTest.cs:PingExample" | ||
| ``` | ||
|
|
||
| Creating index and index alias: | ||
| === "IndexAndAliasCreation" | ||
| ```csharp | ||
| --8<-- "tests/Testcontainers.OpenSearch.Tests/OpenSearchContainerTest.cs:IndexAndAliasCreation" | ||
| ``` | ||
| === "CreateTestIndexImpl" | ||
| ```csharp | ||
| --8<-- "tests/Testcontainers.OpenSearch.Tests/OpenSearchContainerTest.cs:CreateTestIndexImpl" | ||
| ``` | ||
|
|
||
| Indexing and searching for document: | ||
| === "IndexingDocument" | ||
| ```csharp | ||
| --8<-- "tests/Testcontainers.OpenSearch.Tests/OpenSearchContainerTest.cs:IndexingDocument" | ||
| ``` | ||
|
|
||
| The test example uses the following NuGet dependencies: | ||
|
|
||
| === "Package References" | ||
| ```xml | ||
| --8<-- "tests/Testcontainers.OpenSearch.Tests/Testcontainers.OpenSearch.Tests.csproj:PackageReferences" | ||
| ``` | ||
|
|
||
| To execute the tests, use the command `dotnet test` from a terminal. | ||
|
|
||
| --8<-- "docs/modules/_call_out_test_projects.txt" | ||
|
|
||
| ## Using legacy docker images | ||
|
|
||
| Before version `2.12.0` OpenSearch docker images have a hardcoded password for default 'admin' user. If you are using such an image, set password explicitly like this: | ||
|
|
||
| === "LegacyImageAdminPassword" | ||
| ```xml | ||
| --8<-- "tests/Testcontainers.OpenSearch.Tests/OpenSearchContainerDifferentImagesTest.cs:LegacyImageAdminPassword" | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| root = true |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,181 @@ | ||
| namespace Testcontainers.OpenSearch; | ||
|
|
||
| /// <inheritdoc cref="ContainerBuilder{TBuilderEntity, TContainerEntity, TConfigurationEntity}" /> | ||
| [PublicAPI] | ||
| public sealed class OpenSearchBuilder : ContainerBuilder<OpenSearchBuilder, OpenSearchContainer, OpenSearchConfiguration> | ||
| { | ||
| public const string OpenSearchImage = "opensearchproject/opensearch:2.12.0"; | ||
|
|
||
| public const int OpenSearchRestApiPort = 9200; | ||
|
|
||
| public const int OpenSearchTransportPort = 9300; | ||
|
|
||
| public const int OpenSearchPerformanceAnalyzerPort = 9600; | ||
|
|
||
| public const string DefaultUsername = "admin"; | ||
|
|
||
| public const string DefaultPassword = "yourStrong(!)P@ssw0rd"; | ||
|
|
||
| public const string DefaultOldInsecurePassword = "admin"; | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="OpenSearchBuilder" /> class. | ||
| /// </summary> | ||
| public OpenSearchBuilder() | ||
| : this(new OpenSearchConfiguration()) | ||
| { | ||
| DockerResourceConfiguration = Init().DockerResourceConfiguration; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="OpenSearchBuilder" /> class. | ||
| /// </summary> | ||
| /// <param name="resourceConfiguration">The Docker resource configuration.</param> | ||
| private OpenSearchBuilder(OpenSearchConfiguration resourceConfiguration) | ||
| : base(resourceConfiguration) | ||
| { | ||
| DockerResourceConfiguration = resourceConfiguration; | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| protected override OpenSearchConfiguration DockerResourceConfiguration { get; } | ||
|
|
||
| /// <summary> | ||
| /// Sets the password for the <c>admin</c> user. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// The password must meet the following complexity requirements: | ||
| /// <list type="bullet"> | ||
| /// <item><description>Minimum of 8 characters</description></item> | ||
| /// <item><description>At least one uppercase letter</description></item> | ||
| /// <item><description>At least one lowercase letter</description></item> | ||
| /// <item><description>At least one digit</description></item> | ||
| /// <item><description>At least one special character</description></item> | ||
| /// </list> | ||
| /// </remarks> | ||
| /// <param name="password">The <c>admin</c> user password.</param> | ||
| /// <returns>A configured instance of <see cref="OpenSearchBuilder" />.</returns> | ||
| public OpenSearchBuilder WithPassword(string password) | ||
| { | ||
| return Merge(DockerResourceConfiguration, new OpenSearchConfiguration(password: password)) | ||
| .WithEnvironment("OPENSEARCH_INITIAL_ADMIN_PASSWORD", password); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Enables or disables the built-in security plugin in OpenSearch. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// When disabled, the <see cref="OpenSearchContainer.GetConnectionString" /> method | ||
| /// will use the <c>http</c> protocol instead of <c>https</c>. | ||
| /// </remarks> | ||
| /// <param name="securityEnabled"><c>true</c> to enable the security plugin; <c>false</c> to disable it.</param> | ||
| /// <returns>A configured instance of <see cref="OpenSearchBuilder" />.</returns> | ||
| public OpenSearchBuilder WithSecurityEnabled(bool securityEnabled = true) | ||
| { | ||
| return Merge(DockerResourceConfiguration, new OpenSearchConfiguration(tlsEnabled: securityEnabled)) | ||
| .WithEnvironment("plugins.security.disabled", (!securityEnabled).ToString().ToLowerInvariant()); | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| public override OpenSearchContainer Build() | ||
| { | ||
| Validate(); | ||
|
|
||
| // By default, the base builder waits until the container is running. However, for OpenSearch, a more advanced waiting strategy is necessary that requires access to the password. | ||
| // If the user does not provide a custom waiting strategy, append the default OpenSearch waiting strategy. | ||
| var openSearchBuilder = DockerResourceConfiguration.WaitStrategies.Count() > 1 ? this : WithWaitStrategy(Wait.ForUnixContainer().AddCustomWaitStrategy(new WaitUntil(DockerResourceConfiguration))); | ||
| return new OpenSearchContainer(openSearchBuilder.DockerResourceConfiguration); | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| protected override OpenSearchBuilder Init() | ||
| { | ||
| return base.Init() | ||
| .WithImage(OpenSearchImage) | ||
| .WithPortBinding(OpenSearchRestApiPort, true) | ||
| .WithPortBinding(OpenSearchTransportPort, true) | ||
| .WithPortBinding(OpenSearchPerformanceAnalyzerPort, true) | ||
| .WithEnvironment("discovery.type", "single-node") | ||
| .WithSecurityEnabled() | ||
| .WithUsername(DefaultUsername) | ||
| .WithPassword(DefaultPassword); | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| protected override void Validate() | ||
| { | ||
| base.Validate(); | ||
|
|
||
| _ = Guard.Argument(DockerResourceConfiguration.Password, nameof(DockerResourceConfiguration.Password)) | ||
| .NotNull() | ||
| .NotEmpty(); | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| protected override OpenSearchBuilder Clone(IResourceConfiguration<CreateContainerParameters> resourceConfiguration) | ||
| { | ||
| return Merge(DockerResourceConfiguration, new OpenSearchConfiguration(resourceConfiguration)); | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| protected override OpenSearchBuilder Clone(IContainerConfiguration resourceConfiguration) | ||
| { | ||
| return Merge(DockerResourceConfiguration, new OpenSearchConfiguration(resourceConfiguration)); | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| protected override OpenSearchBuilder Merge(OpenSearchConfiguration oldValue, OpenSearchConfiguration newValue) | ||
| { | ||
| return new OpenSearchBuilder(new OpenSearchConfiguration(oldValue, newValue)); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Sets the OpenSearch username. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// The Docker image does not allow to configure the username. | ||
| /// </remarks> | ||
| /// <param name="username">The OpenSearch username.</param> | ||
| /// <returns>A configured instance of <see cref="OpenSearchBuilder" />.</returns> | ||
| private OpenSearchBuilder WithUsername(string username) | ||
| { | ||
| return Merge(DockerResourceConfiguration, new OpenSearchConfiguration(username: username)); | ||
| } | ||
|
|
||
| /// <inheritdoc cref="IWaitUntil" /> | ||
| private sealed class WaitUntil : IWaitUntil | ||
| { | ||
| private readonly bool _tlsEnabled; | ||
|
|
||
| private readonly string _username; | ||
|
|
||
| private readonly string _password; | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="WaitUntil" /> class. | ||
| /// </summary> | ||
| /// <param name="configuration">The container configuration.</param> | ||
| public WaitUntil(OpenSearchConfiguration configuration) | ||
| { | ||
| _tlsEnabled = configuration.TlsEnabled.GetValueOrDefault(); | ||
| _username = configuration.Username; | ||
| _password = configuration.Password; | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| public async Task<bool> UntilAsync(IContainer container) | ||
| { | ||
| using var httpMessageHandler = new HttpClientHandler(); | ||
| httpMessageHandler.ServerCertificateCustomValidationCallback = (_, _, _, _) => true; | ||
|
|
||
| var httpWaitStrategy = new HttpWaitStrategy() | ||
| .UsingHttpMessageHandler(httpMessageHandler) | ||
| .UsingTls(_tlsEnabled) | ||
| .WithBasicAuthentication(_username, _password) | ||
| .ForPort(OpenSearchRestApiPort); | ||
|
|
||
| return await httpWaitStrategy.UntilAsync(container) | ||
| .ConfigureAwait(false); | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.