Skip to content

Commit 093cb87

Browse files
authoredJan 6, 2025··
fix: update request handling of gRPC based CopyWriter (#2858)
1 parent 46e964f commit 093cb87

File tree

3 files changed

+55
-30
lines changed

3 files changed

+55
-30
lines changed
 

‎google-cloud-storage/src/main/java/com/google/cloud/storage/GapicCopyWriter.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ final class GapicCopyWriter extends CopyWriter {
3131
private final GrpcStorageOptions options;
3232
private final UnaryCallable<RewriteObjectRequest, RewriteResponse> callable;
3333
private final ResultRetryAlgorithm<?> alg;
34+
private final RewriteObjectRequest originalRequest;
3435
private final RewriteResponse initialResponse;
3536

3637
private RewriteResponse mostRecentResponse;
@@ -39,13 +40,15 @@ final class GapicCopyWriter extends CopyWriter {
3940
GrpcStorageImpl storage,
4041
UnaryCallable<RewriteObjectRequest, RewriteResponse> callable,
4142
ResultRetryAlgorithm<?> alg,
43+
RewriteObjectRequest originalRequest,
4244
RewriteResponse initialResponse) {
4345
this.storage = storage;
4446
this.options = storage.getOptions();
4547
this.callable = callable;
4648
this.alg = alg;
4749
this.initialResponse = initialResponse;
4850
this.mostRecentResponse = initialResponse;
51+
this.originalRequest = originalRequest;
4952
}
5053

5154
@Override
@@ -76,9 +79,7 @@ public long getTotalBytesCopied() {
7679
public void copyChunk() {
7780
if (!isDone()) {
7881
RewriteObjectRequest req =
79-
RewriteObjectRequest.newBuilder()
80-
.setRewriteToken(mostRecentResponse.getRewriteToken())
81-
.build();
82+
originalRequest.toBuilder().setRewriteToken(mostRecentResponse.getRewriteToken()).build();
8283
GrpcCallContext retryContext = Retrying.newCallContext();
8384
mostRecentResponse =
8485
Retrying.run(options, alg, () -> callable.call(req, retryContext), Decoder.identity());

‎google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageImpl.java

+17-9
Original file line numberDiff line numberDiff line change
@@ -656,17 +656,24 @@ public CopyWriter copy(CopyRequest copyRequest) {
656656
RewriteObjectRequest.newBuilder()
657657
.setDestinationName(dstProto.getName())
658658
.setDestinationBucket(dstProto.getBucket())
659-
// destination_kms_key comes from dstOpts
660-
// according to the docs in the protos, it is illegal to populate the following fields,
661-
// clear them out if they are set
662-
// destination_predefined_acl comes from dstOpts
663-
// if_*_match come from srcOpts and dstOpts
664-
// copy_source_encryption_* come from srcOpts
665-
// common_object_request_params come from dstOpts
666-
.setDestination(dstProto.toBuilder().clearName().clearBucket().clearKmsKey().build())
667659
.setSourceBucket(srcProto.getBucket())
668660
.setSourceObject(srcProto.getName());
669661

662+
// according to the docs in the protos, it is illegal to populate the following fields,
663+
// clear them out if they are set
664+
// * destination_kms_key comes from dstOpts
665+
// * destination_predefined_acl comes from dstOpts
666+
// * if_*_match come from srcOpts and dstOpts
667+
// * copy_source_encryption_* come from srcOpts
668+
// * common_object_request_params come from dstOpts
669+
Object cleanedDst = dstProto.toBuilder().clearName().clearBucket().clearKmsKey().build();
670+
// only set the destination if it is not equal to the default instance
671+
// otherwise we will clobber default values populated in the gcs server side for the object
672+
// metadata
673+
if (!cleanedDst.equals(Object.getDefaultInstance())) {
674+
b.setDestination(cleanedDst);
675+
}
676+
670677
if (src.getGeneration() != null) {
671678
b.setSourceGeneration(src.getGeneration());
672679
}
@@ -685,7 +692,8 @@ public CopyWriter copy(CopyRequest copyRequest) {
685692
getOptions(),
686693
retryAlgorithmManager.getFor(req),
687694
() -> callable.call(req, retryContext),
688-
(resp) -> new GapicCopyWriter(this, callable, retryAlgorithmManager.idempotent(), resp));
695+
(resp) ->
696+
new GapicCopyWriter(this, callable, retryAlgorithmManager.idempotent(), req, resp));
689697
}
690698

691699
@Override

‎google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITObjectTest.java

+34-18
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import com.google.cloud.storage.Bucket;
4040
import com.google.cloud.storage.BucketInfo;
4141
import com.google.cloud.storage.CopyWriter;
42+
import com.google.cloud.storage.DataGenerator;
4243
import com.google.cloud.storage.PackagePrivateMethodWorkarounds;
4344
import com.google.cloud.storage.Storage;
4445
import com.google.cloud.storage.Storage.BlobField;
@@ -562,7 +563,6 @@ public void testListBlobsCurrentDirectoryIncludesBothObjectsAndSyntheticDirector
562563
}
563564

564565
@Test
565-
// When gRPC support is added for matchGlob, enable this test for gRPC.
566566
public void testListBlobsWithMatchGlob() throws Exception {
567567
BucketInfo bucketInfo = BucketInfo.newBuilder(generator.randomBucketName()).build();
568568
try (TemporaryBucket tempBucket =
@@ -848,8 +848,6 @@ public void testComposeBlobFail() {
848848
}
849849

850850
@Test
851-
// Bucket attribute extration on allowlist bug b/246634709
852-
@Exclude(transports = Transport.GRPC)
853851
public void testCopyBlob() {
854852

855853
String sourceBlobName = generator.randomObjectName() + "-source";
@@ -872,8 +870,35 @@ public void testCopyBlob() {
872870
}
873871

874872
@Test
875-
// Bucket attribute extration on allowlist bug b/246634709
876-
@Exclude(transports = Transport.GRPC)
873+
public void copyBlob_classChange_multipleChunks() {
874+
875+
String sourceBlobName = generator.randomObjectName() + "-source";
876+
BlobId source = BlobId.of(bucket.getName(), sourceBlobName);
877+
ImmutableMap<String, String> metadata = ImmutableMap.of("k", "v");
878+
BlobInfo blob = BlobInfo.newBuilder(source).setMetadata(metadata).build();
879+
int _5MiB = 5 * 1024 * 1024;
880+
byte[] bytes = DataGenerator.base64Characters().genBytes(_5MiB);
881+
Blob remoteBlob = storage.create(blob, bytes);
882+
assertThat(remoteBlob).isNotNull();
883+
String targetBlobName = generator.randomObjectName() + "-target";
884+
CopyRequest req =
885+
CopyRequest.newBuilder()
886+
.setSource(source)
887+
.setTarget(
888+
BlobInfo.newBuilder(bucket, targetBlobName)
889+
// change the storage class to force GCS to copy bytes
890+
.setStorageClass(StorageClass.NEARLINE)
891+
.build(),
892+
BlobTargetOption.doesNotExist())
893+
.setMegabytesCopiedPerChunk(2L)
894+
.build();
895+
CopyWriter copyWriter = storage.copy(req);
896+
BlobInfo remoteBlob2 = copyWriter.getResult();
897+
assertThat(copyWriter.isDone()).isTrue();
898+
assertThat(remoteBlob2).isNotNull();
899+
}
900+
901+
@Test
877902
public void testCopyBlobWithPredefinedAcl() {
878903

879904
String sourceBlobName = generator.randomObjectName() + "-source";
@@ -903,8 +928,6 @@ public void testCopyBlobWithPredefinedAcl() {
903928
}
904929

905930
@Test
906-
// Bucket attribute extration on allowlist bug b/246634709
907-
@Exclude(transports = Transport.GRPC)
908931
public void testCopyBlobWithEncryptionKeys() {
909932

910933
String sourceBlobName = generator.randomObjectName() + "-source";
@@ -955,8 +978,6 @@ public void testCopyBlobWithEncryptionKeys() {
955978
}
956979

957980
@Test
958-
// Bucket attribute extration on allowlist bug b/246634709
959-
@Exclude(transports = Transport.GRPC)
960981
public void testCopyBlobUpdateMetadata() {
961982

962983
String sourceBlobName = generator.randomObjectName() + "-source";
@@ -981,9 +1002,7 @@ public void testCopyBlobUpdateMetadata() {
9811002
assertTrue(storage.delete(bucket.getName(), targetBlobName));
9821003
}
9831004

984-
// Re-enable this test when it stops failing
985-
// @Test
986-
@Exclude(transports = Transport.GRPC)
1005+
@Test
9871006
public void testCopyBlobUpdateStorageClass() {
9881007
String sourceBlobName = generator.randomObjectName() + "-source";
9891008
BlobId source = BlobId.of(bucket.getName(), sourceBlobName);
@@ -1007,8 +1026,6 @@ public void testCopyBlobUpdateStorageClass() {
10071026
}
10081027

10091028
@Test
1010-
// Bucket attribute extration on allowlist bug b/246634709
1011-
@Exclude(transports = Transport.GRPC)
10121029
public void testCopyBlobNoContentType() {
10131030

10141031
String sourceBlobName = generator.randomObjectName() + "-source";
@@ -1022,17 +1039,16 @@ public void testCopyBlobNoContentType() {
10221039
CopyWriter copyWriter = storage.copy(req);
10231040
assertEquals(bucket.getName(), copyWriter.getResult().getBucket());
10241041
assertEquals(targetBlobName, copyWriter.getResult().getName());
1025-
assertNull(copyWriter.getResult().getContentType());
1042+
assertTrue(
1043+
copyWriter.getResult().getContentType() == null
1044+
|| copyWriter.getResult().getContentType().isEmpty());
10261045
assertEquals(metadata, copyWriter.getResult().getMetadata());
10271046
assertTrue(copyWriter.isDone());
10281047
assertTrue(remoteSourceBlob.delete());
10291048
assertTrue(storage.delete(bucket.getName(), targetBlobName));
10301049
}
10311050

10321051
@Test
1033-
// Verified against testbench
1034-
// Bucket attribute extration on allowlist bug b/246634709
1035-
@Exclude(transports = Transport.GRPC)
10361052
public void testCopyBlobFail() {
10371053

10381054
String sourceBlobName = "test-copy-blob-source-fail";

0 commit comments

Comments
 (0)
Please sign in to comment.