Skip to content

Commit 8f5e056

Browse files
authored
feat: backup restore to different instance (#300)
1 parent 2a7f91b commit 8f5e056

File tree

3 files changed

+57
-13
lines changed

3 files changed

+57
-13
lines changed

packages/google-cloud-bigtable/google/cloud/bigtable/backup.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -395,17 +395,25 @@ def delete(self):
395395
request={"name": self.name}
396396
)
397397

398-
def restore(self, table_id):
398+
def restore(self, table_id, instance_id=None):
399399
"""Creates a new Table by restoring from this Backup. The new Table
400-
must be in the same Instance as the Instance containing the Backup.
400+
can be created in the same Instance as the Instance containing the
401+
Backup, or another Instance whose ID can be specified in the arguments.
401402
The returned Table ``long-running operation`` can be used to track the
402403
progress of the operation and to cancel it. The ``response`` type is
403404
``Table``, if successful.
404405
406+
:type table_id: str
405407
:param table_id: The ID of the Table to create and restore to.
406408
This Table must not already exist.
407-
:returns: An instance of
408-
:class:`~google.cloud.bigtable_admin_v2.types._OperationFuture`.
409+
410+
:type instance_id: str
411+
:param instance_id: (Optional) The ID of the Instance to restore the
412+
backup into, if different from the current one.
413+
414+
:rtype: :class:`~google.cloud.bigtable_admin_v2.types._OperationFuture`
415+
:returns: A future to be used to poll the status of the 'restore'
416+
request.
409417
410418
:raises: google.api_core.exceptions.AlreadyExists: If the table
411419
already exists.
@@ -416,12 +424,15 @@ def restore(self, table_id):
416424
:raises: ValueError: If the parameters are invalid.
417425
"""
418426
api = self._instance._client._table_admin_client
427+
if instance_id:
428+
parent = BigtableTableAdminClient.instance_path(
429+
project=self._instance._client.project, instance=instance_id,
430+
)
431+
else:
432+
parent = self._instance.name
433+
419434
return api.restore_table(
420-
request={
421-
"parent": self._instance.name,
422-
"table_id": table_id,
423-
"backup": self.name,
424-
}
435+
request={"parent": parent, "table_id": table_id, "backup": self.name}
425436
)
426437

427438
def get_iam_policy(self):

packages/google-cloud-bigtable/tests/system.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1119,12 +1119,33 @@ def test_backup(self):
11191119
restored_table_id = "test-backup-table-restored"
11201120
restored_table = Config.INSTANCE_DATA.table(restored_table_id)
11211121
temp_table.restore(
1122-
restored_table_id, cluster_id=CLUSTER_ID_DATA, backup_id=temp_backup_id
1122+
restored_table_id, cluster_id=CLUSTER_ID_DATA, backup_id=temp_backup_id,
11231123
).result()
11241124
tables = Config.INSTANCE_DATA.list_tables()
11251125
self.assertIn(restored_table, tables)
11261126
restored_table.delete()
11271127

1128+
# Testing `Backup.restore()` into a different instance:
1129+
# Setting up another instance...
1130+
alt_instance_id = "gcp-alt-" + UNIQUE_SUFFIX
1131+
alt_cluster_id = alt_instance_id + "-cluster"
1132+
alt_instance = Config.CLIENT.instance(alt_instance_id, labels=LABELS)
1133+
alt_cluster = alt_instance.cluster(
1134+
cluster_id=alt_cluster_id, location_id=LOCATION_ID, serve_nodes=SERVE_NODES,
1135+
)
1136+
if not Config.IN_EMULATOR:
1137+
alt_instance.create(clusters=[alt_cluster]).result(timeout=10)
1138+
1139+
# Testing `restore()`...
1140+
temp_backup.restore(restored_table_id, alt_instance_id).result()
1141+
restored_table = alt_instance.table(restored_table_id)
1142+
self.assertIn(restored_table, alt_instance.list_tables())
1143+
restored_table.delete()
1144+
1145+
# Tearing down the resources...
1146+
if not Config.IN_EMULATOR:
1147+
retry_429(alt_instance.delete)()
1148+
11281149

11291150
class TestDataAPI(unittest.TestCase):
11301151
@classmethod

packages/google-cloud-bigtable/tests/unit/test_backup.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ class TestBackup(unittest.TestCase):
3232
BACKUP_ID = "backup-id"
3333
BACKUP_NAME = CLUSTER_NAME + "/backups/" + BACKUP_ID
3434

35+
ALT_INSTANCE = "other-instance-id"
36+
ALT_INSTANCE_NAME = "projects/" + PROJECT_ID + "/instances/" + ALT_INSTANCE
37+
ALT_CLUSTER_NAME = ALT_INSTANCE_NAME + "/clusters/" + CLUSTER_ID
38+
ALT_BACKUP_NAME = ALT_CLUSTER_NAME + "/backups/" + BACKUP_ID
39+
3540
@staticmethod
3641
def _get_target_class():
3742
from google.cloud.bigtable.backup import Backup
@@ -736,7 +741,7 @@ def test_restore_cluster_not_set(self):
736741
with self.assertRaises(ValueError):
737742
backup.restore(self.TABLE_ID)
738743

739-
def test_restore_success(self):
744+
def _restore_helper(self, instance_id=None, instance_name=None):
740745
op_future = object()
741746
client = _Client()
742747
api = client._table_admin_client = self._make_table_admin_client()
@@ -751,17 +756,24 @@ def test_restore_success(self):
751756
expire_time=timestamp,
752757
)
753758

754-
future = backup.restore(self.TABLE_ID)
759+
future = backup.restore(self.TABLE_ID, instance_id)
755760
self.assertEqual(backup._cluster, self.CLUSTER_ID)
756761
self.assertIs(future, op_future)
757762

758763
api.restore_table.assert_called_once_with(
759764
request={
760-
"parent": self.INSTANCE_NAME,
765+
"parent": instance_name or self.INSTANCE_NAME,
761766
"table_id": self.TABLE_ID,
762767
"backup": self.BACKUP_NAME,
763768
}
764769
)
770+
api.restore_table.reset_mock()
771+
772+
def test_restore_default(self):
773+
self._restore_helper()
774+
775+
def test_restore_to_another_instance(self):
776+
self._restore_helper(self.ALT_INSTANCE, self.ALT_INSTANCE_NAME)
765777

766778
def test_get_iam_policy(self):
767779
from google.cloud.bigtable.client import Client

0 commit comments

Comments
 (0)