getInstances() {
- return instances;
- }
+ throw error;
}
+
}
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 09f8c20634ee..130cf46d5dfb 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
@@ -49,6 +49,7 @@
public final class CreateClusterRequest {
private final com.google.bigtable.admin.v2.CreateClusterRequest.Builder proto = com.google.bigtable.admin.v2.CreateClusterRequest
.newBuilder();
+ // Partial ids will be set when the project is passed to toProto
private final String instanceId;
private String zone;
@@ -70,12 +71,14 @@ private CreateClusterRequest(String instanceId, String clusterId) {
* 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 type of storage used by this cluster to serve its parent instance's tables. */
+ @SuppressWarnings("WeakerAccess")
public CreateClusterRequest setServeNodes(int numNodes) {
proto.getClusterBuilder().setServeNodes(numNodes);
return this;
@@ -86,6 +89,7 @@ public CreateClusterRequest setServeNodes(int numNodes) {
* 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;
@@ -113,8 +117,8 @@ String getClusterId() {
}
/**
- * Creates the request protobuf. This method is considered an internal implementation detail and
- * not meant to be used by applications.
+ * 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) {
diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/Instance.java b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/Instance.java
index 3be4e4e02c9a..b6cde0128f5b 100644
--- a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/Instance.java
+++ b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/Instance.java
@@ -15,51 +15,61 @@
*/
package com.google.cloud.bigtable.admin.v2.models;
-import com.google.api.core.ApiFunction;
import com.google.api.core.InternalApi;
+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.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Verify;
import java.util.Map;
import javax.annotation.Nonnull;
/**
* Represents an existing Cloud Bigtable instance.
*
- * A Cloud Bigtable instance is mostly just a container for your clusters and nodes, which do all
- * of the real work.
+ *
A Cloud Bigtable instance is mostly just a container for your clusters and nodes, which do
+ * all of the real work.
*/
-public class Instance {
+public final class Instance {
@Nonnull
private final com.google.bigtable.admin.v2.Instance proto;
/**
- * Wraps the protobuf. This method is considered an internal implementation detail and
- * not meant to be used by applications.
+ * Wraps the protobuf. This method is considered an internal implementation detail and not meant
+ * to be used by applications.
*/
@InternalApi
- public static ApiFunction PROTO_TRANSFORMER = new ApiFunction() {
- @Override
- public Instance apply(com.google.bigtable.admin.v2.Instance proto) {
- return new Instance(proto);
- }
- };
+ public static Instance fromProto(com.google.bigtable.admin.v2.Instance proto) {
+ return new Instance(proto);
+ }
- private Instance(com.google.bigtable.admin.v2.Instance proto) {
+ private Instance(@Nonnull com.google.bigtable.admin.v2.Instance proto) {
+ Preconditions.checkNotNull(proto);
+ Preconditions.checkArgument(!proto.getName().isEmpty(), "Name must be set");
this.proto = proto;
}
/** Gets the instance's id. */
+ @SuppressWarnings("WeakerAccess")
public String getId() {
- return InstanceName.parse(proto.getName()).getInstance();
+ // Constructor ensures that name is not null
+ InstanceName fullName = Verify.verifyNotNull(
+ InstanceName.parse(proto.getName()),
+ "Name can never be null");
+
+ //noinspection ConstantConditions
+ return fullName.getInstance();
}
/** Gets the instance's friendly name. */
+ @SuppressWarnings("WeakerAccess")
public String getDisplayName() {
return proto.getDisplayName();
}
/** Gets the instance's current type. Can be DEVELOPMENT or PRODUCTION. */
+ @SuppressWarnings("WeakerAccess")
public Type getType() {
return proto.getType();
}
@@ -67,12 +77,22 @@ public Type getType() {
/**
* Gets the current labels associated with the instance.
*
- * @see For more details
+ * @see For more
+ * details
*/
+ @SuppressWarnings("WeakerAccess")
public Map getLabels() {
return proto.getLabelsMap();
}
+
+ /** The current state of the instance. */
+ // TODO(igorbernstein2): Try to avoid leaking protobuf enums
+ @SuppressWarnings("WeakerAccess")
+ public State getState() {
+ return proto.getState();
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/PartialListInstancesException.java b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/PartialListInstancesException.java
new file mode 100644
index 000000000000..54bb98b98979
--- /dev/null
+++ b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/PartialListInstancesException.java
@@ -0,0 +1,44 @@
+/*
+ * 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 java.util.List;
+
+/**
+ * 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 class PartialListInstancesException extends RuntimeException {
+ private final List failedZones;
+ private final List instances;
+
+ public PartialListInstancesException(List failedZones, List 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 getFailedZones() {
+ return failedZones;
+ }
+
+ /** A partial list of instances that were found in the available zones. */
+ public List getInstances() {
+ return instances;
+ }
+}
diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateInstanceRequest.java b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateInstanceRequest.java
index 01d1ebbf88d3..3203971d9695 100644
--- a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateInstanceRequest.java
+++ b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateInstanceRequest.java
@@ -30,13 +30,14 @@
/**
* Parameters for updating an existing Bigtable instance.
*
- * Existing instances maybe updated to change their superficial appearance (ie. display name) and
- * can also be upgraded from a DEVELOPMENT instance to a PRODUCTION instance. Please note that
+ *
Existing instances maybe updated to change their superficial appearance (ie. display name)
+ * and can also be upgraded from a DEVELOPMENT instance to a PRODUCTION instance. Please note that
* upgrading to a PRODUCTION instance cannot be undone.
*/
public class UpdateInstanceRequest {
private final String instanceId;
- private final PartialUpdateInstanceRequest.Builder builder = PartialUpdateInstanceRequest.newBuilder();
+ private final PartialUpdateInstanceRequest.Builder builder = PartialUpdateInstanceRequest
+ .newBuilder();
/** Builds a new request to update an existing instance with the specified id. */
public static UpdateInstanceRequest of(@Nonnull String instanceId) {
@@ -49,7 +50,8 @@ private UpdateInstanceRequest(@Nonnull String instanceId) {
}
/** Changes the display name of the instance. */
- public UpdateInstanceRequest withDisplayName(@Nonnull String displayName) {
+ @SuppressWarnings("WeakerAccess")
+ public UpdateInstanceRequest setDisplayName(@Nonnull String displayName) {
Preconditions.checkNotNull(displayName);
builder.getInstanceBuilder().setDisplayName(displayName);
updateFieldMask(Instance.DISPLAY_NAME_FIELD_NUMBER);
@@ -61,7 +63,8 @@ public UpdateInstanceRequest withDisplayName(@Nonnull String displayName) {
* Upgrades the instance from a DEVELOPMENT instance to a PRODUCTION instance. This cannot be
* undone.
*/
- public UpdateInstanceRequest withProductionType() {
+ @SuppressWarnings("WeakerAccess")
+ public UpdateInstanceRequest setProductionType() {
builder.getInstanceBuilder().setType(Type.PRODUCTION);
updateFieldMask(Instance.TYPE_FIELD_NUMBER);
@@ -71,9 +74,11 @@ public UpdateInstanceRequest withProductionType() {
/**
* Replaces the labels associated with the instance.
*
- * @see For more details
+ * @see For more
+ * details
*/
- public UpdateInstanceRequest withLabels(@Nonnull Map labels) {
+ @SuppressWarnings("WeakerAccess")
+ public UpdateInstanceRequest setLabels(@Nonnull Map labels) {
Preconditions.checkNotNull(labels, "labels can't be null");
builder.getInstanceBuilder().clearLabels();
builder.getInstanceBuilder().putAllLabels(labels);
@@ -93,6 +98,10 @@ private void updateFieldMask(int fieldNumber) {
*/
@InternalApi
public PartialUpdateInstanceRequest toProto(ProjectName projectName) {
+ // Empty field mask implies full resource replacement, which would clear all fields in an empty
+ // update request.
+ Preconditions.checkState(!builder.getUpdateMask().getPathsList().isEmpty(), "Update request is empty");
+
InstanceName instanceName = InstanceName.of(projectName.getProject(), instanceId);
builder.getInstanceBuilder().setName(instanceName.toString());
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..82c6a78dbacf 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,88 @@
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.Cluster;
+import com.google.bigtable.admin.v2.CreateInstanceMetadata;
+import com.google.bigtable.admin.v2.Instance.Type;
+import com.google.bigtable.admin.v2.InstanceName;
+import com.google.bigtable.admin.v2.ListInstancesResponse;
+import com.google.bigtable.admin.v2.LocationName;
+import com.google.bigtable.admin.v2.PartialUpdateInstanceRequest;
import com.google.bigtable.admin.v2.ProjectName;
+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.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 com.google.protobuf.FieldMask;
+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 BigtableInstanceAdminClient adminClient;
+
@Mock
private BigtableInstanceAdminStub mockStub;
+ @Mock
+ private OperationCallable mockCreateInstanceCallable;
+
+ @Mock
+ private OperationCallable mockUpdateInstanceCallable;
+
+ @Mock
+ private UnaryCallable mockGetInstanceCallable;
+
+ @Mock
+ private UnaryCallable mockListInstancesCallable;
+
+ @Mock
+ private UnaryCallable mockDeleteInstanceCallable;
+
+
@Before
public void setUp() {
adminClient = BigtableInstanceAdminClient
- .create(ProjectName.of("[PROJECT]"), mockStub);
+ .create(PROJECT_NAME, mockStub);
+
+ Mockito.when(mockStub.createInstanceOperationCallable()).thenReturn(mockCreateInstanceCallable);
+ Mockito.when(mockStub.partialUpdateInstanceOperationCallable())
+ .thenReturn(mockUpdateInstanceCallable);
+
+ Mockito.when(mockStub.getInstanceCallable()).thenReturn(mockGetInstanceCallable);
+ Mockito.when(mockStub.listInstancesCallable()).thenReturn(mockListInstancesCallable);
+ Mockito.when(mockStub.deleteInstanceCallable()).thenReturn(mockDeleteInstanceCallable);
}
@Test
public void testProjectName() {
- assertThat(adminClient.getProjectName()).isEqualTo(ProjectName.of("[PROJECT]"));
+ assertThat(adminClient.getProjectName()).isEqualTo(PROJECT_NAME);
}
@Test
@@ -48,4 +106,211 @@ public void testClose() {
adminClient.close();
Mockito.verify(mockStub).close();
}
+
+ @Test
+ public void testCreateInstance() {
+ // Setup
+ com.google.bigtable.admin.v2.CreateInstanceRequest expectedRequest =
+ com.google.bigtable.admin.v2.CreateInstanceRequest.newBuilder()
+ .setParent(PROJECT_NAME.toString())
+ .setInstanceId(INSTANCE_NAME.getInstance())
+ .setInstance(
+ com.google.bigtable.admin.v2.Instance.newBuilder()
+ .setType(Type.DEVELOPMENT)
+ .setDisplayName(INSTANCE_NAME.getInstance())
+ )
+ .putClusters("cluster1", Cluster.newBuilder()
+ .setLocation("projects/my-project/locations/us-east1-c")
+ .setServeNodes(1)
+ .setDefaultStorageType(StorageType.SSD)
+ .build()
+ )
+ .build();
+
+ com.google.bigtable.admin.v2.Instance expectedResponse = com.google.bigtable.admin.v2.Instance
+ .newBuilder()
+ .setName(INSTANCE_NAME.toString())
+ .build();
+
+ mockOperationResult(mockCreateInstanceCallable, expectedRequest, expectedResponse);
+
+ // Execute
+ com.google.cloud.bigtable.admin.v2.models.Instance actualResult = adminClient.createInstance(
+ CreateInstanceRequest.of(INSTANCE_NAME.getInstance())
+ .setType(Type.DEVELOPMENT)
+ .addCluster("cluster1", "us-east1-c", 1, StorageType.SSD)
+ );
+
+ // Verify
+ assertThat(actualResult).isEqualTo(Instance.fromProto(expectedResponse));
+ }
+
+ @Test
+ public void testUpdateInstance() {
+ // Setup
+ com.google.bigtable.admin.v2.PartialUpdateInstanceRequest expectedRequest =
+ PartialUpdateInstanceRequest.newBuilder()
+ .setUpdateMask(
+ FieldMask.newBuilder()
+ .addPaths("display_name")
+ )
+ .setInstance(
+ com.google.bigtable.admin.v2.Instance.newBuilder()
+ .setName(INSTANCE_NAME.toString())
+ .setDisplayName("new display name")
+ )
+ .build();
+
+ com.google.bigtable.admin.v2.Instance expectedResponse = com.google.bigtable.admin.v2.Instance
+ .newBuilder()
+ .setName(INSTANCE_NAME.toString())
+ .build();
+
+ mockOperationResult(mockUpdateInstanceCallable, expectedRequest, expectedResponse);
+
+ // Execute
+ com.google.cloud.bigtable.admin.v2.models.Instance actualResult = adminClient.updateInstance(
+ UpdateInstanceRequest.of(INSTANCE_NAME.getInstance())
+ .setDisplayName("new display name")
+ );
+
+ // Verify
+ assertThat(actualResult).isEqualTo(Instance.fromProto(expectedResponse));
+ }
+
+ @Test
+ public void testGetInstance() {
+ // Setup
+ com.google.bigtable.admin.v2.GetInstanceRequest expectedRequest =
+ com.google.bigtable.admin.v2.GetInstanceRequest.newBuilder()
+ .setName(INSTANCE_NAME.toString())
+ .build();
+
+ com.google.bigtable.admin.v2.Instance expectedResponse = com.google.bigtable.admin.v2.Instance
+ .newBuilder()
+ .setName(INSTANCE_NAME.toString())
+ .build();
+
+ Mockito.when(mockGetInstanceCallable.futureCall(expectedRequest))
+ .thenReturn(ApiFutures.immediateFuture(expectedResponse));
+
+ // Execute
+ Instance actualResult = adminClient.getInstance(INSTANCE_NAME.getInstance());
+
+ // Verify
+ assertThat(actualResult).isEqualTo(Instance.fromProto(expectedResponse));
+ }
+
+ @Test
+ public void testListInstances() {
+ // Setup
+ com.google.bigtable.admin.v2.ListInstancesRequest expectedRequest =
+ com.google.bigtable.admin.v2.ListInstancesRequest.newBuilder()
+ .setParent(PROJECT_NAME.toString())
+ .build();
+
+ com.google.bigtable.admin.v2.ListInstancesResponse expectedResponse = ListInstancesResponse
+ .newBuilder()
+ .addInstances(
+ com.google.bigtable.admin.v2.Instance.newBuilder().setName(INSTANCE_NAME + "1").build()
+ )
+ .addInstances(
+ com.google.bigtable.admin.v2.Instance.newBuilder().setName(INSTANCE_NAME + "2").build()
+ )
+ .build();
+
+ Mockito.when(mockListInstancesCallable.futureCall(expectedRequest))
+ .thenReturn(ApiFutures.immediateFuture(expectedResponse));
+
+ // Execute
+ List actualResult = adminClient.listInstances();
+
+ // Verify
+ assertThat(actualResult).containsExactly(
+ Instance.fromProto(expectedResponse.getInstances(0)),
+ Instance.fromProto(expectedResponse.getInstances(1))
+ );
+ }
+
+ @Test
+ public void testListInstancesFailedZone() {
+ // Setup
+ com.google.bigtable.admin.v2.ListInstancesRequest expectedRequest =
+ com.google.bigtable.admin.v2.ListInstancesRequest.newBuilder()
+ .setParent(PROJECT_NAME.toString())
+ .build();
+
+ com.google.bigtable.admin.v2.ListInstancesResponse expectedResponse = ListInstancesResponse
+ .newBuilder()
+ .addInstances(
+ com.google.bigtable.admin.v2.Instance.newBuilder().setName(INSTANCE_NAME + "1").build()
+ )
+ .addFailedLocations(
+ LocationName.of(PROJECT_NAME.getProject(), "us-east1-d").toString()
+ )
+ .build();
+
+ Mockito.when(mockListInstancesCallable.futureCall(expectedRequest))
+ .thenReturn(ApiFutures.immediateFuture(expectedResponse));
+
+ // Execute
+ Exception actualError = null;
+
+ try {
+ adminClient.listInstances();
+ } catch (Exception e) {
+ actualError = e;
+ }
+
+ // Verify
+ assertThat(actualError).isInstanceOf(PartialListInstancesException.class);
+ assert actualError != null;
+ PartialListInstancesException partialListError = (PartialListInstancesException) actualError;
+
+ assertThat(partialListError.getInstances())
+ .containsExactly(Instance.fromProto(expectedResponse.getInstances(0)));
+ assertThat(partialListError.getFailedZones()).containsExactly("us-east1-d");
+ }
+
+ @Test
+ public void testDeleteInstance() {
+ // Setup
+ com.google.bigtable.admin.v2.DeleteInstanceRequest expectedRequest =
+ com.google.bigtable.admin.v2.DeleteInstanceRequest.newBuilder()
+ .setName(INSTANCE_NAME.toString())
+ .build();
+
+ final AtomicBoolean wasCalled = new AtomicBoolean(false);
+
+ Mockito.when(mockDeleteInstanceCallable.futureCall(expectedRequest))
+ .thenAnswer(new Answer>() {
+ @Override
+ public ApiFuture answer(InvocationOnMock invocationOnMock) {
+ wasCalled.set(true);
+ return ApiFutures.immediateFuture(Empty.getDefaultInstance());
+ }
+ });
+
+ // Execute
+ adminClient.deleteInstance(INSTANCE_NAME.getInstance());
+
+ // 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/CreateClusterRequestTest.java b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequestTest.java
new file mode 100644
index 000000000000..cf4c4319a0b1
--- /dev/null
+++ b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequestTest.java
@@ -0,0 +1,163 @@
+/*
+ * 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;
+import com.google.bigtable.admin.v2.Instance;
+import com.google.bigtable.admin.v2.Instance.Type;
+import com.google.bigtable.admin.v2.ProjectName;
+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 CreateClusterRequestTest {
+ @Test
+ public void testProductionSingle() {
+ CreateInstanceRequest input = CreateInstanceRequest.of("my-instance")
+ .setType(Type.PRODUCTION)
+ .addCluster("cluster1", "us-east1-c", 3, StorageType.SSD);
+
+ com.google.bigtable.admin.v2.CreateInstanceRequest actual = input
+ .toProto(ProjectName.of("my-project"));
+
+ com.google.bigtable.admin.v2.CreateInstanceRequest expected =
+ com.google.bigtable.admin.v2.CreateInstanceRequest.newBuilder()
+ .setParent(ProjectName.of("my-project").toString())
+ .setInstanceId("my-instance")
+ .setInstance(
+ Instance.newBuilder()
+ .setDisplayName("my-instance")
+ .setType(Type.PRODUCTION)
+ )
+ .putClusters("cluster1",
+ Cluster.newBuilder()
+ .setLocation("projects/my-project/locations/us-east1-c")
+ .setServeNodes(3)
+ .setDefaultStorageType(StorageType.SSD)
+ .build()
+ )
+ .build();
+
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ @Test
+ public void testProductionReplication() {
+ CreateInstanceRequest input = CreateInstanceRequest.of("my-instance")
+ .setType(Type.PRODUCTION)
+ .addCluster("cluster1", "us-east1-c", 3, StorageType.SSD)
+ .addCluster("cluster2", "us-east1-a", 3, StorageType.SSD);
+
+ com.google.bigtable.admin.v2.CreateInstanceRequest actual = input
+ .toProto(ProjectName.of("my-project"));
+
+ com.google.bigtable.admin.v2.CreateInstanceRequest expected =
+ com.google.bigtable.admin.v2.CreateInstanceRequest.newBuilder()
+ .setParent(ProjectName.of("my-project").toString())
+ .setInstanceId("my-instance")
+ .setInstance(
+ Instance.newBuilder()
+ .setDisplayName("my-instance")
+ .setType(Type.PRODUCTION)
+ )
+ .putClusters("cluster1",
+ Cluster.newBuilder()
+ .setLocation("projects/my-project/locations/us-east1-c")
+ .setServeNodes(3)
+ .setDefaultStorageType(StorageType.SSD)
+ .build()
+ )
+ .putClusters("cluster2",
+ Cluster.newBuilder()
+ .setLocation("projects/my-project/locations/us-east1-a")
+ .setServeNodes(3)
+ .setDefaultStorageType(StorageType.SSD)
+ .build()
+ )
+ .build();
+
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ @Test
+ public void testDevelopment() {
+ CreateInstanceRequest input = CreateInstanceRequest.of("my-instance")
+ .setType(Type.DEVELOPMENT)
+ .addCluster("cluster1", "us-east1-c", 1, StorageType.SSD);
+
+ com.google.bigtable.admin.v2.CreateInstanceRequest actual = input
+ .toProto(ProjectName.of("my-project"));
+
+ com.google.bigtable.admin.v2.CreateInstanceRequest expected =
+ com.google.bigtable.admin.v2.CreateInstanceRequest.newBuilder()
+ .setParent(ProjectName.of("my-project").toString())
+ .setInstanceId("my-instance")
+ .setInstance(
+ Instance.newBuilder()
+ .setDisplayName("my-instance")
+ .setType(Type.DEVELOPMENT)
+ )
+ .putClusters("cluster1",
+ Cluster.newBuilder()
+ .setLocation("projects/my-project/locations/us-east1-c")
+ .setServeNodes(1)
+ .setDefaultStorageType(StorageType.SSD)
+ .build()
+ )
+ .build();
+
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ @Test
+ public void testOptionalFields() {
+ CreateInstanceRequest input = CreateInstanceRequest.of("my-instance")
+ .setDisplayName("custom display name")
+ .addLabel("my label", "with some value")
+ .addLabel("my other label", "with some value")
+ .setType(Type.DEVELOPMENT)
+ .addCluster("cluster1", "us-east1-c", 1, StorageType.SSD);
+
+ com.google.bigtable.admin.v2.CreateInstanceRequest actual = input
+ .toProto(ProjectName.of("my-project"));
+
+ com.google.bigtable.admin.v2.CreateInstanceRequest expected =
+ com.google.bigtable.admin.v2.CreateInstanceRequest.newBuilder()
+ .setParent(ProjectName.of("my-project").toString())
+ .setInstanceId("my-instance")
+ .setInstance(
+ Instance.newBuilder()
+ .setDisplayName("custom display name")
+ .putLabels("my label", "with some value")
+ .putLabels("my other label", "with some value")
+ .setType(Type.DEVELOPMENT)
+ )
+ .putClusters("cluster1",
+ Cluster.newBuilder()
+ .setLocation("projects/my-project/locations/us-east1-c")
+ .setServeNodes(1)
+ .setDefaultStorageType(StorageType.SSD)
+ .build()
+ )
+ .build();
+
+ assertThat(actual).isEqualTo(expected);
+ }
+}
\ No newline at end of file
diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/InstanceTest.java b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/InstanceTest.java
new file mode 100644
index 000000000000..1904028744ab
--- /dev/null
+++ b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/InstanceTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.Instance.State;
+import com.google.bigtable.admin.v2.Instance.Type;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class InstanceTest {
+ @Test
+ public void testFromProto() {
+ com.google.bigtable.admin.v2.Instance proto = com.google.bigtable.admin.v2.Instance.newBuilder()
+ .setName("projects/my-project/instances/my-instance")
+ .setDisplayName("my display name")
+ .setType(Type.PRODUCTION)
+ .setState(State.READY)
+ .putLabels("label1", "value1")
+ .putLabels("label2", "value2")
+ .build();
+
+ Instance result = Instance.PROTO_TRANSFORMER.apply(proto);
+
+ assertThat(result.getId()).isEqualTo("my-instance");
+ assertThat(result.getDisplayName()).isEqualTo("my display name");
+ assertThat(result.getType()).isEqualTo(Type.PRODUCTION);
+ assertThat(result.getState()).isEqualTo(State.READY);
+ assertThat(result.getLabels()).containsExactly(
+ "label1", "value1",
+ "label2", "value2"
+ );
+ }
+
+ @Test
+ public void testRequiresName() {
+ com.google.bigtable.admin.v2.Instance proto = com.google.bigtable.admin.v2.Instance.newBuilder()
+ .setDisplayName("my display name")
+ .setType(Type.PRODUCTION)
+ .setState(State.READY)
+ .putLabels("label1", "value1")
+ .putLabels("label2", "value2")
+ .build();
+
+ Exception actualException = null;
+
+ try {
+ Instance.PROTO_TRANSFORMER.apply(proto);
+ } catch (Exception e) {
+ actualException = e;
+ }
+
+ assertThat(actualException).isInstanceOf(IllegalArgumentException.class);
+ }
+}
\ No newline at end of file
diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateInstanceRequestTest.java b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateInstanceRequestTest.java
new file mode 100644
index 000000000000..b5d7b3902853
--- /dev/null
+++ b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateInstanceRequestTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.Instance;
+import com.google.bigtable.admin.v2.Instance.Type;
+import com.google.bigtable.admin.v2.PartialUpdateInstanceRequest;
+import com.google.bigtable.admin.v2.ProjectName;
+import com.google.common.collect.ImmutableMap;
+import com.google.protobuf.FieldMask;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class UpdateInstanceRequestTest {
+ @Test
+ public void testEmpty() {
+ UpdateInstanceRequest input = UpdateInstanceRequest.of("my-instance");
+
+ Exception actualError = null;
+
+ try {
+ input.toProto(ProjectName.of("my-project"));
+ } catch (Exception e) {
+ actualError = e;
+ }
+
+ assertThat(actualError).isInstanceOf(IllegalStateException.class);
+ }
+
+ @Test
+ public void testDisplayName() {
+ UpdateInstanceRequest input = UpdateInstanceRequest.of("my-instance")
+ .setDisplayName("my display name");
+
+ PartialUpdateInstanceRequest actual = input.toProto(ProjectName.of("my-project"));
+
+ PartialUpdateInstanceRequest expected = PartialUpdateInstanceRequest.newBuilder()
+ .setUpdateMask(
+ FieldMask.newBuilder()
+ .addPaths("display_name")
+ )
+ .setInstance(
+ Instance.newBuilder()
+ .setName("projects/my-project/instances/my-instance")
+ .setDisplayName("my display name")
+ )
+ .build();
+
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ @Test
+ public void testLabels() {
+ UpdateInstanceRequest input = UpdateInstanceRequest.of("my-instance")
+ .setLabels(ImmutableMap.of(
+ "label1", "value1",
+ "label2", "value2"
+ ));
+
+ PartialUpdateInstanceRequest actual = input.toProto(ProjectName.of("my-project"));
+
+ PartialUpdateInstanceRequest expected = PartialUpdateInstanceRequest.newBuilder()
+ .setUpdateMask(
+ FieldMask.newBuilder()
+ .addPaths("labels")
+ )
+ .setInstance(
+ Instance.newBuilder()
+ .setName("projects/my-project/instances/my-instance")
+ .putLabels("label1", "value1")
+ .putLabels("label2", "value2")
+ )
+ .build();
+
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ @Test
+ public void testType() {
+ UpdateInstanceRequest input = UpdateInstanceRequest.of("my-instance")
+ .setProductionType();
+
+ PartialUpdateInstanceRequest actual = input.toProto(ProjectName.of("my-project"));
+
+ PartialUpdateInstanceRequest expected = PartialUpdateInstanceRequest.newBuilder()
+ .setUpdateMask(
+ FieldMask.newBuilder()
+ .addPaths("type")
+ )
+ .setInstance(
+ Instance.newBuilder()
+ .setName("projects/my-project/instances/my-instance")
+ .setType(Type.PRODUCTION)
+ )
+ .build();
+
+ assertThat(actual).isEqualTo(expected);
+ }
+}