Skip to content

Commit c8df445

Browse files
committed
Use ACL-specific endpoints where feasible for buckets.
- Refactor lazy-handling of 'acl' and 'default_object_acl' using properties. - Fetch 'ac' and 'defaultObjectAcl' via GET to specific endpoints. - Continue to update 'acl' and 'defaultObjectAcl' via PATCH to the object's resource (we can't do incremental updates to ACEs). Fixes #163.
1 parent 9dfb5d6 commit c8df445

File tree

2 files changed

+179
-131
lines changed

2 files changed

+179
-131
lines changed

gcloud/storage/bucket.py

Lines changed: 64 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,27 @@ class Bucket(object):
1919
:type name: string
2020
:param name: The name of the bucket.
2121
"""
22+
# ACL rules are lazily retrieved.
23+
_acl = _default_object_acl = None
2224

2325
def __init__(self, connection=None, name=None, metadata=None):
2426
self.connection = connection
2527
self.name = name
2628
self.metadata = metadata
2729

28-
# ACL rules are lazily retrieved.
29-
self.acl = None
30-
self.default_object_acl = None
30+
@property
31+
def acl(self):
32+
"""Create our ACL on demand."""
33+
if self._acl is None:
34+
self._acl = BucketACL(self)
35+
return self._acl
36+
37+
@property
38+
def default_object_acl(self):
39+
"""Create our defaultObjectACL on demand."""
40+
if self._default_object_acl is None:
41+
self._default_object_acl = DefaultObjectACL(self)
42+
return self._default_object_acl
3143

3244
@classmethod
3345
def from_dict(cls, bucket_dict, connection=None):
@@ -428,11 +440,15 @@ def reload_acl(self):
428440
:rtype: :class:`Bucket`
429441
:returns: The current bucket.
430442
"""
431-
self.acl = BucketACL(bucket=self)
443+
self.acl.clear()
432444

433-
for entry in self.get_metadata('acl', []):
434-
entity = self.acl.entity_from_dict(entry)
435-
self.acl.add_entity(entity)
445+
url_path = '%s/acl' % self.path
446+
found = self.connection.api_request(method='GET', path=url_path)
447+
for entry in found['items']:
448+
self.acl.add_entity(self.acl.entity_from_dict(entry))
449+
450+
# Even if we fetch no entries, the ACL is still loaded.
451+
self.acl.loaded = True
436452

437453
return self
438454

@@ -442,7 +458,7 @@ def get_acl(self):
442458
:rtype: :class:`gcloud.storage.acl.BucketACL`
443459
:returns: An ACL object for the current bucket.
444460
"""
445-
if not self.acl:
461+
if not self.acl.loaded:
446462
self.reload_acl()
447463
return self.acl
448464

@@ -484,12 +500,17 @@ def save_acl(self, acl=None):
484500
# both evaluate to False, but mean very different things.
485501
if acl is None:
486502
acl = self.acl
503+
dirty = acl.loaded
504+
else:
505+
dirty = True
487506

488-
if acl is None:
489-
return self
507+
if dirty:
508+
result = self.connection.api_request(
509+
method='PATCH', path=self.path, data={'acl': list(acl)})
510+
self.acl.clear()
511+
for entry in result['acl']:
512+
self.acl.entity(self.acl.entity_from_dict(entry))
490513

491-
self.patch_metadata({'acl': list(acl)})
492-
self.reload_acl()
493514
return self
494515

495516
def clear_acl(self):
@@ -519,19 +540,28 @@ def clear_acl(self):
519540
520541
At this point all the custom rules you created have been removed.
521542
"""
522-
return self.save_acl(acl=[])
543+
self.connection.api_request(
544+
method='PATCH', path=self.path, data={'acl': []})
545+
self.acl.clear()
546+
self.acl.loaded = True
547+
return self
523548

524549
def reload_default_object_acl(self):
525550
"""Reload the Default Object ACL rules for this bucket.
526551
527552
:rtype: :class:`Bucket`
528553
:returns: The current bucket.
529554
"""
530-
self.default_object_acl = DefaultObjectACL(bucket=self)
555+
doa = self.default_object_acl
556+
doa.clear()
557+
558+
url_path = '%s/defaultObjectAcl' % self.path
559+
found = self.connection.api_request(method='GET', path=url_path)
560+
for entry in found['items']:
561+
doa.add_entity(doa.entity_from_dict(entry))
531562

532-
for entry in self.get_metadata('defaultObjectAcl', []):
533-
entity = self.default_object_acl.entity_from_dict(entry)
534-
self.default_object_acl.add_entity(entity)
563+
# Even if we fetch no entries, the ACL is still loaded.
564+
doa.loaded = True
535565

536566
return self
537567

@@ -544,7 +574,7 @@ def get_default_object_acl(self):
544574
:rtype: :class:`gcloud.storage.acl.DefaultObjectACL`
545575
:returns: A DefaultObjectACL object for this bucket.
546576
"""
547-
if not self.default_object_acl:
577+
if not self.default_object_acl.loaded:
548578
self.reload_default_object_acl()
549579
return self.default_object_acl
550580

@@ -559,18 +589,29 @@ def save_default_object_acl(self, acl=None):
559589
"""
560590
if acl is None:
561591
acl = self.default_object_acl
592+
dirty = acl.loaded
593+
else:
594+
dirty = True
562595

563-
if acl is None:
564-
return self
596+
if dirty:
597+
result = self.connection.api_request(
598+
method='PATCH', path=self.path,
599+
data={'defaultObjectAcl': list(acl)})
600+
doa = self.default_object_acl
601+
doa.clear()
602+
for entry in result['defaultObjectAcl']:
603+
doa.entity(doa.entity_from_dict(entry))
565604

566-
self.patch_metadata({'defaultObjectAcl': list(acl)})
567-
self.reload_default_object_acl()
568605
return self
569606

570607
def clear_default_object_acl(self):
571608
"""Remove the Default Object ACL from this bucket."""
572609

573-
return self.save_default_object_acl(acl=[])
610+
self.connection.api_request(
611+
method='PATCH', path=self.path, data={'defaultObjectAcl': []})
612+
self.default_object_acl.clear()
613+
self.default_object_acl.loaded = True
614+
return self
574615

575616
def make_public(self, recursive=False, future=False):
576617
"""Make a bucket public.

0 commit comments

Comments
 (0)