feat(apierr): convert MinIO errors to UpstreamError via FromMinio

This commit is contained in:
Noooste
2026-04-20 00:02:53 +02:00
parent f2cc660c8f
commit caf8d9fd2e
2 changed files with 94 additions and 0 deletions
+39
View File
@@ -2,9 +2,11 @@ package apierr
import (
"encoding/json"
"errors"
"io"
"github.com/Noooste/azuretls-client"
"github.com/minio/minio-go/v7"
)
// garageErrBody mirrors Garage's JSON error envelope.
@@ -51,3 +53,40 @@ func ParseGarage(resp *azuretls.Response) error {
}
return ue
}
// FromMinio converts a MinIO/S3 error into an *UpstreamError. Returns nil when
// err is nil. If err is not a minio.ErrorResponse (e.g. a network error), a
// generic 500 *UpstreamError is returned with the raw error string as Message.
func FromMinio(err error) *UpstreamError {
if err == nil {
return nil
}
var mer minio.ErrorResponse
if errors.As(err, &mer) {
details := map[string]string{}
if mer.BucketName != "" {
details["bucket"] = mer.BucketName
}
if mer.Key != "" {
details["key"] = mer.Key
}
status := mer.StatusCode
if status == 0 {
status = 500
}
return &UpstreamError{
HTTPStatus: status,
Code: mer.Code,
Message: mer.Message,
Source: "s3",
Details: details,
}
}
return &UpstreamError{
HTTPStatus: 500,
Source: "s3",
Message: err.Error(),
}
}
+55
View File
@@ -2,11 +2,13 @@ package apierr
import (
"bytes"
"errors"
"io"
"strings"
"testing"
"github.com/Noooste/azuretls-client"
"github.com/minio/minio-go/v7"
)
func fakeResp(status int, body string) *azuretls.Response {
@@ -77,3 +79,56 @@ func TestParseGarage_EmptyBody(t *testing.T) {
t.Errorf("HTTPStatus = %d, want 502", ue.HTTPStatus)
}
}
func TestFromMinio_NilInput(t *testing.T) {
if got := FromMinio(nil); got != nil {
t.Fatalf("FromMinio(nil) = %v, want nil", got)
}
}
func TestFromMinio_MinioErrorResponse(t *testing.T) {
in := minio.ErrorResponse{
StatusCode: 404,
Code: "NoSuchBucket",
Message: "The specified bucket does not exist",
BucketName: "missing",
}
got := FromMinio(in)
if got == nil {
t.Fatal("FromMinio returned nil")
}
if got.HTTPStatus != 404 {
t.Errorf("HTTPStatus = %d, want 404", got.HTTPStatus)
}
if got.Code != "NoSuchBucket" {
t.Errorf("Code = %q, want NoSuchBucket", got.Code)
}
if got.Message != "The specified bucket does not exist" {
t.Errorf("Message = %q", got.Message)
}
if got.Source != "s3" {
t.Errorf("Source = %q, want s3", got.Source)
}
if got.Details["bucket"] != "missing" {
t.Errorf("Details[bucket] = %q, want missing", got.Details["bucket"])
}
}
func TestFromMinio_NonMinioError(t *testing.T) {
got := FromMinio(errors.New("connection refused"))
if got == nil {
t.Fatal("FromMinio returned nil")
}
if got.HTTPStatus != 500 {
t.Errorf("HTTPStatus = %d, want 500", got.HTTPStatus)
}
if got.Code != "" {
t.Errorf("Code = %q, want empty", got.Code)
}
if !strings.Contains(got.Message, "connection refused") {
t.Errorf("Message = %q", got.Message)
}
if got.Source != "s3" {
t.Errorf("Source = %q, want s3", got.Source)
}
}