Browse Source

Upgrade to latest GCS SDK (#124062)

Upgrades google cloud SDK used by repository-gcp to com.google.cloud:google-cloud-storage-bom:2.50.0

Closes: ES-9287
Nick Tindall 6 months ago
parent
commit
073ca0e888
20 changed files with 301 additions and 106 deletions
  1. 5 0
      docs/changelog/124062.yaml
  2. 120 15
      gradle/verification-metadata.xml
  3. 86 47
      modules/repository-gcs/build.gradle
  4. 22 0
      modules/repository-gcs/licenses/checker-qual-LICENSE.txt
  5. 0 0
      modules/repository-gcs/licenses/checker-qual-NOTICE.txt
  6. 0 5
      modules/repository-gcs/licenses/commons-logging-NOTICE.txt
  7. 0 0
      modules/repository-gcs/licenses/grpc-api-LICENSE.txt
  8. 0 0
      modules/repository-gcs/licenses/grpc-api-NOTICE.txt
  9. 0 20
      modules/repository-gcs/licenses/log4j-NOTICE.txt
  10. 0 0
      modules/repository-gcs/licenses/opentelemetry-api-LICENSE.txt
  11. 0 0
      modules/repository-gcs/licenses/opentelemetry-api-NOTICE.txt
  12. 2 2
      modules/repository-gcs/licenses/opentelemetry-context-LICENSE.txt
  13. 0 0
      modules/repository-gcs/licenses/opentelemetry-context-NOTICE.txt
  14. 21 0
      modules/repository-gcs/licenses/slf4j-api-LICENSE.txt
  15. 0 0
      modules/repository-gcs/licenses/slf4j-api-NOTICE.txt
  16. 13 3
      modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageBlobStore.java
  17. 5 0
      modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageService.java
  18. 15 8
      modules/repository-gcs/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageBlobContainerRetriesTests.java
  19. 1 1
      test/fixtures/gcs-fixture/src/main/java/fixture/gcs/GoogleCloudStorageHttpHandler.java
  20. 11 5
      test/fixtures/gcs-fixture/src/main/java/fixture/gcs/MockGcsBlobStore.java

+ 5 - 0
docs/changelog/124062.yaml

@@ -0,0 +1,5 @@
+pr: 124062
+summary: Upgrade to repository-gcs to use com.google.cloud:google-cloud-storage-bom:2.50.0
+area: Snapshot/Restore
+type: upgrade
+issues: []

+ 120 - 15
gradle/verification-metadata.xml

@@ -499,14 +499,14 @@
             <sha256 value="896280c8bd0a81a020272af6bfabd855810c4e4fd5ff5605e109f3bb3d2a30e3" origin="Generated by Gradle"/>
          </artifact>
       </component>
-      <component group="com.google.api" name="api-common" version="2.3.1">
-         <artifact name="api-common-2.3.1.jar">
-            <sha256 value="9a8c0f3a638eac791496709b7770c39bda9e56de50728f122237c813bb21f1ee" origin="Generated by Gradle"/>
+      <component group="com.google.api" name="api-common" version="2.46.1">
+         <artifact name="api-common-2.46.1.jar">
+            <sha256 value="8b11e1e1e42702cb80948e7ca62a9e06ddf82fe57a19cd68f9548eac80f39071" origin="Generated by Gradle"/>
          </artifact>
       </component>
-      <component group="com.google.api" name="gax" version="2.20.1">
-         <artifact name="gax-2.20.1.jar">
-            <sha256 value="e6588fc1786557fda95024a040b32106d58ffe72fab0933200c4d3c6197a4fe1" origin="Generated by Gradle"/>
+      <component group="com.google.api" name="gax" version="2.63.1">
+         <artifact name="gax-2.63.1.jar">
+            <sha256 value="26752413f76b8391dacefff40db867c1d33d0bf63d32954de3e9bb74cdcb8568" origin="Generated by Gradle"/>
          </artifact>
       </component>
       <component group="com.google.api" name="gax-httpjson" version="0.105.1">
@@ -514,6 +514,11 @@
             <sha256 value="4b7e1135eb4a97bce9d9d8c56128c5c30594dc2bebf26c9851ac582d2b43b2db" origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="com.google.api" name="gax-httpjson" version="2.63.1">
+         <artifact name="gax-httpjson-2.63.1.jar">
+            <sha256 value="291e606f53ea021ff25b5747bc2e347b9f6e27285faa48239d6762fe2515b60d" origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="com.google.api-client" name="google-api-client" version="1.33.1">
          <artifact name="google-api-client-1.33.1.jar">
             <sha256 value="d471ac82f1b285b3fde1e5b7ff265dad7ab9cd73a052bcb01ef35054f7d5db02" origin="Generated by Gradle"/>
@@ -524,11 +529,31 @@
             <sha256 value="05d846e83a2924c2a6facbd72817228fee68bb522b818b9804b26a712f678bf3" origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="com.google.api-client" name="google-api-client" version="2.7.2">
+         <artifact name="google-api-client-2.7.2.jar">
+            <sha256 value="63b754b7c4a92347c4992c42d295812f8d2ed6b9e8743edba4a8c98b92e9ca9b" origin="Generated by Gradle"/>
+         </artifact>
+      </component>
+      <component group="com.google.api.grpc" name="proto-google-cloud-storage-v2" version="2.50.0">
+         <artifact name="proto-google-cloud-storage-v2-2.50.0.jar">
+            <sha256 value="0b326af2e1828b582a973b026059da6bc1fc6cbb47254c2223873c9cd97cd6a8" origin="Generated by Gradle"/>
+         </artifact>
+      </component>
+      <component group="com.google.api.grpc" name="proto-google-common-protos" version="2.54.1">
+         <artifact name="proto-google-common-protos-2.54.1.jar">
+            <sha256 value="2fcff25fe8a90fcacb146a900222c497ba0a9a531271e6b135a76450d23b1ef2" origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="com.google.api.grpc" name="proto-google-common-protos" version="2.9.6">
          <artifact name="proto-google-common-protos-2.9.6.jar">
             <sha256 value="84e715d92500fd0d44408f58713d5d70ae857d022de3d66d500fbf27fae88103" origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="com.google.api.grpc" name="proto-google-iam-v1" version="1.49.1">
+         <artifact name="proto-google-iam-v1-1.49.1.jar">
+            <sha256 value="138efca2acdd71bca4133731a7f8e619c8fa6a8585901abf2e2a35700353ca74" origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="com.google.api.grpc" name="proto-google-iam-v1" version="1.6.2">
          <artifact name="proto-google-iam-v1-1.6.2.jar">
             <sha256 value="4fce59633b8a9871614231d213992e60e9b50e12f822c33d7e82a79c90addf25" origin="Generated by Gradle"/>
@@ -539,9 +564,9 @@
             <sha256 value="05d913bb9b2baa8e4dfd99a1aee4864240feebf6647986e37aea88f3020313fe" origin="Generated by Gradle"/>
          </artifact>
       </component>
-      <component group="com.google.apis" name="google-api-services-storage" version="v1-rev20220705-2.0.0">
-         <artifact name="google-api-services-storage-v1-rev20220705-2.0.0.jar">
-            <sha256 value="c6027d6c58089402620ce338351ca2efd414f813007844bc252ceb5f47789f4f" origin="Generated by Gradle"/>
+      <component group="com.google.apis" name="google-api-services-storage" version="v1-rev20250224-2.0.0">
+         <artifact name="google-api-services-storage-v1-rev20250224-2.0.0.jar">
+            <sha256 value="c63c5e3c5544998c080424edb3aa9d2ba101774dad7c19ebb44e939e728b0b3e" origin="Generated by Gradle"/>
          </artifact>
       </component>
       <component group="com.google.auth" name="google-auth-library-credentials" version="1.11.0">
@@ -549,24 +574,44 @@
             <sha256 value="2f47d7d2126576f13c22b7442e97593e9e6285adbd6d4c8dd779d87a8cbb8270" origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="com.google.auth" name="google-auth-library-credentials" version="1.33.1">
+         <artifact name="google-auth-library-credentials-1.33.1.jar">
+            <sha256 value="3367d627c5f4d1fa307a3c6ff95db56ad7b611ae4483fe21d72877fa037ff125" origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="com.google.auth" name="google-auth-library-oauth2-http" version="1.11.0">
          <artifact name="google-auth-library-oauth2-http-1.11.0.jar">
             <sha256 value="9fc1548aacc781367b5ca8da8e053018929d71af7303f6752952ddcb6f9b50c4" origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="com.google.auth" name="google-auth-library-oauth2-http" version="1.33.1">
+         <artifact name="google-auth-library-oauth2-http-1.33.1.jar">
+            <sha256 value="6a72ec2bb2350ca1970019e388d00808136e4da2e30296e9d8c346e3850b0eaa" origin="Generated by Gradle"/>
+         </artifact>
+      </component>
+      <component group="com.google.cloud" name="google-cloud-core" version="2.53.1">
+         <artifact name="google-cloud-core-2.53.1.jar">
+            <sha256 value="58e008f119a7aaf68d2d13f530e997db6797b7aaa70e08c563421627bed382b0" origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="com.google.cloud" name="google-cloud-core" version="2.8.28">
          <artifact name="google-cloud-core-2.8.28.jar">
             <sha256 value="f775b5dc6b6478d07be13c64b3aa8ee0eded5e2571ff737e86ed05958cce4e92" origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="com.google.cloud" name="google-cloud-core-http" version="2.53.1">
+         <artifact name="google-cloud-core-http-2.53.1.jar">
+            <sha256 value="c9ec3820b8f17ca5005e608b9f002c049467ccc888c86a54f44ddf1eb5ae9506" origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="com.google.cloud" name="google-cloud-core-http" version="2.8.28">
          <artifact name="google-cloud-core-http-2.8.28.jar">
             <sha256 value="96cd89a23a40b64baa28cba92676fba67d16cfbb1a7085827a1852a71c2dce46" origin="Generated by Gradle"/>
          </artifact>
       </component>
-      <component group="com.google.cloud" name="google-cloud-storage" version="2.13.1">
-         <artifact name="google-cloud-storage-2.13.1.jar">
-            <sha256 value="5fee796ca3c328aabe1b96adfbb264714e82b3ceee2971a83732c8cada12a9b6" origin="Generated by Gradle"/>
+      <component group="com.google.cloud" name="google-cloud-storage" version="2.50.0">
+         <artifact name="google-cloud-storage-2.50.0.jar">
+            <sha256 value="24b5289098fd7d3ffcb1f467339854855a2423d000d740dba7ba32521a533e31" origin="Generated by Gradle"/>
          </artifact>
       </component>
       <component group="com.google.code.findbugs" name="jsr305" version="3.0.0">
@@ -589,6 +634,11 @@
             <sha256 value="57928d6e5a6edeb2abd3770a8f95ba44dce45f3b23b7a9dc2b309c581552a78b" origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="com.google.code.gson" name="gson" version="2.12.1">
+         <artifact name="gson-2.12.1.jar">
+            <sha256 value="ebee13d5fb7477cd7f1cc010e0c356df8ca80709715248da97f79e35ccb4fbec" origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="com.google.code.gson" name="gson" version="2.2.4">
          <artifact name="gson-2.2.4.jar">
             <sha256 value="c0328cd07ca9e363a5acd00c1cf4afe8cf554bd6d373834981ba05cebec687fb" origin="Generated by Gradle"/>
@@ -649,6 +699,11 @@
             <sha256 value="a171ee4c734dd2da837e4b16be9df4661afab72a41adaf31eb84dfdaf936ca26" origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="com.google.guava" name="failureaccess" version="1.0.2">
+         <artifact name="failureaccess-1.0.2.jar">
+            <sha256 value="8a8f81cf9b359e3f6dfa691a1e776985c061ef2f223c9b2c80753e1b458e8064" origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="com.google.guava" name="guava" version="16.0.1">
          <artifact name="guava-16.0.1.jar">
             <sha256 value="a896857d07845d38c7dc5bbc0457b6d9b0f62ecffda010e5e9ec12d561f676d3" origin="Generated by Gradle"/>
@@ -684,6 +739,11 @@
             <sha256 value="6d4e2b5a118aab62e6e5e29d185a0224eed82c85c40ac3d33cf04a270c3b3744" origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="com.google.guava" name="guava" version="33.4.0-jre">
+         <artifact name="guava-33.4.0-jre.jar">
+            <sha256 value="b918c98a7e44dbe94ebd9fe3e40cddaadb5a93e6a78eb6008b42df237241e538" origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="com.google.guava" name="listenablefuture" version="9999.0-empty-to-avoid-conflict-with-guava">
          <artifact name="listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar">
             <sha256 value="b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99" origin="Generated by Gradle"/>
@@ -699,11 +759,21 @@
             <sha256 value="e395dd1765e3e6bceb0c610706bcf4128de84bd6e65cf1d4adbf998b4114161c" origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="com.google.http-client" name="google-http-client" version="1.46.3">
+         <artifact name="google-http-client-1.46.3.jar">
+            <sha256 value="2490a06e44b7f2adbcfe27e4099a576c0ee8d269437188d5391acd48c6d34310" origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="com.google.http-client" name="google-http-client-appengine" version="1.42.3">
          <artifact name="google-http-client-appengine-1.42.3.jar">
             <sha256 value="3a876b1f704ac6d8500b8430b288dbce78c52dc18c43c008a587a7377bb81e2e" origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="com.google.http-client" name="google-http-client-appengine" version="1.46.3">
+         <artifact name="google-http-client-appengine-1.46.3.jar">
+            <sha256 value="b0f48794b234439ad9e9b44f63874c78e8c553d03aa89f2864ca231e3c590b43" origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="com.google.http-client" name="google-http-client-gson" version="1.41.1">
          <artifact name="google-http-client-gson-1.41.1.jar">
             <sha256 value="29deba06c722e00dd31fe7e3b526bdfec0577660b868a1efaceeddb5e04208a8" origin="Generated by Gradle"/>
@@ -714,6 +784,11 @@
             <sha256 value="8196efaa89c5f73b00b2b48edad02fcd78524259407c37ab1860737988545cee" origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="com.google.http-client" name="google-http-client-gson" version="1.46.3">
+         <artifact name="google-http-client-gson-1.46.3.jar">
+            <sha256 value="74eee65c563871659469eb85d703e5a7223427010ad545982fdc53d7db573266" origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="com.google.http-client" name="google-http-client-jackson2" version="1.41.1">
          <artifact name="google-http-client-jackson2-1.41.1.jar">
             <sha256 value="3b9add5859979519236f45cc9c9f53da975e0b5b88e0cdfd4bb009b112b4d435" origin="Generated by Gradle"/>
@@ -724,6 +799,11 @@
             <sha256 value="b03a79f38e924e2eeaee673acf314626825284cb39948d06c2948ad083b4d089" origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="com.google.http-client" name="google-http-client-jackson2" version="1.46.3">
+         <artifact name="google-http-client-jackson2-1.46.3.jar">
+            <sha256 value="06368f5e3704fbe1d983736f802ed172fbc7d957a4f76b381fd0d8c8e92830e4" origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="com.google.inject" name="guice" version="3.0">
          <artifact name="guice-3.0.jar">
             <sha256 value="1a59d0421ffd355cc0b70b42df1c2e9af744c8a2d0c92da379f5fca2f07f1d22" origin="Generated by Gradle"/>
@@ -1369,6 +1449,11 @@
             <sha256 value="626cce8b732f65e3fad4111bea84376c423c7fddffd1c85f8ad8f214a18acde6" origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="io.grpc" name="grpc-api" version="1.70.0">
+         <artifact name="grpc-api-1.70.0.jar">
+            <sha256 value="45faf2ac1bf2791e8fdabce53684a86b62c99b84cba26fb13a5ba3f4abf80d6c" origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="io.grpc" name="grpc-context" version="1.27.2">
          <artifact name="grpc-context-1.27.2.jar">
             <sha256 value="bcbf9055dff453fd6508bd7cca2a0aa2d5f059a9c94beed1f5fda1dc015607b8" origin="Generated by Gradle"/>
@@ -1494,11 +1579,26 @@
             <sha256 value="7de2c7268850a9c1bae4401cf264febb871d811c6be8e5b3fb2cae52886e8ec1" origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="io.opentelemetry" name="opentelemetry-api" version="1.47.0">
+         <artifact name="opentelemetry-api-1.47.0.jar">
+            <sha256 value="6566f1f1133d611ff4e8b8fdb8eb18577b970425620315363ee9be43843b14bf" origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="io.opentelemetry" name="opentelemetry-context" version="1.31.0">
          <artifact name="opentelemetry-context-1.31.0.jar">
             <sha256 value="664896a5c34bcda20c95c8f45198a95e8f97a1cd5e5c2923978f42dddada787d" origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="io.opentelemetry" name="opentelemetry-context" version="1.47.0">
+         <artifact name="opentelemetry-context-1.47.0.jar">
+            <sha256 value="15b4fc4234e6dca6d54800d572694ecbd07ba52c15fc5b221b4da5517ce8d90d" origin="Generated by Gradle"/>
+         </artifact>
+      </component>
+      <component group="io.opentelemetry" name="opentelemetry-sdk" version="1.47.0">
+         <artifact name="opentelemetry-sdk-1.47.0.jar">
+            <sha256 value="4a09eb2ee484769973e14218a34e6da54f35955aa02b26dc5238b0c2ed6a801d" origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="io.opentelemetry" name="opentelemetry-semconv" version="1.21.0-alpha">
          <artifact name="opentelemetry-semconv-1.21.0-alpha.jar">
             <sha256 value="4a8f41b93eec51e85fa6b48e43de6785b742316fdd9c9baf595adbce6d5de6af" origin="Generated by Gradle"/>
@@ -3417,6 +3517,11 @@
             <sha256 value="ccaedd33af0b7894d9f2f3b644f4d19e43928e32902e61ac4d10777830f5aac7" origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="org.checkerframework" name="checker-qual" version="3.49.0">
+         <artifact name="checker-qual-3.49.0.jar">
+            <sha256 value="8b9d9a36eaaf7c0fc26503c83cd97d8c9c0f9e2913cc2a6e92ac26c735d4dcbe" origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="org.codehaus.groovy" name="groovy" version="3.0.9">
          <artifact name="groovy-3.0.9.jar">
             <sha256 value="77bf86897f295f8cae2e1f46b1eca109f487ba81b66ef24a2b6dcba1eb7d6ce7" origin="Generated by Gradle"/>
@@ -4602,9 +4707,9 @@
             <sha256 value="0218fcbfe6358c99f2a12935858829ba86dcb092ce8ca9f58fd8401c3ca7a149" origin="Generated by Gradle"/>
          </artifact>
       </component>
-      <component group="org.threeten" name="threetenbp" version="1.6.5">
-         <artifact name="threetenbp-1.6.5.jar">
-            <sha256 value="b2551604c0d6516428e3065213b74461240378a07622201a37ad32638ecb417c" origin="Generated by Gradle"/>
+      <component group="org.threeten" name="threetenbp" version="1.7.0">
+         <artifact name="threetenbp-1.7.0.jar">
+            <sha256 value="857917d2319a4e92dc1c5e3aeb75a0dac84445ed315e7ac3d82bb8d2b298977f" origin="Generated by Gradle"/>
          </artifact>
       </component>
       <component group="org.tmatesoft.sqljet" name="sqljet" version="1.1.10">

+ 86 - 47
modules/repository-gcs/build.gradle

@@ -18,40 +18,44 @@ apply plugin: 'elasticsearch.internal-cluster-test'
 
 esplugin {
   description = 'The GCS repository plugin adds Google Cloud Storage support for repositories.'
-  classname ='org.elasticsearch.repositories.gcs.GoogleCloudStoragePlugin'
+  classname = 'org.elasticsearch.repositories.gcs.GoogleCloudStoragePlugin'
 }
 
 dependencies {
-  api 'com.google.cloud:google-cloud-storage:2.13.1'
-  api 'com.google.cloud:google-cloud-core:2.8.28'
-  api 'com.google.cloud:google-cloud-core-http:2.8.28'
-  runtimeOnly 'com.google.guava:guava:32.0.1-jre'
-  runtimeOnly 'com.google.guava:failureaccess:1.0.1'
-  api "commons-logging:commons-logging:${versions.commonslogging}"
-  api "org.apache.logging.log4j:log4j-1.2-api:${versions.log4j}"
-  api "commons-codec:commons-codec:${versions.commonscodec}"
-  api 'com.google.api:api-common:2.3.1'
-  api 'com.google.api:gax:2.20.1'
-  api 'org.threeten:threetenbp:1.6.5'
-  api "com.google.protobuf:protobuf-java-util:${versions.protobuf}"
-  api "com.google.protobuf:protobuf-java:${versions.protobuf}"
-  api 'com.google.code.gson:gson:2.10'
-  api 'com.google.api.grpc:proto-google-common-protos:2.9.6'
-  api 'com.google.api.grpc:proto-google-iam-v1:1.6.2'
-  api 'com.google.auth:google-auth-library-credentials:1.11.0'
-  api 'com.google.auth:google-auth-library-oauth2-http:1.11.0'
-  api "com.google.oauth-client:google-oauth-client:${versions.google_oauth_client}"
-  api 'com.google.api-client:google-api-client:2.1.1'
-  api 'com.google.http-client:google-http-client:1.42.3'
-  api 'com.google.http-client:google-http-client-gson:1.42.3'
-  api 'com.google.http-client:google-http-client-appengine:1.42.3'
-  api 'com.google.http-client:google-http-client-jackson2:1.42.3'
-  api "com.fasterxml.jackson.core:jackson-core:${versions.jackson}"
-  api 'com.google.api:gax-httpjson:0.105.1'
-  api 'io.grpc:grpc-context:1.49.2'
-  api 'io.opencensus:opencensus-api:0.31.1'
-  api 'io.opencensus:opencensus-contrib-http-util:0.31.1'
-  api 'com.google.apis:google-api-services-storage:v1-rev20220705-2.0.0'
+  // dependencies consistent with 'com.google.cloud:google-cloud-storage-bom:2.50.0'
+  implementation 'com.google.cloud:google-cloud-storage:2.50.0'
+  implementation 'com.google.cloud:google-cloud-core:2.53.1'
+  implementation 'com.google.cloud:google-cloud-core-http:2.53.1'
+  runtimeOnly 'com.google.guava:guava:33.4.0-jre'
+  runtimeOnly 'com.google.guava:failureaccess:1.0.2'
+  runtimeOnly "org.slf4j:slf4j-api:${versions.slf4j}" // 2.0.16 in bom
+  runtimeOnly "commons-codec:commons-codec:${versions.commonscodec}" // 1.18.0 in bom
+  implementation 'com.google.api:api-common:2.46.1'
+  implementation 'com.google.api:gax:2.63.1'
+  implementation 'org.threeten:threetenbp:1.7.0'
+  runtimeOnly "com.google.protobuf:protobuf-java-util:${versions.protobuf}" // 3.25.5 in bom
+  runtimeOnly "com.google.protobuf:protobuf-java:${versions.protobuf}"
+  runtimeOnly 'com.google.code.gson:gson:2.12.1'
+  runtimeOnly 'com.google.api.grpc:proto-google-common-protos:2.54.1'
+  runtimeOnly 'com.google.api.grpc:proto-google-iam-v1:1.49.1'
+  implementation 'com.google.auth:google-auth-library-credentials:1.33.1'
+  implementation 'com.google.auth:google-auth-library-oauth2-http:1.33.1'
+  runtimeOnly "com.google.oauth-client:google-oauth-client:${versions.google_oauth_client}" // 1.37.0 in bom
+  implementation 'com.google.api-client:google-api-client:2.7.2'
+  implementation 'com.google.http-client:google-http-client:1.46.3'
+  runtimeOnly 'com.google.http-client:google-http-client-gson:1.46.3'
+  runtimeOnly 'com.google.http-client:google-http-client-appengine:1.46.3'
+  runtimeOnly 'com.google.http-client:google-http-client-jackson2:1.46.3'
+  runtimeOnly "com.fasterxml.jackson.core:jackson-core:${versions.jackson}" // 2.18.2 in bom
+  runtimeOnly 'com.google.api:gax-httpjson:2.63.1'
+  runtimeOnly 'io.opencensus:opencensus-api:0.31.1'
+  runtimeOnly 'io.opencensus:opencensus-contrib-http-util:0.31.1'
+  implementation 'com.google.apis:google-api-services-storage:v1-rev20250224-2.0.0'
+  implementation 'org.checkerframework:checker-qual:3.49.0'
+  runtimeOnly 'io.opentelemetry:opentelemetry-api:1.47.0'
+  runtimeOnly 'io.opentelemetry:opentelemetry-context:1.47.0'
+  runtimeOnly 'com.google.api.grpc:proto-google-cloud-storage-v2:2.50.0'
+  runtimeOnly 'io.grpc:grpc-api:1.70.0'
 
   testImplementation "org.apache.httpcomponents:httpclient:${versions.httpclient}"
   testImplementation "org.apache.httpcomponents:httpcore:${versions.httpcore}"
@@ -62,7 +66,7 @@ dependencies {
 
 restResources {
   restApi {
-    include '_common', 'cluster', 'nodes', 'snapshot','indices', 'index', 'bulk', 'count'
+    include '_common', 'cluster', 'nodes', 'snapshot', 'indices', 'index', 'bulk', 'count'
   }
 }
 
@@ -123,11 +127,6 @@ tasks.named("thirdPartyAudit").configure {
     'com.google.appengine.api.urlfetch.HTTPResponse',
     'com.google.appengine.api.urlfetch.URLFetchService',
     'com.google.appengine.api.urlfetch.URLFetchServiceFactory',
-    // commons-logging optional dependencies
-    'org.apache.avalon.framework.logger.Logger',
-    'org.apache.log.Hierarchy',
-    'org.apache.log.Logger',
-    'javax.jms.Message',
 
     // optional apache http client dependencies
     'org.apache.http.ConnectionReuseStrategy',
@@ -171,19 +170,59 @@ tasks.named("thirdPartyAudit").configure {
     'org.apache.http.protocol.HttpProcessor',
     'org.apache.http.protocol.HttpRequestExecutor',
 
-    // commons-logging provided dependencies
-    'javax.servlet.ServletContextEvent',
-    'javax.servlet.ServletContextListener'
+    // grpc/proto stuff
+    'com.google.api.gax.grpc.GrpcCallContext',
+    'com.google.api.gax.grpc.GrpcCallSettings',
+    'com.google.api.gax.grpc.GrpcCallSettings$Builder',
+    'com.google.api.gax.grpc.GrpcInterceptorProvider',
+    'com.google.api.gax.grpc.GrpcStatusCode',
+    'com.google.api.gax.grpc.GrpcStubCallableFactory',
+    'com.google.api.gax.grpc.InstantiatingGrpcChannelProvider',
+    'com.google.api.gax.grpc.InstantiatingGrpcChannelProvider$Builder',
+    'com.google.cloud.grpc.GrpcTransportOptions',
+    'com.google.cloud.grpc.GrpcTransportOptions$Builder',
+    'com.google.cloud.opentelemetry.metric.GoogleCloudMetricExporter',
+    'com.google.cloud.opentelemetry.metric.MetricConfiguration',
+    'com.google.cloud.opentelemetry.metric.MetricConfiguration$Builder',
+    'com.google.storage.v2.StorageClient',
+    'com.google.storage.v2.StorageClient$ListBucketsPagedResponse',
+    'com.google.storage.v2.StorageSettings',
+    'com.google.storage.v2.StorageSettings$Builder',
+    'com.google.storage.v2.stub.GrpcStorageStub',
+    'com.google.storage.v2.stub.StorageStubSettings',
+    // opentelemetry implementation stuff
+    'io.grpc.opentelemetry.GrpcOpenTelemetry',
+    'io.grpc.opentelemetry.GrpcOpenTelemetry$Builder',
+    'io.grpc.protobuf.ProtoUtils',
+    'io.opentelemetry.contrib.gcp.resource.GCPResourceProvider',
+    'io.opentelemetry.sdk.OpenTelemetrySdk',
+    'io.opentelemetry.sdk.OpenTelemetrySdkBuilder',
+    'io.opentelemetry.sdk.common.CompletableResultCode',
+    'io.opentelemetry.sdk.common.export.MemoryMode',
+    'io.opentelemetry.sdk.metrics.Aggregation',
+    'io.opentelemetry.sdk.metrics.InstrumentSelector',
+    'io.opentelemetry.sdk.metrics.InstrumentSelectorBuilder',
+    'io.opentelemetry.sdk.metrics.InstrumentType',
+    'io.opentelemetry.sdk.metrics.SdkMeterProvider',
+    'io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder',
+    'io.opentelemetry.sdk.metrics.View',
+    'io.opentelemetry.sdk.metrics.ViewBuilder',
+    'io.opentelemetry.sdk.metrics.data.AggregationTemporality',
+    'io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector',
+    'io.opentelemetry.sdk.metrics.export.MetricExporter',
+    'io.opentelemetry.sdk.metrics.export.PeriodicMetricReader',
+    'io.opentelemetry.sdk.metrics.export.PeriodicMetricReaderBuilder',
+    'io.opentelemetry.sdk.resources.Resource',
   )
 
 
-  if(buildParams.graalVmRuntime == false) {
+  if (buildParams.graalVmRuntime == false) {
     ignoreMissingClasses(
-        'org.graalvm.nativeimage.hosted.Feature',
-        'org.graalvm.nativeimage.hosted.Feature$BeforeAnalysisAccess',
-        'org.graalvm.nativeimage.hosted.Feature$DuringAnalysisAccess',
-        'org.graalvm.nativeimage.hosted.Feature$FeatureAccess',
-        'org.graalvm.nativeimage.hosted.RuntimeReflection'
+      'org.graalvm.nativeimage.hosted.Feature',
+      'org.graalvm.nativeimage.hosted.Feature$BeforeAnalysisAccess',
+      'org.graalvm.nativeimage.hosted.Feature$DuringAnalysisAccess',
+      'org.graalvm.nativeimage.hosted.Feature$FeatureAccess',
+      'org.graalvm.nativeimage.hosted.RuntimeReflection'
     )
   }
 }
@@ -213,7 +252,7 @@ Map<String, Object> expansions = [
 
 tasks.named("processYamlRestTestResources").configure {
   inputs.properties(expansions)
-  filter("tokens" : expansions, ReplaceTokens.class)
+  filter("tokens": expansions, ReplaceTokens.class)
 }
 
 tasks.named("internalClusterTest").configure {

+ 22 - 0
modules/repository-gcs/licenses/checker-qual-LICENSE.txt

@@ -0,0 +1,22 @@
+Checker Framework qualifiers
+Copyright 2004-present by the Checker Framework developers
+
+MIT License:
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 0 - 0
modules/repository-gcs/licenses/grpc-context-NOTICE.txt → modules/repository-gcs/licenses/checker-qual-NOTICE.txt


+ 0 - 5
modules/repository-gcs/licenses/commons-logging-NOTICE.txt

@@ -1,5 +0,0 @@
-Apache Commons CLI
-Copyright 2001-2009 The Apache Software Foundation
-
-This product includes software developed by
-The Apache Software Foundation (http://www.apache.org/).

+ 0 - 0
modules/repository-gcs/licenses/grpc-context-LICENSE.txt → modules/repository-gcs/licenses/grpc-api-LICENSE.txt


+ 0 - 0
modules/repository-gcs/licenses/grpc-api-NOTICE.txt


+ 0 - 20
modules/repository-gcs/licenses/log4j-NOTICE.txt

@@ -1,20 +0,0 @@
-Apache Log4j
-Copyright 1999-2023 Apache Software Foundation
-
-This product includes software developed at
-The Apache Software Foundation (http://www.apache.org/).
-
-ResolverUtil.java
-Copyright 2005-2006 Tim Fennell
-
-Dumbster SMTP test server
-Copyright 2004 Jason Paul Kitchen
-
-TypeUtil.java
-Copyright 2002-2012 Ramnivas Laddad, Juergen Hoeller, Chris Beams
-
-picocli (http://picocli.info)
-Copyright 2017 Remko Popma
-
-TimeoutBlockingWaitStrategy.java and parts of Util.java
-Copyright 2011 LMAX Ltd.

+ 0 - 0
modules/repository-gcs/licenses/commons-logging-LICENSE.txt → modules/repository-gcs/licenses/opentelemetry-api-LICENSE.txt


+ 0 - 0
modules/repository-gcs/licenses/opentelemetry-api-NOTICE.txt


+ 2 - 2
modules/repository-gcs/licenses/log4j-LICENSE.txt → modules/repository-gcs/licenses/opentelemetry-context-LICENSE.txt

@@ -1,4 +1,3 @@
-
                                  Apache License
                            Version 2.0, January 2004
                         http://www.apache.org/licenses/
@@ -187,7 +186,7 @@
       same "printed page" as the copyright notice for easier
       identification within third-party archives.
 
-   Copyright 1999-2005 The Apache Software Foundation
+   Copyright [yyyy] [name of copyright owner]
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -200,3 +199,4 @@
    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.
+

+ 0 - 0
modules/repository-gcs/licenses/opentelemetry-context-NOTICE.txt


+ 21 - 0
modules/repository-gcs/licenses/slf4j-api-LICENSE.txt

@@ -0,0 +1,21 @@
+Copyright (c) 2004-2014 QOS.ch
+All rights reserved.
+
+Permission is hereby granted, free  of charge, to any person obtaining
+a  copy  of this  software  and  associated  documentation files  (the
+"Software"), to  deal in  the Software without  restriction, including
+without limitation  the rights to  use, copy, modify,  merge, publish,
+distribute,  sublicense, and/or sell  copies of  the Software,  and to
+permit persons to whom the Software  is furnished to do so, subject to
+the following conditions:
+
+The  above  copyright  notice  and  this permission  notice  shall  be
+included in all copies or substantial portions of the Software.
+
+THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
+EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
+MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 0 - 0
modules/repository-gcs/licenses/slf4j-api-NOTICE.txt


+ 13 - 3
modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageBlobStore.java

@@ -75,9 +75,9 @@ import static org.elasticsearch.core.Strings.format;
 class GoogleCloudStorageBlobStore implements BlobStore {
 
     /**
-     * see com.google.cloud.BaseWriteChannel#DEFAULT_CHUNK_SIZE
+     * see {@link com.google.cloud.storage.BaseStorageWriteChannel#chunkSize}
      */
-    static final int SDK_DEFAULT_CHUNK_SIZE = 60 * 256 * 1024;
+    static final int SDK_DEFAULT_CHUNK_SIZE = Math.toIntExact(ByteSizeValue.ofMb(16).getBytes());
 
     private static final Logger logger = LogManager.getLogger(GoogleCloudStorageBlobStore.class);
 
@@ -652,7 +652,17 @@ class GoogleCloudStorageBlobStore implements BlobStore {
         @SuppressForbidden(reason = "channel is based on a socket")
         @Override
         public int write(final ByteBuffer src) throws IOException {
-            return SocketAccess.doPrivilegedIOException(() -> channel.write(src));
+            try {
+                return SocketAccess.doPrivilegedIOException(() -> channel.write(src));
+            } catch (IOException e) {
+                // BaseStorageWriteChannel#write wraps StorageException in an IOException, but BaseStorageWriteChannel#close
+                // does not, if we unwrap StorageExceptions here, it simplifies our retry-on-gone logic
+                final StorageException storageException = (StorageException) ExceptionsHelper.unwrap(e, StorageException.class);
+                if (storageException != null) {
+                    throw storageException;
+                }
+                throw e;
+            }
         }
 
         @Override

+ 5 - 0
modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageService.java

@@ -41,6 +41,7 @@ import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.net.HttpURLConnection;
 import java.net.Proxy;
+import java.net.SocketException;
 import java.net.URI;
 import java.net.URL;
 import java.net.UnknownHostException;
@@ -275,6 +276,10 @@ public class GoogleCloudStorageService {
                 if (ExceptionsHelper.unwrap(prevThrowable, UnknownHostException.class) != null) {
                     return true;
                 }
+                // Also retry on `SocketException`s
+                if (ExceptionsHelper.unwrap(prevThrowable, SocketException.class) != null) {
+                    return true;
+                }
                 return delegate.shouldRetry(prevThrowable, prevResponse);
             }
         );

+ 15 - 8
modules/repository-gcs/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageBlobContainerRetriesTests.java

@@ -21,6 +21,7 @@ import com.google.cloud.storage.StorageOptions;
 import com.sun.net.httpserver.HttpHandler;
 
 import org.apache.http.HttpStatus;
+import org.elasticsearch.ExceptionsHelper;
 import org.elasticsearch.common.BackoffPolicy;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.UUIDs;
@@ -327,8 +328,7 @@ public class GoogleCloudStorageBlobContainerRetriesTests extends AbstractBlobCon
     }
 
     public void testWriteLargeBlob() throws IOException {
-        // See {@link BaseWriteChannel#DEFAULT_CHUNK_SIZE}
-        final int defaultChunkSize = 60 * 256 * 1024;
+        final int defaultChunkSize = GoogleCloudStorageBlobStore.SDK_DEFAULT_CHUNK_SIZE;
         final int nbChunks = randomIntBetween(3, 5);
         final int lastChunkSize = randomIntBetween(1, defaultChunkSize - 1);
         final int totalChunks = nbChunks + 1;
@@ -412,13 +412,16 @@ public class GoogleCloudStorageBlobContainerRetriesTests extends AbstractBlobCon
                     }
                 }
 
-                final String range = exchange.getRequestHeaders().getFirst("Content-Range");
-                assertTrue(Strings.hasLength(range));
+                final String contentRangeHeaderValue = exchange.getRequestHeaders().getFirst("Content-Range");
+                final HttpHeaderParser.ContentRange contentRange = HttpHeaderParser.parseContentRangeHeader(contentRangeHeaderValue);
+                assertNotNull("Invalid content range header: " + contentRangeHeaderValue, contentRange);
 
-                if (range.equals("bytes */*")) {
+                if (contentRange.hasRange() == false) {
+                    // Content-Range: */... is a status check
+                    // https://cloud.google.com/storage/docs/performing-resumable-uploads#status-check
                     final int receivedSoFar = bytesReceived.get();
                     if (receivedSoFar > 0) {
-                        exchange.getResponseHeaders().add("Range", Strings.format("bytes=0-%d", receivedSoFar));
+                        exchange.getResponseHeaders().add("Range", Strings.format("bytes=0-%s", receivedSoFar));
                     }
                     exchange.getResponseHeaders().add("Content-Length", "0");
                     exchange.sendResponseHeaders(308 /* Resume Incomplete */, -1);
@@ -429,7 +432,6 @@ public class GoogleCloudStorageBlobContainerRetriesTests extends AbstractBlobCon
 
                     assertThat(Math.toIntExact(requestBody.length()), anyOf(equalTo(defaultChunkSize), equalTo(lastChunkSize)));
 
-                    final HttpHeaderParser.ContentRange contentRange = HttpHeaderParser.parseContentRangeHeader(range);
                     final int rangeStart = Math.toIntExact(contentRange.start());
                     final int rangeEnd = Math.toIntExact(contentRange.end());
                     assertThat(rangeEnd + 1 - rangeStart, equalTo(Math.toIntExact(requestBody.length())));
@@ -437,15 +439,20 @@ public class GoogleCloudStorageBlobContainerRetriesTests extends AbstractBlobCon
                     bytesReceived.updateAndGet(existing -> Math.max(existing, rangeEnd));
 
                     if (contentRange.size() != null) {
+                        exchange.getResponseHeaders().add("x-goog-stored-content-length", String.valueOf(bytesReceived.get() + 1));
                         exchange.sendResponseHeaders(RestStatus.OK.getStatus(), -1);
                         return;
                     } else {
-                        exchange.getResponseHeaders().add("Range", Strings.format("bytes=%d/%d", rangeStart, rangeEnd));
+                        exchange.getResponseHeaders().add("Range", Strings.format("bytes=%s-%s", rangeStart, rangeEnd));
                         exchange.getResponseHeaders().add("Content-Length", "0");
                         exchange.sendResponseHeaders(308 /* Resume Incomplete */, -1);
                         return;
                     }
                 }
+            } else {
+                ExceptionsHelper.maybeDieOnAnotherThread(
+                    new AssertionError("Unexpected request" + exchange.getRequestMethod() + " " + exchange.getRequestURI())
+                );
             }
 
             if (randomBoolean()) {

+ 1 - 1
test/fixtures/gcs-fixture/src/main/java/fixture/gcs/GoogleCloudStorageHttpHandler.java

@@ -253,7 +253,7 @@ public class GoogleCloudStorageHttpHandler implements HttpHandler {
                 if (updateResponse.rangeHeader() != null) {
                     exchange.getResponseHeaders().add("Range", updateResponse.rangeHeader().headerString());
                 }
-                exchange.getResponseHeaders().add("Content-Length", "0");
+                exchange.getResponseHeaders().add("x-goog-stored-content-length", String.valueOf(updateResponse.storedContentLength()));
                 exchange.sendResponseHeaders(updateResponse.statusCode(), -1);
             } else {
                 exchange.sendResponseHeaders(RestStatus.NOT_FOUND.getStatus(), -1);

+ 11 - 5
test/fixtures/gcs-fixture/src/main/java/fixture/gcs/MockGcsBlobStore.java

@@ -117,10 +117,16 @@ public class MockGcsBlobStore {
             if (contentRange.hasRange() == false) {
                 // Content-Range: */... is a status check https://cloud.google.com/storage/docs/performing-resumable-uploads#status-check
                 if (existing.completed) {
-                    updateResponse.set(new UpdateResponse(RestStatus.OK.getStatus(), calculateRangeHeader(blobs.get(existing.path))));
+                    updateResponse.set(
+                        new UpdateResponse(
+                            RestStatus.OK.getStatus(),
+                            calculateRangeHeader(blobs.get(existing.path)),
+                            existing.contents.length()
+                        )
+                    );
                 } else {
                     final HttpHeaderParser.Range range = calculateRangeHeader(existing);
-                    updateResponse.set(new UpdateResponse(RESUME_INCOMPLETE, range));
+                    updateResponse.set(new UpdateResponse(RESUME_INCOMPLETE, range, existing.contents.length()));
                 }
                 return existing;
             } else {
@@ -146,11 +152,11 @@ public class MockGcsBlobStore {
                 // We just received the last chunk, update the blob and remove the resumable upload from the map
                 if (contentRange.hasSize() && updatedContent.length() == contentRange.size()) {
                     updateBlob(existing.path(), existing.ifGenerationMatch, updatedContent);
-                    updateResponse.set(new UpdateResponse(RestStatus.OK.getStatus(), null));
+                    updateResponse.set(new UpdateResponse(RestStatus.OK.getStatus(), null, updatedContent.length()));
                     return existing.update(BytesArray.EMPTY, true);
                 }
                 final ResumableUpload updated = existing.update(updatedContent, false);
-                updateResponse.set(new UpdateResponse(RESUME_INCOMPLETE, calculateRangeHeader(updated)));
+                updateResponse.set(new UpdateResponse(RESUME_INCOMPLETE, calculateRangeHeader(updated), updated.contents.length()));
                 return updated;
             }
         });
@@ -166,7 +172,7 @@ public class MockGcsBlobStore {
         return blob.contents.length() > 0 ? new HttpHeaderParser.Range(0, blob.contents.length() - 1) : null;
     }
 
-    record UpdateResponse(int statusCode, HttpHeaderParser.Range rangeHeader) {}
+    record UpdateResponse(int statusCode, HttpHeaderParser.Range rangeHeader, long storedContentLength) {}
 
     void deleteBlob(String path) {
         blobs.remove(path);