| Index: py/utils/gs_utils.py
|
| diff --git a/py/utils/gs_utils.py b/py/utils/gs_utils.py
|
| index afc374841b3d7ca5f56fdd2e7848a85b5a4e5110..031a0c3341d15bc70d628f07ff1b0d94a906de0f 100755
|
| --- a/py/utils/gs_utils.py
|
| +++ b/py/utils/gs_utils.py
|
| @@ -37,9 +37,11 @@ for import_subdir in ['boto']:
|
| # imported versions are favored over others that might be in the path.
|
| sys.path.insert(0, import_dirpath)
|
| from boto.gs import acl
|
| +from boto.gs.bucket import Bucket
|
| from boto.gs.connection import GSConnection
|
| from boto.gs.key import Key
|
| from boto.s3.bucketlistresultset import BucketListResultSet
|
| +from boto.s3.connection import SubdomainCallingFormat
|
| from boto.s3.prefix import Prefix
|
|
|
| # Permissions that may be set on each file in Google Storage.
|
| @@ -67,24 +69,46 @@ FIELD_BY_ID_TYPE = {
|
| }
|
|
|
|
|
| +class AnonymousGSConnection(GSConnection):
|
| + """GSConnection class that allows anonymous connections.
|
| +
|
| + The GSConnection class constructor in
|
| + https://github.com/boto/boto/blob/develop/boto/gs/connection.py doesn't allow
|
| + for anonymous connections (connections without credentials), so we have to
|
| + override it.
|
| + """
|
| + def __init__(self):
|
| + super(GSConnection, self).__init__(
|
| + # This is the important bit we need to add...
|
| + anon=True,
|
| + # ...and these are just copied in from GSConnection.__init__()
|
| + bucket_class=Bucket,
|
| + calling_format=SubdomainCallingFormat(),
|
| + host=GSConnection.DefaultHost,
|
| + provider='google')
|
| +
|
| +
|
| class GSUtils(object):
|
| """Utilities for accessing Google Cloud Storage, using the boto library."""
|
|
|
| - def __init__(self, boto_file_path=os.path.join('~','.boto')):
|
| + def __init__(self, boto_file_path=None):
|
| """Constructor.
|
|
|
| Params:
|
| boto_file_path: full path (local-OS-style) on local disk where .boto
|
| - credentials file can be found. An exception is thrown if this file
|
| - is missing.
|
| - TODO(epoger): Change missing-file behavior: allow the caller to
|
| - operate on public files in Google Storage.
|
| + credentials file can be found. If None, then the GSUtils object
|
| + created will be able to access only public files in Google Storage.
|
| +
|
| + Raises an exception if no file is found at boto_file_path, or if the file
|
| + found there is malformed.
|
| """
|
| - boto_file_path = os.path.expanduser(boto_file_path)
|
| - print 'Reading boto file from %s' % boto_file_path
|
| - boto_dict = _config_file_as_dict(filepath=boto_file_path)
|
| - self._gs_access_key_id = boto_dict['gs_access_key_id']
|
| - self._gs_secret_access_key = boto_dict['gs_secret_access_key']
|
| + self._gs_access_key_id = None
|
| + self._gs_secret_access_key = None
|
| + if boto_file_path:
|
| + print 'Reading boto file from %s' % boto_file_path
|
| + boto_dict = _config_file_as_dict(filepath=boto_file_path)
|
| + self._gs_access_key_id = boto_dict['gs_access_key_id']
|
| + self._gs_secret_access_key = boto_dict['gs_secret_access_key']
|
|
|
| def delete_file(self, bucket, path):
|
| """Delete a single file within a GS bucket.
|
| @@ -258,10 +282,12 @@ class GSUtils(object):
|
|
|
| def _create_connection(self):
|
| """Returns a GSConnection object we can use to access Google Storage."""
|
| - return GSConnection(
|
| - gs_access_key_id=self._gs_access_key_id,
|
| - gs_secret_access_key=self._gs_secret_access_key)
|
| -
|
| + if self._gs_access_key_id:
|
| + return GSConnection(
|
| + gs_access_key_id=self._gs_access_key_id,
|
| + gs_secret_access_key=self._gs_secret_access_key)
|
| + else:
|
| + return AnonymousGSConnection()
|
|
|
| def _config_file_as_dict(filepath):
|
| """Reads a boto-style config file into a dict.
|
| @@ -301,12 +327,27 @@ def _makedirs_if_needed(path):
|
| raise
|
|
|
|
|
| -def _run_self_test():
|
| +def _test_public_read():
|
| + """Make sure we can read from public files without .boto file credentials."""
|
| + gs = GSUtils()
|
| + gs.list_bucket_contents(bucket='chromium-skia-gm-summaries', subdir=None)
|
| +
|
| +
|
| +def _test_authenticated_round_trip():
|
| + try:
|
| + gs = GSUtils(boto_file_path=os.path.expanduser(os.path.join('~','.boto')))
|
| + except:
|
| + print """
|
| +Failed to instantiate GSUtils object with default .boto file path.
|
| +Do you have a ~/.boto file that provides the credentials needed to read
|
| +and write gs://chromium-skia-gm ?
|
| +"""
|
| + raise
|
| +
|
| bucket = 'chromium-skia-gm'
|
| remote_dir = 'gs_utils_test/%d' % random.randint(0, sys.maxint)
|
| subdir = 'subdir'
|
| filenames_to_upload = ['file1', 'file2']
|
| - gs = GSUtils()
|
|
|
| # Upload test files to Google Storage.
|
| local_src_dir = tempfile.mkdtemp()
|
| @@ -393,11 +434,10 @@ def _run_self_test():
|
| assert files == [], '%s == []' % files
|
|
|
|
|
| -# TODO(epoger): How should we exercise this self-test?
|
| -# I avoided using the standard unittest framework, because these Google Storage
|
| -# operations are expensive and require .boto permissions.
|
| -#
|
| -# How can we automatically test this code without wasting too many resources
|
| -# or needing .boto permissions?
|
| +# TODO(epoger): How should we exercise these self-tests?
|
| +# See http://skbug.com/2751
|
| if __name__ == '__main__':
|
| - _run_self_test()
|
| + _test_public_read()
|
| + _test_authenticated_round_trip()
|
| + # TODO(epoger): Add _test_unauthenticated_access() to make sure we raise
|
| + # an exception when we try to access without needed credentials.
|
|
|