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 logging | 5 import logging |
6 import os | 6 import os |
7 from StringIO import StringIO | |
7 import sys | 8 import sys |
8 | 9 |
9 from appengine_wrappers import webapp | 10 from appengine_wrappers import webapp |
10 from appengine_wrappers import memcache | 11 from appengine_wrappers import memcache |
11 from appengine_wrappers import urlfetch | 12 from appengine_wrappers import urlfetch |
12 | 13 |
13 from api_data_source import APIDataSource | 14 from api_data_source import APIDataSource |
14 from api_list_data_source import APIListDataSource | 15 from api_list_data_source import APIListDataSource |
15 from appengine_blobstore import AppEngineBlobstore | 16 from appengine_blobstore import AppEngineBlobstore |
16 from appengine_memcache import AppEngineMemcache | 17 from appengine_memcache import AppEngineMemcache |
17 from appengine_url_fetcher import AppEngineUrlFetcher | 18 from appengine_url_fetcher import AppEngineUrlFetcher |
18 from branch_utility import BranchUtility | 19 from branch_utility import BranchUtility |
19 from example_zipper import ExampleZipper | 20 from example_zipper import ExampleZipper |
20 from file_system_cache import FileSystemCache | 21 from file_system_cache import FileSystemCache |
22 import file_system_cache as fs_cache | |
21 from github_file_system import GithubFileSystem | 23 from github_file_system import GithubFileSystem |
22 from intro_data_source import IntroDataSource | 24 from intro_data_source import IntroDataSource |
23 from local_file_system import LocalFileSystem | 25 from local_file_system import LocalFileSystem |
24 from memcache_file_system import MemcacheFileSystem | 26 from memcache_file_system import MemcacheFileSystem |
25 from samples_data_source import SamplesDataSource | 27 from samples_data_source import SamplesDataSource |
26 from server_instance import ServerInstance | 28 from server_instance import ServerInstance |
27 from subversion_file_system import SubversionFileSystem | 29 from subversion_file_system import SubversionFileSystem |
28 from template_data_source import TemplateDataSource | 30 from template_data_source import TemplateDataSource |
29 import url_constants | 31 import url_constants |
30 | 32 |
31 # The branch that the server will default to when no branch is specified in the | 33 # The branch that the server will default to when no branch is specified in the |
32 # URL. This is necessary because it is not possible to pass flags to the script | 34 # URL. This is necessary because it is not possible to pass flags to the script |
33 # handler. | 35 # handler. |
34 DEFAULT_BRANCH = 'local' | 36 DEFAULT_BRANCH = 'local' |
35 | 37 |
36 BRANCH_UTILITY_MEMCACHE = AppEngineMemcache('branch_utility') | 38 BRANCH_UTILITY_MEMCACHE = AppEngineMemcache('branch_utility') |
37 BRANCH_UTILITY = BranchUtility(url_constants.OMAHA_PROXY_URL, | 39 BRANCH_UTILITY = BranchUtility(url_constants.OMAHA_PROXY_URL, |
38 DEFAULT_BRANCH, | 40 DEFAULT_BRANCH, |
39 AppEngineUrlFetcher(None), | 41 AppEngineUrlFetcher(None), |
40 BRANCH_UTILITY_MEMCACHE) | 42 BRANCH_UTILITY_MEMCACHE) |
41 | 43 |
44 GITHUB_MEMCACHE = AppEngineMemcache('github') | |
42 GITHUB_FILE_SYSTEM = GithubFileSystem( | 45 GITHUB_FILE_SYSTEM = GithubFileSystem( |
43 AppEngineUrlFetcher(url_constants.GITHUB_URL), | 46 AppEngineUrlFetcher(url_constants.GITHUB_URL), |
44 AppEngineMemcache('github'), | 47 GITHUB_MEMCACHE, |
45 AppEngineBlobstore()) | 48 AppEngineBlobstore()) |
46 GITHUB_CACHE_BUILDER = FileSystemCache.Builder(GITHUB_FILE_SYSTEM) | 49 GITHUB_CACHE_BUILDER = FileSystemCache.Builder(GITHUB_FILE_SYSTEM, |
50 GITHUB_MEMCACHE) | |
47 | 51 |
48 STATIC_DIR_PREFIX = 'docs/server2' | 52 STATIC_DIR_PREFIX = 'docs/server2' |
49 EXTENSIONS_PATH = 'chrome/common/extensions' | 53 EXTENSIONS_PATH = 'chrome/common/extensions' |
50 DOCS_PATH = 'docs' | 54 DOCS_PATH = 'docs' |
51 API_PATH = 'api' | 55 API_PATH = 'api' |
52 INTRO_PATH = DOCS_PATH + '/server2/templates/intros' | 56 TEMPLATE_PATH = DOCS_PATH + '/server2/templates' |
53 ARTICLE_PATH = DOCS_PATH + '/server2/templates/articles' | 57 INTRO_PATH = TEMPLATE_PATH + '/intros' |
54 PUBLIC_TEMPLATE_PATH = DOCS_PATH + '/server2/templates/public' | 58 ARTICLE_PATH = TEMPLATE_PATH + '/articles' |
55 PRIVATE_TEMPLATE_PATH = DOCS_PATH + '/server2/templates/private' | 59 PUBLIC_TEMPLATE_PATH = TEMPLATE_PATH + '/public' |
60 PRIVATE_TEMPLATE_PATH = TEMPLATE_PATH + '/private' | |
56 EXAMPLES_PATH = DOCS_PATH + '/examples' | 61 EXAMPLES_PATH = DOCS_PATH + '/examples' |
57 FULL_EXAMPLES_PATH = DOCS_PATH + '/' + EXAMPLES_PATH | |
58 | 62 |
59 # Global cache of instances because Handler is recreated for every request. | 63 # Global cache of instances because Handler is recreated for every request. |
60 SERVER_INSTANCES = {} | 64 SERVER_INSTANCES = {} |
61 | 65 |
66 def _CreateMemcacheFileSystem(branch, branch_memcache): | |
67 svn_url = _GetURLFromBranch(branch) + '/' + EXTENSIONS_PATH | |
68 stat_fetcher = AppEngineUrlFetcher( | |
69 svn_url.replace(url_constants.SVN_URL, url_constants.VIEWVC_URL)) | |
70 fetcher = AppEngineUrlFetcher(svn_url) | |
71 return MemcacheFileSystem(SubversionFileSystem(fetcher, stat_fetcher), | |
72 branch_memcache) | |
73 | |
62 def _GetInstanceForBranch(channel_name, local_path): | 74 def _GetInstanceForBranch(channel_name, local_path): |
63 branch = BRANCH_UTILITY.GetBranchNumberForChannelName(channel_name) | 75 branch = BRANCH_UTILITY.GetBranchNumberForChannelName(channel_name) |
64 if branch in SERVER_INSTANCES: | 76 if branch in SERVER_INSTANCES: |
65 return SERVER_INSTANCES[branch] | 77 return SERVER_INSTANCES[branch] |
78 branch_memcache = AppEngineMemcache(branch) | |
66 if branch == 'local': | 79 if branch == 'local': |
67 file_system = LocalFileSystem(local_path) | 80 file_system = LocalFileSystem(local_path) |
68 else: | 81 else: |
69 svn_url = _GetURLFromBranch(branch) + '/' + EXTENSIONS_PATH | 82 file_system = _CreateMemcacheFileSystem(branch, branch_memcache) |
70 stat_fetcher = AppEngineUrlFetcher( | |
71 svn_url.replace(url_constants.SVN_URL, url_constants.VIEWVC_URL)) | |
72 fetcher = AppEngineUrlFetcher(svn_url) | |
73 file_system = MemcacheFileSystem( | |
74 SubversionFileSystem(fetcher, stat_fetcher), | |
75 AppEngineMemcache(branch)) | |
76 | 83 |
77 cache_builder = FileSystemCache.Builder(file_system) | 84 cache_builder = FileSystemCache.Builder(file_system, branch_memcache) |
78 api_list_data_source = APIListDataSource(cache_builder, | 85 api_list_data_source_factory = APIListDataSource.Factory(cache_builder, |
79 file_system, | 86 file_system, |
80 API_PATH, | 87 API_PATH, |
81 PUBLIC_TEMPLATE_PATH) | 88 PUBLIC_TEMPLATE_PATH) |
82 intro_data_source = IntroDataSource(cache_builder, | 89 intro_data_source_factory = IntroDataSource.Factory( |
83 [INTRO_PATH, ARTICLE_PATH]) | 90 cache_builder, |
91 [INTRO_PATH, ARTICLE_PATH]) | |
84 samples_data_source_factory = SamplesDataSource.Factory(branch, | 92 samples_data_source_factory = SamplesDataSource.Factory(branch, |
85 file_system, | 93 file_system, |
86 GITHUB_FILE_SYSTEM, | 94 GITHUB_FILE_SYSTEM, |
87 cache_builder, | 95 cache_builder, |
88 GITHUB_CACHE_BUILDER, | 96 GITHUB_CACHE_BUILDER, |
89 EXAMPLES_PATH) | 97 EXAMPLES_PATH) |
90 api_data_source_factory = APIDataSource.Factory(cache_builder, | 98 api_data_source_factory = APIDataSource.Factory(cache_builder, |
91 API_PATH, | 99 API_PATH, |
92 samples_data_source_factory) | 100 samples_data_source_factory) |
93 template_data_source_factory = TemplateDataSource.Factory( | 101 template_data_source_factory = TemplateDataSource.Factory( |
94 channel_name, | 102 channel_name, |
95 api_data_source_factory, | 103 api_data_source_factory, |
96 api_list_data_source, | 104 api_list_data_source_factory, |
97 intro_data_source, | 105 intro_data_source_factory, |
98 samples_data_source_factory, | 106 samples_data_source_factory, |
99 cache_builder, | 107 cache_builder, |
100 PUBLIC_TEMPLATE_PATH, | 108 PUBLIC_TEMPLATE_PATH, |
101 PRIVATE_TEMPLATE_PATH) | 109 PRIVATE_TEMPLATE_PATH) |
102 example_zipper = ExampleZipper(file_system, | 110 example_zipper = ExampleZipper(file_system, |
103 cache_builder, | 111 cache_builder, |
104 DOCS_PATH, | 112 DOCS_PATH, |
105 EXAMPLES_PATH) | 113 EXAMPLES_PATH) |
106 SERVER_INSTANCES[branch] = ServerInstance( | 114 SERVER_INSTANCES[branch] = ServerInstance( |
107 template_data_source_factory, | 115 template_data_source_factory, |
108 example_zipper, | 116 example_zipper, |
109 cache_builder) | 117 cache_builder) |
110 return SERVER_INSTANCES[branch] | 118 return SERVER_INSTANCES[branch] |
111 | 119 |
112 def _GetURLFromBranch(branch): | 120 def _GetURLFromBranch(branch): |
113 if branch == 'trunk': | 121 if branch == 'trunk': |
114 return url_constants.SVN_TRUNK_URL + '/src' | 122 return url_constants.SVN_TRUNK_URL + '/src' |
115 return url_constants.SVN_BRANCH_URL + '/' + branch + '/src' | 123 return url_constants.SVN_BRANCH_URL + '/' + branch + '/src' |
116 | 124 |
117 def _CleanBranches(): | 125 def _CleanBranches(): |
118 numbers = BRANCH_UTILITY.GetAllBranchNumbers() | 126 numbers = BRANCH_UTILITY.GetAllBranchNumbers() |
119 for key in SERVER_INSTANCES.keys(): | 127 for key in SERVER_INSTANCES.keys(): |
120 if key not in numbers: | 128 if key not in numbers: |
121 SERVER_INSTANCES.pop(key) | 129 SERVER_INSTANCES.pop(key) |
122 | 130 |
131 class _MockResponse(object): | |
132 def __init__(self): | |
133 self.status = 200 | |
134 self.out = StringIO() | |
135 | |
136 def set_status(self, status): | |
137 self.status = status | |
138 | |
139 class _MockRequest(object): | |
140 def __init__(self, path): | |
141 self.headers = {} | |
142 self.path = path | |
143 | |
123 class Handler(webapp.RequestHandler): | 144 class Handler(webapp.RequestHandler): |
124 def __init__(self, request, response, local_path=EXTENSIONS_PATH): | 145 def __init__(self, request, response, local_path=EXTENSIONS_PATH): |
125 self._local_path = local_path | 146 self._local_path = local_path |
126 super(Handler, self).__init__(request, response) | 147 super(Handler, self).__init__(request, response) |
127 | 148 |
128 def _NavigateToPath(self, path): | 149 def _NavigateToPath(self, path): |
not at google - send to devlin
2012/08/20 05:27:10
this name always trips me up. Can we call it _Hand
cduvall
2012/08/20 21:28:09
Done.
| |
129 channel_name, real_path = BRANCH_UTILITY.SplitChannelNameFromPath(path) | 150 channel_name, real_path = BRANCH_UTILITY.SplitChannelNameFromPath(path) |
130 # TODO: Detect that these are directories and serve index.html out of them. | 151 # TODO: Detect that these are directories and serve index.html out of them. |
131 if real_path.strip('/') == 'apps': | 152 if real_path.strip('/') == 'apps': |
132 real_path = 'apps/index.html' | 153 real_path = 'apps/index.html' |
133 if real_path.strip('/') == 'extensions': | 154 if real_path.strip('/') == 'extensions': |
134 real_path = 'extensions/index.html' | 155 real_path = 'extensions/index.html' |
135 _CleanBranches() | 156 _CleanBranches() |
136 _GetInstanceForBranch(channel_name, self._local_path).Get(real_path, | 157 _GetInstanceForBranch(channel_name, self._local_path).Get(real_path, |
137 self.request, | 158 self.request, |
138 self.response) | 159 self.response) |
139 | 160 |
161 def _Render(self, files, branch): | |
162 for f in files: | |
163 path = branch + f.split(PUBLIC_TEMPLATE_PATH)[-1] | |
164 self.request = _MockRequest(path) | |
165 self.response = _MockResponse() | |
166 self._NavigateToPath(path) | |
167 | |
168 def _Cron(self, files): | |
169 self._file_system.Read(files).Get() | |
170 | |
140 def get(self): | 171 def get(self): |
141 path = self.request.path | 172 path = self.request.path |
142 if '_ah/warmup' in path: | 173 if path.startswith('/cron'): |
not at google - send to devlin
2012/08/20 05:27:10
nit: can we split up these two distinct cases (cro
cduvall
2012/08/20 21:28:09
Done.
| |
143 logging.info('Warmup request.') | 174 branch = path.split('/')[-1] |
144 self._NavigateToPath('trunk/extensions/samples.html') | 175 logging.info('Running cron job for %s.' % branch) |
145 self._NavigateToPath('dev/extensions/samples.html') | 176 branch_memcache = AppEngineMemcache(branch) |
146 self._NavigateToPath('beta/extensions/samples.html') | 177 self._file_system = _CreateMemcacheFileSystem(branch, branch_memcache) |
not at google - send to devlin
2012/08/20 05:27:10
file_system doesn't need to be on self, we can def
cduvall
2012/08/20 21:28:09
Done.
| |
147 self._NavigateToPath('stable/extensions/samples.html') | 178 builder = FileSystemCache.Builder(self._file_system, branch_memcache) |
148 # Only do this request if we are on the deployed server. | 179 cache = builder.build(self._Cron, fs_cache.FS_CACHE_CRON) |
149 # Bug: http://crbug.com/141910 | 180 render_cache = builder.build(lambda x: self._Render(x, branch), |
150 if DEFAULT_BRANCH != 'local': | 181 fs_cache.FS_CACHE_RENDER) |
151 self._NavigateToPath('apps/samples.html') | 182 cache.GetFromFileListing(TEMPLATE_PATH) |
183 cache.GetFromFileListing(API_PATH) | |
184 cache.GetFromFileListing(EXAMPLES_PATH) | |
185 render_cache.GetFromFileListing(PUBLIC_TEMPLATE_PATH) | |
not at google - send to devlin
2012/08/20 05:27:10
why does rendering all files from the public templ
cduvall
2012/08/20 21:28:09
I just thought it would be a little faster getting
not at google - send to devlin
2012/08/21 00:30:11
Interesting. Did you find that to be the case? If
cduvall
2012/08/21 01:33:33
I don't think it makes too much of a difference, s
| |
152 return | 186 return |
153 | 187 |
154 # Redirect paths like "directory" to "directory/". This is so relative file | 188 # Redirect paths like "directory" to "directory/". This is so relative file |
155 # paths will know to treat this as a directory. | 189 # paths will know to treat this as a directory. |
156 if os.path.splitext(path)[1] == '' and path[-1] != '/': | 190 if os.path.splitext(path)[1] == '' and path[-1] != '/': |
157 self.redirect(path + '/') | 191 self.redirect(path + '/') |
158 path = path.replace('/chrome/', '') | 192 path = path.replace('/chrome/', '') |
159 path = path.strip('/') | 193 path = path.strip('/') |
160 self._NavigateToPath(path) | 194 self._NavigateToPath(path) |
OLD | NEW |