diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClient.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClient.java index 6eb712e6ac..f83b01b3d4 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClient.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClient.java @@ -40,6 +40,7 @@ import com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient.ListTablesPagedResponse; 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.CopyBackupRequest; 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; @@ -1317,6 +1318,88 @@ public ApiFuture awaitOptimizeRestoredTableAsync( stub.awaitOptimizeRestoredTableCallable().resumeFutureCall(token.getOperationName())); } + /** + * Copy an existing backup to a new backup in a Cloud Bigtable cluster with the specified + * configuration. + * + *

Sample code Note: You want to create the client with project and instance where you want the + * new backup to be copied to. + * + *

{@code
+   * BigtableTableAdminClient client =  BigtableTableAdminClient.create("[PROJECT]", "[INSTANCE]");
+   * CopyBackupRequest request =
+   *         CopyBackupRequest.of(sourceClusterId, sourceBackupId)
+   *             .setDestination(clusterId, backupId)
+   *             .setExpireTime(expireTime);
+   * Backup response = client.copyBackup(request);
+   * }
+ * + * If the source backup is located in a different instance + * + *
{@code
+   * CopyBackupRequest request =
+   *         CopyBackupRequest.of(sourceClusterId, sourceBackupId)
+   *             .setSourceInstance(sourceInstanceId)
+   *             .setDestination(clusterId, backupId)
+   *             .setExpireTime(expireTime);
+   * Backup response = client.copyBackup(request);
+   * }
+ * + * If the source backup is located in a different project + * + *
{@code
+   * CopyBackupRequest request =
+   *         CopyBackupRequest.of(sourceClusterId, sourceBackupId)
+   *             .setSourceInstance(sourceProjectId, sourceInstanceId)
+   *             .setDestination(clusterId, backupId)
+   *             .setExpireTime(expireTime);
+   * Backup response = client.copyBackup(request);
+   * }
+ */ + public Backup copyBackup(CopyBackupRequest request) { + return ApiExceptions.callAndTranslateApiException(copyBackupAsync(request)); + } + + /** + * Creates a copy of a backup from an existing backup in a Cloud Bigtable cluster with the + * specified configuration asynchronously. + * + *

Sample code + * + *

{@code
+   * CopyBackupRequest request =
+   *         CopyBackupRequest.of(sourceClusterId, sourceBackupId)
+   *             .setDestination(clusterId, backupId)
+   *             .setExpireTime(expireTime);
+   * ApiFuture future = client.copyBackupAsync(request);
+   *
+   * ApiFutures.addCallback(
+   *   future,
+   *   new ApiFutureCallback() {
+   *     public void onSuccess(Backup backup) {
+   *       System.out.println("Successfully create the backup " + backup.getId());
+   *     }
+   *
+   *     public void onFailure(Throwable t) {
+   *       t.printStackTrace();
+   *     }
+   *   },
+   *   MoreExecutors.directExecutor()
+   * );
+   * }
+ */ + public ApiFuture copyBackupAsync(CopyBackupRequest request) { + return ApiFutures.transform( + stub.copyBackupOperationCallable().futureCall(request.toProto(projectId, instanceId)), + new ApiFunction() { + @Override + public Backup apply(com.google.bigtable.admin.v2.Backup backupProto) { + return Backup.fromProto(backupProto); + } + }, + MoreExecutors.directExecutor()); + } + /** * Returns a future that is resolved when replication has caught up to the point when this method * was called. This allows callers to make sure that their mutations have been replicated across diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminSettings.java index 4f0c1b49c8..d6630fd887 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminSettings.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminSettings.java @@ -111,6 +111,8 @@ public String toString() { .add("getSnapshotSettings", stubSettings.getSnapshotSettings()) .add("listSnapshotsSettings", stubSettings.listSnapshotsSettings()) .add("deleteSnapshotSettings", stubSettings.deleteSnapshotSettings()) + .add("copyBackupSettings", stubSettings.copyBackupSettings()) + .add("copyBackupOperationSettings", stubSettings.copyBackupOperationSettings()) .add("createBackupSettings", stubSettings.createBackupSettings()) .add("createBackupOperationSettings", stubSettings.createBackupOperationSettings()) .add("getBackupSettings", stubSettings.getBackupSettings()) 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 ce0ed7efc9..59e6fcd038 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 @@ -106,6 +106,11 @@ public String getSourceTableId() { return NameUtil.extractTableIdFromTableName(proto.getSourceTable()); } + /** Get the source backup ID from which the backup is copied. */ + public String getSourceBackupId() { + return NameUtil.extractBackupIdFromBackupName(proto.getSourceBackup()); + } + /** Get the instance ID where this backup is located. */ public String getInstanceId() { return instanceId; diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CopyBackupRequest.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CopyBackupRequest.java new file mode 100644 index 0000000000..88b50376be --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CopyBackupRequest.java @@ -0,0 +1,126 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.admin.v2.models; + +import com.google.api.core.InternalApi; +import com.google.cloud.bigtable.admin.v2.internal.NameUtil; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.protobuf.util.Timestamps; +import javax.annotation.Nonnull; +import org.threeten.bp.Instant; + +/** Build CopyBackupRequest for {@link com.google.bigtable.admin.v2.CopyBackupRequest}. */ +public final class CopyBackupRequest { + private final com.google.bigtable.admin.v2.CopyBackupRequest.Builder requestBuilder = + com.google.bigtable.admin.v2.CopyBackupRequest.newBuilder(); + private final String sourceBackupId; + private final String sourceClusterId; + private String sourceInstanceId; + private String sourceProjectId; + + private String destClusterId; + + /** + * Create a {@link CopyBackupRequest} object. It assumes the source backup is located in the same + * instance and project as the destination backup, which is where the BigtableTableAdminClient is + * created in. use setSourceInstance("[INSTANCE]") if the source backup is located in a different + * instance. use setSourceInstance("[PROJECT]", "[INSTANCE]") if the source backup is located in a + * different project. + */ + public static CopyBackupRequest of(String sourceClusterId, String sourceBackupId) { + CopyBackupRequest request = new CopyBackupRequest(sourceClusterId, sourceBackupId); + return request; + } + + private CopyBackupRequest(@Nonnull String sourceClusterId, @Nonnull String sourceBackupId) { + Preconditions.checkNotNull(sourceClusterId); + Preconditions.checkNotNull(sourceBackupId); + this.sourceClusterId = sourceClusterId; + this.sourceBackupId = sourceBackupId; + } + + public CopyBackupRequest setSourceInstance(String instanceId) { + Preconditions.checkNotNull(instanceId); + this.sourceInstanceId = instanceId; + return this; + } + + public CopyBackupRequest setSourceInstance(String projectId, String instanceId) { + Preconditions.checkNotNull(projectId); + Preconditions.checkNotNull(instanceId); + this.sourceProjectId = projectId; + this.sourceInstanceId = instanceId; + return this; + } + + public CopyBackupRequest setDestination(String clusterId, String backupId) { + Preconditions.checkNotNull(backupId); + Preconditions.checkNotNull(clusterId); + requestBuilder.setBackupId(backupId); + this.destClusterId = clusterId; + return this; + } + + public CopyBackupRequest setExpireTime(Instant expireTime) { + Preconditions.checkNotNull(expireTime); + requestBuilder.setExpireTime(Timestamps.fromMillis(expireTime.toEpochMilli())); + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CopyBackupRequest that = (CopyBackupRequest) o; + return Objects.equal(requestBuilder.getBackupId(), that.requestBuilder.getBackupId()) + && Objects.equal(sourceBackupId, that.sourceBackupId) + && Objects.equal(sourceClusterId, that.sourceClusterId) + && Objects.equal(sourceInstanceId, that.sourceInstanceId) + && Objects.equal(sourceProjectId, that.sourceProjectId); + } + + @Override + public int hashCode() { + return Objects.hashCode( + requestBuilder.getBackupId(), + sourceBackupId, + sourceClusterId, + sourceInstanceId, + sourceProjectId); + } + + @InternalApi + public com.google.bigtable.admin.v2.CopyBackupRequest toProto( + @Nonnull String projectId, @Nonnull String instanceId) { + Preconditions.checkNotNull(projectId); + Preconditions.checkNotNull(instanceId); + + return requestBuilder + .setParent(NameUtil.formatClusterName(projectId, instanceId, destClusterId)) + .setSourceBackup( + NameUtil.formatBackupName( + sourceProjectId == null ? projectId : sourceProjectId, + sourceInstanceId == null ? instanceId : sourceInstanceId, + sourceClusterId, + sourceBackupId)) + .build(); + } +} diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientTests.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientTests.java index 17a2aa4a57..cb9dae8e1d 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientTests.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientTests.java @@ -31,6 +31,7 @@ import com.google.bigtable.admin.v2.BackupInfo; import com.google.bigtable.admin.v2.ChangeStreamConfig; import com.google.bigtable.admin.v2.ColumnFamily; +import com.google.bigtable.admin.v2.CopyBackupMetadata; import com.google.bigtable.admin.v2.CreateBackupMetadata; import com.google.bigtable.admin.v2.DeleteBackupRequest; import com.google.bigtable.admin.v2.DeleteTableRequest; @@ -56,6 +57,7 @@ import com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient.ListTablesPagedResponse; 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.CopyBackupRequest; 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; @@ -172,6 +174,13 @@ public class BigtableTableAdminClientTests { RestoreTableMetadata> mockRestoreTableOperationCallable; + @Mock + private OperationCallable< + com.google.bigtable.admin.v2.CopyBackupRequest, + com.google.bigtable.admin.v2.Backup, + CopyBackupMetadata> + mockCopyBackupOperationCallable; + @Mock private UnaryCallable mockGetIamPolicyCallable; @@ -775,6 +784,73 @@ public void testListBackups() { assertThat(actualResults).containsExactlyElementsIn(expectedResults); } + @Test + public void testCopyBackup() { + // Setup + Mockito.when(mockStub.copyBackupOperationCallable()) + .thenReturn(mockCopyBackupOperationCallable); + + Timestamp startTime = Timestamp.newBuilder().setSeconds(1234).build(); + Timestamp endTime = Timestamp.newBuilder().setSeconds(5678).build(); + + // Create CopyBackupRequest from different source project: + String srcProjectId = "src-project"; + String srcInstanceId = "src-instance"; + String srcTableId = "src-table"; + String srcClusterId = "src-cluster"; + String srcBackupId = "src-backup"; + Instant expireTime = Instant.now().plus(org.threeten.bp.Duration.ofDays(15)); + long sizeBytes = 123456789; + + String dstBackupName = + NameUtil.formatBackupName(PROJECT_ID, INSTANCE_ID, CLUSTER_ID, BACKUP_ID); + String srcBackupName = + NameUtil.formatBackupName(srcProjectId, srcProjectId, srcClusterId, srcBackupId); + String srcTableName = NameUtil.formatTableName(srcProjectId, srcInstanceId, srcTableId); + + CopyBackupRequest req = + CopyBackupRequest.of(srcClusterId, srcBackupId) + .setSourceInstance(srcProjectId, srcInstanceId) + .setDestination(CLUSTER_ID, BACKUP_ID) + .setExpireTime(expireTime); + mockOperationResult( + mockCopyBackupOperationCallable, + req.toProto(PROJECT_ID, INSTANCE_ID), + com.google.bigtable.admin.v2.Backup.newBuilder() + .setName(dstBackupName) + .setSourceTable(srcTableName) + .setSourceBackup(srcBackupName) + .setStartTime(startTime) + .setEndTime(endTime) + .setExpireTime(Timestamps.fromMillis(expireTime.toEpochMilli())) + .setSizeBytes(sizeBytes) + .build(), + CopyBackupMetadata.newBuilder() + .setName(dstBackupName) + .setSourceBackupInfo( + BackupInfo.newBuilder() + .setBackup(srcBackupId) + .setSourceTable(srcTableName) + .setStartTime(startTime) + .setEndTime(endTime) + .build()) + .build()); + + // Execute + Backup actualResult = adminClient.copyBackup(req); + + // Verify + assertThat(actualResult.getId()).isEqualTo(BACKUP_ID); + assertThat(actualResult.getSourceTableId()).isEqualTo(srcTableId); + assertThat(actualResult.getSourceBackupId()).isEqualTo(srcBackupId); + assertThat(actualResult.getStartTime()) + .isEqualTo(Instant.ofEpochMilli(Timestamps.toMillis(startTime))); + assertThat(actualResult.getEndTime()) + .isEqualTo(Instant.ofEpochMilli(Timestamps.toMillis(endTime))); + assertThat(actualResult.getExpireTime()).isEqualTo(expireTime); + assertThat(actualResult.getSizeBytes()).isEqualTo(sizeBytes); + } + @Test public void testGetBackupIamPolicy() { // Setup diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/it/BigtableBackupIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/it/BigtableBackupIT.java index c9032b99b3..0e8b0fb3f3 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/it/BigtableBackupIT.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/it/BigtableBackupIT.java @@ -27,6 +27,7 @@ import com.google.cloud.bigtable.admin.v2.BigtableInstanceAdminClient; import com.google.cloud.bigtable.admin.v2.BigtableTableAdminClient; import com.google.cloud.bigtable.admin.v2.models.Backup; +import com.google.cloud.bigtable.admin.v2.models.CopyBackupRequest; import com.google.cloud.bigtable.admin.v2.models.CreateBackupRequest; import com.google.cloud.bigtable.admin.v2.models.CreateInstanceRequest; import com.google.cloud.bigtable.admin.v2.models.CreateTableRequest; @@ -303,6 +304,104 @@ public void crossInstanceRestoreTest() } } + @Test + public void copyBackupTest() + throws InterruptedException, IOException, ExecutionException, TimeoutException { + String backupId = prefixGenerator.newPrefix(); + String copiedBackupId = prefixGenerator.newPrefix(); + Instant expireTime = Instant.now().plus(Duration.ofHours(36)); + + // Create the backup + tableAdmin.createBackup( + CreateBackupRequest.of(targetCluster, backupId) + .setSourceTableId(testTable.getId()) + .setExpireTime(expireTime)); + + try { + CopyBackupRequest req = + CopyBackupRequest.of(targetCluster, backupId) + .setDestination(targetCluster, copiedBackupId) + .setExpireTime(expireTime); + Backup result = tableAdmin.copyBackup(req); + assertWithMessage("Got wrong copied backup id in CopyBackup API") + .that(result.getId()) + .isEqualTo(copiedBackupId); + assertWithMessage("Got wrong source backup id in CopyBackup API") + .that(result.getSourceBackupId()) + .isEqualTo(backupId); + assertWithMessage("Got wrong expire time in CopyBackup API") + .that(result.getExpireTime()) + .isEqualTo(expireTime); + assertWithMessage("Got empty start time in CopyBackup API") + .that(result.getStartTime()) + .isNotNull(); + assertWithMessage("Got wrong state in CopyBackup API") + .that(result.getState()) + .isAnyOf(Backup.State.CREATING, Backup.State.READY); + + } finally { + tableAdmin.deleteBackup(targetCluster, copiedBackupId); + tableAdmin.deleteBackup(targetCluster, backupId); + } + } + + @Test + public void crossInstanceCopyBackupTest() + throws InterruptedException, IOException, ExecutionException, TimeoutException { + String backupId = prefixGenerator.newPrefix(); + String copiedBackupId = prefixGenerator.newPrefix(); + Instant expireTime = Instant.now().plus(Duration.ofHours(36)); + + // Create the backup + tableAdmin.createBackup( + CreateBackupRequest.of(targetCluster, backupId) + .setSourceTableId(testTable.getId()) + .setExpireTime(expireTime)); + + // Set up a new instance to test cross-instance copy. The backup will be copied here + String destInstance = prefixGenerator.newPrefix(); + String destCluster = prefixGenerator.newPrefix(); + instanceAdmin.createInstance( + CreateInstanceRequest.of(destInstance) + .addCluster(destCluster, testEnvRule.env().getSecondaryZone(), 1, StorageType.SSD) + .setDisplayName("backups-dest-test-instance") + .addLabel("state", "readytodelete") + .setType(Type.PRODUCTION)); + + try (BigtableTableAdminClient destTableAdmin = + testEnvRule.env().getTableAdminClientForInstance(destInstance)) { + + try { + CopyBackupRequest req = + CopyBackupRequest.of(targetCluster, backupId) + .setSourceInstance(testEnvRule.env().getInstanceId()) + .setDestination(destCluster, copiedBackupId) + .setExpireTime(expireTime); + Backup result = destTableAdmin.copyBackup(req); + assertWithMessage("Got wrong copied backup id in CopyBackup API") + .that(result.getId()) + .isEqualTo(copiedBackupId); + assertWithMessage("Got wrong source backup id in CopyBackup API") + .that(result.getSourceBackupId()) + .isEqualTo(backupId); + assertWithMessage("Got wrong expire time in CopyBackup API") + .that(result.getExpireTime()) + .isEqualTo(expireTime); + assertWithMessage("Got empty start time in CopyBackup API") + .that(result.getStartTime()) + .isNotNull(); + assertWithMessage("Got wrong state in CopyBackup API") + .that(result.getState()) + .isAnyOf(Backup.State.CREATING, Backup.State.READY); + + } finally { + destTableAdmin.deleteBackup(destCluster, copiedBackupId); + tableAdmin.deleteBackup(targetCluster, backupId); + instanceAdmin.deleteInstance(destInstance); + } + } + } + @Test public void backupIamTest() { String backupId = prefixGenerator.newPrefix(); 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 fe73c5588c..8b9e7e919a 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 @@ -57,6 +57,8 @@ public void testFromProto() { 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") + .setSourceBackup( + "projects/my-project/instances/instance1/clusters/cluster1/backups/backup2") .setExpireTime(expireTime) .setStartTime(startTime) .setEndTime(endTime) @@ -68,6 +70,7 @@ public void testFromProto() { assertThat(result.getId()).isEqualTo("backup1"); assertThat(result.getSourceTableId()).isEqualTo("table1"); + assertThat(result.getSourceBackupId()).isEqualTo("backup2"); assertThat(result.getExpireTime()) .isEqualTo(Instant.ofEpochMilli(Timestamps.toMillis(expireTime))); assertThat(result.getStartTime()) diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CopyBackupRequestTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CopyBackupRequestTest.java new file mode 100644 index 0000000000..df8830da8e --- /dev/null +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CopyBackupRequestTest.java @@ -0,0 +1,231 @@ +/* + * Copyright 2022 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.cloud.bigtable.admin.v2.internal.NameUtil; +import com.google.protobuf.util.Timestamps; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.threeten.bp.Duration; +import org.threeten.bp.Instant; + +@RunWith(JUnit4.class) +public class CopyBackupRequestTest { + + private static final String BACKUP_ID = "my-backup"; + private static final String CLUSTER_ID = "my-cluster"; + private static final String INSTANCE_ID = "my-instance"; + private static final String PROJECT_ID = "my-project"; + private static final String SOURCE_BACKUP_ID = "source-backup-id"; + private static final String SOURCE_CLUSTER_ID = "source-cluster-id"; + private static final String SOURCE_INSTANCE_ID = "source-instance-id"; + private static final String SOURCE_PROJECT_ID = "source-project-id"; + private static final Instant EXPIRE_TIME = Instant.now().plus(Duration.ofDays(15)); + + @Test + public void testToProto() { + CopyBackupRequest request = + CopyBackupRequest.of(SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID) + .setDestination(CLUSTER_ID, BACKUP_ID) + .setExpireTime(EXPIRE_TIME); + + com.google.bigtable.admin.v2.CopyBackupRequest requestProto = + com.google.bigtable.admin.v2.CopyBackupRequest.newBuilder() + .setParent(NameUtil.formatClusterName(PROJECT_ID, INSTANCE_ID, CLUSTER_ID)) + .setSourceBackup( + NameUtil.formatBackupName( + PROJECT_ID, INSTANCE_ID, SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID)) + .setExpireTime(Timestamps.fromMillis(EXPIRE_TIME.toEpochMilli())) + .setBackupId(BACKUP_ID) + .build(); + assertThat(request.toProto(PROJECT_ID, INSTANCE_ID)).isEqualTo(requestProto); + } + + @Test + public void testToProtoCrossInstance() { + CopyBackupRequest request = + CopyBackupRequest.of(SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID) + .setSourceInstance(SOURCE_INSTANCE_ID) + .setDestination(CLUSTER_ID, BACKUP_ID) + .setExpireTime(EXPIRE_TIME); + + com.google.bigtable.admin.v2.CopyBackupRequest requestProto = + com.google.bigtable.admin.v2.CopyBackupRequest.newBuilder() + .setParent(NameUtil.formatClusterName(PROJECT_ID, INSTANCE_ID, CLUSTER_ID)) + .setSourceBackup( + NameUtil.formatBackupName( + PROJECT_ID, SOURCE_INSTANCE_ID, SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID)) + .setExpireTime(Timestamps.fromMillis(EXPIRE_TIME.toEpochMilli())) + .setBackupId(BACKUP_ID) + .build(); + assertThat(request.toProto(PROJECT_ID, INSTANCE_ID)).isEqualTo(requestProto); + } + + @Test + public void testToProtoCrossProject() { + CopyBackupRequest request = + CopyBackupRequest.of(SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID) + .setSourceInstance(SOURCE_PROJECT_ID, SOURCE_INSTANCE_ID) + .setDestination(CLUSTER_ID, BACKUP_ID) + .setExpireTime(EXPIRE_TIME); + + com.google.bigtable.admin.v2.CopyBackupRequest requestProto = + com.google.bigtable.admin.v2.CopyBackupRequest.newBuilder() + .setParent(NameUtil.formatClusterName(PROJECT_ID, INSTANCE_ID, CLUSTER_ID)) + .setSourceBackup( + NameUtil.formatBackupName( + SOURCE_PROJECT_ID, SOURCE_INSTANCE_ID, SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID)) + .setExpireTime(Timestamps.fromMillis(EXPIRE_TIME.toEpochMilli())) + .setBackupId(BACKUP_ID) + .build(); + assertThat(request.toProto(PROJECT_ID, INSTANCE_ID)).isEqualTo(requestProto); + } + + @Test + public void testEquality() { + CopyBackupRequest request = + CopyBackupRequest.of(SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID) + .setDestination(CLUSTER_ID, BACKUP_ID) + .setExpireTime(EXPIRE_TIME); + + assertThat(request) + .isEqualTo( + CopyBackupRequest.of(SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID) + .setDestination(CLUSTER_ID, BACKUP_ID) + .setExpireTime(EXPIRE_TIME)); + assertThat(request) + .isNotEqualTo( + CopyBackupRequest.of(SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID) + .setDestination(CLUSTER_ID, "another-backup") + .setExpireTime(EXPIRE_TIME)); + } + + @Test + public void testEqualityCrossInstance() { + CopyBackupRequest request = + CopyBackupRequest.of(SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID) + .setSourceInstance(SOURCE_INSTANCE_ID) + .setDestination(CLUSTER_ID, BACKUP_ID) + .setExpireTime(EXPIRE_TIME); + + assertThat(request) + .isEqualTo( + CopyBackupRequest.of(SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID) + .setSourceInstance(SOURCE_INSTANCE_ID) + .setDestination(CLUSTER_ID, BACKUP_ID) + .setExpireTime(EXPIRE_TIME)); + assertThat(request) + .isNotEqualTo( + CopyBackupRequest.of(SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID) + .setSourceInstance(SOURCE_INSTANCE_ID) + .setDestination(CLUSTER_ID, "another-backup") + .setExpireTime(EXPIRE_TIME)); + } + + @Test + public void testEqualityCrossProject() { + CopyBackupRequest request = + CopyBackupRequest.of(SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID) + .setSourceInstance(SOURCE_PROJECT_ID, SOURCE_INSTANCE_ID) + .setDestination(CLUSTER_ID, BACKUP_ID) + .setExpireTime(EXPIRE_TIME); + + assertThat(request) + .isEqualTo( + CopyBackupRequest.of(SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID) + .setSourceInstance(SOURCE_PROJECT_ID, SOURCE_INSTANCE_ID) + .setDestination(CLUSTER_ID, BACKUP_ID) + .setExpireTime(EXPIRE_TIME)); + assertThat(request) + .isNotEqualTo( + CopyBackupRequest.of(SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID) + .setSourceInstance(SOURCE_PROJECT_ID, SOURCE_INSTANCE_ID) + .setDestination(CLUSTER_ID, "another-backup") + .setExpireTime(EXPIRE_TIME)); + } + + @Test + public void testHashCode() { + CopyBackupRequest request = + CopyBackupRequest.of(SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID) + .setDestination(CLUSTER_ID, BACKUP_ID) + .setExpireTime(EXPIRE_TIME); + + assertThat(request.hashCode()) + .isEqualTo( + CopyBackupRequest.of(SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID) + .setDestination(CLUSTER_ID, BACKUP_ID) + .setExpireTime(EXPIRE_TIME) + .hashCode()); + assertThat(request.hashCode()) + .isNotEqualTo( + CopyBackupRequest.of(SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID) + .setDestination(CLUSTER_ID, "another-backup") + .setExpireTime(EXPIRE_TIME) + .hashCode()); + } + + @Test + public void testHashCodeCrossInstance() { + CopyBackupRequest request = + CopyBackupRequest.of(SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID) + .setSourceInstance(SOURCE_INSTANCE_ID) + .setDestination(CLUSTER_ID, BACKUP_ID) + .setExpireTime(EXPIRE_TIME); + + assertThat(request.hashCode()) + .isEqualTo( + CopyBackupRequest.of(SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID) + .setSourceInstance(SOURCE_INSTANCE_ID) + .setDestination(CLUSTER_ID, BACKUP_ID) + .setExpireTime(EXPIRE_TIME) + .hashCode()); + assertThat(request.hashCode()) + .isNotEqualTo( + CopyBackupRequest.of(SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID) + .setSourceInstance(SOURCE_INSTANCE_ID) + .setDestination(CLUSTER_ID, "another-backup") + .setExpireTime(EXPIRE_TIME) + .hashCode()); + } + + @Test + public void testHashCodeCrossProject() { + CopyBackupRequest request = + CopyBackupRequest.of(SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID) + .setSourceInstance(SOURCE_PROJECT_ID, SOURCE_INSTANCE_ID) + .setDestination(CLUSTER_ID, BACKUP_ID) + .setExpireTime(EXPIRE_TIME); + + assertThat(request.hashCode()) + .isEqualTo( + CopyBackupRequest.of(SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID) + .setSourceInstance(SOURCE_PROJECT_ID, SOURCE_INSTANCE_ID) + .setDestination(CLUSTER_ID, BACKUP_ID) + .setExpireTime(EXPIRE_TIME) + .hashCode()); + assertThat(request.hashCode()) + .isNotEqualTo( + CopyBackupRequest.of(SOURCE_CLUSTER_ID, SOURCE_BACKUP_ID) + .setSourceInstance(SOURCE_PROJECT_ID, SOURCE_INSTANCE_ID) + .setDestination(CLUSTER_ID, "another-backup") + .setExpireTime(EXPIRE_TIME) + .hashCode()); + } +}