Skip to content
Merged
6 changes: 6 additions & 0 deletions docs/modules/azure.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ In example: `Run(context.Background(), "mcr.microsoft.com/azure-storage/azurite:

When starting the Azurite container, you can pass options in a variadic way to configure it.

#### WithEnabledServices

- Since <a href="https://github.com/testcontainers/testcontainers-go/releases/tag/v0.40.0"><span class="tc-version">:material-tag: v0.40.0</span></a>

The default Azurite container entrypoint runs all three storage services: blob, queue, and table. Use this option to specify the required services for fewer exposed ports and slightly reduced container resources. E.g. `azurite.WithEnabledServices(azurite.BlobService)`.

#### WithInMemoryPersistence

- Since <a href="https://github.com/testcontainers/testcontainers-go/releases/tag/v0.36.0"><span class="tc-version">:material-tag: v0.36.0</span></a>
Expand Down
79 changes: 45 additions & 34 deletions modules/azure/azurite/azurite.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,74 +33,85 @@ type Container struct {
opts options
}

// Deprecated: Use [azure.BlobServiceURL], [azure.QueueServiceURL], or [azure.TableServiceURL] methods instead.
// ServiceURL returns the URL of the given service
func (c *Container) ServiceURL(ctx context.Context, srv Service) (string, error) {
return c.serviceURL(ctx, srv)
port, err := servicePort(srv)
if err != nil {
return "", err
}

return c.PortEndpoint(ctx, port, "http")
}

// BlobServiceURL returns the URL of the Blob service
func (c *Container) BlobServiceURL(ctx context.Context) (string, error) {
return c.serviceURL(ctx, blobService)
return c.ServiceURL(ctx, BlobService)
}

// QueueServiceURL returns the URL of the Queue service
func (c *Container) QueueServiceURL(ctx context.Context) (string, error) {
return c.serviceURL(ctx, queueService)
return c.ServiceURL(ctx, QueueService)
}

// TableServiceURL returns the URL of the Table service
func (c *Container) TableServiceURL(ctx context.Context) (string, error) {
return c.serviceURL(ctx, tableService)
return c.ServiceURL(ctx, TableService)
}

func (c *Container) serviceURL(ctx context.Context, srv service) (string, error) {
var port nat.Port
func servicePort(srv Service) (nat.Port, error) {
switch srv {
case blobService:
port = BlobPort
case queueService:
port = QueuePort
case tableService:
port = TablePort
case BlobService:
return BlobPort, nil
case QueueService:
return QueuePort, nil
case TableService:
return TablePort, nil
default:
return "", fmt.Errorf("unknown service: %s", srv)
}

return c.PortEndpoint(ctx, port, "http")
}

// Run creates an instance of the Azurite container type
func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustomizer) (*Container, error) {
moduleCmd := []string{}

moduleOpts := []testcontainers.ContainerCustomizer{
testcontainers.WithEntrypoint("azurite"),
testcontainers.WithExposedPorts(BlobPort, QueuePort, TablePort),
}

// 1. Gather all config options (defaults and then apply provided options)
settings := defaultOptions()
for _, opt := range opts {
if o, ok := opt.(Option); ok {
if err := o(&settings); err != nil {
return nil, fmt.Errorf("azurite option: %w", err)
}
}
}

entrypoint := "azurite"
if len(settings.EnabledServices) == 1 && settings.EnabledServices[0] != TableService {
// Use azurite-table in future once it matures. Graceful shutdown is currently very slow.
entrypoint = fmt.Sprintf("%s-%s", entrypoint, settings.EnabledServices[0])
}
moduleOpts := []testcontainers.ContainerCustomizer{testcontainers.WithEntrypoint(entrypoint)}

// 2. evaluate the enabled services to apply the right wait strategy and Cmd options
if len(settings.EnabledServices) > 0 {
cmd := make([]string, 0, len(settings.EnabledServices))
exposedPorts := make([]string, 0, len(settings.EnabledServices))
waitingFor := make([]wait.Strategy, 0, len(settings.EnabledServices))

for _, srv := range settings.EnabledServices {
switch srv {
case BlobService:
moduleCmd = append(moduleCmd, "--blobHost", "0.0.0.0")
waitingFor = append(waitingFor, wait.ForListeningPort(BlobPort))
case QueueService:
moduleCmd = append(moduleCmd, "--queueHost", "0.0.0.0")
waitingFor = append(waitingFor, wait.ForListeningPort(QueuePort))
case TableService:
moduleCmd = append(moduleCmd, "--tableHost", "0.0.0.0")
waitingFor = append(waitingFor, wait.ForListeningPort(TablePort))
port, err := servicePort(srv)
if err != nil {
return nil, err
}

cmd = append(cmd, fmt.Sprintf("--%sHost", srv), "0.0.0.0", fmt.Sprintf("--%sPort", srv), port.Port())
exposedPorts = append(exposedPorts, string(port))
waitingFor = append(waitingFor, wait.ForListeningPort(port))
}

moduleOpts = append(moduleOpts, testcontainers.WithCmd(moduleCmd...))
moduleOpts = append(moduleOpts, testcontainers.WithWaitStrategy(waitingFor...))
moduleOpts = append(moduleOpts,
testcontainers.WithCmd(cmd...),
testcontainers.WithExposedPorts(exposedPorts...),
testcontainers.WithWaitStrategy(waitingFor...),
)
}

moduleOpts = append(moduleOpts, opts...)
Expand Down
26 changes: 26 additions & 0 deletions modules/azure/azurite/azurite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,32 @@ func TestAzurite_inMemoryPersistence(t *testing.T) {
})
}

func TestAzurite_enabledServices(t *testing.T) {
ctx := context.Background()

services := []azurite.Service{azurite.BlobService, azurite.QueueService, azurite.TableService, "invalid"}
for _, service := range services {
t.Run(string(service), func(t *testing.T) {
ctr, err := azurite.Run(ctx, "mcr.microsoft.com/azure-storage/azurite:3.33.0", azurite.WithInMemoryPersistence(0), azurite.WithEnabledServices(service))
testcontainers.CleanupContainer(t, ctr)
if service == "invalid" {
require.Error(t, err)
return
}
require.NoError(t, err)

for _, srv := range services {
_, err = ctr.ServiceURL(ctx, srv)
if srv == service {
require.NoError(t, err)
} else {
require.Error(t, err)
}
}
})
}
}

func TestAzurite_serviceURL(t *testing.T) {
ctx := context.Background()

Expand Down
3 changes: 3 additions & 0 deletions modules/azure/azurite/examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ func ExampleRun_blobOperations() {
ctx,
"mcr.microsoft.com/azure-storage/azurite:3.33.0",
azurite.WithInMemoryPersistence(64),
azurite.WithEnabledServices(azurite.BlobService),
)
defer func() {
if err := testcontainers.TerminateContainer(azuriteContainer); err != nil {
Expand Down Expand Up @@ -198,6 +199,7 @@ func ExampleRun_queueOperations() {
ctx,
"mcr.microsoft.com/azure-storage/azurite:3.28.0",
azurite.WithInMemoryPersistence(64),
azurite.WithEnabledServices(azurite.QueueService),
)
defer func() {
if err := testcontainers.TerminateContainer(azuriteContainer); err != nil {
Expand Down Expand Up @@ -292,6 +294,7 @@ func ExampleRun_tableOperations() {
ctx,
"mcr.microsoft.com/azure-storage/azurite:3.28.0",
azurite.WithInMemoryPersistence(64),
azurite.WithEnabledServices(azurite.TableService),
)
defer func() {
if err := testcontainers.TerminateContainer(azuriteContainer); err != nil {
Expand Down
43 changes: 41 additions & 2 deletions modules/azure/azurite/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,51 @@ import (

type options struct {
// EnabledServices is a list of services that should be enabled
EnabledServices []service
EnabledServices []Service
}

func defaultOptions() options {
return options{
EnabledServices: []service{blobService, queueService, tableService},
EnabledServices: []Service{BlobService, QueueService, TableService},
}
}

// Satisfy the testcontainers.ContainerCustomizer interface
var _ testcontainers.ContainerCustomizer = (Option)(nil)

// Option is an option for the Azurite container.
type Option func(*options) error

// Customize is a NOOP. It's defined to satisfy the testcontainers.ContainerCustomizer interface.
func (o Option) Customize(*testcontainers.GenericContainerRequest) error {
// NOOP to satisfy interface.
return nil
}

// WithEnabledServices is a custom option to specify which services should be enabled.
func WithEnabledServices(services ...Service) Option {
return func(o *options) error {
if len(services) == 0 {
services = []Service{BlobService, QueueService, TableService}
} else {
seen := make(map[Service]bool, len(services))
for _, srv := range services {
if seen[srv] {
return fmt.Errorf("duplicate service: %s", srv)
}
seen[srv] = true

switch srv {
case BlobService, QueueService, TableService:
// valid service, continue
default:
return fmt.Errorf("unknown service: %s", srv)
}
}
}

o.EnabledServices = services
return nil
}
}

Expand Down
24 changes: 4 additions & 20 deletions modules/azure/azurite/services.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,15 @@
package azurite

const (
// blobService is the service name for the Blob service
blobService Service = "blob"

// Deprecated: this constant is kept for backward compatibility, but it'll be removed in the next major version.
// BlobService is the service name for the Blob service
BlobService Service = blobService

// queueService is the service name for the Queue service
queueService Service = "queue"
BlobService Service = "blob"

// Deprecated: this constant is kept for backward compatibility, but it'll be removed in the next major version.
// QueueService is the service name for the Queue service
QueueService Service = queueService
QueueService Service = "queue"

// tableService is the service name for the Table service
tableService Service = "table"

// Deprecated: this constant is kept for backward compatibility, but it'll be removed in the next major version.
// TableService is the service name for the Table service
TableService Service = tableService
TableService Service = "table"
)

// Deprecated: this type is kept for backward compatibility, but it'll be removed in the next major version.
// Service is the type for the services that Azurite can provide
type Service = service

// service is the type for the services that Azurite can provide
type service string
type Service string
2 changes: 1 addition & 1 deletion modules/azure/eventhubs/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func defaultOptions() options {
}
}

// Satisfy the testcontainers.CustomizeRequestOption interface
// Satisfy the testcontainers.ContainerCustomizer interface
var _ testcontainers.ContainerCustomizer = (Option)(nil)

// Option is an option for the EventHubs container.
Expand Down
2 changes: 1 addition & 1 deletion modules/azure/servicebus/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func defaultOptions() options {
}
}

// Satisfy the testcontainers.CustomizeRequestOption interface
// Satisfy the testcontainers.ContainerCustomizer interface
var _ testcontainers.ContainerCustomizer = (Option)(nil)

// Option is an option for the ServiceBus container.
Expand Down
Loading