Skip to content

Commit 558a408

Browse files
TracyCuiCanTracy Cui
and
Tracy Cui
authored
feat: copy backup API support (#1398)
* feat: copy backup API support * revert auto gen changes/files for copy backup API * Revert "revert auto gen changes/files for copy backup API" This reverts commit b6efd2d. * address comments * Revert "Revert "revert auto gen changes/files for copy backup API"" This reverts commit f446cd0. * change params order in private CopyBackupRequest and remove dependency-reduced-pom.xml * Rename variables in CopyBackupRequest to reduce confusion. * update request code * udpate test * update CopyBackup method * remove dead code * format new files --------- Co-authored-by: Tracy Cui <tracycui@google.com>
1 parent d6e934f commit 558a408

File tree

8 files changed

+625
-0
lines changed

8 files changed

+625
-0
lines changed

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClient.java

+83
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient.ListTablesPagedResponse;
4141
import com.google.cloud.bigtable.admin.v2.internal.NameUtil;
4242
import com.google.cloud.bigtable.admin.v2.models.Backup;
43+
import com.google.cloud.bigtable.admin.v2.models.CopyBackupRequest;
4344
import com.google.cloud.bigtable.admin.v2.models.CreateBackupRequest;
4445
import com.google.cloud.bigtable.admin.v2.models.CreateTableRequest;
4546
import com.google.cloud.bigtable.admin.v2.models.EncryptionInfo;
@@ -1317,6 +1318,88 @@ public ApiFuture<Void> awaitOptimizeRestoredTableAsync(
13171318
stub.awaitOptimizeRestoredTableCallable().resumeFutureCall(token.getOperationName()));
13181319
}
13191320

1321+
/**
1322+
* Copy an existing backup to a new backup in a Cloud Bigtable cluster with the specified
1323+
* configuration.
1324+
*
1325+
* <p>Sample code Note: You want to create the client with project and instance where you want the
1326+
* new backup to be copied to.
1327+
*
1328+
* <pre>{@code
1329+
* BigtableTableAdminClient client = BigtableTableAdminClient.create("[PROJECT]", "[INSTANCE]");
1330+
* CopyBackupRequest request =
1331+
* CopyBackupRequest.of(sourceClusterId, sourceBackupId)
1332+
* .setDestination(clusterId, backupId)
1333+
* .setExpireTime(expireTime);
1334+
* Backup response = client.copyBackup(request);
1335+
* }</pre>
1336+
*
1337+
* If the source backup is located in a different instance
1338+
*
1339+
* <pre>{@code
1340+
* CopyBackupRequest request =
1341+
* CopyBackupRequest.of(sourceClusterId, sourceBackupId)
1342+
* .setSourceInstance(sourceInstanceId)
1343+
* .setDestination(clusterId, backupId)
1344+
* .setExpireTime(expireTime);
1345+
* Backup response = client.copyBackup(request);
1346+
* }</pre>
1347+
*
1348+
* If the source backup is located in a different project
1349+
*
1350+
* <pre>{@code
1351+
* CopyBackupRequest request =
1352+
* CopyBackupRequest.of(sourceClusterId, sourceBackupId)
1353+
* .setSourceInstance(sourceProjectId, sourceInstanceId)
1354+
* .setDestination(clusterId, backupId)
1355+
* .setExpireTime(expireTime);
1356+
* Backup response = client.copyBackup(request);
1357+
* }</pre>
1358+
*/
1359+
public Backup copyBackup(CopyBackupRequest request) {
1360+
return ApiExceptions.callAndTranslateApiException(copyBackupAsync(request));
1361+
}
1362+
1363+
/**
1364+
* Creates a copy of a backup from an existing backup in a Cloud Bigtable cluster with the
1365+
* specified configuration asynchronously.
1366+
*
1367+
* <p>Sample code
1368+
*
1369+
* <pre>{@code
1370+
* CopyBackupRequest request =
1371+
* CopyBackupRequest.of(sourceClusterId, sourceBackupId)
1372+
* .setDestination(clusterId, backupId)
1373+
* .setExpireTime(expireTime);
1374+
* ApiFuture<Backup> future = client.copyBackupAsync(request);
1375+
*
1376+
* ApiFutures.addCallback(
1377+
* future,
1378+
* new ApiFutureCallback<Backup>() {
1379+
* public void onSuccess(Backup backup) {
1380+
* System.out.println("Successfully create the backup " + backup.getId());
1381+
* }
1382+
*
1383+
* public void onFailure(Throwable t) {
1384+
* t.printStackTrace();
1385+
* }
1386+
* },
1387+
* MoreExecutors.directExecutor()
1388+
* );
1389+
* }</pre>
1390+
*/
1391+
public ApiFuture<Backup> copyBackupAsync(CopyBackupRequest request) {
1392+
return ApiFutures.transform(
1393+
stub.copyBackupOperationCallable().futureCall(request.toProto(projectId, instanceId)),
1394+
new ApiFunction<com.google.bigtable.admin.v2.Backup, Backup>() {
1395+
@Override
1396+
public Backup apply(com.google.bigtable.admin.v2.Backup backupProto) {
1397+
return Backup.fromProto(backupProto);
1398+
}
1399+
},
1400+
MoreExecutors.directExecutor());
1401+
}
1402+
13201403
/**
13211404
* Returns a future that is resolved when replication has caught up to the point when this method
13221405
* was called. This allows callers to make sure that their mutations have been replicated across

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminSettings.java

+2
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ public String toString() {
111111
.add("getSnapshotSettings", stubSettings.getSnapshotSettings())
112112
.add("listSnapshotsSettings", stubSettings.listSnapshotsSettings())
113113
.add("deleteSnapshotSettings", stubSettings.deleteSnapshotSettings())
114+
.add("copyBackupSettings", stubSettings.copyBackupSettings())
115+
.add("copyBackupOperationSettings", stubSettings.copyBackupOperationSettings())
114116
.add("createBackupSettings", stubSettings.createBackupSettings())
115117
.add("createBackupOperationSettings", stubSettings.createBackupOperationSettings())
116118
.add("getBackupSettings", stubSettings.getBackupSettings())

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/Backup.java

+5
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ public String getSourceTableId() {
106106
return NameUtil.extractTableIdFromTableName(proto.getSourceTable());
107107
}
108108

109+
/** Get the source backup ID from which the backup is copied. */
110+
public String getSourceBackupId() {
111+
return NameUtil.extractBackupIdFromBackupName(proto.getSourceBackup());
112+
}
113+
109114
/** Get the instance ID where this backup is located. */
110115
public String getInstanceId() {
111116
return instanceId;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
* Copyright 2022 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.bigtable.admin.v2.models;
17+
18+
import com.google.api.core.InternalApi;
19+
import com.google.cloud.bigtable.admin.v2.internal.NameUtil;
20+
import com.google.common.base.Objects;
21+
import com.google.common.base.Preconditions;
22+
import com.google.protobuf.util.Timestamps;
23+
import javax.annotation.Nonnull;
24+
import org.threeten.bp.Instant;
25+
26+
/** Build CopyBackupRequest for {@link com.google.bigtable.admin.v2.CopyBackupRequest}. */
27+
public final class CopyBackupRequest {
28+
private final com.google.bigtable.admin.v2.CopyBackupRequest.Builder requestBuilder =
29+
com.google.bigtable.admin.v2.CopyBackupRequest.newBuilder();
30+
private final String sourceBackupId;
31+
private final String sourceClusterId;
32+
private String sourceInstanceId;
33+
private String sourceProjectId;
34+
35+
private String destClusterId;
36+
37+
/**
38+
* Create a {@link CopyBackupRequest} object. It assumes the source backup is located in the same
39+
* instance and project as the destination backup, which is where the BigtableTableAdminClient is
40+
* created in. use setSourceInstance("[INSTANCE]") if the source backup is located in a different
41+
* instance. use setSourceInstance("[PROJECT]", "[INSTANCE]") if the source backup is located in a
42+
* different project.
43+
*/
44+
public static CopyBackupRequest of(String sourceClusterId, String sourceBackupId) {
45+
CopyBackupRequest request = new CopyBackupRequest(sourceClusterId, sourceBackupId);
46+
return request;
47+
}
48+
49+
private CopyBackupRequest(@Nonnull String sourceClusterId, @Nonnull String sourceBackupId) {
50+
Preconditions.checkNotNull(sourceClusterId);
51+
Preconditions.checkNotNull(sourceBackupId);
52+
this.sourceClusterId = sourceClusterId;
53+
this.sourceBackupId = sourceBackupId;
54+
}
55+
56+
public CopyBackupRequest setSourceInstance(String instanceId) {
57+
Preconditions.checkNotNull(instanceId);
58+
this.sourceInstanceId = instanceId;
59+
return this;
60+
}
61+
62+
public CopyBackupRequest setSourceInstance(String projectId, String instanceId) {
63+
Preconditions.checkNotNull(projectId);
64+
Preconditions.checkNotNull(instanceId);
65+
this.sourceProjectId = projectId;
66+
this.sourceInstanceId = instanceId;
67+
return this;
68+
}
69+
70+
public CopyBackupRequest setDestination(String clusterId, String backupId) {
71+
Preconditions.checkNotNull(backupId);
72+
Preconditions.checkNotNull(clusterId);
73+
requestBuilder.setBackupId(backupId);
74+
this.destClusterId = clusterId;
75+
return this;
76+
}
77+
78+
public CopyBackupRequest setExpireTime(Instant expireTime) {
79+
Preconditions.checkNotNull(expireTime);
80+
requestBuilder.setExpireTime(Timestamps.fromMillis(expireTime.toEpochMilli()));
81+
return this;
82+
}
83+
84+
@Override
85+
public boolean equals(Object o) {
86+
if (this == o) {
87+
return true;
88+
}
89+
if (o == null || getClass() != o.getClass()) {
90+
return false;
91+
}
92+
CopyBackupRequest that = (CopyBackupRequest) o;
93+
return Objects.equal(requestBuilder.getBackupId(), that.requestBuilder.getBackupId())
94+
&& Objects.equal(sourceBackupId, that.sourceBackupId)
95+
&& Objects.equal(sourceClusterId, that.sourceClusterId)
96+
&& Objects.equal(sourceInstanceId, that.sourceInstanceId)
97+
&& Objects.equal(sourceProjectId, that.sourceProjectId);
98+
}
99+
100+
@Override
101+
public int hashCode() {
102+
return Objects.hashCode(
103+
requestBuilder.getBackupId(),
104+
sourceBackupId,
105+
sourceClusterId,
106+
sourceInstanceId,
107+
sourceProjectId);
108+
}
109+
110+
@InternalApi
111+
public com.google.bigtable.admin.v2.CopyBackupRequest toProto(
112+
@Nonnull String projectId, @Nonnull String instanceId) {
113+
Preconditions.checkNotNull(projectId);
114+
Preconditions.checkNotNull(instanceId);
115+
116+
return requestBuilder
117+
.setParent(NameUtil.formatClusterName(projectId, instanceId, destClusterId))
118+
.setSourceBackup(
119+
NameUtil.formatBackupName(
120+
sourceProjectId == null ? projectId : sourceProjectId,
121+
sourceInstanceId == null ? instanceId : sourceInstanceId,
122+
sourceClusterId,
123+
sourceBackupId))
124+
.build();
125+
}
126+
}

google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientTests.java

+76
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.google.bigtable.admin.v2.BackupInfo;
3232
import com.google.bigtable.admin.v2.ChangeStreamConfig;
3333
import com.google.bigtable.admin.v2.ColumnFamily;
34+
import com.google.bigtable.admin.v2.CopyBackupMetadata;
3435
import com.google.bigtable.admin.v2.CreateBackupMetadata;
3536
import com.google.bigtable.admin.v2.DeleteBackupRequest;
3637
import com.google.bigtable.admin.v2.DeleteTableRequest;
@@ -56,6 +57,7 @@
5657
import com.google.cloud.bigtable.admin.v2.BaseBigtableTableAdminClient.ListTablesPagedResponse;
5758
import com.google.cloud.bigtable.admin.v2.internal.NameUtil;
5859
import com.google.cloud.bigtable.admin.v2.models.Backup;
60+
import com.google.cloud.bigtable.admin.v2.models.CopyBackupRequest;
5961
import com.google.cloud.bigtable.admin.v2.models.CreateBackupRequest;
6062
import com.google.cloud.bigtable.admin.v2.models.CreateTableRequest;
6163
import com.google.cloud.bigtable.admin.v2.models.EncryptionInfo;
@@ -172,6 +174,13 @@ public class BigtableTableAdminClientTests {
172174
RestoreTableMetadata>
173175
mockRestoreTableOperationCallable;
174176

177+
@Mock
178+
private OperationCallable<
179+
com.google.bigtable.admin.v2.CopyBackupRequest,
180+
com.google.bigtable.admin.v2.Backup,
181+
CopyBackupMetadata>
182+
mockCopyBackupOperationCallable;
183+
175184
@Mock
176185
private UnaryCallable<com.google.iam.v1.GetIamPolicyRequest, com.google.iam.v1.Policy>
177186
mockGetIamPolicyCallable;
@@ -775,6 +784,73 @@ public void testListBackups() {
775784
assertThat(actualResults).containsExactlyElementsIn(expectedResults);
776785
}
777786

787+
@Test
788+
public void testCopyBackup() {
789+
// Setup
790+
Mockito.when(mockStub.copyBackupOperationCallable())
791+
.thenReturn(mockCopyBackupOperationCallable);
792+
793+
Timestamp startTime = Timestamp.newBuilder().setSeconds(1234).build();
794+
Timestamp endTime = Timestamp.newBuilder().setSeconds(5678).build();
795+
796+
// Create CopyBackupRequest from different source project:
797+
String srcProjectId = "src-project";
798+
String srcInstanceId = "src-instance";
799+
String srcTableId = "src-table";
800+
String srcClusterId = "src-cluster";
801+
String srcBackupId = "src-backup";
802+
Instant expireTime = Instant.now().plus(org.threeten.bp.Duration.ofDays(15));
803+
long sizeBytes = 123456789;
804+
805+
String dstBackupName =
806+
NameUtil.formatBackupName(PROJECT_ID, INSTANCE_ID, CLUSTER_ID, BACKUP_ID);
807+
String srcBackupName =
808+
NameUtil.formatBackupName(srcProjectId, srcProjectId, srcClusterId, srcBackupId);
809+
String srcTableName = NameUtil.formatTableName(srcProjectId, srcInstanceId, srcTableId);
810+
811+
CopyBackupRequest req =
812+
CopyBackupRequest.of(srcClusterId, srcBackupId)
813+
.setSourceInstance(srcProjectId, srcInstanceId)
814+
.setDestination(CLUSTER_ID, BACKUP_ID)
815+
.setExpireTime(expireTime);
816+
mockOperationResult(
817+
mockCopyBackupOperationCallable,
818+
req.toProto(PROJECT_ID, INSTANCE_ID),
819+
com.google.bigtable.admin.v2.Backup.newBuilder()
820+
.setName(dstBackupName)
821+
.setSourceTable(srcTableName)
822+
.setSourceBackup(srcBackupName)
823+
.setStartTime(startTime)
824+
.setEndTime(endTime)
825+
.setExpireTime(Timestamps.fromMillis(expireTime.toEpochMilli()))
826+
.setSizeBytes(sizeBytes)
827+
.build(),
828+
CopyBackupMetadata.newBuilder()
829+
.setName(dstBackupName)
830+
.setSourceBackupInfo(
831+
BackupInfo.newBuilder()
832+
.setBackup(srcBackupId)
833+
.setSourceTable(srcTableName)
834+
.setStartTime(startTime)
835+
.setEndTime(endTime)
836+
.build())
837+
.build());
838+
839+
// Execute
840+
Backup actualResult = adminClient.copyBackup(req);
841+
842+
// Verify
843+
assertThat(actualResult.getId()).isEqualTo(BACKUP_ID);
844+
assertThat(actualResult.getSourceTableId()).isEqualTo(srcTableId);
845+
assertThat(actualResult.getSourceBackupId()).isEqualTo(srcBackupId);
846+
assertThat(actualResult.getStartTime())
847+
.isEqualTo(Instant.ofEpochMilli(Timestamps.toMillis(startTime)));
848+
assertThat(actualResult.getEndTime())
849+
.isEqualTo(Instant.ofEpochMilli(Timestamps.toMillis(endTime)));
850+
assertThat(actualResult.getExpireTime()).isEqualTo(expireTime);
851+
assertThat(actualResult.getSizeBytes()).isEqualTo(sizeBytes);
852+
}
853+
778854
@Test
779855
public void testGetBackupIamPolicy() {
780856
// Setup

0 commit comments

Comments
 (0)