mirror of
https://github.com/versity/versitygw.git
synced 2026-10-22 06:12:39 +00:00
fix: only look up the parts count when ObjectParts is requested
The controller now passes the requested attributes to the backend in ObjectAttributes, so posix and azure only do the extra HeadObject for the parts count when ObjectParts was asked for. This also lets the s3 backend reach the upstream service, which rejected the request before because the field was missing. The s3 backend now copies TotalPartsCount into PartsCount, leaves out ObjectParts when the upstream response has none, and sets the part number markers, which were dropped by inverted strconv.Atoi error checks.
This commit is contained in:
+16
-15
@@ -764,22 +764,23 @@ func (az *Azure) GetObjectAttributes(ctx context.Context, input *s3.GetObjectAtt
|
||||
return s3response.GetObjectAttributesResponse{}, err
|
||||
}
|
||||
|
||||
// HeadObject reports the parts count only when a part is requested, and
|
||||
// only for objects completed from a multipart upload
|
||||
partNumber := int32(1)
|
||||
part, err := az.HeadObject(ctx, &s3.HeadObjectInput{
|
||||
Bucket: input.Bucket,
|
||||
Key: input.Key,
|
||||
PartNumber: &partNumber,
|
||||
})
|
||||
if err != nil {
|
||||
return s3response.GetObjectAttributesResponse{}, err
|
||||
}
|
||||
|
||||
var objParts *s3response.ObjectParts
|
||||
if part.PartsCount != nil {
|
||||
objParts = &s3response.ObjectParts{
|
||||
PartsCount: part.PartsCount,
|
||||
if slices.Contains(input.ObjectAttributes, types.ObjectAttributesObjectParts) {
|
||||
// HeadObject reports the parts count only when a part is requested,
|
||||
// and only for objects completed from a multipart upload
|
||||
partNumber := int32(1)
|
||||
part, err := az.HeadObject(ctx, &s3.HeadObjectInput{
|
||||
Bucket: input.Bucket,
|
||||
Key: input.Key,
|
||||
PartNumber: &partNumber,
|
||||
})
|
||||
if err != nil {
|
||||
return s3response.GetObjectAttributesResponse{}, err
|
||||
}
|
||||
if part.PartsCount != nil {
|
||||
objParts = &s3response.ObjectParts{
|
||||
PartsCount: part.PartsCount,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ import (
|
||||
)
|
||||
|
||||
// TestGetObjectAttributesPartsCount checks that GetObjectAttributes reports
|
||||
// the parts count of an object completed from a multipart upload, and no
|
||||
// object parts for an object that wasn't.
|
||||
// the parts count of an object completed from a multipart upload when
|
||||
// ObjectParts is requested, and no object parts for an object that wasn't.
|
||||
func TestGetObjectAttributesPartsCount(t *testing.T) {
|
||||
for name, mkMeta := range metaModes(t) {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
@@ -79,9 +79,11 @@ func TestGetObjectAttributesPartsCount(t *testing.T) {
|
||||
t.Fatalf("complete multipart upload: %v", err)
|
||||
}
|
||||
|
||||
partsAttr := []types.ObjectAttributes{types.ObjectAttributesObjectParts}
|
||||
out, err := p.GetObjectAttributes(ctx, &s3.GetObjectAttributesInput{
|
||||
Bucket: &bucket,
|
||||
Key: &mpKey,
|
||||
Bucket: &bucket,
|
||||
Key: &mpKey,
|
||||
ObjectAttributes: partsAttr,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("get multipart object attributes: %v", err)
|
||||
@@ -96,6 +98,19 @@ func TestGetObjectAttributesPartsCount(t *testing.T) {
|
||||
t.Errorf("expected object size %d, got %d", totalSize, got)
|
||||
}
|
||||
|
||||
// the parts count is only looked up when ObjectParts is requested
|
||||
out, err = p.GetObjectAttributes(ctx, &s3.GetObjectAttributesInput{
|
||||
Bucket: &bucket,
|
||||
Key: &mpKey,
|
||||
ObjectAttributes: []types.ObjectAttributes{types.ObjectAttributesObjectSize},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("get multipart object size: %v", err)
|
||||
}
|
||||
if out.ObjectParts != nil {
|
||||
t.Errorf("expected no object parts when not requested, got %+v", *out.ObjectParts)
|
||||
}
|
||||
|
||||
key := "obj"
|
||||
_, err = p.PutObject(ctx, s3response.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
@@ -108,8 +123,9 @@ func TestGetObjectAttributesPartsCount(t *testing.T) {
|
||||
}
|
||||
|
||||
out, err = p.GetObjectAttributes(ctx, &s3.GetObjectAttributesInput{
|
||||
Bucket: &bucket,
|
||||
Key: &key,
|
||||
Bucket: &bucket,
|
||||
Key: &key,
|
||||
ObjectAttributes: partsAttr,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("get object attributes: %v", err)
|
||||
|
||||
+17
-16
@@ -6451,23 +6451,24 @@ func (p *Posix) GetObjectAttributes(ctx context.Context, input *s3.GetObjectAttr
|
||||
return s3response.GetObjectAttributesResponse{}, err
|
||||
}
|
||||
|
||||
// HeadObject reports the parts count only when a part is requested, and
|
||||
// only for objects completed from a multipart upload
|
||||
partNumber := int32(1)
|
||||
part, err := p.HeadObject(withCtxNoSlot(ctx), &s3.HeadObjectInput{
|
||||
Bucket: input.Bucket,
|
||||
Key: input.Key,
|
||||
VersionId: input.VersionId,
|
||||
PartNumber: &partNumber,
|
||||
})
|
||||
if err != nil {
|
||||
return s3response.GetObjectAttributesResponse{}, err
|
||||
}
|
||||
|
||||
var objParts *s3response.ObjectParts
|
||||
if part.PartsCount != nil {
|
||||
objParts = &s3response.ObjectParts{
|
||||
PartsCount: part.PartsCount,
|
||||
if slices.Contains(input.ObjectAttributes, types.ObjectAttributesObjectParts) {
|
||||
// HeadObject reports the parts count only when a part is requested,
|
||||
// and only for objects completed from a multipart upload
|
||||
partNumber := int32(1)
|
||||
part, err := p.HeadObject(withCtxNoSlot(ctx), &s3.HeadObjectInput{
|
||||
Bucket: input.Bucket,
|
||||
Key: input.Key,
|
||||
VersionId: input.VersionId,
|
||||
PartNumber: &partNumber,
|
||||
})
|
||||
if err != nil {
|
||||
return s3response.GetObjectAttributesResponse{}, err
|
||||
}
|
||||
if part.PartsCount != nil {
|
||||
objParts = &s3response.ObjectParts{
|
||||
PartsCount: part.PartsCount,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-21
@@ -1222,27 +1222,23 @@ func (s *S3Proxy) GetObjectAttributes(ctx context.Context, input *s3.GetObjectAt
|
||||
return s3response.GetObjectAttributesResponse{}, handleError(err)
|
||||
}
|
||||
|
||||
parts := s3response.ObjectParts{}
|
||||
objParts := out.ObjectParts
|
||||
if objParts != nil {
|
||||
var parts *s3response.ObjectParts
|
||||
if objParts := out.ObjectParts; objParts != nil {
|
||||
parts = &s3response.ObjectParts{
|
||||
Parts: objParts.Parts,
|
||||
PartsCount: objParts.TotalPartsCount,
|
||||
}
|
||||
if objParts.PartNumberMarker != nil {
|
||||
partNumberMarker, err := strconv.Atoi(*objParts.PartNumberMarker)
|
||||
if err != nil {
|
||||
parts.PartNumberMarker = partNumberMarker
|
||||
}
|
||||
if objParts.NextPartNumberMarker != nil {
|
||||
nextPartNumberMarker, err := strconv.Atoi(*objParts.NextPartNumberMarker)
|
||||
if err != nil {
|
||||
parts.NextPartNumberMarker = nextPartNumberMarker
|
||||
}
|
||||
}
|
||||
if objParts.IsTruncated != nil {
|
||||
parts.IsTruncated = *objParts.IsTruncated
|
||||
}
|
||||
if objParts.MaxParts != nil {
|
||||
parts.MaxParts = int(*objParts.MaxParts)
|
||||
}
|
||||
parts.Parts = objParts.Parts
|
||||
parts.PartNumberMarker, _ = strconv.Atoi(*objParts.PartNumberMarker)
|
||||
}
|
||||
if objParts.NextPartNumberMarker != nil {
|
||||
parts.NextPartNumberMarker, _ = strconv.Atoi(*objParts.NextPartNumberMarker)
|
||||
}
|
||||
if objParts.MaxParts != nil {
|
||||
parts.MaxParts = int(*objParts.MaxParts)
|
||||
}
|
||||
if objParts.IsTruncated != nil {
|
||||
parts.IsTruncated = *objParts.IsTruncated
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1251,7 +1247,7 @@ func (s *S3Proxy) GetObjectAttributes(ctx context.Context, input *s3.GetObjectAt
|
||||
LastModified: out.LastModified,
|
||||
ObjectSize: out.ObjectSize,
|
||||
StorageClass: out.StorageClass,
|
||||
ObjectParts: &parts,
|
||||
ObjectParts: parts,
|
||||
Checksum: out.Checksum,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -388,8 +388,14 @@ func (c S3ApiController) GetObjectAttributes(ctx fiber.Ctx) (*Response, error) {
|
||||
}, err
|
||||
}
|
||||
|
||||
objAttrs := make([]types.ObjectAttributes, 0, len(attrs))
|
||||
for attr := range attrs {
|
||||
objAttrs = append(objAttrs, types.ObjectAttributes(attr))
|
||||
}
|
||||
|
||||
res, err := c.be.GetObjectAttributes(ctx.RequestCtx(),
|
||||
&s3.GetObjectAttributesInput{
|
||||
ObjectAttributes: objAttrs,
|
||||
SSECustomerAlgorithm: ssec.Algorithm,
|
||||
SSECustomerKey: ssec.Key,
|
||||
SSECustomerKeyMD5: ssec.KeyMD5,
|
||||
|
||||
Reference in New Issue
Block a user