Skip to content

Commit 97faf6c

Browse files
asthamohtarenovate-botgcf-owl-bot[bot]
authored
feat: add support for spanner copy backup feature (#600)
* changes for copy backup feature * changes to test case * changes to documenttation * feat: changes as per review, adding shared_backup * changes for cross region backup * samples: changes to list backup operations * chore(deps): update all dependencies (#689) * chore(deps): update dependency pytest to v7.1.1 (#690) * feat: add support for Cross region backup proto changes (#691) * Synchronize new proto/yaml changes. PiperOrigin-RevId: 436114471 Source-Link: googleapis/googleapis@6379d5f Source-Link: https://github.com/googleapis/googleapis-gen/commit/a59984b4cb711eeb186bca4f5b35adbfe60825df Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiYTU5OTg0YjRjYjcxMWVlYjE4NmJjYTRmNWIzNWFkYmZlNjA4MjVkZiJ9 * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com> * feat: adding samples * linting Co-authored-by: WhiteSource Renovate <bot@renovateapp.com> Co-authored-by: gcf-owl-bot[bot] <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent 8ac62cb commit 97faf6c

File tree

13 files changed

+395
-100
lines changed

13 files changed

+395
-100
lines changed

google/cloud/spanner_v1/backup.py

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
from google.cloud.spanner_admin_database_v1 import Backup as BackupPB
2222
from google.cloud.spanner_admin_database_v1 import CreateBackupEncryptionConfig
2323
from google.cloud.spanner_admin_database_v1 import CreateBackupRequest
24+
from google.cloud.spanner_admin_database_v1 import CopyBackupEncryptionConfig
25+
from google.cloud.spanner_admin_database_v1 import CopyBackupRequest
2426
from google.cloud.spanner_v1._helpers import _metadata_with_prefix
2527

2628
_BACKUP_NAME_RE = re.compile(
@@ -77,19 +79,30 @@ def __init__(
7779
expire_time=None,
7880
version_time=None,
7981
encryption_config=None,
82+
source_backup=None,
8083
):
8184
self.backup_id = backup_id
8285
self._instance = instance
8386
self._database = database
87+
self._source_backup = source_backup
8488
self._expire_time = expire_time
8589
self._create_time = None
8690
self._version_time = version_time
8791
self._size_bytes = None
8892
self._state = None
8993
self._referencing_databases = None
9094
self._encryption_info = None
95+
self._max_expire_time = None
96+
self._referencing_backups = None
9197
if type(encryption_config) == dict:
92-
self._encryption_config = CreateBackupEncryptionConfig(**encryption_config)
98+
if source_backup:
99+
self._encryption_config = CopyBackupEncryptionConfig(
100+
**encryption_config
101+
)
102+
else:
103+
self._encryption_config = CreateBackupEncryptionConfig(
104+
**encryption_config
105+
)
93106
else:
94107
self._encryption_config = encryption_config
95108

@@ -185,6 +198,24 @@ def encryption_info(self):
185198
"""
186199
return self._encryption_info
187200

201+
@property
202+
def max_expire_time(self):
203+
"""The max allowed expiration time of the backup.
204+
:rtype: :class:`datetime.datetime`
205+
:returns: a datetime object representing the max expire time of
206+
this backup
207+
"""
208+
return self._max_expire_time
209+
210+
@property
211+
def referencing_backups(self):
212+
"""The names of the destination backups being created by copying this source backup.
213+
:rtype: list of strings
214+
:returns: a list of backup path strings which specify the backups that are
215+
referencing this copy backup
216+
"""
217+
return self._referencing_backups
218+
188219
@classmethod
189220
def from_pb(cls, backup_pb, instance):
190221
"""Create an instance of this class from a protobuf message.
@@ -223,7 +254,7 @@ def from_pb(cls, backup_pb, instance):
223254
return cls(backup_id, instance)
224255

225256
def create(self):
226-
"""Create this backup within its instance.
257+
"""Create this backup or backup copy within its instance.
227258
228259
:rtype: :class:`~google.api_core.operation.Operation`
229260
:returns: a future used to poll the status of the create request
@@ -234,17 +265,39 @@ def create(self):
234265
"""
235266
if not self._expire_time:
236267
raise ValueError("expire_time not set")
237-
if not self._database:
238-
raise ValueError("database not set")
268+
269+
if not self._database and not self._source_backup:
270+
raise ValueError("database and source backup both not set")
271+
239272
if (
240-
self._encryption_config
273+
(
274+
self._encryption_config
275+
and self._encryption_config.kms_key_name
276+
and self._encryption_config.encryption_type
277+
!= CreateBackupEncryptionConfig.EncryptionType.CUSTOMER_MANAGED_ENCRYPTION
278+
)
279+
and self._encryption_config
241280
and self._encryption_config.kms_key_name
242281
and self._encryption_config.encryption_type
243-
!= CreateBackupEncryptionConfig.EncryptionType.CUSTOMER_MANAGED_ENCRYPTION
282+
!= CopyBackupEncryptionConfig.EncryptionType.CUSTOMER_MANAGED_ENCRYPTION
244283
):
245284
raise ValueError("kms_key_name only used with CUSTOMER_MANAGED_ENCRYPTION")
285+
246286
api = self._instance._client.database_admin_api
247287
metadata = _metadata_with_prefix(self.name)
288+
289+
if self._source_backup:
290+
request = CopyBackupRequest(
291+
parent=self._instance.name,
292+
backup_id=self.backup_id,
293+
source_backup=self._source_backup,
294+
expire_time=self._expire_time,
295+
encryption_config=self._encryption_config,
296+
)
297+
298+
future = api.copy_backup(request=request, metadata=metadata,)
299+
return future
300+
248301
backup = BackupPB(
249302
database=self._database,
250303
expire_time=self.expire_time,
@@ -294,6 +347,8 @@ def reload(self):
294347
self._state = BackupPB.State(pb.state)
295348
self._referencing_databases = pb.referencing_databases
296349
self._encryption_info = pb.encryption_info
350+
self._max_expire_time = pb.max_expire_time
351+
self._referencing_backups = pb.referencing_backups
297352

298353
def update_expire_time(self, new_expire_time):
299354
"""Update the expire time of this backup.

google/cloud/spanner_v1/instance.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
_OPERATION_METADATA_MESSAGES = (
4545
backup.Backup,
4646
backup.CreateBackupMetadata,
47+
backup.CopyBackupMetadata,
4748
spanner_database_admin.CreateDatabaseMetadata,
4849
spanner_database_admin.Database,
4950
spanner_database_admin.OptimizeRestoredDatabaseMetadata,
@@ -58,6 +59,7 @@
5859

5960
_OPERATION_RESPONSE_TYPES = {
6061
backup.CreateBackupMetadata: backup.Backup,
62+
backup.CopyBackupMetadata: backup.Backup,
6163
spanner_database_admin.CreateDatabaseMetadata: spanner_database_admin.Database,
6264
spanner_database_admin.OptimizeRestoredDatabaseMetadata: spanner_database_admin.Database,
6365
spanner_database_admin.RestoreDatabaseMetadata: spanner_database_admin.Database,
@@ -551,6 +553,37 @@ def backup(
551553
encryption_config=encryption_config,
552554
)
553555

556+
def copy_backup(
557+
self, backup_id, source_backup, expire_time=None, encryption_config=None,
558+
):
559+
"""Factory to create a copy backup within this instance.
560+
561+
:type backup_id: str
562+
:param backup_id: The ID of the backup copy.
563+
:type source_backup: str
564+
:param source_backup_id: The full path of the source backup to be copied.
565+
:type expire_time: :class:`datetime.datetime`
566+
:param expire_time:
567+
Optional. The expire time that will be used when creating the copy backup.
568+
Required if the create method needs to be called.
569+
:type encryption_config:
570+
:class:`~google.cloud.spanner_admin_database_v1.types.CopyBackupEncryptionConfig`
571+
or :class:`dict`
572+
:param encryption_config:
573+
(Optional) Encryption configuration for the backup.
574+
If a dict is provided, it must be of the same form as the protobuf
575+
message :class:`~google.cloud.spanner_admin_database_v1.types.CopyBackupEncryptionConfig`
576+
:rtype: :class:`~google.cloud.spanner_v1.backup.Backup`
577+
:returns: a copy backup owned by this instance.
578+
"""
579+
return Backup(
580+
backup_id,
581+
self,
582+
source_backup=source_backup,
583+
expire_time=expire_time,
584+
encryption_config=encryption_config,
585+
)
586+
554587
def list_backups(self, filter_="", page_size=None):
555588
"""List backups for the instance.
556589

samples/samples/autocommit.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,11 @@ def enable_autocommit_mode(instance_id, database_id):
4646

4747
if __name__ == "__main__":
4848
parser = argparse.ArgumentParser(
49-
description=__doc__,
50-
formatter_class=argparse.RawDescriptionHelpFormatter,
49+
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter,
5150
)
5251
parser.add_argument("instance_id", help="Your Cloud Spanner instance ID.")
5352
parser.add_argument(
54-
"--database-id",
55-
help="Your Cloud Spanner database ID.",
56-
default="example_db",
53+
"--database-id", help="Your Cloud Spanner database ID.", default="example_db",
5754
)
5855
subparsers = parser.add_subparsers(dest="command")
5956
subparsers.add_parser("enable_autocommit_mode", help=enable_autocommit_mode.__doc__)

samples/samples/autocommit_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def sample_name():
1919
@RetryErrors(exception=Aborted, max_tries=2)
2020
def test_enable_autocommit_mode(capsys, instance_id, sample_database):
2121
# Delete table if it exists for retry attempts.
22-
table = sample_database.table('Singers')
22+
table = sample_database.table("Singers")
2323
if table.exists():
2424
op = sample_database.update_ddl(["DROP TABLE Singers"])
2525
op.result()

0 commit comments

Comments
 (0)