diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/AppProfile.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/AppProfile.java index 848b84ba6..52be2b0d5 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/AppProfile.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/AppProfile.java @@ -22,6 +22,8 @@ import com.google.common.base.Preconditions; import com.google.common.base.Verify; import com.google.common.base.VerifyException; +import com.google.common.collect.ImmutableSet; +import java.util.Set; import javax.annotation.Nonnull; /** @@ -64,7 +66,8 @@ private AppProfile(@Nonnull com.google.bigtable.admin.v2.AppProfile proto) { @SuppressWarnings("WeakerAccess") public RoutingPolicy getPolicy() { if (proto.hasMultiClusterRoutingUseAny()) { - return MultiClusterRoutingPolicy.of(); + return MultiClusterRoutingPolicy.of( + ImmutableSet.copyOf(proto.getMultiClusterRoutingUseAny().getClusterIdsList())); } else if (proto.hasSingleClusterRouting()) { return new SingleClusterRoutingPolicy(proto.getSingleClusterRouting()); } else { @@ -226,15 +229,42 @@ public int hashCode() { * available cluster. */ public static class MultiClusterRoutingPolicy implements RoutingPolicy { - private static final MultiClusterRoutingUseAny proto = - MultiClusterRoutingUseAny.getDefaultInstance(); + private final MultiClusterRoutingUseAny proto; /** Creates a new instance of {@link MultiClusterRoutingPolicy}. */ public static MultiClusterRoutingPolicy of() { - return new MultiClusterRoutingPolicy(); + return new MultiClusterRoutingPolicy(MultiClusterRoutingUseAny.getDefaultInstance()); } - private MultiClusterRoutingPolicy() {} + /** + * Creates a new instance of {@link MultiClusterRoutingPolicy} with specified cluster ids to + * route to. + */ + public static MultiClusterRoutingPolicy of(String... clusterIds) { + return of(ImmutableSet.copyOf(clusterIds)); + } + + /** + * Creates a new instance of {@link MultiClusterRoutingPolicy} with specified cluster ids to + * route to. + */ + public static MultiClusterRoutingPolicy of(Set clusterIds) { + return new MultiClusterRoutingPolicy( + MultiClusterRoutingUseAny.newBuilder().addAllClusterIds(clusterIds).build()); + } + + /* + * Returns the set of clusters to route to. The order is ignored; clusters will be + * tried in order of distance. If empty, all clusters are eligible. + */ + public Set getClusterIds() { + return ImmutableSet.copyOf(proto.getClusterIdsList()); + } + + private MultiClusterRoutingPolicy( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny proto) { + this.proto = proto; + } /** * Creates the request protobuf. This method is considered an internal implementation detail and @@ -253,8 +283,8 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } - - return true; + MultiClusterRoutingPolicy that = (MultiClusterRoutingPolicy) o; + return Objects.equal(proto, that.proto); } @Override diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTest.java index 528a51cf1..9ea6146c1 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTest.java @@ -50,6 +50,7 @@ import com.google.cloud.bigtable.admin.v2.models.UpdateAppProfileRequest; import com.google.cloud.bigtable.admin.v2.models.UpdateInstanceRequest; import com.google.cloud.bigtable.admin.v2.stub.BigtableInstanceAdminStub; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.io.BaseEncoding; import com.google.protobuf.ByteString; @@ -617,6 +618,131 @@ public void testCreateAppProfile() { assertThat(actualResult).isEqualTo(AppProfile.fromProto(expectedResponse)); } + @Test + public void testCreateAppProfileAddSingleClusterId() { + // Setup + Mockito.when(mockStub.createAppProfileCallable()).thenReturn(mockCreateAppProfileCallable); + + com.google.bigtable.admin.v2.CreateAppProfileRequest expectedRequest = + com.google.bigtable.admin.v2.CreateAppProfileRequest.newBuilder() + .setParent(NameUtil.formatInstanceName(PROJECT_ID, INSTANCE_ID)) + .setAppProfileId(APP_PROFILE_ID) + .setAppProfile( + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setDescription("my description") + .setMultiClusterRoutingUseAny( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny + .newBuilder() + .addClusterIds("cluster-id-1"))) + .build(); + + com.google.bigtable.admin.v2.AppProfile expectedResponse = + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setName(APP_PROFILE_NAME) + .setDescription("my description") + .setMultiClusterRoutingUseAny( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny.newBuilder() + .addClusterIds("cluster-id-1")) + .build(); + + Mockito.when(mockCreateAppProfileCallable.futureCall(expectedRequest)) + .thenReturn(ApiFutures.immediateFuture(expectedResponse)); + + // Execute + AppProfile actualResult = + adminClient.createAppProfile( + CreateAppProfileRequest.of(INSTANCE_ID, APP_PROFILE_ID) + .setDescription("my description") + .setRoutingPolicy(MultiClusterRoutingPolicy.of("cluster-id-1"))); + + // Verify + assertThat(actualResult).isEqualTo(AppProfile.fromProto(expectedResponse)); + } + + @Test + public void testCreateAppProfileAddMultipleClusterIds() { + // Setup + Mockito.when(mockStub.createAppProfileCallable()).thenReturn(mockCreateAppProfileCallable); + + com.google.bigtable.admin.v2.CreateAppProfileRequest expectedRequest = + com.google.bigtable.admin.v2.CreateAppProfileRequest.newBuilder() + .setParent(NameUtil.formatInstanceName(PROJECT_ID, INSTANCE_ID)) + .setAppProfileId(APP_PROFILE_ID) + .setAppProfile( + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setDescription("my description") + .setMultiClusterRoutingUseAny( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny + .newBuilder() + .addClusterIds("cluster-id-1") + .addClusterIds("cluster-id-2"))) + .build(); + + com.google.bigtable.admin.v2.AppProfile expectedResponse = + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setName(APP_PROFILE_NAME) + .setDescription("my description") + .setMultiClusterRoutingUseAny( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny.newBuilder() + .addClusterIds("cluster-id-1") + .addClusterIds("cluster-id-2")) + .build(); + + Mockito.when(mockCreateAppProfileCallable.futureCall(expectedRequest)) + .thenReturn(ApiFutures.immediateFuture(expectedResponse)); + + // Execute + AppProfile actualResult = + adminClient.createAppProfile( + CreateAppProfileRequest.of(INSTANCE_ID, APP_PROFILE_ID) + .setDescription("my description") + .setRoutingPolicy(MultiClusterRoutingPolicy.of("cluster-id-1", "cluster-id-2"))); + + // Verify + assertThat(actualResult).isEqualTo(AppProfile.fromProto(expectedResponse)); + } + + @Test + public void testCreateAppProfileAddMultipleClusterIdsWithList() { + // Setup + Mockito.when(mockStub.createAppProfileCallable()).thenReturn(mockCreateAppProfileCallable); + + com.google.bigtable.admin.v2.CreateAppProfileRequest expectedRequest = + com.google.bigtable.admin.v2.CreateAppProfileRequest.newBuilder() + .setParent(NameUtil.formatInstanceName(PROJECT_ID, INSTANCE_ID)) + .setAppProfileId(APP_PROFILE_ID) + .setAppProfile( + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setDescription("my description") + .setMultiClusterRoutingUseAny( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny + .newBuilder() + .addAllClusterIds(ImmutableList.of("cluster-id-1", "cluster-id-2")))) + .build(); + + com.google.bigtable.admin.v2.AppProfile expectedResponse = + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setName(APP_PROFILE_NAME) + .setDescription("my description") + .setMultiClusterRoutingUseAny( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny.newBuilder() + .addAllClusterIds(ImmutableList.of("cluster-id-1", "cluster-id-2"))) + .build(); + + Mockito.when(mockCreateAppProfileCallable.futureCall(expectedRequest)) + .thenReturn(ApiFutures.immediateFuture(expectedResponse)); + + // Execute + AppProfile actualResult = + adminClient.createAppProfile( + CreateAppProfileRequest.of(INSTANCE_ID, APP_PROFILE_ID) + .setDescription("my description") + .setRoutingPolicy(MultiClusterRoutingPolicy.of("cluster-id-1", "cluster-id-2"))); + + // Verify + assertThat(actualResult).isEqualTo(AppProfile.fromProto(expectedResponse)); + } + @Test public void testGetAppProfile() { // Setup diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/it/BigtableInstanceAdminClientIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/it/BigtableInstanceAdminClientIT.java index e341b439c..0746a7fd2 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/it/BigtableInstanceAdminClientIT.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/it/BigtableInstanceAdminClientIT.java @@ -96,6 +96,99 @@ public void appProfileTest() { assertThat(actualEx).isNull(); } + @Test + public void appProfileTestMultiClusterWithIds() { + String newInstanceId = prefixGenerator.newPrefix(); + String newClusterId = newInstanceId + "-c1"; + String newClusterId2 = newInstanceId + "-c2"; + + client.createInstance( + CreateInstanceRequest.of(newInstanceId) + .addCluster(newClusterId, testEnvRule.env().getPrimaryZone(), 1, StorageType.SSD) + .addCluster(newClusterId2, testEnvRule.env().getSecondaryZone(), 1, StorageType.SSD) + .setDisplayName("Multi-Cluster-Instance-Test") + .addLabel("state", "readytodelete") + .setType(Type.PRODUCTION)); + + try { + assertThat(client.exists(newInstanceId)).isTrue(); + + String testAppProfile = "test-app-profile"; + + CreateAppProfileRequest request = + CreateAppProfileRequest.of(newInstanceId, testAppProfile) + .setRoutingPolicy(AppProfile.MultiClusterRoutingPolicy.of(newClusterId)) + .setDescription("This is to test app profile"); + + AppProfile newlyCreatedAppProfile = client.createAppProfile(request); + + AppProfile updated = + client.updateAppProfile( + UpdateAppProfileRequest.of(newlyCreatedAppProfile).setDescription("new description")); + + AppProfile freshAppProfile = client.getAppProfile(newInstanceId, testAppProfile); + assertThat(freshAppProfile.getDescription()).isEqualTo(updated.getDescription()); + + AppProfile.MultiClusterRoutingPolicy freshAppProfilePolicy = + (AppProfile.MultiClusterRoutingPolicy) freshAppProfile.getPolicy(); + AppProfile.MultiClusterRoutingPolicy updatedAppProfilePolicy = + (AppProfile.MultiClusterRoutingPolicy) updated.getPolicy(); + + assertThat(freshAppProfilePolicy.getClusterIds()).containsExactly(newClusterId); + assertThat(freshAppProfilePolicy.getClusterIds()) + .isEqualTo(updatedAppProfilePolicy.getClusterIds()); + assertThat(freshAppProfilePolicy).isEqualTo(updatedAppProfilePolicy); + + assertThat(client.listAppProfiles(newInstanceId)).contains(freshAppProfile); + + // update again with routing policy + AppProfile updated2 = + client.updateAppProfile( + UpdateAppProfileRequest.of(updated) + .setRoutingPolicy(AppProfile.MultiClusterRoutingPolicy.of(newClusterId2))); + + AppProfile freshAppProfile2 = client.getAppProfile(newInstanceId, testAppProfile); + assertThat(freshAppProfile2.getDescription()).isEqualTo(updated2.getDescription()); + + AppProfile.MultiClusterRoutingPolicy freshAppProfilePolicy2 = + (AppProfile.MultiClusterRoutingPolicy) freshAppProfile2.getPolicy(); + AppProfile.MultiClusterRoutingPolicy updatedAppProfilePolicy2 = + (AppProfile.MultiClusterRoutingPolicy) updated2.getPolicy(); + + assertThat(freshAppProfilePolicy2.getClusterIds()).containsExactly(newClusterId2); + assertThat(freshAppProfilePolicy2.getClusterIds()) + .isEqualTo(updatedAppProfilePolicy2.getClusterIds()); + assertThat(freshAppProfilePolicy2).isEqualTo(updatedAppProfilePolicy2); + + assertThat(client.listAppProfiles(newInstanceId)).contains(freshAppProfile2); + + // update to single routing policy + AppProfile updated3 = + client.updateAppProfile( + UpdateAppProfileRequest.of(updated) + .setRoutingPolicy(AppProfile.SingleClusterRoutingPolicy.of(newClusterId))); + + AppProfile freshAppProfile3 = client.getAppProfile(newInstanceId, testAppProfile); + assertThat(freshAppProfile3.getDescription()).isEqualTo(updated3.getDescription()); + + AppProfile.SingleClusterRoutingPolicy freshAppProfilePolicy3 = + (AppProfile.SingleClusterRoutingPolicy) freshAppProfile3.getPolicy(); + AppProfile.SingleClusterRoutingPolicy updatedAppProfilePolicy3 = + (AppProfile.SingleClusterRoutingPolicy) updated3.getPolicy(); + + assertThat(freshAppProfilePolicy3.getClusterId()).isEqualTo(newClusterId); + assertThat(freshAppProfilePolicy3.getClusterId()) + .isEqualTo(updatedAppProfilePolicy3.getClusterId()); + assertThat(freshAppProfilePolicy3).isEqualTo(updatedAppProfilePolicy3); + + assertThat(client.listAppProfiles(newInstanceId)).contains(freshAppProfile3); + } finally { + if (client.exists(newInstanceId)) { + client.deleteInstance(newInstanceId); + } + } + } + @Test public void iamUpdateTest() { Policy policy = client.getIamPolicy(instanceId); diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/AppProfileTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/AppProfileTest.java index 5cc7c6cf3..f8d8f3fb6 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/AppProfileTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/AppProfileTest.java @@ -20,6 +20,7 @@ import com.google.bigtable.admin.v2.AppProfile.SingleClusterRouting; import com.google.bigtable.admin.v2.AppProfileName; import com.google.cloud.bigtable.admin.v2.models.AppProfile.SingleClusterRoutingPolicy; +import com.google.common.collect.ImmutableList; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -48,6 +49,46 @@ public void testFromProto() { assertThat(profile.getPolicy()).isEqualTo(SingleClusterRoutingPolicy.of("my-cluster", true)); } + @Test + public void testFromProtoWithMultiCluster() { + AppProfile profile = + AppProfile.fromProto( + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setName(AppProfileName.of("my-project", "my-instance", "my-profile").toString()) + .setDescription("my description") + .setMultiClusterRoutingUseAny( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny.newBuilder() + .build()) + .setEtag("my-etag") + .build()); + + assertThat(profile.getInstanceId()).isEqualTo("my-instance"); + assertThat(profile.getId()).isEqualTo("my-profile"); + assertThat(profile.getDescription()).isEqualTo("my description"); + assertThat(profile.getPolicy()).isEqualTo(AppProfile.MultiClusterRoutingPolicy.of()); + } + + @Test + public void testFromProtoWithMultiClusterWithIds() { + AppProfile profile = + AppProfile.fromProto( + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setName(AppProfileName.of("my-project", "my-instance", "my-profile").toString()) + .setDescription("my description") + .setMultiClusterRoutingUseAny( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny.newBuilder() + .addAllClusterIds(ImmutableList.of("cluster-id-1", "cluster-id-2")) + .build()) + .setEtag("my-etag") + .build()); + + assertThat(profile.getInstanceId()).isEqualTo("my-instance"); + assertThat(profile.getId()).isEqualTo("my-profile"); + assertThat(profile.getDescription()).isEqualTo("my description"); + assertThat(profile.getPolicy()) + .isEqualTo(AppProfile.MultiClusterRoutingPolicy.of("cluster-id-1", "cluster-id-2")); + } + @Test public void testNoNameError() { Exception actualException = null;