8000 fix: bucket.delete(force=True) now works with version-enabled buckets… · googleapis/python-storage@0de09d3 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0de09d3

Browse files
authored
fix: bucket.delete(force=True) now works with version-enabled buckets (#1172)
Fixes #1071 🦕 As a side-effect, the behavior of this method during a race condition has changed slightly. Previously, if a new object was created while the bucket.delete(force=True) method is running, it would fail, but if a new generation of an existing object was uploaded, it would still succeed. Now it will fail in both cases. Regardless of the exact behavior, please do not use this method on a bucket that is still being updated by another process.
1 parent 0a243fa commit 0de09d3

File tree

3 files changed

+52
-2
lines changed

3 files changed

+52
-2
lines changed

google/cloud/storage/bucket.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1539,6 +1539,7 @@ def delete(
15391539
client=client,
15401540
timeout=timeout,
15411541
retry=retry,
1542+
versions=True,
15421543
)
15431544
)
15441545
if len(blobs) > self._MAX_OBJECTS_FOR_ITERATION:
@@ -1557,6 +1558,7 @@ def delete(
15571558
client=client,
15581559
timeout=timeout,
15591560
retry=retry,
1561+
preserve_generation=True,
15601562
)
15611563

15621564
# We intentionally pass `_target_object=None` since a DELETE

tests/system/test_bucket.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,6 +1069,48 @@ def test_new_bucket_with_autoclass(
10691069
assert bucket.autoclass_toggle_time != previous_toggle_time
10701070

10711071

1072+
def test_bucket_delete_force(storage_client):
1073+
bucket_name = _helpers.unique_name("version-disabled")
1074+
bucket_obj = storage_client.bucket(bucket_name)
1075+
bucket = storage_client.create_bucket(bucket_obj)
1076+
1077+
BLOB_NAME = "my_object"
1078+
blob = bucket.blob(BLOB_NAME)
1079+
blob.upload_from_string("abcd")
1080+
blob.upload_from_string("efgh")
1081+
1082+
blobs = bucket.list_blobs(versions=True)
1083+
counter = 0
1084+
for blob in blobs:
1085+
counter += 1
1086+
assert blob.name == BLOB_NAME
1087+
assert counter == 1
1088+
1089+
bucket.delete(force=True) # Will fail with 409 if blobs aren't deleted
1090+
1091+
1092+
def test_bucket_delete_force_works_with_versions(storage_client):
1093+
bucket_name = _helpers.unique_name("version-enabled")
1094+
bucket_obj = storage_client.bucket(bucket_name)
1095+
bucket_obj.versioning_enabled = True
1096+
bucket = storage_client.create_bucket(bucket_obj)
1097+
assert bucket.versioning_enabled
1098+
1099+
BLOB_NAME = "my_versioned_object"
1100+
blob = bucket.blob(BLOB_NAME)
1101+
blob.upload_from_string("abcd")
1102+
blob.upload_from_string("efgh")
1103+
1104+
blobs = bucket.list_blobs(versions=True)
1105+
counter = 0
1106+
for blob in blobs:
1107+
counter += 1
1108+
assert blob.name == BLOB_NAME
1109+
assert counter == 2
1110+
1111+
bucket.delete(force=True) # Will fail with 409 if versions aren't deleted
1112+
1113+
10721114
def test_config_autoclass_w_existing_bucket(
10731115
storage_client,
10741116
buckets_to_delete,

tests/unit/test_bucket.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,6 +1419,7 @@ def test_delete_hit_w_force_w_user_project_w_explicit_timeout_retry(self):
14191419
client=client,
14201420
timeout=timeout,
14211421
retry=retry,
1422+
versions=True,
14221423
)
14231424

14241425
bucket.delete_blobs.assert_called_once_with(
@@ -1427,6 +1428,7 @@ def test_delete_hit_w_force_w_user_project_w_explicit_timeout_retry(self):
14271428
client=client,
14281429
timeout=timeout,
14291430
retry=retry,
1431+
preserve_generation=True,
14301432
)
14311433

14321434
expected_query_params = {"userProject": user_project}
@@ -1456,6 +1458,7 @@ def test_delete_hit_w_force_delete_blobs(self):
14561458
client=client,
14571459
timeout=self._get_default_timeout(),
14581460
retry=DEFAULT_RETRY,
1461+
versions=True,
14591462
)
14601463

14611464
bucket.delete_blobs.assert_called_once_with(
@@ -1464,6 +1467,7 @@ def test_delete_hit_w_force_delete_blobs(self):
14641467
client=client,
14651468
timeout=self._get_default_timeout(),
14661469
retry=DEFAULT_RETRY,
1470+
preserve_generation=True,
14671471
)
14681472

14691473
expected_query_params = {}
@@ -1483,8 +1487,10 @@ def test_delete_w_force_w_user_project_w_miss_on_blob(self):
14831487
client = mock.Mock(spec=["_delete_resource"])
14841488
client._delete_resource.return_value = None
14851489
bucket = self._make_one(client=client, name=name)
1486-
blob = mock.Mock(spec=["name"])
1490+
blob = mock.Mock(spec=["name", "generation"])
14871491
blob.name = blob_name
1492+
GEN = 1234
1493+
blob.generation = GEN
14881494
blobs = [blob]
14891495
bucket.list_blobs = mock.Mock(return_value=iter(blobs))
14901496
bucket.delete_blob = mock.Mock(side_effect=NotFound("testing"))
@@ -1496,7 +1502,7 @@ def test_delete_w_force_w_user_project_w_miss_on_blob(self):
14961502
bucket.delete_blob.assert_called_once_with(
14971503
blob_name,
14981504
client=client,
1499-
generation=< 4DAA /span>None,
1505+
generation=GEN,
15001506
if_generation_match=None,
15011507
if_generation_not_match=None,
15021508
if_metageneration_match=None,

0 commit comments

Comments
 (0)
0