diff --git a/Directory.Packages.props b/Directory.Packages.props
index 6eeb39533..3b7235616 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -47,6 +47,7 @@
+
diff --git a/Testcontainers.sln b/Testcontainers.sln
index 94af5e2c3..9595905ed 100644
--- a/Testcontainers.sln
+++ b/Testcontainers.sln
@@ -63,6 +63,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.LocalStack",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.MariaDb", "src\Testcontainers.MariaDb\Testcontainers.MariaDb.csproj", "{4B204EB3-C478-422E-9B6F-62DF3871291A}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Milvus", "src\Testcontainers.Milvus\Testcontainers.Milvus.csproj", "{B024E315-831F-429D-92AA-44B839AC10F4}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Minio", "src\Testcontainers.Minio\Testcontainers.Minio.csproj", "{1266E1E6-5CEF-4161-8B45-83282455746E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.MongoDb", "src\Testcontainers.MongoDb\Testcontainers.MongoDb.csproj", "{2613F146-6C66-4059-9D37-D48BA6B61515}"
@@ -151,6 +153,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.LocalStack.T
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.MariaDb.Tests", "tests\Testcontainers.MariaDb.Tests\Testcontainers.MariaDb.Tests.csproj", "{7F0AE083-9DB8-4BD4-91F7-C199DCC7301D}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Milvus.Tests", "tests\Testcontainers.Milvus.Tests\Testcontainers.Milvus.Tests.csproj", "{5247DF94-32F3-4ED6-AE71-6AB4F4078E6D}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Minio.Tests", "tests\Testcontainers.Minio.Tests\Testcontainers.Minio.Tests.csproj", "{5DB1F35F-B714-4B62-84BE-16A33084D3E1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.MongoDb.Tests", "tests\Testcontainers.MongoDb.Tests\Testcontainers.MongoDb.Tests.csproj", "{82A7E7B8-3187-4CAE-845B-0BF43409B38A}"
@@ -300,6 +304,10 @@ Global
{4B204EB3-C478-422E-9B6F-62DF3871291A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4B204EB3-C478-422E-9B6F-62DF3871291A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4B204EB3-C478-422E-9B6F-62DF3871291A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B024E315-831F-429D-92AA-44B839AC10F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B024E315-831F-429D-92AA-44B839AC10F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B024E315-831F-429D-92AA-44B839AC10F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B024E315-831F-429D-92AA-44B839AC10F4}.Release|Any CPU.Build.0 = Release|Any CPU
{1266E1E6-5CEF-4161-8B45-83282455746E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1266E1E6-5CEF-4161-8B45-83282455746E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1266E1E6-5CEF-4161-8B45-83282455746E}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -476,6 +484,10 @@ Global
{7F0AE083-9DB8-4BD4-91F7-C199DCC7301D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7F0AE083-9DB8-4BD4-91F7-C199DCC7301D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7F0AE083-9DB8-4BD4-91F7-C199DCC7301D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5247DF94-32F3-4ED6-AE71-6AB4F4078E6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5247DF94-32F3-4ED6-AE71-6AB4F4078E6D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5247DF94-32F3-4ED6-AE71-6AB4F4078E6D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5247DF94-32F3-4ED6-AE71-6AB4F4078E6D}.Release|Any CPU.Build.0 = Release|Any CPU
{5DB1F35F-B714-4B62-84BE-16A33084D3E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5DB1F35F-B714-4B62-84BE-16A33084D3E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5DB1F35F-B714-4B62-84BE-16A33084D3E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -583,6 +595,7 @@ Global
{FCF59758-2403-4EC9-9EAE-4EC69A3F27AF} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{3792268A-EF08-4569-8118-991E08FD61C4} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{4B204EB3-C478-422E-9B6F-62DF3871291A} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
+ {B024E315-831F-429D-92AA-44B839AC10F4} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{1266E1E6-5CEF-4161-8B45-83282455746E} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{2613F146-6C66-4059-9D37-D48BA6B61515} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{121FB123-40D9-44D4-9AB7-AD57ED34F466} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
@@ -627,6 +640,7 @@ Global
{FA59D75A-8D3A-412C-92E6-4A56033162DD} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{728CBE16-1D52-4F84-AF01-7229E6013512} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{7F0AE083-9DB8-4BD4-91F7-C199DCC7301D} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
+ {5247DF94-32F3-4ED6-AE71-6AB4F4078E6D} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{5DB1F35F-B714-4B62-84BE-16A33084D3E1} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{82A7E7B8-3187-4CAE-845B-0BF43409B38A} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{25DBED78-99F4-433F-BBF5-1B4E9DEAE437} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
diff --git a/src/Testcontainers.Milvus/.editorconfig b/src/Testcontainers.Milvus/.editorconfig
new file mode 100644
index 000000000..6f066619d
--- /dev/null
+++ b/src/Testcontainers.Milvus/.editorconfig
@@ -0,0 +1 @@
+root = true
\ No newline at end of file
diff --git a/src/Testcontainers.Milvus/MilvusBuilder.cs b/src/Testcontainers.Milvus/MilvusBuilder.cs
new file mode 100644
index 000000000..24c6ed318
--- /dev/null
+++ b/src/Testcontainers.Milvus/MilvusBuilder.cs
@@ -0,0 +1,94 @@
+namespace Testcontainers.Milvus;
+
+///
+[PublicAPI]
+public sealed class MilvusBuilder : ContainerBuilder
+{
+ public const string MilvusEtcdConfigFilePath = "/milvus/configs/embedEtcd.yaml";
+
+ public const string MilvusImage = "milvusdb/milvus:v2.3.10";
+
+ public const ushort MilvusManagementPort = 9091;
+
+ public const ushort MilvusGrpcPort = 19530;
+
+ private static readonly byte[] EtcdConfig = Encoding.Default.GetBytes(string.Join("\n", "advertise-client-urls: http://0.0.0.0:2379", "listen-client-urls: http://0.0.0.0:2379"));
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MilvusBuilder()
+ : this(new MilvusConfiguration())
+ {
+ DockerResourceConfiguration = Init().DockerResourceConfiguration;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Docker resource configuration.
+ private MilvusBuilder(MilvusConfiguration resourceConfiguration)
+ : base(resourceConfiguration)
+ {
+ DockerResourceConfiguration = resourceConfiguration;
+ }
+
+ ///
+ protected override MilvusConfiguration DockerResourceConfiguration { get; }
+
+ ///
+ /// Sets the etcd endpoint.
+ ///
+ /// The etcd endpoint.
+ /// A configured instance of .
+ public MilvusBuilder WithEtcdEndpoint(string etcdEndpoint)
+ {
+ return WithEnvironment("ETCD_USE_EMBED", "false")
+ .WithEnvironment("ETCD_CONFIG_PATH", string.Empty)
+ .WithEnvironment("ETCD_DATA_DIR", string.Empty)
+ .WithEnvironment("ETCD_ENDPOINTS", etcdEndpoint);
+ }
+
+ ///
+ public override MilvusContainer Build()
+ {
+ Validate();
+ return new MilvusContainer(DockerResourceConfiguration, TestcontainersSettings.Logger);
+ }
+
+ ///
+ protected override MilvusBuilder Init()
+ {
+ return base.Init()
+ .WithImage(MilvusImage)
+ .WithPortBinding(MilvusManagementPort, true)
+ .WithPortBinding(MilvusGrpcPort, true)
+ .WithCommand("milvus", "run", "standalone")
+ .WithEnvironment("COMMON_STORAGETYPE", "local")
+ // For embedded etcd only; see WithEtcdEndpoint(string) for using an external etcd.
+ .WithEnvironment("ETCD_USE_EMBED", "true")
+ .WithEnvironment("ETCD_CONFIG_PATH", MilvusEtcdConfigFilePath)
+ .WithEnvironment("ETCD_DATA_DIR", "/var/lib/milvus/etcd")
+ .WithResourceMapping(EtcdConfig, MilvusEtcdConfigFilePath)
+ .WithWaitStrategy(Wait.ForUnixContainer().UntilHttpRequestIsSucceeded(request =>
+ request.ForPort(MilvusManagementPort).ForPath("/healthz")));
+ }
+
+ ///
+ protected override MilvusBuilder Clone(IResourceConfiguration resourceConfiguration)
+ {
+ return Merge(DockerResourceConfiguration, new MilvusConfiguration(resourceConfiguration));
+ }
+
+ ///
+ protected override MilvusBuilder Clone(IContainerConfiguration resourceConfiguration)
+ {
+ return Merge(DockerResourceConfiguration, new MilvusConfiguration(resourceConfiguration));
+ }
+
+ ///
+ protected override MilvusBuilder Merge(MilvusConfiguration oldValue, MilvusConfiguration newValue)
+ {
+ return new MilvusBuilder(new MilvusConfiguration(oldValue, newValue));
+ }
+}
\ No newline at end of file
diff --git a/src/Testcontainers.Milvus/MilvusConfiguration.cs b/src/Testcontainers.Milvus/MilvusConfiguration.cs
new file mode 100644
index 000000000..32b7dba70
--- /dev/null
+++ b/src/Testcontainers.Milvus/MilvusConfiguration.cs
@@ -0,0 +1,53 @@
+namespace Testcontainers.Milvus;
+
+///
+[PublicAPI]
+public sealed class MilvusConfiguration : ContainerConfiguration
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MilvusConfiguration()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Docker resource configuration.
+ public MilvusConfiguration(IResourceConfiguration resourceConfiguration)
+ : base(resourceConfiguration)
+ {
+ // Passes the configuration upwards to the base implementations to create an updated immutable copy.
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Docker resource configuration.
+ public MilvusConfiguration(IContainerConfiguration resourceConfiguration)
+ : base(resourceConfiguration)
+ {
+ // Passes the configuration upwards to the base implementations to create an updated immutable copy.
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Docker resource configuration.
+ public MilvusConfiguration(MilvusConfiguration resourceConfiguration)
+ : this(new MilvusConfiguration(), resourceConfiguration)
+ {
+ // Passes the configuration upwards to the base implementations to create an updated immutable copy.
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The old Docker resource configuration.
+ /// The new Docker resource configuration.
+ public MilvusConfiguration(MilvusConfiguration oldValue, MilvusConfiguration newValue)
+ : base(oldValue, newValue)
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/Testcontainers.Milvus/MilvusContainer.cs b/src/Testcontainers.Milvus/MilvusContainer.cs
new file mode 100644
index 000000000..87c58f855
--- /dev/null
+++ b/src/Testcontainers.Milvus/MilvusContainer.cs
@@ -0,0 +1,25 @@
+namespace Testcontainers.Milvus;
+
+///
+[PublicAPI]
+public sealed class MilvusContainer : DockerContainer
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The container configuration.
+ /// The logger.
+ public MilvusContainer(MilvusConfiguration configuration, ILogger logger)
+ : base(configuration, logger)
+ {
+ }
+
+ ///
+ /// Gets the Milvus endpoint.
+ ///
+ /// The Milvus endpoint.
+ public Uri GetEndpoint()
+ {
+ return new UriBuilder(Uri.UriSchemeHttp, Hostname, GetMappedPublicPort(MilvusBuilder.MilvusGrpcPort)).Uri;
+ }
+}
\ No newline at end of file
diff --git a/src/Testcontainers.Milvus/Testcontainers.Milvus.csproj b/src/Testcontainers.Milvus/Testcontainers.Milvus.csproj
new file mode 100644
index 000000000..51735310a
--- /dev/null
+++ b/src/Testcontainers.Milvus/Testcontainers.Milvus.csproj
@@ -0,0 +1,12 @@
+
+
+ net6.0;net8.0;netstandard2.0;netstandard2.1;net462
+ latest
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Testcontainers.Milvus/Usings.cs b/src/Testcontainers.Milvus/Usings.cs
new file mode 100644
index 000000000..fd93092b4
--- /dev/null
+++ b/src/Testcontainers.Milvus/Usings.cs
@@ -0,0 +1,8 @@
+global using System;
+global using System.Text;
+global using Docker.DotNet.Models;
+global using DotNet.Testcontainers.Builders;
+global using DotNet.Testcontainers.Configurations;
+global using DotNet.Testcontainers.Containers;
+global using JetBrains.Annotations;
+global using Microsoft.Extensions.Logging;
\ No newline at end of file
diff --git a/tests/Testcontainers.Milvus.Tests/.editorconfig b/tests/Testcontainers.Milvus.Tests/.editorconfig
new file mode 100644
index 000000000..78b36ca08
--- /dev/null
+++ b/tests/Testcontainers.Milvus.Tests/.editorconfig
@@ -0,0 +1 @@
+root = true
diff --git a/tests/Testcontainers.Milvus.Tests/MilvusContainerTest.cs b/tests/Testcontainers.Milvus.Tests/MilvusContainerTest.cs
new file mode 100644
index 000000000..b41c488bf
--- /dev/null
+++ b/tests/Testcontainers.Milvus.Tests/MilvusContainerTest.cs
@@ -0,0 +1,79 @@
+namespace Testcontainers.Milvus;
+
+public abstract class MilvusContainerTest : IAsyncLifetime
+{
+ private const string MilvusVersion = "v2.3.10";
+
+ private readonly MilvusContainer _milvusContainer;
+
+ private MilvusContainerTest(MilvusContainer milvusContainer)
+ {
+ _milvusContainer = milvusContainer;
+ }
+
+ public Task InitializeAsync()
+ {
+ return _milvusContainer.StartAsync();
+ }
+
+ public Task DisposeAsync()
+ {
+ return _milvusContainer.DisposeAsync().AsTask();
+ }
+
+ [Fact]
+ [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))]
+ public async Task GetVersionReturnsExpectedVersion()
+ {
+ // Given
+ using var client = new MilvusClient(_milvusContainer.GetEndpoint());
+
+ // When
+ var version = await client.GetVersionAsync()
+ .ConfigureAwait(true);
+
+ // Then
+ Assert.Equal(MilvusVersion, version);
+ }
+
+ [UsedImplicitly]
+ public sealed class MilvusDefaultConfiguration : MilvusContainerTest
+ {
+ public MilvusDefaultConfiguration()
+ : base(new MilvusBuilder().WithImage("milvusdb/milvus:" + MilvusVersion).Build())
+ {
+ }
+ }
+
+ [UsedImplicitly]
+ public sealed class MilvusSidecarConfiguration : MilvusContainerTest
+ {
+ public MilvusSidecarConfiguration()
+ : this(new NetworkBuilder().Build())
+ {
+ }
+
+ private MilvusSidecarConfiguration(INetwork network)
+ : base(new MilvusBuilder()
+ .WithImage("milvusdb/milvus:" + MilvusVersion)
+ .WithEtcdEndpoint("etcd:2379")
+ .DependsOn(new ContainerBuilder()
+ .WithImage("quay.io/coreos/etcd:v3.5.5")
+ .WithNetworkAliases("etcd")
+ .WithCommand("etcd")
+ .WithCommand("-advertise-client-urls=http://127.0.0.1:2379")
+ .WithCommand("-listen-client-urls=http://0.0.0.0:2379")
+ .WithCommand("-data-dir=/etcd")
+ .WithEnvironment("ETCD_AUTO_COMPACTION_MODE", "periodic")
+ .WithEnvironment("ETCD_AUTO_COMPACTION_RETENTION", "0")
+ .WithEnvironment("ETCD_QUOTA_BACKEND_BYTES", "0")
+ .WithEnvironment("ETCD_SNAPSHOT_COUNT", "100000")
+ .WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged("ready to serve client requests"))
+ .DependsOn(network)
+ .Build())
+ .DependsOn(network)
+ .Build())
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Testcontainers.Milvus.Tests/Testcontainers.Milvus.Tests.csproj b/tests/Testcontainers.Milvus.Tests/Testcontainers.Milvus.Tests.csproj
new file mode 100644
index 000000000..dfb79e9e2
--- /dev/null
+++ b/tests/Testcontainers.Milvus.Tests/Testcontainers.Milvus.Tests.csproj
@@ -0,0 +1,18 @@
+
+
+ net8.0
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/Testcontainers.Milvus.Tests/Usings.cs b/tests/Testcontainers.Milvus.Tests/Usings.cs
new file mode 100644
index 000000000..4f8ec9c80
--- /dev/null
+++ b/tests/Testcontainers.Milvus.Tests/Usings.cs
@@ -0,0 +1,7 @@
+global using System.Threading.Tasks;
+global using DotNet.Testcontainers.Builders;
+global using DotNet.Testcontainers.Commons;
+global using DotNet.Testcontainers.Networks;
+global using JetBrains.Annotations;
+global using Milvus.Client;
+global using Xunit;
\ No newline at end of file