diff --git a/.kokoro/presubmit/graalvm-native-17.cfg b/.kokoro/presubmit/graalvm-native-17.cfg index 6b633cf8a9..ce36c4a929 100644 --- a/.kokoro/presubmit/graalvm-native-17.cfg +++ b/.kokoro/presubmit/graalvm-native-17.cfg @@ -3,7 +3,7 @@ # Configure the docker image for kokoro-trampoline. env_vars: { key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/graalvm17:22.3.2" + value: "gcr.io/cloud-devrel-kokoro-resources/graalvm17:22.3.3" } env_vars: { diff --git a/.kokoro/presubmit/graalvm-native.cfg b/.kokoro/presubmit/graalvm-native.cfg index 297e47c0d3..ee6ff82123 100644 --- a/.kokoro/presubmit/graalvm-native.cfg +++ b/.kokoro/presubmit/graalvm-native.cfg @@ -3,7 +3,7 @@ # Configure the docker image for kokoro-trampoline. env_vars: { key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/graalvm:22.3.2" + value: "gcr.io/cloud-devrel-kokoro-resources/graalvm:22.3.3" } env_vars: { diff --git a/CHANGELOG.md b/CHANGELOG.md index c0dce6039e..5e016233b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## [2.29.1](https://github.com/googleapis/java-storage/compare/v2.29.0...v2.29.1) (2023-11-02) + + +### Bug Fixes + +* Improve 503 handling for json resumable uploads ([#2289](https://github.com/googleapis/java-storage/issues/2289)) ([9b4bb82](https://github.com/googleapis/java-storage/commit/9b4bb8221294bcd94037b69281a37f33b364b174)) + + +### Dependencies + +* Update dependency com.google.apis:google-api-services-storage to v1-rev20231028-2.0.0 ([#2281](https://github.com/googleapis/java-storage/issues/2281)) ([94b8dd6](https://github.com/googleapis/java-storage/commit/94b8dd601d33c25edcff05885b0fadf0decbb86e)) +* Update dependency com.google.cloud:google-cloud-shared-dependencies to v3.19.0 ([#2288](https://github.com/googleapis/java-storage/issues/2288)) ([cc65fd0](https://github.com/googleapis/java-storage/commit/cc65fd0897a064d7efc7b50f33acecee9ffa2c4b)) + ## [2.29.0](https://github.com/googleapis/java-storage/compare/v2.28.0...v2.29.0) (2023-10-23) diff --git a/README.md b/README.md index 0d375de41f..cb3350e198 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ If you are using Maven with [BOM][libraries-bom], add this to your pom.xml file: com.google.cloud libraries-bom - 26.25.0 + 26.26.0 pom import @@ -42,7 +42,7 @@ If you are using Maven without the BOM, add this to your dependencies: com.google.cloud google-cloud-storage - 2.28.0 + 2.29.0 ``` @@ -50,20 +50,20 @@ If you are using Maven without the BOM, add this to your dependencies: If you are using Gradle 5.x or later, add this to your dependencies: ```Groovy -implementation platform('com.google.cloud:libraries-bom:26.25.0') +implementation platform('com.google.cloud:libraries-bom:26.26.0') implementation 'com.google.cloud:google-cloud-storage' ``` If you are using Gradle without BOM, add this to your dependencies: ```Groovy -implementation 'com.google.cloud:google-cloud-storage:2.28.0' +implementation 'com.google.cloud:google-cloud-storage:2.29.0' ``` If you are using SBT, add this to your dependencies: ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-storage" % "2.28.0" +libraryDependencies += "com.google.cloud" % "google-cloud-storage" % "2.29.0" ``` @@ -428,7 +428,7 @@ Java is a registered trademark of Oracle and/or its affiliates. [kokoro-badge-link-5]: http://storage.googleapis.com/cloud-devrel-public/java/badges/java-storage/java11.html [stability-image]: https://img.shields.io/badge/stability-stable-green [maven-version-image]: https://img.shields.io/maven-central/v/com.google.cloud/google-cloud-storage.svg -[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-storage/2.28.0 +[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-storage/2.29.0 [authentication]: https://github.com/googleapis/google-cloud-java#authentication [auth-scopes]: https://developers.google.com/identity/protocols/oauth2/scopes [predefined-iam-roles]: https://cloud.google.com/iam/docs/understanding-roles#predefined_roles diff --git a/gapic-google-cloud-storage-v2/pom.xml b/gapic-google-cloud-storage-v2/pom.xml index 27342960b0..f1f00aa92d 100644 --- a/gapic-google-cloud-storage-v2/pom.xml +++ b/gapic-google-cloud-storage-v2/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc gapic-google-cloud-storage-v2 - 2.29.0-alpha + 2.29.1-alpha gapic-google-cloud-storage-v2 GRPC library for gapic-google-cloud-storage-v2 com.google.cloud google-cloud-storage-parent - 2.29.0 + 2.29.1 diff --git a/google-cloud-storage-bom/pom.xml b/google-cloud-storage-bom/pom.xml index fd72015e73..98f9bb7ac2 100644 --- a/google-cloud-storage-bom/pom.xml +++ b/google-cloud-storage-bom/pom.xml @@ -19,12 +19,12 @@ 4.0.0 com.google.cloud google-cloud-storage-bom - 2.29.0 + 2.29.1 pom com.google.cloud google-cloud-shared-config - 1.6.0 + 1.6.1 @@ -69,22 +69,22 @@ com.google.cloud google-cloud-storage - 2.29.0 + 2.29.1 com.google.api.grpc gapic-google-cloud-storage-v2 - 2.29.0-alpha + 2.29.1-alpha com.google.api.grpc grpc-google-cloud-storage-v2 - 2.29.0-alpha + 2.29.1-alpha com.google.api.grpc proto-google-cloud-storage-v2 - 2.29.0-alpha + 2.29.1-alpha diff --git a/google-cloud-storage/pom.xml b/google-cloud-storage/pom.xml index 75b739f041..f68d2db40e 100644 --- a/google-cloud-storage/pom.xml +++ b/google-cloud-storage/pom.xml @@ -2,7 +2,7 @@ 4.0.0 google-cloud-storage - 2.29.0 + 2.29.1 jar Google Cloud Storage https://github.com/googleapis/java-storage @@ -12,11 +12,11 @@ com.google.cloud google-cloud-storage-parent - 2.29.0 + 2.29.1 google-cloud-storage - 1.107.7 + 1.107.11 5.10.0 @@ -173,13 +173,13 @@ com.google.api.grpc proto-google-cloud-kms-v1 - 0.122.0 + 0.123.0 test com.google.cloud google-cloud-kms - 2.31.0 + 2.32.0 test diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonResumableSessionPutTask.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonResumableSessionPutTask.java index 5a4864996f..3f5e6ccb63 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonResumableSessionPutTask.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonResumableSessionPutTask.java @@ -190,6 +190,7 @@ public void rewindTo(long offset) { } else { HttpResponseException cause = new HttpResponseException(response); String contentType = response.getHeaders().getContentType(); + Long contentLength = response.getHeaders().getContentLength(); // If the content-range header value has run ahead of the backend, it will respond with // a 503 with plain text content // Attempt to detect this very loosely as to minimize impact of modified error message @@ -197,7 +198,9 @@ public void rewindTo(long offset) { if ((!JsonResumableSessionFailureScenario.isOk(code) && !JsonResumableSessionFailureScenario.isContinue(code)) && contentType != null - && contentType.startsWith("text/plain")) { + && contentType.startsWith("text/plain") + && contentLength != null + && contentLength > 0) { String errorMessage = cause.getContent().toLowerCase(Locale.US); if (errorMessage.contains("content-range")) { StorageException se = diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/ITJsonResumableSessionPutTaskTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/ITJsonResumableSessionPutTaskTest.java index b7d9d6c74a..f3e3750e2e 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/ITJsonResumableSessionPutTaskTest.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/ITJsonResumableSessionPutTaskTest.java @@ -740,6 +740,37 @@ public void scenario5() throws Exception { } } + @Test + public void _503_emptyBody() throws Exception { + HttpRequestHandler handler = + req -> { + FullHttpResponse resp = + new DefaultFullHttpResponse(req.protocolVersion(), APPEND_GREATER_THAN_CURRENT_SIZE); + resp.headers().set(CONTENT_TYPE, "text/plain; charset=utf-8"); + return resp; + }; + + try (FakeHttpServer fakeHttpServer = FakeHttpServer.of(handler); + TmpFile tmpFile = + DataGenerator.base64Characters().tempFile(temp.newFolder().toPath(), _256KiBL)) { + URI endpoint = fakeHttpServer.getEndpoint(); + String uploadUrl = String.format("%s/upload/%s", endpoint.toString(), UUID.randomUUID()); + + AtomicLong confirmedBytes = new AtomicLong(-1L); + + JsonResumableSessionPutTask task = + new JsonResumableSessionPutTask( + httpClientContext, + uploadUrl, + RewindableContent.of(tmpFile.getPath()), + HttpContentRange.of(ByteRangeSpec.explicit(_512KiBL, _768KiBL))); + + StorageException se = assertThrows(StorageException.class, task::call); + assertThat(se.getCode()).isEqualTo(503); + assertThat(confirmedBytes.get()).isEqualTo(-1); + } + } + @Test public void jsonParseFailure() throws Exception { diff --git a/grpc-google-cloud-storage-v2/pom.xml b/grpc-google-cloud-storage-v2/pom.xml index 9eb6681628..5bc81ee7c8 100644 --- a/grpc-google-cloud-storage-v2/pom.xml +++ b/grpc-google-cloud-storage-v2/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-storage-v2 - 2.29.0-alpha + 2.29.1-alpha grpc-google-cloud-storage-v2 GRPC library for grpc-google-cloud-storage-v2 com.google.cloud google-cloud-storage-parent - 2.29.0 + 2.29.1 diff --git a/pom.xml b/pom.xml index f3f0e66c76..98833ebbc7 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-storage-parent pom - 2.29.0 + 2.29.1 Storage Parent https://github.com/googleapis/java-storage @@ -14,7 +14,7 @@ com.google.cloud google-cloud-shared-config - 1.6.0 + 1.6.1 @@ -54,7 +54,7 @@ UTF-8 github google-cloud-storage-parent - 3.18.0 + 3.19.0 @@ -76,17 +76,17 @@ com.google.cloud google-cloud-storage - 2.29.0 + 2.29.1 com.google.apis google-api-services-storage - v1-rev20231012-2.0.0 + v1-rev20231028-2.0.0 com.google.cloud google-cloud-pubsub - 1.125.7 + 1.125.11 test @@ -117,17 +117,17 @@ com.google.api.grpc proto-google-cloud-storage-v2 - 2.29.0-alpha + 2.29.1-alpha com.google.api.grpc grpc-google-cloud-storage-v2 - 2.29.0-alpha + 2.29.1-alpha com.google.api.grpc gapic-google-cloud-storage-v2 - 2.29.0-alpha + 2.29.1-alpha com.google.cloud diff --git a/proto-google-cloud-storage-v2/pom.xml b/proto-google-cloud-storage-v2/pom.xml index 365c5e81a3..6c791836e8 100644 --- a/proto-google-cloud-storage-v2/pom.xml +++ b/proto-google-cloud-storage-v2/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-storage-v2 - 2.29.0-alpha + 2.29.1-alpha proto-google-cloud-storage-v2 PROTO library for proto-google-cloud-storage-v2 com.google.cloud google-cloud-storage-parent - 2.29.0 + 2.29.1 diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index bff873fee1..4e1674d244 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -30,7 +30,7 @@ com.google.cloud google-cloud-storage - 2.28.0 + 2.29.0 @@ -61,7 +61,7 @@ com.google.cloud google-cloud-pubsub - 1.125.7 + 1.125.11 test diff --git a/samples/native-image-sample/pom.xml b/samples/native-image-sample/pom.xml index 41b80921a2..765965a3fa 100644 --- a/samples/native-image-sample/pom.xml +++ b/samples/native-image-sample/pom.xml @@ -29,7 +29,7 @@ com.google.cloud libraries-bom - 26.25.0 + 26.26.0 pom import @@ -61,7 +61,7 @@ com.google.cloud google-cloud-pubsub - 1.125.7 + 1.125.11 test @@ -86,7 +86,7 @@ org.apache.maven.plugins maven-dependency-plugin - 3.6.0 + 3.6.1 copy-dependencies @@ -124,7 +124,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.1.2 + 3.2.1 **/*IT diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index 4f8667e767..9a88bfdcda 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -28,7 +28,7 @@ com.google.cloud google-cloud-storage - 2.29.0 + 2.29.1 @@ -52,7 +52,7 @@ com.google.cloud google-cloud-pubsub - 1.125.7 + 1.125.11 test diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index 1452f748e3..beedf1aedc 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -31,7 +31,7 @@ com.google.cloud libraries-bom - 26.25.0 + 26.26.0 pom import @@ -72,7 +72,7 @@ com.google.cloud google-cloud-pubsub - 1.125.7 + 1.125.11 test diff --git a/samples/snippets/src/main/java/com/example/storage/bucket/GetBucketAutoclass.java b/samples/snippets/src/main/java/com/example/storage/bucket/GetBucketAutoclass.java index d01e0b3936..77430b03d1 100644 --- a/samples/snippets/src/main/java/com/example/storage/bucket/GetBucketAutoclass.java +++ b/samples/snippets/src/main/java/com/example/storage/bucket/GetBucketAutoclass.java @@ -20,7 +20,9 @@ import com.google.cloud.storage.BucketInfo.Autoclass; import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageClass; import com.google.cloud.storage.StorageOptions; +import java.time.OffsetDateTime; public class GetBucketAutoclass { public static void getBucketAutoclass(String projectId, String bucketName) { @@ -34,6 +36,8 @@ public static void getBucketAutoclass(String projectId, String bucketName) { Autoclass autoclass = storage.get(bucketName).getAutoclass(); String status = autoclass.getEnabled() ? "enabled" : "disabled"; String toggleTime = autoclass.getToggleTime().toString(); + StorageClass terminalStorageClass = autoclass.getTerminalStorageClass(); + OffsetDateTime terminalStorageClassUpdateTime = autoclass.getTerminalStorageClassUpdateTime(); System.out.println( "Autoclass is currently " @@ -41,7 +45,11 @@ public static void getBucketAutoclass(String projectId, String bucketName) { + " for bucket " + bucketName + " and was last changed at " - + toggleTime); + + toggleTime + + ". The terminal storage class is set to be " + + terminalStorageClass.name() + + " last updated at " + + terminalStorageClassUpdateTime.toString()); } } // [END storage_get_autoclass] diff --git a/samples/snippets/src/main/java/com/example/storage/bucket/SetBucketAutoclass.java b/samples/snippets/src/main/java/com/example/storage/bucket/SetBucketAutoclass.java index f59e383af5..0f9ae0f4ef 100644 --- a/samples/snippets/src/main/java/com/example/storage/bucket/SetBucketAutoclass.java +++ b/samples/snippets/src/main/java/com/example/storage/bucket/SetBucketAutoclass.java @@ -21,32 +21,54 @@ import com.google.cloud.storage.Bucket; import com.google.cloud.storage.BucketInfo.Autoclass; import com.google.cloud.storage.Storage; +import com.google.cloud.storage.Storage.BucketTargetOption; +import com.google.cloud.storage.StorageClass; import com.google.cloud.storage.StorageOptions; public class SetBucketAutoclass { - public static void setBucketAutoclass(String projectId, String bucketName) { + public static void setBucketAutoclass( + String projectId, String bucketName, StorageClass storageClass) throws Exception { // The ID of your GCP project // String projectId = "your-project-id"; // The ID of your GCS bucket // String bucketName = "your-unique-bucket-name"; - // Whether to set Autoclass to on or off. - // Note: Only update requests that disable qutoclass are currently supported. - // To enable autoclass, you must enable it at bucket creation time. - boolean enabled = false; + // The storage class that objects in an Autoclass bucket eventually transition to if not read + // for a certain length of time + // StorageClass storageClass = StorageClass.ARCHIVE; - Storage storage = StorageOptions.newBuilder().setProjectId(projectId).build().getService(); - Bucket bucket = storage.get(bucketName); + // Configure the Autoclass setting for a bucket. - bucket - .toBuilder() - .setAutoclass(Autoclass.newBuilder().setEnabled(enabled).build()) - .build() - .update(); + // Note: terminal_storage_class field is optional and defaults to NEARLINE if not otherwise + // specified. Valid terminal_storage_class values are NEARLINE and ARCHIVE. + boolean enabled = true; - System.out.println( - "Autoclass for bucket " + bucketName + " was " + (enabled ? "enabled." : "disabled.")); + try (Storage storage = + StorageOptions.newBuilder().setProjectId(projectId).build().getService()) { + Bucket bucket = storage.get(bucketName); + + Bucket toUpdate = + bucket + .toBuilder() + .setAutoclass( + Autoclass.newBuilder() + .setEnabled(enabled) + .setTerminalStorageClass(storageClass) + .build()) + .build(); + + Bucket updated = storage.update(toUpdate, BucketTargetOption.metagenerationMatch()); + + System.out.println( + "Autoclass for bucket " + + bucketName + + " was " + + (updated.getAutoclass().getEnabled() ? "enabled." : "disabled.")); + System.out.println( + "Autoclass terminal storage class is " + + updated.getAutoclass().getTerminalStorageClass().toString()); + } } } // [END storage_set_autoclass] diff --git a/samples/snippets/src/test/java/com/example/storage/bucket/AutoclassTest.java b/samples/snippets/src/test/java/com/example/storage/bucket/AutoclassTest.java index beb0dea6d8..82297c69e0 100644 --- a/samples/snippets/src/test/java/com/example/storage/bucket/AutoclassTest.java +++ b/samples/snippets/src/test/java/com/example/storage/bucket/AutoclassTest.java @@ -21,8 +21,8 @@ import com.example.storage.TestBase; import com.google.cloud.storage.BucketInfo; import com.google.cloud.storage.BucketInfo.Autoclass; +import com.google.cloud.storage.StorageClass; import com.google.cloud.storage.testing.RemoteStorageHelper; -import org.junit.Assert; import org.junit.Test; public class AutoclassTest extends TestBase { @@ -30,16 +30,16 @@ public class AutoclassTest extends TestBase { private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT"); @Test - public void testSetGetBucketAutoclass() { + public void testSetGetBucketAutoclass() throws Exception { String autoclassBucket = RemoteStorageHelper.generateBucketName(); storage.create( BucketInfo.newBuilder(autoclassBucket) .setAutoclass(Autoclass.newBuilder().setEnabled(true).build()) .build()); try { - SetBucketAutoclass.setBucketAutoclass(PROJECT_ID, autoclassBucket); + SetBucketAutoclass.setBucketAutoclass(PROJECT_ID, autoclassBucket, StorageClass.NEARLINE); Autoclass autoclass = storage.get(autoclassBucket).getAutoclass(); - Assert.assertFalse(autoclass.getEnabled()); + assertThat(autoclass.getEnabled()).isTrue(); GetBucketAutoclass.getBucketAutoclass(PROJECT_ID, autoclassBucket); assertThat(stdOut.getCapturedOutputAsUtf8String()) diff --git a/storage-shared-benchmarking/pom.xml b/storage-shared-benchmarking/pom.xml index 7ea1c3ea03..f31d9408ca 100644 --- a/storage-shared-benchmarking/pom.xml +++ b/storage-shared-benchmarking/pom.xml @@ -10,7 +10,7 @@ com.google.cloud google-cloud-storage-parent - 2.29.0 + 2.29.1 @@ -31,7 +31,7 @@ com.google.cloud google-cloud-storage - 2.29.0 + 2.29.1 tests diff --git a/storage-shared-benchmarking/src/main/java/com/google/cloud/storage/benchmarking/StorageSharedBenchmarkingCli.java b/storage-shared-benchmarking/src/main/java/com/google/cloud/storage/benchmarking/StorageSharedBenchmarkingCli.java index 8862b544f8..04734cca33 100644 --- a/storage-shared-benchmarking/src/main/java/com/google/cloud/storage/benchmarking/StorageSharedBenchmarkingCli.java +++ b/storage-shared-benchmarking/src/main/java/com/google/cloud/storage/benchmarking/StorageSharedBenchmarkingCli.java @@ -94,6 +94,9 @@ public void run() { case "w1r3": runWorkload1(); break; + case "w1r3-grpc-dp": + runWorkload4(); + break; default: throw new IllegalStateException("Specify a workload to run"); } @@ -105,6 +108,18 @@ private void runWorkload1() { StorageOptions retryStorageOptions = StorageOptions.newBuilder().setProjectId(project).setRetrySettings(retrySettings).build(); Storage storageClient = retryStorageOptions.getService(); + runW1R3(storageClient); + } + + private void runWorkload4() { + RetrySettings retrySettings = StorageOptions.getDefaultRetrySettings().toBuilder().build(); + StorageOptions retryStorageOptions = + StorageOptions.grpc().setRetrySettings(retrySettings).setAttemptDirectPath(true).build(); + Storage storageClient = retryStorageOptions.getService(); + runW1R3(storageClient); + } + + private void runW1R3(Storage storageClient) { Path tempDir = tempDirLocation != null ? Paths.get(tempDirLocation) diff --git a/versions.txt b/versions.txt index 34972a6539..060440e154 100644 --- a/versions.txt +++ b/versions.txt @@ -1,7 +1,7 @@ # Format: # module:released-version:current-version -google-cloud-storage:2.29.0:2.29.0 -gapic-google-cloud-storage-v2:2.29.0-alpha:2.29.0-alpha -grpc-google-cloud-storage-v2:2.29.0-alpha:2.29.0-alpha -proto-google-cloud-storage-v2:2.29.0-alpha:2.29.0-alpha +google-cloud-storage:2.29.1:2.29.1 +gapic-google-cloud-storage-v2:2.29.1-alpha:2.29.1-alpha +grpc-google-cloud-storage-v2:2.29.1-alpha:2.29.1-alpha +proto-google-cloud-storage-v2:2.29.1-alpha:2.29.1-alpha