Skip to content

Commit

Permalink
WIP: Bigtable: add CRUD instances
Browse files Browse the repository at this point in the history
  • Loading branch information
igorbernstein2 committed Aug 16, 2018
1 parent 9cbc0ef commit f01e813
Show file tree
Hide file tree
Showing 5 changed files with 658 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,26 @@
*/
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.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.CreateInstanceRequest;
import com.google.cloud.bigtable.admin.v2.models.Instance;
import com.google.cloud.bigtable.admin.v2.models.UpdateInstanceRequest;
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.MoreExecutors;
import com.google.protobuf.Empty;
import java.io.IOException;
import java.util.List;
import javax.annotation.Nonnull;

/**
Expand All @@ -28,13 +45,10 @@
*
* <pre>{@code
* try(BigtableInstanceAdminClient client = BigtableInstanceAdminClient.create(ProjectName.of("[PROJECT]"))) {
* CreateInstanceRequest request = CreateInstanceRequest.of(ProjectName)
* .addFamily("cf1")
* .addFamily("cf2", GCRULES.maxVersions(10))
* .addSplit(ByteString.copyFromUtf8("b"))
* .addSplit(ByteString.copyFromUtf8("q"));
* CreateInstanceRequest request = CreateInstanceRequest.of("my-instance")
* .addCluster("my-cluster", "us-east1-c", 3, StorageType.SSD);
*
* client.createInstance(request);
* Instance instance = client.createInstance(request);
* }
* }</pre>
*
Expand Down Expand Up @@ -105,4 +119,168 @@ public ProjectName getProjectName() {
public void close() {
stub.close();
}

/**
* Creates a new instance and returns its representation.
*
* @see CreateInstanceRequest for details.
*/
public Instance createInstance(CreateInstanceRequest request) {
return awaitFuture(createInstanceAsync(request));
}

/**
* Asynchronously creates a new instance and returns its representation wrapped in a future.
*
* @see CreateInstanceRequest for details.
*/
public ApiFuture<Instance> createInstanceAsync(CreateInstanceRequest request) {
return ApiFutures.transform(
stub.createInstanceOperationCallable().futureCall(request.toProto(projectName)),
Instance.PROTO_TRANSFORMER,
MoreExecutors.directExecutor());
}

/**
* Updates a new instance and returns its representation.
*
* @see UpdateInstanceRequest for details.
*/
public Instance updateInstance(UpdateInstanceRequest request) {
return awaitFuture(updateInstanceAsync(request));
}

/**
* Asynchronously updates a new instance and returns its representation wrapped in a future.
*
* @see UpdateInstanceRequest for details.
*/
public ApiFuture<Instance> updateInstanceAsync(UpdateInstanceRequest request) {
return ApiFutures.transform(
stub.partialUpdateInstanceOperationCallable().futureCall(request.toProto(projectName)),
Instance.PROTO_TRANSFORMER,
MoreExecutors.directExecutor());
}

/** Get the instance representation. */
public Instance getInstance(String id) {
return awaitFuture(getInstanceAsync(id));
}

/** Asynchronously gets the instance representation wrapped in a future. */
public ApiFuture<Instance> getInstanceAsync(String instanceId) {

InstanceName name = InstanceName.of(projectName.getProject(), instanceId);

GetInstanceRequest request = GetInstanceRequest.newBuilder()
.setName(name.toString())
.build();

return ApiFutures.transform(
stub.getInstanceCallable().futureCall(request),
Instance.PROTO_TRANSFORMER,
MoreExecutors.directExecutor());
}

/** Lists all of the instances in the current project. */
public List<Instance> listInstances() {
return awaitFuture(listInstancesAsync());
}

/** Asynchronously lists all of the instances in the current project. */
public ApiFuture<List<Instance>> listInstancesAsync() {
ListInstancesRequest request = ListInstancesRequest.newBuilder()
.setParent(projectName.toString())
.build();

ApiFuture<ListInstancesResponse> responseFuture = stub.listInstancesCallable()
.futureCall(request);

return ApiFutures.transform(responseFuture, new ApiFunction<ListInstancesResponse, List<Instance>>() {
@Override
public List<Instance> apply(ListInstancesResponse proto) {
// NOTE: pagination is intentionally ignored. The server does not implement it.
Verify.verify(proto.getNextPageToken().isEmpty(),
"Server returned an unexpected paginated response");

ImmutableList.Builder<Instance> instances = ImmutableList.builder();

for (com.google.bigtable.admin.v2.Instance protoInstance : proto.getInstancesList()) {
instances.add(Instance.PROTO_TRANSFORMER.apply(protoInstance));
}

ImmutableList.Builder<String> failedZones = ImmutableList.builder();
for (String locationStr : proto.getFailedLocationsList()) {
failedZones.add(LocationName.parse(locationStr).getLocation());
}


if (!failedZones.build().isEmpty()) {
throw new PartialListInstancesException(failedZones.build(), instances.build());
}

return instances.build();
}
}, MoreExecutors.directExecutor());
}

/** Deletes the specified instance. */
public void deleteInstance(String instanceId) {
awaitFuture(deleteInstanceAsync(instanceId));
}

/** Asynchronously deletes the specified instance. */
private ApiFuture<Void> deleteInstanceAsync(String instanceId) {
InstanceName instanceName = InstanceName.of(projectName.getProject(), instanceId);

DeleteInstanceRequest request = DeleteInstanceRequest.newBuilder()
.setName(instanceName.toString())
.build();

return ApiFutures.transform(stub.deleteInstanceCallable().futureCall(request),
new ApiFunction<Empty, Void>() {
@Override
public Void apply(Empty input) {
return null;
}
},
MoreExecutors.directExecutor()
);
}


private <T> T awaitFuture(ApiFuture<T> future) {
try {
return future.get();
} catch(Throwable t) {
// TODO(igorbernstein2): figure out a better wrapper exception.
throw new RuntimeException(t);
}
}

/**
* Exception thrown when some zones are unavailable and listInstances is unable to return a full
* instance list. This exception can be inspected to get a partial list.
*/
public static class PartialListInstancesException extends RuntimeException {
private final List<String> failedZones;
private final List<Instance> instances;

PartialListInstancesException(List<String> failedZones, List<Instance> instances) {
super("Failed to list all instances, some zones where unavailable");

this.failedZones = failedZones;
this.instances = instances;
}

/** A list of zones, whose unavailability caused this error. */
public List<String> getFailedZones() {
return failedZones;
}

/** A partial list of instances that were found in the available zones. */
public List<Instance> getInstances() {
return instances;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* 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.
*
* <p>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.
*
* <p>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 <a href="https://cloud.google.com/bigtable/docs/locations">Cloud Bigtable Locations</a>.
*
*
* Examples:
*
* <pre>{@code
* // Small production instance:
* CreateClusterRequest clusterRequest = CreateClusterRequest.of("my-existing-instance", "my-cluster")
* .setZone("us-east1-c")
* .setServeNodes(3)
* .setStorageType(StorageType.SSD);
* }</pre>
*
* @see <a href="https://cloud.google.com/bigtable/docs/instances-clusters-nodes#clusters">For more details</a>
*/
public final class CreateClusterRequest {
private final com.google.bigtable.admin.v2.CreateClusterRequest.Builder proto = com.google.bigtable.admin.v2.CreateClusterRequest
.newBuilder();
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.
*/
public CreateClusterRequest setZone(String zone) {
this.zone = zone;
return this;
}

/** Sets the type of storage used by this cluster to serve its parent instance's tables. */
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
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. 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();
}
}
Loading

0 comments on commit f01e813

Please sign in to comment.