diff --git a/google-cloud-clients/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/InstanceAdminClient.java b/google-cloud-clients/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/InstanceAdminClient.java new file mode 100644 index 000000000000..72ed4aaba45d --- /dev/null +++ b/google-cloud-clients/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/InstanceAdminClient.java @@ -0,0 +1,454 @@ +package com.google.cloud.bigtable.admin.v2; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import com.google.api.core.ApiFunction; +import com.google.api.core.ApiFuture; +import com.google.api.core.BetaApi; +import com.google.api.core.SettableApiFuture; +import com.google.api.gax.longrunning.OperationFuture; +import com.google.bigtable.admin.v2.AppProfileName; +import com.google.bigtable.admin.v2.ClusterName; +import com.google.bigtable.admin.v2.CreateAppProfileRequest; +import com.google.bigtable.admin.v2.CreateClusterMetadata; +import com.google.bigtable.admin.v2.CreateClusterRequest; +import com.google.bigtable.admin.v2.CreateInstanceMetadata; +import com.google.bigtable.admin.v2.DeleteAppProfileRequest; +import com.google.bigtable.admin.v2.DeleteClusterRequest; +import com.google.bigtable.admin.v2.DeleteInstanceRequest; +import com.google.bigtable.admin.v2.GetAppProfileRequest; +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.ListAppProfilesRequest; +import com.google.bigtable.admin.v2.ListAppProfilesResponse; +import com.google.bigtable.admin.v2.ListClustersRequest; +import com.google.bigtable.admin.v2.ListClustersResponse; +import com.google.bigtable.admin.v2.ListInstancesRequest; +import com.google.bigtable.admin.v2.ListInstancesResponse; +import com.google.bigtable.admin.v2.PartialUpdateInstanceRequest; +import com.google.bigtable.admin.v2.ProjectName; +import com.google.bigtable.admin.v2.UpdateAppProfileMetadata; +import com.google.bigtable.admin.v2.UpdateAppProfileRequest; +import com.google.bigtable.admin.v2.UpdateClusterMetadata; +import com.google.bigtable.admin.v2.UpdateInstanceMetadata; +import com.google.cloud.bigtable.admin.v2.models.FailedLocationException; +import com.google.cloud.bigtable.admin.v2.models.InstanceAdminRequests; +import com.google.cloud.bigtable.admin.v2.models.InstanceAdminRequests.AppProfile; +import com.google.cloud.bigtable.admin.v2.models.InstanceAdminRequests.AppProfile.UpdateAppProfile; +import com.google.cloud.bigtable.admin.v2.models.InstanceAdminRequests.Cluster; +import com.google.cloud.bigtable.admin.v2.models.InstanceAdminRequests.CreateInstance; +import com.google.cloud.bigtable.admin.v2.models.InstanceAdminRequests.Instance; +import com.google.cloud.bigtable.admin.v2.models.InstanceAdminRequests.Policy; +import com.google.cloud.bigtable.admin.v2.models.InstanceAdminRequests.UpdateCluster; +import com.google.cloud.bigtable.admin.v2.models.InstanceAdminRequests.UpdateInstance; +import com.google.cloud.bigtable.admin.v2.stub.BigtableInstanceAdminStub; +import com.google.cloud.bigtable.admin.v2.stub.BigtableInstanceAdminStubSettings; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.iam.v1.GetIamPolicyRequest; +import com.google.iam.v1.SetIamPolicyRequest; +import com.google.iam.v1.TestIamPermissionsRequest; + +@BetaApi +public class InstanceAdminClient implements AutoCloseable { + private final BigtableInstanceAdminStub stub; + + public static final InstanceAdminClient create() throws IOException { + return new InstanceAdminClient(BigtableInstanceAdminSettings.newBuilder().build()); + } + + public static final InstanceAdminClient create(BigtableInstanceAdminSettings settings) + throws IOException { + return new InstanceAdminClient(settings); + } + + public static final InstanceAdminClient create(BigtableInstanceAdminStub stub) { + return new InstanceAdminClient(stub); + } + + public InstanceAdminClient(BigtableInstanceAdminSettings settings) throws IOException { + this(((BigtableInstanceAdminStubSettings) settings.getStubSettings()).createStub()); + } + + public InstanceAdminClient(BigtableInstanceAdminStub stub) { + this.stub = stub; + } + + @Override + public void close() throws Exception { + stub.close(); + } + + /** + * + *
+   * {@code 
+   * try(InstanceAdminClient instanceAdmin = InstanceAdminClient.create()) {
+   *   CreateInstance request =
+   *     InstanceAdminRequests.createInstance(
+   *         ProjectName.of("project"),
+   *         Instance.ofNewProdInstance("instanceid", "displayname"),
+   *         Cluster.ofNewProdCluster(
+   *           "clusterone", Location.of("project", "zone"), 3))
+   *       .addCluster(
+   *         Cluster.ofNewProdCluster(
+   *           "clustertwo", Location.of("project", "us-east1-c"), 4));
+   *
+   *    Instance createdInstance = instanceAdmin.createInstanceAsync(request).get();
+   * }
+   * }
+   * 
+ * + * @param createInstance + * @return + */ + public ApiFuture createInstanceAsync(CreateInstance createInstance) { + final OperationFuture future = + stub.createInstanceOperationCallable().futureCall(createInstance.toProto()); + + return transfromOperationFuture(future, + new ApiFunction() { + @Override + public Instance apply(com.google.bigtable.admin.v2.Instance input) { + return InstanceAdminRequests.convertToInstance(input); + } + }); + } + + public Instance getInstance(InstanceName instanceName) { + return InstanceAdminRequests.convertToInstance( + stub.getInstanceCallable().call(composeGetInstanceRequest(instanceName))); + } + + public List listInstances(ProjectName projectName) { + return convertToInstances( + stub.listInstancesCallable().call(composeListInstanceRequest(projectName))); + } + + /** + * + *
+   * {@code 
+   * try(InstanceAdminClient instanceAdmin = InstanceAdminClient.create()) {
+   *    Instance devInstance = instanceAdmin.getInstance(
+   *        InstanceName.of("project", "instance"));
+   *    
+   *    instanceAdmin.updateInstance(
+   *      Instance.ofUpdateInstance(devInstance)
+   *        .updateDisplayName("display")
+   *        .upgradeType()
+   *        .updateLabels(updatedLabels));
+   * }
+   * }
+   * 
+ * + * @param updatedInstance + * @return + */ + public ApiFuture updateInstance(UpdateInstance updatedInstance) { + OperationFuture future = + stub.partialUpdateInstanceOperationCallable() + .futureCall(composePartialUpdateInstanceCallable(updatedInstance)); + + return transfromOperationFuture(future, + new ApiFunction() { + @Override + public Instance apply(com.google.bigtable.admin.v2.Instance input) { + return InstanceAdminRequests.convertToInstance(input); + } + }); + } + + public void deleteInstanceRequest(InstanceName instanceName) { + stub.deleteInstanceCallable().call(composeDeleteInstanceRequest(instanceName)); + } + + public ApiFuture createCluster(InstanceName instanceName, Cluster cluster) { + OperationFuture future = + stub.createClusterOperationCallable() + .futureCall(composeCreateClusterRequest(instanceName, cluster)); + + return transfromOperationFuture(future, + new ApiFunction() { + @Override + public Cluster apply(com.google.bigtable.admin.v2.Cluster input) { + return InstanceAdminRequests.convertToCluster(input); + } + }); + } + + public Cluster getCluster(ClusterName clusterName) { + return InstanceAdminRequests.convertToCluster( + stub.getClusterCallable().call(composeGetClusterRequest(clusterName))); + } + + public List listClusters(InstanceName instanceName) { + return convertToClusters( + stub.listClustersCallable().call(composeListClustersRequest(instanceName))); + } + + /** + * + *
+   * {@code 
+   * try(InstanceAdminClient instanceAdmin = InstanceAdminClient.create()) {
+   *   Cluster prodCluster = instanceAdmin.getCluster(
+   *     ClusterName.of("project", "instance", "cluster"));
+   *    
+   *   instanceAdmin.updateCluster(
+   *     Cluster.ofUpdateCluster(prodCluster)
+   *       .updateNumNodes(5));
+   * }
+   * }
+   * 
+ * + * @param updatedCluster + * @return + */ + public ApiFuture updateCluster(UpdateCluster updatedCluster) { + OperationFuture future = + stub.updateClusterOperationCallable().futureCall(updatedCluster.toProto()); + + return transfromOperationFuture(future, + new ApiFunction() { + @Override + public Cluster apply(com.google.bigtable.admin.v2.Cluster input) { + return InstanceAdminRequests.convertToCluster(input); + } + }); + } + + public void deleteCluster(ClusterName clusterName) { + stub.deleteClusterCallable().call(composeDeleteClusterRequest(clusterName)); + } + + /** + * + *
+   * {@code 
+   * try(InstanceAdminClient instanceAdmin = InstanceAdminClient.create()) {
+   *   instanceAdmin.createAppProfile(
+   *     InstanceName.of("project", "instance"),
+   *     AppProfile.ofNewAppProfile("roundRobin")
+   *       .routeToAny());
+   * }
+   * }
+   * 
+ * + * @param instanceName + * @param appProfile + * @return + */ + public AppProfile createAppProfile(InstanceName instanceName, AppProfile appProfile) { + return InstanceAdminRequests.convertToAppProfile(stub.createAppProfileCallable().call( + composeCreateAppProfileRequest(instanceName, appProfile))); + } + + public AppProfile getAppProfile(AppProfileName appProfileName) { + return InstanceAdminRequests.convertToAppProfile( + stub.getAppProfileCallable().call(composeGetAppProfileRequest(appProfileName))); + } + + public List listAppProfiles(InstanceName instanceName) { + return convertToAppProfiles( + stub.listAppProfilesCallable().call(composeListAppProfilesRequest(instanceName))); + } + + /** + * + *
+   * {@code 
+   * try(InstanceAdminClient instanceAdmin = InstanceAdminClient.create()) {
+   *   AppProfile appProfileAny = instanceAdmin.getAppProfile(
+   *       AppProfileName.of("project", "instance", "profile"));
+   *   
+   *   AppProfile appProfileCluster = instanceAdmin.updateAppProfile(
+   *       AppProfile.ofUpdateAppProfile(appProfileAny)
+   *         .updateDescription("newDescription")
+   *         .updateRouteToCluster("clusterToUse", false))
+   *         .get();
+   * }
+   * }
+   * 
+ * + * @param updateAppProfile + * @return + */ + public ApiFuture updateAppProfile(UpdateAppProfile updateAppProfile) { + OperationFuture future = + stub.updateAppProfileOperationCallable() + .futureCall(composeUpdateAppProfileRequest(updateAppProfile)); + + return transfromOperationFuture(future, + new ApiFunction() { + @Override + public AppProfile apply(com.google.bigtable.admin.v2.AppProfile input) { + return InstanceAdminRequests.convertToAppProfile(input); + } + }); + } + + public void deleteAppProfile(AppProfileName appProfileName, boolean ignoreWarnings) { + stub.deleteAppProfileCallable() + .call(composeDeleteAppProfileRequest(appProfileName, ignoreWarnings)); + } + + public Policy getIamPolicy(InstanceName instanceName) { + return InstanceAdminRequests.convertToPolicy( + stub.getIamPolicyCallable().call(composeGetIamPolicyRequest(instanceName))); + } + + public Policy setIamPolicy(InstanceName instanceName, Policy policy) { + return InstanceAdminRequests.convertToPolicy( + stub.setIamPolicyCallable().call(composeSetIamPolicyRequest(instanceName, policy))); + } + + public List testIamPermissions(InstanceName instanceName, List permissions) { + return stub.testIamPermissionsCallable() + .call(composeTestIamPermissionsRequest(instanceName, permissions)) + .getPermissionsList(); + } + + /** compose proto request helpers * */ + private static GetInstanceRequest composeGetInstanceRequest(InstanceName instanceName) { + return GetInstanceRequest.newBuilder().setName(instanceName.toString()).build(); + } + + private static ListInstancesRequest composeListInstanceRequest(ProjectName projectName) { + return ListInstancesRequest.newBuilder().setParent(projectName.toString()).build(); + } + + private static PartialUpdateInstanceRequest composePartialUpdateInstanceCallable( + UpdateInstance updatedInstance) { + return PartialUpdateInstanceRequest.newBuilder().setInstance(updatedInstance.toProto()) + .setUpdateMask(updatedInstance.getPartialUpdateFieldMask()) + .build(); + } + + private static DeleteInstanceRequest composeDeleteInstanceRequest(InstanceName instanceName) { + return DeleteInstanceRequest.newBuilder().setName(instanceName.toString()).build(); + } + + private static CreateClusterRequest composeCreateClusterRequest(InstanceName instanceName, + Cluster cluster) { + return CreateClusterRequest.newBuilder().setParent(instanceName.toString()) + .setClusterId(cluster.getId()).setCluster(cluster.toProto()).build(); + } + + private static GetClusterRequest composeGetClusterRequest(ClusterName clusterName) { + return GetClusterRequest.newBuilder().setName(clusterName.toString()).build(); + } + + private static ListClustersRequest composeListClustersRequest(InstanceName instanceName) { + return ListClustersRequest.newBuilder().setParent(instanceName.toString()).build(); + } + + private static DeleteClusterRequest composeDeleteClusterRequest(ClusterName clusterName) { + return DeleteClusterRequest.newBuilder().setName(clusterName.toString()).build(); + } + + private static CreateAppProfileRequest composeCreateAppProfileRequest(InstanceName instanceName, + AppProfile appProfile) { + return CreateAppProfileRequest.newBuilder().setParent(instanceName.toString()) + .setAppProfileId(appProfile.getId()) + .setAppProfile(appProfile.toProto()).build(); + } + + private static GetAppProfileRequest composeGetAppProfileRequest(AppProfileName appProfileName) { + return GetAppProfileRequest.newBuilder().setName(appProfileName.toString()).build(); + } + + private static ListAppProfilesRequest composeListAppProfilesRequest(InstanceName instanceName) { + return ListAppProfilesRequest.newBuilder().setParent(instanceName.toString()).build(); + } + + private static UpdateAppProfileRequest composeUpdateAppProfileRequest( + UpdateAppProfile updateAppProfile) { + return UpdateAppProfileRequest.newBuilder().setAppProfile(updateAppProfile.toProto()) + .setUpdateMask(updateAppProfile.getPartialUpdateFieldMask()) + .build(); + } + + private static DeleteAppProfileRequest composeDeleteAppProfileRequest( + AppProfileName appProfileName, boolean ignoreWarnings) { + return DeleteAppProfileRequest.newBuilder().setName(appProfileName.toString()) + .setIgnoreWarnings(ignoreWarnings).build(); + } + + private static GetIamPolicyRequest composeGetIamPolicyRequest(InstanceName instanceName) { + return GetIamPolicyRequest.newBuilder().setResource(instanceName.toString()).build(); + } + + private static SetIamPolicyRequest composeSetIamPolicyRequest(InstanceName instanceName, + Policy policy) { + return SetIamPolicyRequest.newBuilder().setResource(instanceName.toString()) + .setPolicy(policy.toProto()).build(); + } + + private static TestIamPermissionsRequest composeTestIamPermissionsRequest( + InstanceName instanceName, List permissions) { + return TestIamPermissionsRequest.newBuilder().setResource(instanceName.toString()) + .addAllPermissions(permissions).build(); + } + + private static List convertToInstances(ListInstancesResponse listInstancesResponse) { + List instances = new ArrayList<>(); + List succeededLocations = new ArrayList<>(); + for (com.google.bigtable.admin.v2.Instance instance : listInstancesResponse.getInstancesList()) { + instances.add(InstanceAdminRequests.convertToInstance(instance)); + } + + if (listInstancesResponse.getFailedLocationsList().size() > 0) { + throw new FailedLocationException("Failed to list all locations", succeededLocations, + listInstancesResponse.getFailedLocationsList()); + } + + return instances; + } + + private static List convertToClusters(ListClustersResponse listClustersResponse) { + List clusters = new ArrayList<>(); + List succeededLocations = new ArrayList<>(); + for (com.google.bigtable.admin.v2.Cluster cluster : listClustersResponse.getClustersList()) { + clusters.add(InstanceAdminRequests.convertToCluster(cluster)); + succeededLocations.add(cluster.getLocation()); + } + + if (listClustersResponse.getFailedLocationsList().size() > 0) { + throw new FailedLocationException("Failed to list all locations", succeededLocations, + listClustersResponse.getFailedLocationsList()); + } + + return clusters; + } + + private static List convertToAppProfiles( + ListAppProfilesResponse listAppProfilesResponse) { + List appProfiles = new ArrayList<>(); + + for (com.google.bigtable.admin.v2.AppProfile profile : listAppProfilesResponse.getAppProfilesList()) { + appProfiles.add(InstanceAdminRequests.convertToAppProfile(profile)); + } + return appProfiles; + } + + /** Can be removed after https://github.com/googleapis/gax-java/issues/552 is handled */ + private static ApiFuture transfromOperationFuture( + final OperationFuture future, + final ApiFunction function) { + final SettableApiFuture result = SettableApiFuture.create(); + future.addListener(new Runnable() { + @Override + public void run() { + try { + result.set(function.apply(future.get())); + } catch (InterruptedException | ExecutionException e) { + result.setException(e); + } + } + }, MoreExecutors.directExecutor()); + return result; + } +} diff --git a/google-cloud-clients/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/FailedLocationException.java b/google-cloud-clients/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/FailedLocationException.java new file mode 100644 index 000000000000..cfc62e44a516 --- /dev/null +++ b/google-cloud-clients/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/FailedLocationException.java @@ -0,0 +1,26 @@ +package com.google.cloud.bigtable.admin.v2.models; + +import java.util.List; +import com.google.api.core.BetaApi; + +@BetaApi +public class FailedLocationException extends RuntimeException { + private static final long serialVersionUID = 1L; + private final List succeededLocations; + private final List failedLocations; + + public FailedLocationException( + String message, List succeededLocations, List failedLocations) { + super(message); + this.succeededLocations = succeededLocations; + this.failedLocations = failedLocations; + } + + public List getSucceededLocations() { + return succeededLocations; + } + + public List getFailedLocations() { + return failedLocations; + } +} diff --git a/google-cloud-clients/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/InstanceAdminRequests.java b/google-cloud-clients/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/InstanceAdminRequests.java new file mode 100644 index 000000000000..6d598e70ef82 --- /dev/null +++ b/google-cloud-clients/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/InstanceAdminRequests.java @@ -0,0 +1,585 @@ +package com.google.cloud.bigtable.admin.v2.models; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import com.google.api.core.BetaApi; +import com.google.api.core.InternalApi; +import com.google.bigtable.admin.v2.AppProfile.RoutingPolicyCase; +import com.google.bigtable.admin.v2.AppProfileName; +import com.google.bigtable.admin.v2.ClusterName; +import com.google.bigtable.admin.v2.CreateInstanceRequest; +import com.google.bigtable.admin.v2.Instance.State; +import com.google.bigtable.admin.v2.Instance.Type; +import com.google.bigtable.admin.v2.InstanceName; +import com.google.bigtable.admin.v2.ProjectName; +import com.google.bigtable.admin.v2.StorageType; +import com.google.cloud.Role; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import com.google.iam.v1.Binding; +import com.google.protobuf.ByteString; +import com.google.protobuf.FieldMask; + +@BetaApi +public final class InstanceAdminRequests { + + public static CreateInstance createInstance( + ProjectName projectName, Instance instance, Cluster cluster) { + return new CreateInstance(projectName, instance, cluster); + } + + public static Instance convertToInstance(com.google.bigtable.admin.v2.Instance protoInstance) { + return new Instance(protoInstance); + } + + public static Cluster convertToCluster(com.google.bigtable.admin.v2.Cluster protoCluster) { + return new Cluster(protoCluster); + } + + public static AppProfile convertToAppProfile(com.google.bigtable.admin.v2.AppProfile appProfile) { + return new AppProfile(appProfile); + } + + public static Policy convertToPolicy(com.google.iam.v1.Policy policy) { + return new Policy(policy); + } + + /** */ + public static final class CreateInstance { + private final CreateInstanceRequest.Builder createInstanceRequest = + CreateInstanceRequest.newBuilder(); + + private CreateInstance(ProjectName projectName, Instance instance, Cluster cluster) { + Preconditions.checkNotNull(projectName); + Preconditions.checkNotNull(instance); + Preconditions.checkNotNull(cluster); + + createInstanceRequest + .setParent(projectName.toString()) + .setInstanceId(instance.getid()) + .setInstance(instance.toProto()) + .putClusters(cluster.getId(), cluster.toProto()); + } + + public CreateInstance addCluster(Cluster cluster) { + createInstanceRequest.putClusters(cluster.getId(), cluster.toProto()); + return this; + } + + public CreateInstanceRequest toProto() { + return createInstanceRequest.build(); + } + } + + public static final class Instance { + private final String id; + private com.google.bigtable.admin.v2.Instance.Builder protoInstance = + com.google.bigtable.admin.v2.Instance.newBuilder(); + + public static Instance ofNewDevInstance(String instanceId, String displayName) { + return new Instance(instanceId, displayName, Type.DEVELOPMENT); + } + + public static Instance ofNewProdInstance(String instanceId, String displayName) { + return new Instance(instanceId, displayName, Type.PRODUCTION); + } + + public static UpdateInstance ofUpdateInstance(Instance instance) { + return new UpdateInstance(instance); + } + + private Instance(String instanceId, String displayName, Type instanceType) { + this.id = instanceId; + protoInstance.setDisplayName(displayName).setType(instanceType); + } + + private Instance(com.google.bigtable.admin.v2.Instance instance) { + this.protoInstance = instance.toBuilder(); + id = getName().getInstance(); + } + + public String getid() { + return id; + } + + public InstanceName getName() { + return InstanceName.parse(protoInstance.getName()); + } + + public String getDisplayName() { + return protoInstance.getDisplayName(); + } + + public State getState() { + return protoInstance.getState(); + } + + public Type getType() { + return protoInstance.getType(); + } + + public Map getLabelsMap() { + return protoInstance.getLabelsMap(); + } + + public Instance addLabel(String key, String value) { + // TODO: add an regex check. Error from is hard to decipher + protoInstance.putLabels(key, value); + return this; + } + + @InternalApi + public com.google.bigtable.admin.v2.Instance toProto() { + return protoInstance.build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("name", getName()) + .add("displayName", getDisplayName()) + .add("state", getState()) + .add("type", getType()) + .add("labels", getLabelsMap()) + .toString(); + } + } + + public static final class UpdateInstance { + private final String DISPLAY_NAME_FIELDMASK = "display_name"; + private final String TYPE_FIELDMASK = "type"; + private final String LABELS_FIELDMASK = "labels"; + + private final List replacefields = new ArrayList<>(); + private com.google.bigtable.admin.v2.Instance.Builder protoUpdateInstance = + com.google.bigtable.admin.v2.Instance.newBuilder(); + + private UpdateInstance(Instance instance) { + protoUpdateInstance = instance.toProto().toBuilder().clone(); + } + + public UpdateInstance updateDisplayName(String updatedDisplayName) { + replacefields.add(DISPLAY_NAME_FIELDMASK); + protoUpdateInstance.setDisplayName(updatedDisplayName); + return this; + } + + public UpdateInstance upgradeType() { + Preconditions.checkArgument( + Type.DEVELOPMENT.equals(protoUpdateInstance.getType()), + "PRODUCTION type cannot be upgraded"); + replacefields.add(TYPE_FIELDMASK); + protoUpdateInstance.setType(Type.PRODUCTION); + return this; + } + + public UpdateInstance updateLabels(Map updatedLabels) { + replacefields.add(LABELS_FIELDMASK); + protoUpdateInstance.putAllLabels(updatedLabels); + return this; + } + + @InternalApi + public com.google.bigtable.admin.v2.Instance toProto() { + return protoUpdateInstance.clearState().build(); + } + + @InternalApi + public FieldMask getPartialUpdateFieldMask() { + return FieldMask.newBuilder().addAllPaths(replacefields).build(); + } + } + + public static final class Cluster { + public static Cluster ofNewDevCluster(String clusterId, Location location) { + return new Cluster(clusterId, location, 0); + } + + public static Cluster ofNewProdCluster(String clusterId, Location location, int serverNodes) { + return new Cluster(clusterId, location, serverNodes); + } + + public static UpdateCluster ofUpdateCluster(Cluster cluster) { + return new UpdateCluster(cluster); + } + + private final String id; + private com.google.bigtable.admin.v2.Cluster.Builder protoCluster = + com.google.bigtable.admin.v2.Cluster.newBuilder(); + + private Cluster(String clusterId, Location location, int serverNodes) { + protoCluster.setLocation(location.toString()).setServeNodes(serverNodes); + this.id = clusterId; + } + + private Cluster(com.google.bigtable.admin.v2.Cluster cluster) { + protoCluster = cluster.toBuilder(); + id = getName().getCluster(); + } + + public Cluster setDefaultStorage(StorageType defaultStorage) { + protoCluster.setDefaultStorageType(defaultStorage); + return this; + } + + public String getId() { + return id; + } + + public ClusterName getName() { + return ClusterName.parse(protoCluster.getName()); + } + + public String getLocation() { + return protoCluster.getLocation(); + } + + public com.google.bigtable.admin.v2.Cluster.State getState() { + return protoCluster.getState(); + } + + public int getServerNodes() { + return protoCluster.getServeNodes(); + } + + public StorageType getDefaultStorageType() { + return protoCluster.getDefaultStorageType(); + } + + @InternalApi + public com.google.bigtable.admin.v2.Cluster toProto() { + // TODO: defaultStorage is currently optional, should we make this required? + return protoCluster.build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("name", getName()) + .add("location", getLocation()) + .add("state", getState()) + .add("serverNodes", getServerNodes()) + .add("defaultStorageType", getDefaultStorageType()) + .toString(); + } + } + + public static final class UpdateCluster { + private com.google.bigtable.admin.v2.Cluster.Builder protoUpdateCluster = + com.google.bigtable.admin.v2.Cluster.newBuilder(); + + private UpdateCluster(Cluster cluster) { + protoUpdateCluster = cluster.toProto().toBuilder().clone(); + } + + public UpdateCluster updateNumNodes(int serverNodes) { + protoUpdateCluster.setServeNodes(serverNodes); + return this; + } + + @InternalApi + public com.google.bigtable.admin.v2.Cluster toProto() { + return protoUpdateCluster.clearState().clearDefaultStorageType().build(); + } + } + + public static final class AppProfile { + private final String id; + private com.google.bigtable.admin.v2.AppProfile.Builder protoProfile = + com.google.bigtable.admin.v2.AppProfile.newBuilder(); + + public static AppProfile ofNewAppProfile(String profileId) { + return new AppProfile(profileId); + } + + public static UpdateAppProfile ofUpdateAppProfile(AppProfile appProfile) { + return new UpdateAppProfile(appProfile); + } + + private AppProfile(String profileId) { + id = profileId; + } + + private AppProfile(com.google.bigtable.admin.v2.AppProfile appProfile) { + protoProfile = appProfile.toBuilder(); + id = getName().getAppProfile(); + } + + public AppProfile setDescription(String description) { + protoProfile.setDescription(description); + return this; + } + + public AppProfile routeToAny() { + protoProfile.setMultiClusterRoutingUseAny( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny.newBuilder().build()); + protoProfile.clearSingleClusterRouting(); + return this; + } + + public AppProfile routeToCluster(String clusterId, boolean ignoreWarnings) { + protoProfile.setSingleClusterRouting( + com.google.bigtable.admin.v2.AppProfile.SingleClusterRouting.newBuilder() + .setClusterId(clusterId) + .setAllowTransactionalWrites(ignoreWarnings)); + protoProfile.clearMultiClusterRoutingUseAny(); + return this; + } + + public String getId() { + return id; + } + + public AppProfileName getName() { + return AppProfileName.parse(protoProfile.getName()); + } + + public String getEtag() { + return protoProfile.getEtag(); + } + + public String getDescription() { + return protoProfile.getDescription(); + } + + public RoutingPolicy getRoutingPolicy() { + if (RoutingPolicyCase.MULTI_CLUSTER_ROUTING_USE_ANY + .name() + .equals(protoProfile.getRoutingPolicyCase().name())) { + return new MultiClusterRoutingUseAny(); + } + + if (RoutingPolicyCase.SINGLE_CLUSTER_ROUTING + .name() + .equals(protoProfile.getRoutingPolicyCase().name())) { + return new SingleClusterRouting(protoProfile.getSingleClusterRouting()); + } + + return new RoutingPolicyNotSet(); + } + + @InternalApi + public com.google.bigtable.admin.v2.AppProfile toProto() { + // TODO: should we default this to any instead? + Preconditions.checkState( + protoProfile.hasMultiClusterRoutingUseAny() || protoProfile.hasSingleClusterRouting(), + "Must specify a routing option"); + return protoProfile.build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("name", getName()) + .add("etag", getEtag()) + .add("description", getDescription()) + .add("routingPolicy", getRoutingPolicy()) + .toString(); + } + + public static class UpdateAppProfile { + private static final String DESCRIPTION_FIELDMASK = "description"; + private static final String MULTI_CLUSTER_FIELDMASK = "multi_cluster_routing_use_any"; + private static final String SINGLE_CLUSTER__FIELDMASK = "single_cluster_routing"; + + private final List replacefields = new ArrayList<>(); + private com.google.bigtable.admin.v2.AppProfile.Builder protoUpdateProfile = + com.google.bigtable.admin.v2.AppProfile.newBuilder(); + + private UpdateAppProfile(AppProfile appProfile) { + Preconditions.checkNotNull(appProfile); + protoUpdateProfile = appProfile.toProto().toBuilder().clone(); + } + + public UpdateAppProfile updateDescription(String updatedDescription) { + replacefields.add(DESCRIPTION_FIELDMASK); + protoUpdateProfile.setDescription(updatedDescription); + return this; + } + + public UpdateAppProfile updateRouteToAny() { + protoUpdateProfile.setMultiClusterRoutingUseAny( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny.newBuilder().build()); + protoUpdateProfile.clearSingleClusterRouting(); + replacefields.add(MULTI_CLUSTER_FIELDMASK); + return this; + } + + public UpdateAppProfile updateRouteToCluster(String clusterId, boolean ignoreWarnings) { + protoUpdateProfile.setSingleClusterRouting( + com.google.bigtable.admin.v2.AppProfile.SingleClusterRouting.newBuilder() + .setClusterId(clusterId) + .setAllowTransactionalWrites(ignoreWarnings)); + protoUpdateProfile.clearMultiClusterRoutingUseAny(); + replacefields.add(SINGLE_CLUSTER__FIELDMASK); + return this; + } + + @InternalApi + public com.google.bigtable.admin.v2.AppProfile toProto() { + return protoUpdateProfile.build(); + } + + @InternalApi + public FieldMask getPartialUpdateFieldMask() { + return FieldMask.newBuilder().addAllPaths(replacefields).build(); + } + } + + public interface RoutingPolicy { + RoutingPolicyCase name(); + } + + public static class RoutingPolicyNotSet implements RoutingPolicy { + private RoutingPolicyNotSet() {} + + @Override + public RoutingPolicyCase name() { + return RoutingPolicyCase.ROUTINGPOLICY_NOT_SET; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("name", name()).toString(); + } + } + + public static class MultiClusterRoutingUseAny implements RoutingPolicy { + private MultiClusterRoutingUseAny() {} + + @Override + public RoutingPolicyCase name() { + return RoutingPolicyCase.MULTI_CLUSTER_ROUTING_USE_ANY; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("name", name()).toString(); + } + } + + public static class SingleClusterRouting implements RoutingPolicy { + private final String clusterId; + private final boolean allowTransactionalWrites; + + private SingleClusterRouting(String clusterId, boolean allowTransactionalWrites) { + this.clusterId = clusterId; + this.allowTransactionalWrites = allowTransactionalWrites; + } + + private SingleClusterRouting( + com.google.bigtable.admin.v2.AppProfile.SingleClusterRouting routing) { + this.clusterId = routing.getClusterId(); + this.allowTransactionalWrites = routing.getAllowTransactionalWrites(); + } + + public String getClusterId() { + return clusterId; + } + + public boolean isAllowTransactionalWrites() { + return allowTransactionalWrites; + } + + @Override + public RoutingPolicyCase name() { + return RoutingPolicyCase.SINGLE_CLUSTER_ROUTING; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("name", name()) + .add("clusterId", getClusterId()) + .add("allowTransactionalWrites", isAllowTransactionalWrites()) + .toString(); + } + } + } + + public static final class Policy { + private com.google.iam.v1.Policy.Builder protoPolicy = com.google.iam.v1.Policy.newBuilder(); + + public static Policy of(int version, Role role, List members) { + return new Policy(version, role, members); + } + + private Policy(int version, Role role, List members) { + Preconditions.checkNotNull(role); + Preconditions.checkNotNull(members); + + protoPolicy.setVersion(version); + addRole(role, members); + } + + private Policy(com.google.iam.v1.Policy policy) { + protoPolicy = policy.toBuilder(); + } + + public Policy addRole(Role role, List members) { + Preconditions.checkNotNull(role); + Preconditions.checkNotNull(members); + + protoPolicy.addBindings( + com.google.iam.v1.Binding.newBuilder().setRole(role.toString()).addAllMembers(members)); + return this; + } + + public int getVersion() { + return protoPolicy.getVersion(); + } + + public ByteString getEtag() { + return protoPolicy.getEtag(); + } + + public Map> getBindingsMap() { + Map> bindings = new HashMap<>(); + + for (Binding binding : protoPolicy.getBindingsList()) { + bindings.put(Role.of(binding.getRole()), binding.getMembersList()); + } + return bindings; + } + + @InternalApi + public com.google.iam.v1.Policy toProto() { + return protoPolicy.build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("version", getVersion()) + .add("etag", getEtag()) + .add("bindingsMap", getBindingsMap()) + .toString(); + } + } + + public static final class Location { + public static Location of(String project, String zone) { + return new Location(project, zone); + } + + private final String location; + + private Location(String project, String zone) { + Preconditions.checkNotNull(project); + Preconditions.checkNotNull(zone); + + location = + new StringBuilder(ProjectName.of(project).toString()) + .append("/locations/") + .append(zone) + .toString(); + } + + @Override + public String toString() { + return location; + } + } +} diff --git a/google-cloud-clients/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/it/InstanceAdminClientIT.java b/google-cloud-clients/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/it/InstanceAdminClientIT.java new file mode 100644 index 000000000000..54517334aac6 --- /dev/null +++ b/google-cloud-clients/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/it/InstanceAdminClientIT.java @@ -0,0 +1,351 @@ +package com.google.cloud.bigtable.admin.v2.it; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import com.google.bigtable.admin.v2.AppProfile.RoutingPolicyCase; +import com.google.bigtable.admin.v2.AppProfileName; +import com.google.bigtable.admin.v2.ClusterName; +import com.google.bigtable.admin.v2.Instance.State; +import com.google.bigtable.admin.v2.Instance.Type; +import com.google.bigtable.admin.v2.InstanceName; +import com.google.bigtable.admin.v2.ProjectName; +import com.google.bigtable.admin.v2.StorageType; +import com.google.cloud.bigtable.admin.v2.InstanceAdminClient; +import com.google.cloud.bigtable.admin.v2.models.InstanceAdminRequests; +import com.google.cloud.bigtable.admin.v2.models.InstanceAdminRequests.AppProfile; +import com.google.cloud.bigtable.admin.v2.models.InstanceAdminRequests.Cluster; +import com.google.cloud.bigtable.admin.v2.models.InstanceAdminRequests.CreateInstance; +import com.google.cloud.bigtable.admin.v2.models.InstanceAdminRequests.Instance; +import com.google.cloud.bigtable.admin.v2.models.InstanceAdminRequests.Location; +import com.google.cloud.bigtable.admin.v2.models.InstanceAdminRequests.Policy; +import com.google.common.collect.ImmutableMap; + +public class InstanceAdminClientIT { + private static InstanceAdminClient instanceAdmin; + private static final ProjectName TEST_PROJECT = ProjectName.of("sduskis-hello-shakespear"); + private static final String TEST_PROD_INST_ID = "instadmprodtest"; + private static final String TEST_DEV_INST_ID = "instadmdevtest"; + private static final int PROD_CLUSTER_SIZE = 3; + + @BeforeClass + public static void createClient() throws Exception { + instanceAdmin = InstanceAdminClient.create(); + } + + @AfterClass + public static void closeClient() throws Exception { + instanceAdmin.close(); + } + + @Test + public void createInstanceHarness() throws Exception { + Instance newProdInstance = Instance.ofNewProdInstance(TEST_PROD_INST_ID, "disp"); + + Cluster newCluster = + Cluster.ofNewProdCluster( + TEST_PROD_INST_ID + "us-east1-c", + Location.of(TEST_PROJECT.getProject(), "us-east1-c"), + PROD_CLUSTER_SIZE); + + try { + // Instance tests + int numInstances = instanceAdmin.listInstances(TEST_PROJECT).size(); + Instance createdInstance = createInstance(TEST_PROJECT, newProdInstance, newCluster); + listInstances(numInstances + 1); + getInstance(createdInstance); + updateInstance(createdInstance); + partialUpdateInstance(createdInstance); + + // Cluster tests + getCluster( + newCluster, + ClusterName.of(TEST_PROJECT.getProject(), TEST_PROD_INST_ID, newCluster.getId())); + listClusters(createdInstance.getName(), 1); + + Exception cannotMixClusterType = null; + try { + createCluster( + createdInstance, + Cluster.ofNewDevCluster( + TEST_DEV_INST_ID + "us-east1-d", + Location.of(TEST_PROJECT.getProject(), "us-east1-d"))); + } catch (Exception ex) { + cannotMixClusterType = ex; + } + assertNotNull(cannotMixClusterType); + + Cluster createdCluster2 = + createCluster( + createdInstance, + Cluster.ofNewProdCluster( + TEST_PROD_INST_ID + "us-east1-d", + Location.of(TEST_PROJECT.getProject(), "us-east1-d"), + PROD_CLUSTER_SIZE + 1)); + getCluster(createdCluster2); + updateCluster(createdCluster2); + listClusters(createdInstance.getName(), 2); + + // AppProfile tests + AppProfile createdAny = + createAppProfile( + createdInstance.getName(), AppProfile.ofNewAppProfile("roundRobin").routeToAny()); + getAppProfile(createdAny); + updateAppProfileToSingle(createdAny, createdCluster2.getId()); + listAppProfiles(createdInstance.getName(), 2); // +1 for default appProfile + deleteAppProfile(createdAny.getName()); + + // IamPolicy tests + // TODO: Needs Iam permissions to test this. + // Policy rawPolicy = Policy.of(1, Role.owner(), + // Arrays.asList("user:user@domain.com")); + // Policy actualPolicy = instanceAdmin.setIamPolicy(createdInstance.getName(), rawPolicy); + + Policy iamPolicy = instanceAdmin.getIamPolicy(createdInstance.getName()); + assertNotNull(iamPolicy); + + List actualPermissions = + instanceAdmin.testIamPermissions( + createdInstance.getName(), Arrays.asList("bigtable.tables.checkConsistency")); + assertEquals(Arrays.asList("bigtable.tables.checkConsistency"), actualPermissions); + + deleteCluster(createdCluster2); + listClusters(createdInstance.getName(), 1); + } finally { + instanceAdmin.deleteInstanceRequest( + InstanceName.of(TEST_PROJECT.getProject(), TEST_PROD_INST_ID)); + } + } + + @Test + public void createInstanceMultiCluster() throws Exception { + CreateInstance request = + InstanceAdminRequests.createInstance( + TEST_PROJECT, + Instance.ofNewProdInstance("multitest", "twoclusters"), + Cluster.ofNewProdCluster( + "clusterone", Location.of(TEST_PROJECT.getProject(), "us-east1-c"), 3)) + .addCluster( + Cluster.ofNewProdCluster( + "clustertwo", Location.of(TEST_PROJECT.getProject(), "us-east1-c"), 4)); + + try { + Instance createdInstance = instanceAdmin.createInstanceAsync(request).get(); + getInstance(createdInstance); + listClusters(createdInstance.getName(), 2); + } finally { + instanceAdmin.deleteInstanceRequest(InstanceName.of(TEST_PROJECT.getProject(), "multitest")); + } + } + + @Test + public void createInstanceUpgradeHarness() throws Exception { + Instance newDevInstance = + Instance.ofNewDevInstance(TEST_DEV_INST_ID, TEST_DEV_INST_ID + "disp") + .addLabel("label_name_1", "label_value_1") + .addLabel("label_name_2", "label_value_2"); + + Cluster newDevCluster = + Cluster.ofNewDevCluster( + TEST_DEV_INST_ID + "us-east1-c", Location.of(TEST_PROJECT.getProject(), "us-east1-c")); + try { + Instance createdInstance = + instanceAdmin + .createInstanceAsync( + InstanceAdminRequests.createInstance(TEST_PROJECT, newDevInstance, newDevCluster)) + .get(); + assertInstanceEquals(newDevInstance, createdInstance); + + listClusters(createdInstance.getName(), 1); + getCluster( + newDevCluster, + ClusterName.of(TEST_PROJECT.getProject(), TEST_DEV_INST_ID, newDevCluster.getId())); + + Instance upgradedInstance = + instanceAdmin + .updateInstance(Instance.ofUpdateInstance(createdInstance).upgradeType()) + .get(); + assertEquals(Type.PRODUCTION, upgradedInstance.getType()); + } finally { + instanceAdmin.deleteInstanceRequest( + InstanceName.of(TEST_PROJECT.getProject(), TEST_DEV_INST_ID)); + } + } + + /** helpers to execute and assert * */ + private Instance createInstance(ProjectName projectName, Instance newInstance, Cluster newCluster) + throws Exception { + Instance actualInstance = + instanceAdmin + .createInstanceAsync( + InstanceAdminRequests.createInstance(projectName, newInstance, newCluster)) + .get(); + assertInstanceEquals(newInstance, actualInstance); + return actualInstance; + } + + private void getInstance(Instance expected) { + Instance actual = instanceAdmin.getInstance(expected.getName()); + assertThat(actual).isEqualTo(actual); + } + + private void updateInstance(Instance instance) throws Exception { + Map updatedLabels = ImmutableMap.of("team", "team1", "subteam", "subteam1"); + Instance updatedInstance = + instanceAdmin + .updateInstance( + Instance.ofUpdateInstance(instance) + .updateDisplayName(TEST_DEV_INST_ID + "upddisp") + .updateLabels(updatedLabels)) + .get(); + + assertEquals(TEST_DEV_INST_ID + "upddisp", updatedInstance.getDisplayName()); + assertEquals(updatedLabels, updatedInstance.getLabelsMap()); + } + + private void partialUpdateInstance(Instance instance) throws Exception { + Instance updatedInstance = + instanceAdmin + .updateInstance( + Instance.ofUpdateInstance(instance).updateDisplayName(TEST_PROD_INST_ID + "disp")) + .get(); + + assertEquals(TEST_PROD_INST_ID + "disp", updatedInstance.getDisplayName()); + assertEquals(instance.getLabelsMap(), updatedInstance.getLabelsMap()); + } + + private void getCluster(Cluster raw, ClusterName clusterName) { + Cluster actual = instanceAdmin.getCluster(clusterName); + assertClusterEquals(raw, actual, clusterName); + } + + private Cluster createCluster(Instance instance, Cluster rawCluster) throws Exception { + Cluster actual = instanceAdmin.createCluster(instance.getName(), rawCluster).get(); + assertClusterEquals( + rawCluster, + actual, + ClusterName.of( + instance.getName().getProject(), instance.getName().getInstance(), rawCluster.getId())); + return actual; + } + + private void getCluster(Cluster expected) { + Cluster actual = instanceAdmin.getCluster(expected.getName()); + assertClusterEquals(expected, actual, expected.getName()); + } + + private void updateCluster(Cluster cluster) throws Exception { + Cluster updatedCluster = + instanceAdmin + .updateCluster( + Cluster.ofUpdateCluster(cluster).updateNumNodes(cluster.getServerNodes() + 1)) + .get(); + assertEquals(cluster.getServerNodes() + 1, updatedCluster.getServerNodes()); + } + + private void deleteCluster(Cluster cluster) { + instanceAdmin.deleteCluster(cluster.getName()); + } + + private void listInstances(int expectedSize) { + List instances = instanceAdmin.listInstances(TEST_PROJECT); + assertEquals(expectedSize, instances.size()); + + for (Instance instance : instances) { + getInstance(instance); + } + } + + private void listClusters(InstanceName instanceName, int expectedSize) { + List clusters = instanceAdmin.listClusters(instanceName); + assertEquals(expectedSize, clusters.size()); + + for (Cluster cluster : clusters) { + getCluster(cluster); + } + } + + private AppProfile createAppProfile(InstanceName instanceName, AppProfile rawProfile) { + AppProfile actual = instanceAdmin.createAppProfile(instanceName, rawProfile); + assertAppProfileEquals( + rawProfile, + actual, + AppProfileName.of(TEST_PROJECT.getProject(), TEST_PROD_INST_ID, rawProfile.getId())); + return actual; + } + + private void getAppProfile(AppProfile expected) { + AppProfile actual = instanceAdmin.getAppProfile(expected.getName()); + assertAppProfileEquals(expected, actual, expected.getName()); + } + + private void listAppProfiles(InstanceName instanceName, int expectedSize) { + List appProfiles = instanceAdmin.listAppProfiles(instanceName); + assertEquals(expectedSize, appProfiles.size()); + + for (AppProfile appProfile : appProfiles) { + getAppProfile(appProfile); + } + } + + private void updateAppProfileToSingle(AppProfile original, String singleClusterId) + throws Exception { + AppProfile updated = + instanceAdmin + .updateAppProfile( + AppProfile.ofUpdateAppProfile(original) + .updateDescription("newDescription") + .updateRouteToCluster(singleClusterId, false)) + .get(); + + assertEquals("newDescription", updated.getDescription()); + assertEquals(RoutingPolicyCase.SINGLE_CLUSTER_ROUTING, updated.getRoutingPolicy().name()); + } + + private void deleteAppProfile(AppProfileName appProfileName) { + instanceAdmin.deleteAppProfile(appProfileName, true); + + Exception ensureDelete = null; + try { + instanceAdmin.getAppProfile(appProfileName); + } catch (Exception e) { + ensureDelete = e; + } + assertNotNull(ensureDelete); + } + + private static void assertInstanceEquals(Instance exptected, Instance actual) { + assertEquals(InstanceName.of(TEST_PROJECT.getProject(), exptected.getid()), actual.getName()); + assertEquals(State.READY, actual.getState()); + + assertEquals(actual.getDisplayName(), actual.getDisplayName()); + assertEquals(actual.getType(), actual.getType()); + assertEquals(actual.getLabelsMap(), actual.getLabelsMap()); + } + + private void assertClusterEquals(Cluster exptected, Cluster actual, ClusterName expectedName) { + assertEquals(com.google.bigtable.admin.v2.Cluster.State.READY, actual.getState()); + assertEquals(expectedName, actual.getName()); + assertEquals(exptected.getId(), actual.getId()); + + assertEquals(exptected.getLocation(), actual.getLocation()); + assertEquals(exptected.getServerNodes(), actual.getServerNodes()); + assertEquals(StorageType.SSD, actual.getDefaultStorageType()); + } + + private void assertAppProfileEquals( + AppProfile exptected, AppProfile actual, AppProfileName expectedName) { + assertEquals(expectedName, actual.getName()); + + assertEquals(exptected.getId(), actual.getId()); + assertEquals(exptected.getEtag(), actual.getEtag()); + assertEquals(exptected.getDescription(), actual.getDescription()); + assertEquals(exptected.getRoutingPolicy().toString(), actual.getRoutingPolicy().toString()); + } +}