1+ namespace Testcontainers . EventHubs ;
2+
3+ /// <inheritdoc cref="ContainerBuilder{TBuilderEntity, TContainerEntity, TConfigurationEntity}" />
4+ [ PublicAPI ]
5+ public sealed class EventHubsBuilder : ContainerBuilder < EventHubsBuilder , EventHubsContainer , EventHubsConfiguration >
6+ {
7+ public const string EventHubsNetworkAlias = "eventhubs-container" ;
8+
9+ public const string AzuriteNetworkAlias = "azurite-container" ;
10+
11+ public const string EventHubsImage = "mcr.microsoft.com/azure-messaging/eventhubs-emulator:latest" ;
12+
13+ public const ushort EventHubsPort = 5672 ;
14+
15+ /// <summary>
16+ /// Initializes a new instance of the <see cref="EventHubsBuilder" /> class.
17+ /// </summary>
18+ public EventHubsBuilder ( )
19+ : this ( new EventHubsConfiguration ( ) )
20+ {
21+ DockerResourceConfiguration = Init ( ) . DockerResourceConfiguration ;
22+ }
23+
24+ /// <summary>
25+ /// Initializes a new instance of the <see cref="EventHubsBuilder" /> class.
26+ /// </summary>
27+ /// <param name="resourceConfiguration">The Docker resource configuration.</param>
28+ private EventHubsBuilder ( EventHubsConfiguration resourceConfiguration )
29+ : base ( resourceConfiguration )
30+ {
31+ DockerResourceConfiguration = resourceConfiguration ;
32+ }
33+
34+ /// <inheritdoc />
35+ protected override EventHubsConfiguration DockerResourceConfiguration { get ; }
36+
37+ /// <inheritdoc />
38+ protected override string AcceptLicenseAgreementEnvVar { get ; } = "ACCEPT_EULA" ;
39+
40+ /// <inheritdoc />
41+ protected override string AcceptLicenseAgreement { get ; } = "Y" ;
42+
43+ /// <inheritdoc />
44+ protected override string DeclineLicenseAgreement { get ; } = "N" ;
45+
46+ /// <summary>
47+ /// Accepts the license agreement.
48+ /// </summary>
49+ /// <remarks>
50+ /// When <paramref name="acceptLicenseAgreement" /> is set to <c>true</c>, the Azure Event Hubs Emulator <see href="https://github.com/Azure/azure-event-hubs-emulator-installer/blob/main/EMULATOR_EULA.md">license</see> is accepted.
51+ /// </remarks>
52+ /// <param name="acceptLicenseAgreement">A boolean value indicating whether the Azure Event Hubs Emulator license agreement is accepted.</param>
53+ /// <returns>A configured instance of <see cref="EventHubsBuilder" />.</returns>
54+ public override EventHubsBuilder WithAcceptLicenseAgreement ( bool acceptLicenseAgreement )
55+ {
56+ var licenseAgreement = acceptLicenseAgreement ? AcceptLicenseAgreement : DeclineLicenseAgreement ;
57+ return WithEnvironment ( AcceptLicenseAgreementEnvVar , licenseAgreement ) ;
58+ }
59+
60+ /// <summary>
61+ /// Sets the dependent Azurite container for the Azure Event Hubs Emulator.
62+ /// </summary>
63+ /// <remarks>
64+ /// This method allows an existing Azurite container to be attached to the Azure Event
65+ /// Hubs Emulator. The containers must be on the same network to enable communication
66+ /// between them.
67+ /// </remarks>
68+ /// <param name="network">The network to connect the container to.</param>
69+ /// <param name="container">The Azurite container.</param>
70+ /// <param name="networkAlias">The Azurite container network alias.</param>
71+ /// <returns>A configured instance of <see cref="EventHubsBuilder" />.</returns>
72+ public EventHubsBuilder WithAzuriteContainer (
73+ INetwork network ,
74+ AzuriteContainer container ,
75+ string networkAlias )
76+ {
77+ return Merge ( DockerResourceConfiguration , new EventHubsConfiguration ( azuriteContainer : container ) )
78+ . DependsOn ( container )
79+ . WithNetwork ( network )
80+ . WithNetworkAliases ( EventHubsNetworkAlias )
81+ . WithEnvironment ( "BLOB_SERVER" , networkAlias )
82+ . WithEnvironment ( "METADATA_SERVER" , networkAlias ) ;
83+ }
84+
85+ /// <summary>
86+ /// Sets the Azure Event Hubs Emulator configuration.
87+ /// </summary>
88+ /// <param name="serviceConfiguration">The service configuration.</param>
89+ /// <returns>A configured instance of <see cref="EventHubsBuilder" />.</returns>
90+ public EventHubsBuilder WithConfigurationBuilder ( EventHubsServiceConfiguration serviceConfiguration )
91+ {
92+ var resourceContent = Encoding . Default . GetBytes ( serviceConfiguration . Build ( ) ) ;
93+ return Merge ( DockerResourceConfiguration , new EventHubsConfiguration ( serviceConfiguration : serviceConfiguration ) )
94+ . WithResourceMapping ( resourceContent , "Eventhubs_Emulator/ConfigFiles/Config.json" ) ;
95+ }
96+
97+ /// <inheritdoc />
98+ public override EventHubsContainer Build ( )
99+ {
100+ Validate ( ) ;
101+ ValidateLicenseAgreement ( ) ;
102+
103+ if ( DockerResourceConfiguration . AzuriteContainer != null )
104+ {
105+ return new EventHubsContainer ( DockerResourceConfiguration ) ;
106+ }
107+
108+ // If the user has not provided an existing Azurite container instance,
109+ // we configure one.
110+ var network = new NetworkBuilder ( )
111+ . Build ( ) ;
112+
113+ var container = new AzuriteBuilder ( )
114+ . WithNetwork ( network )
115+ . WithNetworkAliases ( AzuriteNetworkAlias )
116+ . Build ( ) ;
117+
118+ var eventHubsContainer = WithAzuriteContainer ( network , container , AzuriteNetworkAlias ) ;
119+ return new EventHubsContainer ( eventHubsContainer . DockerResourceConfiguration ) ;
120+ }
121+
122+ /// <inheritdoc />
123+ protected override void Validate ( )
124+ {
125+ base . Validate ( ) ;
126+
127+ _ = Guard . Argument ( DockerResourceConfiguration . ServiceConfiguration , nameof ( DockerResourceConfiguration . ServiceConfiguration ) )
128+ . NotNull ( )
129+ . ThrowIf ( argument => ! argument . Value . Validate ( ) , _ => throw new ArgumentException ( "The service configuration of the Azure Event Hubs Emulator is invalid." ) ) ;
130+ }
131+
132+ /// <inheritdoc />
133+ protected override EventHubsBuilder Init ( )
134+ {
135+ return base . Init ( )
136+ . WithImage ( EventHubsImage )
137+ . WithPortBinding ( EventHubsPort , true )
138+ . WithWaitStrategy ( Wait . ForUnixContainer ( )
139+ . UntilMessageIsLogged ( "Emulator Service is Successfully Up!" )
140+ . AddCustomWaitStrategy ( new WaitTwoSeconds ( ) ) ) ;
141+ }
142+
143+ /// <inheritdoc />
144+ protected override EventHubsBuilder Clone ( IResourceConfiguration < CreateContainerParameters > resourceConfiguration )
145+ {
146+ return Merge ( DockerResourceConfiguration , new EventHubsConfiguration ( resourceConfiguration ) ) ;
147+ }
148+
149+ /// <inheritdoc />
150+ protected override EventHubsBuilder Clone ( IContainerConfiguration resourceConfiguration )
151+ {
152+ return Merge ( DockerResourceConfiguration , new EventHubsConfiguration ( resourceConfiguration ) ) ;
153+ }
154+
155+ /// <inheritdoc />
156+ protected override EventHubsBuilder Merge ( EventHubsConfiguration oldValue , EventHubsConfiguration newValue )
157+ {
158+ return new EventHubsBuilder ( new EventHubsConfiguration ( oldValue , newValue ) ) ;
159+ }
160+
161+ /// <inheritdoc cref="IWaitUntil" />
162+ /// <remarks>
163+ /// This is a workaround to ensure that the wait strategy does not indicate
164+ /// readiness too early:
165+ /// https://github.com/Azure/azure-service-bus-emulator-installer/issues/35#issuecomment-2497164533.
166+ /// </remarks>
167+ private sealed class WaitTwoSeconds : IWaitUntil
168+ {
169+ /// <inheritdoc />
170+ public async Task < bool > UntilAsync ( IContainer container )
171+ {
172+ await Task . Delay ( TimeSpan . FromSeconds ( 2 ) )
173+ . ConfigureAwait ( false ) ;
174+
175+ return true ;
176+ }
177+ }
178+ }
0 commit comments