From 704e6249e6875be6fb13b1317e5371dac5973b84 Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Mon, 27 Aug 2018 18:15:24 -0400 Subject: [PATCH 1/7] WIP: Bigtable: add CRUD for clusters Another step in continuing the work in: https://github.com/spollapally/google-cloud-java/tree/instance-admin-client/google-cloud-clients/google-cloud-bigtable Please note that this PR contains a copy of CreateClusterRequest from #3569. --- .../admin/v2/BigtableInstanceAdminClient.java | 321 ++++++++++++++++++ .../bigtable/admin/v2/models/Cluster.java | 83 +++++ .../admin/v2/models/CreateClusterRequest.java | 131 +++++++ .../models/PartialListClustersException.java | 46 +++ 4 files changed, 581 insertions(+) create mode 100644 google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/Cluster.java create mode 100644 google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequest.java create mode 100644 google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/PartialListClustersException.java diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java index b2a2f741126d..2966ff764b2c 100644 --- a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java @@ -15,9 +15,30 @@ */ package com.google.cloud.bigtable.admin.v2; +import com.google.api.core.ApiFunction; +import com.google.api.core.ApiFuture; +import com.google.api.core.ApiFutures; +import com.google.bigtable.admin.v2.ClusterName; +import com.google.bigtable.admin.v2.DeleteClusterRequest; +import com.google.bigtable.admin.v2.GetClusterRequest; +import com.google.bigtable.admin.v2.InstanceName; +import com.google.bigtable.admin.v2.ListClustersRequest; +import com.google.bigtable.admin.v2.ListClustersResponse; +import com.google.bigtable.admin.v2.LocationName; import com.google.bigtable.admin.v2.ProjectName; +import com.google.cloud.bigtable.admin.v2.models.Cluster; +import com.google.cloud.bigtable.admin.v2.models.CreateClusterRequest; +import com.google.cloud.bigtable.admin.v2.models.PartialListClustersException; import com.google.cloud.bigtable.admin.v2.stub.BigtableInstanceAdminStub; +import com.google.common.base.Verify; +import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.common.util.concurrent.UncheckedExecutionException; +import com.google.protobuf.Empty; import java.io.IOException; +import java.util.List; +import java.util.Objects; import javax.annotation.Nonnull; /** @@ -105,4 +126,304 @@ public ProjectName getProjectName() { public void close() { stub.close(); } + + /** + * Creates a new cluster in the specified instance. + * + *

Sample code: + * + *

{@code
+   * Cluster cluster = client.createCluster(
+   *   CreateClusterRequest.of("my-instance", "my-new-cluster")
+   *     .setZone("us-east1-c")
+   *     .setServeNodes(3)
+   *     .setStorageType(StorageType.SSD)
+   * );
+   * }
+ */ + @SuppressWarnings("WeakerAccess") + public Cluster createCluster(CreateClusterRequest request) { + return awaitFuture(createClusterAsync(request)); + } + + /** + * Asynchronously creates a new cluster in the specified instance. + * + *

Sample code: + * + *

{@code
+   * ApiFuture clusterFuture = client.createClusterAsync(
+   *   CreateClusterRequest.of("my-instance", "my-new-cluster")
+   *     .setZone("us-east1-c")
+   *     .setServeNodes(3)
+   *     .setStorageType(StorageType.SSD)
+   * );
+   *
+   * Cluster cluste = clusterFuture.get();
+   * }
+ */ + @SuppressWarnings("WeakerAccess") + public ApiFuture createClusterAsync(CreateClusterRequest request) { + return ApiFutures.transform( + stub.createClusterOperationCallable().futureCall(request.toProto(projectName)), + new ApiFunction() { + @Override + public Cluster apply(com.google.bigtable.admin.v2.Cluster proto) { + return Cluster.fromProto(proto); + } + }, + MoreExecutors.directExecutor() + ); + } + + /** + * Get the cluster representation by ID. + * + *

Sample code: + * + *

{@code
+   * Cluster cluster = client.getCluster("my-instance", "my-cluster");
+   * }
+ */ + @SuppressWarnings("WeakerAccess") + public Cluster getCluster(String instanceId, String clusterId) { + return awaitFuture(getClusterAsync(instanceId, clusterId)); + } + + /** + * Asynchronously gets the cluster representation by ID. + * + *

Sample code: + * + *

{@code
+   * ApiFuture clusterFuture = client.getClusterAsync("my-instance", "my-cluster");
+   * Cluster cluster = clusterFuture.get();
+   * }
+ */ + @SuppressWarnings("WeakerAccess") + public ApiFuture getClusterAsync(String instanceId, String clusterId) { + ClusterName name = ClusterName.of(projectName.getProject(), instanceId, clusterId); + + GetClusterRequest request = GetClusterRequest.newBuilder() + .setName(name.toString()) + .build(); + + return ApiFutures.transform( + stub.getClusterCallable().futureCall(request), + new ApiFunction() { + @Override + public Cluster apply(com.google.bigtable.admin.v2.Cluster proto) { + return Cluster.fromProto(proto); + } + }, + MoreExecutors.directExecutor() + ); + } + + /** + * Lists all clusters in the specified instance. + * + *

This method will throw a {@link PartialListClustersException} when any zone is + * unavailable. If partial listing are ok, the exception can be caught and inspected. + * + *

Sample code: + * + *

{@code
+   * try {
+   *   List clusters = clister.listClusters("my-instance");
+   * } catch (PartialListClustersException e) {
+   *   System.out.println("The following zones are unavailable: " + e.getUnavailableZones());
+   *   System.out.println("But the following clusters are reachable: " + e.getClusters())
+   * }
+   * }
+ */ + @SuppressWarnings("WeakerAccess") + public List listClusters(String instanceId) { + return awaitFuture(listClustersAsync(instanceId)); + } + + /** + * Asynchronously lists all clusters in the specified instance. + * + *

This method will throw a {@link PartialListClustersException} when any zone is + * unavailable. If partial listing are ok, the exception can be caught and inspected. + * + *

Sample code: + * + *

{@code
+   * ApiFuture clustersFuture = client.listClustersAsync("my-instance");
+   *
+   * ApiFutures.addCallback(clustersFuture, new ApiFutureCallback>() {
+   *   public void onFailure(Throwable t) {
+   *     if (t instanceof PartialListClustersException) {
+   *       PartialListClustersException partialError = (PartialListClustersException)t;
+   *       System.out.println("The following zones are unavailable: " + partialError.getUnavailableZones());
+   *       System.out.println("But the following clusters are reachable: " + partialError.getClusters());
+   *     } else {
+   *       t.printStackTrace();
+   *     }
+   *   }
+   *
+   *   public void onSuccess(List result) {
+   *     System.out.println("Found a complete set of instances: " + result);
+   *   }
+   * }, MoreExecutors.directExecutor());
+   * }
+ */ + @SuppressWarnings("WeakerAccess") + public ApiFuture> listClustersAsync(String instanceId) { + InstanceName name = InstanceName.of(projectName.getProject(), instanceId); + ListClustersRequest request = ListClustersRequest.newBuilder() + .setParent(name.toString()) + .build(); + + return ApiFutures.transform( + stub.listClustersCallable().futureCall(request), + new ApiFunction>() { + @Override + public List apply(ListClustersResponse proto) { + // NOTE: pagination is intentionally ignored. The server does not implement it and never + // will. + Verify.verify(proto.getNextPageToken().isEmpty(), + "Server returned an unexpected paginated response"); + + ImmutableList.Builder clusters = ImmutableList.builder(); + for (com.google.bigtable.admin.v2.Cluster cluster : proto.getClustersList()) { + clusters.add(Cluster.fromProto(cluster)); + } + + ImmutableList.Builder failedZones = ImmutableList.builder(); + for (String locationStr : proto.getFailedLocationsList()) { + LocationName fullLocation = Objects.requireNonNull(LocationName.parse(locationStr)); + failedZones.add(fullLocation.getLocation()); + } + + if (!failedZones.build().isEmpty()) { + throw new PartialListClustersException(failedZones.build(), clusters.build()); + } + + return clusters.build(); + } + }, + MoreExecutors.directExecutor() + ); + } + + /** + * Resizes the cluster's node count. Please note that only clusters that belong to a PRODUCTION + * instance can be resized. + * + *

Sample code: + * + *

{@code
+   * Cluster cluster = clister.resizeCluster("my-instance", "my-cluster", 30);
+   * }
+ */ + @SuppressWarnings("WeakerAccess") + public Cluster resizeCluster(String instanceId, String clusterId, int numServeNodes) { + return awaitFuture(resizeClusterAsync(instanceId, clusterId, numServeNodes)); + } + + /** + * Asynchronously resizes the cluster's node count. Please note that only clusters that belong to + * a PRODUCTION instance can be resized. + * + *
{@code
+   * ApiFuture clusterFuture = clister.resizeCluster("my-instance", "my-cluster", 30);
+   * Cluster cluster = clusterFuture.get();
+   * }
+ */ + @SuppressWarnings("WeakerAccess") + public ApiFuture resizeClusterAsync(String instanceId, String clusterId, + int numServeNodes) { + + ClusterName name = ClusterName.of(projectName.getProject(), instanceId, clusterId); + + com.google.bigtable.admin.v2.Cluster request = com.google.bigtable.admin.v2.Cluster.newBuilder() + .setName(name.toString()) + .setServeNodes(numServeNodes) + .build(); + + return ApiFutures.transform( + stub.updateClusterOperationCallable().futureCall(request), + new ApiFunction() { + @Override + public Cluster apply(com.google.bigtable.admin.v2.Cluster proto) { + return Cluster.fromProto(proto); + } + }, + MoreExecutors.directExecutor() + ); + } + + /** + * Deletes the specified cluster. Please note that an instance must have at least 1 cluster. To + * remove the last cluster, please use {@link BigtableInstanceAdminClient#deleteInstance(String)}. + * + *

Sample code: + * + *

{@code
+   * client.deleteCluster("my-instance", "my-cluster");
+   * }
+ */ + @SuppressWarnings("WeakerAccess") + public void deleteCluster(String instanceId, String clusterId) { + awaitFuture(deleteClusterAsync(instanceId, clusterId)); + } + + /** + * Asynchronously deletes the specified cluster. Please note that an instance must have at least 1 + * cluster. To remove the last cluster, please use {@link BigtableInstanceAdminClient#deleteInstanceAsync(String)}. + * + *

Sample code: + * + *

{@code
+   * ApiFuture future = client.deleteClusterAsync("my-instance", "my-cluster");
+   * future.get();
+   * }
+ */ + @SuppressWarnings("WeakerAccess") + public ApiFuture deleteClusterAsync(String instanceId, String clusterId) { + ClusterName name = ClusterName.of(projectName.getProject(), instanceId, clusterId); + + DeleteClusterRequest request = DeleteClusterRequest.newBuilder() + .setName(name.toString()) + .build(); + + return ApiFutures.transform( + stub.deleteClusterCallable().futureCall(request), + new ApiFunction() { + @Override + public Void apply(Empty input) { + return null; + } + }, + MoreExecutors.directExecutor() + ); + } + + /** + * Awaits the result of a future, taking care to propagate errors while maintaining the call site + * in a suppressed exception. This allows semantic errors to be caught across threads, while + * preserving the call site in the error. The caller's stacktrace will be made available as a + * suppressed exception. + */ + // TODO(igorbernstein2): try to move this into gax + private T awaitFuture(ApiFuture future) { + RuntimeException error; + try { + return Futures.getUnchecked(future); + } catch (UncheckedExecutionException e) { + if (e.getCause() instanceof RuntimeException) { + error = (RuntimeException) e.getCause(); + } else { + error = e; + } + } catch (RuntimeException e) { + error = e; + } + // Add the caller's stack as a suppressed exception + error.addSuppressed(new RuntimeException("Encountered error while awaiting future")); + throw error; + } } diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/Cluster.java b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/Cluster.java new file mode 100644 index 000000000000..9eba3695df36 --- /dev/null +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/Cluster.java @@ -0,0 +1,83 @@ +/* + * Copyright 2018 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 + * + * https://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.bigtable.admin.v2.models; + +import com.google.api.core.InternalApi; +import com.google.bigtable.admin.v2.Cluster.State; +import com.google.bigtable.admin.v2.ClusterName; +import com.google.bigtable.admin.v2.LocationName; +import com.google.bigtable.admin.v2.StorageType; +import com.google.common.base.Preconditions; +import com.google.common.base.Verify; +import javax.annotation.Nonnull; + +public class Cluster { + @Nonnull + private final com.google.bigtable.admin.v2.Cluster proto; + + @InternalApi + public static Cluster fromProto(com.google.bigtable.admin.v2.Cluster proto) { + return new Cluster(proto); + } + + private Cluster(com.google.bigtable.admin.v2.Cluster proto) { + this.proto = proto; + Preconditions.checkNotNull(proto); + Preconditions.checkArgument(!proto.getName().isEmpty(), "Name must be set"); + } + + + /** Gets the cluster's id. */ + @SuppressWarnings("WeakerAccess") + public String getId() { + // Constructor ensures that name is not null + ClusterName fullName = Verify.verifyNotNull( + ClusterName.parse(proto.getName()), + "Name can never be null"); + //noinspection ConstantConditions + return fullName.getCluster(); + } + + /** Get the zone where this cluster is located. */ + public String getZone() { + LocationName location = Verify.verifyNotNull(LocationName.parse(proto.getLocation())); + //noinspection ConstantConditions + return location.getLocation(); + } + + /** Gets the current state of the cluster */ + // TODO(igorbernstein2): try to avoid exposing proto enums + public State getState() { + return proto.getState(); + } + + /** + * Get the number of nodes allocated to this cluster. More nodes enable higher throughput and + * more consistent performance. + */ + public int getServeNodes() { + return proto.getServeNodes(); + } + + /** + * The type of storage used by this cluster to serve its parent instance's tables, unless + * explicitly overridden. + */ + // TODO(igorbernstein2): try to avoid exposing proto enums + public StorageType getStorageType() { + return proto.getDefaultStorageType(); + } +} diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequest.java b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequest.java new file mode 100644 index 000000000000..d653be2ac99b --- /dev/null +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequest.java @@ -0,0 +1,131 @@ +/* + * Copyright 2018 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 + * + * https://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.bigtable.admin.v2.models; + +import com.google.api.core.InternalApi; +import com.google.bigtable.admin.v2.InstanceName; +import com.google.bigtable.admin.v2.LocationName; +import com.google.bigtable.admin.v2.ProjectName; +import com.google.bigtable.admin.v2.StorageType; + +/** + * Parameters for creating a new Bigtable cluster. + * + *

A cluster represents the actual Cloud Bigtable service. Each cluster belongs to a single Cloud + * Bigtable instance. When your application sends requests to a Cloud Bigtable instance, those + * requests are actually handled by one of the clusters in the instance. + * + *

Each cluster is located in a single zone. An instance's clusters must be in unique zones that + * are within the same region. For example, if the first cluster is in us-east1-b, then us-east1-c + * is a valid zone for the second cluster. For a list of zones and regions where Cloud Bigtable is + * available, see Cloud Bigtable Locations. + * + * + * Examples: + * + *

{@code
+ * // Small production instance:
+ * CreateClusterRequest clusterRequest = CreateClusterRequest.of("my-existing-instance", "my-cluster")
+ *   .setZone("us-east1-c")
+ *   .setServeNodes(3)
+ *   .setStorageType(StorageType.SSD);
+ * }
+ * + * @see For more details + */ +public final class CreateClusterRequest { + private final com.google.bigtable.admin.v2.CreateClusterRequest.Builder proto = com.google.bigtable.admin.v2.CreateClusterRequest + .newBuilder(); + // instanceId and zone are short ids, which will be expanded to full names when the project name + // is passed to toProto + private final String instanceId; + private String zone; + + + /** + * Builds a new request to create a new cluster to the specified instance with the specified + * cluster id. */ + public static CreateClusterRequest of(String instanceId, String clusterId) { + return new CreateClusterRequest(instanceId, clusterId); + } + + private CreateClusterRequest(String instanceId, String clusterId) { + this.instanceId = instanceId; + proto.setClusterId(clusterId); + proto.getClusterBuilder().setDefaultStorageType(StorageType.SSD); + } + + /** + * Sets the zone where the new cluster will be located. Must be different from the existing + * cluster. + */ + @SuppressWarnings("WeakerAccess") + public CreateClusterRequest setZone(String zone) { + this.zone = zone; + return this; + } + + /** Sets the number of nodes allocated to this cluster. More nodes enable higher throughput and + * more consistent performance. */ + @SuppressWarnings("WeakerAccess") + public CreateClusterRequest setServeNodes(int numNodes) { + proto.getClusterBuilder().setServeNodes(numNodes); + return this; + } + + /** + * Sets the type of storage used by this cluster to serve its parent instance's tables. + * Defaults to {@code SSD}. + */ + // TODO(igorbernstein2): try to avoid leaking protobuf generated enums + @SuppressWarnings("WeakerAccess") + public CreateClusterRequest setStorageType(StorageType storageType) { + proto.getClusterBuilder().setDefaultStorageType(storageType); + return this; + } + + /** + * Creates the request protobuf. This method is considered an internal implementation detail and + * not meant to be used by applications. + */ + @InternalApi + public com.google.bigtable.admin.v2.CreateClusterRequest toProto(ProjectName projectName) { + proto.setParent(InstanceName.of(projectName.getProject(), instanceId).toString()); + proto.getClusterBuilder().setLocation(LocationName.of(projectName.getProject(), zone).toString()); + + return proto.build(); + } + + /** + * Gets the clusterId. This method is meant to be used by {@link CreateClusterRequest} and is + * considered an internal implementation detail and not meant to be used by applications. + */ + @InternalApi + String getClusterId() { + return proto.getClusterId(); + } + + /** + * Creates the request protobuf to be used in {@link CreateInstanceRequest}. This method is + * considered an internal implementation detail and not meant to be used by applications. + */ + @InternalApi + com.google.bigtable.admin.v2.Cluster toEmbeddedProto(ProjectName projectName) { + proto.getClusterBuilder().setLocation(LocationName.of(projectName.getProject(), zone).toString()); + + return proto.getClusterBuilder().build(); + } +} diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/PartialListClustersException.java b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/PartialListClustersException.java new file mode 100644 index 000000000000..743479a1273a --- /dev/null +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/PartialListClustersException.java @@ -0,0 +1,46 @@ +/* + * Copyright 2018 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 + * + * https://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.bigtable.admin.v2.models; + +import com.google.api.core.InternalApi; +import com.google.common.collect.ImmutableList; +import java.util.List; + +/** + * Exception thrown when some zones are unavailable and listClustersis unable to return a full + * cluster list. This exception can be inspected to get a partial list. + */ +public class PartialListClustersException extends RuntimeException { + private final List unavailableZones; + private final List clusters; + + @InternalApi + public PartialListClustersException(List unavailableZones, List clusters) { + super("Failed to list all instances, some zones where unavailable"); + this.unavailableZones = ImmutableList.copyOf(unavailableZones); + this.clusters= ImmutableList.copyOf(clusters); + } + + /** A list of zones, whose unavailability caused this error. */ + public List getUnavailableZones() { + return unavailableZones; + } + + /** A partial list of clusters that were found in the available zones. */ + public List getClusters() { + return clusters; + } +} From d207c5d955461ace8ab61aae6c9f987948b3e2f4 Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Tue, 28 Aug 2018 16:28:30 -0400 Subject: [PATCH 2/7] add tests --- .../google-cloud-bigtable-admin/pom.xml | 6 + .../bigtable/admin/v2/models/Cluster.java | 41 ++- .../models/PartialListClustersException.java | 4 +- .../v2/BigtableInstanceAdminClientTest.java | 247 +++++++++++++++++- .../bigtable/admin/v2/models/ClusterTest.java | 67 +++++ 5 files changed, 358 insertions(+), 7 deletions(-) create mode 100644 google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/ClusterTest.java diff --git a/google-cloud-clients/google-cloud-bigtable-admin/pom.xml b/google-cloud-clients/google-cloud-bigtable-admin/pom.xml index 5f02a6eeb8a8..5fa7506fa16e 100644 --- a/google-cloud-clients/google-cloud-bigtable-admin/pom.xml +++ b/google-cloud-clients/google-cloud-bigtable-admin/pom.xml @@ -61,6 +61,12 @@ test-jar test + + com.google.api + gax + testlib + test + junit junit diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/Cluster.java b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/Cluster.java index 9eba3695df36..48705f6dc67e 100644 --- a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/Cluster.java +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/Cluster.java @@ -20,6 +20,7 @@ import com.google.bigtable.admin.v2.ClusterName; import com.google.bigtable.admin.v2.LocationName; import com.google.bigtable.admin.v2.StorageType; +import com.google.common.base.Objects; import com.google.common.base.Preconditions; import com.google.common.base.Verify; import javax.annotation.Nonnull; @@ -33,7 +34,7 @@ public static Cluster fromProto(com.google.bigtable.admin.v2.Cluster proto) { return new Cluster(proto); } - private Cluster(com.google.bigtable.admin.v2.Cluster proto) { + private Cluster(@Nonnull com.google.bigtable.admin.v2.Cluster proto) { this.proto = proto; Preconditions.checkNotNull(proto); Preconditions.checkArgument(!proto.getName().isEmpty(), "Name must be set"); @@ -51,7 +52,21 @@ public String getId() { return fullName.getCluster(); } + /** Gets the instance id. */ + @SuppressWarnings("WeakerAccess") + public String getInstanceId() { + // Constructor ensures that name is not null + ClusterName fullName = Verify.verifyNotNull( + ClusterName.parse(proto.getName()), + "Name can never be null"); + //noinspection ConstantConditions + return fullName.getInstance(); + + } + + /** Get the zone where this cluster is located. */ + @SuppressWarnings("WeakerAccess") public String getZone() { LocationName location = Verify.verifyNotNull(LocationName.parse(proto.getLocation())); //noinspection ConstantConditions @@ -60,14 +75,16 @@ public String getZone() { /** Gets the current state of the cluster */ // TODO(igorbernstein2): try to avoid exposing proto enums + @SuppressWarnings("WeakerAccess") public State getState() { return proto.getState(); } /** - * Get the number of nodes allocated to this cluster. More nodes enable higher throughput and - * more consistent performance. + * Get the number of nodes allocated to this cluster. More nodes enable higher throughput and more + * consistent performance. */ + @SuppressWarnings("WeakerAccess") public int getServeNodes() { return proto.getServeNodes(); } @@ -77,7 +94,25 @@ public int getServeNodes() { * explicitly overridden. */ // TODO(igorbernstein2): try to avoid exposing proto enums + @SuppressWarnings("WeakerAccess") public StorageType getStorageType() { return proto.getDefaultStorageType(); } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Cluster cluster = (Cluster) o; + return Objects.equal(proto, cluster.proto); + } + + @Override + public int hashCode() { + return Objects.hashCode(proto); + } } diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/PartialListClustersException.java b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/PartialListClustersException.java index 743479a1273a..73ac4cf39fe0 100644 --- a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/PartialListClustersException.java +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/PartialListClustersException.java @@ -20,7 +20,7 @@ import java.util.List; /** - * Exception thrown when some zones are unavailable and listClustersis unable to return a full + * Exception thrown when some zones are unavailable and listClusters is unable to return a full * cluster list. This exception can be inspected to get a partial list. */ public class PartialListClustersException extends RuntimeException { @@ -31,7 +31,7 @@ public class PartialListClustersException extends RuntimeException { public PartialListClustersException(List unavailableZones, List clusters) { super("Failed to list all instances, some zones where unavailable"); this.unavailableZones = ImmutableList.copyOf(unavailableZones); - this.clusters= ImmutableList.copyOf(clusters); + this.clusters = ImmutableList.copyOf(clusters); } /** A list of zones, whose unavailability caused this error. */ diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTest.java b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTest.java index d76193b7afb5..f7896e48a1f7 100644 --- a/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTest.java +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTest.java @@ -17,30 +17,77 @@ import static com.google.common.truth.Truth.assertThat; +import com.google.api.core.ApiFuture; +import com.google.api.core.ApiFutures; +import com.google.api.gax.grpc.GrpcStatusCode; +import com.google.api.gax.longrunning.OperationFuture; +import com.google.api.gax.longrunning.OperationFutures; +import com.google.api.gax.longrunning.OperationSnapshot; +import com.google.api.gax.rpc.OperationCallable; +import com.google.api.gax.rpc.UnaryCallable; +import com.google.api.gax.rpc.testing.FakeOperationSnapshot; +import com.google.bigtable.admin.v2.ClusterName; +import com.google.bigtable.admin.v2.InstanceName; +import com.google.bigtable.admin.v2.LocationName; import com.google.bigtable.admin.v2.ProjectName; +import com.google.bigtable.admin.v2.StorageType; +import com.google.bigtable.admin.v2.UpdateClusterMetadata; +import com.google.cloud.bigtable.admin.v2.models.Cluster; +import com.google.cloud.bigtable.admin.v2.models.CreateClusterRequest; +import com.google.cloud.bigtable.admin.v2.models.PartialListClustersException; import com.google.cloud.bigtable.admin.v2.stub.BigtableInstanceAdminStub; +import com.google.protobuf.Empty; +import io.grpc.Status.Code; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; @RunWith(MockitoJUnitRunner.class) public class BigtableInstanceAdminClientTest { + private static final ProjectName PROJECT_NAME = ProjectName.of("my-project"); + private static final InstanceName INSTANCE_NAME = + InstanceName.of(PROJECT_NAME.getProject(), "my-instance"); + private static final ClusterName CLUSTER_NAME = + ClusterName.of(INSTANCE_NAME.getProject(), INSTANCE_NAME.getInstance(), "my-cluster"); + private BigtableInstanceAdminClient adminClient; + @Mock private BigtableInstanceAdminStub mockStub; + @Mock + private OperationCallable mockCreateClusterCallable; + @Mock + private UnaryCallable mockGetClusterCallable; + @Mock + private UnaryCallable mockListClustersCallable; + @Mock + private OperationCallable mockUpdateClusterCallable; + @Mock + private UnaryCallable mockDeleteClusterCallable; + @Before public void setUp() { adminClient = BigtableInstanceAdminClient - .create(ProjectName.of("[PROJECT]"), mockStub); + .create(PROJECT_NAME, mockStub); + + Mockito.when(mockStub.createClusterOperationCallable()).thenReturn(mockCreateClusterCallable); + Mockito.when(mockStub.getClusterCallable()).thenReturn(mockGetClusterCallable); + Mockito.when(mockStub.listClustersCallable()).thenReturn(mockListClustersCallable); + Mockito.when(mockStub.updateClusterOperationCallable()).thenReturn(mockUpdateClusterCallable); + Mockito.when(mockStub.deleteClusterCallable()).thenReturn(mockDeleteClusterCallable); } @Test public void testProjectName() { - assertThat(adminClient.getProjectName()).isEqualTo(ProjectName.of("[PROJECT]")); + assertThat(adminClient.getProjectName()).isEqualTo(PROJECT_NAME); } @Test @@ -48,4 +95,200 @@ public void testClose() { adminClient.close(); Mockito.verify(mockStub).close(); } + + @Test + public void testCreateCluster() { + // Setup + com.google.bigtable.admin.v2.CreateClusterRequest expectedRequest = + com.google.bigtable.admin.v2.CreateClusterRequest.newBuilder() + .setParent(INSTANCE_NAME.toString()) + .setClusterId(CLUSTER_NAME.getCluster()) + .setCluster( + com.google.bigtable.admin.v2.Cluster.newBuilder() + .setLocation("projects/my-project/locations/us-east1-c") + .setServeNodes(3) + .setDefaultStorageType(StorageType.SSD) + ) + .build(); + com.google.bigtable.admin.v2.Cluster expectedResponse = com.google.bigtable.admin.v2.Cluster + .newBuilder() + .setName(CLUSTER_NAME.toString()) + .build(); + mockOperationResult(mockCreateClusterCallable, expectedRequest, expectedResponse); + + // Execute + Cluster actualResult = adminClient.createCluster( + CreateClusterRequest.of(CLUSTER_NAME.getInstance(), CLUSTER_NAME.getCluster()) + .setZone("us-east1-c") + .setServeNodes(3) + .setStorageType(StorageType.SSD) + ); + // Verify + assertThat(actualResult).isEqualTo(Cluster.fromProto(expectedResponse)); + } + + @Test + public void testGetCluster() { + // Setup + com.google.bigtable.admin.v2.GetClusterRequest expectedRequest = + com.google.bigtable.admin.v2.GetClusterRequest.newBuilder() + .setName(CLUSTER_NAME.toString()) + .build(); + + com.google.bigtable.admin.v2.Cluster expectedResponse = com.google.bigtable.admin.v2.Cluster + .newBuilder() + .setName(CLUSTER_NAME.toString()) + .build(); + + Mockito.when(mockGetClusterCallable.futureCall(expectedRequest)).thenReturn( + ApiFutures.immediateFuture(expectedResponse) + ); + + // Execute + Cluster actualResult = adminClient + .getCluster(CLUSTER_NAME.getInstance(), CLUSTER_NAME.getCluster()); + + // Verify + assertThat(actualResult).isEqualTo(Cluster.fromProto(expectedResponse)); + } + + @Test + public void testListClusters() { + // Setup + com.google.bigtable.admin.v2.ListClustersRequest expectedRequest = + com.google.bigtable.admin.v2.ListClustersRequest.newBuilder() + .setParent(INSTANCE_NAME.toString()) + .build(); + + com.google.bigtable.admin.v2.ListClustersResponse expectedResponse = + com.google.bigtable.admin.v2.ListClustersResponse.newBuilder() + .addClusters( + com.google.bigtable.admin.v2.Cluster.newBuilder() + .setName(CLUSTER_NAME.toString() + "1") + ) + .addClusters( + com.google.bigtable.admin.v2.Cluster.newBuilder() + .setName(CLUSTER_NAME.toString() + "2") + ) + .build(); + + Mockito.when(mockListClustersCallable.futureCall(expectedRequest)).thenReturn( + ApiFutures.immediateFuture(expectedResponse) + ); + + // Execute + List actualResult = adminClient.listClusters(INSTANCE_NAME.getInstance()); + + // Verify + assertThat(actualResult).containsExactly( + Cluster.fromProto(expectedResponse.getClusters(0)), + Cluster.fromProto(expectedResponse.getClusters(1)) + ); + } + + @Test + public void testListClustersFailedZone() { + // Setup + com.google.bigtable.admin.v2.ListClustersRequest expectedRequest = + com.google.bigtable.admin.v2.ListClustersRequest.newBuilder() + .setParent(INSTANCE_NAME.toString()) + .build(); + + com.google.bigtable.admin.v2.ListClustersResponse expectedResponse = + com.google.bigtable.admin.v2.ListClustersResponse.newBuilder() + .addClusters( + com.google.bigtable.admin.v2.Cluster.newBuilder() + .setName(CLUSTER_NAME.toString()) + ) + .addFailedLocations( + LocationName.of(PROJECT_NAME.getProject(), "us-east1-c").toString() + ) + .build(); + + Mockito.when(mockListClustersCallable.futureCall(expectedRequest)).thenReturn( + ApiFutures.immediateFuture(expectedResponse) + ); + + // Execute + Exception actualError = null; + + try { + adminClient.listClusters(INSTANCE_NAME.getInstance()); + } catch (Exception e) { + actualError = e; + } + + // Verify + assertThat(actualError).isInstanceOf(PartialListClustersException.class); + assert actualError != null; + PartialListClustersException partialListError = (PartialListClustersException) actualError; + assertThat(partialListError.getClusters()) + .containsExactly(Cluster.fromProto(expectedResponse.getClusters(0))); + assertThat(partialListError.getUnavailableZones()).containsExactly("us-east1-c"); + } + + @Test + public void testResizeCluster() { + // Setup + com.google.bigtable.admin.v2.Cluster expectedRequest = + com.google.bigtable.admin.v2.Cluster.newBuilder() + .setName(CLUSTER_NAME.toString()) + .setServeNodes(30) + .build(); + + com.google.bigtable.admin.v2.Cluster expectedResponse = + com.google.bigtable.admin.v2.Cluster.newBuilder() + .setName(CLUSTER_NAME.toString()) + .setLocation(LocationName.of(PROJECT_NAME.getProject(), "us-east1-c").toString()) + .setServeNodes(30) + .build(); + + mockOperationResult(mockUpdateClusterCallable, expectedRequest, expectedResponse); + + // Execute + Cluster actualResult = adminClient + .resizeCluster(CLUSTER_NAME.getInstance(), CLUSTER_NAME.getCluster(), 30); + + // Verify + assertThat(actualResult).isEqualTo(Cluster.fromProto(expectedResponse)); + } + + @Test + public void testDeleteCluster() { + // Setup + com.google.bigtable.admin.v2.DeleteClusterRequest expectedRequest = + com.google.bigtable.admin.v2.DeleteClusterRequest.newBuilder() + .setName(CLUSTER_NAME.toString()) + .build(); + + final AtomicBoolean wasCalled = new AtomicBoolean(false); + + Mockito.when(mockDeleteClusterCallable.futureCall(expectedRequest)) + .thenAnswer(new Answer>() { + @Override + public ApiFuture answer(InvocationOnMock invocationOnMock) { + wasCalled.set(true); + return ApiFutures.immediateFuture(Empty.getDefaultInstance()); + } + }); + + // Execute + adminClient.deleteCluster(CLUSTER_NAME.getInstance(), CLUSTER_NAME.getCluster()); + + // Verify + assertThat(wasCalled.get()).isTrue(); + } + + private void mockOperationResult( + OperationCallable callable, ReqT request, RespT response) { + OperationSnapshot operationSnapshot = FakeOperationSnapshot.newBuilder() + .setDone(true) + .setErrorCode(GrpcStatusCode.of(Code.OK)) + .setName("fake-name") + .setResponse(response) + .build(); + OperationFuture operationFuture = OperationFutures + .immediateOperationFuture(operationSnapshot); + Mockito.when(callable.futureCall(request)).thenReturn(operationFuture); + } } diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/ClusterTest.java b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/ClusterTest.java new file mode 100644 index 000000000000..de521c5cda20 --- /dev/null +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/ClusterTest.java @@ -0,0 +1,67 @@ +/* + * Copyright 2018 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 + * + * https://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.bigtable.admin.v2.models; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.bigtable.admin.v2.Cluster.State; +import com.google.bigtable.admin.v2.StorageType; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class ClusterTest { + @Test + public void testFromProto() { + com.google.bigtable.admin.v2.Cluster proto = com.google.bigtable.admin.v2.Cluster.newBuilder() + .setName("projects/my-project/instances/my-instance/clusters/my-cluster") + .setLocation("projects/my-project/locations/us-east1-c") + .setState(State.READY) + .setServeNodes(30) + .setDefaultStorageType(StorageType.SSD) + .build(); + + Cluster result = Cluster.fromProto(proto); + + assertThat(result.getId()).isEqualTo("my-cluster"); + assertThat(result.getInstanceId()).isEqualTo("my-instance"); + assertThat(result.getZone()).isEqualTo("us-east1-c"); + assertThat(result.getState()).isEqualTo(State.READY); + assertThat(result.getServeNodes()).isEqualTo(30); + assertThat(result.getStorageType()).isEqualTo(StorageType.SSD); + } + + @Test + public void testRequiresName() { + com.google.bigtable.admin.v2.Cluster proto = com.google.bigtable.admin.v2.Cluster.newBuilder() + .setLocation("projects/my-project/locations/us-east1-c") + .setState(State.READY) + .setServeNodes(30) + .setDefaultStorageType(StorageType.SSD) + .build(); + + Exception actualException = null; + + try { + Cluster.fromProto(proto); + } catch (Exception e) { + actualException = e; + } + + assertThat(actualException).isInstanceOf(IllegalArgumentException.class); + } +} \ No newline at end of file From 3cff1192ee715a68c75d62fa001073f91fe86285 Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Wed, 29 Aug 2018 11:36:41 -0400 Subject: [PATCH 3/7] internal javadoc --- .../cloud/bigtable/admin/v2/models/Cluster.java | 12 ++++++++++++ .../admin/v2/models/CreateClusterRequest.java | 6 ++++++ .../v2/models/PartialListClustersException.java | 4 ++++ 3 files changed, 22 insertions(+) diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/Cluster.java b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/Cluster.java index 48705f6dc67e..644fd24b2710 100644 --- a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/Cluster.java +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/Cluster.java @@ -25,10 +25,22 @@ import com.google.common.base.Verify; import javax.annotation.Nonnull; +/** + * A cluster represents the actual Cloud Bigtable service. Each cluster belongs to a single Cloud + * Bigtable instance, and an instance can have up to 2 clusters. When your application sends + * requests to a Cloud Bigtable instance, those requests are actually handled by one of the clusters + * in the instance. + */ public class Cluster { @Nonnull private final com.google.bigtable.admin.v2.Cluster proto; + /** + * Wraps a protobuf response. + * + *

This method is considered an internal implementation detail and not meant to be used by + * applications. + */ @InternalApi public static Cluster fromProto(com.google.bigtable.admin.v2.Cluster proto) { return new Cluster(proto); diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequest.java b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequest.java index d653be2ac99b..aefccd3f692b 100644 --- a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequest.java +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequest.java @@ -112,6 +112,9 @@ public com.google.bigtable.admin.v2.CreateClusterRequest toProto(ProjectName pro /** * Gets the clusterId. This method is meant to be used by {@link CreateClusterRequest} and is * considered an internal implementation detail and not meant to be used by applications. + * + *

This method is considered an internal implementation detail and not meant to be used by + * applications. */ @InternalApi String getClusterId() { @@ -121,6 +124,9 @@ String getClusterId() { /** * Creates the request protobuf to be used in {@link CreateInstanceRequest}. This method is * considered an internal implementation detail and not meant to be used by applications. + * + *

This method is considered an internal implementation detail and not meant to be used by + * applications. */ @InternalApi com.google.bigtable.admin.v2.Cluster toEmbeddedProto(ProjectName projectName) { diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/PartialListClustersException.java b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/PartialListClustersException.java index 73ac4cf39fe0..1696877ca443 100644 --- a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/PartialListClustersException.java +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/PartialListClustersException.java @@ -27,6 +27,10 @@ public class PartialListClustersException extends RuntimeException { private final List unavailableZones; private final List clusters; + /** + *

This method is considered an internal implementation detail and not meant to be used by + * applications. + */ @InternalApi public PartialListClustersException(List unavailableZones, List clusters) { super("Failed to list all instances, some zones where unavailable"); From aab9371f13bc451966ca87178816e6752dd6ff17 Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Wed, 29 Aug 2018 17:00:08 -0400 Subject: [PATCH 4/7] Address feedback --- .../cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java | 5 +++-- .../com/google/cloud/bigtable/admin/v2/models/Cluster.java | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java index 2966ff764b2c..2faa6fceaa8c 100644 --- a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java @@ -282,8 +282,9 @@ public ApiFuture> listClustersAsync(String instanceId) { new ApiFunction>() { @Override public List apply(ListClustersResponse proto) { - // NOTE: pagination is intentionally ignored. The server does not implement it and never - // will. + // NOTE: servserside pagination is not and will not be implemented, so remaining pages + // are not fetched. However, if that assumption turns out to be wrong, fail fast to + // avoid returning partial data. Verify.verify(proto.getNextPageToken().isEmpty(), "Server returned an unexpected paginated response"); diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/Cluster.java b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/Cluster.java index 644fd24b2710..19ef7a026b69 100644 --- a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/Cluster.java +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/Cluster.java @@ -47,9 +47,9 @@ public static Cluster fromProto(com.google.bigtable.admin.v2.Cluster proto) { } private Cluster(@Nonnull com.google.bigtable.admin.v2.Cluster proto) { - this.proto = proto; Preconditions.checkNotNull(proto); Preconditions.checkArgument(!proto.getName().isEmpty(), "Name must be set"); + this.proto = proto; } From 4ed81710940a6c4d31fd3d1820b985dca648f863 Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Fri, 31 Aug 2018 12:58:16 -0400 Subject: [PATCH 5/7] empty commit to trigger ci From 94602258bf0514fdcb68f07f6f50cf654bf6b05e Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Tue, 4 Sep 2018 11:44:42 -0400 Subject: [PATCH 6/7] fix ups --- .../admin/v2/BigtableInstanceAdminClient.java | 53 ++++--------------- .../v2/BigtableInstanceAdminClientTest.java | 40 ++------------ .../bigtable/admin/v2/models/ClusterTest.java | 2 +- 3 files changed, 14 insertions(+), 81 deletions(-) diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java index 0a54b7e33468..9246533ed6ca 100644 --- a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java @@ -20,28 +20,23 @@ import com.google.api.core.ApiFutures; import com.google.bigtable.admin.v2.ClusterName; import com.google.bigtable.admin.v2.DeleteClusterRequest; +import com.google.bigtable.admin.v2.DeleteInstanceRequest; import com.google.bigtable.admin.v2.GetClusterRequest; +import com.google.bigtable.admin.v2.GetInstanceRequest; import com.google.bigtable.admin.v2.InstanceName; import com.google.bigtable.admin.v2.ListClustersRequest; import com.google.bigtable.admin.v2.ListClustersResponse; -import com.google.bigtable.admin.v2.LocationName; -import com.google.api.core.ApiFunction; -import com.google.api.core.ApiFuture; -import com.google.api.core.ApiFutures; -import com.google.bigtable.admin.v2.DeleteInstanceRequest; -import com.google.bigtable.admin.v2.GetInstanceRequest; -import com.google.bigtable.admin.v2.InstanceName; import com.google.bigtable.admin.v2.ListInstancesRequest; import com.google.bigtable.admin.v2.ListInstancesResponse; import com.google.bigtable.admin.v2.LocationName; import com.google.bigtable.admin.v2.ProjectName; +import com.google.cloud.bigtable.admin.v2.models.Cluster; +import com.google.cloud.bigtable.admin.v2.models.CreateClusterRequest; import com.google.cloud.bigtable.admin.v2.models.CreateInstanceRequest; import com.google.cloud.bigtable.admin.v2.models.Instance; +import com.google.cloud.bigtable.admin.v2.models.PartialListClustersException; import com.google.cloud.bigtable.admin.v2.models.PartialListInstancesException; import com.google.cloud.bigtable.admin.v2.models.UpdateInstanceRequest; -import com.google.cloud.bigtable.admin.v2.models.Cluster; -import com.google.cloud.bigtable.admin.v2.models.CreateClusterRequest; -import com.google.cloud.bigtable.admin.v2.models.PartialListClustersException; import com.google.cloud.bigtable.admin.v2.stub.BigtableInstanceAdminStub; import com.google.common.base.Verify; import com.google.common.collect.ImmutableList; @@ -410,34 +405,6 @@ public Void apply(Empty input) { ); } - /** - * Awaits the result of a future, taking care to propagate errors while maintaining the call site - * in a suppressed exception. This allows semantic errors to be caught across threads, while - * preserving the call site in the error. The caller's stacktrace will be made available as a - * suppressed exception. - */ - // TODO(igorbernstein2): try to move this into gax - private T awaitFuture(ApiFuture future) { - RuntimeException error; - - try { - return Futures.getUnchecked(future); - } catch (UncheckedExecutionException e) { - if (e.getCause() instanceof RuntimeException) { - error = (RuntimeException) e.getCause(); - } else { - error = e; - } - } catch (RuntimeException e) { - error = e; - } - - // Add the caller's stack as a suppressed exception - error.addSuppressed(new RuntimeException("Encountered error while awaiting future")); - - throw error; - } - /** * Creates a new cluster in the specified instance. * @@ -470,7 +437,7 @@ public Cluster createCluster(CreateClusterRequest request) { * .setStorageType(StorageType.SSD) * ); * - * Cluster cluste = clusterFuture.get(); + * Cluster cluster = clusterFuture.get(); * } */ @SuppressWarnings("WeakerAccess") @@ -541,7 +508,7 @@ public Cluster apply(com.google.bigtable.admin.v2.Cluster proto) { * *

{@code
    * try {
-   *   List clusters = clister.listClusters("my-instance");
+   *   List clusters = cluster.listClusters("my-instance");
    * } catch (PartialListClustersException e) {
    *   System.out.println("The following zones are unavailable: " + e.getUnavailableZones());
    *   System.out.println("But the following clusters are reachable: " + e.getClusters())
@@ -593,7 +560,7 @@ public ApiFuture> listClustersAsync(String instanceId) {
         new ApiFunction>() {
           @Override
           public List apply(ListClustersResponse proto) {
-            // NOTE: servserside pagination is not and will not be implemented, so remaining pages
+            // NOTE: serverside pagination is not and will not be implemented, so remaining pages
             // are not fetched. However, if that assumption turns out to be wrong, fail fast to
             // avoid returning partial data.
             Verify.verify(proto.getNextPageToken().isEmpty(),
@@ -628,7 +595,7 @@ public List apply(ListClustersResponse proto) {
    * 

Sample code: * *

{@code
-   * Cluster cluster = clister.resizeCluster("my-instance", "my-cluster", 30);
+   * Cluster cluster = cluster.resizeCluster("my-instance", "my-cluster", 30);
    * }
*/ @SuppressWarnings("WeakerAccess") @@ -641,7 +608,7 @@ public Cluster resizeCluster(String instanceId, String clusterId, int numServeNo * a PRODUCTION instance can be resized. * *
{@code
-   * ApiFuture clusterFuture = clister.resizeCluster("my-instance", "my-cluster", 30);
+   * ApiFuture clusterFuture = cluster.resizeCluster("my-instance", "my-cluster", 30);
    * Cluster cluster = clusterFuture.get();
    * }
*/ diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTest.java b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTest.java index 995423a087f4..444ba99053ec 100644 --- a/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTest.java +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTest.java @@ -27,18 +27,6 @@ import com.google.api.gax.rpc.UnaryCallable; import com.google.api.gax.rpc.testing.FakeOperationSnapshot; import com.google.bigtable.admin.v2.ClusterName; -import com.google.bigtable.admin.v2.InstanceName; -import com.google.bigtable.admin.v2.LocationName; -import com.google.api.core.ApiFuture; -import com.google.api.core.ApiFutures; -import com.google.api.gax.grpc.GrpcStatusCode; -import com.google.api.gax.longrunning.OperationFuture; -import com.google.api.gax.longrunning.OperationFutures; -import com.google.api.gax.longrunning.OperationSnapshot; -import com.google.api.gax.rpc.OperationCallable; -import com.google.api.gax.rpc.UnaryCallable; -import com.google.api.gax.rpc.testing.FakeOperationSnapshot; -import com.google.bigtable.admin.v2.Cluster; import com.google.bigtable.admin.v2.CreateInstanceMetadata; import com.google.bigtable.admin.v2.Instance.Type; import com.google.bigtable.admin.v2.InstanceName; @@ -46,21 +34,16 @@ import com.google.bigtable.admin.v2.ProjectName; import com.google.bigtable.admin.v2.StorageType; import com.google.bigtable.admin.v2.UpdateClusterMetadata; +import com.google.bigtable.admin.v2.UpdateInstanceMetadata; import com.google.cloud.bigtable.admin.v2.models.Cluster; import com.google.cloud.bigtable.admin.v2.models.CreateClusterRequest; -import com.google.cloud.bigtable.admin.v2.models.PartialListClustersException; -import com.google.bigtable.admin.v2.StorageType; -import com.google.bigtable.admin.v2.UpdateInstanceMetadata; import com.google.cloud.bigtable.admin.v2.models.CreateInstanceRequest; import com.google.cloud.bigtable.admin.v2.models.Instance; +import com.google.cloud.bigtable.admin.v2.models.PartialListClustersException; import com.google.cloud.bigtable.admin.v2.models.PartialListInstancesException; import com.google.cloud.bigtable.admin.v2.models.UpdateInstanceRequest; import com.google.cloud.bigtable.admin.v2.stub.BigtableInstanceAdminStub; import com.google.protobuf.Empty; -import io.grpc.Status.Code; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; -import com.google.protobuf.Empty; import com.google.protobuf.FieldMask; import io.grpc.Status.Code; import java.util.List; @@ -126,7 +109,6 @@ public void setUp() { Mockito.when(mockStub.getInstanceCallable()).thenReturn(mockGetInstanceCallable); Mockito.when(mockStub.listInstancesCallable()).thenReturn(mockListInstancesCallable); Mockito.when(mockStub.deleteInstanceCallable()).thenReturn(mockDeleteInstanceCallable); - .create(PROJECT_NAME, mockStub); Mockito.when(mockStub.createClusterOperationCallable()).thenReturn(mockCreateClusterCallable); Mockito.when(mockStub.getClusterCallable()).thenReturn(mockGetClusterCallable); @@ -158,7 +140,7 @@ public void testCreateInstance() { .setType(Type.DEVELOPMENT) .setDisplayName(INSTANCE_NAME.getInstance()) ) - .putClusters("cluster1", Cluster.newBuilder() + .putClusters("cluster1", com.google.bigtable.admin.v2.Cluster.newBuilder() .setLocation("projects/my-project/locations/us-east1-c") .setServeNodes(1) .setDefaultStorageType(StorageType.SSD) @@ -342,22 +324,6 @@ public ApiFuture answer(InvocationOnMock invocationOnMock) { assertThat(wasCalled.get()).isTrue(); } - - private void mockOperationResult( - OperationCallable callable, ReqT request, RespT response) { - OperationSnapshot operationSnapshot = FakeOperationSnapshot.newBuilder() - .setDone(true) - .setErrorCode(GrpcStatusCode.of(Code.OK)) - .setName("fake-name") - .setResponse(response) - .build(); - - OperationFuture operationFuture = OperationFutures - .immediateOperationFuture(operationSnapshot); - - Mockito.when(callable.futureCall(request)).thenReturn(operationFuture); - } - @Test public void testCreateCluster() { // Setup diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/ClusterTest.java b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/ClusterTest.java index de521c5cda20..76e158c7603d 100644 --- a/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/ClusterTest.java +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/ClusterTest.java @@ -64,4 +64,4 @@ public void testRequiresName() { assertThat(actualException).isInstanceOf(IllegalArgumentException.class); } -} \ No newline at end of file +} From 0e9d7208e879e1a03dd5ef5f2479185f44b9702a Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Tue, 4 Sep 2018 11:47:49 -0400 Subject: [PATCH 7/7] fixups --- .../cloud/bigtable/admin/v2/models/CreateClusterRequest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequest.java b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequest.java index 6ec1e0b22d40..aefccd3f692b 100644 --- a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequest.java +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequest.java @@ -134,4 +134,4 @@ com.google.bigtable.admin.v2.Cluster toEmbeddedProto(ProjectName projectName) { return proto.getClusterBuilder().build(); } -} \ No newline at end of file +}