| OLD | NEW |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import json | 5 import json |
| 6 import logging | 6 import logging |
| 7 import os | 7 import os |
| 8 | 8 |
| 9 import appengine_blobstore as blobstore | 9 import appengine_blobstore as blobstore |
| 10 from appengine_wrappers import urlfetch | 10 from appengine_wrappers import urlfetch |
| 11 import object_store | |
| 12 from file_system import FileSystem, StatInfo | 11 from file_system import FileSystem, StatInfo |
| 12 from future import Future |
| 13 from object_store_creator import ObjectStoreCreator |
| 13 from StringIO import StringIO | 14 from StringIO import StringIO |
| 14 from future import Future | |
| 15 from zipfile import ZipFile, BadZipfile | 15 from zipfile import ZipFile, BadZipfile |
| 16 | 16 |
| 17 ZIP_KEY = 'zipball' | 17 ZIP_KEY = 'zipball' |
| 18 USERNAME = None | 18 USERNAME = None |
| 19 PASSWORD = None | 19 PASSWORD = None |
| 20 | 20 |
| 21 def _MakeKey(version): | 21 def _MakeBlobstoreKey(version): |
| 22 return ZIP_KEY + '.' + str(version) | 22 return ZIP_KEY + '.' + str(version) |
| 23 | 23 |
| 24 class _AsyncFetchFutureZip(object): | 24 class _AsyncFetchFutureZip(object): |
| 25 def __init__(self, fetcher, blobstore, key_to_set, key_to_delete=None): | 25 def __init__(self, fetcher, blobstore, key_to_set, key_to_delete=None): |
| 26 self._fetcher = fetcher | 26 self._fetcher = fetcher |
| 27 self._fetch = fetcher.FetchAsync(ZIP_KEY, | 27 self._fetch = fetcher.FetchAsync(ZIP_KEY, |
| 28 username=USERNAME, | 28 username=USERNAME, |
| 29 password=PASSWORD) | 29 password=PASSWORD) |
| 30 self._blobstore = blobstore | 30 self._blobstore = blobstore |
| 31 self._key_to_set = key_to_set | 31 self._key_to_set = key_to_set |
| 32 self._key_to_delete = key_to_delete | 32 self._key_to_delete = key_to_delete |
| 33 | 33 |
| 34 def Get(self): | 34 def Get(self): |
| 35 try: | 35 try: |
| 36 result = self._fetch.Get() | 36 result = self._fetch.Get() |
| 37 # Check if Github authentication failed. | 37 # Check if Github authentication failed. |
| 38 if result.status_code == 401: | 38 if result.status_code == 401: |
| 39 logging.error('Github authentication failed for %s, falling back to ' | 39 logging.error('Github authentication failed for %s, falling back to ' |
| 40 'unauthenticated.' % USERNAME) | 40 'unauthenticated.' % USERNAME) |
| 41 blob = self._fetcher.Fetch(ZIP_KEY).content | 41 blob = self._fetcher.Fetch(ZIP_KEY).content |
| 42 else: | 42 else: |
| 43 blob = result.content | 43 blob = result.content |
| 44 except urlfetch.DownloadError as e: | 44 except urlfetch.DownloadError as e: |
| 45 logging.error('Bad github zip file: %s' % e) | 45 logging.error('Bad github zip file: %s' % e) |
| 46 return None | 46 return None |
| 47 if self._key_to_delete is not None: | 47 if self._key_to_delete is not None: |
| 48 self._blobstore.Delete(_MakeKey(self._key_to_delete), | 48 self._blobstore.Delete(_MakeBlobstoreKey(self._key_to_delete), |
| 49 blobstore.BLOBSTORE_GITHUB) | 49 blobstore.BLOBSTORE_GITHUB) |
| 50 try: | 50 try: |
| 51 return_zip = ZipFile(StringIO(blob)) | 51 return_zip = ZipFile(StringIO(blob)) |
| 52 except BadZipfile as e: | 52 except BadZipfile as e: |
| 53 logging.error('Bad github zip file: %s' % e) | 53 logging.error('Bad github zip file: %s' % e) |
| 54 return None | 54 return None |
| 55 | 55 |
| 56 self._blobstore.Set(_MakeKey(self._key_to_set), | 56 self._blobstore.Set(_MakeBlobstoreKey(self._key_to_set), |
| 57 blob, | 57 blob, |
| 58 blobstore.BLOBSTORE_GITHUB) | 58 blobstore.BLOBSTORE_GITHUB) |
| 59 return return_zip | 59 return return_zip |
| 60 | 60 |
| 61 class GithubFileSystem(FileSystem): | 61 class GithubFileSystem(FileSystem): |
| 62 """FileSystem implementation which fetches resources from github. | 62 """FileSystem implementation which fetches resources from github. |
| 63 """ | 63 """ |
| 64 def __init__(self, fetcher, object_store, blobstore): | 64 def __init__(self, fetcher, blobstore): |
| 65 self._fetcher = fetcher | 65 self._fetcher = fetcher |
| 66 self._object_store = object_store | 66 self._stat_object_store = ObjectStoreCreator(GithubFileSystem).Create() |
| 67 self._blobstore = blobstore | 67 self._blobstore = blobstore |
| 68 self._version = None | 68 self._version = None |
| 69 self._GetZip(self.Stat(ZIP_KEY).version) | 69 self._GetZip(self.Stat(ZIP_KEY).version) |
| 70 | 70 |
| 71 def _GetZip(self, version): | 71 def _GetZip(self, version): |
| 72 blob = self._blobstore.Get(_MakeKey(version), blobstore.BLOBSTORE_GITHUB) | 72 blob = self._blobstore.Get(_MakeBlobstoreKey(version), |
| 73 blobstore.BLOBSTORE_GITHUB) |
| 73 if blob is not None: | 74 if blob is not None: |
| 74 try: | 75 try: |
| 75 self._zip_file = Future(value=ZipFile(StringIO(blob))) | 76 self._zip_file = Future(value=ZipFile(StringIO(blob))) |
| 76 except BadZipfile as e: | 77 except BadZipfile as e: |
| 77 self._blobstore.Delete(_MakeKey(version), blobstore.BLOBSTORE_GITHUB) | 78 self._blobstore.Delete(_MakeBlobstoreKey(version), |
| 79 blobstore.BLOBSTORE_GITHUB) |
| 78 logging.error('Bad github zip file: %s' % e) | 80 logging.error('Bad github zip file: %s' % e) |
| 79 self._zip_file = Future(value=None) | 81 self._zip_file = Future(value=None) |
| 80 else: | 82 else: |
| 81 self._zip_file = Future( | 83 self._zip_file = Future( |
| 82 delegate=_AsyncFetchFutureZip(self._fetcher, | 84 delegate=_AsyncFetchFutureZip(self._fetcher, |
| 83 self._blobstore, | 85 self._blobstore, |
| 84 version, | 86 version, |
| 85 key_to_delete=self._version)) | 87 key_to_delete=self._version)) |
| 86 self._version = version | 88 self._version = version |
| 87 | 89 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 123 for path in paths: | 125 for path in paths: |
| 124 if path.endswith('/'): | 126 if path.endswith('/'): |
| 125 result[path] = self._ListDir(path) | 127 result[path] = self._ListDir(path) |
| 126 else: | 128 else: |
| 127 result[path] = self._ReadFile(path) | 129 result[path] = self._ReadFile(path) |
| 128 return Future(value=result) | 130 return Future(value=result) |
| 129 | 131 |
| 130 def _DefaultStat(self, path): | 132 def _DefaultStat(self, path): |
| 131 version = 0 | 133 version = 0 |
| 132 # Cache for a minute so we don't try to keep fetching bad data. | 134 # Cache for a minute so we don't try to keep fetching bad data. |
| 133 self._object_store.Set(path, version, object_store.GITHUB_STAT, time=60) | 135 self._stat_object_store.Set(path, version, time=60) |
| 134 return StatInfo(version) | 136 return StatInfo(version) |
| 135 | 137 |
| 136 def Stat(self, path): | 138 def Stat(self, path): |
| 137 version = self._object_store.Get(path, object_store.GITHUB_STAT).Get() | 139 version = self._stat_object_store.Get(path).Get() |
| 138 if version is not None: | 140 if version is not None: |
| 139 return StatInfo(version) | 141 return StatInfo(version) |
| 140 try: | 142 try: |
| 141 result = self._fetcher.Fetch('commits/HEAD', | 143 result = self._fetcher.Fetch('commits/HEAD', |
| 142 username=USERNAME, | 144 username=USERNAME, |
| 143 password=PASSWORD) | 145 password=PASSWORD) |
| 144 except urlfetch.DownloadError as e: | 146 except urlfetch.DownloadError as e: |
| 145 logging.error('GithubFileSystem Stat: %s' % e) | 147 logging.error('GithubFileSystem Stat: %s' % e) |
| 146 return self._DefaultStat(path) | 148 return self._DefaultStat(path) |
| 147 # Check if Github authentication failed. | 149 # Check if Github authentication failed. |
| 148 if result.status_code == 401: | 150 if result.status_code == 401: |
| 149 logging.error('Github authentication failed for %s, falling back to ' | 151 logging.error('Github authentication failed for %s, falling back to ' |
| 150 'unauthenticated.' % USERNAME) | 152 'unauthenticated.' % USERNAME) |
| 151 try: | 153 try: |
| 152 result = self._fetcher.Fetch('commits/HEAD') | 154 result = self._fetcher.Fetch('commits/HEAD') |
| 153 except urlfetch.DownloadError as e: | 155 except urlfetch.DownloadError as e: |
| 154 logging.error('GithubFileSystem Stat: %s' % e) | 156 logging.error('GithubFileSystem Stat: %s' % e) |
| 155 return self._DefaultStat(path) | 157 return self._DefaultStat(path) |
| 156 version = (json.loads(result.content).get('commit', {}) | 158 version = (json.loads(result.content).get('commit', {}) |
| 157 .get('tree', {}) | 159 .get('tree', {}) |
| 158 .get('sha', None)) | 160 .get('sha', None)) |
| 159 # Check if the JSON was valid, and set to 0 if not. | 161 # Check if the JSON was valid, and set to 0 if not. |
| 160 if version is not None: | 162 if version is not None: |
| 161 self._object_store.Set(path, version, object_store.GITHUB_STAT) | 163 self._stat_object_store.Set(path, version) |
| 162 else: | 164 else: |
| 163 logging.warning('Problem fetching commit hash from github.') | 165 logging.warning('Problem fetching commit hash from github.') |
| 164 return self._DefaultStat(path) | 166 return self._DefaultStat(path) |
| 165 return StatInfo(version) | 167 return StatInfo(version) |
| OLD | NEW |