Skip to content

Commit

Permalink
feat: add support for spanner copy backup feature (googleapis#600)
Browse files Browse the repository at this point in the history
* 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 (googleapis#689)

* chore(deps): update dependency pytest to v7.1.1 (googleapis#690)

* feat: add support for Cross region backup proto changes (googleapis#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>
  • Loading branch information
4 people authored and vi3k6i5 committed Mar 29, 2022
1 parent 1c33bdc commit 076fc0a
Show file tree
Hide file tree
Showing 13 changed files with 395 additions and 100 deletions.
67 changes: 61 additions & 6 deletions google/cloud/spanner_v1/backup.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
from google.cloud.spanner_admin_database_v1 import Backup as BackupPB
from google.cloud.spanner_admin_database_v1 import CreateBackupEncryptionConfig
from google.cloud.spanner_admin_database_v1 import CreateBackupRequest
from google.cloud.spanner_admin_database_v1 import CopyBackupEncryptionConfig
from google.cloud.spanner_admin_database_v1 import CopyBackupRequest
from google.cloud.spanner_v1._helpers import _metadata_with_prefix

_BACKUP_NAME_RE = re.compile(
Expand Down Expand Up @@ -77,19 +79,30 @@ def __init__(
expire_time=None,
version_time=None,
encryption_config=None,
source_backup=None,
):
self.backup_id = backup_id
self._instance = instance
self._database = database
self._source_backup = source_backup
self._expire_time = expire_time
self._create_time = None
self._version_time = version_time
self._size_bytes = None
self._state = None
self._referencing_databases = None
self._encryption_info = None
self._max_expire_time = None
self._referencing_backups = None
if type(encryption_config) == dict:
self._encryption_config = CreateBackupEncryptionConfig(**encryption_config)
if source_backup:
self._encryption_config = CopyBackupEncryptionConfig(
**encryption_config
)
else:
self._encryption_config = CreateBackupEncryptionConfig(
**encryption_config
)
else:
self._encryption_config = encryption_config

Expand Down Expand Up @@ -185,6 +198,24 @@ def encryption_info(self):
"""
return self._encryption_info

@property
def max_expire_time(self):
"""The max allowed expiration time of the backup.
:rtype: :class:`datetime.datetime`
:returns: a datetime object representing the max expire time of
this backup
"""
return self._max_expire_time

@property
def referencing_backups(self):
"""The names of the destination backups being created by copying this source backup.
:rtype: list of strings
:returns: a list of backup path strings which specify the backups that are
referencing this copy backup
"""
return self._referencing_backups

@classmethod
def from_pb(cls, backup_pb, instance):
"""Create an instance of this class from a protobuf message.
Expand Down Expand Up @@ -223,7 +254,7 @@ def from_pb(cls, backup_pb, instance):
return cls(backup_id, instance)

def create(self):
"""Create this backup within its instance.
"""Create this backup or backup copy within its instance.
:rtype: :class:`~google.api_core.operation.Operation`
:returns: a future used to poll the status of the create request
Expand All @@ -234,17 +265,39 @@ def create(self):
"""
if not self._expire_time:
raise ValueError("expire_time not set")
if not self._database:
raise ValueError("database not set")

if not self._database and not self._source_backup:
raise ValueError("database and source backup both not set")

if (
self._encryption_config
(
self._encryption_config
and self._encryption_config.kms_key_name
and self._encryption_config.encryption_type
!= CreateBackupEncryptionConfig.EncryptionType.CUSTOMER_MANAGED_ENCRYPTION
)
and self._encryption_config
and self._encryption_config.kms_key_name
and self._encryption_config.encryption_type
!= CreateBackupEncryptionConfig.EncryptionType.CUSTOMER_MANAGED_ENCRYPTION
!= CopyBackupEncryptionConfig.EncryptionType.CUSTOMER_MANAGED_ENCRYPTION
):
raise ValueError("kms_key_name only used with CUSTOMER_MANAGED_ENCRYPTION")

api = self._instance._client.database_admin_api
metadata = _metadata_with_prefix(self.name)

if self._source_backup:
request = CopyBackupRequest(
parent=self._instance.name,
backup_id=self.backup_id,
source_backup=self._source_backup,
expire_time=self._expire_time,
encryption_config=self._encryption_config,
)

future = api.copy_backup(request=request, metadata=metadata,)
return future

backup = BackupPB(
database=self._database,
expire_time=self.expire_time,
Expand Down Expand Up @@ -294,6 +347,8 @@ def reload(self):
self._state = BackupPB.State(pb.state)
self._referencing_databases = pb.referencing_databases
self._encryption_info = pb.encryption_info
self._max_expire_time = pb.max_expire_time
self._referencing_backups = pb.referencing_backups

def update_expire_time(self, new_expire_time):
"""Update the expire time of this backup.
Expand Down
33 changes: 33 additions & 0 deletions google/cloud/spanner_v1/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
_OPERATION_METADATA_MESSAGES = (
backup.Backup,
backup.CreateBackupMetadata,
backup.CopyBackupMetadata,
spanner_database_admin.CreateDatabaseMetadata,
spanner_database_admin.Database,
spanner_database_admin.OptimizeRestoredDatabaseMetadata,
Expand All @@ -59,6 +60,7 @@

_OPERATION_RESPONSE_TYPES = {
backup.CreateBackupMetadata: backup.Backup,
backup.CopyBackupMetadata: backup.Backup,
spanner_database_admin.CreateDatabaseMetadata: spanner_database_admin.Database,
spanner_database_admin.OptimizeRestoredDatabaseMetadata: spanner_database_admin.Database,
spanner_database_admin.RestoreDatabaseMetadata: spanner_database_admin.Database,
Expand Down Expand Up @@ -554,6 +556,37 @@ def backup(
encryption_config=encryption_config,
)

def copy_backup(
self, backup_id, source_backup, expire_time=None, encryption_config=None,
):
"""Factory to create a copy backup within this instance.
:type backup_id: str
:param backup_id: The ID of the backup copy.
:type source_backup: str
:param source_backup_id: The full path of the source backup to be copied.
:type expire_time: :class:`datetime.datetime`
:param expire_time:
Optional. The expire time that will be used when creating the copy backup.
Required if the create method needs to be called.
:type encryption_config:
:class:`~google.cloud.spanner_admin_database_v1.types.CopyBackupEncryptionConfig`
or :class:`dict`
:param encryption_config:
(Optional) Encryption configuration for the backup.
If a dict is provided, it must be of the same form as the protobuf
message :class:`~google.cloud.spanner_admin_database_v1.types.CopyBackupEncryptionConfig`
:rtype: :class:`~google.cloud.spanner_v1.backup.Backup`
:returns: a copy backup owned by this instance.
"""
return Backup(
backup_id,
self,
source_backup=source_backup,
expire_time=expire_time,
encryption_config=encryption_config,
)

def list_backups(self, filter_="", page_size=None):
"""List backups for the instance.
Expand Down
7 changes: 2 additions & 5 deletions samples/samples/autocommit.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,11 @@ def enable_autocommit_mode(instance_id, database_id):

if __name__ == "__main__":
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter,
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument("instance_id", help="Your Cloud Spanner instance ID.")
parser.add_argument(
"--database-id",
help="Your Cloud Spanner database ID.",
default="example_db",
"--database-id", help="Your Cloud Spanner database ID.", default="example_db",
)
subparsers = parser.add_subparsers(dest="command")
subparsers.add_parser("enable_autocommit_mode", help=enable_autocommit_mode.__doc__)
Expand Down
2 changes: 1 addition & 1 deletion samples/samples/autocommit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def sample_name():
@RetryErrors(exception=Aborted, max_tries=2)
def test_enable_autocommit_mode(capsys, instance_id, sample_database):
# Delete table if it exists for retry attempts.
table = sample_database.table('Singers')
table = sample_database.table("Singers")
if table.exists():
op = sample_database.update_ddl(["DROP TABLE Singers"])
op.result()
Expand Down
Loading

0 comments on commit 076fc0a

Please sign in to comment.