Skip to content

Commit 13fe28b

Browse files
committed
S3: log human readable error on connection failure
Should BucketExists (HeadBucket) fail because of an error related to the connection rather than the existence of the bucket, no information is available and the admin is left guessing. https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadBucket.html > This action is useful to determine if a bucket exists and you have > permission to access it. The action returns a 200 OK if the bucket > exists and you have permission to access it. > > If the bucket does not exist or you do not have permission to access > it, the HEAD request returns a generic 400 Bad Request, 403 > Forbidden or 404 Not Found code. A message body is not included, so > you cannot determine the exception beyond these error codes. GetBucketVersioning is used instead and exclusively dedicated to asserting if using the connection does not return a BadRequest. If it does the NewMinioStorage logs an error and returns. Otherwise it keeps going knowing that BucketExists is not going to fail for reasons unrelated to the existence of the bucket and the permissions to access it. (cherry picked from commit d1df4b3bc62e5e61893a923f1c4b58f084eb03af) Refs: https://codeberg.org/forgejo/forgejo/issues/1338
1 parent ebff051 commit 13fe28b

File tree

2 files changed

+55
-0
lines changed

2 files changed

+55
-0
lines changed

modules/storage/minio.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ func convertMinioErr(err error) error {
7171
return err
7272
}
7373

74+
var getBucketVersioning = func(ctx context.Context, minioClient *minio.Client, bucket string) error {
75+
_, err := minioClient.GetBucketVersioning(ctx, bucket)
76+
return err
77+
}
78+
7479
// NewMinioStorage returns a minio storage
7580
func NewMinioStorage(ctx context.Context, cfg *setting.Storage) (ObjectStorage, error) {
7681
config := cfg.MinioConfig
@@ -90,6 +95,23 @@ func NewMinioStorage(ctx context.Context, cfg *setting.Storage) (ObjectStorage,
9095
return nil, convertMinioErr(err)
9196
}
9297

98+
// The GetBucketVersioning is only used for checking whether the Object Storage parameters are generally good. It doesn't need to succeed.
99+
// The assumption is that if the API returns the HTTP code 400, then the parameters could be incorrect.
100+
// Otherwise even if the request itself fails (403, 404, etc), the code should still continue because the parameters seem "good" enough.
101+
// Keep in mind that GetBucketVersioning requires "owner" to really succeed, so it can't be used to check the existence.
102+
// Not using "BucketExists (HeadBucket)" because it doesn't include detailed failure reasons.
103+
err = getBucketVersioning(ctx, minioClient, config.Bucket)
104+
if err != nil {
105+
errResp, ok := err.(minio.ErrorResponse)
106+
if !ok {
107+
return nil, err
108+
}
109+
if errResp.StatusCode == http.StatusBadRequest {
110+
log.Error("S3 storage connection failure at %s:%s with base path %s and region: %s", config.Endpoint, config.Bucket, config.Location, errResp.Message)
111+
return nil, err
112+
}
113+
}
114+
93115
// Check to see if we already own this bucket
94116
exists, err := minioClient.BucketExists(ctx, config.Bucket)
95117
if err != nil {

modules/storage/minio_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,15 @@
44
package storage
55

66
import (
7+
"context"
8+
"net/http"
79
"os"
810
"testing"
911

1012
"code.gitea.io/gitea/modules/setting"
13+
14+
"github.com/minio/minio-go/v7"
15+
"github.com/stretchr/testify/assert"
1116
)
1217

1318
func TestMinioStorageIterator(t *testing.T) {
@@ -25,3 +30,31 @@ func TestMinioStorageIterator(t *testing.T) {
2530
},
2631
})
2732
}
33+
34+
func TestS3StorageBadRequest(t *testing.T) {
35+
if os.Getenv("CI") == "" {
36+
t.Skip("S3Storage not present outside of CI")
37+
return
38+
}
39+
cfg := &setting.Storage{
40+
MinioConfig: setting.MinioStorageConfig{
41+
Endpoint: "minio:9000",
42+
AccessKeyID: "123456",
43+
SecretAccessKey: "12345678",
44+
Bucket: "bucket",
45+
Location: "us-east-1",
46+
},
47+
}
48+
message := "ERROR"
49+
old := getBucketVersioning
50+
defer func() { getBucketVersioning = old }()
51+
getBucketVersioning = func(ctx context.Context, minioClient *minio.Client, bucket string) error {
52+
return minio.ErrorResponse{
53+
StatusCode: http.StatusBadRequest,
54+
Code: "FixtureError",
55+
Message: message,
56+
}
57+
}
58+
_, err := NewStorage(setting.MinioStorageType, cfg)
59+
assert.ErrorContains(t, err, message)
60+
}

0 commit comments

Comments
 (0)