Skip to content

[Feature]: improve developer experience with azure/eventhubs #3618

@thecoldwine

Description

@thecoldwine

Problem

Currently, there is no feature parity between options provided by golang azure/eventhubs and C# azure/eventhubs modules and I personally do find dev experience in go a bit lacking. Example:

void Setup()
{
    var network = new NetworkBuilder().Build();
    
    AzuriteInstance = new AzuriteBuilder("mcr.microsoft.com/azure-storage/azurite:3.34.0")
        .WithNetwork(network)
        .WithNetworkAliases("azurite")
        .Build();

    EventHubsInstance = new EventHubsBuilder("mcr.microsoft.com/azure-messaging/eventhubs-emulator:latest")
        .WithAcceptLicenseAgreement(true)
        .WithAzuriteContainer(network, AzuriteInstance, "azurite")
        .WithConfigurationBuilder(
            EventHubsServiceConfiguration.Create()
                .WithEntity(EventHubEntityName, 2, "$Default"))
        .WithWaitStrategy(Wait.ForUnixContainer()
            .UntilMessageIsLogged("Emulator Service is Successfully Up!"))
        .Build();
}

Solution

In go we're missing a couple of things here:

  1. Statically typed configurations (fluent api or building a structure with merge as an alternative to plain reader configuration).
  2. Way to provide Azurite container itself, not just an image (in case I would like to reset checkpoints within image and to have 1 less container to spin up).

As far as I see all necessary infrastructure is already in place in https://github.com/testcontainers/testcontainers-go/blob/main/modules/azure/eventhubs/options.go

It is especially useful if one needs azurite with non-standard options, i.e.: testcontainers.WithEntrypointArgs("--skipApiVersionCheck")

So, it would be nice to have something in line with:

func WithAzuriteContainer(
    azuriteContainer *azurite.Container, 
    network          *testcontainers.DockerNetwork,
    opts ...testcontainers.ContainerCustomizer,
) Option {
	return func(o *options) error {
    	o.azuriteOptions = opts
    	o.azuriteContainer = azuriteContainer
    	o.network = network
    }
}

For statically typed configuration it is a bit trickier, but it would be nice to just supply a struct which is afterwards marshalled as JSON and then put into the configuration, so:

cfg := NewConfigBuilder().
	WithLoggingType("File").
	AddNamespace("emulatorNs1", "EventHub").
		AddEntity("eh1", "2").
			AddConsumerGroup("cg1").
			Done(). // back to NamespaceBuilder
		Done(). // back to ConfigBuilder
	Build()

and then supply it to options via:

func WithConfigObject(cfg *eventhubConfig) testcontainers.CustomizeRequestOption {
	return func(req *testcontainers.GenericContainerRequest) error {
                 // marshal JSON to configuration path here

		req.Files = append(req.Files, testcontainers.ContainerFile{
			Reader:            r,
			ContainerFilePath: containerConfigFile,
			FileMode:          0o644,
		})

		return nil
	}
}

Benefit

It makes dev ux a bit less frustrating and more consistent to what is already there in testcontainers for other languages

Alternatives

Generate helper functions in every codebase or publish as a separate library, but it is much more incovenient. Another option is do templating:

const ConfigTemplate = `
{
  "UserConfig": {
    "NamespaceConfig": [
      {
        "Type": "EventHub",
        "Name": "%s",
        "Entities": [
          {
            "Name": "%s",
            "PartitionCount": "%d",
            "ConsumerGroups": [{ "Name": "%s" }]
          }
        ]
      }
    ],
    "LoggingConfig": { "Type": "File" }
  }
}
`
const EventHubNamespace = "ehNamespace"
const EventHubName = "eh1"
const PartitionCount = 2
const ConsumerGroup = "default"

func renderConfiguration() io.Reader {
	renderedConfig := fmt.Sprintf(
		ConfigTemplate,
		EventHubNamespace,
		EventHubName,
		PartitionCount,
		ConsumerGroup,
	)

	return strings.NewReader(renderedConfig)
}

// then use it like this:
...

	eventhubsContainer, err := eventhubs.Run(
		context.Background(),
		"mcr.microsoft.com/azure-messaging/eventhubs-emulator:latest",
		eventhubs.WithAcceptEULA(),
		eventhubs.WithAzurite("mcr.microsoft.com/azure-storage/azurite:3.34.0"),
		eventhubs.WithConfig(renderConfiguration()),
	)
...

Would you like to help contributing this feature?

Yes

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureNew functionality or new behaviors on the existing one

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions