Skip to content

Commit b822e61

Browse files
feat: Add Mosquitto module (#1522)
Co-authored-by: Andre Hofmeister <[email protected]>
1 parent 8617ccf commit b822e61

17 files changed

+500
-1
lines changed

Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
<PackageVersion Include="Microsoft.Playwright" Version="1.55.0"/>
7272
<PackageVersion Include="Milvus.Client" Version="2.2.2-preview.6"/>
7373
<PackageVersion Include="MongoDB.Driver" Version="3.2.0"/>
74+
<PackageVersion Include="MQTTnet" Version="5.0.1.1416"/>
7475
<PackageVersion Include="MyCouch" Version="7.6.0"/>
7576
<PackageVersion Include="MySqlConnector" Version="2.2.5"/>
7677
<PackageVersion Include="NATS.Client" Version="1.0.8"/>

Testcontainers.dic

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ lipsum
1717
ltsc
1818
memopt
1919
mongosh
20+
mosquitto
2021
mycounter
2122
mydatabase
2223
myregistry

Testcontainers.sln

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Minio", "src
8888
EndProject
8989
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.MongoDb", "src\Testcontainers.MongoDb\Testcontainers.MongoDb.csproj", "{2613F146-6C66-4059-9D37-D48BA6B61515}"
9090
EndProject
91+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Mosquitto", "src\Testcontainers.Mosquitto\Testcontainers.Mosquitto.csproj", "{3A64B210-645C-4229-B089-5BB2AAFCF535}"
92+
EndProject
9193
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.MsSql", "src\Testcontainers.MsSql\Testcontainers.MsSql.csproj", "{121FB123-40D9-44D4-9AB7-AD57ED34F466}"
9294
EndProject
9395
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.MySql", "src\Testcontainers.MySql\Testcontainers.MySql.csproj", "{9FDCFAEA-AE42-4C69-89EF-F1FF75E88CCC}"
@@ -210,6 +212,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Minio.Tests"
210212
EndProject
211213
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.MongoDb.Tests", "tests\Testcontainers.MongoDb.Tests\Testcontainers.MongoDb.Tests.csproj", "{82A7E7B8-3187-4CAE-845B-0BF43409B38A}"
212214
EndProject
215+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Mosquitto.Tests", "tests\Testcontainers.Mosquitto.Tests\Testcontainers.Mosquitto.Tests.csproj", "{6314B57A-EE0C-4C3B-A9A9-64D68A47312A}"
216+
EndProject
213217
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.MsSql.Tests", "tests\Testcontainers.MsSql.Tests\Testcontainers.MsSql.Tests.csproj", "{25DBED78-99F4-433F-BBF5-1B4E9DEAE437}"
214218
EndProject
215219
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.MySql.Tests", "tests\Testcontainers.MySql.Tests\Testcontainers.MySql.Tests.csproj", "{E42DA1CE-698F-4E45-8D1F-5D5895893840}"
@@ -418,6 +422,10 @@ Global
418422
{2613F146-6C66-4059-9D37-D48BA6B61515}.Debug|Any CPU.Build.0 = Debug|Any CPU
419423
{2613F146-6C66-4059-9D37-D48BA6B61515}.Release|Any CPU.ActiveCfg = Release|Any CPU
420424
{2613F146-6C66-4059-9D37-D48BA6B61515}.Release|Any CPU.Build.0 = Release|Any CPU
425+
{3A64B210-645C-4229-B089-5BB2AAFCF535}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
426+
{3A64B210-645C-4229-B089-5BB2AAFCF535}.Debug|Any CPU.Build.0 = Debug|Any CPU
427+
{3A64B210-645C-4229-B089-5BB2AAFCF535}.Release|Any CPU.ActiveCfg = Release|Any CPU
428+
{3A64B210-645C-4229-B089-5BB2AAFCF535}.Release|Any CPU.Build.0 = Release|Any CPU
421429
{121FB123-40D9-44D4-9AB7-AD57ED34F466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
422430
{121FB123-40D9-44D4-9AB7-AD57ED34F466}.Debug|Any CPU.Build.0 = Debug|Any CPU
423431
{121FB123-40D9-44D4-9AB7-AD57ED34F466}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -662,6 +670,10 @@ Global
662670
{82A7E7B8-3187-4CAE-845B-0BF43409B38A}.Debug|Any CPU.Build.0 = Debug|Any CPU
663671
{82A7E7B8-3187-4CAE-845B-0BF43409B38A}.Release|Any CPU.ActiveCfg = Release|Any CPU
664672
{82A7E7B8-3187-4CAE-845B-0BF43409B38A}.Release|Any CPU.Build.0 = Release|Any CPU
673+
{6314B57A-EE0C-4C3B-A9A9-64D68A47312A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
674+
{6314B57A-EE0C-4C3B-A9A9-64D68A47312A}.Debug|Any CPU.Build.0 = Debug|Any CPU
675+
{6314B57A-EE0C-4C3B-A9A9-64D68A47312A}.Release|Any CPU.ActiveCfg = Release|Any CPU
676+
{6314B57A-EE0C-4C3B-A9A9-64D68A47312A}.Release|Any CPU.Build.0 = Release|Any CPU
665677
{25DBED78-99F4-433F-BBF5-1B4E9DEAE437}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
666678
{25DBED78-99F4-433F-BBF5-1B4E9DEAE437}.Debug|Any CPU.Build.0 = Debug|Any CPU
667679
{25DBED78-99F4-433F-BBF5-1B4E9DEAE437}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -834,6 +846,7 @@ Global
834846
{B024E315-831F-429D-92AA-44B839AC10F4} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
835847
{1266E1E6-5CEF-4161-8B45-83282455746E} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
836848
{2613F146-6C66-4059-9D37-D48BA6B61515} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
849+
{3A64B210-645C-4229-B089-5BB2AAFCF535} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
837850
{121FB123-40D9-44D4-9AB7-AD57ED34F466} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
838851
{9FDCFAEA-AE42-4C69-89EF-F1FF75E88CCC} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
839852
{BF37BEA1-0816-4326-B1E0-E82290F8FCE0} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
@@ -895,6 +908,7 @@ Global
895908
{5247DF94-32F3-4ED6-AE71-6AB4F4078E6D} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
896909
{5DB1F35F-B714-4B62-84BE-16A33084D3E1} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
897910
{82A7E7B8-3187-4CAE-845B-0BF43409B38A} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
911+
{6314B57A-EE0C-4C3B-A9A9-64D68A47312A} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
898912
{25DBED78-99F4-433F-BBF5-1B4E9DEAE437} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
899913
{E42DA1CE-698F-4E45-8D1F-5D5895893840} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
900914
{87A3F137-6DC3-4CE5-91E6-01797D076086} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}

Testcontainers.sln.DotSettings

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<s:Boolean x:Key="/Default/UserDictionary/Words/=ltsc/@EntryIndexedValue">True</s:Boolean>
2727
<s:Boolean x:Key="/Default/UserDictionary/Words/=memopt/@EntryIndexedValue">True</s:Boolean>
2828
<s:Boolean x:Key="/Default/UserDictionary/Words/=mongosh/@EntryIndexedValue">True</s:Boolean>
29+
<s:Boolean x:Key="/Default/UserDictionary/Words/=mosquitto/@EntryIndexedValue">True</s:Boolean>
2930
<s:Boolean x:Key="/Default/UserDictionary/Words/=mycounter/@EntryIndexedValue">True</s:Boolean>
3031
<s:Boolean x:Key="/Default/UserDictionary/Words/=mydatabase/@EntryIndexedValue">True</s:Boolean>
3132
<s:Boolean x:Key="/Default/UserDictionary/Words/=myregistry/@EntryIndexedValue">True</s:Boolean>

docs/modules/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ await moduleNameContainer.StartAsync();
5555
| Milvus | `milvusdb/milvus:v2.3.10` | [NuGet](https://www.nuget.org/packages/Testcontainers.Milvus) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Milvus) |
5656
| MinIO | `minio/minio:RELEASE.2023-01-31T02-24-19Z` | [NuGet](https://www.nuget.org/packages/Testcontainers.Minio) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Minio) |
5757
| MongoDB | `mongo:6.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.MongoDb) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.MongoDb) |
58+
| Mosquitto | `eclipse-mosquitto:2.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.Mosquitto) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Mosquitto) |
5859
| MySQL | `mysql:8.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.MySql) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.MySql) |
5960
| NATS | `nats:2.9` | [NuGet](https://www.nuget.org/packages/Testcontainers.Nats) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Nats) |
6061
| Neo4j | `neo4j:5.4` | [NuGet](https://www.nuget.org/packages/Testcontainers.Neo4j) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Neo4j) |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
root = true
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
namespace Testcontainers.Mosquitto;
2+
3+
/// <inheritdoc cref="ContainerBuilder{TBuilderEntity, TContainerEntity, TConfigurationEntity}" />
4+
[PublicAPI]
5+
public sealed class MosquittoBuilder : ContainerBuilder<MosquittoBuilder, MosquittoContainer, MosquittoConfiguration>
6+
{
7+
public const string MosquittoImage = "eclipse-mosquitto:2.0";
8+
9+
public const ushort MqttPort = 1883;
10+
11+
public const ushort MqttTlsPort = 8883;
12+
13+
public const ushort MqttWsPort = 8080;
14+
15+
public const ushort MqttWssPort = 8081;
16+
17+
public const string CertificateFilePath = "/etc/mosquitto/certs/server.crt";
18+
19+
public const string CertificateKeyFilePath = "/etc/mosquitto/certs/server.key";
20+
21+
/// <summary>
22+
/// Initializes a new instance of the <see cref="MosquittoBuilder" /> class.
23+
/// </summary>
24+
public MosquittoBuilder()
25+
: this(new MosquittoConfiguration())
26+
{
27+
DockerResourceConfiguration = Init().DockerResourceConfiguration;
28+
}
29+
30+
/// <summary>
31+
/// Initializes a new instance of the <see cref="MosquittoBuilder" /> class.
32+
/// </summary>
33+
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
34+
public MosquittoBuilder(MosquittoConfiguration resourceConfiguration)
35+
: base(resourceConfiguration)
36+
{
37+
DockerResourceConfiguration = resourceConfiguration;
38+
}
39+
40+
/// <inheritdoc />
41+
protected override MosquittoConfiguration DockerResourceConfiguration { get; }
42+
43+
/// <summary>
44+
/// Sets the public certificate and private key to enable TLS.
45+
/// </summary>
46+
/// <param name="certificate">The public certificate in PEM format.</param>
47+
/// <param name="certificateKey">The private key associated with the certificate in PEM format.</param>
48+
/// <returns>A configured instance of <see cref="MosquittoBuilder" />.</returns>
49+
public MosquittoBuilder WithCertificate(string certificate, string certificateKey)
50+
{
51+
return Merge(DockerResourceConfiguration, new MosquittoConfiguration(certificate: certificate, certificateKey: certificateKey))
52+
.WithPortBinding(MqttTlsPort, true)
53+
.WithPortBinding(MqttWssPort, true)
54+
.WithResourceMapping(Encoding.Default.GetBytes(certificate), CertificateFilePath)
55+
.WithResourceMapping(Encoding.Default.GetBytes(certificateKey), CertificateKeyFilePath);
56+
}
57+
58+
/// <inheritdoc />
59+
public override MosquittoContainer Build()
60+
{
61+
Validate();
62+
63+
// Maybe we should move this into the startup callback.
64+
var mosquittoConfig = new StringWriter();
65+
mosquittoConfig.NewLine = "\n";
66+
67+
mosquittoConfig.WriteLine("per_listener_settings true");
68+
mosquittoConfig.WriteLine("log_dest stdout");
69+
mosquittoConfig.WriteLine("log_type information");
70+
71+
mosquittoConfig.WriteLine();
72+
mosquittoConfig.WriteLine("persistence false");
73+
mosquittoConfig.WriteLine("persistence_location /mosquitto/data/");
74+
75+
mosquittoConfig.WriteLine();
76+
mosquittoConfig.WriteLine("# MQTT, unencrypted, unauthenticated");
77+
mosquittoConfig.WriteLine($"listener {MqttPort} 0.0.0.0");
78+
mosquittoConfig.WriteLine("protocol mqtt");
79+
mosquittoConfig.WriteLine("allow_anonymous true");
80+
81+
mosquittoConfig.WriteLine();
82+
mosquittoConfig.WriteLine("# MQTT over WebSockets, unencrypted, unauthenticated");
83+
mosquittoConfig.WriteLine($"listener {MqttWsPort} 0.0.0.0");
84+
mosquittoConfig.WriteLine("protocol websockets");
85+
mosquittoConfig.WriteLine("allow_anonymous true");
86+
87+
if (DockerResourceConfiguration.TlsEnabled)
88+
{
89+
mosquittoConfig.WriteLine();
90+
mosquittoConfig.WriteLine("# MQTT, encrypted, unauthenticated");
91+
mosquittoConfig.WriteLine($"listener {MqttTlsPort} 0.0.0.0");
92+
mosquittoConfig.WriteLine("protocol mqtt");
93+
mosquittoConfig.WriteLine("allow_anonymous true");
94+
mosquittoConfig.WriteLine("tls_version tlsv1.2");
95+
mosquittoConfig.WriteLine($"certfile {CertificateFilePath}");
96+
mosquittoConfig.WriteLine($"keyfile {CertificateKeyFilePath}");
97+
98+
mosquittoConfig.WriteLine();
99+
mosquittoConfig.WriteLine("# MQTT over WebSockets, encrypted, unauthenticated");
100+
mosquittoConfig.WriteLine($"listener {MqttWssPort} 0.0.0.0");
101+
mosquittoConfig.WriteLine("protocol websockets");
102+
mosquittoConfig.WriteLine("allow_anonymous true");
103+
mosquittoConfig.WriteLine("tls_version tlsv1.2");
104+
mosquittoConfig.WriteLine($"certfile {CertificateFilePath}");
105+
mosquittoConfig.WriteLine($"keyfile {CertificateKeyFilePath}");
106+
}
107+
108+
var mosquittoBuilder = WithResourceMapping(Encoding.Default.GetBytes(mosquittoConfig.ToString()), "/mosquitto/config/mosquitto.conf");
109+
return new MosquittoContainer(mosquittoBuilder.DockerResourceConfiguration);
110+
}
111+
112+
/// <inheritdoc />
113+
protected override MosquittoBuilder Init()
114+
{
115+
return base.Init()
116+
.WithImage(MosquittoImage)
117+
.WithPortBinding(MqttPort, true)
118+
.WithPortBinding(MqttWsPort, true)
119+
.WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged("mosquitto.*running"));
120+
}
121+
122+
/// <inheritdoc />
123+
protected override MosquittoBuilder Clone(IResourceConfiguration<CreateContainerParameters> resourceConfiguration)
124+
{
125+
return Merge(DockerResourceConfiguration, new MosquittoConfiguration(resourceConfiguration));
126+
}
127+
128+
/// <inheritdoc />
129+
protected override MosquittoBuilder Clone(IContainerConfiguration resourceConfiguration)
130+
{
131+
return Merge(DockerResourceConfiguration, new MosquittoConfiguration(resourceConfiguration));
132+
}
133+
134+
/// <inheritdoc />
135+
protected override MosquittoBuilder Merge(MosquittoConfiguration oldValue, MosquittoConfiguration newValue)
136+
{
137+
return new MosquittoBuilder(new MosquittoConfiguration(oldValue, newValue));
138+
}
139+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
namespace Testcontainers.Mosquitto;
2+
3+
/// <inheritdoc cref="ContainerConfiguration" />
4+
[PublicAPI]
5+
public sealed class MosquittoConfiguration : ContainerConfiguration
6+
{
7+
/// <summary>
8+
/// Initializes a new instance of the <see cref="MosquittoConfiguration" /> class.
9+
/// </summary>
10+
/// <param name="certificate">The public certificate in PEM format.</param>
11+
/// <param name="certificateKey">The private key associated with the certificate in PEM format.</param>
12+
public MosquittoConfiguration(
13+
string certificate = null,
14+
string certificateKey = null)
15+
{
16+
Certificate = certificate;
17+
CertificateKey = certificateKey;
18+
}
19+
20+
/// <summary>
21+
/// Initializes a new instance of the <see cref="MosquittoConfiguration" /> class.
22+
/// </summary>
23+
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
24+
public MosquittoConfiguration(IResourceConfiguration<CreateContainerParameters> resourceConfiguration)
25+
: base(resourceConfiguration)
26+
{
27+
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
28+
}
29+
30+
/// <summary>
31+
/// Initializes a new instance of the <see cref="MosquittoConfiguration" /> class.
32+
/// </summary>
33+
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
34+
public MosquittoConfiguration(IContainerConfiguration resourceConfiguration)
35+
: base(resourceConfiguration)
36+
{
37+
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
38+
}
39+
40+
/// <summary>
41+
/// Initializes a new instance of the <see cref="MosquittoConfiguration" /> class.
42+
/// </summary>
43+
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
44+
public MosquittoConfiguration(MosquittoConfiguration resourceConfiguration)
45+
: this(new MosquittoConfiguration(), resourceConfiguration)
46+
{
47+
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
48+
}
49+
50+
/// <summary>
51+
/// Initializes a new instance of the <see cref="MosquittoConfiguration" /> class.
52+
/// </summary>
53+
/// <param name="oldValue">The old Docker resource configuration.</param>
54+
/// <param name="newValue">The new Docker resource configuration.</param>
55+
public MosquittoConfiguration(MosquittoConfiguration oldValue, MosquittoConfiguration newValue)
56+
: base(oldValue, newValue)
57+
{
58+
Certificate = BuildConfiguration.Combine(oldValue.Certificate, newValue.Certificate);
59+
CertificateKey = BuildConfiguration.Combine(oldValue.CertificateKey, newValue.CertificateKey);
60+
}
61+
62+
/// <summary>
63+
/// Gets a value indicating whether TLS is enabled or not.
64+
/// </summary>
65+
public bool TlsEnabled => Certificate != null && CertificateKey != null;
66+
67+
/// <summary>
68+
/// Gets the public certificate in PEM format.
69+
/// </summary>
70+
public string Certificate { get; }
71+
72+
/// <summary>
73+
/// Gets the private key associated with the certificate in PEM format.
74+
/// </summary>
75+
public string CertificateKey { get; }
76+
}

0 commit comments

Comments
 (0)