diff --git a/google-cloud-storage/clirr-ignored-differences.xml b/google-cloud-storage/clirr-ignored-differences.xml index ae8d781dbe..a136652927 100644 --- a/google-cloud-storage/clirr-ignored-differences.xml +++ b/google-cloud-storage/clirr-ignored-differences.xml @@ -21,4 +21,15 @@ com.google.cloud.storage.PostPolicyV4 generateSignedPostPolicyV4(com.google.cloud.storage.BlobInfo, long, java.util.concurrent.TimeUnit, com.google.cloud.storage.Storage$PostPolicyV4Option[]) 7012 - \ No newline at end of file + + com/google/cloud/storage/Storage + boolean deleteLifecycleRules(java.lang.String) + 7012 + + + com/google/cloud/storage/spi/v1/StorageRpc + boolean deleteLifecycleRules(com.google.api.services.storage.model.Bucket) + 7012 + + + diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/Bucket.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/Bucket.java index 7aa847c9b6..1cbe0e7d4e 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/Bucket.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/Bucket.java @@ -1114,6 +1114,37 @@ public boolean deleteDefaultAcl(Entity entity) { return storage.deleteDefaultAcl(getName(), entity); } + /** + * Delete the lifecycle rules of this bucket. + * + *

Example of deleting the lifecycle rules of this bucket. + * + *

{@code
+   * String bucketName = "my-unique-bucket";
+   * Bucket bucket =
+   *     storage.create(
+   *         BucketInfo.newBuilder(bucketName)
+   *             .setStorageClass(StorageClass.COLDLINE)
+   *             .setLocation("us-central1")
+   *             .setLifecycleRules(
+   *                 ImmutableList.of(
+   *                     new LifecycleRule(
+   *                         LifecycleAction.newDeleteAction(),
+   *                         LifecycleCondition.newBuilder().setAge(2).build())))
+   *             .build());
+   * boolean rulesDeleted = bucket.deleteLifecycleRules();
+   * if (rulesDeleted) {
+   *   // the lifecycle rules were deleted
+   * }
+   * }
+ * + * @return {@code true} if the bucket lifecycle rules were deleted. + * @throws StorageException upon failure + */ + public boolean deleteLifecycleRules() { + return storage.deleteLifecycleRules(getName()); + } + /** * Creates a new default blob ACL entry on this bucket. * diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java index e28a941088..92f0dd16d4 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java @@ -1999,7 +1999,6 @@ Blob create( * only if supplied Decrpytion Key decrypts the blob successfully, otherwise a {@link * StorageException} is thrown. For more information review * - * @throws StorageException upon failure * @see Encrypted * Elements @@ -3076,6 +3075,36 @@ PostPolicyV4 generateSignedPostPolicyV4( */ boolean deleteDefaultAcl(String bucket, Entity entity); + /** + * Delete the lifecycle rules of the requested bucket. + * + *

Example of deleting the lifecycle rules of the requested bucket. + * + *

{@code
+   * String bucketName = "my-unique-bucket";
+   * Bucket bucket =
+   *     storage.create(
+   *         BucketInfo.newBuilder(bucketName)
+   *             .setStorageClass(StorageClass.COLDLINE)
+   *             .setLocation("us-central1")
+   *             .setLifecycleRules(
+   *                 ImmutableList.of(
+   *                     new LifecycleRule(
+   *                         LifecycleAction.newDeleteAction(),
+   *                         LifecycleCondition.newBuilder().setAge(2).build())))
+   *             .build());
+   * boolean rulesDeleted = storage.deleteLifecycleRules(bucketName);
+   * if (rulesDeleted) {
+   *   // the lifecycle rules were deleted
+   * }
+   * }
+ * + * @param bucket name of the bucket. + * @return {@code true} if the bucket lifecycle rules were deleted. + * @throws StorageException upon failure + */ + boolean deleteLifecycleRules(String bucket); + /** * Creates a new default blob ACL entry on the specified bucket. * diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java index 0e24521eb6..49589dc933 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java @@ -1158,6 +1158,25 @@ public Boolean call() { } } + @Override + public boolean deleteLifecycleRules(final String bucket) { + final com.google.api.services.storage.model.Bucket bucketPb = BucketInfo.of(bucket).toPb(); + try { + return runWithRetries( + new Callable() { + @Override + public Boolean call() { + return storageRpc.deleteLifecycleRules(bucketPb); + } + }, + getOptions().getRetrySettings(), + EXCEPTION_HANDLER, + getOptions().getClock()); + } catch (RetryHelperException e) { + throw StorageException.translateAndThrow(e); + } + } + @Override public boolean deleteAcl(final String bucket, final Entity entity) { return deleteAcl(bucket, entity, new BucketSourceOption[0]); diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java index e211c0277e..78d2cad88b 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java @@ -25,6 +25,7 @@ import com.google.api.client.googleapis.json.GoogleJsonError; import com.google.api.client.http.ByteArrayContent; import com.google.api.client.http.GenericUrl; +import com.google.api.client.http.HttpContent; import com.google.api.client.http.HttpHeaders; import com.google.api.client.http.HttpRequest; import com.google.api.client.http.HttpRequestFactory; @@ -87,7 +88,8 @@ public class HttpStorageRpc implements StorageRpc { public static final String NO_ACL_PROJECTION = "noAcl"; private static final String ENCRYPTION_KEY_PREFIX = "x-goog-encryption-"; private static final String SOURCE_ENCRYPTION_KEY_PREFIX = "x-goog-copy-source-encryption-"; - + private static final String GOOGLE_STORAGE_API_ENDPOINT = + "https://storage.googleapis.com/storage/v1/b/"; // declare this HttpStatus code here as it's not included in java.net.HttpURLConnection private static final int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416; @@ -1000,6 +1002,32 @@ public boolean deleteAcl(String bucket, String entity, Map options) { } } + @Override + public boolean deleteLifecycleRules(Bucket bucket) { + Span span = startSpan(HttpStorageRpcSpans.SPAN_NAME_DELETE_BUCKET_LIFECYCLE_RULE); + Scope scope = tracer.withSpan(span); + try { + HttpRequestFactory requestFactory = storage.getRequestFactory(); + HttpContent httpContent = new JsonHttpContent(new JacksonFactory(), ""); + HttpRequest httpRequest = + requestFactory.buildPutRequest( + new GenericUrl(GOOGLE_STORAGE_API_ENDPOINT + bucket.getName() + "?fields=lifecycle"), + httpContent); + httpRequest.execute(); + return true; + } catch (IOException ex) { + span.setStatus(Status.UNKNOWN.withDescription(ex.getMessage())); + StorageException serviceException = translate(ex); + if (serviceException.getCode() == HTTP_NOT_FOUND) { + return false; + } + throw serviceException; + } finally { + scope.close(); + span.end(); + } + } + @Override public BucketAccessControl createAcl(BucketAccessControl acl, Map options) { Span span = startSpan(HttpStorageRpcSpans.SPAN_NAME_CREATE_BUCKET_ACL); diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpcSpans.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpcSpans.java index a5e4daf7da..35ef19a7fe 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpcSpans.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpcSpans.java @@ -33,6 +33,8 @@ class HttpStorageRpcSpans { static final String SPAN_NAME_PATCH_BUCKET = getTraceSpanName("patch(Bucket,Map)"); static final String SPAN_NAME_PATCH_OBJECT = getTraceSpanName("patch(StorageObject,Map)"); static final String SPAN_NAME_DELETE_BUCKET = getTraceSpanName("delete(Bucket,Map)"); + static final String SPAN_NAME_DELETE_BUCKET_LIFECYCLE_RULE = + getTraceSpanName("deleteLifecycleRules(String)"); static final String SPAN_NAME_DELETE_OBJECT = getTraceSpanName("delete(StorageObject,Map)"); static final String SPAN_NAME_CREATE_BATCH = getTraceSpanName("createBatch()"); static final String SPAN_NAME_COMPOSE = getTraceSpanName("compose(Iterable,StorageObject,Map)"); diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/StorageRpc.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/StorageRpc.java index 36c7a5ff1b..76f10a8a4a 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/StorageRpc.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/StorageRpc.java @@ -256,6 +256,14 @@ public int hashCode() { */ boolean delete(Bucket bucket, Map options); + /** + * Delete the lifecycle rules of the requested bucket. + * + * @return {@code true} if the bucket lifecycle rules were deleted. + * @throws StorageException upon failure + */ + boolean deleteLifecycleRules(Bucket bucket); + /** * Deletes the requested storage object. * diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketTest.java index a96422f141..554e583f60 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketTest.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketTest.java @@ -703,6 +703,15 @@ public void testDeleteDefaultAcl() throws Exception { assertTrue(bucket.deleteDefaultAcl(User.ofAllAuthenticatedUsers())); } + @Test + public void testDeleteLifecycleRules() { + expect(storage.getOptions()).andReturn(mockOptions).times(1); + expect(storage.deleteLifecycleRules(BUCKET_INFO.getName())).andReturn(true); + replay(storage); + initializeBucket(); + assertTrue(bucket.deleteLifecycleRules()); + } + @Test public void testCreateDefaultAcl() throws Exception { initializeExpectedBucket(4); diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java index 3d590e0071..436d3ed021 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java @@ -2737,6 +2737,17 @@ public void testDeleteDefaultBucketAcl() { assertTrue(storage.deleteDefaultAcl(BUCKET_NAME1, User.ofAllAuthenticatedUsers())); } + @Test + public void testDeleteLifecyclesRulesOfBucket() { + EasyMock.expect( + storageRpcMock.deleteLifecycleRules( + EasyMock.isA(com.google.api.services.storage.model.Bucket.class))) + .andReturn(true); + EasyMock.replay(storageRpcMock); + initializeService(); + assertTrue(storage.deleteLifecycleRules(BUCKET_NAME1)); + } + @Test public void testCreateDefaultBucketAcl() { Acl returnedAcl = ACL.toBuilder().setEtag("ETAG").setId("ID").build(); diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java index 2bbb20ce70..e73f63fe29 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java @@ -75,6 +75,7 @@ import com.google.cloud.storage.Storage.BlobField; import com.google.cloud.storage.Storage.BlobWriteOption; import com.google.cloud.storage.Storage.BucketField; +import com.google.cloud.storage.Storage.BucketGetOption; import com.google.cloud.storage.StorageBatch; import com.google.cloud.storage.StorageBatchResult; import com.google.cloud.storage.StorageClass; @@ -3273,4 +3274,36 @@ public void testBlobReload() throws Exception { updated.delete(); assertNull(updated.reload()); } + + @Test + public void testDeleteLifecycleRules() throws ExecutionException, InterruptedException { + String lifeCycleRuleBucket = RemoteStorageHelper.generateBucketName(); + List lifecycleRules = + ImmutableList.of( + new LifecycleRule( + LifecycleAction.newDeleteAction(), + LifecycleCondition.newBuilder().setAge(2).setIsLive(Boolean.TRUE).build())); + try { + Bucket bucket = + storage.create( + BucketInfo.newBuilder(lifeCycleRuleBucket) + .setStorageClass(StorageClass.COLDLINE) + .setLocation("us-central1") + .setLifecycleRules(lifecycleRules) + .build()); + assertEquals(lifeCycleRuleBucket, bucket.getName()); + assertEquals(StorageClass.COLDLINE, bucket.getStorageClass()); + assertEquals(lifecycleRules, bucket.getLifecycleRules()); + assertNotNull(bucket.getLifecycleRules()); + boolean rulesDeleted = bucket.deleteLifecycleRules(); + assertTrue(rulesDeleted); + bucket = + storage.get(lifeCycleRuleBucket, BucketGetOption.fields(Storage.BucketField.values())); + assertNull(bucket.getLifecycleRules()); + } catch (StorageException ex) { + throw ex; + } finally { + RemoteStorageHelper.forceDelete(storage, lifeCycleRuleBucket, 5, TimeUnit.SECONDS); + } + } }