-
-
Notifications
You must be signed in to change notification settings - Fork 307
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
feat: Add Azure EventHubs module #1183
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
root = true |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
namespace Testcontainers.EventHubs.Configuration | ||
{ | ||
public class ConfigurationBuilder | ||
{ | ||
private const string DefaultNamespace = "emulatorns1"; | ||
|
||
private readonly RootConfiguration _rootConfiguration = new RootConfiguration(); | ||
|
||
private ConfigurationBuilder() | ||
{ | ||
_rootConfiguration.UserConfig = new UserConfig | ||
{ | ||
NamespaceConfig = new List<NamespaceConfig>() | ||
{ | ||
new NamespaceConfig | ||
{ | ||
Type = "EventHub", | ||
Name = DefaultNamespace | ||
} | ||
}, | ||
LoggingConfig = new LoggingConfig() { Type = "File" } | ||
}; | ||
} | ||
|
||
public static ConfigurationBuilder Create() | ||
{ | ||
return new ConfigurationBuilder(); | ||
} | ||
|
||
public ConfigurationBuilder WithEventHub(string entityName, string partitionCount, IEnumerable<string> consumerGroups) | ||
{ | ||
var namespaceConfig = _rootConfiguration.UserConfig.NamespaceConfig.FirstOrDefault(x => x.Name == DefaultNamespace); | ||
if (namespaceConfig == null) | ||
{ | ||
throw new InvalidOperationException($"Default Namespace '{DefaultNamespace}' not found."); | ||
} | ||
|
||
namespaceConfig.Entities.Add(new Entity | ||
{ | ||
Name = entityName, | ||
PartitionCount = partitionCount, | ||
ConsumerGroups = consumerGroups.Select(consumerGroupName => new ConsumerGroup { Name = consumerGroupName }).ToList() | ||
}); | ||
|
||
return this; | ||
} | ||
|
||
public string Build() | ||
{ | ||
return JsonSerializer.Serialize(_rootConfiguration); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace Testcontainers.EventHubs.Configuration | ||
{ | ||
public record ConsumerGroup | ||
{ | ||
public string Name { get; set; } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
namespace Testcontainers.EventHubs.Configuration | ||
{ | ||
public record Entity | ||
{ | ||
public string Name { get; set; } | ||
public string PartitionCount { get; set; } | ||
public List<ConsumerGroup> ConsumerGroups { get; set; } = []; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace Testcontainers.EventHubs.Configuration | ||
{ | ||
public record LoggingConfig | ||
{ | ||
public string Type { get; set; } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
namespace Testcontainers.EventHubs.Configuration | ||
{ | ||
public record NamespaceConfig | ||
{ | ||
public string Type { get; set; } | ||
public string Name { get; set; } | ||
|
||
public List<Entity> Entities { get; set; } = []; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace Testcontainers.EventHubs.Configuration | ||
{ | ||
public record RootConfiguration | ||
{ | ||
public UserConfig UserConfig { get; set; } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
namespace Testcontainers.EventHubs.Configuration | ||
{ | ||
public record UserConfig | ||
{ | ||
public List<NamespaceConfig> NamespaceConfig { get; set; } = []; | ||
public LoggingConfig LoggingConfig { get; set; } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
using DotNet.Testcontainers; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use the global usings file. |
||
|
||
namespace Testcontainers.EventHubs; | ||
|
||
/// <inheritdoc cref="ContainerBuilder{TBuilderEntity, TContainerEntity, TConfigurationEntity}" /> | ||
[PublicAPI] | ||
public sealed class EventHubsBuilder : ContainerBuilder<EventHubsBuilder, EventHubsContainer, EventHubsConfiguration> | ||
{ | ||
public const string EventHubsImage = "mcr.microsoft.com/azure-messaging/eventhubs-emulator:latest"; | ||
|
||
public const ushort EventHubsPort = 5672; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="EventHubsBuilder" /> class. | ||
/// </summary> | ||
public EventHubsBuilder() | ||
: this(new EventHubsConfiguration()) | ||
{ | ||
DockerResourceConfiguration = Init().DockerResourceConfiguration; | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="EventHubsBuilder" /> class. | ||
/// </summary> | ||
/// <param name="resourceConfiguration">The Docker resource configuration.</param> | ||
private EventHubsBuilder(EventHubsConfiguration resourceConfiguration) | ||
: base(resourceConfiguration) | ||
{ | ||
DockerResourceConfiguration = resourceConfiguration; | ||
} | ||
|
||
/// <inheritdoc /> | ||
protected override EventHubsConfiguration DockerResourceConfiguration { get; } | ||
|
||
/// <summary> | ||
/// Sets the event hub configuration | ||
/// </summary> | ||
/// <param name="configurationBuilder"></param> | ||
/// <returns></returns> | ||
public EventHubsBuilder WithConfigurationBuilder(ConfigurationBuilder configurationBuilder) | ||
{ | ||
var configBytes = Encoding.UTF8.GetBytes(configurationBuilder.Build()); | ||
|
||
return Merge(DockerResourceConfiguration, new EventHubsConfiguration(configurationBuilder: configurationBuilder)) | ||
.WithResourceMapping(configBytes, "Eventhubs_Emulator/ConfigFiles/Config.json"); | ||
} | ||
Comment on lines
+40
to
+46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this builder cover all Event Hubs configuration? Are we responsible for it? Is there a default configuration? The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With configuration builder is needed, because we don't have a default eventhub assigned to the default namespace. We can eventually introduce something, but I don't advise to do so as usually we want to have something custom. For an example, we have azure function: public static class ProcessSalesData
{
[FunctionName("ProcessSalesData")]
public static void Run(
[EventHubTrigger("SalesDataHub", Connection = "EventHubConnectionAppSetting", ConsumerGroup = "RealTimeAnalytics")] string[] events,
ILogger log)
{
foreach (var eventData in events)
{
log.LogInformation($"C# Event Hub trigger function processed an event: {eventData}");
}
}
} Our eventhub is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On a second thought we can introduce |
||
|
||
/// <summary> | ||
/// Sets the endpoint of the azurite blob service | ||
/// </summary> | ||
/// <param name="azuriteBlobEndpoint"></param> | ||
/// <returns></returns> | ||
public EventHubsBuilder WithAzuriteBlobEndpoint(string azuriteBlobEndpoint) | ||
{ | ||
return Merge(DockerResourceConfiguration, new EventHubsConfiguration(azuriteBlobEndpoint: azuriteBlobEndpoint)) | ||
.WithEnvironment("BLOB_SERVER", azuriteBlobEndpoint); | ||
} | ||
|
||
/// <summary> | ||
/// Sets the endpoint of the azurite table service | ||
/// </summary> | ||
/// <param name="azuriteTableEndpoint"></param> | ||
/// <returns></returns> | ||
public EventHubsBuilder WithAzuriteTableEndpoint(string azuriteTableEndpoint) | ||
{ | ||
return Merge(DockerResourceConfiguration, new EventHubsConfiguration(azuriteTableEndpoint: azuriteTableEndpoint)) | ||
.WithEnvironment("METADATA_SERVER", azuriteTableEndpoint); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public override EventHubsContainer Build() | ||
{ | ||
Validate(); | ||
|
||
var waitStrategy = Wait.ForUnixContainer().UntilMessageIsLogged("Emulator Service is Successfully Up!"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The wait strategy should be moved to |
||
|
||
var eventHubsBuilder = DockerResourceConfiguration.WaitStrategies.Count() > 1 ? this : WithWaitStrategy(waitStrategy); | ||
return new EventHubsContainer(eventHubsBuilder.DockerResourceConfiguration); | ||
} | ||
|
||
/// <inheritdoc /> | ||
protected override void Validate() | ||
{ | ||
base.Validate(); | ||
|
||
_ = Guard.Argument(DockerResourceConfiguration.ConfigurationBuilder, | ||
nameof(DockerResourceConfiguration.ConfigurationBuilder)) | ||
.NotNull(); | ||
|
||
_ = Guard.Argument(DockerResourceConfiguration.AzuriteBlobEndpoint, | ||
nameof(DockerResourceConfiguration.AzuriteBlobEndpoint)) | ||
.NotNull() | ||
.NotEmpty(); | ||
|
||
_ = Guard.Argument(DockerResourceConfiguration.AzuriteTableEndpoint, | ||
nameof(DockerResourceConfiguration.AzuriteTableEndpoint)) | ||
.NotNull() | ||
.NotEmpty(); | ||
} | ||
|
||
/// <inheritdoc /> | ||
protected override EventHubsBuilder Init() | ||
{ | ||
return base.Init() | ||
.WithImage(EventHubsImage) | ||
.WithEnvironment("ACCEPT_EULA", "Y") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please do not hide license agreements. Right now, we use the following pattern. |
||
.WithPortBinding(EventHubsPort, true); | ||
} | ||
|
||
/// <inheritdoc /> | ||
protected override EventHubsBuilder Clone(IResourceConfiguration<CreateContainerParameters> resourceConfiguration) | ||
{ | ||
return Merge(DockerResourceConfiguration, new EventHubsConfiguration(resourceConfiguration)); | ||
} | ||
|
||
/// <inheritdoc /> | ||
protected override EventHubsBuilder Clone(IContainerConfiguration resourceConfiguration) | ||
{ | ||
return Merge(DockerResourceConfiguration, new EventHubsConfiguration(resourceConfiguration)); | ||
} | ||
|
||
/// <inheritdoc /> | ||
protected override EventHubsBuilder Merge(EventHubsConfiguration oldValue, EventHubsConfiguration newValue) | ||
{ | ||
return new EventHubsBuilder(new EventHubsConfiguration(oldValue, newValue)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
namespace Testcontainers.EventHubs; | ||
|
||
/// <inheritdoc cref="ContainerConfiguration" /> | ||
[PublicAPI] | ||
public sealed class EventHubsConfiguration : ContainerConfiguration | ||
{ | ||
/// <summary> | ||
/// Initializes a new instance of the <see cref="EventHubsConfiguration" /> class. | ||
/// </summary> | ||
/// <param name="configurationBuilder">The configuration builder.</param> | ||
/// <param name="azuriteBlobEndpoint">The Azurite blob endpoint.</param> | ||
/// <param name="azuriteTableEndpoint">The Azurite table endpoint.</param> | ||
public EventHubsConfiguration( | ||
ConfigurationBuilder configurationBuilder = null, | ||
string azuriteBlobEndpoint = null, | ||
string azuriteTableEndpoint = null) | ||
{ | ||
ConfigurationBuilder = configurationBuilder; | ||
AzuriteBlobEndpoint = azuriteBlobEndpoint; | ||
AzuriteTableEndpoint = azuriteTableEndpoint; | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="EventHubsConfiguration" /> class. | ||
/// </summary> | ||
/// <param name="resourceConfiguration">The Docker resource configuration.</param> | ||
public EventHubsConfiguration(IResourceConfiguration<CreateContainerParameters> resourceConfiguration) | ||
: base(resourceConfiguration) | ||
{ | ||
// Passes the configuration upwards to the base implementations to create an updated immutable copy. | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="EventHubsConfiguration" /> class. | ||
/// </summary> | ||
/// <param name="resourceConfiguration">The Docker resource configuration.</param> | ||
public EventHubsConfiguration(IContainerConfiguration resourceConfiguration) | ||
: base(resourceConfiguration) | ||
{ | ||
// Passes the configuration upwards to the base implementations to create an updated immutable copy. | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="EventHubsConfiguration" /> class. | ||
/// </summary> | ||
/// <param name="resourceConfiguration">The Docker resource configuration.</param> | ||
public EventHubsConfiguration(EventHubsConfiguration resourceConfiguration) | ||
: this(new EventHubsConfiguration(), resourceConfiguration) | ||
{ | ||
// Passes the configuration upwards to the base implementations to create an updated immutable copy. | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="EventHubsConfiguration" /> class. | ||
/// </summary> | ||
/// <param name="oldValue">The old Docker resource configuration.</param> | ||
/// <param name="newValue">The new Docker resource configuration.</param> | ||
public EventHubsConfiguration(EventHubsConfiguration oldValue, EventHubsConfiguration newValue) | ||
: base(oldValue, newValue) | ||
{ | ||
ConfigurationBuilder = BuildConfiguration.Combine(oldValue.ConfigurationBuilder, newValue.ConfigurationBuilder); | ||
AzuriteBlobEndpoint = BuildConfiguration.Combine(oldValue.AzuriteBlobEndpoint, newValue.AzuriteBlobEndpoint); | ||
AzuriteTableEndpoint = BuildConfiguration.Combine(oldValue.AzuriteTableEndpoint, newValue.AzuriteTableEndpoint); | ||
} | ||
|
||
/// <summary> | ||
/// Gets the configuration builder | ||
/// </summary> | ||
public ConfigurationBuilder ConfigurationBuilder { get; } | ||
Comment on lines
+66
to
+69
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like we do not use the |
||
|
||
/// <summary> | ||
/// Gets the Azurite blob endpoint | ||
/// </summary> | ||
public string AzuriteBlobEndpoint { get; } | ||
|
||
/// <summary> | ||
/// Gets the Azurite table endpoint | ||
/// </summary> | ||
public string AzuriteTableEndpoint { get; } | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please change all to file scoped namespaces.