Skip to content

Commit cf38c8d

Browse files
committed
Merge pull request #878 from dhermes/limit-make-public
Limiting Bucket.make_public(recursive=True).
2 parents f3d8799 + 3868f81 commit cf38c8d

File tree

2 files changed

+55
-9
lines changed

2 files changed

+55
-9
lines changed

gcloud/storage/bucket.py

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,11 @@ class Bucket(_PropertyMixin):
101101
"""
102102
_iterator_class = _BlobIterator
103103

104-
_MAX_OBJECTS_FOR_BUCKET_DELETE = 256
105-
"""Maximum number of existing objects allowed in Bucket.delete()."""
104+
_MAX_OBJECTS_FOR_ITERATION = 256
105+
"""Maximum number of existing objects allowed in iteration.
106+
107+
This is used in Bucket.delete() and Bucket.make_public().
108+
"""
106109

107110
def __init__(self, name=None):
108111
super(Bucket, self).__init__(name=name)
@@ -349,15 +352,15 @@ def delete(self, force=False, connection=None):
349352
connection = _require_connection(connection)
350353
if force:
351354
blobs = list(self.list_blobs(
352-
max_results=self._MAX_OBJECTS_FOR_BUCKET_DELETE + 1,
355+
max_results=self._MAX_OBJECTS_FOR_ITERATION + 1,
353356
connection=connection))
354-
if len(blobs) > self._MAX_OBJECTS_FOR_BUCKET_DELETE:
357+
if len(blobs) > self._MAX_OBJECTS_FOR_ITERATION:
355358
message = (
356359
'Refusing to delete bucket with more than '
357360
'%d objects. If you actually want to delete '
358361
'this bucket, please delete the objects '
359362
'yourself before calling Bucket.delete().'
360-
) % (self._MAX_OBJECTS_FOR_BUCKET_DELETE,)
363+
) % (self._MAX_OBJECTS_FOR_ITERATION,)
361364
raise ValueError(message)
362365

363366
# Ignore 404 errors on delete.
@@ -849,6 +852,10 @@ def disable_website(self):
849852
def make_public(self, recursive=False, future=False, connection=None):
850853
"""Make a bucket public.
851854
855+
If ``recursive=True`` and the bucket contains more than 256
856+
objects / blobs this will cowardly refuse to make the objects public.
857+
This is to prevent extremely long runtime of this method.
858+
852859
:type recursive: boolean
853860
:param recursive: If True, this will make all blobs inside the bucket
854861
public as well.
@@ -875,7 +882,19 @@ def make_public(self, recursive=False, future=False, connection=None):
875882
doa.save(connection=connection)
876883

877884
if recursive:
878-
for blob in self.list_blobs(projection='full',
879-
connection=connection):
885+
blobs = list(self.list_blobs(
886+
projection='full',
887+
max_results=self._MAX_OBJECTS_FOR_ITERATION + 1,
888+
connection=connection))
889+
if len(blobs) > self._MAX_OBJECTS_FOR_ITERATION:
890+
message = (
891+
'Refusing to make public recursively with more than '
892+
'%d objects. If you actually want to make every object '
893+
'in this bucket public, please do it on the objects '
894+
'yourself.'
895+
) % (self._MAX_OBJECTS_FOR_ITERATION,)
896+
raise ValueError(message)
897+
898+
for blob in blobs:
880899
blob.acl.all().grant_read()
881900
blob.acl.save(connection=connection)

gcloud/storage/test_bucket.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ def test_delete_explicit_too_many(self):
443443
bucket = self._makeOne(NAME)
444444

445445
# Make the Bucket refuse to delete with 2 objects.
446-
bucket._MAX_OBJECTS_FOR_BUCKET_DELETE = 1
446+
bucket._MAX_OBJECTS_FOR_ITERATION = 1
447447
self.assertRaises(ValueError, bucket.delete, force=True,
448448
connection=connection)
449449
self.assertEqual(connection._deleted_buckets, [])
@@ -1009,7 +1009,34 @@ def get_items_from_response(self, response):
10091009
self.assertEqual(kw[0]['query_params'], {'projection': 'full'})
10101010
self.assertEqual(kw[1]['method'], 'GET')
10111011
self.assertEqual(kw[1]['path'], '/b/%s/o' % NAME)
1012-
self.assertEqual(kw[1]['query_params'], {'projection': 'full'})
1012+
max_results = bucket._MAX_OBJECTS_FOR_ITERATION + 1
1013+
self.assertEqual(kw[1]['query_params'],
1014+
{'maxResults': max_results, 'projection': 'full'})
1015+
1016+
def test_make_public_recursive_too_many(self):
1017+
from gcloud.storage.acl import _ACLEntity
1018+
1019+
PERMISSIVE = [{'entity': 'allUsers', 'role': _ACLEntity.READER_ROLE}]
1020+
AFTER = {'acl': PERMISSIVE, 'defaultObjectAcl': []}
1021+
1022+
NAME = 'name'
1023+
BLOB_NAME1 = 'blob-name1'
1024+
BLOB_NAME2 = 'blob-name2'
1025+
GET_BLOBS_RESP = {
1026+
'items': [
1027+
{'name': BLOB_NAME1},
1028+
{'name': BLOB_NAME2},
1029+
],
1030+
}
1031+
connection = _Connection(AFTER, GET_BLOBS_RESP)
1032+
bucket = self._makeOne(NAME, connection)
1033+
bucket.acl.loaded = True
1034+
bucket.default_object_acl.loaded = True
1035+
1036+
# Make the Bucket refuse to make_public with 2 objects.
1037+
bucket._MAX_OBJECTS_FOR_ITERATION = 1
1038+
self.assertRaises(ValueError, bucket.make_public, recursive=True,
1039+
connection=connection)
10131040

10141041

10151042
class _Connection(object):

0 commit comments

Comments
 (0)