* store S3 storage class in extended atrributes #7961 * canonical * remove issue reference --------- Co-authored-by: Robert Schade <robert.schade@uni-paderborn.de> Co-authored-by: Chris Lu <chris.lu@gmail.com>
This commit is contained in:
@@ -32,7 +32,7 @@ const (
|
||||
// Standard S3 HTTP request constants
|
||||
const (
|
||||
// S3 storage class
|
||||
AmzStorageClass = "x-amz-storage-class"
|
||||
AmzStorageClass = "X-Amz-Storage-Class"
|
||||
|
||||
// S3 user-defined metadata
|
||||
AmzUserMetaPrefix = "X-Amz-Meta-"
|
||||
|
||||
@@ -571,6 +571,15 @@ func (s3a *S3ApiServer) putToFiler(r *http.Request, filePath string, dataReader
|
||||
}
|
||||
}
|
||||
|
||||
// Store the storage class from header
|
||||
if sc := r.Header.Get(s3_constants.AmzStorageClass); sc != "" {
|
||||
if !validateStorageClass(sc) {
|
||||
glog.Warningf("putToFiler: Invalid storage class '%s' for %s", sc, filePath)
|
||||
return "", s3err.ErrInvalidStorageClass, SSEResponseMetadata{}
|
||||
}
|
||||
entry.Extended[s3_constants.AmzStorageClass] = []byte(sc)
|
||||
}
|
||||
|
||||
// Parse and store object tags from X-Amz-Tagging header
|
||||
// Fix for GitHub issue #7589: Tags sent during object upload were not being stored
|
||||
if tagging := r.Header.Get(s3_constants.AmzObjectTagging); tagging != "" {
|
||||
@@ -1881,3 +1890,20 @@ func (s3a *S3ApiServer) deleteOrphanedChunks(chunks []*filer_pb.FileChunk) {
|
||||
glog.V(3).Infof("deleteOrphanedChunks: successfully deleted all %d orphaned chunks", successCount)
|
||||
}
|
||||
}
|
||||
|
||||
func validateStorageClass(sc string) bool {
|
||||
switch StorageClass(sc) {
|
||||
case "STANDARD", "REDUCED_REDUNDANCY", "STANDARD_IA", "ONEZONE_IA", "INTELLIGENT_TIERING", "GLACIER", "DEEP_ARCHIVE", "OUTPOSTS", "GLACIER_IR", "SNOW":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) getStorageClassFromExtended(extended map[string][]byte) string {
|
||||
if extended != nil {
|
||||
if sc, ok := extended[s3_constants.AmzStorageClass]; ok {
|
||||
return string(sc)
|
||||
}
|
||||
}
|
||||
return "STANDARD"
|
||||
}
|
||||
|
||||
@@ -134,6 +134,7 @@ type ObjectVersion struct {
|
||||
ETag string
|
||||
Size int64
|
||||
OwnerID string // Owner ID extracted from entry metadata
|
||||
StorageClass string
|
||||
}
|
||||
|
||||
// createDeleteMarker creates a delete marker for versioned delete operations
|
||||
@@ -413,7 +414,7 @@ func (vc *versionCollector) addVersion(version *ObjectVersion, objectKey string)
|
||||
ETag: version.ETag,
|
||||
Size: version.Size,
|
||||
Owner: vc.s3a.getObjectOwnerFromVersion(version, vc.bucket, objectKey),
|
||||
StorageClass: "STANDARD",
|
||||
StorageClass: StorageClass(vc.s3a.getStorageClassFromExtended(entryExtended(version))),
|
||||
}
|
||||
*vc.allVersions = append(*vc.allVersions, versionEntry)
|
||||
}
|
||||
@@ -484,7 +485,7 @@ func (vc *versionCollector) processExplicitDirectory(entryPath string, entry *fi
|
||||
ETag: "\"d41d8cd98f00b204e9800998ecf8427e\"", // Empty content ETag
|
||||
Size: 0,
|
||||
Owner: vc.s3a.getObjectOwnerFromEntry(entry),
|
||||
StorageClass: "STANDARD",
|
||||
StorageClass: StorageClass(vc.s3a.getStorageClassFromExtended(entry.Extended)),
|
||||
}
|
||||
*vc.allVersions = append(*vc.allVersions, versionEntry)
|
||||
}
|
||||
@@ -545,7 +546,7 @@ func (vc *versionCollector) processRegularFile(currentPath, entryPath string, en
|
||||
ETag: vc.s3a.calculateETagFromChunks(entry.Chunks),
|
||||
Size: int64(entry.Attributes.FileSize),
|
||||
Owner: vc.s3a.getObjectOwnerFromEntry(entry),
|
||||
StorageClass: "STANDARD",
|
||||
StorageClass: StorageClass(vc.s3a.getStorageClassFromExtended(entry.Extended)),
|
||||
}
|
||||
*vc.allVersions = append(*vc.allVersions, versionEntry)
|
||||
}
|
||||
@@ -726,6 +727,7 @@ func (s3a *S3ApiServer) getObjectVersionList(bucket, object string) ([]*ObjectVe
|
||||
IsDeleteMarker: isDeleteMarker,
|
||||
LastModified: time.Unix(entry.Attributes.Mtime, 0),
|
||||
OwnerID: ownerID,
|
||||
StorageClass: s3a.getStorageClassFromExtended(entry.Extended),
|
||||
}
|
||||
|
||||
if !isDeleteMarker {
|
||||
@@ -1253,10 +1255,18 @@ func (s3a *S3ApiServer) getObjectOwnerFromVersion(version *ObjectVersion, bucket
|
||||
}
|
||||
}
|
||||
|
||||
// Ultimate fallback: return anonymous if no owner found
|
||||
// Fallback: return anonymous if no owner found
|
||||
return CanonicalUser{ID: s3_constants.AccountAnonymousId, DisplayName: "anonymous"}
|
||||
}
|
||||
|
||||
func entryExtended(v *ObjectVersion) map[string][]byte {
|
||||
return map[string][]byte{
|
||||
s3_constants.AmzStorageClass: []byte(v.StorageClass),
|
||||
s3_constants.ExtAmzOwnerKey: []byte(v.OwnerID),
|
||||
s3_constants.ExtETagKey: []byte(v.ETag),
|
||||
}
|
||||
}
|
||||
|
||||
// getObjectOwnerFromEntry extracts object owner information from a file entry
|
||||
func (s3a *S3ApiServer) getObjectOwnerFromEntry(entry *filer_pb.Entry) CanonicalUser {
|
||||
if entry != nil && entry.Extended != nil {
|
||||
|
||||
@@ -141,6 +141,7 @@ const (
|
||||
|
||||
// Bucket encryption errors
|
||||
ErrNoSuchBucketEncryptionConfiguration
|
||||
ErrInvalidStorageClass
|
||||
)
|
||||
|
||||
// Error message constants for checksum validation
|
||||
@@ -588,6 +589,11 @@ var errorCodeResponse = map[ErrorCode]APIError{
|
||||
Description: "The server side encryption configuration was not found.",
|
||||
HTTPStatusCode: http.StatusNotFound,
|
||||
},
|
||||
ErrInvalidStorageClass: {
|
||||
Code: "InvalidStorageClass",
|
||||
Description: "The storage class you specified is not valid",
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
}
|
||||
|
||||
// GetAPIError provides API Error for input API error code.
|
||||
|
||||
Reference in New Issue
Block a user