gateway-azure: Convert S3 metadata to azure metadata (#4384)

fixes #4292
master
Krishna Srinivas 8 years ago committed by Harshavardhana
parent bac5303b10
commit 0bba3cc8e3
  1. 70
      cmd/gateway-azure.go
  2. 27
      cmd/gateway-azure_test.go

@ -37,6 +37,36 @@ import (
const globalAzureAPIVersion = "2016-05-31" const globalAzureAPIVersion = "2016-05-31"
// Canonicalize the metadata headers, without this azure-sdk calculates
// incorrect signature. This attempt to canonicalize is to convert
// any HTTP header which is of form say `accept-encoding` should be
// converted to `Accept-Encoding` in its canonical form.
// Also replaces X-Amz-Meta prefix with X-Ms-Meta as Azure expects user
// defined metadata to have X-Ms-Meta prefix.
func s3ToAzureHeaders(headers map[string]string) (newHeaders map[string]string) {
newHeaders = make(map[string]string)
for k, v := range headers {
k = http.CanonicalHeaderKey(k)
if strings.HasPrefix(k, "X-Amz-Meta") {
k = strings.Replace(k, "X-Amz-Meta", "X-Ms-Meta", -1)
}
newHeaders[k] = v
}
return newHeaders
}
// Prefix user metadata with "X-Amz-Meta-".
// client.GetBlobMetadata() already strips "X-Ms-Meta-"
func azureToS3Metadata(meta map[string]string) (newMeta map[string]string) {
newMeta = make(map[string]string)
for k, v := range meta {
k = "X-Amz-Meta-" + k
newMeta[k] = v
}
return newMeta
}
// To store metadata during NewMultipartUpload which will be used after // To store metadata during NewMultipartUpload which will be used after
// CompleteMultipartUpload to call SetBlobMetadata. // CompleteMultipartUpload to call SetBlobMetadata.
type azureMultipartMetaInfo struct { type azureMultipartMetaInfo struct {
@ -275,6 +305,13 @@ func (a *azureObjects) GetObject(bucket, object string, startOffset int64, lengt
// GetObjectInfo - reads blob metadata properties and replies back ObjectInfo, // GetObjectInfo - reads blob metadata properties and replies back ObjectInfo,
// uses zure equivalent GetBlobProperties. // uses zure equivalent GetBlobProperties.
func (a *azureObjects) GetObjectInfo(bucket, object string) (objInfo ObjectInfo, err error) { func (a *azureObjects) GetObjectInfo(bucket, object string) (objInfo ObjectInfo, err error) {
blobMeta, err := a.client.GetBlobMetadata(bucket, object)
if err != nil {
return objInfo, azureToObjectError(traceError(err), bucket, object)
}
meta := azureToS3Metadata(blobMeta)
prop, err := a.client.GetBlobProperties(bucket, object) prop, err := a.client.GetBlobProperties(bucket, object)
if err != nil { if err != nil {
return objInfo, azureToObjectError(traceError(err), bucket, object) return objInfo, azureToObjectError(traceError(err), bucket, object)
@ -283,31 +320,22 @@ func (a *azureObjects) GetObjectInfo(bucket, object string) (objInfo ObjectInfo,
if err != nil { if err != nil {
return objInfo, traceError(err) return objInfo, traceError(err)
} }
if prop.ContentEncoding != "" {
meta["Content-Encoding"] = prop.ContentEncoding
}
meta["Content-Type"] = prop.ContentType
objInfo = ObjectInfo{ objInfo = ObjectInfo{
Bucket: bucket, Bucket: bucket,
UserDefined: make(map[string]string), UserDefined: meta,
ETag: canonicalizeETag(prop.Etag), ETag: canonicalizeETag(prop.Etag),
ModTime: t, ModTime: t,
Name: object, Name: object,
Size: prop.ContentLength, Size: prop.ContentLength,
} }
if prop.ContentEncoding != "" {
objInfo.UserDefined["Content-Encoding"] = prop.ContentEncoding
}
objInfo.UserDefined["Content-Type"] = prop.ContentType
return objInfo, nil
}
// Canonicalize the metadata headers, without this azure-sdk calculates return objInfo, nil
// incorrect signature. This attempt to canonicalize is to convert
// any HTTP header which is of form say `accept-encoding` should be
// converted to `Accept-Encoding` in its canonical form.
func canonicalMetadata(metadata map[string]string) (canonical map[string]string) {
canonical = make(map[string]string)
for k, v := range metadata {
canonical[http.CanonicalHeaderKey(k)] = v
}
return canonical
} }
// PutObject - Create a new blob with the incoming data, // PutObject - Create a new blob with the incoming data,
@ -337,7 +365,7 @@ func (a *azureObjects) PutObject(bucket, object string, size int64, data io.Read
teeReader = io.TeeReader(data, io.MultiWriter(writers...)) teeReader = io.TeeReader(data, io.MultiWriter(writers...))
} }
err = a.client.CreateBlockBlobFromReader(bucket, object, uint64(size), teeReader, canonicalMetadata(metadata)) err = a.client.CreateBlockBlobFromReader(bucket, object, uint64(size), teeReader, s3ToAzureHeaders(metadata))
if err != nil { if err != nil {
return objInfo, azureToObjectError(traceError(err), bucket, object) return objInfo, azureToObjectError(traceError(err), bucket, object)
} }
@ -410,7 +438,7 @@ func (a *azureObjects) NewMultipartUpload(bucket, object string, metadata map[st
// Store an empty map as a placeholder else ListObjectParts/PutObjectPart will not work properly. // Store an empty map as a placeholder else ListObjectParts/PutObjectPart will not work properly.
metadata = make(map[string]string) metadata = make(map[string]string)
} else { } else {
metadata = canonicalMetadata(metadata) metadata = s3ToAzureHeaders(metadata)
} }
a.metaInfo.set(uploadID, metadata) a.metaInfo.set(uploadID, metadata)
return uploadID, nil return uploadID, nil
@ -587,6 +615,10 @@ func (a *azureObjects) CompleteMultipartUpload(bucket, object, uploadID string,
if err != nil { if err != nil {
return objInfo, azureToObjectError(traceError(err), bucket, object) return objInfo, azureToObjectError(traceError(err), bucket, object)
} }
err = a.client.SetBlobMetadata(bucket, object, nil, meta)
if err != nil {
return objInfo, azureToObjectError(traceError(err), bucket, object)
}
} }
a.metaInfo.del(uploadID) a.metaInfo.del(uploadID)
return a.GetObjectInfo(bucket, object) return a.GetObjectInfo(bucket, object)

@ -25,18 +25,33 @@ import (
) )
// Test canonical metadata. // Test canonical metadata.
func TestCanonicalMetadata(t *testing.T) { func TestS3ToAzureHeaders(t *testing.T) {
metadata := map[string]string{ headers := map[string]string{
"accept-encoding": "gzip", "accept-encoding": "gzip",
"content-encoding": "gzip", "content-encoding": "gzip",
} }
expectedCanonicalM := map[string]string{ expectedHeaders := map[string]string{
"Accept-Encoding": "gzip", "Accept-Encoding": "gzip",
"Content-Encoding": "gzip", "Content-Encoding": "gzip",
} }
actualCanonicalM := canonicalMetadata(metadata) actualHeaders := s3ToAzureHeaders(headers)
if !reflect.DeepEqual(actualCanonicalM, expectedCanonicalM) { if !reflect.DeepEqual(actualHeaders, expectedHeaders) {
t.Fatalf("Test failed, expected %#v, got %#v", expectedCanonicalM, actualCanonicalM) t.Fatalf("Test failed, expected %#v, got %#v", expectedHeaders, actualHeaders)
}
}
func TestAzureToS3Metadata(t *testing.T) {
// Just one testcase. Adding more test cases does not add value to the testcase
// as azureToS3Metadata() just adds a prefix.
metadata := map[string]string{
"First-Name": "myname",
}
expectedMeta := map[string]string{
"X-Amz-Meta-First-Name": "myname",
}
actualMeta := azureToS3Metadata(metadata)
if !reflect.DeepEqual(actualMeta, expectedMeta) {
t.Fatalf("Test failed, expected %#v, got %#v", expectedMeta, actualMeta)
} }
} }

Loading…
Cancel
Save