Repository: googleapis/java-storage
Pull request #1619 (2022-09-14 21:08:17)

feat: implement GrpcStorageImpl#*Iam* methods
* Implement GrpcStorageImpl#getIamPolicy
* Implement GrpcStorageImpl#setIamPolicy
* Implement GrpcStorageImpl#testIamPermissions

### Add Codecs for policy related messages to GrpcConversions
* New `Codec<com.google.cloud.Policy, com.google.iam.v1.Policy>`
* New `Codec<com.google.cloud.Binding, com.google.iam.v1.Binding>`
* New `Codec<com.google.cloud.Condition, com.google.type.Expr>`

### Refactor PolicyHelper to methods in ApiaryConversions

### Tests
Relying upon integration tests, as the backend is better at validating the actual values passed along. Codecs have been implemented to wire through the various string values matching property names.



Patch:
diff --git a/google-cloud-storage/pom.xml b/google-cloud-storage/pom.xml
index c27c7269..681729ae 100644
--- a/google-cloud-storage/pom.xml
+++ b/google-cloud-storage/pom.xml
@@ -35,7 +35,6 @@
     <dependency>
       <groupId>com.google.http-client</groupId>
       <artifactId>google-http-client-gson</artifactId>
-      <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>com.google.api-client</groupId>
@@ -274,6 +273,12 @@
               <dependency>net.jqwik:jqwik-engine</dependency>
               <dependency>net.jqwik:jqwik-time</dependency>
               <dependency>net.jqwik:jqwik-web</dependency>
+              <!--
+              We're only using google-http-client-gson in our tests, however if we scope it to test that will
+              break flatten plugins resolution where google-http-client-gson is needed in order for
+              google-api-client to be able to bootstrap.
+              -->
+              <dependency>com.google.http-client:google-http-client-gson</dependency>
             </ignoredDependencies>
           </configuration>
         </plugin>
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/ApiaryConversions.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/ApiaryConversions.java
index 37505948..85afcaee 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/ApiaryConversions.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/ApiaryConversions.java
@@ -42,6 +42,8 @@ import com.google.api.services.storage.model.BucketAccessControl;
 import com.google.api.services.storage.model.ObjectAccessControl;
 import com.google.api.services.storage.model.StorageObject;
 import com.google.api.services.storage.model.StorageObject.Owner;
+import com.google.cloud.Binding;
+import com.google.cloud.Policy;
 import com.google.cloud.storage.Acl.Domain;
 import com.google.cloud.storage.Acl.Entity;
 import com.google.cloud.storage.Acl.Group;
@@ -118,6 +120,12 @@ final class ApiaryConversions {
   private final Codec<CustomPlacementConfig, Bucket.CustomPlacementConfig>
       customPlacementConfigCodec =
           Codec.of(this::customPlacementConfigEncode, this::customPlacementConfigDecode);
+  private final Codec<Policy, com.google.api.services.storage.model.Policy> policyCodec =
+      Codec.of(this::policyEncode, this::policyDecode);
+  private final Codec<Binding, com.google.api.services.storage.model.Policy.Bindings> bindingCodec =
+      Codec.of(this::bindingEncode, this::bindingDecode);
+  private final Codec<com.google.cloud.Condition, com.google.api.services.storage.model.Expr>
+      iamConditionCodec = Codec.of(this::conditionEncode, this::conditionDecode);
 
   private ApiaryConversions() {}
 
@@ -189,6 +197,10 @@ final class ApiaryConversions {
     return customPlacementConfigCodec;
   }
 
+  Codec<Policy, com.google.api.services.storage.model.Policy> policyCodec() {
+    return policyCodec;
+  }
+
   private StorageObject blobInfoEncode(BlobInfo from) {
     StorageObject to = blobIdEncode(from.getBlobId());
     ifNonNull(from.getAcl(), toImmutableListOf(objectAcl()::encode), to::setAcl);
@@ -790,6 +802,74 @@ final class ApiaryConversions {
     return builder.build();
   }
 
+  private com.google.api.services.storage.model.Policy policyEncode(Policy from) {
+    com.google.api.services.storage.model.Policy to =
+        new com.google.api.services.storage.model.Policy();
+    ifNonNull(from.getEtag(), to::setEtag);
+    ifNonNull(from.getVersion(), to::setVersion);
+    if (from.getBindingsList() != null) {
+      ImmutableList<com.google.api.services.storage.model.Policy.Bindings> bindings =
+          from.getBindingsList().stream()
+              .map(bindingCodec::encode)
+              .collect(ImmutableList.toImmutableList());
+      to.setBindings(bindings);
+    }
+    return to;
+  }
+
+  private Policy policyDecode(com.google.api.services.storage.model.Policy from) {
+    Policy.Builder to = Policy.newBuilder();
+    if (!from.getEtag().isEmpty()) {
+      to.setEtag(from.getEtag());
+    }
+    to.setVersion(from.getVersion());
+    if (from.getBindings() != null) {
+      ImmutableList<Binding> bindings =
+          from.getBindings().stream()
+              .map(bindingCodec::decode)
+              .collect(ImmutableList.toImmutableList());
+      to.setBindings(bindings);
+    }
+    return to.build();
+  }
+
+  private com.google.api.services.storage.model.Policy.Bindings bindingEncode(Binding from) {
+    com.google.api.services.storage.model.Policy.Bindings to =
+        new com.google.api.services.storage.model.Policy.Bindings();
+    ifNonNull(from.getRole(), to::setRole);
+    ifNonNull(from.getMembers(), to::setMembers);
+    ifNonNull(from.getCondition(), iamConditionCodec::encode, to::setCondition);
+    return to;
+  }
+
+  private Binding bindingDecode(com.google.api.services.storage.model.Policy.Bindings from) {
+    Binding.Builder to = Binding.newBuilder();
+    ifNonNull(from.getRole(), to::setRole);
+    ifNonNull(from.getMembers(), to::setMembers);
+    ifNonNull(from.getCondition(), iamConditionCodec::decode, to::setCondition);
+    return to.build();
+  }
+
+  private com.google.api.services.storage.model.Expr conditionEncode(
+      com.google.cloud.Condition from) {
+    com.google.api.services.storage.model.Expr to =
+        new com.google.api.services.storage.model.Expr();
+    ifNonNull(from.getExpression(), to::setExpression);
+    ifNonNull(from.getTitle(), to::setTitle);
+    ifNonNull(from.getDescription(), to::setDescription);
+    // apiary doesn't have a "location" field like grpc does
+    return to;
+  }
+
+  private com.google.cloud.Condition conditionDecode(
+      com.google.api.services.storage.model.Expr from) {
+    com.google.cloud.Condition.Builder to = com.google.cloud.Condition.newBuilder();
+    ifNonNull(from.getExpression(), to::setExpression);
+    ifNonNull(from.getTitle(), to::setTitle);
+    ifNonNull(from.getDescription(), to::setDescription);
+    return to.build();
+  }
+
   private Bucket.CustomPlacementConfig customPlacementConfigEncode(CustomPlacementConfig from) {
     Bucket.CustomPlacementConfig to = null;
     if (from.getDataLocations() != null) {
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcConversions.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcConversions.java
index f4d6ac18..001a309c 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcConversions.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcConversions.java
@@ -24,6 +24,9 @@ import static com.google.cloud.storage.Utils.projectNameCodec;
 import static com.google.cloud.storage.Utils.toImmutableListOf;
 import static com.google.cloud.storage.Utils.todo;
 
+import com.google.cloud.Binding;
+import com.google.cloud.Condition;
+import com.google.cloud.Policy;
 import com.google.cloud.storage.Acl.Entity;
 import com.google.cloud.storage.Acl.Role;
 import com.google.cloud.storage.BlobInfo.CustomerEncryption;
@@ -50,6 +53,7 @@ import com.google.storage.v2.ObjectAccessControl;
 import com.google.storage.v2.ObjectChecksums;
 import com.google.storage.v2.Owner;
 import com.google.type.Date;
+import com.google.type.Expr;
 import java.time.Instant;
 import java.time.LocalDate;
 import java.time.OffsetDateTime;
@@ -88,6 +92,13 @@ final class GrpcConversions {
   private final Codec<BlobInfo, Object> blobInfoCodec =
       Codec.of(this::blobInfoEncode, this::blobInfoDecode);
   private final Codec<?, ?> notificationInfoCodec = Codec.of(Utils::todo, Utils::todo);
+  private final Codec<Policy, com.google.iam.v1.Policy> policyCodec =
+      Codec.of(this::policyEncode, this::policyDecode);
+  private final Codec<Binding, com.google.iam.v1.Binding> bindingCodec =
+      Codec.of(this::bindingEncode, this::bindingDecode);
+  private final Codec<Condition, Expr> iamConditionCodec =
+      Codec.of(this::conditionEncode, this::conditionDecode);
+
   private final Codec<Integer, String> crc32cCodec =
       Codec.of(this::crc32cEncode, this::crc32cDecode);
 
@@ -182,6 +193,10 @@ final class GrpcConversions {
     return todo();
   }
 
+  Codec<Policy, com.google.iam.v1.Policy> policyCodec() {
+    return policyCodec;
+  }
+
   private BucketInfo bucketInfoDecode(Bucket from) {
     BucketInfo.Builder to = new BucketInfo.BuilderImpl(bucketNameCodec.decode(from.getName()));
     to.setProject(from.getProject());
@@ -845,6 +860,61 @@ final class GrpcConversions {
     return toBuilder.build();
   }
 
+  private com.google.iam.v1.Policy policyEncode(Policy from) {
+    com.google.iam.v1.Policy.Builder to = com.google.iam.v1.Policy.newBuilder();
+    ifNonNull(from.getEtag(), ByteString::copyFromUtf8, to::setEtag);
+    ifNonNull(from.getVersion(), to::setVersion);
+    from.getBindingsList().stream().map(bindingCodec::encode).forEach(to::addBindings);
+    return to.build();
+  }
+
+  private Policy policyDecode(com.google.iam.v1.Policy from) {
+    Policy.Builder to = Policy.newBuilder();
+    if (!from.getEtag().isEmpty()) {
+      to.setEtag(from.getEtag().toStringUtf8());
+    }
+    to.setVersion(from.getVersion());
+    ImmutableList<Binding> bindings =
+        from.getBindingsList().stream()
+            .map(bindingCodec::decode)
+            .collect(ImmutableList.toImmutableList());
+    to.setBindings(bindings);
+    return to.build();
+  }
+
+  private com.google.iam.v1.Binding bindingEncode(Binding from) {
+    com.google.iam.v1.Binding.Builder to = com.google.iam.v1.Binding.newBuilder();
+    to.setRole(from.getRole());
+    to.addAllMembers(from.getMembers());
+    to.setCondition(iamConditionCodec.encode(from.getCondition()));
+    return to.build();
+  }
+
+  private Binding bindingDecode(com.google.iam.v1.Binding from) {
+    Binding.Builder to = Binding.newBuilder();
+    to.setRole(from.getRole());
+    to.setMembers(from.getMembersList());
+    to.setCondition(iamConditionCodec.decode(from.getCondition()));
+    return to.build();
+  }
+
+  private Expr conditionEncode(Condition from) {
+    Expr.Builder to = Expr.newBuilder();
+    to.setExpression(from.getExpression());
+    to.setTitle(from.getTitle());
+    to.setDescription(from.getDescription());
+    // apiary doesn't have a "location" field like grpc does
+    return to.build();
+  }
+
+  private Condition conditionDecode(Expr from) {
+    Condition.Builder to = Condition.newBuilder();
+    to.setExpression(from.getExpression());
+    to.setTitle(from.getTitle());
+    to.setDescription(from.getDescription());
+    return to.build();
+  }
+
   private int crc32cDecode(String from) {
     byte[] decodeCrc32c = BaseEncoding.base64().decode(from);
     return Ints.fromByteArray(decodeCrc32c);
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageImpl.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageImpl.java
index 85370781..59edd305 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageImpl.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageImpl.java
@@ -65,6 +65,9 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.io.BaseEncoding;
 import com.google.common.io.ByteStreams;
+import com.google.iam.v1.GetIamPolicyRequest;
+import com.google.iam.v1.SetIamPolicyRequest;
+import com.google.iam.v1.TestIamPermissionsRequest;
 import com.google.protobuf.ByteString;
 import com.google.protobuf.FieldMask;
 import com.google.protobuf.Message;
@@ -536,7 +539,9 @@ final class GrpcStorageImpl extends BaseService<StorageOptions> implements Stora
     GrpcCallContext grpcCallContext =
         opts.grpcMetadataMapper().apply(GrpcCallContext.createDefault());
     DeleteObjectRequest.Builder builder =
-        DeleteObjectRequest.newBuilder().setBucket(blob.getBucket()).setObject(blob.getName());
+        DeleteObjectRequest.newBuilder()
+            .setBucket(bucketNameCodec.encode(blob.getBucket()))
+            .setObject(blob.getName());
     ifNonNull(blob.getGeneration(), builder::setGeneration);
     DeleteObjectRequest req = opts.deleteObjectsRequest().apply(builder).build();
     try {
@@ -1037,21 +1042,57 @@ final class GrpcStorageImpl extends BaseService<StorageOptions> implements Stora
 
   @Override
   public Policy getIamPolicy(String bucket, BucketSourceOption... options) {
-    return throwNotYetImplemented(
-        fmtMethodName("getIamPolicy", String.class, BucketSourceOption[].class));
+    Opts<BucketSourceOpt> opts = Opts.unwrap(options);
+    GrpcCallContext grpcCallContext =
+        opts.grpcMetadataMapper().apply(GrpcCallContext.createDefault());
+    GetIamPolicyRequest.Builder builder =
+        GetIamPolicyRequest.newBuilder().setResource(bucketNameCodec.encode(bucket));
+    GetIamPolicyRequest req = opts.getIamPolicyRequest().apply(builder).build();
+    return Retrying.run(
+        getOptions(),
+        retryAlgorithmManager.getFor(req),
+        () -> storageClient.getIamPolicyCallable().call(req, grpcCallContext),
+        codecs.policyCodec());
   }
 
   @Override
   public Policy setIamPolicy(String bucket, Policy policy, BucketSourceOption... options) {
-    return throwNotYetImplemented(
-        fmtMethodName("setIamPolicy", String.class, Policy.class, BucketSourceOption[].class));
+    Opts<BucketSourceOpt> opts = Opts.unwrap(options);
+    GrpcCallContext grpcCallContext =
+        opts.grpcMetadataMapper().apply(GrpcCallContext.createDefault());
+    SetIamPolicyRequest req =
+        SetIamPolicyRequest.newBuilder()
+            .setResource(bucketNameCodec.encode(bucket))
+            .setPolicy(codecs.policyCodec().encode(policy))
+            .build();
+    return Retrying.run(
+        getOptions(),
+        retryAlgorithmManager.getFor(req),
+        () -> storageClient.setIamPolicyCallable().call(req, grpcCallContext),
+        codecs.policyCodec());
   }
 
   @Override
   public List<Boolean> testIamPermissions(
       String bucket, List<String> permissions, BucketSourceOption... options) {
-    return throwNotYetImplemented(
-        fmtMethodName("testIamPermissions", String.class, List.class, BucketSourceOption.class));
+    Opts<BucketSourceOpt> opts = Opts.unwrap(options);
+    GrpcCallContext grpcCallContext =
+        opts.grpcMetadataMapper().apply(GrpcCallContext.createDefault());
+    TestIamPermissionsRequest req =
+        TestIamPermissionsRequest.newBuilder()
+            .setResource(bucketNameCodec.encode(bucket))
+            .addAllPermissions(permissions)
+            .build();
+    return Retrying.run(
+        getOptions(),
+        retryAlgorithmManager.getFor(req),
+        () -> storageClient.testIamPermissionsCallable().call(req, grpcCallContext),
+        resp -> {
+          Set<String> heldPermissions = ImmutableSet.copyOf(resp.getPermissionsList());
+          return permissions.stream()
+              .map(heldPermissions::contains)
+              .collect(ImmutableList.toImmutableList());
+        });
   }
 
   @Override
@@ -1388,6 +1429,8 @@ final class GrpcStorageImpl extends BaseService<StorageOptions> implements Stora
         "acl",
         "default_object_acl",
         "storage_class",
-        "rpo");
+        "rpo",
+        "labels",
+        "event_based_hold");
   }
 }
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/PolicyHelper.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/PolicyHelper.java
deleted file mode 100644
index ca3e8bc3..00000000
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/PolicyHelper.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2017 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.services.storage.model.Expr;
-import com.google.api.services.storage.model.Policy.Bindings;
-import com.google.cloud.Binding;
-import com.google.cloud.Condition;
-import com.google.cloud.Policy;
-import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Helper for converting between the Policy model provided by the API and the Policy model provided
- * by this library.
- */
-class PolicyHelper {
-
-  static Policy convertFromApiPolicy(com.google.api.services.storage.model.Policy apiPolicy) {
-    Policy.Builder policyBuilder = Policy.newBuilder();
-    List<Bindings> bindings = apiPolicy.getBindings();
-    ImmutableList.Builder<Binding> coreBindings = ImmutableList.builder();
-    if (null != bindings && !bindings.isEmpty()) {
-      for (Bindings binding : bindings) {
-        Binding.Builder bindingBuilder = Binding.newBuilder();
-        bindingBuilder.setRole(binding.getRole());
-        bindingBuilder.setMembers(binding.getMembers());
-        if (binding.getCondition() != null) {
-          Condition.Builder conditionBuilder = Condition.newBuilder();
-          conditionBuilder.setTitle(binding.getCondition().getTitle());
-          conditionBuilder.setDescription(binding.getCondition().getDescription());
-          conditionBuilder.setExpression(binding.getCondition().getExpression());
-          bindingBuilder.setCondition(conditionBuilder.build());
-        }
-        coreBindings.add(bindingBuilder.build());
-      }
-    }
-    policyBuilder.setBindings(coreBindings.build());
-    return policyBuilder.setEtag(apiPolicy.getEtag()).setVersion(apiPolicy.getVersion()).build();
-  }
-
-  static com.google.api.services.storage.model.Policy convertToApiPolicy(Policy policy) {
-    List<Bindings> bindings = new ArrayList<>(policy.getBindingsList().size());
-    for (Binding binding : policy.getBindingsList()) {
-      Bindings apiBinding = new Bindings();
-      apiBinding.setRole(binding.getRole());
-      apiBinding.setMembers(new ArrayList<>(binding.getMembers()));
-      if (binding.getCondition() != null) {
-        Expr expr = new Expr();
-        expr.setTitle(binding.getCondition().getTitle());
-        expr.setDescription(binding.getCondition().getDescription());
-        expr.setExpression(binding.getCondition().getExpression());
-        apiBinding.setCondition(expr);
-      }
-      bindings.add(apiBinding);
-    }
-    return new com.google.api.services.storage.model.Policy()
-        .setBindings(bindings)
-        .setEtag(policy.getEtag())
-        .setVersion(policy.getVersion());
-  }
-
-  private PolicyHelper() {
-    // Intentionally left blank.
-  }
-}
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 14c4f618..13a8e361 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
@@ -3585,7 +3585,7 @@ public interface Storage extends Service<StorageOptions>, AutoCloseable {
    * @param options extra parameters to apply to this operation
    * @throws StorageException upon failure
    */
-  @TransportCompatibility({Transport.HTTP})
+  @TransportCompatibility({Transport.HTTP, Transport.GRPC})
   Policy getIamPolicy(String bucket, BucketSourceOption... options);
 
   /**
@@ -3613,7 +3613,7 @@ public interface Storage extends Service<StorageOptions>, AutoCloseable {
    * @param options extra parameters to apply to this operation
    * @throws StorageException upon failure
    */
-  @TransportCompatibility({Transport.HTTP})
+  @TransportCompatibility({Transport.HTTP, Transport.GRPC})
   Policy setIamPolicy(String bucket, Policy policy, BucketSourceOption... options);
 
   /**
@@ -3638,7 +3638,7 @@ public interface Storage extends Service<StorageOptions>, AutoCloseable {
    * @param options extra parameters to apply to this operation
    * @throws StorageException upon failure
    */
-  @TransportCompatibility({Transport.HTTP})
+  @TransportCompatibility({Transport.HTTP, Transport.GRPC})
   List<Boolean> testIamPermissions(
       String bucket, List<String> permissions, BucketSourceOption... options);
 
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 f8c73cbf..3774f605 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
@@ -17,7 +17,6 @@
 package com.google.cloud.storage;
 
 import static com.google.cloud.RetryHelper.runWithRetries;
-import static com.google.cloud.storage.PolicyHelper.convertToApiPolicy;
 import static com.google.cloud.storage.SignedUrlEncodingHelper.Rfc3986UriEncode;
 import static com.google.common.base.MoreObjects.firstNonNull;
 import static com.google.common.base.Preconditions.checkArgument;
@@ -1417,20 +1416,21 @@ final class StorageImpl extends BaseService<StorageOptions> implements Storage {
     return run(
         algorithm,
         () -> storageRpc.getIamPolicy(bucket, optionsMap),
-        PolicyHelper::convertFromApiPolicy);
+        apiPolicy -> Conversions.apiary().policyCodec().decode(apiPolicy));
   }
 
   @Override
   public Policy setIamPolicy(
       final String bucket, final Policy policy, BucketSourceOption... options) {
-    com.google.api.services.storage.model.Policy pb = convertToApiPolicy(policy);
+    com.google.api.services.storage.model.Policy pb =
+        Conversions.apiary().policyCodec().encode(policy);
     ImmutableMap<StorageRpc.Option, ?> optionsMap = Opts.unwrap(options).getRpcOptions();
     ResultRetryAlgorithm<?> algorithm =
         retryAlgorithmManager.getForBucketsSetIamPolicy(bucket, pb, optionsMap);
     return run(
         algorithm,
         () -> storageRpc.setIamPolicy(bucket, pb, optionsMap),
-        PolicyHelper::convertFromApiPolicy);
+        apiPolicy -> Conversions.apiary().policyCodec().decode(apiPolicy));
   }
 
   @Override
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/UnifiedOpts.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/UnifiedOpts.java
index d8735d49..95412f18 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/UnifiedOpts.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/UnifiedOpts.java
@@ -1983,6 +1983,10 @@ final class UnifiedOpts {
       return fuseMappers(HmacKeyTargetOpt.class, HmacKeyTargetOpt::deleteHmacKey);
     }
 
+    Mapper<GetIamPolicyRequest.Builder> getIamPolicyRequest() {
+      return fuseMappers(BucketSourceOpt.class, BucketSourceOpt::getIamPolicy);
+    }
+
     Mapper<BlobInfo.Builder> blobInfoMapper() {
       return fuseMappers(ObjectTargetOpt.class, ObjectTargetOpt::blobInfo);
     }


Test patch:
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/GapicReadTimeoutTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/GapicReadTimeoutTest.java
index d18d2c54..b5f064e1 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/GapicReadTimeoutTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/GapicReadTimeoutTest.java
@@ -130,7 +130,10 @@ public final class GapicReadTimeoutTest {
         ReadObjectResponse actualResponse2 = iter.next();
         boolean hasNext = iter.hasNext();
         Stopwatch stop = started.stop();
-        assertThat(stop.elapsed(TimeUnit.MILLISECONDS)).isAtLeast(sleepDurationMillis);
+        // reduce our expectation by 1% to allow for the fact that sleep can sometimes be slightly
+        // less than the stated amount.
+        long minimumElapsedTime = sleepDurationMillis - (long) (sleepDurationMillis * 0.01);
+        assertThat(stop.elapsed(TimeUnit.MILLISECONDS)).isAtLeast(minimumElapsedTime);
         assertThat(actualResponse1).isEqualTo(resp1);
         assertThat(actualResponse2).isEqualTo(resp2);
         assertThat(hasNext).isFalse();
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/PolicyHelperTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/PolicyHelperTest.java
index a34538c1..1dd70d38 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/PolicyHelperTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/PolicyHelperTest.java
@@ -57,9 +57,9 @@ public class PolicyHelperTest {
             .setEtag(ETAG)
             .setVersion(1);
 
-    Policy actualLibPolicy = PolicyHelper.convertFromApiPolicy(apiPolicy);
+    Policy actualLibPolicy = Conversions.apiary().policyCodec().decode(apiPolicy);
     com.google.api.services.storage.model.Policy actualApiPolicy =
-        PolicyHelper.convertToApiPolicy(libPolicy);
+        Conversions.apiary().policyCodec().encode(libPolicy);
 
     assertEquals(libPolicy, actualLibPolicy);
     assertTrue(new ApiPolicyMatcher(apiPolicy).matches(actualApiPolicy));
@@ -73,7 +73,7 @@ public class PolicyHelperTest {
             .setBindings(bindings)
             .setEtag(ETAG)
             .setVersion(1);
-    Policy policy = PolicyHelper.convertFromApiPolicy(apiPolicy);
+    Policy policy = Conversions.apiary().policyCodec().decode(apiPolicy);
     assertEquals(policy.getBindings().size(), 0);
   }
 }
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBucketTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBucketTest.java
index 1b6a98f7..3d6f7dac 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBucketTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBucketTest.java
@@ -23,6 +23,7 @@ import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
 import com.google.cloud.Policy;
 import com.google.cloud.storage.Blob;
@@ -38,115 +39,177 @@ import com.google.cloud.storage.Storage;
 import com.google.cloud.storage.Storage.BlobField;
 import com.google.cloud.storage.Storage.BucketField;
 import com.google.cloud.storage.StorageFixture;
+import com.google.cloud.storage.conformance.retry.ParallelParameterized;
 import com.google.cloud.storage.testing.RemoteStorageHelper;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
 import org.junit.ClassRule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized.Parameters;
 
+@RunWith(ParallelParameterized.class)
 public class ITBucketTest {
 
   @ClassRule(order = 1)
-  public static final StorageFixture storageFixture = StorageFixture.defaultHttp();
+  public static final StorageFixture storageFixtureGrpc = StorageFixture.defaultGrpc();
+
+  @ClassRule(order = 1)
+  public static final StorageFixture storageFixtureHttp = StorageFixture.defaultHttp();
 
   @ClassRule(order = 2)
-  public static final BucketFixture bucketFixture =
-      BucketFixture.newBuilder().setHandle(storageFixture::getInstance).build();
+  public static final BucketFixture bucketFixtureHttp =
+      BucketFixture.newBuilder()
+          .setBucketNameFmtString("java-storage-http-%s")
+          .setHandle(storageFixtureHttp::getInstance)
+          .build();
 
-  @ClassRule(order = 3)
-  public static final BucketFixture requesterPaysFixture =
-      BucketFixture.newBuilder().setHandle(storageFixture::getInstance).build();
+  @ClassRule(order = 2)
+  public static final BucketFixture requesterPaysFixtureHttp =
+      BucketFixture.newBuilder()
+          .setBucketNameFmtString("java-storage-http-%s")
+          .setHandle(storageFixtureHttp::getInstance)
+          .build();
 
-  private static Storage storage;
-  private static String bucketName;
-  private static String requesterPaysBucketName;
+  @ClassRule(order = 2)
+  public static final BucketFixture bucketFixtureGrpc =
+      BucketFixture.newBuilder()
+          .setBucketNameFmtString("java-storage-grpc-%s")
+          .setHandle(storageFixtureHttp::getInstance)
+          .build();
+
+  @ClassRule(order = 2)
+  public static final BucketFixture requesterPaysFixtureGrpc =
+      BucketFixture.newBuilder()
+          .setBucketNameFmtString("java-storage-grpc-%s")
+          .setHandle(storageFixtureHttp::getInstance)
+          .build();
+
+  private final StorageFixture storageFixture;
+  private final BucketFixture bucketFixture;
+  private final BucketFixture requesterPaysFixture;
+  private final String clientName;
 
   private static final byte[] BLOB_BYTE_CONTENT = {0xD, 0xE, 0xA, 0xD};
   private static final Map<String, String> BUCKET_LABELS = ImmutableMap.of("label1", "value1");
   private static final Long RETENTION_PERIOD = 5L;
 
-  @BeforeClass
-  public static void setup() {
-    storage = storageFixture.getInstance();
-    bucketName = bucketFixture.getBucketInfo().getName();
-    requesterPaysBucketName = requesterPaysFixture.getBucketInfo().getName();
+  public ITBucketTest(
+      String clientName,
+      StorageFixture storageFixture,
+      BucketFixture bucketFixture,
+      BucketFixture requesterPaysFixture) {
+    this.clientName = clientName;
+    this.storageFixture = storageFixture;
+    this.bucketFixture = bucketFixture;
+    this.requesterPaysFixture = requesterPaysFixture;
   }
 
-  @AfterClass
-  public static void cleanup() {
-    unsetRequesterPays();
+  @Parameters(name = "{0}")
+  public static Iterable<Object[]> data() {
+    return Arrays.asList(
+        new Object[] {"JSON/Prod", storageFixtureHttp, bucketFixtureHttp, requesterPaysFixtureHttp},
+        new Object[] {
+          "GRPC/TestBench", storageFixtureGrpc, bucketFixtureGrpc, requesterPaysFixtureGrpc
+        });
   }
 
   @Test(timeout = 5000)
   public void testListBuckets() throws InterruptedException {
     Iterator<Bucket> bucketIterator =
-        storage
-            .list(Storage.BucketListOption.prefix(bucketName), Storage.BucketListOption.fields())
+        storageFixture
+            .getInstance()
+            .list(
+                Storage.BucketListOption.prefix(bucketFixture.getBucketInfo().getName()),
+                Storage.BucketListOption.fields())
             .iterateAll()
             .iterator();
     while (!bucketIterator.hasNext()) {
       Thread.sleep(500);
       bucketIterator =
-          storage
-              .list(Storage.BucketListOption.prefix(bucketName), Storage.BucketListOption.fields())
+          storageFixture
+              .getInstance()
+              .list(
+                  Storage.BucketListOption.prefix(bucketFixture.getBucketInfo().getName()),
+                  Storage.BucketListOption.fields())
               .iterateAll()
               .iterator();
     }
     while (bucketIterator.hasNext()) {
       Bucket remoteBucket = bucketIterator.next();
-      assertTrue(remoteBucket.getName().startsWith(bucketName));
-      assertNull(remoteBucket.getCreateTime());
-      assertNull(remoteBucket.getSelfLink());
+      assertTrue(remoteBucket.getName().startsWith(bucketFixture.getBucketInfo().getName()));
     }
   }
 
   @Test
   public void testGetBucketSelectedFields() {
-    Bucket remoteBucket = storage.get(bucketName, Storage.BucketGetOption.fields(BucketField.ID));
-    assertEquals(bucketName, remoteBucket.getName());
+    // FieldMask not supported in GRPC get currently.
+    assumeTrue(clientName.startsWith("JSON"));
+
+    Bucket remoteBucket =
+        storageFixture
+            .getInstance()
+            .get(
+                bucketFixture.getBucketInfo().getName(),
+                Storage.BucketGetOption.fields(BucketField.ID));
+    assertEquals(bucketFixture.getBucketInfo().getName(), remoteBucket.getName());
     assertNull(remoteBucket.getCreateTime());
     assertNotNull(remoteBucket.getGeneratedId());
   }
 
   @Test
   public void testGetBucketAllSelectedFields() {
+    // FieldMask not supported in GRPC currently.
+    assumeTrue(clientName.startsWith("JSON"));
     Bucket remoteBucket =
-        storage.get(bucketName, Storage.BucketGetOption.fields(BucketField.values()));
-    assertEquals(bucketName, remoteBucket.getName());
+        storageFixture
+            .getInstance()
+            .get(
+                bucketFixture.getBucketInfo().getName(),
+                Storage.BucketGetOption.fields(BucketField.values()));
+    assertEquals(bucketFixture.getBucketInfo().getName(), remoteBucket.getName());
     assertNotNull(remoteBucket.getCreateTime());
     assertNotNull(remoteBucket.getSelfLink());
   }
 
   @Test
   public void testBucketLocationType() {
-    String bucketName = RemoteStorageHelper.generateBucketName();
-    Bucket bucket = storage.create(BucketInfo.newBuilder(bucketName).setLocation("us").build());
+    // Cannot turn on for GRPC until b/246634709 is resolved, verified locally.
+    assumeTrue(clientName.startsWith("JSON"));
+    String bucketName = bucketFixture.newBucketName();
+    Bucket bucket =
+        storageFixture
+            .getInstance()
+            .create(BucketInfo.newBuilder(bucketName).setLocation("us").build());
     assertEquals("multi-region", bucket.getLocationType());
   }
 
   @Test
   public void testBucketCustomPlacmentConfigDualRegion() {
-    String bucketName = RemoteStorageHelper.generateBucketName();
+    // Cannot turn on for GRPC until creation bug b/246634709 is resolved, verified locally.
+    assumeTrue(clientName.startsWith("JSON"));
+    String bucketName = bucketFixture.newBucketName();
     List<String> locations = new ArrayList<>();
     locations.add("US-EAST1");
     locations.add("US-WEST1");
     CustomPlacementConfig customPlacementConfig =
         CustomPlacementConfig.newBuilder().setDataLocations(locations).build();
     Bucket bucket =
-        storage.create(
-            BucketInfo.newBuilder(bucketName)
-                .setCustomPlacementConfig(customPlacementConfig)
-                .setLocation("us")
-                .build());
+        storageFixture
+            .getInstance()
+            .create(
+                BucketInfo.newBuilder(bucketName)
+                    .setCustomPlacementConfig(customPlacementConfig)
+                    .setLocation("us")
+                    .build());
     assertTrue(bucket.getCustomPlacementConfig().getDataLocations().contains("US-EAST1"));
     assertTrue(bucket.getCustomPlacementConfig().getDataLocations().contains("US-WEST1"));
     assertTrue(bucket.getLocation().equalsIgnoreCase("us"));
@@ -154,11 +217,16 @@ public class ITBucketTest {
 
   @Test
   public void testBucketLogging() throws ExecutionException, InterruptedException {
-    String logsBucket = RemoteStorageHelper.generateBucketName();
-    String loggingBucket = RemoteStorageHelper.generateBucketName();
+    // Cannot turn on until GRPC Update logic bug is fixed b/247133805
+    assumeTrue(clientName.startsWith("JSON"));
+    String logsBucket = bucketFixture.newBucketName();
+    String loggingBucket = bucketFixture.newBucketName();
     try {
-      assertNotNull(storage.create(BucketInfo.newBuilder(logsBucket).setLocation("us").build()));
-      Policy policy = storage.getIamPolicy(logsBucket);
+      assertNotNull(
+          storageFixture
+              .getInstance()
+              .create(BucketInfo.newBuilder(logsBucket).setLocation("us").build()));
+      Policy policy = storageFixture.getInstance().getIamPolicy(logsBucket);
       assertNotNull(policy);
       BucketInfo.Logging logging =
           BucketInfo.Logging.newBuilder()
@@ -166,8 +234,13 @@ public class ITBucketTest {
               .setLogObjectPrefix("test-logs")
               .build();
       Bucket bucket =
-          storage.create(
-              BucketInfo.newBuilder(loggingBucket).setLocation("us").setLogging(logging).build());
+          storageFixture
+              .getInstance()
+              .create(
+                  BucketInfo.newBuilder(loggingBucket)
+                      .setLocation("us")
+                      .setLogging(logging)
+                      .build());
       assertEquals(logsBucket, bucket.getLogging().getLogBucket());
       assertEquals("test-logs", bucket.getLogging().getLogObjectPrefix());
 
@@ -176,14 +249,18 @@ public class ITBucketTest {
       assertNull(updatedBucket.getLogging());
 
     } finally {
-      RemoteStorageHelper.forceDelete(storage, logsBucket, 5, TimeUnit.SECONDS);
-      RemoteStorageHelper.forceDelete(storage, loggingBucket, 5, TimeUnit.SECONDS);
+      RemoteStorageHelper.forceDelete(
+          storageFixture.getInstance(), logsBucket, 5, TimeUnit.SECONDS);
+      RemoteStorageHelper.forceDelete(
+          storageFixture.getInstance(), loggingBucket, 5, TimeUnit.SECONDS);
     }
   }
 
   @Test
   public void testRemoveBucketCORS() throws ExecutionException, InterruptedException {
-    String bucketName = RemoteStorageHelper.generateBucketName();
+    // GRPC Update logic bug b/247133805
+    assumeTrue(clientName.startsWith("JSON"));
+    String bucketName = bucketFixture.newBucketName();
     List<Cors.Origin> origins = ImmutableList.of(Cors.Origin.of("http://cloud.google.com"));
     List<HttpMethod> httpMethods = ImmutableList.of(HttpMethod.GET);
     List<String> responseHeaders = ImmutableList.of("Content-Type");
@@ -195,11 +272,16 @@ public class ITBucketTest {
               .setResponseHeaders(responseHeaders)
               .setMaxAgeSeconds(100)
               .build();
-      storage.create(BucketInfo.newBuilder(bucketName).setCors(ImmutableList.of(cors)).build());
+      // GRPC creation bug
+      storageFixtureHttp
+          .getInstance()
+          .create(BucketInfo.newBuilder(bucketName).setCors(ImmutableList.of(cors)).build());
 
       // case-1 : Cors are set and field selector is selected then returns not-null.
       Bucket remoteBucket =
-          storage.get(bucketName, Storage.BucketGetOption.fields(BucketField.CORS));
+          storageFixture
+              .getInstance()
+              .get(bucketName, Storage.BucketGetOption.fields(BucketField.CORS));
       assertThat(remoteBucket.getCors()).isNotNull();
       assertThat(remoteBucket.getCors().get(0).getMaxAgeSeconds()).isEqualTo(100);
       assertThat(remoteBucket.getCors().get(0).getMethods()).isEqualTo(httpMethods);
@@ -207,7 +289,7 @@ public class ITBucketTest {
       assertThat(remoteBucket.getCors().get(0).getResponseHeaders()).isEqualTo(responseHeaders);
 
       // case-2 : Cors are set but field selector isn't selected then returns not-null.
-      remoteBucket = storage.get(bucketName);
+      remoteBucket = storageFixture.getInstance().get(bucketName);
       assertThat(remoteBucket.getCors()).isNotNull();
 
       // Remove CORS configuration from the bucket.
@@ -215,45 +297,62 @@ public class ITBucketTest {
       assertThat(updatedBucket.getCors()).isNull();
 
       // case-3 : Cors are not set and field selector is selected then returns null.
-      updatedBucket = storage.get(bucketName, Storage.BucketGetOption.fields(BucketField.CORS));
+      updatedBucket =
+          storageFixture
+              .getInstance()
+              .get(bucketName, Storage.BucketGetOption.fields(BucketField.CORS));
       assertThat(updatedBucket.getCors()).isNull();
 
       // case-4 : Cors are not set and field selector isn't selected then returns null.
-      updatedBucket = storage.get(bucketName);
+      updatedBucket = storageFixture.getInstance().get(bucketName);
       assertThat(updatedBucket.getCors()).isNull();
 
     } finally {
-      RemoteStorageHelper.forceDelete(storage, bucketName, 5, TimeUnit.SECONDS);
+      RemoteStorageHelper.forceDelete(
+          storageFixtureHttp.getInstance(), bucketName, 5, TimeUnit.SECONDS);
     }
   }
 
   @Test
   public void testRpoConfig() {
-    String rpoBucket = RemoteStorageHelper.generateBucketName();
+    // Cannot turn on for GRPC until b/246634709 is resolved, verified locally.
+    assumeTrue(clientName.startsWith("JSON"));
+    String rpoBucket = bucketFixture.newBucketName();
     try {
       Bucket bucket =
-          storage.create(
-              BucketInfo.newBuilder(rpoBucket).setLocation("NAM4").setRpo(Rpo.ASYNC_TURBO).build());
+          storageFixtureHttp
+              .getInstance()
+              .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());
+      assertEquals("DEFAULT", storageFixture.getInstance().get(rpoBucket).getRpo().toString());
     } finally {
-      storage.delete(rpoBucket);
+      storageFixture.getInstance().delete(rpoBucket);
     }
   }
 
   @Test
   public void testRetentionPolicyLock() throws ExecutionException, InterruptedException {
-    retentionPolicyLockRequesterPays(true);
     retentionPolicyLockRequesterPays(false);
   }
 
+  @Test
+  public void testRetentionPolicyLockRequesterPays()
+      throws ExecutionException, InterruptedException {
+    assumeTrue(clientName.startsWith("JSON"));
+    retentionPolicyLockRequesterPays(true);
+  }
+
   private void retentionPolicyLockRequesterPays(boolean requesterPays)
       throws ExecutionException, InterruptedException {
-    String projectId = storage.getOptions().getProjectId();
-    String bucketName = RemoteStorageHelper.generateBucketName();
+    String projectId = storageFixture.getInstance().getOptions().getProjectId();
+    String bucketName = bucketFixture.newBucketName();
     BucketInfo bucketInfo;
     if (requesterPays) {
       bucketInfo =
@@ -264,21 +363,25 @@ public class ITBucketTest {
     } else {
       bucketInfo = BucketInfo.newBuilder(bucketName).setRetentionPeriod(RETENTION_PERIOD).build();
     }
-    Bucket remoteBucket = storage.create(bucketInfo);
+    Bucket remoteBucket = storageFixtureHttp.getInstance().create(bucketInfo);
     try {
       assertNull(remoteBucket.retentionPolicyIsLocked());
       assertNotNull(remoteBucket.getRetentionEffectiveTime());
       assertNotNull(remoteBucket.getMetageneration());
       if (requesterPays) {
         remoteBucket =
-            storage.lockRetentionPolicy(
-                remoteBucket,
-                Storage.BucketTargetOption.metagenerationMatch(),
-                Storage.BucketTargetOption.userProject(projectId));
+            storageFixture
+                .getInstance()
+                .lockRetentionPolicy(
+                    remoteBucket,
+                    Storage.BucketTargetOption.metagenerationMatch(),
+                    Storage.BucketTargetOption.userProject(projectId));
       } else {
         remoteBucket =
-            storage.lockRetentionPolicy(
-                remoteBucket, Storage.BucketTargetOption.metagenerationMatch());
+            storageFixture
+                .getInstance()
+                .lockRetentionPolicy(
+                    remoteBucket, Storage.BucketTargetOption.metagenerationMatch());
       }
       assertTrue(remoteBucket.retentionPolicyIsLocked());
       assertNotNull(remoteBucket.getRetentionEffectiveTime());
@@ -286,94 +389,126 @@ public class ITBucketTest {
       if (requesterPays) {
         bucketInfo = bucketInfo.toBuilder().setRequesterPays(false).build();
         Bucket updateBucket =
-            storage.update(bucketInfo, Storage.BucketTargetOption.userProject(projectId));
+            storageFixture
+                .getInstance()
+                .update(bucketInfo, Storage.BucketTargetOption.userProject(projectId));
         assertFalse(updateBucket.requesterPays());
       }
-      RemoteStorageHelper.forceDelete(storage, bucketName, 5, TimeUnit.SECONDS, projectId);
+      RemoteStorageHelper.forceDelete(
+          storageFixtureHttp.getInstance(), bucketName, 5, TimeUnit.SECONDS, projectId);
     }
   }
 
   @Test
   public void testUpdateBucketLabel() {
+    // Bug in UpdateMask
+    assumeTrue(clientName.startsWith("JSON"));
     Bucket remoteBucket =
-        storage.get(
-            bucketName, Storage.BucketGetOption.fields(BucketField.ID, BucketField.BILLING));
+        storageFixture
+            .getInstance()
+            .get(
+                bucketFixture.getBucketInfo().getName(),
+                Storage.BucketGetOption.fields(BucketField.ID, BucketField.BILLING));
     assertNull(remoteBucket.getLabels());
     remoteBucket = remoteBucket.toBuilder().setLabels(BUCKET_LABELS).build();
-    Bucket updatedBucket = storage.update(remoteBucket);
+    Bucket updatedBucket = storageFixture.getInstance().update(remoteBucket);
     assertEquals(BUCKET_LABELS, updatedBucket.getLabels());
     remoteBucket.toBuilder().setLabels(Collections.emptyMap()).build().update();
-    assertNull(storage.get(bucketName).getLabels());
+    assertNull(
+        storageFixture.getInstance().get(bucketFixture.getBucketInfo().getName()).getLabels());
   }
 
   @Test
   public void testUpdateBucketRequesterPays() {
+    // Bug in UpdateMask
+    assumeTrue(clientName.startsWith("JSON"));
     unsetRequesterPays();
     Bucket remoteBucket =
-        storage.get(
-            requesterPaysBucketName,
-            Storage.BucketGetOption.fields(BucketField.ID, BucketField.BILLING));
+        storageFixture
+            .getInstance()
+            .get(
+                requesterPaysFixture.getBucketInfo().getName(),
+                Storage.BucketGetOption.fields(BucketField.ID, BucketField.BILLING));
     assertTrue(remoteBucket.requesterPays() == null || !remoteBucket.requesterPays());
     remoteBucket = remoteBucket.toBuilder().setRequesterPays(true).build();
-    Bucket updatedBucket = storage.update(remoteBucket);
+    Bucket updatedBucket = storageFixture.getInstance().update(remoteBucket);
     assertTrue(updatedBucket.requesterPays());
 
-    String projectId = storage.getOptions().getProjectId();
+    String projectId = storageFixture.getInstance().getOptions().getProjectId();
     Bucket.BlobTargetOption option = Bucket.BlobTargetOption.userProject(projectId);
     String blobName = "test-create-empty-blob-requester-pays";
     Blob remoteBlob = updatedBucket.create(blobName, BLOB_BYTE_CONTENT, option);
     assertNotNull(remoteBlob);
     byte[] readBytes =
-        storage.readAllBytes(
-            requesterPaysBucketName, blobName, Storage.BlobSourceOption.userProject(projectId));
+        storageFixture
+            .getInstance()
+            .readAllBytes(
+                requesterPaysFixture.getBucketInfo().getName(),
+                blobName,
+                Storage.BlobSourceOption.userProject(projectId));
     assertArrayEquals(BLOB_BYTE_CONTENT, readBytes);
     remoteBucket = remoteBucket.toBuilder().setRequesterPays(false).build();
-    updatedBucket = storage.update(remoteBucket, Storage.BucketTargetOption.userProject(projectId));
+    updatedBucket =
+        storageFixture
+            .getInstance()
+            .update(remoteBucket, Storage.BucketTargetOption.userProject(projectId));
     assertFalse(updatedBucket.requesterPays());
   }
 
   @Test
   public void testEnableDisableBucketDefaultEventBasedHold()
       throws ExecutionException, InterruptedException {
-    String bucketName = RemoteStorageHelper.generateBucketName();
+    assumeTrue(clientName.startsWith("JSON"));
+    String bucketName = bucketFixture.newBucketName();
     Bucket remoteBucket =
-        storage.create(BucketInfo.newBuilder(bucketName).setDefaultEventBasedHold(true).build());
+        storageFixtureHttp
+            .getInstance()
+            .create(BucketInfo.newBuilder(bucketName).setDefaultEventBasedHold(true).build());
     try {
       assertTrue(remoteBucket.getDefaultEventBasedHold());
       remoteBucket =
-          storage.get(
-              bucketName, Storage.BucketGetOption.fields(BucketField.DEFAULT_EVENT_BASED_HOLD));
+          storageFixture
+              .getInstance()
+              .get(
+                  bucketName, Storage.BucketGetOption.fields(BucketField.DEFAULT_EVENT_BASED_HOLD));
       assertTrue(remoteBucket.getDefaultEventBasedHold());
       String blobName = "test-create-with-event-based-hold";
       BlobInfo blobInfo = BlobInfo.newBuilder(bucketName, blobName).build();
-      Blob remoteBlob = storage.create(blobInfo);
+      Blob remoteBlob = storageFixture.getInstance().create(blobInfo);
       assertTrue(remoteBlob.getEventBasedHold());
       remoteBlob =
-          storage.get(
-              blobInfo.getBlobId(), Storage.BlobGetOption.fields(BlobField.EVENT_BASED_HOLD));
+          storageFixture
+              .getInstance()
+              .get(blobInfo.getBlobId(), Storage.BlobGetOption.fields(BlobField.EVENT_BASED_HOLD));
       assertTrue(remoteBlob.getEventBasedHold());
       remoteBlob = remoteBlob.toBuilder().setEventBasedHold(false).build().update();
       assertFalse(remoteBlob.getEventBasedHold());
       remoteBucket = remoteBucket.toBuilder().setDefaultEventBasedHold(false).build().update();
       assertFalse(remoteBucket.getDefaultEventBasedHold());
     } finally {
-      RemoteStorageHelper.forceDelete(storage, bucketName, 5, TimeUnit.SECONDS);
+      RemoteStorageHelper.forceDelete(
+          storageFixtureHttp.getInstance(), bucketName, 5, TimeUnit.SECONDS);
     }
   }
 
-  private static void unsetRequesterPays() {
+  private void unsetRequesterPays() {
     Bucket remoteBucket =
-        storage.get(
-            requesterPaysBucketName,
-            Storage.BucketGetOption.fields(BucketField.ID, BucketField.BILLING),
-            Storage.BucketGetOption.userProject(storage.getOptions().getProjectId()));
+        storageFixture
+            .getInstance()
+            .get(
+                requesterPaysFixture.getBucketInfo().getName(),
+                Storage.BucketGetOption.fields(BucketField.ID, BucketField.BILLING),
+                Storage.BucketGetOption.userProject(
+                    storageFixture.getInstance().getOptions().getProjectId()));
     // Disable requester pays in case a test fails to clean up.
     if (remoteBucket.requesterPays() != null && remoteBucket.requesterPays() == true) {
       remoteBucket
           .toBuilder()
           .setRequesterPays(false)
           .build()
-          .update(Storage.BucketTargetOption.userProject(storage.getOptions().getProjectId()));
+          .update(
+              Storage.BucketTargetOption.userProject(
+                  storageFixture.getInstance().getOptions().getProjectId()));
     }
   }
 }