CompleteMultipartUpload and CreateObjectPart now fully support signature v4

This commit is contained in:
Harshavardhana 2015-07-09 19:01:15 -07:00
parent 89c1215194
commit 00890c254e
11 changed files with 156 additions and 90 deletions

View File

@ -87,6 +87,24 @@ type PartMetadata struct {
Size int64 Size int64
} }
// CompletePart - completed part container
type CompletePart struct {
PartNumber int
ETag string
}
// completedParts is a sortable interface for Part slice
type completedParts []CompletePart
func (a completedParts) Len() int { return len(a) }
func (a completedParts) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a completedParts) Less(i, j int) bool { return a[i].PartNumber < a[j].PartNumber }
// CompleteMultipartUpload container for completing multipart upload
type CompleteMultipartUpload struct {
Part []CompletePart
}
// ObjectResourcesMetadata - various types of object resources // ObjectResourcesMetadata - various types of object resources
type ObjectResourcesMetadata struct { type ObjectResourcesMetadata struct {
Bucket string Bucket string

View File

@ -371,11 +371,6 @@ func (donut API) createObject(bucket, key, contentType, expectedMD5Sum string, s
var length int var length int
byteBuffer := make([]byte, 1024*1024) byteBuffer := make([]byte, 1024*1024)
length, err = data.Read(byteBuffer) length, err = data.Read(byteBuffer)
// While hash.Write() wouldn't mind a Nil byteBuffer
// It is necessary for us to verify this and break
if length == 0 {
break
}
hash.Write(byteBuffer[0:length]) hash.Write(byteBuffer[0:length])
sha256hash.Write(byteBuffer[0:length]) sha256hash.Write(byteBuffer[0:length])
ok := donut.objects.Append(objectKey, byteBuffer[0:length]) ok := donut.objects.Append(objectKey, byteBuffer[0:length])

View File

@ -334,3 +334,19 @@ type MissingDateHeader struct{}
func (e MissingDateHeader) Error() string { func (e MissingDateHeader) Error() string {
return "Missing date header" return "Missing date header"
} }
// InvalidPartOrder parts are not ordered as Requested
type InvalidPartOrder struct {
UploadID string
}
func (e InvalidPartOrder) Error() string {
return "Invalid part order sent for " + e.UploadID
}
// MalformedXML invalid xml format
type MalformedXML struct{}
func (e MalformedXML) Error() string {
return "Malformed XML"
}

View File

@ -51,8 +51,8 @@ type ObjectStorage interface {
type Multipart interface { type Multipart interface {
NewMultipartUpload(bucket, key, contentType string) (string, error) NewMultipartUpload(bucket, key, contentType string) (string, error)
AbortMultipartUpload(bucket, key, uploadID string) error AbortMultipartUpload(bucket, key, uploadID string) error
CreateObjectPart(bucket, key, uploadID string, partID int, contentType, expectedMD5Sum string, size int64, data io.Reader) (string, error) CreateObjectPart(string, string, string, int, string, string, int64, io.Reader, *Signature) (string, error)
CompleteMultipartUpload(bucket, key, uploadID string, parts map[int]string) (ObjectMetadata, error) CompleteMultipartUpload(bucket, key, uploadID string, data io.Reader, signature *Signature) (ObjectMetadata, error)
ListMultipartUploads(bucket string, resources BucketMultipartResourcesMetadata) (BucketMultipartResourcesMetadata, error) ListMultipartUploads(bucket string, resources BucketMultipartResourcesMetadata) (BucketMultipartResourcesMetadata, error)
ListObjectParts(bucket, key string, resources ObjectResourcesMetadata) (ObjectResourcesMetadata, error) ListObjectParts(bucket, key string, resources ObjectResourcesMetadata) (ObjectResourcesMetadata, error)
} }

View File

@ -22,8 +22,10 @@ import (
"crypto/sha512" "crypto/sha512"
"encoding/base64" "encoding/base64"
"encoding/hex" "encoding/hex"
"encoding/xml"
"errors" "errors"
"io" "io"
"io/ioutil"
"math/rand" "math/rand"
"runtime/debug" "runtime/debug"
"sort" "sort"
@ -31,6 +33,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/minio/minio/pkg/crypto/sha256"
"github.com/minio/minio/pkg/donut/cache/data" "github.com/minio/minio/pkg/donut/cache/data"
"github.com/minio/minio/pkg/iodine" "github.com/minio/minio/pkg/iodine"
) )
@ -90,11 +93,11 @@ func (donut API) AbortMultipartUpload(bucket, key, uploadID string) error {
} }
// CreateObjectPart - create a part in a multipart session // CreateObjectPart - create a part in a multipart session
func (donut API) CreateObjectPart(bucket, key, uploadID string, partID int, contentType, expectedMD5Sum string, size int64, data io.Reader) (string, error) { func (donut API) CreateObjectPart(bucket, key, uploadID string, partID int, contentType, expectedMD5Sum string, size int64, data io.Reader, signature *Signature) (string, error) {
donut.lock.Lock() donut.lock.Lock()
defer donut.lock.Unlock() defer donut.lock.Unlock()
etag, err := donut.createObjectPart(bucket, key, uploadID, partID, "", expectedMD5Sum, size, data) etag, err := donut.createObjectPart(bucket, key, uploadID, partID, "", expectedMD5Sum, size, data, signature)
// possible free // possible free
debug.FreeOSMemory() debug.FreeOSMemory()
@ -102,7 +105,7 @@ func (donut API) CreateObjectPart(bucket, key, uploadID string, partID int, cont
} }
// createObject - internal wrapper function called by CreateObjectPart // createObject - internal wrapper function called by CreateObjectPart
func (donut API) createObjectPart(bucket, key, uploadID string, partID int, contentType, expectedMD5Sum string, size int64, data io.Reader) (string, error) { func (donut API) createObjectPart(bucket, key, uploadID string, partID int, contentType, expectedMD5Sum string, size int64, data io.Reader, signature *Signature) (string, error) {
if !IsValidBucket(bucket) { if !IsValidBucket(bucket) {
return "", iodine.New(BucketNameInvalid{Bucket: bucket}, nil) return "", iodine.New(BucketNameInvalid{Bucket: bucket}, nil)
} }
@ -138,6 +141,7 @@ func (donut API) createObjectPart(bucket, key, uploadID string, partID int, cont
// calculate md5 // calculate md5
hash := md5.New() hash := md5.New()
sha256hash := sha256.New()
var err error var err error
var totalLength int64 var totalLength int64
@ -145,12 +149,8 @@ func (donut API) createObjectPart(bucket, key, uploadID string, partID int, cont
var length int var length int
byteBuffer := make([]byte, 1024*1024) byteBuffer := make([]byte, 1024*1024)
length, err = data.Read(byteBuffer) length, err = data.Read(byteBuffer)
// While hash.Write() wouldn't mind a Nil byteBuffer
// It is necessary for us to verify this and break
if length == 0 {
break
}
hash.Write(byteBuffer[0:length]) hash.Write(byteBuffer[0:length])
sha256hash.Write(byteBuffer[0:length])
ok := donut.multiPartObjects[uploadID].Append(partID, byteBuffer[0:length]) ok := donut.multiPartObjects[uploadID].Append(partID, byteBuffer[0:length])
if !ok { if !ok {
return "", iodine.New(InternalError{}, nil) return "", iodine.New(InternalError{}, nil)
@ -174,6 +174,17 @@ func (donut API) createObjectPart(bucket, key, uploadID string, partID int, cont
return "", iodine.New(BadDigest{}, nil) return "", iodine.New(BadDigest{}, nil)
} }
} }
if signature != nil {
ok, err := signature.DoesSignatureMatch(hex.EncodeToString(sha256hash.Sum(nil)))
if err != nil {
return "", iodine.New(err, nil)
}
if !ok {
return "", iodine.New(SignatureDoesNotMatch{}, nil)
}
}
newPart := PartMetadata{ newPart := PartMetadata{
PartNumber: partID, PartNumber: partID,
LastModified: time.Now().UTC(), LastModified: time.Now().UTC(),
@ -200,7 +211,7 @@ func (donut API) cleanupMultipartSession(bucket, key, uploadID string) {
} }
// CompleteMultipartUpload - complete a multipart upload and persist the data // CompleteMultipartUpload - complete a multipart upload and persist the data
func (donut API) CompleteMultipartUpload(bucket, key, uploadID string, parts map[int]string) (ObjectMetadata, error) { func (donut API) CompleteMultipartUpload(bucket, key, uploadID string, data io.Reader, signature *Signature) (ObjectMetadata, error) {
donut.lock.Lock() donut.lock.Lock()
if !IsValidBucket(bucket) { if !IsValidBucket(bucket) {
@ -221,11 +232,36 @@ func (donut API) CompleteMultipartUpload(bucket, key, uploadID string, parts map
donut.lock.Unlock() donut.lock.Unlock()
return ObjectMetadata{}, iodine.New(InvalidUploadID{UploadID: uploadID}, nil) return ObjectMetadata{}, iodine.New(InvalidUploadID{UploadID: uploadID}, nil)
} }
partBytes, err := ioutil.ReadAll(data)
if err != nil {
donut.lock.Unlock()
return ObjectMetadata{}, iodine.New(err, nil)
}
if signature != nil {
ok, err := signature.DoesSignatureMatch(hex.EncodeToString(sha256.Sum256(partBytes)[:]))
if err != nil {
donut.lock.Unlock()
return ObjectMetadata{}, iodine.New(err, nil)
}
if !ok {
donut.lock.Unlock()
return ObjectMetadata{}, iodine.New(SignatureDoesNotMatch{}, nil)
}
}
parts := &CompleteMultipartUpload{}
if err := xml.Unmarshal(partBytes, parts); err != nil {
donut.lock.Unlock()
return ObjectMetadata{}, iodine.New(MalformedXML{}, nil)
}
if !sort.IsSorted(completedParts(parts.Part)) {
donut.lock.Unlock()
return ObjectMetadata{}, iodine.New(InvalidPartOrder{}, nil)
}
var size int64 var size int64
var fullObject bytes.Buffer var fullObject bytes.Buffer
for i := 1; i <= len(parts); i++ { for i := 1; i <= len(parts.Part); i++ {
recvMD5 := parts[i] recvMD5 := parts.Part[i-1].ETag
object, ok := donut.multiPartObjects[uploadID].Get(i) object, ok := donut.multiPartObjects[uploadID].Get(parts.Part[i-1].PartNumber)
if ok == false { if ok == false {
donut.lock.Unlock() donut.lock.Unlock()
return ObjectMetadata{}, iodine.New(errors.New("missing part: "+strconv.Itoa(i)), nil) return ObjectMetadata{}, iodine.New(errors.New("missing part: "+strconv.Itoa(i)), nil)

View File

@ -100,7 +100,7 @@ func (api Minio) ListMultipartUploadsHandler(w http.ResponseWriter, req *http.Re
case nil: // success case nil: // success
{ {
// generate response // generate response
response := generateListMultipartUploadsResult(bucket, resources) response := generateListMultipartUploadsResponse(bucket, resources)
encodedSuccessResponse := encodeSuccessResponse(response, acceptsContentType) encodedSuccessResponse := encodeSuccessResponse(response, acceptsContentType)
// write headers // write headers
setCommonHeaders(w, getContentTypeString(acceptsContentType), len(encodedSuccessResponse)) setCommonHeaders(w, getContentTypeString(acceptsContentType), len(encodedSuccessResponse))

View File

@ -62,6 +62,14 @@ type ListObjectsResponse struct {
Prefix string Prefix string
} }
// Part container for part metadata
type Part struct {
PartNumber int
ETag string
LastModified string
Size int64
}
// ListPartsResponse - format for list parts response // ListPartsResponse - format for list parts response
type ListPartsResponse struct { type ListPartsResponse struct {
XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListPartsResult" json:"-"` XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListPartsResult" json:"-"`
@ -134,14 +142,6 @@ type Bucket struct {
CreationDate string CreationDate string
} }
// Part container for part metadata
type Part struct {
PartNumber int
ETag string
LastModified string
Size int64
}
// Object container for object metadata // Object container for object metadata
type Object struct { type Object struct {
ETag string ETag string
@ -164,8 +164,8 @@ type Owner struct {
DisplayName string DisplayName string
} }
// InitiateMultipartUploadResult container for InitiateMultiPartUpload response, provides uploadID to start MultiPart upload // InitiateMultipartUploadResponse container for InitiateMultiPartUpload response, provides uploadID to start MultiPart upload
type InitiateMultipartUploadResult struct { type InitiateMultipartUploadResponse struct {
XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ InitiateMultipartUploadResult" json:"-"` XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ InitiateMultipartUploadResult" json:"-"`
Bucket string Bucket string
@ -173,20 +173,8 @@ type InitiateMultipartUploadResult struct {
UploadID string `xml:"UploadId"` UploadID string `xml:"UploadId"`
} }
// completedParts is a sortable interface for Part slice // CompleteMultipartUploadResponse container for completed multipart upload response
type completedParts []Part type CompleteMultipartUploadResponse struct {
func (a completedParts) Len() int { return len(a) }
func (a completedParts) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a completedParts) Less(i, j int) bool { return a[i].PartNumber < a[j].PartNumber }
// CompleteMultipartUpload container for completing multipart upload
type CompleteMultipartUpload struct {
Part []Part
}
// CompleteMultipartUploadResult container for completed multipart upload response
type CompleteMultipartUploadResult struct {
XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ CompleteMultipartUploadResult" json:"-"` XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ CompleteMultipartUploadResult" json:"-"`
Location string Location string

View File

@ -18,11 +18,8 @@ package api
import ( import (
"net/http" "net/http"
"sort"
"strconv" "strconv"
"encoding/xml"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/minio/minio/pkg/donut" "github.com/minio/minio/pkg/donut"
"github.com/minio/minio/pkg/iodine" "github.com/minio/minio/pkg/iodine"
@ -277,7 +274,7 @@ func (api Minio) NewMultipartUploadHandler(w http.ResponseWriter, req *http.Requ
switch iodine.ToError(err).(type) { switch iodine.ToError(err).(type) {
case nil: case nil:
{ {
response := generateInitiateMultipartUploadResult(bucket, object, uploadID) response := generateInitiateMultipartUploadResponse(bucket, object, uploadID)
encodedSuccessResponse := encodeSuccessResponse(response, acceptsContentType) encodedSuccessResponse := encodeSuccessResponse(response, acceptsContentType)
// write headers // write headers
setCommonHeaders(w, getContentTypeString(acceptsContentType), len(encodedSuccessResponse)) setCommonHeaders(w, getContentTypeString(acceptsContentType), len(encodedSuccessResponse))
@ -346,7 +343,18 @@ func (api Minio) PutObjectPartHandler(w http.ResponseWriter, req *http.Request)
writeErrorResponse(w, req, InvalidPart, acceptsContentType, req.URL.Path) writeErrorResponse(w, req, InvalidPart, acceptsContentType, req.URL.Path)
} }
calculatedMD5, err := api.Donut.CreateObjectPart(bucket, object, uploadID, partID, "", md5, sizeInt64, req.Body) var signature *donut.Signature
if _, ok := req.Header["Authorization"]; ok {
// Init signature V4 verification
var err error
signature, err = InitSignatureV4(req)
if err != nil {
writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
return
}
}
calculatedMD5, err := api.Donut.CreateObjectPart(bucket, object, uploadID, partID, "", md5, sizeInt64, req.Body, signature)
switch iodine.ToError(err).(type) { switch iodine.ToError(err).(type) {
case nil: case nil:
w.Header().Set("ETag", calculatedMD5) w.Header().Set("ETag", calculatedMD5)
@ -357,6 +365,8 @@ func (api Minio) PutObjectPartHandler(w http.ResponseWriter, req *http.Request)
writeErrorResponse(w, req, MethodNotAllowed, acceptsContentType, req.URL.Path) writeErrorResponse(w, req, MethodNotAllowed, acceptsContentType, req.URL.Path)
case donut.BadDigest: case donut.BadDigest:
writeErrorResponse(w, req, BadDigest, acceptsContentType, req.URL.Path) writeErrorResponse(w, req, BadDigest, acceptsContentType, req.URL.Path)
case donut.SignatureDoesNotMatch:
writeErrorResponse(w, req, SignatureDoesNotMatch, acceptsContentType, req.URL.Path)
case donut.IncompleteBody: case donut.IncompleteBody:
writeErrorResponse(w, req, IncompleteBody, acceptsContentType, req.URL.Path) writeErrorResponse(w, req, IncompleteBody, acceptsContentType, req.URL.Path)
case donut.EntityTooLarge: case donut.EntityTooLarge:
@ -433,7 +443,7 @@ func (api Minio) ListObjectPartsHandler(w http.ResponseWriter, req *http.Request
switch iodine.ToError(err).(type) { switch iodine.ToError(err).(type) {
case nil: case nil:
{ {
response := generateListPartsResult(objectResourcesMetadata) response := generateListPartsResponse(objectResourcesMetadata)
encodedSuccessResponse := encodeSuccessResponse(response, acceptsContentType) encodedSuccessResponse := encodeSuccessResponse(response, acceptsContentType)
// write headers // write headers
setCommonHeaders(w, getContentTypeString(acceptsContentType), len(encodedSuccessResponse)) setCommonHeaders(w, getContentTypeString(acceptsContentType), len(encodedSuccessResponse))
@ -464,35 +474,27 @@ func (api Minio) CompleteMultipartUploadHandler(w http.ResponseWriter, req *http
return return
} }
decoder := xml.NewDecoder(req.Body)
parts := &CompleteMultipartUpload{}
err := decoder.Decode(parts)
if err != nil {
log.Error.Println(iodine.New(err, nil))
writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
return
}
if !sort.IsSorted(completedParts(parts.Part)) {
writeErrorResponse(w, req, InvalidPartOrder, acceptsContentType, req.URL.Path)
return
}
vars := mux.Vars(req) vars := mux.Vars(req)
bucket := vars["bucket"] bucket := vars["bucket"]
object := vars["object"] object := vars["object"]
objectResourcesMetadata := getObjectResources(req.URL.Query()) objectResourcesMetadata := getObjectResources(req.URL.Query())
partMap := make(map[int]string) var signature *donut.Signature
for _, part := range parts.Part { if _, ok := req.Header["Authorization"]; ok {
partMap[part.PartNumber] = part.ETag // Init signature V4 verification
var err error
signature, err = InitSignatureV4(req)
if err != nil {
writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
return
} }
}
metadata, err := api.Donut.CompleteMultipartUpload(bucket, object, objectResourcesMetadata.UploadID, partMap) metadata, err := api.Donut.CompleteMultipartUpload(bucket, object, objectResourcesMetadata.UploadID, req.Body, signature)
switch iodine.ToError(err).(type) { switch iodine.ToError(err).(type) {
case nil: case nil:
{ {
response := generateCompleteMultpartUploadResult(bucket, object, "", metadata.MD5Sum) response := generateCompleteMultpartUploadResponse(bucket, object, "", metadata.MD5Sum)
encodedSuccessResponse := encodeSuccessResponse(response, acceptsContentType) encodedSuccessResponse := encodeSuccessResponse(response, acceptsContentType)
// write headers // write headers
setCommonHeaders(w, getContentTypeString(acceptsContentType), len(encodedSuccessResponse)) setCommonHeaders(w, getContentTypeString(acceptsContentType), len(encodedSuccessResponse))
@ -501,6 +503,16 @@ func (api Minio) CompleteMultipartUploadHandler(w http.ResponseWriter, req *http
} }
case donut.InvalidUploadID: case donut.InvalidUploadID:
writeErrorResponse(w, req, NoSuchUpload, acceptsContentType, req.URL.Path) writeErrorResponse(w, req, NoSuchUpload, acceptsContentType, req.URL.Path)
case donut.InvalidPartOrder:
writeErrorResponse(w, req, InvalidPartOrder, acceptsContentType, req.URL.Path)
case donut.MissingDateHeader:
writeErrorResponse(w, req, RequestTimeTooSkewed, acceptsContentType, req.URL.Path)
case donut.SignatureDoesNotMatch:
writeErrorResponse(w, req, SignatureDoesNotMatch, acceptsContentType, req.URL.Path)
case donut.IncompleteBody:
writeErrorResponse(w, req, IncompleteBody, acceptsContentType, req.URL.Path)
case donut.MalformedXML:
writeErrorResponse(w, req, MalformedXML, acceptsContentType, req.URL.Path)
default: default:
log.Error.Println(iodine.New(err, nil)) log.Error.Println(iodine.New(err, nil))
writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path) writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)

View File

@ -111,18 +111,18 @@ func generateListObjectsResponse(bucket string, objects []donut.ObjectMetadata,
return data return data
} }
// generateInitiateMultipartUploadResult // generateInitiateMultipartUploadResponse
func generateInitiateMultipartUploadResult(bucket, key, uploadID string) InitiateMultipartUploadResult { func generateInitiateMultipartUploadResponse(bucket, key, uploadID string) InitiateMultipartUploadResponse {
return InitiateMultipartUploadResult{ return InitiateMultipartUploadResponse{
Bucket: bucket, Bucket: bucket,
Key: key, Key: key,
UploadID: uploadID, UploadID: uploadID,
} }
} }
// generateCompleteMultipartUploadResult // generateCompleteMultipartUploadResponse
func generateCompleteMultpartUploadResult(bucket, key, location, etag string) CompleteMultipartUploadResult { func generateCompleteMultpartUploadResponse(bucket, key, location, etag string) CompleteMultipartUploadResponse {
return CompleteMultipartUploadResult{ return CompleteMultipartUploadResponse{
Location: location, Location: location,
Bucket: bucket, Bucket: bucket,
Key: key, Key: key,
@ -131,7 +131,7 @@ func generateCompleteMultpartUploadResult(bucket, key, location, etag string) Co
} }
// generateListPartsResult // generateListPartsResult
func generateListPartsResult(objectMetadata donut.ObjectResourcesMetadata) ListPartsResponse { func generateListPartsResponse(objectMetadata donut.ObjectResourcesMetadata) ListPartsResponse {
// TODO - support EncodingType in xml decoding // TODO - support EncodingType in xml decoding
listPartsResponse := ListPartsResponse{} listPartsResponse := ListPartsResponse{}
listPartsResponse.Bucket = objectMetadata.Bucket listPartsResponse.Bucket = objectMetadata.Bucket
@ -160,8 +160,8 @@ func generateListPartsResult(objectMetadata donut.ObjectResourcesMetadata) ListP
return listPartsResponse return listPartsResponse
} }
// generateListMultipartUploadsResult // generateListMultipartUploadsResponse
func generateListMultipartUploadsResult(bucket string, metadata donut.BucketMultipartResourcesMetadata) ListMultipartUploadsResponse { func generateListMultipartUploadsResponse(bucket string, metadata donut.BucketMultipartResourcesMetadata) ListMultipartUploadsResponse {
listMultipartUploadsResponse := ListMultipartUploadsResponse{} listMultipartUploadsResponse := ListMultipartUploadsResponse{}
listMultipartUploadsResponse.Bucket = bucket listMultipartUploadsResponse.Bucket = bucket
listMultipartUploadsResponse.Delimiter = metadata.Delimiter listMultipartUploadsResponse.Delimiter = metadata.Delimiter

View File

@ -27,6 +27,7 @@ import (
"net/http/httptest" "net/http/httptest"
. "github.com/minio/check" . "github.com/minio/check"
"github.com/minio/minio/pkg/donut"
"github.com/minio/minio/pkg/server/api" "github.com/minio/minio/pkg/server/api"
) )
@ -627,7 +628,7 @@ func (s *MyAPIDonutCacheSuite) TestObjectMultipartAbort(c *C) {
c.Assert(response.StatusCode, Equals, http.StatusOK) c.Assert(response.StatusCode, Equals, http.StatusOK)
decoder := xml.NewDecoder(response.Body) decoder := xml.NewDecoder(response.Body)
newResponse := &api.InitiateMultipartUploadResult{} newResponse := &api.InitiateMultipartUploadResponse{}
err = decoder.Decode(newResponse) err = decoder.Decode(newResponse)
c.Assert(err, IsNil) c.Assert(err, IsNil)
@ -672,7 +673,7 @@ func (s *MyAPIDonutCacheSuite) TestBucketMultipartList(c *C) {
c.Assert(response.StatusCode, Equals, http.StatusOK) c.Assert(response.StatusCode, Equals, http.StatusOK)
decoder := xml.NewDecoder(response.Body) decoder := xml.NewDecoder(response.Body)
newResponse := &api.InitiateMultipartUploadResult{} newResponse := &api.InitiateMultipartUploadResponse{}
err = decoder.Decode(newResponse) err = decoder.Decode(newResponse)
c.Assert(err, IsNil) c.Assert(err, IsNil)
@ -723,7 +724,7 @@ func (s *MyAPIDonutCacheSuite) TestObjectMultipartList(c *C) {
c.Assert(response.StatusCode, Equals, http.StatusOK) c.Assert(response.StatusCode, Equals, http.StatusOK)
decoder := xml.NewDecoder(response.Body) decoder := xml.NewDecoder(response.Body)
newResponse := &api.InitiateMultipartUploadResult{} newResponse := &api.InitiateMultipartUploadResponse{}
err = decoder.Decode(newResponse) err = decoder.Decode(newResponse)
c.Assert(err, IsNil) c.Assert(err, IsNil)
@ -771,7 +772,7 @@ func (s *MyAPIDonutCacheSuite) TestObjectMultipart(c *C) {
c.Assert(response.StatusCode, Equals, http.StatusOK) c.Assert(response.StatusCode, Equals, http.StatusOK)
decoder := xml.NewDecoder(response.Body) decoder := xml.NewDecoder(response.Body)
newResponse := &api.InitiateMultipartUploadResult{} newResponse := &api.InitiateMultipartUploadResponse{}
err = decoder.Decode(newResponse) err = decoder.Decode(newResponse)
c.Assert(err, IsNil) c.Assert(err, IsNil)
@ -795,8 +796,8 @@ func (s *MyAPIDonutCacheSuite) TestObjectMultipart(c *C) {
c.Assert(response2.StatusCode, Equals, http.StatusOK) c.Assert(response2.StatusCode, Equals, http.StatusOK)
// complete multipart upload // complete multipart upload
completeUploads := &api.CompleteMultipartUpload{ completeUploads := &donut.CompleteMultipartUpload{
Part: []api.Part{ Part: []donut.CompletePart{
{ {
PartNumber: 1, PartNumber: 1,
ETag: response1.Header.Get("ETag"), ETag: response1.Header.Get("ETag"),

View File

@ -664,7 +664,7 @@ func (s *MyAPIDonutSuite) TestObjectMultipartAbort(c *C) {
c.Assert(response.StatusCode, Equals, http.StatusOK) c.Assert(response.StatusCode, Equals, http.StatusOK)
decoder := xml.NewDecoder(response.Body) decoder := xml.NewDecoder(response.Body)
newResponse := &api.InitiateMultipartUploadResult{} newResponse := &api.InitiateMultipartUploadResponse{}
err = decoder.Decode(newResponse) err = decoder.Decode(newResponse)
c.Assert(err, IsNil) c.Assert(err, IsNil)
@ -709,7 +709,7 @@ func (s *MyAPIDonutSuite) TestBucketMultipartList(c *C) {
c.Assert(response.StatusCode, Equals, http.StatusOK) c.Assert(response.StatusCode, Equals, http.StatusOK)
decoder := xml.NewDecoder(response.Body) decoder := xml.NewDecoder(response.Body)
newResponse := &api.InitiateMultipartUploadResult{} newResponse := &api.InitiateMultipartUploadResponse{}
err = decoder.Decode(newResponse) err = decoder.Decode(newResponse)
c.Assert(err, IsNil) c.Assert(err, IsNil)
@ -760,7 +760,7 @@ func (s *MyAPIDonutSuite) TestObjectMultipartList(c *C) {
c.Assert(response.StatusCode, Equals, http.StatusOK) c.Assert(response.StatusCode, Equals, http.StatusOK)
decoder := xml.NewDecoder(response.Body) decoder := xml.NewDecoder(response.Body)
newResponse := &api.InitiateMultipartUploadResult{} newResponse := &api.InitiateMultipartUploadResponse{}
err = decoder.Decode(newResponse) err = decoder.Decode(newResponse)
c.Assert(err, IsNil) c.Assert(err, IsNil)
@ -808,7 +808,7 @@ func (s *MyAPIDonutSuite) TestObjectMultipart(c *C) {
c.Assert(response.StatusCode, Equals, http.StatusOK) c.Assert(response.StatusCode, Equals, http.StatusOK)
decoder := xml.NewDecoder(response.Body) decoder := xml.NewDecoder(response.Body)
newResponse := &api.InitiateMultipartUploadResult{} newResponse := &api.InitiateMultipartUploadResponse{}
err = decoder.Decode(newResponse) err = decoder.Decode(newResponse)
c.Assert(err, IsNil) c.Assert(err, IsNil)
@ -832,8 +832,8 @@ func (s *MyAPIDonutSuite) TestObjectMultipart(c *C) {
c.Assert(response2.StatusCode, Equals, http.StatusOK) c.Assert(response2.StatusCode, Equals, http.StatusOK)
// complete multipart upload // complete multipart upload
completeUploads := &api.CompleteMultipartUpload{ completeUploads := &donut.CompleteMultipartUpload{
Part: []api.Part{ Part: []donut.CompletePart{
{ {
PartNumber: 1, PartNumber: 1,
ETag: response1.Header.Get("ETag"), ETag: response1.Header.Get("ETag"),