diff --git a/google-cloud-examples/src/main/java/com/google/cloud/examples/storage/snippets/StorageSnippets.java b/google-cloud-examples/src/main/java/com/google/cloud/examples/storage/snippets/StorageSnippets.java index a151f94d5a5e..120f5bc832f3 100644 --- a/google-cloud-examples/src/main/java/com/google/cloud/examples/storage/snippets/StorageSnippets.java +++ b/google-cloud-examples/src/main/java/com/google/cloud/examples/storage/snippets/StorageSnippets.java @@ -44,6 +44,8 @@ import com.google.cloud.storage.Storage.BlobListOption; import com.google.cloud.storage.Storage.BlobSourceOption; import com.google.cloud.storage.Storage.BlobTargetOption; +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.Storage.BucketListOption; import com.google.cloud.storage.Storage.BucketSourceOption; @@ -60,6 +62,8 @@ import java.io.InputStream; import java.net.URL; import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; @@ -107,6 +111,66 @@ public Bucket createBucketWithStorageClassAndLocation(String bucketName) { return bucket; } + /** + * Example of changing a bucket's default storage class. + */ + // [TARGET update(BucketInfo, BucketTargetOption...)] + // [VARIABLE "my_unique_bucket"] + // [VARIABLE "nearline"] + public Bucket changeBucketStorageClass(String bucketName, String storageClass) { + // [START storageChangeStorageClass] + Bucket bucket = storage.update(BucketInfo.newBuilder(bucketName) + // See here for possible values: http://g.co/cloud/storage/docs/storage-classes + .setStorageClass(storageClass) + .build()); + // [END storageChangeStorageClass] + return bucket; + } + + /** + * Example of enabling lifecycle management rules on a bucket. + */ + // [TARGET update(BucketInfo, BucketTargetOption...)] + // [VARIABLE "my_unique_bucket"] + public Bucket enableBucketLifecycleManagement(String bucketName) { + // [START storageEnableLifecycleManagement] + Bucket bucket = storage.update(BucketInfo.newBuilder(bucketName) + .setDeleteRules(Arrays.asList( + // Delete objects older than a week + new BucketInfo.AgeDeleteRule(7), + // Only keep up to 3 versions of a given object + new BucketInfo.NumNewerVersionsDeleteRule(3))) + .build()); + // [END storageEnableLifecycleManagement] + return bucket; + } + + /** + * Example of getting lifecycle management rules on a bucket. + */ + // [TARGET update(BucketInfo, BucketTargetOption...)] + // [VARIABLE "my_unique_bucket"] + public List getBucketLifecycleManagement(String bucketName) { + // [START storageGetLifecycleManagement] + Bucket bucket = storage.get(bucketName, BucketGetOption.fields(BucketField.LIFECYCLE)); + return bucket.getDeleteRules(); + // [END storageGetLifecycleManagement] + } + + /** + * Example of disabling lifecycle management rules on a bucket. + */ + // [TARGET update(BucketInfo, BucketTargetOption...)] + // [VARIABLE "my_unique_bucket"] + public Bucket disableBucketLifecycleManagement(String bucketName) { + // [START storageDisableLifecycleManagement] + Bucket bucket = storage.update(BucketInfo.newBuilder(bucketName) + .setDeleteRules(Collections.EMPTY_LIST) + .build()); + // [END storageDisableLifecycleManagement] + return bucket; + } + /** * Example of creating a blob with no content. */ @@ -153,6 +217,26 @@ public Blob createBlobFromInputStream(String bucketName, String blobName) { return blob; } + /** + * Example of uploading an encrypted blob. + */ + // [TARGET create(BlobInfo, InputStream, BlobWriteOption...)] + // [VARIABLE "my_unique_bucket"] + // [VARIABLE "my_blob_name"] + // [VARIABLE "my_encryption_key"] + public Blob createEncryptedBlob(String bucketName, String blobName, String encryptionKey) { + // [START storageUploadEncryptedFile] + InputStream content = new ByteArrayInputStream("Hello, World!".getBytes(UTF_8)); + + BlobId blobId = BlobId.of(bucketName, blobName); + BlobInfo blobInfo = BlobInfo.newBuilder(blobId) + .setContentType("text/plain") + .build(); + Blob blob = storage.create(blobInfo, content, BlobWriteOption.encryptionKey(encryptionKey)); + // [END storageUploadEncryptedFile] + return blob; + } + /** * Example of getting information on a bucket, only if its metageneration matches a value, * otherwise a {@link StorageException} is thrown. @@ -168,6 +252,20 @@ public Bucket getBucketWithMetageneration(String bucketName, long bucketMetagene return bucket; } + /** + * Example of getting storage class and location of a bucket. + */ + // [TARGET get(String, BucketGetOption...)] + // [VARIABLE "my_unique_bucket"] + // [VARIABLE 42] + public Bucket getBucketStorageClassAndLocation(String bucketName) { + // [START storageGetClassLocation] + Bucket bucket = storage.get(bucketName, BucketGetOption.fields( + BucketField.STORAGE_CLASS, BucketField.LOCATION)); + // [END storageGetClassLocation] + return bucket; + } + /** * Example of getting information on a blob, only if its metageneration matches a value, * otherwise a {@link StorageException} is thrown. @@ -446,6 +544,28 @@ public Blob copyBlobInChunks(String bucketName, String blobName, String copyBlob return blob; } + /** + * Example of rotating the encryption key of a blob. + */ + // [TARGET copy(CopyRequest)] + // [VARIABLE "my_unique_bucket"] + // [VARIABLE "my_blob_name"] + // [VARIABLE "old_encryption_key"] + // [VARIABLE "new_encryption_key"] + public Blob rotateBlobEncryptionKey( + String bucketName, String blobName, String oldEncryptionKey, String newEncryptionKey) { + // [START storageRotateEncryptionKey] + BlobId blobId = BlobId.of(bucketName, blobName); + CopyRequest request = CopyRequest.newBuilder() + .setSource(blobId) + .setSourceOptions(BlobSourceOption.decryptionKey(oldEncryptionKey)) + .setTarget(blobId, BlobTargetOption.encryptionKey(newEncryptionKey)) + .build(); + Blob blob = storage.copy(request).getResult(); + // [END storageRotateEncryptionKey] + return blob; + } + /** * Example of reading all bytes of a blob, if generation matches a value, otherwise a * {@link StorageException} is thrown. @@ -470,7 +590,7 @@ public byte[] readBlobFromStringsWithGeneration(String bucketName, String blobNa // [TARGET readAllBytes(BlobId, BlobSourceOption...)] // [VARIABLE "my_unique_bucket"] // [VARIABLE "my_blob_name"] - // [VARIABLE 42"] + // [VARIABLE 42] public byte[] readBlobFromId(String bucketName, String blobName, long blobGeneration) { // [START readBlobFromId] BlobId blobId = BlobId.of(bucketName, blobName, blobGeneration); @@ -479,6 +599,21 @@ public byte[] readBlobFromId(String bucketName, String blobName, long blobGenera return content; } + /** + * Example of reading all bytes of an encrypted blob. + */ + // [TARGET readAllBytes(BlobId, BlobSourceOption...)] + // [VARIABLE "my_unique_bucket"] + // [VARIABLE "my_blob_name"] + // [VARIABLE "my_encryption_key"] + public byte[] readEncryptedBlob(String bucketName, String blobName, String decryptionKey) { + // [START readEncryptedBlob] + byte[] content = storage.readAllBytes( + bucketName, blobName, BlobSourceOption.decryptionKey(decryptionKey)); + // [END readEncryptedBlob] + return content; + } + /** * Example of using a batch request to delete, update and get a blob. */ diff --git a/google-cloud-examples/src/test/java/com/google/cloud/examples/storage/snippets/ITStorageSnippets.java b/google-cloud-examples/src/test/java/com/google/cloud/examples/storage/snippets/ITStorageSnippets.java index 21c0821c0cbc..e541dfaaf4ca 100644 --- a/google-cloud-examples/src/test/java/com/google/cloud/examples/storage/snippets/ITStorageSnippets.java +++ b/google-cloud-examples/src/test/java/com/google/cloud/examples/storage/snippets/ITStorageSnippets.java @@ -33,6 +33,7 @@ import com.google.cloud.storage.BlobId; import com.google.cloud.storage.BlobInfo; import com.google.cloud.storage.Bucket; +import com.google.cloud.storage.BucketInfo; import com.google.cloud.storage.Storage; import com.google.cloud.storage.StorageException; import com.google.cloud.storage.testing.RemoteStorageHelper; @@ -110,6 +111,36 @@ public void testCreateBucketWithStorageClassAndLocation() assertNotNull(bucket); } + @Test + public void testChangeBucketStorageClass() + throws ExecutionException, InterruptedException { + Bucket bucket = storageSnippets.changeBucketStorageClass(BUCKET, "nearline"); + assertEquals("NEARLINE", bucket.getStorageClass()); + + // Restore it to regional for the sake of the rest of the tests + bucket = storageSnippets.changeBucketStorageClass(BUCKET, "standard"); + assertEquals("STANDARD", bucket.getStorageClass()); + } + + @Test + public void testEnableDisableBucketLifecycleManagement() { + Bucket bucket = storageSnippets.enableBucketLifecycleManagement(BUCKET); + List deleteRules = bucket.getDeleteRules(); + assertEquals(2, deleteRules.size()); + assertEquals(BucketInfo.DeleteRule.Type.AGE, deleteRules.get(0).getType()); + assertEquals(BucketInfo.DeleteRule.Type.NUM_NEWER_VERSIONS, deleteRules.get(1).getType()); + + deleteRules = storageSnippets.getBucketLifecycleManagement(BUCKET); + assertEquals(2, deleteRules.size()); + + bucket = storageSnippets.disableBucketLifecycleManagement(BUCKET); + deleteRules = bucket.getDeleteRules(); + assertNull(deleteRules); + + deleteRules = storageSnippets.getBucketLifecycleManagement(BUCKET); + assertNull(deleteRules); + } + @Test public void testBlob() throws InterruptedException { String blobName = "directory/test-blob"; @@ -145,6 +176,34 @@ public void testBlob() throws InterruptedException { copiedBlob.delete(); } + @Test + public void testCreateUpdateEncryptedBlob() throws InterruptedException { + // Note: DO NOT put your encryption key in your code, like it is here. Store it somewhere safe, + // and read it in when you need it. This key is just here to make the code easier to read. + String encryptionKey1 = "0mMWhFvQOdS4AmxRpo8SJxXn5MjFhbz7DkKBUdUIef8="; + String blobName = "encrypted-blob"; + + Blob blob = storageSnippets.createEncryptedBlob(BUCKET, blobName, encryptionKey1); + + assertNotNull(blob); + assertEquals("text/plain", blob.getContentType()); + byte[] encryptedContent = storageSnippets.readEncryptedBlob(BUCKET, blobName, encryptionKey1); + assertEquals("Hello, World!", new String(encryptedContent)); + blob = storageSnippets.getBlobFromId(BUCKET, blobName); + assertEquals("text/plain", blob.getContentType()); + + String encryptionKey2 = "wnxMO0w+dmxribu7rICJ+Q2ES9TLpFRIDy3/L7HN5ZA="; + + blob = storageSnippets.rotateBlobEncryptionKey( + BUCKET, blobName, encryptionKey1, encryptionKey2); + + assertNotNull(blob); + encryptedContent = storageSnippets.readEncryptedBlob(BUCKET, blobName, encryptionKey2); + assertEquals("Hello, World!", new String(encryptedContent)); + blob = storageSnippets.getBlobFromId(BUCKET, blobName); + assertEquals("text/plain", blob.getContentType()); + } + @Test public void testCreateCopyAndGetBlob() { String blobName = "test-create-copy-get-blob"; @@ -178,6 +237,13 @@ public void testGetBucketWithMetageneration() { storageSnippets.getBucketWithMetageneration(BUCKET, -1); } + @Test + public void testGetBucketStorageClassAndLocation() { + Bucket bucket = storageSnippets.getBucketStorageClassAndLocation(BUCKET); + assertEquals("STANDARD", bucket.getStorageClass()); + assertEquals("US", bucket.getLocation()); + } + @Test public void testListBucketsWithSizeAndPrefix() throws InterruptedException { Page buckets = storageSnippets.listBucketsWithSizeAndPrefix(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 6e70e75008d3..8ac1c3feb3bc 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 @@ -90,7 +90,8 @@ enum BucketField implements FieldSelector { VERSIONING("versioning"), CORS("cors"), STORAGE_CLASS("storageClass"), - ETAG("etag"); + ETAG("etag"), + LIFECYCLE("lifecycle"); static final List REQUIRED_FIELDS = ImmutableList.of(NAME); @@ -1216,6 +1217,19 @@ public Builder target(BlobInfo target, BlobTargetOption... options) { return setTarget(target, options); } + /** + * Sets the copy target. Target blob information is copied from source, except for those + * options specified in {@code options}. + * + * @return the builder + */ + public Builder setTarget(BlobId targetId, BlobTargetOption... options) { + this.overrideInfo = false; + this.target = BlobInfo.newBuilder(targetId).build(); + Collections.addAll(targetOptions, options); + return this; + } + /** * Sets the copy target and target options. {@code target} parameter is used to override * source blob information (e.g. {@code contentType}, {@code contentLanguage}). Target blob @@ -1259,6 +1273,19 @@ public Builder setTarget(BlobInfo target, Iterable options) { return this; } + /** + * Sets the copy target and target options. Target blob information is copied from source, + * except for those options specified in {@code options}. + * + * @return the builder + */ + public Builder setTarget(BlobId targetId, Iterable options) { + this.overrideInfo = false; + this.target = BlobInfo.newBuilder(targetId).build(); + Iterables.addAll(targetOptions, options); + return this; + } + /** * Sets the maximum number of megabytes to copy for each RPC call. This parameter is ignored * if source and target blob share the same location and storage class as copy is made with @@ -1566,6 +1593,20 @@ public static Builder newBuilder() { * Blob blob = storage.create(blobInfo, content); * } * + *

Example of uploading an encrypted blob. + *

 {@code
+   * String bucketName = "my_unique_bucket";
+   * String blobName = "my_blob_name";
+   * String encryptionKey = "my_encryption_key";
+   * InputStream content = new ByteArrayInputStream("Hello, World!".getBytes(UTF_8));
+   * 
+   * BlobId blobId = BlobId.of(bucketName, blobName);
+   * BlobInfo blobInfo = BlobInfo.newBuilder(blobId)
+   *     .setContentType("text/plain")
+   *     .build();
+   * Blob blob = storage.create(blobInfo, content, BlobWriteOption.encryptionKey(encryptionKey));
+   * }
+ * * @return a [@code Blob} with complete information * @throws StorageException upon failure */ @@ -1888,6 +1929,21 @@ public static Builder newBuilder() { * Blob blob = copyWriter.getResult(); * } * + *

Example of rotating the encryption key of a blob. + *

 {@code
+   * String bucketName = "my_unique_bucket";
+   * String blobName = "my_blob_name";
+   * String oldEncryptionKey = "old_encryption_key";
+   * String newEncryptionKey = "new_encryption_key";
+   * BlobId blobId = BlobId.of(bucketName, blobName);
+   * CopyRequest request = CopyRequest.newBuilder()
+   *     .setSource(blobId)
+   *     .setSourceOptions(BlobSourceOption.decryptionKey(oldEncryptionKey))
+   *     .setTarget(blobId, BlobTargetOption.encryptionKey(newEncryptionKey))
+   *     .build();
+   * Blob blob = storage.copy(request).getResult();
+   * }
+ * * @return a {@link CopyWriter} object that can be used to get information on the newly created * blob or to complete the copy if more than one RPC request is needed * @throws StorageException upon failure @@ -1921,11 +1977,20 @@ public static Builder newBuilder() { *
 {@code
    * String bucketName = "my_unique_bucket";
    * String blobName = "my_blob_name";
-   * long blobGeneration = 42";
+   * long blobGeneration = 42;
    * BlobId blobId = BlobId.of(bucketName, blobName, blobGeneration);
    * byte[] content = storage.readAllBytes(blobId);
    * }
* + *

Example of reading all bytes of an encrypted blob. + *

 {@code
+   * String bucketName = "my_unique_bucket";
+   * String blobName = "my_blob_name";
+   * String decryptionKey = "my_encryption_key";
+   * byte[] content = storage.readAllBytes(
+   *     bucketName, blobName, BlobSourceOption.decryptionKey(decryptionKey));
+   * }
+ * * @return the blob's content * @throws StorageException upon failure */