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 |