From 6f9dfdfdbf9f1466839a17ef97489f207f18bec6 Mon Sep 17 00:00:00 2001 From: JesseLovelace <43148100+JesseLovelace@users.noreply.github.com> Date: Wed, 12 Jan 2022 12:56:48 -0800 Subject: [PATCH] feat: Add RPO metadata settings (#1105) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * (feat) Add RPO metadata settings * Add javadoc * Apply suggestions from code review Co-authored-by: BenWhitehead * typo * 🦉 Updates from OwlBot See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * fix clirr Co-authored-by: BenWhitehead Co-authored-by: Owl Bot --- .../clirr-ignored-differences.xml | 5 ++ .../java/com/google/cloud/storage/Bucket.java | 6 ++ .../com/google/cloud/storage/BucketInfo.java | 35 ++++++++++ .../java/com/google/cloud/storage/Rpo.java | 70 +++++++++++++++++++ .../cloud/storage/it/ITStorageTest.java | 30 ++++++++ 5 files changed, 146 insertions(+) create mode 100644 google-cloud-storage/src/main/java/com/google/cloud/storage/Rpo.java diff --git a/google-cloud-storage/clirr-ignored-differences.xml b/google-cloud-storage/clirr-ignored-differences.xml index 189316769..c45b439cc 100644 --- a/google-cloud-storage/clirr-ignored-differences.xml +++ b/google-cloud-storage/clirr-ignored-differences.xml @@ -1,6 +1,11 @@ + + com/google/cloud/storage/BucketInfo$Builder + com.google.cloud.storage.BucketInfo$Builder setRpo(com.google.cloud.storage.Rpo) + 7013 + com/google/cloud/storage/BucketInfo$LifecycleRule$LifecycleAction BucketInfo$LifecycleRule$LifecycleAction() 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 3714e6e94..95acd19d6 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 @@ -577,6 +577,12 @@ public Builder deleteLifecycleRules() { return this; } + @Override + public Builder setRpo(Rpo rpo) { + infoBuilder.setRpo(rpo); + return this; + } + @Override public Builder setStorageClass(StorageClass storageClass) { infoBuilder.setStorageClass(storageClass); diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/BucketInfo.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/BucketInfo.java index 2404a7c1e..5e06b6ce2 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/BucketInfo.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/BucketInfo.java @@ -92,6 +92,7 @@ public com.google.api.services.storage.model.Bucket apply(BucketInfo bucketInfo) private final List acl; private final List defaultAcl; private final String location; + private final Rpo rpo; private final StorageClass storageClass; private final Map labels; private final String defaultKmsKeyName; @@ -1205,6 +1206,15 @@ public abstract static class Builder { /** Deletes the lifecycle rules of this bucket. */ public abstract Builder deleteLifecycleRules(); + /** + * Sets the bucket's Recovery Point Objective (RPO). This can only be set for a dual-region + * bucket, and determines the speed at which data will be replicated between regions. See the + * {@code Rpo} class for supported values, and here for additional + * details. + */ + public abstract Builder setRpo(Rpo rpo); + /** * Sets the bucket's storage class. This defines how blobs in the bucket are stored and * determines the SLA and the cost of storage. A list of supported values is available deleteRules; private List lifecycleRules; + private Rpo rpo; private StorageClass storageClass; private String location; private String etag; @@ -1337,6 +1348,7 @@ static final class BuilderImpl extends Builder { updateTime = bucketInfo.updateTime; metageneration = bucketInfo.metageneration; location = bucketInfo.location; + rpo = bucketInfo.rpo; storageClass = bucketInfo.storageClass; cors = bucketInfo.cors; acl = bucketInfo.acl; @@ -1430,6 +1442,12 @@ public Builder deleteLifecycleRules() { return this; } + @Override + public Builder setRpo(Rpo rpo) { + this.rpo = rpo; + return this; + } + @Override public Builder setStorageClass(StorageClass storageClass) { this.storageClass = storageClass; @@ -1568,6 +1586,7 @@ public BucketInfo build() { updateTime = builder.updateTime; metageneration = builder.metageneration; location = builder.location; + rpo = builder.rpo; storageClass = builder.storageClass; cors = builder.cors; acl = builder.acl; @@ -1729,6 +1748,16 @@ public String getLocationType() { return locationType; } + /** + * Returns the bucket's recovery point objective (RPO). This defines how quickly data is + * replicated between regions in a dual-region bucket. Not defined for single-region buckets. + * + * @see + */ + public Rpo getRpo() { + return rpo; + } + /** * Returns the bucket's storage class. This defines how blobs in the bucket are stored and * determines the SLA and the cost of storage. @@ -1899,6 +1928,9 @@ com.google.api.services.storage.model.Bucket toPb() { if (locationType != null) { bucketPb.setLocationType(locationType); } + if (rpo != null) { + bucketPb.setRpo(rpo.toString()); + } if (storageClass != null) { bucketPb.setStorageClass(storageClass.toString()); } @@ -2062,6 +2094,9 @@ static BucketInfo fromPb(com.google.api.services.storage.model.Bucket bucketPb) if (bucketPb.getLocation() != null) { builder.setLocation(bucketPb.getLocation()); } + if (bucketPb.getRpo() != null) { + builder.setRpo(Rpo.valueOf(bucketPb.getRpo())); + } if (bucketPb.getStorageClass() != null) { builder.setStorageClass(StorageClass.valueOf(bucketPb.getStorageClass())); } diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/Rpo.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/Rpo.java new file mode 100644 index 000000000..ca6a4b0ab --- /dev/null +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/Rpo.java @@ -0,0 +1,70 @@ +/* + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.storage; + +import com.google.api.core.ApiFunction; +import com.google.cloud.StringEnumType; +import com.google.cloud.StringEnumValue; + +/** + * Enums for the Recovery Point Objective (RPO) of dual-region buckets, which determines how fast + * data is replicated between regions. + * + * @see https://cloud.google.com/storage/docs/turbo-replication + */ +public final class Rpo extends StringEnumValue { + + private static final long serialVersionUID = -3954216195295821508L; + + private Rpo(String constant) { + super(constant); + } + + private static final ApiFunction CONSTRUCTOR = Rpo::new; + + private static final StringEnumType type = new StringEnumType<>(Rpo.class, CONSTRUCTOR); + + /** + * Default recovery point objective. With this setting, there is no guarantee on the amount of + * time it takes for data to replicate between regions. + */ + public static final Rpo DEFAULT = type.createAndRegister("DEFAULT"); + + /** + * Turbo recovery point objective. With this setting, data in a dual-region bucket will replicate + * between regions within 15 minutes. + */ + public static final Rpo ASYNC_TURBO = type.createAndRegister("ASYNC_TURBO"); + + /** + * Get the Rpo for the given String constant, and throw an exception if the constant is not + * recognized. + */ + public static Rpo valueOfStrict(String constant) { + return type.valueOfStrict(constant); + } + + /** Get the Rpo for the given String constant, and allow unrecognized values. */ + public static Rpo valueOf(String constant) { + return type.valueOf(constant); + } + + /** Return the known values for Rpo. */ + public static Rpo[] values() { + return type.values(); + } +} 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 77e472ac4..ad8dc6c03 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 @@ -73,6 +73,7 @@ import com.google.cloud.storage.HttpMethod; import com.google.cloud.storage.PostPolicyV4; import com.google.cloud.storage.PostPolicyV4.PostFieldsV4; +import com.google.cloud.storage.Rpo; import com.google.cloud.storage.ServiceAccount; import com.google.cloud.storage.Storage; import com.google.cloud.storage.Storage.BlobField; @@ -3951,4 +3952,33 @@ protected Object handleInvocation( blobGen2.downloadTo(actualData); assertEquals(contentGen2Expected, ByteBuffer.wrap(actualData.toByteArray())); } + + @Test + public void testRpoConfig() { + String rpoBucket = RemoteStorageHelper.generateBucketName(); + try { + Bucket bucket = + storage.create( + BucketInfo.newBuilder(rpoBucket).setLocation("NAM4").setRpo(Rpo.ASYNC_TURBO).build()); + assertEquals("ASYNC_TURBO", bucket.getRpo().toString()); + + bucket.toBuilder().setRpo(Rpo.DEFAULT).build().update(); + + assertEquals("DEFAULT", storage.get(rpoBucket).getRpo().toString()); + } finally { + storage.delete(rpoBucket); + } + } + + private static String randString(Random rand, int length) { + final StringBuilder sb = new StringBuilder(); + while (sb.length() < length) { + int i = rand.nextInt('z'); + char c = (char) i; + if (Character.isLetter(c) || Character.isDigit(c)) { + sb.append(c); + } + } + return sb.toString(); + } }