Introduces code.nochebuena.dev/einherjar/storage-minio — the object storage starter for the Einherjar framework. Absorbs the minio package from micro-lib, replacing fmt.Errorf wrapping with core/xerrors. Interfaces (CT-6: one TypeSpec per file): - Provider — PutObject, RemoveObject, GetObject, PresignedGetObject, HandleError - Component — lifecycle.Component + observability.Checkable + Provider + Native() Implementation: - New(logger, cfg) Component — client not created until OnInit - OnInit: minio.New with credentials and transport; bucket existence check - OnStart: BucketExists PING; logs "minio: connected" - OnStop: logs "minio: closing client" (minio-go is stateless; no explicit close) - HealthCheck: BucketExists check; Priority LevelCritical - Native() *miniogo.Client — escape hatch for operations not in Provider - HandleError: maps minio-go errors to xerrors (NotFound, AlreadyExists, Internal) Config (EINHERJAR_MINIO_* env vars): Endpoint(required), AccessKey(required), SecretKey(required), Bucket(required), UseSSL(false), Region(us-east-1) - Component interface embeds observability.Identifiable; identifiable.go implements ModulePath and ModuleVersion via runtime/debug.ReadBuildInfo() — prints in launcher banner
3.4 KiB
einherjar/storage-minio
The shield does not care who forged it. It holds what it is given and gives it back unchanged.
code.nochebuena.dev/einherjar/storage-minio is the MinIO/S3 object storage component of the Einherjar framework. It wraps minio-go/v7 behind a lifecycle-aware Component with four common operations — upload, download, delete, and presigned URLs. For anything beyond that scope, Native() returns the raw *miniogo.Client.
Usage
Setup
import storageminio "code.nochebuena.dev/einherjar/storage-minio"
s := storageminio.New(logger, storageminio.DefaultConfig())
launcher.Append(s) // OnInit connects; OnStop is a no-op (stateless client)
health.Register(s) // BucketExists check; LevelCritical
Uploading
import miniogo "github.com/minio/minio-go/v7"
info, err := s.PutObject(ctx, "my-bucket", "uploads/photo.jpg", reader, size, miniogo.PutObjectOptions{
ContentType: "image/jpeg",
})
Downloading
obj, err := s.GetObject(ctx, "my-bucket", "uploads/photo.jpg", miniogo.GetObjectOptions{})
if err != nil {
return s.HandleError(err)
}
defer obj.Close()
Presigned URL (time-limited public access)
url, err := s.PresignedGetObject(ctx, "my-bucket", "uploads/photo.jpg", 15*time.Minute, nil)
// url is a *url.URL — call url.String() to get the string form
Deleting
err := s.RemoveObject(ctx, "my-bucket", "uploads/photo.jpg", miniogo.RemoveObjectOptions{})
Native escape hatch
For multipart uploads, bucket management, or any operation not in Provider, use the raw client:
native := s.Native() // *miniogo.Client
Callers that use Native() must import github.com/minio/minio-go/v7 directly.
Error handling
if err := s.HandleError(someErr); err != nil {
// minio-go error responses mapped to xerrors:
// NoSuchKey / NoSuchBucket → ErrNotFound
// AccessDenied → ErrPermissionDenied
// context.Canceled → ErrCancelled
// context.DeadlineExceeded → ErrDeadlineExceeded
}
HandleError is also available as a package-level function: storageminio.HandleError(err).
Environment variables
| Variable | Required | Default | Description |
|---|---|---|---|
EINHERJAR_MINIO_ENDPOINT |
Yes | — | MinIO/S3 endpoint (host:port or domain) |
EINHERJAR_MINIO_ACCESS_KEY |
Yes | — | Access key ID |
EINHERJAR_MINIO_SECRET_KEY |
Yes | — | Secret access key |
EINHERJAR_MINIO_BUCKET |
Yes | — | Default bucket for health check |
EINHERJAR_MINIO_USE_SSL |
No | false |
Use TLS |
EINHERJAR_MINIO_REGION |
No | us-east-1 |
Bucket region |
Dependency graph
contracts (zero dependencies)
↑
core
↑
storage-minio (contracts, core, minio-go/v7)
↑
your app
Verification
cd storage-minio/
go build ./...
go vet ./...
go test ./...
gofmt -l .
The artifact survives the battle that created it. Store it well. Someone will need it after you are gone.