infos = ImmutableList.builder();
+
+ for (com.google.bigtable.admin.v2.EncryptionInfo infoProto :
+ entry.getValue().getEncryptionInfoList()) {
+ infos.add(EncryptionInfo.fromProto(infoProto));
+ }
+
+ result.put(entry.getKey(), infos.build());
+ }
+
+ return result.build();
+ }
+ },
+ MoreExecutors.directExecutor());
+ }
+
/**
* Lists all table IDs in the instance.
*
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/Backup.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/Backup.java
index 54002da634..ce0ed7efc9 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/Backup.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/Backup.java
@@ -142,6 +142,18 @@ public State getState() {
return State.fromProto(proto.getState());
}
+ /**
+ * Get the encryption information for the backup.
+ *
+ * If encryption_type is CUSTOMER_MANAGED_ENCRYPTION, kms_key_version will be filled in with
+ * status UNKNOWN.
+ *
+ *
If encryption_type is GOOGLE_DEFAULT_ENCRYPTION, all other fields will have default value.
+ */
+ public EncryptionInfo getEncryptionInfo() {
+ return EncryptionInfo.fromProto(proto.getEncryptionInfo());
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/Cluster.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/Cluster.java
index 1d9d695cea..cf7218b062 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/Cluster.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/Cluster.java
@@ -30,6 +30,7 @@
* in the instance.
*/
public class Cluster {
+
public enum State {
/** The state of the cluster could not be determined. */
NOT_KNOWN(com.google.bigtable.admin.v2.Cluster.State.STATE_NOT_KNOWN),
@@ -156,6 +157,18 @@ public StorageType getStorageType() {
return StorageType.fromProto(stateProto.getDefaultStorageType());
}
+ /**
+ * Google Cloud Key Management Service (KMS) settings for a CMEK-protected Bigtable cluster. This
+ * returns the full resource name of the Cloud KMS key in the format
+ * `projects/{key_project_id}/locations/{location}/keyRings/{ring_name}/cryptoKeys/{key_name}`
+ */
+ public String getKmsKeyName() {
+ if (stateProto.hasEncryptionConfig()) {
+ return stateProto.getEncryptionConfig().getKmsKeyName();
+ }
+ return null;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequest.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequest.java
index 4711097d53..cf06f63b09 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequest.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequest.java
@@ -49,6 +49,7 @@
* details
*/
public final class CreateClusterRequest {
+
private final com.google.bigtable.admin.v2.CreateClusterRequest.Builder proto =
com.google.bigtable.admin.v2.CreateClusterRequest.newBuilder();
// instanceId and zone are short ids, which will be expanded to full names when the project name
@@ -104,6 +105,17 @@ public CreateClusterRequest setStorageType(@Nonnull StorageType storageType) {
return this;
}
+ /**
+ * Sets the Google Cloud Key Management Service (KMS) key for a CMEK-protected Bigtable. This
+ * requires the full resource name of the Cloud KMS key, in the format
+ * `projects/{key_project_id}/locations/{location}/keyRings/{ring_name}/cryptoKeys/{key_name}`
+ */
+ public CreateClusterRequest setKmsKeyName(@Nonnull String kmsKeyName) {
+ Preconditions.checkNotNull(kmsKeyName);
+ proto.getClusterBuilder().getEncryptionConfigBuilder().setKmsKeyName(kmsKeyName);
+ return this;
+ }
+
/**
* Creates the request protobuf. This method is considered an internal implementation detail and
* not meant to be used by applications.
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateInstanceRequest.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateInstanceRequest.java
index 467344c821..b318c95cab 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateInstanceRequest.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateInstanceRequest.java
@@ -147,6 +147,35 @@ public CreateInstanceRequest addCluster(
return this;
}
+ /**
+ * Adds a CMEK protected cluster using the specified KMS key name.
+ *
+ * @param clusterId the name of the cluster.
+ * @param zone the zone where the cluster will be created.
+ * @param serveNodes the number of nodes that cluster will contain. DEVELOPMENT instance clusters
+ * must have exactly one node.
+ * @param storageType the type of storage used by this cluster to serve its parent instance's
+ * tables.
+ * @param kmsKeyName the full name of the KMS key name to use in the format
+ * `projects/{key_project_id}/locations/{location}/keyRings/{ring_name}/cryptoKeys/{key_name}`
+ */
+ public CreateInstanceRequest addCmekCluster(
+ @Nonnull String clusterId,
+ @Nonnull String zone,
+ int serveNodes,
+ @Nonnull StorageType storageType,
+ @Nonnull String kmsKeyName) {
+ CreateClusterRequest clusterRequest =
+ CreateClusterRequest.of("ignored-instance-id", clusterId)
+ .setZone(zone)
+ .setServeNodes(serveNodes)
+ .setStorageType(storageType)
+ .setKmsKeyName(kmsKeyName);
+ clusterRequests.add(clusterRequest);
+
+ return this;
+ }
+
/**
* Adds a DEVELOPMENT cluster to the instance request.
*
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/EncryptionInfo.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/EncryptionInfo.java
new file mode 100644
index 0000000000..9eb3b13e18
--- /dev/null
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/EncryptionInfo.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2021 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.bigtable.admin.v2.EncryptionInfo.EncryptionType;
+import com.google.cloud.bigtable.common.Status;
+import com.google.common.base.Objects;
+
+/**
+ * Encryption information for a given resource.
+ *
+ *
If this resource is protected with customer managed encryption, the in-use Google Cloud Key
+ * Management Service (KMS) key versions will be specified along with their status.
+ */
+public final class EncryptionInfo {
+ public enum Type {
+ /** Encryption type was not specified, though data at rest remains encrypted. */
+ ENCRYPTION_TYPE_UNSPECIFIED(
+ com.google.bigtable.admin.v2.EncryptionInfo.EncryptionType.ENCRYPTION_TYPE_UNSPECIFIED),
+ /**
+ * The data backing this resource is encrypted at rest with a key that is fully managed by
+ * Google. No key version or status will be populated.
+ */
+ GOOGLE_DEFAULT_ENCRYPTION(
+ com.google.bigtable.admin.v2.EncryptionInfo.EncryptionType.GOOGLE_DEFAULT_ENCRYPTION),
+ /**
+ * The data backing this resource is encrypted at rest with a key that is fully managed by
+ * Google. No key version or status will be populated. This is the default state.
+ */
+ CUSTOMER_MANAGED_ENCRYPTION(
+ com.google.bigtable.admin.v2.EncryptionInfo.EncryptionType.CUSTOMER_MANAGED_ENCRYPTION),
+ /** Type not known by the client, please upgrade your client */
+ UNRECOGNIZED(com.google.bigtable.admin.v2.EncryptionInfo.EncryptionType.UNRECOGNIZED);
+
+ private final com.google.bigtable.admin.v2.EncryptionInfo.EncryptionType proto;
+
+ Type(EncryptionType proto) {
+ this.proto = proto;
+ }
+
+ /** Wraps the EncryptionInfo protobuf. */
+ public static Type fromProto(com.google.bigtable.admin.v2.EncryptionInfo.EncryptionType proto) {
+ for (Type type : values()) {
+ if (Objects.equal(type.proto, proto)) {
+ return type;
+ }
+ }
+ return UNRECOGNIZED;
+ }
+ }
+
+ private com.google.bigtable.admin.v2.EncryptionInfo proto;
+
+ public static EncryptionInfo fromProto(com.google.bigtable.admin.v2.EncryptionInfo proto) {
+ return new EncryptionInfo(proto);
+ }
+
+ private EncryptionInfo(com.google.bigtable.admin.v2.EncryptionInfo proto) {
+ this.proto = proto;
+ }
+
+ public Type getType() {
+ return EncryptionInfo.Type.fromProto(proto.getEncryptionType());
+ }
+
+ public String getKmsKeyVersion() {
+ return proto.getKmsKeyVersion();
+ }
+
+ public Status getStatus() {
+ return Status.fromProto(proto.getEncryptionStatus());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ EncryptionInfo that = (EncryptionInfo) o;
+ return Objects.equal(proto, that.proto);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(proto);
+ }
+
+ @Override
+ public String toString() {
+ return proto.toString();
+ }
+}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/common/Status.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/common/Status.java
new file mode 100644
index 0000000000..83f7c188d4
--- /dev/null
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/common/Status.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2021 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.common;
+
+import com.google.common.base.Objects;
+
+/**
+ * The `Status` type defines a logical error model. Each `Status` message contains an error code and
+ * a error message.
+ *
+ *
This primarily wraps the protobuf {@link com.google.rpc.Status}.
+ */
+public final class Status {
+ public enum Code {
+ OK(com.google.rpc.Code.OK),
+ CANCELLED(com.google.rpc.Code.CANCELLED),
+ UNKNOWN(com.google.rpc.Code.UNKNOWN),
+ INVALID_ARGUMENT(com.google.rpc.Code.INVALID_ARGUMENT),
+ DEADLINE_EXCEEDED(com.google.rpc.Code.DEADLINE_EXCEEDED),
+ NOT_FOUND(com.google.rpc.Code.NOT_FOUND),
+ ALREADY_EXISTS(com.google.rpc.Code.ALREADY_EXISTS),
+ PERMISSION_DENIED(com.google.rpc.Code.PERMISSION_DENIED),
+ UNAUTHENTICATED(com.google.rpc.Code.UNAUTHENTICATED),
+ RESOURCE_EXHAUSTED(com.google.rpc.Code.RESOURCE_EXHAUSTED),
+ FAILED_PRECONDITION(com.google.rpc.Code.FAILED_PRECONDITION),
+ ABORTED(com.google.rpc.Code.ABORTED),
+ OUT_OF_RANGE(com.google.rpc.Code.OUT_OF_RANGE),
+ UNIMPLEMENTED(com.google.rpc.Code.UNIMPLEMENTED),
+ INTERNAL(com.google.rpc.Code.INTERNAL),
+ UNAVAILABLE(com.google.rpc.Code.UNAVAILABLE),
+ DATA_LOSS(com.google.rpc.Code.DATA_LOSS),
+
+ /** Code not known by the client, please upgrade your client */
+ UNRECOGNIZED(com.google.rpc.Code.UNRECOGNIZED);
+
+ private final com.google.rpc.Code proto;
+
+ public static Code fromProto(com.google.rpc.Code proto) {
+ for (Code code : values()) {
+ if (code.proto.equals(proto)) {
+ return code;
+ }
+ }
+ return UNRECOGNIZED;
+ }
+
+ public static Code fromCodeNumber(int num) {
+ for (Code code : values()) {
+ if (code.proto == com.google.rpc.Code.UNRECOGNIZED) {
+ continue;
+ }
+ if (code.proto.getNumber() == num) {
+ return code;
+ }
+ }
+ return UNRECOGNIZED;
+ }
+
+ Code(com.google.rpc.Code proto) {
+ this.proto = proto;
+ }
+
+ public com.google.rpc.Code toProto() {
+ return proto;
+ }
+ }
+
+ private final com.google.rpc.Status proto;
+
+ /** Wraps the given protobuf Status */
+ public static Status fromProto(com.google.rpc.Status proto) {
+ return new Status(proto);
+ }
+
+ private Status(com.google.rpc.Status proto) {
+ this.proto = proto;
+ }
+
+ /** Gets the typesafe code. */
+ public Code getCode() {
+ return Code.fromCodeNumber(proto.getCode());
+ }
+
+ /** Gets error message. */
+ public String getMessage() {
+ return proto.getMessage();
+ }
+
+ /** Gets the underlying protobuf. */
+ public com.google.rpc.Status toProto() {
+ return proto;
+ }
+
+ public String toString() {
+ return proto.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Status status = (Status) o;
+ return Objects.equal(proto, status.proto);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(proto);
+ }
+}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientTest.java
index eeb87ada2d..adc7ae0526 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientTest.java
@@ -42,6 +42,7 @@
import com.google.bigtable.admin.v2.ModifyColumnFamiliesRequest.Modification;
import com.google.bigtable.admin.v2.RestoreSourceType;
import com.google.bigtable.admin.v2.RestoreTableMetadata;
+import com.google.bigtable.admin.v2.Table.ClusterState;
import com.google.bigtable.admin.v2.Table.View;
import com.google.bigtable.admin.v2.TableName;
import com.google.cloud.Identity;
@@ -55,12 +56,14 @@
import com.google.cloud.bigtable.admin.v2.models.Backup;
import com.google.cloud.bigtable.admin.v2.models.CreateBackupRequest;
import com.google.cloud.bigtable.admin.v2.models.CreateTableRequest;
+import com.google.cloud.bigtable.admin.v2.models.EncryptionInfo;
import com.google.cloud.bigtable.admin.v2.models.ModifyColumnFamiliesRequest;
import com.google.cloud.bigtable.admin.v2.models.RestoreTableRequest;
import com.google.cloud.bigtable.admin.v2.models.RestoredTableResult;
import com.google.cloud.bigtable.admin.v2.models.Table;
import com.google.cloud.bigtable.admin.v2.models.UpdateBackupRequest;
import com.google.cloud.bigtable.admin.v2.stub.EnhancedBigtableTableAdminStub;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.io.BaseEncoding;
import com.google.longrunning.Operation;
@@ -72,6 +75,7 @@
import io.grpc.Status;
import io.grpc.Status.Code;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Before;
@@ -300,6 +304,45 @@ public void testGetTable() {
assertThat(actualResult).isEqualTo(Table.fromProto(expectedResponse));
}
+ @Test
+ public void testGetEncryptionInfos() {
+ // Setup
+ GetTableRequest expectedRequest =
+ GetTableRequest.newBuilder().setName(TABLE_NAME).setView(View.ENCRYPTION_VIEW).build();
+
+ com.google.bigtable.admin.v2.EncryptionInfo expectedEncryptionInfo =
+ com.google.bigtable.admin.v2.EncryptionInfo.newBuilder()
+ .setKmsKeyVersion("some key")
+ .setEncryptionType(
+ com.google.bigtable.admin.v2.EncryptionInfo.EncryptionType
+ .CUSTOMER_MANAGED_ENCRYPTION)
+ .setEncryptionStatus(
+ com.google.rpc.Status.newBuilder()
+ .setCode(Code.FAILED_PRECONDITION.value())
+ .setMessage("something failed"))
+ .build();
+
+ com.google.bigtable.admin.v2.Table expectedResponse =
+ com.google.bigtable.admin.v2.Table.newBuilder()
+ .setName(TABLE_NAME)
+ .putClusterStates(
+ "cluster1",
+ ClusterState.newBuilder().addEncryptionInfo(expectedEncryptionInfo).build())
+ .build();
+
+ Mockito.when(mockGetTableCallable.futureCall(expectedRequest))
+ .thenReturn(ApiFutures.immediateFuture(expectedResponse));
+
+ // Execute
+ Map> actualResult =
+ adminClient.getEncryptionInfo(TABLE_ID);
+
+ // Verify that the encryption info is transfered from the proto to the model.
+ assertThat(actualResult)
+ .containsExactly(
+ "cluster1", ImmutableList.of(EncryptionInfo.fromProto(expectedEncryptionInfo)));
+ }
+
@Test
public void testListTables() {
// Setup
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/it/BigtableCmekIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/it/BigtableCmekIT.java
new file mode 100644
index 0000000000..b3c9c4ce66
--- /dev/null
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/it/BigtableCmekIT.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2021 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.it;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
+import static org.junit.Assert.fail;
+
+import com.google.api.gax.rpc.ApiException;
+import com.google.cloud.bigtable.admin.v2.BigtableInstanceAdminClient;
+import com.google.cloud.bigtable.admin.v2.BigtableTableAdminClient;
+import com.google.cloud.bigtable.admin.v2.internal.NameUtil;
+import com.google.cloud.bigtable.admin.v2.models.Backup;
+import com.google.cloud.bigtable.admin.v2.models.Cluster;
+import com.google.cloud.bigtable.admin.v2.models.CreateBackupRequest;
+import com.google.cloud.bigtable.admin.v2.models.CreateClusterRequest;
+import com.google.cloud.bigtable.admin.v2.models.CreateInstanceRequest;
+import com.google.cloud.bigtable.admin.v2.models.CreateTableRequest;
+import com.google.cloud.bigtable.admin.v2.models.EncryptionInfo;
+import com.google.cloud.bigtable.admin.v2.models.StorageType;
+import com.google.cloud.bigtable.common.Status;
+import com.google.cloud.bigtable.common.Status.Code;
+import com.google.cloud.bigtable.test_helpers.env.AbstractTestEnv;
+import com.google.cloud.bigtable.test_helpers.env.EmulatorEnv;
+import com.google.cloud.bigtable.test_helpers.env.TestEnvRule;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.threeten.bp.Instant;
+import org.threeten.bp.temporal.ChronoUnit;
+
+/**
+ * Tests our CMEK offering. It can take up to 5 mins after a CMEK-protected table is created for the
+ * key version and status fields to be populated. Set the `bigtable.wait-for-cmek-key-status` system
+ * property to `true` when running the test in order to poll until the final state can be asserted.
+ */
+@RunWith(JUnit4.class)
+public class BigtableCmekIT {
+
+ private static final int[] BACKOFF_DURATION = {5, 10, 50, 100, 150, 200, 250, 300};
+ private static final Logger LOGGER = Logger.getLogger(BigtableCmekIT.class.getName());
+ private static final String TEST_TABLE_ID = "test-table-for-cmek-it";
+ private static final String BACKUP_ID = "test-table-for-cmek-it-backup";
+
+ @ClassRule public static TestEnvRule testEnvRule = new TestEnvRule();
+
+ private static String instanceId;
+ private static String clusterId1;
+ private static String clusterId2;
+ private static String kmsKeyName;
+ private static String zoneId;
+
+ private static BigtableInstanceAdminClient instanceAdmin;
+ private static BigtableTableAdminClient tableAdmin;
+
+ @BeforeClass
+ public static void validatePlatform() throws IOException {
+ assume()
+ .withMessage("Emulator doesn't support CMEK")
+ .that(testEnvRule.env())
+ .isNotInstanceOf(EmulatorEnv.class);
+
+ kmsKeyName = testEnvRule.env().getKmsKeyName();
+ assertThat(kmsKeyName).isNotNull();
+ assertThat(kmsKeyName).isNotEmpty();
+
+ instanceId = AbstractTestEnv.TEST_INSTANCE_PREFIX + Instant.now().getEpochSecond();
+ clusterId1 = instanceId + "-c1";
+ clusterId2 = instanceId + "-c2";
+ zoneId = testEnvRule.env().getPrimaryZone();
+
+ instanceAdmin = testEnvRule.env().getInstanceAdminClient();
+ tableAdmin =
+ BigtableTableAdminClient.create(
+ testEnvRule
+ .env()
+ .getTableAdminSettings()
+ .toBuilder()
+ .setInstanceId(instanceId)
+ .build());
+ }
+
+ @AfterClass
+ public static void teardown() {
+ if (tableAdmin != null) {
+ tableAdmin.close();
+ }
+ if (instanceAdmin != null) {
+ instanceAdmin.close();
+ }
+ }
+
+ @Test
+ public void instanceAndClusterTest() {
+ try {
+ // With a KMS_KEY created and specified using `bigtable.kms_key_name` env variable, create a
+ // CMEK protected instance
+ instanceAdmin.createInstance(
+ CreateInstanceRequest.of(instanceId)
+ .addCmekCluster(clusterId1, zoneId, 1, StorageType.SSD, kmsKeyName));
+
+ // Keys are specified per-cluster with each cluster requesting the same key and the cluster's
+ // zone must be within the region of the key
+ Cluster cluster = instanceAdmin.getCluster(instanceId, clusterId1);
+ assertThat(cluster.getKmsKeyName()).isEqualTo(kmsKeyName);
+
+ String secondZoneId = testEnvRule.env().getPrimaryRegionSecondZone();
+ instanceAdmin.createCluster(
+ CreateClusterRequest.of(instanceId, clusterId2)
+ .setZone(secondZoneId)
+ .setServeNodes(1)
+ .setStorageType(StorageType.SSD)
+ .setKmsKeyName(kmsKeyName));
+
+ Cluster secondCluster = instanceAdmin.getCluster(instanceId, clusterId2);
+ assertThat(secondCluster.getKmsKeyName()).isEqualTo(kmsKeyName);
+
+ final String nonPrimaryRegionZoneId = testEnvRule.env().getSecondaryZone();
+ try {
+ instanceAdmin.createCluster(
+ CreateClusterRequest.of(instanceId, clusterId2)
+ .setZone(nonPrimaryRegionZoneId)
+ .setServeNodes(1)
+ .setStorageType(StorageType.SSD)
+ .setKmsKeyName(kmsKeyName));
+ } catch (com.google.api.gax.rpc.FailedPreconditionException e) {
+ assertThat(e.getMessage())
+ .contains(
+ "FAILED_PRECONDITION: Error in field 'cluster' : "
+ + "Error in field 'encryption_config.kms_key_name' : CMEK key "
+ + kmsKeyName
+ + " cannot be used to protect a cluster in zone "
+ + NameUtil.formatLocationName(
+ testEnvRule.env().getProjectId(), nonPrimaryRegionZoneId));
+ }
+ } finally {
+ instanceAdmin.deleteInstance(instanceId);
+ }
+ }
+
+ @Test
+ public void tableTest() throws Exception {
+ try {
+ instanceAdmin.createInstance(
+ CreateInstanceRequest.of(instanceId)
+ .addCmekCluster(clusterId1, zoneId, 1, StorageType.SSD, kmsKeyName));
+
+ // Create a table. Key is inherited from the cluster configuration
+ tableAdmin.createTable(CreateTableRequest.of(TEST_TABLE_ID).addFamily("cf"));
+
+ // Confirm that table is CMEK-protected
+ if (testEnvRule.env().shouldWaitForCmekKeyStatusUpdate()) {
+ waitForCmekStatus(TEST_TABLE_ID, clusterId1);
+ }
+ Map> encryptionInfos =
+ tableAdmin.getEncryptionInfo(TEST_TABLE_ID);
+ assertThat(encryptionInfos).hasSize(1);
+ assertThat(encryptionInfos.get(clusterId1)).hasSize(1);
+ EncryptionInfo encryptionInfo = encryptionInfos.get(clusterId1).get(0);
+ assertThat(encryptionInfo.getType())
+ .isEqualTo(EncryptionInfo.Type.CUSTOMER_MANAGED_ENCRYPTION);
+ assertThat(encryptionInfo.getStatus().getCode()).isAnyOf(Status.Code.OK, Status.Code.UNKNOWN);
+ if (testEnvRule.env().shouldWaitForCmekKeyStatusUpdate()) {
+ assertThat(encryptionInfo.getStatus().getCode()).isEqualTo(Status.Code.OK);
+ }
+ // For up to 5 minutes after a table is newly created, the key version and status fields are
+ // not
+ // populated.
+ // Set the `bigtable.wait-for-cmek-key-status` system property to `true` when running the test
+ // in order to poll until the final state can be asserted.
+ if (encryptionInfo.getStatus().getCode() == Code.UNKNOWN) {
+ assertThat(encryptionInfo.getKmsKeyVersion()).isEmpty();
+ assertThat(encryptionInfo.getStatus().getMessage())
+ .isEqualTo("Key version is not yet known.");
+ } else {
+ assertThat(encryptionInfo.getKmsKeyVersion()).startsWith(kmsKeyName);
+ assertThat(encryptionInfo.getStatus().getMessage()).isEqualTo("");
+ }
+ } finally {
+ tableAdmin.deleteTable(TEST_TABLE_ID);
+ instanceAdmin.deleteInstance(instanceId);
+ }
+ }
+
+ @Test
+ public void backupTest() {
+ try {
+ instanceAdmin.createInstance(
+ CreateInstanceRequest.of(instanceId)
+ .addCmekCluster(clusterId1, zoneId, 1, StorageType.SSD, kmsKeyName));
+ tableAdmin.createTable(CreateTableRequest.of(TEST_TABLE_ID).addFamily("cf"));
+
+ // Create a backup.
+ // Backups are pinned to the primary version of their table's CMEK key at the time they are
+ // taken
+ tableAdmin.createBackup(
+ CreateBackupRequest.of(clusterId1, BACKUP_ID)
+ .setExpireTime(Instant.now().plus(6, ChronoUnit.HOURS))
+ .setSourceTableId(TEST_TABLE_ID));
+
+ Backup backup = tableAdmin.getBackup(clusterId1, BACKUP_ID);
+
+ // Confirm encryption details for an existing backup
+ // The backup will be returned with the CMEK key version that the backup is pinned to.
+ // The status of that key version will always be UNKNOWN.
+ assertThat(backup.getEncryptionInfo().getKmsKeyVersion()).startsWith(kmsKeyName);
+ assertThat(backup.getEncryptionInfo().getStatus().getCode()).isEqualTo(Status.Code.UNKNOWN);
+ assertThat(backup.getEncryptionInfo().getType())
+ .isEqualTo(EncryptionInfo.Type.CUSTOMER_MANAGED_ENCRYPTION);
+ assertThat(backup.getEncryptionInfo().getStatus().getMessage())
+ .isEqualTo("Status of the associated key version is not tracked.");
+ } finally {
+ tableAdmin.deleteBackup(clusterId1, BACKUP_ID);
+ tableAdmin.deleteTable(TEST_TABLE_ID);
+ instanceAdmin.deleteInstance(instanceId);
+ }
+ }
+
+ private void waitForCmekStatus(String tableId, String clusterId) throws InterruptedException {
+ for (int i = 0; i < BACKOFF_DURATION.length; i++) {
+ try {
+ EncryptionInfo encryptionInfo = tableAdmin.getEncryptionInfo(tableId).get(clusterId).get(0);
+ if (encryptionInfo.getStatus().getCode() == Code.OK) {
+ return;
+ }
+ } catch (ApiException ex) {
+ LOGGER.info(
+ "Wait for "
+ + BACKOFF_DURATION[i]
+ + " seconds for key status for table "
+ + tableId
+ + " and cluster "
+ + clusterId);
+ }
+ Thread.sleep(BACKOFF_DURATION[i] * 1000);
+ }
+ fail("CMEK key status failed to return");
+ }
+}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/BackupTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/BackupTest.java
index be32058e23..fe73c5588c 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/BackupTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/BackupTest.java
@@ -17,9 +17,12 @@
import static com.google.common.truth.Truth.assertThat;
+import com.google.bigtable.admin.v2.EncryptionInfo.EncryptionType;
+import com.google.cloud.bigtable.common.Status;
import com.google.common.collect.Lists;
import com.google.protobuf.Timestamp;
import com.google.protobuf.util.Timestamps;
+import com.google.rpc.Code;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -74,6 +77,34 @@ public void testFromProto() {
assertThat(result.getState()).isEqualTo(Backup.State.READY);
}
+ @Test
+ public void testFromProtoCmek() {
+ com.google.bigtable.admin.v2.Backup proto =
+ com.google.bigtable.admin.v2.Backup.newBuilder()
+ .setName("projects/my-project/instances/instance1/clusters/cluster1/backups/backup1")
+ .setSourceTable("projects/my-project/instances/instance1/tables/table1")
+ .setExpireTime(Timestamp.newBuilder().setSeconds(1234))
+ .setStartTime(Timestamp.newBuilder().setSeconds(1234))
+ .setEndTime(Timestamp.newBuilder().setSeconds(1234))
+ .setSizeBytes(123456)
+ .setState(com.google.bigtable.admin.v2.Backup.State.READY)
+ .setEncryptionInfo(
+ com.google.bigtable.admin.v2.EncryptionInfo.newBuilder()
+ .setEncryptionType(EncryptionType.CUSTOMER_MANAGED_ENCRYPTION)
+ .setKmsKeyVersion("some key version")
+ .setEncryptionStatus(
+ com.google.rpc.Status.newBuilder().setCode(Code.OK.getNumber()).build())
+ .build())
+ .build();
+
+ Backup result = Backup.fromProto(proto);
+
+ assertThat(result.getEncryptionInfo().getType())
+ .isEqualTo(EncryptionInfo.Type.CUSTOMER_MANAGED_ENCRYPTION);
+ assertThat(result.getEncryptionInfo().getKmsKeyVersion()).isEqualTo("some key version");
+ assertThat(result.getEncryptionInfo().getStatus().getCode()).isEqualTo(Status.Code.OK);
+ }
+
@Test
public void testRequiresName() {
com.google.bigtable.admin.v2.Backup proto =
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/ClusterTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/ClusterTest.java
index 20a143e097..f2f217ab56 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/ClusterTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/ClusterTest.java
@@ -17,6 +17,8 @@
import static com.google.common.truth.Truth.assertThat;
+import com.google.bigtable.admin.v2.Cluster.EncryptionConfig;
+import com.google.bigtable.admin.v2.Cluster.State;
import com.google.common.collect.Lists;
import java.util.List;
import org.junit.Test;
@@ -32,7 +34,7 @@ public void testFromProto() {
com.google.bigtable.admin.v2.Cluster.newBuilder()
.setName("projects/my-project/instances/my-instance/clusters/my-cluster")
.setLocation("projects/my-project/locations/us-east1-c")
- .setState(com.google.bigtable.admin.v2.Cluster.State.READY)
+ .setState(State.READY)
.setServeNodes(30)
.setDefaultStorageType(com.google.bigtable.admin.v2.StorageType.SSD)
.build();
@@ -45,6 +47,30 @@ public void testFromProto() {
assertThat(result.getState()).isEqualTo(Cluster.State.READY);
assertThat(result.getServeNodes()).isEqualTo(30);
assertThat(result.getStorageType()).isEqualTo(StorageType.SSD);
+ assertThat(result.getKmsKeyName()).isEqualTo(null);
+ }
+
+ @Test
+ public void testFromProtoCmek() {
+ com.google.bigtable.admin.v2.Cluster proto =
+ com.google.bigtable.admin.v2.Cluster.newBuilder()
+ .setName("projects/my-project/instances/my-instance/clusters/my-cluster")
+ .setLocation("projects/my-project/locations/us-east1-c")
+ .setState(State.READY)
+ .setServeNodes(30)
+ .setDefaultStorageType(com.google.bigtable.admin.v2.StorageType.SSD)
+ .setEncryptionConfig(
+ EncryptionConfig.newBuilder()
+ .setKmsKeyName(
+ "projects/my-project/locations/us-east1-c/keyRings/my-key-ring/cryptoKeys/my-key")
+ .build())
+ .build();
+
+ Cluster result = Cluster.fromProto(proto);
+
+ assertThat(result.getKmsKeyName())
+ .isEqualTo(
+ "projects/my-project/locations/us-east1-c/keyRings/my-key-ring/cryptoKeys/my-key");
}
@Test
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequestTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequestTest.java
index 17152d5461..566641039a 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequestTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateClusterRequestTest.java
@@ -16,7 +16,10 @@
package com.google.cloud.bigtable.admin.v2.models;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
+import com.google.bigtable.admin.v2.Cluster;
+import com.google.bigtable.admin.v2.Cluster.EncryptionConfig;
import com.google.cloud.bigtable.admin.v2.internal.NameUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -151,4 +154,28 @@ public void testOptionalFields() {
assertThat(actual).isEqualTo(expected);
}
+
+ @Test
+ public void testCmek() {
+ String kmsKeyName =
+ "projects/my-project/locations/us-east1-c/keyRings/my-key-ring/cryptoKeys/my-key";
+
+ CreateInstanceRequest input =
+ CreateInstanceRequest.of("my-instance")
+ .addCmekCluster("cluster1", "us-east1-c", 1, StorageType.SSD, kmsKeyName);
+
+ com.google.bigtable.admin.v2.CreateInstanceRequest actual = input.toProto("my-project");
+
+ assertThat(actual)
+ .comparingExpectedFieldsOnly()
+ .isEqualTo(
+ com.google.bigtable.admin.v2.CreateInstanceRequest.newBuilder()
+ .putClusters(
+ "cluster1",
+ Cluster.newBuilder()
+ .setEncryptionConfig(
+ EncryptionConfig.newBuilder().setKmsKeyName(kmsKeyName).build())
+ .build())
+ .build());
+ }
}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/EncryptionInfoTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/EncryptionInfoTest.java
new file mode 100644
index 0000000000..d0d077be3d
--- /dev/null
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/EncryptionInfoTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2021 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 static com.google.common.truth.Truth.assertWithMessage;
+
+import com.google.bigtable.admin.v2.EncryptionInfo.EncryptionType;
+import com.google.cloud.bigtable.common.Status;
+import com.google.common.base.Objects;
+import com.google.rpc.Code;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class EncryptionInfoTest {
+
+ @Test
+ public void testAllTypes() {
+ for (EncryptionType protoValue :
+ com.google.bigtable.admin.v2.EncryptionInfo.EncryptionType.values()) {
+ EncryptionInfo.Type modelValue = EncryptionInfo.Type.fromProto(protoValue);
+
+ assertWithMessage("proto enum value %s should be wrapped", protoValue.toString())
+ .that(modelValue.toString())
+ .isEqualTo(protoValue.toString());
+ }
+
+ com.google.bigtable.admin.v2.EncryptionInfo randomEncryptionInfo =
+ com.google.bigtable.admin.v2.EncryptionInfo.newBuilder().setEncryptionTypeValue(14).build();
+ assertWithMessage("Unrecognized proto enum value should be wrapped")
+ .that(EncryptionInfo.Type.fromProto(randomEncryptionInfo.getEncryptionType()))
+ .isEqualTo(EncryptionInfo.Type.UNRECOGNIZED);
+ }
+
+ @Test
+ public void testFromProto() {
+ com.google.rpc.Status protoStatus =
+ com.google.rpc.Status.newBuilder()
+ .setCode(Code.UNAVAILABLE.getNumber())
+ .setMessage("kms is unavailable")
+ .build();
+
+ com.google.bigtable.admin.v2.EncryptionInfo proto =
+ com.google.bigtable.admin.v2.EncryptionInfo.newBuilder()
+ .setEncryptionType(EncryptionType.CUSTOMER_MANAGED_ENCRYPTION)
+ .setKmsKeyVersion("some version")
+ .setEncryptionStatus(protoStatus)
+ .build();
+ EncryptionInfo encryptionInfo = EncryptionInfo.fromProto(proto);
+
+ assertThat(encryptionInfo.getStatus()).isEqualTo(Status.fromProto(protoStatus));
+ assertThat(encryptionInfo.getType()).isEqualTo(EncryptionInfo.Type.CUSTOMER_MANAGED_ENCRYPTION);
+ assertThat(encryptionInfo.getKmsKeyVersion()).isEqualTo("some version");
+ assertThat(encryptionInfo.toString()).isEqualTo(proto.toString());
+ assertThat(encryptionInfo.hashCode()).isEqualTo(Objects.hashCode(proto));
+ }
+}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/common/StatusTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/common/StatusTest.java
new file mode 100644
index 0000000000..dccbd34408
--- /dev/null
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/common/StatusTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2021 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.common;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import com.google.rpc.Code;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class StatusTest {
+
+ @Test
+ public void testAllCodes() {
+ for (Code protoValue : com.google.rpc.Code.values()) {
+ Status.Code modelValue = Status.Code.fromProto(protoValue);
+
+ assertWithMessage("proto enum value %s should be wrapped", protoValue.toString())
+ .that(modelValue.toString())
+ .isEqualTo(protoValue.toString());
+ }
+
+ com.google.rpc.Status randomProto =
+ com.google.rpc.Status.newBuilder().setCode(49).setMessage("some message").build();
+ assertWithMessage("Unrecognized proto value should be wrapped")
+ .that(Status.Code.fromProto(com.google.rpc.Code.forNumber(randomProto.getCode())))
+ .isEqualTo(Status.Code.UNRECOGNIZED);
+ }
+
+ @Test
+ public void testAllCodeNumbers() {
+ for (Code protoValue : com.google.rpc.Code.values()) {
+ if (protoValue == com.google.rpc.Code.UNRECOGNIZED) {
+ continue;
+ }
+ Status.Code modelValue = Status.Code.fromCodeNumber(protoValue.getNumber());
+
+ assertWithMessage("proto enum value %s should be wrapped", protoValue.toString())
+ .that(modelValue.toString())
+ .isEqualTo(protoValue.toString());
+ }
+
+ assertWithMessage("Unrecognized proto enum value should be wrapped")
+ .that(Status.Code.fromCodeNumber(-1))
+ .isEqualTo(Status.Code.UNRECOGNIZED);
+ }
+
+ @Test
+ public void testFromProto() {
+ com.google.rpc.Status proto =
+ com.google.rpc.Status.newBuilder()
+ .setCode(Code.UNAVAILABLE.getNumber())
+ .setMessage("some message")
+ .build();
+
+ Status model = Status.fromProto(proto);
+ assertThat(model.getCode()).isEqualTo(Status.Code.UNAVAILABLE);
+ assertThat(model.getMessage()).isEqualTo("some message");
+ }
+
+ @Test
+ public void testToProto() {
+ com.google.rpc.Code code = Code.UNAVAILABLE;
+ com.google.rpc.Status proto =
+ com.google.rpc.Status.newBuilder()
+ .setCode(code.getNumber())
+ .setMessage("some message")
+ .build();
+
+ Status model = Status.fromProto(proto);
+ assertThat(model.getCode().toProto()).isEqualTo(code);
+ assertThat(model.toProto()).isEqualTo(proto);
+
+ assertThat(model.toString()).isEqualTo(proto.toString());
+ }
+}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/AbstractTestEnv.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/AbstractTestEnv.java
index f183e5eea0..4fcc53f7c6 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/AbstractTestEnv.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/AbstractTestEnv.java
@@ -16,7 +16,9 @@
package com.google.cloud.bigtable.test_helpers.env;
import com.google.cloud.bigtable.admin.v2.BigtableInstanceAdminClient;
+import com.google.cloud.bigtable.admin.v2.BigtableInstanceAdminSettings;
import com.google.cloud.bigtable.admin.v2.BigtableTableAdminClient;
+import com.google.cloud.bigtable.admin.v2.BigtableTableAdminSettings;
import com.google.cloud.bigtable.admin.v2.models.AppProfile;
import com.google.cloud.bigtable.admin.v2.models.Cluster;
import com.google.cloud.bigtable.admin.v2.models.Instance;
@@ -54,12 +56,18 @@ public abstract BigtableTableAdminClient getTableAdminClientForInstance(String i
public abstract BigtableDataSettings getDataClientSettings();
+ public abstract BigtableInstanceAdminSettings getInstanceAdminClientSettings();
+
+ public abstract BigtableTableAdminSettings getTableAdminSettings();
+
public abstract String getProjectId();
public abstract String getInstanceId();
public abstract String getTableId();
+ public abstract String getKmsKeyName();
+
public String getFamilyId() {
return "cf";
}
@@ -88,10 +96,18 @@ public boolean isDirectPathIpv4Only() {
return Boolean.getBoolean("bigtable.directpath-ipv4only");
}
+ public boolean shouldWaitForCmekKeyStatusUpdate() {
+ return Boolean.getBoolean("bigtable.wait-for-cmek-key-status");
+ }
+
public String getPrimaryZone() {
return "us-central1-b";
}
+ public String getPrimaryRegionSecondZone() {
+ return "us-central1-c";
+ }
+
public String getSecondaryZone() {
return "us-east1-b";
}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/CloudEnv.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/CloudEnv.java
index 6afe733ab7..1de8ee53be 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/CloudEnv.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/CloudEnv.java
@@ -63,10 +63,12 @@ class CloudEnv extends AbstractTestEnv {
private static final String PROJECT_PROPERTY_NAME = "bigtable.project";
private static final String INSTANCE_PROPERTY_NAME = "bigtable.instance";
private static final String TABLE_PROPERTY_NAME = "bigtable.table";
+ private static final String CMEK_KMS_KEY_PROPERTY_NAME = "bigtable.kms_key_name";
private final String projectId;
private final String instanceId;
private final String tableId;
+ private final String kmsKeyName;
private final BigtableDataSettings.Builder dataSettings;
private final BigtableTableAdminSettings.Builder tableAdminSettings;
@@ -80,6 +82,7 @@ static CloudEnv fromSystemProperties() {
return new CloudEnv(
getOptionalProperty(DATA_ENDPOINT_PROPERTY_NAME, ""),
getOptionalProperty(ADMIN_ENDPOINT_PROPERTY_NAME, ""),
+ getOptionalProperty(CMEK_KMS_KEY_PROPERTY_NAME, ""),
getRequiredProperty(PROJECT_PROPERTY_NAME),
getRequiredProperty(INSTANCE_PROPERTY_NAME),
getRequiredProperty(TABLE_PROPERTY_NAME));
@@ -88,12 +91,14 @@ static CloudEnv fromSystemProperties() {
private CloudEnv(
@Nullable String dataEndpoint,
@Nullable String adminEndpoint,
+ @Nullable String kmsKeyName,
String projectId,
String instanceId,
String tableId) {
this.projectId = projectId;
this.instanceId = instanceId;
this.tableId = tableId;
+ this.kmsKeyName = kmsKeyName;
this.dataSettings =
BigtableDataSettings.newBuilder().setProjectId(projectId).setInstanceId(instanceId);
@@ -191,6 +196,25 @@ public BigtableDataSettings getDataClientSettings() {
return dataSettings.build();
}
+ @Override
+ public BigtableInstanceAdminSettings getInstanceAdminClientSettings() {
+ try {
+ return instanceAdminSettings.build();
+ } catch (IOException e) {
+ throw new IllegalStateException(
+ "Caught unexpected error building instance admin settings", e);
+ }
+ }
+
+ @Override
+ public BigtableTableAdminSettings getTableAdminSettings() {
+ try {
+ return tableAdminSettings.build();
+ } catch (IOException e) {
+ throw new IllegalStateException("Caught unexpected error building table admin settings", e);
+ }
+ }
+
@Override
public String getProjectId() {
return projectId;
@@ -206,6 +230,10 @@ public String getTableId() {
return tableId;
}
+ public String getKmsKeyName() {
+ return kmsKeyName;
+ }
+
private static String getOptionalProperty(String prop, String defaultValue) {
return MoreObjects.firstNonNull(System.getProperty(prop), defaultValue);
}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/EmulatorEnv.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/EmulatorEnv.java
index edb9439dce..e4b0a26515 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/EmulatorEnv.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/EmulatorEnv.java
@@ -16,6 +16,7 @@
package com.google.cloud.bigtable.test_helpers.env;
import com.google.cloud.bigtable.admin.v2.BigtableInstanceAdminClient;
+import com.google.cloud.bigtable.admin.v2.BigtableInstanceAdminSettings;
import com.google.cloud.bigtable.admin.v2.BigtableTableAdminClient;
import com.google.cloud.bigtable.admin.v2.BigtableTableAdminSettings;
import com.google.cloud.bigtable.admin.v2.models.CreateTableRequest;
@@ -38,6 +39,7 @@ public class EmulatorEnv extends AbstractTestEnv {
private BigtableDataClient dataClient;
private BigtableDataSettings dataSettings;
+ private BigtableTableAdminSettings tableAdminSettings;
public static EmulatorEnv createBundled() {
return new EmulatorEnv();
@@ -63,12 +65,13 @@ void start() throws Exception {
dataClient = BigtableDataClient.create(dataSettings);
- tableAdminClient =
- BigtableTableAdminClient.create(
- BigtableTableAdminSettings.newBuilderForEmulator(emulator.getPort())
- .setProjectId("fake-project")
- .setInstanceId("fake-instance")
- .build());
+ tableAdminSettings =
+ BigtableTableAdminSettings.newBuilderForEmulator(emulator.getPort())
+ .setProjectId("fake-project")
+ .setInstanceId("fake-instance")
+ .build();
+
+ tableAdminClient = BigtableTableAdminClient.create(tableAdminSettings);
tableAdminClient.createTable(CreateTableRequest.of(TABLE_ID).addFamily(getFamilyId()));
}
@@ -85,6 +88,16 @@ public BigtableDataSettings getDataClientSettings() {
return dataSettings;
}
+ @Override
+ public BigtableInstanceAdminSettings getInstanceAdminClientSettings() {
+ throw new UnsupportedOperationException("instance admin is not support by the emulator");
+ }
+
+ @Override
+ public BigtableTableAdminSettings getTableAdminSettings() {
+ return tableAdminSettings;
+ }
+
@Override
public String getProjectId() {
return PROJECT_ID;
@@ -126,6 +139,10 @@ public BigtableInstanceAdminClient getInstanceAdminClient() {
throw new UnsupportedOperationException("InstanceAdminClient is not supported with emulator");
}
+ public String getKmsKeyName() {
+ throw new UnsupportedOperationException("CMEK is not supported with emulator");
+ }
+
@Override
public boolean isInstanceAdminSupported() {
return false;
diff --git a/pom.xml b/pom.xml
index 8428244c54..59f778a447 100644
--- a/pom.xml
+++ b/pom.xml
@@ -183,6 +183,12 @@
truth
1.1.2
+
+ com.google.truth.extensions
+ truth-proto-extension
+ 1.0.1
+ test
+
junit
junit
From 13e7796d96222fe0181771d160d12a3da7f0a7a7 Mon Sep 17 00:00:00 2001
From: WhiteSource Renovate
Date: Wed, 7 Apr 2021 00:26:05 +0200
Subject: [PATCH 6/8] deps: update autovalue.version to v1.8 (#703)
[](https://renovatebot.com)
This PR contains the following updates:
| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [com.google.auto.value:auto-value-annotations](https://togithub.com/google/auto) | `1.7.5` -> `1.8` | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) |
| [com.google.auto.value:auto-value](https://togithub.com/google/auto) | `1.7.5` -> `1.8` | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) |
---
### Configuration
:date: **Schedule**: At any time (no schedule defined).
:vertical_traffic_light: **Automerge**: Disabled by config. Please merge this manually once you are satisfied.
:recycle: **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
:no_bell: **Ignore**: Close this PR and you won't be reminded about these updates again.
---
- [ ] If you want to rebase/retry this PR, check this box.
---
This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#github/googleapis/java-bigtable).
---
google-cloud-bigtable-deps-bom/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/google-cloud-bigtable-deps-bom/pom.xml b/google-cloud-bigtable-deps-bom/pom.xml
index 5efda749db..b734e2b9b6 100644
--- a/google-cloud-bigtable-deps-bom/pom.xml
+++ b/google-cloud-bigtable-deps-bom/pom.xml
@@ -71,7 +71,7 @@
- 1.7.5
+ 1.8
From 3fa8b0069d58a5705f11fac481ddcd4ad7d447f4 Mon Sep 17 00:00:00 2001
From: WhiteSource Renovate
Date: Wed, 7 Apr 2021 00:30:02 +0200
Subject: [PATCH 7/8] deps: update dependency
com.google.truth.extensions:truth-proto-extension to v1.1.2 (#704)
[](https://renovatebot.com)
This PR contains the following updates:
| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| com.google.truth.extensions:truth-proto-extension | `1.0.1` -> `1.1.2` | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) |
---
### Configuration
:date: **Schedule**: At any time (no schedule defined).
:vertical_traffic_light: **Automerge**: Disabled by config. Please merge this manually once you are satisfied.
:recycle: **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
:no_bell: **Ignore**: Close this PR and you won't be reminded about this update again.
---
- [ ] If you want to rebase/retry this PR, check this box.
---
This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#github/googleapis/java-bigtable).
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 59f778a447..392fac731c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -186,7 +186,7 @@
com.google.truth.extensions
truth-proto-extension
- 1.0.1
+ 1.1.2
test
From 89ec4e0b487b8514b684b8aea4a42c23cdfc4b3f Mon Sep 17 00:00:00 2001
From: "release-please[bot]"
<55107282+release-please[bot]@users.noreply.github.com>
Date: Wed, 7 Apr 2021 14:10:06 +0000
Subject: [PATCH 8/8] chore: release 1.22.0 (#701)
:robot: I have created a release \*beep\* \*boop\*
---
## [1.22.0](https://www.github.com/googleapis/java-bigtable/compare/v1.21.3...v1.22.0) (2021-04-06)
### Features
* add CMEK Support ([#656](https://www.github.com/googleapis/java-bigtable/issues/656)) ([2821902](https://www.github.com/googleapis/java-bigtable/commit/2821902b34ae04596771a9fc5e2b62d5c24a7253))
### Dependencies
* update autovalue.version to v1.8 ([#703](https://www.github.com/googleapis/java-bigtable/issues/703)) ([13e7796](https://www.github.com/googleapis/java-bigtable/commit/13e7796d96222fe0181771d160d12a3da7f0a7a7))
* update dependency com.google.truth.extensions:truth-proto-extension to v1.1.2 ([#704](https://www.github.com/googleapis/java-bigtable/issues/704)) ([3fa8b00](https://www.github.com/googleapis/java-bigtable/commit/3fa8b0069d58a5705f11fac481ddcd4ad7d447f4))
---
This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
---
CHANGELOG.md | 13 +++++++++++++
google-cloud-bigtable-bom/pom.xml | 14 +++++++-------
google-cloud-bigtable-deps-bom/pom.xml | 2 +-
google-cloud-bigtable-emulator/pom.xml | 8 ++++----
google-cloud-bigtable/pom.xml | 10 +++++-----
grpc-google-cloud-bigtable-admin-v2/pom.xml | 8 ++++----
grpc-google-cloud-bigtable-v2/pom.xml | 8 ++++----
pom.xml | 2 +-
proto-google-cloud-bigtable-admin-v2/pom.xml | 8 ++++----
proto-google-cloud-bigtable-v2/pom.xml | 8 ++++----
samples/snapshot/pom.xml | 2 +-
versions.txt | 12 ++++++------
12 files changed, 54 insertions(+), 41 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 82fb681263..7188612641 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,18 @@
# Changelog
+## [1.22.0](https://www.github.com/googleapis/java-bigtable/compare/v1.21.3...v1.22.0) (2021-04-06)
+
+
+### Features
+
+* add CMEK Support ([#656](https://www.github.com/googleapis/java-bigtable/issues/656)) ([2821902](https://www.github.com/googleapis/java-bigtable/commit/2821902b34ae04596771a9fc5e2b62d5c24a7253))
+
+
+### Dependencies
+
+* update autovalue.version to v1.8 ([#703](https://www.github.com/googleapis/java-bigtable/issues/703)) ([13e7796](https://www.github.com/googleapis/java-bigtable/commit/13e7796d96222fe0181771d160d12a3da7f0a7a7))
+* update dependency com.google.truth.extensions:truth-proto-extension to v1.1.2 ([#704](https://www.github.com/googleapis/java-bigtable/issues/704)) ([3fa8b00](https://www.github.com/googleapis/java-bigtable/commit/3fa8b0069d58a5705f11fac481ddcd4ad7d447f4))
+
### [1.21.3](https://www.github.com/googleapis/java-bigtable/compare/v1.21.2...v1.21.3) (2021-04-01)
diff --git a/google-cloud-bigtable-bom/pom.xml b/google-cloud-bigtable-bom/pom.xml
index b476e9da69..e5754d18d9 100644
--- a/google-cloud-bigtable-bom/pom.xml
+++ b/google-cloud-bigtable-bom/pom.xml
@@ -3,7 +3,7 @@
4.0.0
com.google.cloud
google-cloud-bigtable-bom
- 1.21.4-SNAPSHOT
+ 1.22.0
pom
com.google.cloud
@@ -72,32 +72,32 @@
com.google.cloud
google-cloud-bigtable
- 1.21.4-SNAPSHOT
+ 1.22.0
com.google.cloud
google-cloud-bigtable-emulator
- 0.130.4-SNAPSHOT
+ 0.131.0
com.google.api.grpc
grpc-google-cloud-bigtable-admin-v2
- 1.21.4-SNAPSHOT
+ 1.22.0
com.google.api.grpc
grpc-google-cloud-bigtable-v2
- 1.21.4-SNAPSHOT
+ 1.22.0
com.google.api.grpc
proto-google-cloud-bigtable-admin-v2
- 1.21.4-SNAPSHOT
+ 1.22.0
com.google.api.grpc
proto-google-cloud-bigtable-v2
- 1.21.4-SNAPSHOT
+ 1.22.0
diff --git a/google-cloud-bigtable-deps-bom/pom.xml b/google-cloud-bigtable-deps-bom/pom.xml
index b734e2b9b6..1e76a325c1 100644
--- a/google-cloud-bigtable-deps-bom/pom.xml
+++ b/google-cloud-bigtable-deps-bom/pom.xml
@@ -12,7 +12,7 @@
com.google.cloud
google-cloud-bigtable-deps-bom
- 1.21.4-SNAPSHOT
+ 1.22.0
pom
diff --git a/google-cloud-bigtable-emulator/pom.xml b/google-cloud-bigtable-emulator/pom.xml
index 3aad64dfba..f2d3f28e1b 100644
--- a/google-cloud-bigtable-emulator/pom.xml
+++ b/google-cloud-bigtable-emulator/pom.xml
@@ -5,7 +5,7 @@
4.0.0
google-cloud-bigtable-emulator
- 0.130.4-SNAPSHOT
+ 0.131.0
Google Cloud Java - Bigtable Emulator
https://github.com/googleapis/java-bigtable
@@ -14,7 +14,7 @@
com.google.cloud
google-cloud-bigtable-parent
- 1.21.4-SNAPSHOT
+ 1.22.0
scm:git:git@github.com:googleapis/java-bigtable.git
@@ -80,14 +80,14 @@
com.google.cloud
google-cloud-bigtable-deps-bom
- 1.21.4-SNAPSHOT
+ 1.22.0
pom
import
com.google.cloud
google-cloud-bigtable-bom
- 1.21.4-SNAPSHOT
+ 1.22.0
pom
import
diff --git a/google-cloud-bigtable/pom.xml b/google-cloud-bigtable/pom.xml
index 3e3ba88c5c..82b8c4b74f 100644
--- a/google-cloud-bigtable/pom.xml
+++ b/google-cloud-bigtable/pom.xml
@@ -2,7 +2,7 @@
4.0.0
google-cloud-bigtable
- 1.21.4-SNAPSHOT
+ 1.22.0
jar
Google Cloud Bigtable
https://github.com/googleapis/java-bigtable
@@ -12,11 +12,11 @@
com.google.cloud
google-cloud-bigtable-parent
- 1.21.4-SNAPSHOT
+ 1.22.0
- 1.21.4-SNAPSHOT
+ 1.22.0
google-cloud-bigtable
@@ -39,14 +39,14 @@
com.google.cloud
google-cloud-bigtable-deps-bom
- 1.21.4-SNAPSHOT
+ 1.22.0
pom
import
com.google.cloud
google-cloud-bigtable-bom
- 1.21.4-SNAPSHOT
+ 1.22.0
pom
import
diff --git a/grpc-google-cloud-bigtable-admin-v2/pom.xml b/grpc-google-cloud-bigtable-admin-v2/pom.xml
index 8ebf9e1133..db2eb2625e 100644
--- a/grpc-google-cloud-bigtable-admin-v2/pom.xml
+++ b/grpc-google-cloud-bigtable-admin-v2/pom.xml
@@ -4,13 +4,13 @@
4.0.0
com.google.api.grpc
grpc-google-cloud-bigtable-admin-v2
- 1.21.4-SNAPSHOT
+ 1.22.0
grpc-google-cloud-bigtable-admin-v2
GRPC library for grpc-google-cloud-bigtable-admin-v2
com.google.cloud
google-cloud-bigtable-parent
- 1.21.4-SNAPSHOT
+ 1.22.0
@@ -18,14 +18,14 @@
com.google.cloud
google-cloud-bigtable-deps-bom
- 1.21.4-SNAPSHOT
+ 1.22.0
pom
import
com.google.cloud
google-cloud-bigtable-bom
- 1.21.4-SNAPSHOT
+ 1.22.0
pom
import
diff --git a/grpc-google-cloud-bigtable-v2/pom.xml b/grpc-google-cloud-bigtable-v2/pom.xml
index c5ac213b24..8d7b9def4c 100644
--- a/grpc-google-cloud-bigtable-v2/pom.xml
+++ b/grpc-google-cloud-bigtable-v2/pom.xml
@@ -4,13 +4,13 @@
4.0.0
com.google.api.grpc
grpc-google-cloud-bigtable-v2
- 1.21.4-SNAPSHOT
+ 1.22.0
grpc-google-cloud-bigtable-v2
GRPC library for grpc-google-cloud-bigtable-v2
com.google.cloud
google-cloud-bigtable-parent
- 1.21.4-SNAPSHOT
+ 1.22.0
@@ -18,14 +18,14 @@
com.google.cloud
google-cloud-bigtable-deps-bom
- 1.21.4-SNAPSHOT
+ 1.22.0
pom
import
com.google.cloud
google-cloud-bigtable-bom
- 1.21.4-SNAPSHOT
+ 1.22.0
pom
import
diff --git a/pom.xml b/pom.xml
index 392fac731c..76de1a94b6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
google-cloud-bigtable-parent
pom
- 1.21.4-SNAPSHOT
+ 1.22.0
Google Cloud Bigtable Parent
https://github.com/googleapis/java-bigtable
diff --git a/proto-google-cloud-bigtable-admin-v2/pom.xml b/proto-google-cloud-bigtable-admin-v2/pom.xml
index 11ab9756d3..e1c2c8fce4 100644
--- a/proto-google-cloud-bigtable-admin-v2/pom.xml
+++ b/proto-google-cloud-bigtable-admin-v2/pom.xml
@@ -4,13 +4,13 @@
4.0.0
com.google.api.grpc
proto-google-cloud-bigtable-admin-v2
- 1.21.4-SNAPSHOT
+ 1.22.0
proto-google-cloud-bigtable-admin-v2
PROTO library for proto-google-cloud-bigtable-admin-v2
com.google.cloud
google-cloud-bigtable-parent
- 1.21.4-SNAPSHOT
+ 1.22.0
@@ -18,14 +18,14 @@
com.google.cloud
google-cloud-bigtable-deps-bom
- 1.21.4-SNAPSHOT
+ 1.22.0
pom
import
com.google.cloud
google-cloud-bigtable-bom
- 1.21.4-SNAPSHOT
+ 1.22.0
pom
import
diff --git a/proto-google-cloud-bigtable-v2/pom.xml b/proto-google-cloud-bigtable-v2/pom.xml
index 2f9722cd4b..519ace5ead 100644
--- a/proto-google-cloud-bigtable-v2/pom.xml
+++ b/proto-google-cloud-bigtable-v2/pom.xml
@@ -4,13 +4,13 @@
4.0.0
com.google.api.grpc
proto-google-cloud-bigtable-v2
- 1.21.4-SNAPSHOT
+ 1.22.0
proto-google-cloud-bigtable-v2
PROTO library for proto-google-cloud-bigtable-v2
com.google.cloud
google-cloud-bigtable-parent
- 1.21.4-SNAPSHOT
+ 1.22.0
@@ -18,14 +18,14 @@
com.google.cloud
google-cloud-bigtable-deps-bom
- 1.21.4-SNAPSHOT
+ 1.22.0
pom
import
com.google.cloud
google-cloud-bigtable-bom
- 1.21.4-SNAPSHOT
+ 1.22.0
pom
import
diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml
index 0ad31afbdc..a6aa91e406 100644
--- a/samples/snapshot/pom.xml
+++ b/samples/snapshot/pom.xml
@@ -28,7 +28,7 @@
com.google.cloud
google-cloud-bigtable
- 1.21.4-SNAPSHOT
+ 1.22.0
diff --git a/versions.txt b/versions.txt
index f20bf3c910..23315d67c5 100644
--- a/versions.txt
+++ b/versions.txt
@@ -1,9 +1,9 @@
# Format:
# module:released-version:current-version
-google-cloud-bigtable:1.21.3:1.21.4-SNAPSHOT
-grpc-google-cloud-bigtable-admin-v2:1.21.3:1.21.4-SNAPSHOT
-grpc-google-cloud-bigtable-v2:1.21.3:1.21.4-SNAPSHOT
-proto-google-cloud-bigtable-admin-v2:1.21.3:1.21.4-SNAPSHOT
-proto-google-cloud-bigtable-v2:1.21.3:1.21.4-SNAPSHOT
-google-cloud-bigtable-emulator:0.130.3:0.130.4-SNAPSHOT
+google-cloud-bigtable:1.22.0:1.22.0
+grpc-google-cloud-bigtable-admin-v2:1.22.0:1.22.0
+grpc-google-cloud-bigtable-v2:1.22.0:1.22.0
+proto-google-cloud-bigtable-admin-v2:1.22.0:1.22.0
+proto-google-cloud-bigtable-v2:1.22.0:1.22.0
+google-cloud-bigtable-emulator:0.131.0:0.131.0