Skip to content

Commit f7022e7

Browse files
authored
blobstore: Support objectLock in GCP and AWS (#242)
1 parent a591aeb commit f7022e7

File tree

23 files changed

+2223
-43
lines changed

23 files changed

+2223
-43
lines changed

blob/blob-ali/src/main/java/com/salesforce/multicloudj/blob/ali/AliBlobStore.java

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,11 @@
4444
import com.salesforce.multicloudj.blob.driver.UploadPartResponse;
4545
import com.salesforce.multicloudj.blob.driver.UploadRequest;
4646
import com.salesforce.multicloudj.blob.driver.UploadResponse;
47+
import com.salesforce.multicloudj.blob.driver.ObjectLockInfo;
4748
import com.salesforce.multicloudj.common.ali.AliConstants;
4849
import com.salesforce.multicloudj.common.exceptions.InvalidArgumentException;
4950
import com.salesforce.multicloudj.common.exceptions.SubstrateSdkException;
51+
import com.salesforce.multicloudj.common.exceptions.UnSupportedOperationException;
5052
import com.salesforce.multicloudj.common.exceptions.UnknownException;
5153
import com.salesforce.multicloudj.common.provider.Provider;
5254
import lombok.Getter;
@@ -448,6 +450,30 @@ protected void doSetTags(String key, Map<String, String> tags) {
448450
ossClient.setObjectTagging(bucket, key, new TagSet(tags));
449451
}
450452

453+
/**
454+
* {@inheritdoc}
455+
*/
456+
@Override
457+
public ObjectLockInfo getObjectLock(String key, String versionId) {
458+
throw new UnSupportedOperationException("Alibaba OSS does not support object lock");
459+
}
460+
461+
/**
462+
* {@inheritdoc}
463+
*/
464+
@Override
465+
public void updateObjectRetention(String key, String versionId, java.time.Instant retainUntilDate) {
466+
throw new UnSupportedOperationException("Alibaba OSS does not support object lock/retention");
467+
}
468+
469+
/**
470+
* {@inheritdoc}
471+
*/
472+
@Override
473+
public void updateLegalHold(String key, String versionId, boolean legalHold) {
474+
throw new UnSupportedOperationException("Alibaba OSS does not support object lock/legal hold");
475+
}
476+
451477
/**
452478
* Generates a presigned URL for uploading/downloading blobs
453479
* @param request The PresignedUrlRequest
@@ -465,10 +491,7 @@ protected URL doGeneratePresignedUrl(PresignedUrlRequest request) {
465491
}
466492

467493
/**
468-
* Determines if an object exists for a given key/versionId
469-
* @param key Name of the blob to check
470-
* @param versionId The version of the blob to check
471-
* @return Returns true if the object exists. Returns false if it doesn't exist.
494+
* {@inheritdoc}
472495
*/
473496
@Override
474497
protected boolean doDoesObjectExist(String key, String versionId) {

blob/blob-ali/src/test/java/com/salesforce/multicloudj/blob/ali/AliBlobStoreTest.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import com.salesforce.multicloudj.common.ali.AliConstants;
5555
import com.salesforce.multicloudj.common.exceptions.InvalidArgumentException;
5656
import com.salesforce.multicloudj.common.exceptions.UnAuthorizedException;
57+
import com.salesforce.multicloudj.common.exceptions.UnSupportedOperationException;
5758
import com.salesforce.multicloudj.common.exceptions.UnknownException;
5859
import com.salesforce.multicloudj.sts.model.CredentialsOverrider;
5960
import com.salesforce.multicloudj.sts.model.CredentialsType;
@@ -874,4 +875,41 @@ private OSSObject buildTestGetObjectResult(Instant now) {
874875
doReturn(inputStream).when(ossObject).getObjectContent();
875876
return ossObject;
876877
}
878+
879+
@Test
880+
void testGetObjectLock_ThrowsUnsupportedException() {
881+
// Given
882+
String key = "test-key";
883+
String versionId = "version-1";
884+
885+
// When/Then
886+
assertThrows(UnSupportedOperationException.class, () -> {
887+
ali.getObjectLock(key, versionId);
888+
});
889+
}
890+
891+
@Test
892+
void testUpdateObjectRetention_ThrowsUnsupportedException() {
893+
// Given
894+
String key = "test-key";
895+
String versionId = "version-1";
896+
Instant retainUntil = Instant.now().plusSeconds(3600);
897+
898+
// When/Then
899+
assertThrows(UnSupportedOperationException.class, () -> {
900+
ali.updateObjectRetention(key, versionId, retainUntil);
901+
});
902+
}
903+
904+
@Test
905+
void testUpdateLegalHold_ThrowsUnsupportedException() {
906+
// Given
907+
String key = "test-key";
908+
String versionId = "version-1";
909+
910+
// When/Then
911+
assertThrows(UnSupportedOperationException.class, () -> {
912+
ali.updateLegalHold(key, versionId, true);
913+
});
914+
}
877915
}

blob/blob-aws/src/main/java/com/salesforce/multicloudj/blob/aws/AwsBlobClient.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,17 @@ protected void doCreateBucket(String bucketName) {
6161
}
6262

6363
/**
64-
* Constructs an instance of {@link AwsBlobClient} using the provided builder and S3 client.
65-
*
64+
* Constructs an instance of {@link AwsBlobClient} using the provided builder and S3 client.
65+
*
6666
* @param builder the builder used to configure this client.
6767
*/
6868
public AwsBlobClient(Builder builder) {
6969
this(builder, buildS3Client(builder));
7070
}
7171

7272
/**
73-
* Constructs an instance of {@link AwsBlobClient} using the provided builder and S3 client.
74-
*
73+
* Constructs an instance of {@link AwsBlobClient} using the provided builder and S3 client.
74+
*
7575
* @param builder the builder used to configure this client.
7676
* @param s3Client the S3 client used to communicate with the S3 service.
7777
*/

blob/blob-aws/src/main/java/com/salesforce/multicloudj/blob/aws/AwsBlobStore.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,16 @@
2121
import com.salesforce.multicloudj.blob.driver.PresignedUrlRequest;
2222
import com.salesforce.multicloudj.blob.driver.UploadPartResponse;
2323
import com.salesforce.multicloudj.blob.driver.UploadRequest;
24+
import com.salesforce.multicloudj.blob.driver.ObjectLockInfo;
2425
import com.salesforce.multicloudj.blob.driver.UploadResponse;
2526
import com.salesforce.multicloudj.common.aws.AwsConstants;
2627
import com.salesforce.multicloudj.common.aws.CredentialsProvider;
28+
import com.salesforce.multicloudj.common.exceptions.FailedPreconditionException;
2729
import com.salesforce.multicloudj.common.exceptions.InvalidArgumentException;
30+
import com.salesforce.multicloudj.common.exceptions.ResourceNotFoundException;
2831
import com.salesforce.multicloudj.common.exceptions.SubstrateSdkException;
2932
import com.salesforce.multicloudj.common.exceptions.UnknownException;
33+
import com.salesforce.multicloudj.common.exceptions.UnSupportedOperationException;
3034
import lombok.Getter;
3135
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
3236
import software.amazon.awssdk.awscore.exception.AwsServiceException;
@@ -60,6 +64,9 @@
6064
import software.amazon.awssdk.services.s3.model.ListPartsResponse;
6165
import software.amazon.awssdk.services.s3.model.Part;
6266
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
67+
import software.amazon.awssdk.services.s3.model.GetObjectLegalHoldResponse;
68+
import software.amazon.awssdk.services.s3.model.GetObjectRetentionResponse;
69+
import software.amazon.awssdk.services.s3.model.ObjectLockRetentionMode;
6370
import software.amazon.awssdk.services.s3.model.PutObjectResponse;
6471
import software.amazon.awssdk.services.s3.model.S3Exception;
6572
import software.amazon.awssdk.services.s3.model.Tag;
@@ -72,6 +79,7 @@
7279
import java.net.URL;
7380
import java.nio.file.Path;
7481
import java.time.Duration;
82+
import java.time.Instant;
7583
import java.util.Collection;
7684
import java.util.Comparator;
7785
import java.util.Iterator;
@@ -503,6 +511,53 @@ protected boolean doDoesBucketExist() {
503511
}
504512
}
505513

514+
/**
515+
* Gets object lock configuration for a blob.
516+
*/
517+
@Override
518+
public ObjectLockInfo getObjectLock(String key, String versionId) {
519+
GetObjectRetentionResponse retentionResponse = s3Client.getObjectRetention(
520+
transformer.toGetObjectRetentionRequest(key, versionId));
521+
GetObjectLegalHoldResponse legalHoldResponse = s3Client.getObjectLegalHold(
522+
transformer.toGetObjectLegalHoldRequest(key, versionId));
523+
return transformer.toObjectLockInfo(retentionResponse, legalHoldResponse);
524+
}
525+
526+
/**
527+
* Updates object retention date.
528+
* Only works if object is in GOVERNANCE mode. COMPLIANCE mode objects cannot be updated.
529+
*/
530+
@Override
531+
public void updateObjectRetention(String key, String versionId, Instant retainUntilDate) {
532+
// First get current retention to check mode
533+
GetObjectRetentionResponse currentRetention = s3Client.getObjectRetention(
534+
transformer.toGetObjectRetentionRequest(key, versionId));
535+
536+
if (currentRetention == null || currentRetention.retention() == null) {
537+
throw new FailedPreconditionException(
538+
"Object does not have retention configured. Cannot update retention.");
539+
}
540+
541+
ObjectLockRetentionMode currentMode = currentRetention.retention().mode();
542+
543+
if (currentMode == ObjectLockRetentionMode.COMPLIANCE) {
544+
throw new FailedPreconditionException(
545+
"Cannot update retention for objects in COMPLIANCE mode. " +
546+
"Only GOVERNANCE mode objects can have their retention updated.");
547+
}
548+
549+
s3Client.putObjectRetention(transformer.toPutObjectRetentionRequest(
550+
key, versionId, currentMode, retainUntilDate));
551+
}
552+
553+
/**
554+
* Updates legal hold status on an object.
555+
*/
556+
@Override
557+
public void updateLegalHold(String key, String versionId, boolean legalHold) {
558+
s3Client.putObjectLegalHold(transformer.toPutObjectLegalHoldRequest(key, versionId, legalHold));
559+
}
560+
506561
/**
507562
* Closes the underlying S3 client and releases any resources.
508563
*/

0 commit comments

Comments
 (0)