Skip to content

Commit b1eaaa5

Browse files
committed
feat(default_retention_period): Changes to backup.create API to support null expire time
With the default retention period feature, null value for expiration time is a valid value. If the expiration time is not specified (null) at the time of create backup then the backup will be retained for the duration of maximum retention period. In this commit, the precondition check on expiration time in the create backup API path is removed. The condition remains intact for copy backup API and that would be addressed in a follow-up commit.
1 parent 72a56ac commit b1eaaa5

File tree

4 files changed

+80
-9
lines changed

4 files changed

+80
-9
lines changed

google/cloud/spanner_v1/backup.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ class Backup(object):
5353
5454
:type expire_time: :class:`datetime.datetime`
5555
:param expire_time: (Optional) The expire time that will be used to
56-
create the backup. Required if the create method
57-
needs to be called.
56+
create the backup. If the expire time is not specified
57+
then the backup will be retained for the duration of
58+
maximum retention period.
5859
5960
:type version_time: :class:`datetime.datetime`
6061
:param version_time: (Optional) The version time that was specified for
@@ -271,8 +272,6 @@ def create(self):
271272
:raises BadRequest: if the database or expire_time values are invalid
272273
or expire_time is not set
273274
"""
274-
if not self._expire_time:
275-
raise ValueError("expire_time not set")
276275

277276
if not self._database and not self._source_backup:
278277
raise ValueError("database and source backup both not set")
@@ -295,6 +294,9 @@ def create(self):
295294
metadata = _metadata_with_prefix(self.name)
296295

297296
if self._source_backup:
297+
if not self._expire_time:
298+
raise ValueError("expire_time not set")
299+
298300
request = CopyBackupRequest(
299301
parent=self._instance.name,
300302
backup_id=self.backup_id,

google/cloud/spanner_v1/instance.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,8 @@ def backup(
532532
:type expire_time: :class:`datetime.datetime`
533533
:param expire_time:
534534
Optional. The expire time that will be used when creating the backup.
535-
Required if the create method needs to be called.
535+
If the expire time is not specified then the backup will be retained
536+
for the duration of maximum retention period.
536537
537538
:type version_time: :class:`datetime.datetime`
538539
:param version_time:

tests/unit/test_backup.py

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ class _BaseTest(unittest.TestCase):
2727
DATABASE_NAME = INSTANCE_NAME + "/databases/" + DATABASE_ID
2828
BACKUP_ID = "backup_id"
2929
BACKUP_NAME = INSTANCE_NAME + "/backups/" + BACKUP_ID
30+
DST_BACKUP_ID = "dst_backup_id"
31+
DST_BACKUP_NAME = INSTANCE_NAME + "/backups" + DST_BACKUP_ID
3032

3133
def _make_one(self, *args, **kwargs):
3234
return self._get_target_class()(*args, **kwargs)
@@ -329,11 +331,45 @@ def test_create_instance_not_found(self):
329331
)
330332

331333
def test_create_expire_time_not_set(self):
332-
instance = _Instance(self.INSTANCE_NAME)
333-
backup = self._make_one(self.BACKUP_ID, instance, database=self.DATABASE_NAME)
334+
from google.cloud.spanner_admin_database_v1 import Backup
335+
from google.cloud.spanner_admin_database_v1 import CreateBackupRequest
336+
from datetime import datetime
337+
from datetime import timedelta
338+
from datetime import timezone
334339

335-
with self.assertRaises(ValueError):
336-
backup.create()
340+
op_future = object()
341+
client = _Client()
342+
api = client.database_admin_api = self._make_database_admin_api()
343+
api.create_backup.return_value = op_future
344+
345+
instance = _Instance(self.INSTANCE_NAME, client=client)
346+
version_timestamp = datetime.utcnow() - timedelta(minutes=5)
347+
version_timestamp = version_timestamp.replace(tzinfo=timezone.utc)
348+
backup = self._make_one(
349+
self.BACKUP_ID,
350+
instance,
351+
database=self.DATABASE_NAME,
352+
version_time=version_timestamp,
353+
)
354+
355+
backup_pb = Backup(
356+
database=self.DATABASE_NAME,
357+
version_time=version_timestamp,
358+
)
359+
360+
future = backup.create()
361+
self.assertIs(future, op_future)
362+
363+
request = CreateBackupRequest(
364+
parent=self.INSTANCE_NAME,
365+
backup_id=self.BACKUP_ID,
366+
backup=backup_pb,
367+
)
368+
369+
api.create_backup.assert_called_once_with(
370+
request=request,
371+
metadata=[("google-cloud-resource-prefix", backup.name)],
372+
)
337373

338374
def test_create_database_not_set(self):
339375
instance = _Instance(self.INSTANCE_NAME)
@@ -413,6 +449,20 @@ def test_create_w_invalid_encryption_config(self):
413449
with self.assertRaises(ValueError):
414450
backup.create()
415451

452+
def test_copy_expire_time_not_set(self):
453+
client = _Client()
454+
client.database_admin_api = self._make_database_admin_api()
455+
instance = _Instance(self.INSTANCE_NAME, client=client)
456+
backup = self._make_one(
457+
self.DST_BACKUP_ID,
458+
instance,
459+
database=self.DATABASE_NAME,
460+
source_backup=self.BACKUP_ID,
461+
)
462+
463+
with self.assertRaises(ValueError):
464+
backup.create()
465+
416466
def test_exists_grpc_error(self):
417467
from google.api_core.exceptions import Unknown
418468

tests/unit/test_instance.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,24 @@ def test_backup_factory_explicit(self):
695695
self.assertIs(backup._expire_time, timestamp)
696696
self.assertEqual(backup._encryption_config, encryption_config)
697697

698+
def test_backup_factory_without_expiration_time(self):
699+
from google.cloud.spanner_v1.backup import Backup
700+
701+
client = _Client(self.PROJECT)
702+
instance = self._make_one(self.INSTANCE_ID, client, self.CONFIG_NAME)
703+
BACKUP_ID = "backup-id"
704+
DATABASE_NAME = "database-name"
705+
706+
backup = instance.backup(
707+
BACKUP_ID,
708+
database=DATABASE_NAME,
709+
)
710+
711+
self.assertIsInstance(backup, Backup)
712+
self.assertEqual(backup.backup_id, BACKUP_ID)
713+
self.assertIs(backup._instance, instance)
714+
self.assertEqual(backup._database, DATABASE_NAME)
715+
698716
def test_list_backups_defaults(self):
699717
from google.cloud.spanner_admin_database_v1 import Backup as BackupPB
700718
from google.cloud.spanner_admin_database_v1 import DatabaseAdminClient

0 commit comments

Comments
 (0)