Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

S3 Blobstore: Support for buckets that don't support certain features #94

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,7 @@
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreConfigurationHelper.getBucketPrefix;
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreConfigurationHelper.getConfiguredBucket;
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreConfigurationHelper.getConfiguredExpirationInDays;
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreException.ACCESS_DENIED_CODE;
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreException.INVALID_ACCESS_KEY_ID_CODE;
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreException.SIGNATURE_DOES_NOT_MATCH_CODE;
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreException.bucketOwnershipError;
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreException.buildException;
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreException.insufficientCreatePermissionsError;
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreException.unexpectedError;
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreException.*;

/**
* Creates and deletes buckets for the {@link S3BlobStore}.
Expand Down Expand Up @@ -87,9 +81,17 @@ public void prepareStorageLocation(final BlobStoreConfiguration blobStoreConfigu
}
else {
// bucket exists, we should test that the correct lifecycle config is present
BucketLifecycleConfiguration lifecycleConfiguration = s3.getBucketLifecycleConfiguration(bucket);
if (!isExpirationLifecycleConfigurationPresent(lifecycleConfiguration, blobStoreConfiguration)) {
setBucketLifecycleConfiguration(s3, blobStoreConfiguration, lifecycleConfiguration);
try {
BucketLifecycleConfiguration lifecycleConfiguration = s3.getBucketLifecycleConfiguration(bucket);
if (!isExpirationLifecycleConfigurationPresent(lifecycleConfiguration, blobStoreConfiguration)) {
setBucketLifecycleConfiguration(s3, blobStoreConfiguration, lifecycleConfiguration);
}
} catch (AmazonS3Exception e) {
if (NOT_IMPLEMENTED_CODE.equals(e.getErrorCode())) {
log.warn("Bucket {} does not support managing lifecycle config.", bucket, e);
} else {
throw e;
}
}
}
}
Expand All @@ -103,13 +105,21 @@ public void deleteStorageLocation(final BlobStoreConfiguration blobStoreConfigur
}
else {
log.info("Not removing S3 bucket {} because it is not empty", bucket);
BucketLifecycleConfiguration lifecycleConfiguration = s3.getBucketLifecycleConfiguration(bucket);
List<Rule> nonBlobstoreRules = nonBlobstoreRules(lifecycleConfiguration, blobStoreConfiguration.getName());
if(!isEmpty(nonBlobstoreRules)) {
lifecycleConfiguration.setRules(nonBlobstoreRules);
s3.setBucketLifecycleConfiguration(bucket, lifecycleConfiguration);
} else {
s3.deleteBucketLifecycleConfiguration(bucket);
try {
BucketLifecycleConfiguration lifecycleConfiguration = s3.getBucketLifecycleConfiguration(bucket);
List<Rule> nonBlobstoreRules = nonBlobstoreRules(lifecycleConfiguration, blobStoreConfiguration.getName());
if(!isEmpty(nonBlobstoreRules)) {
lifecycleConfiguration.setRules(nonBlobstoreRules);
s3.setBucketLifecycleConfiguration(bucket, lifecycleConfiguration);
} else {
s3.deleteBucketLifecycleConfiguration(bucket);
}
} catch (AmazonS3Exception e) {
if (NOT_IMPLEMENTED_CODE.equals(e.getErrorCode())) {
log.warn("Bucket {} does not support managing lifecycle config.", bucket, e);
} else {
throw e;
}
}
}
}
Expand Down Expand Up @@ -181,11 +191,19 @@ private void setBucketLifecycleConfiguration(final AmazonS3 s3,
String bucket = getConfiguredBucket(blobStoreConfiguration);
BucketLifecycleConfiguration newLifecycleConfiguration =
makeLifecycleConfiguration(lifecycleConfiguration, blobStoreConfiguration);
if (newLifecycleConfiguration != null) {
s3.setBucketLifecycleConfiguration(bucket, newLifecycleConfiguration);
}
else if (lifecycleConfiguration != null && !lifecycleConfiguration.getRules().isEmpty()) {
s3.deleteBucketLifecycleConfiguration(bucket);
try {
if (newLifecycleConfiguration != null) {
s3.setBucketLifecycleConfiguration(bucket, newLifecycleConfiguration);
}
else if (lifecycleConfiguration != null && !lifecycleConfiguration.getRules().isEmpty()) {
s3.deleteBucketLifecycleConfiguration(bucket);
}
} catch (AmazonS3Exception e) {
if (NOT_IMPLEMENTED_CODE.equals(e.getErrorCode())) {
log.warn("Bucket {} does not support managing lifecycle config.", bucket, e);
} else {
throw e;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
import static org.sonatype.nexus.blobstore.api.OperationType.DOWNLOAD;
import static org.sonatype.nexus.blobstore.api.OperationType.UPLOAD;
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreConfigurationHelper.getConfiguredExpirationInDays;
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreException.NOT_IMPLEMENTED_CODE;
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreException.buildException;
import static org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport.State.FAILED;
import static org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport.State.NEW;
Expand Down Expand Up @@ -497,11 +498,19 @@ else if (blobAttributes.isDeleted()) {
blobAttributes.setDeletedDateTime(new DateTime());
blobAttributes.store();

// soft delete is implemented using an S3 lifecycle that sets expiration on objects with DELETED_TAG
// tag the bytes
s3.setObjectTagging(tagAsDeleted(contentPath(blobId)));
// tag the attributes
s3.setObjectTagging(tagAsDeleted(attributePath(blobId)));
try {
// soft delete is implemented using an S3 lifecycle that sets expiration on objects with DELETED_TAG
// tag the bytes
s3.setObjectTagging(tagAsDeleted(contentPath(blobId)));
// tag the attributes
s3.setObjectTagging(tagAsDeleted(attributePath(blobId)));
} catch (AmazonS3Exception e) {
if (NOT_IMPLEMENTED_CODE.equals(e.getErrorCode())) {
log.warn("Bucket does not support object tagging.", e);
} else {
throw e;
}
}
blob.markStale();

Long contentSize = getContentSizeForDeletion(blobAttributes);
Expand Down Expand Up @@ -797,8 +806,16 @@ public void setBlobAttributes(BlobId blobId, BlobAttributes blobAttributes) {
@Override
@Timed
protected void doUndelete(final BlobId blobId, final BlobAttributes attributes) {
s3.setObjectTagging(untagAsDeleted(contentPath(blobId)));
s3.setObjectTagging(untagAsDeleted(attributePath(blobId)));
try {
s3.setObjectTagging(untagAsDeleted(contentPath(blobId)));
s3.setObjectTagging(untagAsDeleted(attributePath(blobId)));
} catch (AmazonS3Exception e) {
if (NOT_IMPLEMENTED_CODE.equals(e.getErrorCode())) {
log.warn("Bucket does not support object tagging.", e);
} else {
throw e;
}
}
storeMetrics.recordAddition(attributes.getMetrics().getContentSize());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ public class S3BlobStoreException

public static final String SIGNATURE_DOES_NOT_MATCH_CODE = "SignatureDoesNotMatch";

public static final String NOT_IMPLEMENTED_CODE = "NotImplemented";

public static Map<String, String> ERROR_CODE_MESSAGES = ImmutableMap.of(
INVALID_ACCESS_KEY_ID_CODE, "The Access Key ID provided was invalid.",
ACCESS_DENIED_CODE, "Access denied. Please check the credentials provided have proper permissions.",
Expand Down