Index: chrome/common/extensions/docs/server2/cron_servlet.py |
diff --git a/chrome/common/extensions/docs/server2/cron_servlet.py b/chrome/common/extensions/docs/server2/cron_servlet.py |
index e76f07423e2865d738b86011246ca2ca13edfca2..7b3488153221f1be247fb27f9724dd9ceb7d604f 100644 |
--- a/chrome/common/extensions/docs/server2/cron_servlet.py |
+++ b/chrome/common/extensions/docs/server2/cron_servlet.py |
@@ -6,9 +6,12 @@ import logging |
import time |
import traceback |
-from appengine_wrappers import DeadlineExceededError, IsDevServer, logservice |
+from app_yaml_helper import AppYamlHelper |
+from appengine_wrappers import ( |
+ GetAppVersion, DeadlineExceededError, IsDevServer, logservice) |
from branch_utility import BranchUtility |
from caching_file_system import CachingFileSystem |
+from empty_dir_file_system import EmptyDirFileSystem |
from github_file_system import GithubFileSystem |
from object_store_creator import ObjectStoreCreator |
from render_servlet import RenderServlet |
@@ -18,20 +21,6 @@ from subversion_file_system import SubversionFileSystem |
import svn_constants |
from third_party.json_schema_compiler.memoize import memoize |
-def _CreateServerInstanceForChannel(channel, delegate): |
- object_store_creator = ObjectStoreCreator(channel, start_empty=True) |
- branch = (delegate.CreateBranchUtility(object_store_creator) |
- .GetBranchForChannel(channel)) |
- host_file_system = CachingFileSystem( |
- delegate.CreateHostFileSystemForBranch(branch), |
- object_store_creator) |
- app_samples_file_system = delegate.CreateAppSamplesFileSystem( |
- object_store_creator) |
- return ServerInstance(channel, |
- object_store_creator, |
- host_file_system, |
- app_samples_file_system) |
- |
class _SingletonRenderServletDelegate(RenderServlet.Delegate): |
def __init__(self, server_instance): |
self._server_instance = server_instance |
@@ -44,16 +33,17 @@ class CronServlet(Servlet): |
''' |
def __init__(self, request, delegate_for_test=None): |
Servlet.__init__(self, request) |
+ self._channel = request.path.strip('/') |
self._delegate = delegate_for_test or CronServlet.Delegate() |
class Delegate(object): |
- '''Allow runtime dependencies to be overridden for testing. |
+ '''CronServlet's runtime dependencies. Override for testing. |
''' |
def CreateBranchUtility(self, object_store_creator): |
return BranchUtility.Create(object_store_creator) |
- def CreateHostFileSystemForBranch(self, branch): |
- return SubversionFileSystem.Create(branch) |
+ def CreateHostFileSystemForBranchAndRevision(self, branch, revision): |
+ return SubversionFileSystem.Create(branch, revision=revision) |
def CreateAppSamplesFileSystem(self, object_store_creator): |
# TODO(kalman): CachingFileSystem wrapper for GithubFileSystem, but it's |
@@ -61,6 +51,9 @@ class CronServlet(Servlet): |
return (EmptyDirFileSystem() if IsDevServer() else |
GithubFileSystem.Create(object_store_creator)) |
+ def GetAppVersion(self): |
+ return GetAppVersion() |
+ |
def Get(self): |
# Crons often time out, and when they do *and* then eventually try to |
# flush logs they die. Turn off autoflush and manually do so at the end. |
@@ -77,12 +70,12 @@ class CronServlet(Servlet): |
# the time these won't have changed since the last cron run, so it's a |
# little wasteful, but hopefully rendering is really fast (if it isn't we |
# have a problem). |
- channel = self._request.path.strip('/') |
+ channel = self._channel |
logging.info('cron/%s: starting' % channel) |
# This is returned every time RenderServlet wants to create a new |
# ServerInstance. |
- server_instance = _CreateServerInstanceForChannel(channel, self._delegate) |
+ server_instance = self._GetSafeServerInstance() |
def get_via_render_servlet(path): |
return RenderServlet( |
@@ -123,18 +116,23 @@ class CronServlet(Servlet): |
success = True |
try: |
# Render all of the publicly accessible files. |
- for path, path_prefix in ( |
- # Note: rendering the public templates will pull in all of the private |
- # templates. |
- (svn_constants.PUBLIC_TEMPLATE_PATH, ''), |
- # Note: rendering the public templates will have pulled in the .js |
- # and manifest.json files (for listing examples on the API reference |
- # pages), but there are still images, CSS, etc. |
- (svn_constants.STATIC_PATH, 'static/'), |
- (svn_constants.EXAMPLES_PATH, 'extensions/examples/')): |
- # Note: don't try to short circuit any of this stuff. We want to run |
- # the cron for all the directories regardless of intermediate |
- # failures. |
+ cron_runs = [ |
+ # Note: rendering the public templates will pull in all of the private |
+ # templates. |
+ (svn_constants.PUBLIC_TEMPLATE_PATH, ''), |
+ # Note: rendering the public templates will have pulled in the .js |
+ # and manifest.json files (for listing examples on the API reference |
+ # pages), but there are still images, CSS, etc. |
+ (svn_constants.STATIC_PATH, 'static/'), |
+ ] |
+ if not IsDevServer(): |
+ cron_runs.append( |
+ (svn_constants.EXAMPLES_PATH, 'extensions/examples/')) |
+ |
+ # Note: don't try to short circuit any of this stuff. We want to run |
+ # the cron for all the directories regardless of intermediate |
+ # failures. |
+ for path, path_prefix in cron_runs: |
success = run_cron_for_dir(path, path_prefix=path_prefix) and success |
# TODO(kalman): Generic way for classes to request cron access. The next |
@@ -143,22 +141,23 @@ class CronServlet(Servlet): |
# Extension examples have zip files too. Well, so do apps, but the app |
# file system doesn't get the Offline treatment so they don't need cron. |
- manifest_json = '/manifest.json' |
- example_zips = [ |
- '%s.zip' % filename[:-len(manifest_json)] |
- for filename in server_instance.content_cache.GetFromFileListing( |
- svn_constants.EXAMPLES_PATH) |
- if filename.endswith(manifest_json)] |
- logging.info('cron/%s: rendering %s example zips...' % ( |
- channel, len(example_zips))) |
- start_time = time.time() |
- try: |
- success = success and all( |
- get_via_render_servlet('extensions/examples/%s' % z).status == 200 |
- for z in example_zips) |
- finally: |
- logging.info('cron/%s: rendering %s example zips took %s seconds' % ( |
- channel, len(example_zips), time.time() - start_time)) |
+ if not IsDevServer(): |
+ manifest_json = '/manifest.json' |
+ example_zips = [ |
+ '%s.zip' % filename[:-len(manifest_json)] |
+ for filename in server_instance.content_cache.GetFromFileListing( |
+ svn_constants.EXAMPLES_PATH) |
+ if filename.endswith(manifest_json)] |
+ logging.info('cron/%s: rendering %s example zips...' % ( |
+ channel, len(example_zips))) |
+ start_time = time.time() |
+ try: |
+ success = success and all( |
+ get_via_render_servlet('extensions/examples/%s' % z).status == 200 |
+ for z in example_zips) |
+ finally: |
+ logging.info('cron/%s: rendering %s example zips took %s seconds' % ( |
+ channel, len(example_zips), time.time() - start_time)) |
# Also trigger a redirect so that PathCanonicalizer has an opportunity to |
# cache file listings. |
@@ -172,3 +171,61 @@ class CronServlet(Servlet): |
return (Response.Ok('Success') if success else |
Response.InternalError('Failure')) |
+ |
+ def _GetSafeServerInstance(self): |
+ '''Returns a ServerInstance with a host file system at a safe revision, |
+ meaning the last revision that the current running version of the server |
+ existed. |
+ ''' |
+ channel = self._channel |
+ delegate = self._delegate |
+ |
+ server_instance_at_head = self._CreateServerInstance(channel, None) |
+ |
+ get_branch_for_channel = self._GetBranchForChannel |
cduvall
2013/05/10 06:45:19
is this line necessary? does it not like you using
not at google - send to devlin
2013/05/10 17:49:58
Yeah, I either do this or save a reference to Cron
|
+ class AppYamlHelperDelegate(AppYamlHelper.Delegate): |
+ def GetHostFileSystemForRevision(self, revision): |
+ return delegate.CreateHostFileSystemForBranchAndRevision( |
+ get_branch_for_channel(channel), |
+ revision) |
+ |
+ app_yaml_handler = AppYamlHelper( |
+ svn_constants.APP_YAML_PATH, |
+ server_instance_at_head.host_file_system, |
+ AppYamlHelperDelegate(), |
+ server_instance_at_head.object_store_creator) |
+ |
+ if app_yaml_handler.IsUpToDate(delegate.GetAppVersion()): |
+ return server_instance_at_head |
方觉(Fang Jue)
2013/05/10 08:49:17
I was wondering what will happen if at this point
not at google - send to devlin
2013/05/10 17:49:58
Yes true. I have a bug filed about this. There's a
|
+ |
+ # The version in app.yaml is greater than the currently running app's. |
+ # The safe version is the one before it changed. |
+ safe_revision = app_yaml_handler.GetFirstRevisionGreaterThan( |
+ delegate.GetAppVersion()) - 1 |
+ |
+ logging.info('cron/%s: app version %s is out of date, safe is %s' % ( |
+ channel, delegate.GetAppVersion(), safe_revision)) |
+ |
+ return self._CreateServerInstance(channel, safe_revision) |
+ |
+ def _CreateObjectStoreCreator(self, channel): |
+ return ObjectStoreCreator(channel, start_empty=True) |
+ |
+ def _GetBranchForChannel(self, channel): |
+ object_store_creator = self._CreateObjectStoreCreator(channel) |
+ return (self._delegate.CreateBranchUtility(object_store_creator) |
+ .GetBranchForChannel(channel)) |
+ |
+ def _CreateServerInstance(self, channel, revision): |
+ object_store_creator = self._CreateObjectStoreCreator(channel) |
+ host_file_system = CachingFileSystem( |
+ self._delegate.CreateHostFileSystemForBranchAndRevision( |
+ self._GetBranchForChannel(channel), |
+ revision), |
+ object_store_creator) |
+ app_samples_file_system = self._delegate.CreateAppSamplesFileSystem( |
+ object_store_creator) |
+ return ServerInstance(channel, |
+ object_store_creator, |
+ host_file_system, |
+ app_samples_file_system) |