Chromium Code Reviews| Index: chrome/common/extensions/docs/server2/gcs_file_system.py |
| diff --git a/chrome/common/extensions/docs/server2/gcs_file_system.py b/chrome/common/extensions/docs/server2/gcs_file_system.py |
| index 36f28fa08f33f2d3ce73cfa4a2f0d86fa0bc7ee3..62c21be73faed035957416a6a90c843c7d79e39c 100644 |
| --- a/chrome/common/extensions/docs/server2/gcs_file_system.py |
| +++ b/chrome/common/extensions/docs/server2/gcs_file_system.py |
| @@ -2,19 +2,19 @@ |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| -from third_party.cloudstorage import cloudstorage_api |
| -from third_party.cloudstorage import common |
| -from third_party.cloudstorage import errors |
| +import json |
| +import logging |
| +import posixpath |
| +import traceback |
| +import urllib |
| from docs_server_utils import StringIdentity |
| +from environment_wrappers import CreateUrlFetcher |
|
Ken Rockot(use gerrit already)
2015/05/26 00:26:24
Unfortunately cloudstorage_api only works from App
|
| from file_system import FileSystem, FileNotFoundError, StatInfo |
| from future import Future |
| from path_util import ( |
| AssertIsDirectory, AssertIsFile, AssertIsValid, IsDirectory, Join) |
| -import logging |
| -import traceback |
| - |
| # See gcs_file_system_provider.py for documentation on using Google Cloud |
| # Storage as a filesystem. |
| @@ -25,76 +25,32 @@ import traceback |
| # Name of the file containing the Git hash of the latest commit sync'ed |
| # to Cloud Storage. This file is generated by the Github->GCS sync script |
| -LAST_COMMIT_HASH_FILENAME = '.__lastcommit.txt' |
| - |
| -def _ReadFile(filename): |
| - AssertIsFile(filename) |
| - try: |
| - with cloudstorage_api.open('/' + filename, 'r') as f: |
| - return f.read() |
| - except errors.Error: |
| - raise FileNotFoundError('Read failed for %s: %s' % (filename, |
| - traceback.format_exc())) |
| - |
| -def _ListDir(dir_name, recursive=False): |
| - AssertIsDirectory(dir_name) |
| - try: |
| - # The listbucket method uses a prefix approach to simulate hierarchy. |
| - # Calling it with the "delimiter" argument set to '/' gets only files |
| - # directly inside the directory, not all recursive content. |
| - delimiter = None if recursive else '/' |
| - files = cloudstorage_api.listbucket('/' + dir_name, delimiter=delimiter) |
| - return [os_path.filename.lstrip('/')[len(dir_name):] for os_path in files] |
| - except errors.Error: |
| - raise FileNotFoundError('cloudstorage.listbucket failed for %s: %s' % |
| - (dir_name, traceback.format_exc())) |
| - |
| -def _CreateStatInfo(bucket, path): |
| - full_path = Join(bucket, path) |
| - last_commit_file = Join(bucket, LAST_COMMIT_HASH_FILENAME) |
| - try: |
| - last_commit = _ReadFile(last_commit_file) |
| - if IsDirectory(full_path): |
| - child_versions = dict((filename, last_commit) |
| - for filename in _ListDir(full_path)) |
| - else: |
| - child_versions = None |
| - return StatInfo(last_commit, child_versions) |
| - except (TypeError, errors.Error): |
| - raise FileNotFoundError('cloudstorage.stat failed for %s: %s' % (path, |
| - traceback.format_exc())) |
| +_LAST_COMMIT_HASH_FILENAME = '.__lastcommit.txt' |
| + |
| + |
| +# Base URL for GCS requests. |
| +_STORAGE_API_BASE = 'https://www.googleapis.com/storage/v1' |
| + |
| class CloudStorageFileSystem(FileSystem): |
| '''FileSystem implementation which fetches resources from Google Cloud |
| Storage. |
| ''' |
| - def __init__(self, bucket, debug_access_token=None, debug_bucket_prefix=None): |
| + def __init__(self, bucket, debug_bucket_prefix=None): |
| self._bucket = bucket |
| - if debug_access_token: |
| - logging.debug('gcs: using debug access token: %s' % debug_access_token) |
| - common.set_access_token(debug_access_token) |
| - if debug_bucket_prefix: |
| - logging.debug('gcs: prefixing all bucket names with %s' % |
| - debug_bucket_prefix) |
| - self._bucket = debug_bucket_prefix + self._bucket |
| + self._access_token = None |
| + self._last_commit_hash = None |
| AssertIsValid(self._bucket) |
| def Read(self, paths, skip_not_found=False): |
| def resolve(): |
| - try: |
| - result = {} |
| - for path in paths: |
| - full_path = Join(self._bucket, path) |
| - logging.debug('gcs: requested path "%s", reading "%s"' % |
| - (path, full_path)) |
| - if IsDirectory(path): |
| - result[path] = _ListDir(full_path) |
| - else: |
| - result[path] = _ReadFile(full_path) |
| - return result |
| - except errors.AuthorizationError: |
| - self._warnAboutAuthError() |
| - raise |
| + result = {} |
| + for path in paths: |
| + if IsDirectory(path): |
| + result[path] = self._ListDir(path) |
| + else: |
| + result[path] = self._ReadFile(path) |
| + return result |
| return Future(callback=resolve) |
| @@ -103,25 +59,62 @@ class CloudStorageFileSystem(FileSystem): |
| def Stat(self, path): |
| AssertIsValid(path) |
| - try: |
| - return _CreateStatInfo(self._bucket, path) |
| - except errors.AuthorizationError: |
| - self._warnAboutAuthError() |
| - raise |
| + return self._CreateStatInfo(path) |
| def GetIdentity(self): |
| return '@'.join((self.__class__.__name__, StringIdentity(self._bucket))) |
| + def _CreateStatInfo(self, path): |
| + if not self._last_commit_hash: |
| + self._last_commit_hash = self._ReadFile(_LAST_COMMIT_HASH_FILENAME) |
| + if IsDirectory(path): |
| + child_versions = dict((filename, self._last_commit_hash) |
| + for filename in self._ListDir(path)) |
| + else: |
| + child_versions = None |
| + return StatInfo(self._last_commit_hash, child_versions) |
| + |
| + def _ReadFile(self, path): |
| + AssertIsFile(path) |
| + return self._FetchObjectData(path) |
| + |
| + def _ListDir(self, path, recursive=False): |
| + AssertIsDirectory(path) |
| + # The listbucket method uses a prefix approach to simulate hierarchy. |
| + # Calling it with the "delimiter" argument set to '/' gets only files |
| + # directly inside the directory, not all recursive content. |
| + |
| + # Subdirectories are returned in the 'prefixes' property, but they are |
| + # full paths from the root. This plucks off the name of the leaf with a |
| + # trailing slash. |
| + def path_from_prefix(prefix): |
| + return posixpath.split(posixpath.split(prefix)[0])[1] + '/' |
| + |
| + query = { 'prefix': path } |
| + if not recursive: |
| + query['delimiter'] = '/' |
| + root_object = json.loads(self._FetchObject('', query=query)) |
| + files = [posixpath.basename(o['name']) |
| + for o in root_object.get('items', [])] |
| + dirs = [path_from_prefix(prefix) |
| + for prefix in root_object.get('prefixes', [])] |
| + return files + dirs |
| + |
| + def _FetchObject(self, path, query={}): |
| + # Escape the path, including slashes. |
| + url_path = urllib.quote(path.lstrip('/'), safe='') |
| + fetcher = CreateUrlFetcher() |
| + object_url = '%s/b/%s/o/%s' % (_STORAGE_API_BASE, self._bucket, url_path) |
| + response = fetcher.Fetch(object_url, query=query) |
| + if response.status_code != 200: |
| + raise FileNotFoundError( |
| + 'Path %s not found in GCS bucket %s' % (path, self._bucket)) |
| + return response.content |
| + |
| + def _FetchObjectData(self, path, query={}): |
| + q = query.copy() |
| + q.update({ 'alt': 'media' }) |
| + return self._FetchObject(path, query=q) |
| + |
| def __repr__(self): |
| return 'CloudStorageFileSystem(%s)' % self._bucket |
| - |
| - def _warnAboutAuthError(self): |
|
Ken Rockot(use gerrit already)
2015/05/26 00:26:24
Auth is no longer required. The github repos are p
|
| - logging.warn(('Authentication error on Cloud Storage. Check if your' |
| - ' appengine project has permissions to Read the GCS' |
| - ' buckets. If you are running a local appengine server,' |
| - ' you need to set an access_token in' |
| - ' local_debug/gcs_debug.conf.' |
| - ' Remember that this token expires in less than 10' |
| - ' minutes, so keep it updated. See' |
| - ' gcs_file_system_provider.py for instructions.')); |
| - logging.debug(traceback.format_exc()) |