Skip to content

Commit 02dcded

Browse files
committed
Add support for the Bucket's 'logging' field.
See: https://cloud.google.com/storage/docs/accesslogs Addresses 'logging' part of #314.
1 parent aa22386 commit 02dcded

File tree

2 files changed

+142
-0
lines changed

2 files changed

+142
-0
lines changed

gcloud/storage/bucket.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class Bucket(_MetadataMixin):
2424
CUSTOM_METADATA_FIELDS = {
2525
'acl': 'get_acl',
2626
'defaultObjectAcl': 'get_default_object_acl',
27+
'logging': 'get_logging',
2728
}
2829
"""Mapping of field name -> accessor for fields w/ custom accessors."""
2930

@@ -441,6 +442,45 @@ def make_public(self, recursive=False, future=False):
441442
key.get_acl().all().grant_read()
442443
key.save_acl()
443444

445+
def get_logging(self):
446+
"""Return info about access logging for this bucket.
447+
448+
See: https://cloud.google.com/storage/docs/accesslogs#status
449+
450+
:rtype: dict or None
451+
:returns: a dict w/ keys, ``bucket_name`` and ``object_prefix``
452+
(if logging is enabled), or None (if not).
453+
"""
454+
if not self.has_metadata('logging'):
455+
self.reload_metadata()
456+
info = self.metadata.get('logging')
457+
if info is not None:
458+
info = info.copy()
459+
info['bucket_name'] = info.pop('logBucket')
460+
info['object_prefix'] = info.pop('logObjectPrefix', '')
461+
return info
462+
463+
def enable_logging(self, bucket_name, object_prefix=''):
464+
"""Enable access logging for this bucket.
465+
466+
See: https://cloud.google.com/storage/docs/accesslogs#delivery
467+
468+
:type bucket_name: string
469+
:param bucket_name: name of bucket in which to store access logs
470+
471+
:type object_prefix: string
472+
:param object_prefix: prefix for access log filenames
473+
"""
474+
info = {'logBucket': bucket_name, 'logObjectPrefix': object_prefix}
475+
self.patch_metadata({'logging': info})
476+
477+
def disable_logging(self):
478+
"""Disable access logging for this bucket.
479+
480+
See: https://cloud.google.com/storage/docs/accesslogs#disabling
481+
"""
482+
self.patch_metadata({'logging': None})
483+
444484

445485
class BucketIterator(Iterator):
446486
"""An iterator listing all buckets.

gcloud/storage/test_bucket.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,23 @@ def test_get_metadata_none_set_defaultObjectAcl_miss_clear_default(self):
489489
kw = connection._requested
490490
self.assertEqual(len(kw), 0)
491491

492+
def test_get_metadata_logging_no_default(self):
493+
NAME = 'name'
494+
connection = _Connection()
495+
bucket = self._makeOne(connection, NAME)
496+
self.assertRaises(KeyError, bucket.get_metadata, 'logging')
497+
kw = connection._requested
498+
self.assertEqual(len(kw), 0)
499+
500+
def test_get_metadata_logging_w_default(self):
501+
NAME = 'name'
502+
connection = _Connection()
503+
bucket = self._makeOne(connection, NAME)
504+
default = object()
505+
self.assertRaises(KeyError, bucket.get_metadata, 'logging', default)
506+
kw = connection._requested
507+
self.assertEqual(len(kw), 0)
508+
492509
def test_get_metadata_miss(self):
493510
NAME = 'name'
494511
before = {'bar': 'Bar'}
@@ -713,6 +730,91 @@ def get_items_from_response(self, response):
713730
self.assertEqual(kw[1]['path'], '/b/%s/o' % NAME)
714731
self.assertEqual(kw[1]['query_params'], None)
715732

733+
def test_get_logging_eager_w_prefix(self):
734+
NAME = 'name'
735+
LOG_BUCKET = 'logs'
736+
LOG_PREFIX = 'pfx'
737+
before = {
738+
'logging': {'logBucket': LOG_BUCKET,
739+
'logObjectPrefix': LOG_PREFIX}}
740+
connection = _Connection()
741+
bucket = self._makeOne(connection, NAME, before)
742+
info = bucket.get_logging()
743+
self.assertEqual(info['bucket_name'], LOG_BUCKET)
744+
self.assertEqual(info['object_prefix'], LOG_PREFIX)
745+
kw = connection._requested
746+
self.assertEqual(len(kw), 0)
747+
748+
def test_get_logging_lazy_wo_prefix(self):
749+
NAME = 'name'
750+
LOG_BUCKET = 'logs'
751+
after = {'logging': {'logBucket': LOG_BUCKET}}
752+
connection = _Connection(after)
753+
bucket = self._makeOne(connection, NAME)
754+
info = bucket.get_logging()
755+
self.assertEqual(info['bucket_name'], LOG_BUCKET)
756+
self.assertEqual(info['object_prefix'], '')
757+
kw = connection._requested
758+
self.assertEqual(len(kw), 1)
759+
self.assertEqual(kw[0]['path'], '/b/%s' % NAME)
760+
self.assertEqual(kw[0]['query_params'], {'projection': 'noAcl'})
761+
762+
def test_enable_logging_defaults(self):
763+
NAME = 'name'
764+
LOG_BUCKET = 'logs'
765+
before = {'logging': None}
766+
after = {'logging': {'logBucket': LOG_BUCKET, 'logObjectPrefix': ''}}
767+
connection = _Connection(after)
768+
bucket = self._makeOne(connection, NAME, before)
769+
self.assertTrue(bucket.get_logging() is None)
770+
bucket.enable_logging(LOG_BUCKET)
771+
info = bucket.get_logging()
772+
self.assertEqual(info['bucket_name'], LOG_BUCKET)
773+
self.assertEqual(info['object_prefix'], '')
774+
kw = connection._requested
775+
self.assertEqual(len(kw), 1)
776+
self.assertEqual(kw[0]['method'], 'PATCH')
777+
self.assertEqual(kw[0]['path'], '/b/%s' % NAME)
778+
self.assertEqual(kw[0]['data'], after)
779+
self.assertEqual(kw[0]['query_params'], {'projection': 'full'})
780+
781+
def test_enable_logging_explicit(self):
782+
NAME = 'name'
783+
LOG_BUCKET = 'logs'
784+
LOG_PFX = 'pfx'
785+
before = {'logging': None}
786+
after = {
787+
'logging': {'logBucket': LOG_BUCKET, 'logObjectPrefix': LOG_PFX}}
788+
connection = _Connection(after)
789+
bucket = self._makeOne(connection, NAME, before)
790+
self.assertTrue(bucket.get_logging() is None)
791+
bucket.enable_logging(LOG_BUCKET, LOG_PFX)
792+
info = bucket.get_logging()
793+
self.assertEqual(info['bucket_name'], LOG_BUCKET)
794+
self.assertEqual(info['object_prefix'], LOG_PFX)
795+
kw = connection._requested
796+
self.assertEqual(len(kw), 1)
797+
self.assertEqual(kw[0]['method'], 'PATCH')
798+
self.assertEqual(kw[0]['path'], '/b/%s' % NAME)
799+
self.assertEqual(kw[0]['data'], after)
800+
self.assertEqual(kw[0]['query_params'], {'projection': 'full'})
801+
802+
def test_disable_logging(self):
803+
NAME = 'name'
804+
before = {'logging': {'logBucket': 'logs', 'logObjectPrefix': 'pfx'}}
805+
after = {'logging': None}
806+
connection = _Connection(after)
807+
bucket = self._makeOne(connection, NAME, before)
808+
self.assertTrue(bucket.get_logging() is not None)
809+
bucket.disable_logging()
810+
self.assertTrue(bucket.get_logging() is None)
811+
kw = connection._requested
812+
self.assertEqual(len(kw), 1)
813+
self.assertEqual(kw[0]['method'], 'PATCH')
814+
self.assertEqual(kw[0]['path'], '/b/%s' % NAME)
815+
self.assertEqual(kw[0]['data'], {'logging': None})
816+
self.assertEqual(kw[0]['query_params'], {'projection': 'full'})
817+
716818

717819
class TestBucketIterator(unittest2.TestCase):
718820

0 commit comments

Comments
 (0)