From 069badc7e9eac06090c936c2223767a561b9e0f3 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Tue, 20 Aug 2019 07:19:22 -1000 Subject: [PATCH] Allow CopyObjectPart to work in federated setups (#8066) Fixes #8065 --- cmd/object-handlers.go | 62 ++++++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/cmd/object-handlers.go b/cmd/object-handlers.go index e292542b7..7bfab7d44 100644 --- a/cmd/object-handlers.go +++ b/cmd/object-handlers.go @@ -601,8 +601,21 @@ var getRemoteInstanceClient = func(r *http.Request, host string) (*miniogo.Core, return core, nil } +// Check if the destination bucket is on a remote site, this code only gets executed +// when federation is enabled, ie when globalDNSConfig is non 'nil'. +// +// This function is similar to isRemoteCallRequired but specifically for COPY object API +// if destination and source are same we do not need to check for destnation bucket +// to exist locally. +func isRemoteCopyRequired(ctx context.Context, srcBucket, dstBucket string, objAPI ObjectLayer) bool { + if srcBucket == dstBucket { + return false + } + return isRemoteCallRequired(ctx, dstBucket, objAPI) +} + // Check if the bucket is on a remote site, this code only gets executed when federation is enabled. -var isRemoteCallRequired = func(ctx context.Context, bucket string, objAPI ObjectLayer) bool { +func isRemoteCallRequired(ctx context.Context, bucket string, objAPI ObjectLayer) bool { if globalDNSConfig == nil { return false } @@ -787,23 +800,6 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re length = actualSize } - // Check if the destination bucket is on a remote site, this code only gets executed - // when federation is enabled, ie when globalDNSConfig is non 'nil'. - // - // This function is similar to isRemoteCallRequired but specifically for COPY object API - // if destination and source are same we do not need to check for destnation bucket - // to exist locally. - var isRemoteCopyRequired = func(ctx context.Context, srcBucket, dstBucket string, objAPI ObjectLayer) bool { - if globalDNSConfig == nil { - return false - } - if srcBucket == dstBucket { - return false - } - _, err := objAPI.GetBucketInfo(ctx, dstBucket) - return err == toObjectErr(errVolumeNotFound, dstBucket) - } - var compressMetadata map[string]string // No need to compress for remote etcd calls // Pass the decompressed stream to such calls. @@ -1591,6 +1587,36 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt return } + if isRemoteCopyRequired(ctx, srcBucket, dstBucket, objectAPI) { + var dstRecords []dns.SrvRecord + dstRecords, err = globalDNSConfig.Get(dstBucket) + if err != nil { + writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) + return + } + + // Send PutObject request to appropriate instance (in federated deployment) + client, rerr := getRemoteInstanceClient(r, getHostFromSrv(dstRecords)) + if rerr != nil { + writeErrorResponse(ctx, w, toAPIError(ctx, rerr), r.URL, guessIsBrowserReq(r)) + return + } + + partInfo, err := client.PutObjectPart(dstBucket, dstObject, uploadID, partID, + srcInfo.Reader, srcInfo.Size, "", "", dstOpts.ServerSideEncryption) + if err != nil { + writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) + return + } + + response := generateCopyObjectPartResponse(partInfo.ETag, partInfo.LastModified) + encodedSuccessResponse := encodeResponse(response) + + // Write success response. + writeSuccessResponseXML(w, encodedSuccessResponse) + return + } + actualPartSize = length var reader io.Reader