Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(381)

Side by Side Diff: chrome/common/extensions/docs/server2/cron_servlet.py

Issue 14247024: Devserver: allow SubversionFileSystem to be pinned to a specific rev on construction (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: comments Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 # Copyright 2013 The Chromium Authors. All rights reserved. 1 # Copyright 2013 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 time 6 import time
7 import traceback 7 import traceback
8 8
9 from appengine_wrappers import DeadlineExceededError, IsDevServer, logservice 9 from app_yaml_helper import AppYamlHelper
10 from appengine_wrappers import (
11 GetAppVersion, DeadlineExceededError, IsDevServer, logservice)
10 from branch_utility import BranchUtility 12 from branch_utility import BranchUtility
11 from caching_file_system import CachingFileSystem 13 from caching_file_system import CachingFileSystem
14 from empty_dir_file_system import EmptyDirFileSystem
12 from github_file_system import GithubFileSystem 15 from github_file_system import GithubFileSystem
13 from object_store_creator import ObjectStoreCreator 16 from object_store_creator import ObjectStoreCreator
14 from render_servlet import RenderServlet 17 from render_servlet import RenderServlet
15 from server_instance import ServerInstance 18 from server_instance import ServerInstance
16 from servlet import Servlet, Request, Response 19 from servlet import Servlet, Request, Response
17 from subversion_file_system import SubversionFileSystem 20 from subversion_file_system import SubversionFileSystem
18 import svn_constants 21 import svn_constants
19 from third_party.json_schema_compiler.memoize import memoize 22 from third_party.json_schema_compiler.memoize import memoize
20 23
21 def _CreateServerInstanceForChannel(channel, delegate):
22 object_store_creator = ObjectStoreCreator(channel, start_empty=True)
23 branch = (delegate.CreateBranchUtility(object_store_creator)
24 .GetBranchForChannel(channel))
25 host_file_system = CachingFileSystem(
26 delegate.CreateHostFileSystemForBranch(branch),
27 object_store_creator)
28 app_samples_file_system = delegate.CreateAppSamplesFileSystem(
29 object_store_creator)
30 return ServerInstance(channel,
31 object_store_creator,
32 host_file_system,
33 app_samples_file_system)
34
35 class _SingletonRenderServletDelegate(RenderServlet.Delegate): 24 class _SingletonRenderServletDelegate(RenderServlet.Delegate):
36 def __init__(self, server_instance): 25 def __init__(self, server_instance):
37 self._server_instance = server_instance 26 self._server_instance = server_instance
38 27
39 def CreateServerInstanceForChannel(self, channel): 28 def CreateServerInstanceForChannel(self, channel):
40 return self._server_instance 29 return self._server_instance
41 30
42 class CronServlet(Servlet): 31 class CronServlet(Servlet):
43 '''Servlet which runs a cron job. 32 '''Servlet which runs a cron job.
44 ''' 33 '''
45 def __init__(self, request, delegate_for_test=None): 34 def __init__(self, request, delegate_for_test=None):
46 Servlet.__init__(self, request) 35 Servlet.__init__(self, request)
36 self._channel = request.path.strip('/')
47 self._delegate = delegate_for_test or CronServlet.Delegate() 37 self._delegate = delegate_for_test or CronServlet.Delegate()
48 38
49 class Delegate(object): 39 class Delegate(object):
50 '''Allow runtime dependencies to be overridden for testing. 40 '''CronServlet's runtime dependencies. Override for testing.
51 ''' 41 '''
52 def CreateBranchUtility(self, object_store_creator): 42 def CreateBranchUtility(self, object_store_creator):
53 return BranchUtility.Create(object_store_creator) 43 return BranchUtility.Create(object_store_creator)
54 44
55 def CreateHostFileSystemForBranch(self, branch): 45 def CreateHostFileSystemForBranchAndRevision(self, branch, revision):
56 return SubversionFileSystem.Create(branch) 46 return SubversionFileSystem.Create(branch, revision=revision)
57 47
58 def CreateAppSamplesFileSystem(self, object_store_creator): 48 def CreateAppSamplesFileSystem(self, object_store_creator):
59 # TODO(kalman): CachingFileSystem wrapper for GithubFileSystem, but it's 49 # TODO(kalman): CachingFileSystem wrapper for GithubFileSystem, but it's
60 # not supported yet (see comment there). 50 # not supported yet (see comment there).
61 return (EmptyDirFileSystem() if IsDevServer() else 51 return (EmptyDirFileSystem() if IsDevServer() else
62 GithubFileSystem.Create(object_store_creator)) 52 GithubFileSystem.Create(object_store_creator))
63 53
54 def GetAppVersion(self):
55 return GetAppVersion()
56
64 def Get(self): 57 def Get(self):
65 # Crons often time out, and when they do *and* then eventually try to 58 # Crons often time out, and when they do *and* then eventually try to
66 # flush logs they die. Turn off autoflush and manually do so at the end. 59 # flush logs they die. Turn off autoflush and manually do so at the end.
67 logservice.AUTOFLUSH_ENABLED = False 60 logservice.AUTOFLUSH_ENABLED = False
68 try: 61 try:
69 return self._GetImpl() 62 return self._GetImpl()
70 finally: 63 finally:
71 logservice.flush() 64 logservice.flush()
72 65
73 def _GetImpl(self): 66 def _GetImpl(self):
74 # Cron strategy: 67 # Cron strategy:
75 # 68 #
76 # Find all public template files and static files, and render them. Most of 69 # Find all public template files and static files, and render them. Most of
77 # the time these won't have changed since the last cron run, so it's a 70 # the time these won't have changed since the last cron run, so it's a
78 # little wasteful, but hopefully rendering is really fast (if it isn't we 71 # little wasteful, but hopefully rendering is really fast (if it isn't we
79 # have a problem). 72 # have a problem).
80 channel = self._request.path.strip('/') 73 channel = self._channel
81 logging.info('cron/%s: starting' % channel) 74 logging.info('cron/%s: starting' % channel)
82 75
83 # This is returned every time RenderServlet wants to create a new 76 # This is returned every time RenderServlet wants to create a new
84 # ServerInstance. 77 # ServerInstance.
85 server_instance = _CreateServerInstanceForChannel(channel, self._delegate) 78 server_instance = self._GetSafeServerInstance()
86 79
87 def get_via_render_servlet(path): 80 def get_via_render_servlet(path):
88 return RenderServlet( 81 return RenderServlet(
89 Request(path, self._request.host, self._request.headers), 82 Request(path, self._request.host, self._request.headers),
90 _SingletonRenderServletDelegate(server_instance)).Get() 83 _SingletonRenderServletDelegate(server_instance)).Get()
91 84
92 def run_cron_for_dir(d, path_prefix=''): 85 def run_cron_for_dir(d, path_prefix=''):
93 success = True 86 success = True
94 start_time = time.time() 87 start_time = time.time()
95 files = [f for f in server_instance.content_cache.GetFromFileListing(d) 88 files = [f for f in server_instance.content_cache.GetFromFileListing(d)
(...skipping 20 matching lines...) Expand all
116 channel, path, error)) 109 channel, path, error))
117 success = False 110 success = False
118 finally: 111 finally:
119 logging.info('cron/%s: rendering %s files from %s took %s seconds' % ( 112 logging.info('cron/%s: rendering %s files from %s took %s seconds' % (
120 channel, len(files), d, time.time() - start_time)) 113 channel, len(files), d, time.time() - start_time))
121 return success 114 return success
122 115
123 success = True 116 success = True
124 try: 117 try:
125 # Render all of the publicly accessible files. 118 # Render all of the publicly accessible files.
126 for path, path_prefix in ( 119 cron_runs = [
127 # Note: rendering the public templates will pull in all of the private 120 # Note: rendering the public templates will pull in all of the private
128 # templates. 121 # templates.
129 (svn_constants.PUBLIC_TEMPLATE_PATH, ''), 122 (svn_constants.PUBLIC_TEMPLATE_PATH, ''),
130 # Note: rendering the public templates will have pulled in the .js 123 # Note: rendering the public templates will have pulled in the .js
131 # and manifest.json files (for listing examples on the API reference 124 # and manifest.json files (for listing examples on the API reference
132 # pages), but there are still images, CSS, etc. 125 # pages), but there are still images, CSS, etc.
133 (svn_constants.STATIC_PATH, 'static/'), 126 (svn_constants.STATIC_PATH, 'static/'),
134 (svn_constants.EXAMPLES_PATH, 'extensions/examples/')): 127 ]
135 # Note: don't try to short circuit any of this stuff. We want to run 128 if not IsDevServer():
136 # the cron for all the directories regardless of intermediate 129 cron_runs.append(
137 # failures. 130 (svn_constants.EXAMPLES_PATH, 'extensions/examples/'))
131
132 # Note: don't try to short circuit any of this stuff. We want to run
133 # the cron for all the directories regardless of intermediate
134 # failures.
135 for path, path_prefix in cron_runs:
138 success = run_cron_for_dir(path, path_prefix=path_prefix) and success 136 success = run_cron_for_dir(path, path_prefix=path_prefix) and success
139 137
140 # TODO(kalman): Generic way for classes to request cron access. The next 138 # TODO(kalman): Generic way for classes to request cron access. The next
141 # two special cases are ugly. It would potentially greatly speed up cron 139 # two special cases are ugly. It would potentially greatly speed up cron
142 # runs, too. 140 # runs, too.
143 141
144 # Extension examples have zip files too. Well, so do apps, but the app 142 # Extension examples have zip files too. Well, so do apps, but the app
145 # file system doesn't get the Offline treatment so they don't need cron. 143 # file system doesn't get the Offline treatment so they don't need cron.
146 manifest_json = '/manifest.json' 144 if not IsDevServer():
147 example_zips = [ 145 manifest_json = '/manifest.json'
148 '%s.zip' % filename[:-len(manifest_json)] 146 example_zips = [
149 for filename in server_instance.content_cache.GetFromFileListing( 147 '%s.zip' % filename[:-len(manifest_json)]
150 svn_constants.EXAMPLES_PATH) 148 for filename in server_instance.content_cache.GetFromFileListing(
151 if filename.endswith(manifest_json)] 149 svn_constants.EXAMPLES_PATH)
152 logging.info('cron/%s: rendering %s example zips...' % ( 150 if filename.endswith(manifest_json)]
153 channel, len(example_zips))) 151 logging.info('cron/%s: rendering %s example zips...' % (
154 start_time = time.time() 152 channel, len(example_zips)))
155 try: 153 start_time = time.time()
156 success = success and all( 154 try:
157 get_via_render_servlet('extensions/examples/%s' % z).status == 200 155 success = success and all(
158 for z in example_zips) 156 get_via_render_servlet('extensions/examples/%s' % z).status == 200
159 finally: 157 for z in example_zips)
160 logging.info('cron/%s: rendering %s example zips took %s seconds' % ( 158 finally:
161 channel, len(example_zips), time.time() - start_time)) 159 logging.info('cron/%s: rendering %s example zips took %s seconds' % (
160 channel, len(example_zips), time.time() - start_time))
162 161
163 # Also trigger a redirect so that PathCanonicalizer has an opportunity to 162 # Also trigger a redirect so that PathCanonicalizer has an opportunity to
164 # cache file listings. 163 # cache file listings.
165 logging.info('cron/%s: triggering a redirect...' % channel) 164 logging.info('cron/%s: triggering a redirect...' % channel)
166 redirect_response = get_via_render_servlet('storage.html') 165 redirect_response = get_via_render_servlet('storage.html')
167 success = success and redirect_response.status == 302 166 success = success and redirect_response.status == 302
168 except DeadlineExceededError: 167 except DeadlineExceededError:
169 success = False 168 success = False
170 169
171 logging.info('cron/%s: finished' % channel) 170 logging.info('cron/%s: finished' % channel)
172 171
173 return (Response.Ok('Success') if success else 172 return (Response.Ok('Success') if success else
174 Response.InternalError('Failure')) 173 Response.InternalError('Failure'))
174
175 def _GetSafeServerInstance(self):
176 '''Returns a ServerInstance with a host file system at a safe revision,
177 meaning the last revision that the current running version of the server
178 existed.
179 '''
180 channel = self._channel
181 delegate = self._delegate
182
183 server_instance_at_head = self._CreateServerInstance(channel, None)
184
185 get_branch_for_channel = self._GetBranchForChannel
186 class AppYamlHelperDelegate(AppYamlHelper.Delegate):
187 def GetHostFileSystemForRevision(self, revision):
188 return delegate.CreateHostFileSystemForBranchAndRevision(
189 get_branch_for_channel(channel),
190 revision)
191
192 app_yaml_handler = AppYamlHelper(
193 svn_constants.APP_YAML_PATH,
194 server_instance_at_head.host_file_system,
195 AppYamlHelperDelegate(),
196 server_instance_at_head.object_store_creator)
197
198 if app_yaml_handler.IsUpToDate(delegate.GetAppVersion()):
199 # TODO(kalman): return a new ServerInstance at an explicit revision in
200 # case the HEAD version changes underneath us.
201 return server_instance_at_head
202
203 # The version in app.yaml is greater than the currently running app's.
204 # The safe version is the one before it changed.
205 safe_revision = app_yaml_handler.GetFirstRevisionGreaterThan(
206 delegate.GetAppVersion()) - 1
207
208 logging.info('cron/%s: app version %s is out of date, safe is %s' % (
209 channel, delegate.GetAppVersion(), safe_revision))
210
211 return self._CreateServerInstance(channel, safe_revision)
212
213 def _CreateObjectStoreCreator(self, channel):
214 return ObjectStoreCreator(channel, start_empty=True)
215
216 def _GetBranchForChannel(self, channel):
217 object_store_creator = self._CreateObjectStoreCreator(channel)
218 return (self._delegate.CreateBranchUtility(object_store_creator)
219 .GetBranchForChannel(channel))
220
221 def _CreateServerInstance(self, channel, revision):
222 object_store_creator = self._CreateObjectStoreCreator(channel)
223 host_file_system = CachingFileSystem(
224 self._delegate.CreateHostFileSystemForBranchAndRevision(
225 self._GetBranchForChannel(channel),
226 revision),
227 object_store_creator)
228 app_samples_file_system = self._delegate.CreateAppSamplesFileSystem(
229 object_store_creator)
230 return ServerInstance(channel,
231 object_store_creator,
232 host_file_system,
233 app_samples_file_system)
OLDNEW
« no previous file with comments | « chrome/common/extensions/docs/server2/cron.yaml ('k') | chrome/common/extensions/docs/server2/cron_servlet_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698