Documentation
¶
Overview ¶
Package minioenv provides MinIO environment management for testing. It offers flexible connection management with support for containers, and integration with the testenv framework.
The package provides:
- Connector type for managing MinIO connections
- Environment type for high-level MinIO environment management
- ReuseStrategyCreateNewBucket for per-Connect bucket isolation
- WithBuckets for ensuring shared buckets exist
- Integration with Docker containers via testcontainers
Example Usage:
// Simple connector
connector := minioenv.NewConnector(
minioenv.WithEndpoint("localhost:9000"),
minioenv.WithAccessKey("minioadmin"),
minioenv.WithSecretKey("minioadmin"),
)
// Connect to MinIO
client, err := env.Connect(ctx)
if err != nil {
return err
}
defer client.Close()
// Use client for object operations...
Index ¶
- type BeforeConnectHook
- type Client
- type CloseHook
- type ConnectionConfig
- type Connector
- type ConnectorOption
- func WithAccessKey(accessKey string) ConnectorOption
- func WithBeforeConnectHook(hook BeforeConnectHook) ConnectorOption
- func WithCloseHook(hook CloseHook) ConnectorOption
- func WithEndpoint(endpoint string) ConnectorOption
- func WithRegion(region string) ConnectorOption
- func WithReuseOptions(opts ...reuse.Option[*Connector]) ConnectorOption
- func WithSecretKey(secretKey string) ConnectorOption
- func WithUseSSL(useSSL bool) ConnectorOption
- type Environment
- type EnvironmentOption
- type Option
- type ReuseStrategy
- type ReuseStrategyCreateNewBucket
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type BeforeConnectHook ¶
type BeforeConnectHook func(ctx context.Context, cfg *ConnectionConfig) error
BeforeConnectHook is a function called before establishing a MinIO connection. It allows modification of the connection parameters before the *minio.Client is created. This is useful for scenarios like:
- Starting a container before the first connection
- Modifying connection parameters based on runtime conditions
- Implementing custom connection initialization logic
Parameters:
- ctx: Context for cancellation and timeout
- cfg: Connection parameters that can be modified
Returns:
- error: Non-nil to prevent the connection from being established
Example:
hook := func(ctx context.Context, cfg *minioenv.ConnectionConfig) error {
// Start container on first connection
return container.Launch(ctx, cfg)
}
type Client ¶
type Client struct {
// *minio.Client is embedded (not named) so that all minio.Client
// methods are promoted onto minioenv.Client. This lets callers
// write client.BucketExists(...), client.PutObject(...), etc.
// without an intermediate accessor.
*minio.Client
// contains filtered or unexported fields
}
Client is a wrapper around *minio.Client that owns bucket-cleanup lifecycle and the reuse Exit. It mirrors postgresenv.Pool but adapts the wrapped type to MinIO's *minio.Client.
Client instances are created by Environment.Connect and should be closed using the Close method when no longer needed. The Close method handles both bucket cleanup (if any was created by a ReuseStrategy) and connector reuse refcount decrement.
Thread Safety: Client is safe for concurrent use. The underlying *minio.Client is concurrency-safe per the minio-go docs.
Example:
cnt := containers.NewContainer()
connector := minioenv.NewConnector(cnt.ConnectorOptions()...)
env := minioenv.New(
minioenv.WithConnector(connector),
minioenv.WithReuseStrategy(&minioenv.ReuseStrategyCreateNewBucket{}),
)
client, err := env.Connect(ctx)
if err != nil {
return err
}
defer client.Close()
// Use client for object operations...
err = client.PutObject(ctx, client.Bucket(), "key", reader)
func Use ¶
Use connects to a MinIO environment and returns the client. This is a simple convenience function that calls the environment's Connect method, applies any provided options, and wraps any error with additional context.
This function is suitable for production code where you need to manage the client lifecycle manually. For testing, consider using UseForTesting which provides automatic cleanup.
Cleanup contract: Use does NOT register any cleanup. The caller is REQUIRED to call client.Close() exactly once when done. Forgetting to call Close() will leak a MinIO container. See the package-level "Resource Management" documentation for details. client.Close() is idempotent (subsequent calls return the first call's result and do not re-run the cleanup).
Parameters:
- ctx: Context for cancellation and timeout
- env: MinIO environment to connect to
- opts: Optional configuration functions to apply after connection
Returns:
- *Client: MinIO client ready to use
- error: Non-nil if connection or any option fails
Example:
// Connect to environment
client, err := Use(ctx, env)
if err != nil {
return fmt.Errorf("connect: %w", err)
}
defer client.Close()
// Use client...
err = client.PutObject(ctx, "my-bucket", "key", reader)
Example with options:
// Connect with seed object
client, err := Use(ctx, env,
func(ctx context.Context, c *minioenv.Client) error {
_, err := c.PutObject(ctx, "config", "seed.json", strings.NewReader("{}"))
return err
},
)
func UseForTesting ¶
func UseForTesting(t *testing.T, env *Environment, opts ...Option) *Client
UseForTesting connects to a MinIO environment for use in tests. This function is designed specifically for testing scenarios and provides automatic client cleanup via t.Cleanup.
Key features:
- Calls t.Helper() to improve error reporting
- Automatically closes the client when the test completes (via t.Cleanup). The client is closed even if the test panics.
- Fails the test immediately if connection fails (t.Fatal)
- Reports cleanup errors to the test log (t.Error)
- Applies any provided options after connection
Cleanup contract: this is the recommended helper for tests. The t.Cleanup registration ensures the client is closed exactly once per test, no matter what. If a test also wraps the client in its own t.Cleanup that calls client.Close(), the second Close is a no-op (idempotent). See the package-level "Resource Management" documentation for details.
Parameters:
- t: Testing context (must not be nil)
- env: MinIO environment to connect to
- opts: Optional configuration functions to apply after connection
Returns:
- *Client: MinIO client ready to use (automatically closed after test)
The function will call t.Fatal if connection fails, which immediately stops the test execution.
Example:
func TestWithMinio(t *testing.T) {
t.Parallel()
// Create environment
cnt := containers.NewContainer()
connector := minioenv.NewConnector(cnt.ConnectorOptions()...)
env := minioenv.New(
minioenv.WithConnector(connector),
minioenv.WithReuseStrategy(&minioenv.ReuseStrategyCreateNewBucket{}),
)
// Get client — automatically closed after test
client := minioenv.UseForTesting(t, env)
// Use client in test
err := client.PutObject(ctx, client.Bucket(), "key", reader)
require.NoError(t, err)
}
Note: The client is closed using t.Cleanup(), which runs after the test completes. If you need to close the client earlier, use Use instead and manage the lifecycle manually.
func (*Client) Bucket ¶
Bucket returns the name of the isolated bucket this client created via ReuseStrategyCreateNewBucket.
Returns:
- string: The bucket name. Empty if no reuse strategy was used, or if only WithBuckets was used (in which case buckets are referenced by their declared names directly).
Example:
client, _ := env.Connect(ctx) defer client.Close() // Put object into the isolated bucket err := client.PutObject(ctx, client.Bucket(), "key", reader)
func (*Client) Close ¶
Close closes the MinIO client and releases all associated resources. This method:
- Runs the bucket cleanup function (if set) to remove any bucket created by the reuse strategy
- Exits the connector reuse tracking
Order of operations: cleanup runs first, then Exit (via defer). This means that if cleanup fails, Exit still happens, so the reuse refcount is properly decremented even on cleanup failure.
Close is safe to call multiple times and from concurrent goroutines. The bucket cleanup is wrapped in a sync.Once, so subsequent calls return the error from the first call (or nil if the first cleanup succeeded) without re-running the cleanup. This protects against double-cleanup of the same bucket, which would otherwise fail with "bucket not found" on the second call.
Close should be called when the client is no longer needed. After Close is called, the client should not be used for any object operations.
Returns:
- error: Non-nil if the first bucket cleanup failed. Cleanup errors are wrapped via fmt.Errorf("cleanup buckets: %w", err). Subsequent calls return the same error (or nil if the first cleanup succeeded) without re-running the cleanup. The caller can decide whether to ignore the error.
Example:
client, err := env.Connect(ctx)
if err != nil {
return err
}
defer client.Close()
type CloseHook ¶
type CloseHook func() error
CloseHook is a function that is called when the connector is closed. It allows cleanup of resources associated with the connector. This is useful for scenarios like:
- Stopping containers when the connector is no longer needed
- Cleaning up external resources
- Releasing allocated resources
Returns:
- error: Non-nil if cleanup fails
Example:
hook := func() error {
// Stop container when connector closes
return container.Close()
}
type ConnectionConfig ¶
type ConnectionConfig struct {
// Endpoint is the address of the MinIO server in host:port form.
// Mutated by BeforeConnectHook to point at a real server.
Endpoint string
// AccessKey is the access key for authenticating to the MinIO server.
AccessKey string
// SecretKey is the secret key for authenticating to the MinIO server.
SecretKey string
// UseSSL controls whether connections to the MinIO server use TLS.
// Defaults to false (suitable for local/test environments).
UseSSL bool
// Region is the MinIO region passed to the client. Empty means
// "unspecified" (MinIO will use its server-default region, typically
// "us-east-1").
Region string
}
ConnectionConfig holds the connection parameters used to build a *minio.Client. It is passed to BeforeConnectHook so the hook can modify the endpoint (e.g. when launching a container, the hook writes the container's host:port back into Endpoint) and read the credentials to pass to the underlying service.
type Connector ¶
type Connector struct {
// contains filtered or unexported fields
}
Connector manages connections to a MinIO instance. It provides a flexible connection management system with support for:
- Lazy *minio.Client creation via BeforeConnectHook
- Hook-based lifecycle management
- Reuse-based reference counting across multiple environments
Connector is designed to work with various MinIO deployment scenarios:
- Local MinIO instances
- Docker containers
- Cloud storage (S3-compatible)
Thread Safety: Connector is safe for concurrent use. The embedded reuse instance serializes Enter/Exit operations across all goroutines.
Example:
// Create connector with container lifecycle
cnt := containers.NewContainer()
connector := NewConnector(cnt.ConnectorOptions()...)
// Use connector...
client, err := connector.Client(ctx)
if err != nil {
return err
}
// Clean up when done
defer connector.Close()
func NewConnector ¶
func NewConnector(opts ...ConnectorOption) *Connector
NewConnector creates a new MinIO connector with the specified options. If no options are provided, the connector uses defaults suitable for connecting to a local MinIO instance with the testcontainers default credentials.
Default values:
- Endpoint: "0.0.0.0:9000"
- AccessKey: "minioadmin"
- SecretKey: "minioadmin"
- UseSSL: false
- Region: "" (MinIO server default)
Options are applied in order, allowing later options to override earlier ones.
Example:
// Create connector with custom settings
connector := NewConnector(
WithEndpoint("localhost:9000"),
WithAccessKey("minioadmin"),
WithSecretKey("minioadmin"),
)
// Create connector with container lifecycle
cnt := containers.NewContainer()
connector := NewConnector(cnt.ConnectorOptions()...)
// Create connector with hooks
connector := NewConnector(
WithBeforeConnectHook(func(ctx context.Context, cfg *ConnectionConfig) error {
fmt.Println("Connecting to MinIO...")
return nil
}),
WithCloseHook(func() error {
fmt.Println("Connector closed")
return nil
}),
)
func (*Connector) Client ¶
Client builds and returns a configured *minio.Client.
Connection Process:
- Builds a ConnectionConfig from the connector's settings
- Calls beforeConnectHook (if set), which may modify the config (e.g. to set Endpoint to a running container's host:port)
- Creates a *minio.Client using the (possibly modified) config
The returned client is concurrency-safe per the minio-go docs and may be used by multiple goroutines.
Parameters:
- ctx: Context for cancellation and timeout
Returns:
- *minio.Client: Client ready to use
- error: Non-nil if connection fails
Example:
client, err := connector.Client(ctx)
if err != nil {
return err
}
defer client.Close()
// Use client for object operations...
err = client.MakeBucket(ctx, "my-bucket", minio.MakeBucketOptions{})
func (*Connector) Close ¶
Close closes the connector by invoking the closeHook. This method is called by the reuse instance's cleanup after all users have exited and the waitUntilCleanup grace period has elapsed.
After Close is called, the connector should not be used to create new clients. The BeforeConnectHook may re-launch resources on next Enter (e.g. lazy container restart on first Connect of a new lifecycle).
Close is safe to call multiple times and from concurrent goroutines. The closeHook itself is expected to be idempotent (see containers.Container.Close — it no-ops on a nil container). The first error returned by the closeHook is cached in c.closeErr and returned by every subsequent Close call, so callers can observe the result of the cleanup even if they call Close many times.
Note: this method does NOT use sync.Once. The reuse subsystem drives Close at the end of each lifecycle (after the last user exits and WaitUntilCleanup elapses), and the closeHook must run each time the lifecycle ends so that newly-launched resources (e.g., a MinIO container re-launched by BeforeConnectHook) are properly torn down. The closeHook's own idempotency prevents double-cleanup effects when the same Close is called by both the reuse and a manual caller.
Returns:
- error: Non-nil if the closeHook returned a non-nil error (the first such error is cached and returned by every subsequent Close call). The error is wrapped via fmt.Errorf("closeHook: %w", err).
Example:
cnt := containers.NewContainer() connector := NewConnector(cnt.ConnectorOptions()...) // Use connector... client, _ := connector.Client(ctx) defer client.Close() // Close is called automatically by the reuse cleanup // (or you can call it manually to force immediate cleanup)
func (*Connector) Enter ¶
Enter enters the connector's reuse instance, incrementing the user count. This is used by Environment to track connector usage across multiple environments.
When the connector is shared, Enter blocks if the reuse weight limit is reached. When the connector is reused after cleanup, the CreateFunc returns the same connector, and the BeforeConnectHook will re-launch the container on next connection.
type ConnectorOption ¶
type ConnectorOption func(*Connector)
ConnectorOption is a function that configures a Connector. Options can be passed to NewConnector to customize the connector's behavior.
func WithAccessKey ¶
func WithAccessKey(accessKey string) ConnectorOption
WithAccessKey sets the MinIO access key for authentication. The connector passes this to both the *minio.Client and to any BeforeConnectHook that needs to configure an external service (e.g. a container launcher that creates a MinIO container with matching credentials).
Default: "minioadmin"
Example:
connector := NewConnector(
WithAccessKey("admin"),
WithSecretKey("secret123"),
)
func WithBeforeConnectHook ¶
func WithBeforeConnectHook(hook BeforeConnectHook) ConnectorOption
WithBeforeConnectHook sets a callback function that is invoked before establishing a MinIO connection. This allows for custom initialization logic or resource setup before the client is created.
The hook is called every time Client() is invoked, allowing for dynamic behavior based on runtime conditions (e.g. lazy container launch on first connection).
Example:
// Start container before first connection
cnt := containers.NewContainer()
connector := NewConnector(
WithBeforeConnectHook(cnt.Launch),
WithCloseHook(cnt.Close),
)
func WithCloseHook ¶
func WithCloseHook(hook CloseHook) ConnectorOption
WithCloseHook sets a callback function that is invoked when the connector is closed. This allows for cleanup of resources associated with the connector.
The hook is called when Close() is invoked by the reuse instance's cleanup, after all users across all environments have exited.
Example:
// Stop container when connector closes
cnt := containers.NewContainer()
connector := NewConnector(
WithBeforeConnectHook(cnt.Launch),
WithCloseHook(cnt.Close),
)
func WithEndpoint ¶
func WithEndpoint(endpoint string) ConnectorOption
WithEndpoint sets the MinIO server endpoint in host:port form.
Default: "0.0.0.0:9000"
Example:
connector := NewConnector(
WithEndpoint("minio.example.com:9000"),
)
func WithRegion ¶
func WithRegion(region string) ConnectorOption
WithRegion sets the MinIO region passed to the client. An empty string means "unspecified" (MinIO uses its server default, typically "us-east-1").
Default: "" (unspecified)
Example:
connector := NewConnector(
WithRegion("us-west-1"),
)
func WithReuseOptions ¶
func WithReuseOptions(opts ...reuse.Option[*Connector]) ConnectorOption
WithReuseOptions configures the reuse instance that manages the connector's lifecycle. These options control how the connector is shared and cleaned up across multiple environments.
Use this to set:
- Weight: maximum concurrent users of this connector
- WaitUntilCleanup: grace period before cleanup after last user exits
- CleanupTimeout: maximum time allowed for cleanup operations
- CleanupFunc: custom cleanup logic
- ManualCleanup: disable automatic cleanup
When multiple environments share the same connector, the reuse instance ensures correct reference counting — cleanup only fires when ALL users across ALL environments have exited.
Example:
cnt := containers.NewContainer()
connector := minioenv.NewConnector(
append(
cnt.ConnectorOptions(),
minioenv.WithReuseOptions(
reuse.WithWeight[*minioenv.Connector](10),
reuse.WithWaitUntilCleanup[*minioenv.Connector](time.Second),
),
)...,
)
func WithSecretKey ¶
func WithSecretKey(secretKey string) ConnectorOption
WithSecretKey sets the MinIO secret key for authentication.
Default: "minioadmin"
Example:
connector := NewConnector(
WithAccessKey("admin"),
WithSecretKey("secret123"),
)
func WithUseSSL ¶
func WithUseSSL(useSSL bool) ConnectorOption
WithUseSSL controls whether connections to the MinIO server use TLS.
Default: false
Example:
// Connect to a MinIO server with TLS
connector := NewConnector(
WithUseSSL(true),
)
type Environment ¶
type Environment struct {
// contains filtered or unexported fields
}
Environment manages MinIO client lifecycles with flexible reuse strategies. It provides a high-level interface for creating MinIO clients with automatic resource management and bucket isolation.
Environment supports different reuse strategies to balance isolation and performance:
- No strategy (default): Raw client, no bucket management
- CreateNewBucket: Full isolation with a new bucket per connection
Environment integrates with the reuse package to efficiently manage connectors and resources across multiple connections and tests.
Thread Safety: Environment is safe for concurrent use. Multiple goroutines can call Connect concurrently, and each will receive an independent Client.
Example:
cnt := containers.NewContainer()
connector := minioenv.NewConnector(cnt.ConnectorOptions()...)
env := minioenv.New(
minioenv.WithConnector(connector),
)
client, err := env.Connect(ctx)
if err != nil {
return err
}
defer client.Close()
func New ¶
func New(opts ...EnvironmentOption) *Environment
New creates a new MinIO environment with the specified options. The environment manages client lifecycles and bucket isolation according to the configured strategy.
Default Configuration:
- Reuse Strategy: None (raw client, no bucket management)
- Connector: Default connector with standard MinIO settings
- WithBuckets: none
Options are applied in order, allowing later options to override earlier ones.
Example:
// Simple environment
env := minioenv.New()
// Environment with container
cnt := containers.NewContainer()
connector := minioenv.NewConnector(cnt.ConnectorOptions()...)
env := minioenv.New(minioenv.WithConnector(connector))
// Environment with all options
env := minioenv.New(
minioenv.WithConnector(connector),
minioenv.WithReuseStrategy(&ReuseStrategyCreateNewBucket{}),
minioenv.WithBuckets("config"),
)
func (*Environment) Connect ¶
func (e *Environment) Connect(ctx context.Context) (*Client, error)
Connect creates a new MinIO client according to the configured reuse strategy.
Connection Process:
- Acquires a connector from the reuse pool
- Ensures all WithBuckets buckets exist (idempotent)
- Applies the configured reuse strategy: - No strategy: Returns the raw client - CreateNewBucket: Creates a new bucket, returns client with cleanup
- Returns a Client ready for object operations
The returned Client must be closed when no longer needed. Closing the client automatically cleans up any bucket created by the reuse strategy and decrements the connector reuse refcount.
Parameters:
- ctx: Context for cancellation and timeout
Returns:
- *Client: MinIO client ready for object operations
- error: Non-nil if connection fails
Example:
client, err := env.Connect(ctx)
if err != nil {
return err
}
defer client.Close()
// Use client for object operations...
err = client.PutObject(ctx, client.Bucket(), "key", reader)
type EnvironmentOption ¶
type EnvironmentOption func(*Environment)
EnvironmentOption is a function that configures an Environment. Options can be passed to New to customize the environment's behavior.
func WithBuckets ¶
func WithBuckets(names ...string) EnvironmentOption
WithBuckets is an EnvironmentOption that ensures the listed buckets exist before Connect returns. The check is idempotent: if a bucket already exists, it is left alone. If creation fails because the bucket appeared between the existence check and the create call (race with another concurrent test), the error is treated as success.
Buckets declared via WithBuckets are NOT removed when the Client is closed — they live for the entire env lifecycle and persist across tests/runs. Use this for shared, well-known buckets that multiple tests reference by name.
For per-Connect isolated buckets, use ReuseStrategyCreateNewBucket instead. The two can be combined: WithBuckets for shared fixtures, the reuse strategy for an isolated working bucket.
Example:
env := minioenv.New(
minioenv.WithConnector(connector),
minioenv.WithBuckets("config", "uploads"),
)
client, _ := env.Connect(ctx)
defer client.Close()
// "config" and "uploads" are guaranteed to exist
_ = client.PutObject(ctx, "config", "app.yaml", reader)
func WithConnector ¶
func WithConnector(connector *Connector) EnvironmentOption
WithConnector sets the connector for the environment. The connector manages its own reuse lifecycle internally, so multiple environments can safely share the same connector — cleanup only fires when ALL users across ALL environments have exited.
Example:
cnt := containers.NewContainer()
connector := minioenv.NewConnector(cnt.ConnectorOptions()...)
env := minioenv.New(
minioenv.WithConnector(connector),
minioenv.WithReuseStrategy(&ReuseStrategyCreateNewBucket{}),
)
func WithReuseStrategy ¶
func WithReuseStrategy(strategy ReuseStrategy) EnvironmentOption
WithReuseStrategy sets the strategy for reusing buckets. Different strategies provide different levels of isolation.
If not set, the environment returns a raw client without any bucket management (the user creates and removes buckets themselves).
Example:
// Create new bucket per connection
env := minioenv.New(
minioenv.WithReuseStrategy(&ReuseStrategyCreateNewBucket{}),
)
type Option ¶
Option is a function that configures a MinIO client after it is established. Options are applied in order and can be used for tasks like:
- Putting seed objects into a known bucket
- Configuring bucket policies
- Setting up bucket notifications
The option receives the context and the established MinIO client, and returns an error if the configuration fails.
Example:
// Custom option that seeds an object
func WithSeedObject(bucket, key string, body io.Reader) minioenv.Option {
return func(ctx context.Context, c *minioenv.Client) error {
_, err := c.PutObject(ctx, bucket, key, body, -1, -1)
return err
}
}
type ReuseStrategy ¶
type ReuseStrategy interface {
// contains filtered or unexported methods
}
ReuseStrategy defines the strategy for reusing buckets across multiple connections. Different strategies provide different levels of isolation and cleanup semantics.
Implementations:
- ReuseStrategyCreateNewBucket: Creates a new bucket per connection
- nil: Uses the raw client without any bucket management
ReuseStrategy is a sealed interface — only types defined in this package can implement it.
type ReuseStrategyCreateNewBucket ¶
type ReuseStrategyCreateNewBucket struct {
// NewBucketName is a function that generates a unique bucket name.
// If nil, a default name generator is used that creates names like
// "bucket123456789" using crypto/rand.
//
// The function should return a unique name each time it's called.
// Names are automatically converted to lowercase.
NewBucketName func() string
}
ReuseStrategyCreateNewBucket creates a new bucket for each connection. This provides complete isolation between connections but the bucket is removed when the client is closed, keeping the MinIO server clean.
Each time Connect is called, a new bucket is created with a unique name. When the Client is closed, the bucket is automatically removed via RemoveBucket (using context.WithoutCancel so cleanup succeeds even if the caller's context is canceled).
Use this strategy when:
- You need complete isolation between tests
- Tests modify bucket-level settings
- Tests create/drop objects in the bucket
Example:
cnt := containers.NewContainer()
connector := minioenv.NewConnector(cnt.ConnectorOptions()...)
env := minioenv.New(
minioenv.WithConnector(connector),
minioenv.WithReuseStrategy(&ReuseStrategyCreateNewBucket{}),
)
Directories
¶
| Path | Synopsis |
|---|---|
|
Package containers provides Docker container management for MinIO test environments.
|
Package containers provides Docker container management for MinIO test environments. |