1c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)# Copyright 2013 The Chromium Authors. All rights reserved. 2c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 3c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)# found in the LICENSE file. 4c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import logging 6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import traceback 7c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 8a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)from app_yaml_helper import AppYamlHelper 9a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)from appengine_wrappers import ( 100f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) GetAppVersion, IsDeadlineExceededError, logservice) 11c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)from branch_utility import BranchUtility 12a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)from compiled_file_system import CompiledFileSystem 13424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)from data_source_registry import CreateDataSources 140f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)from environment import IsDevServer 15d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)from extensions_paths import EXAMPLES, PUBLIC_TEMPLATES, STATIC_DOCS 16ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochfrom file_system_util import CreateURLsFromPaths 17f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)from future import Gettable, Future 181e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)from github_file_system_provider import GithubFileSystemProvider 194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)from host_file_system_provider import HostFileSystemProvider 20b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)from object_store_creator import ObjectStoreCreator 21c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)from render_servlet import RenderServlet 22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)from server_instance import ServerInstance 23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)from servlet import Servlet, Request, Response 24f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)from timer import Timer, TimerClosure 25f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 26b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 27b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)class _SingletonRenderServletDelegate(RenderServlet.Delegate): 28b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def __init__(self, server_instance): 29b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) self._server_instance = server_instance 30b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 31ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch def CreateServerInstance(self): 32b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return self._server_instance 33c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 343551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)class _CronLogger(object): 353551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) '''Wraps the logging.* methods to prefix them with 'cron' and flush 363551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) immediately. The flushing is important because often these cron runs time 373551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) out and we lose the logs. 383551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) ''' 393551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) def info(self, msg, *args): self._log(logging.info, msg, args) 403551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) def warning(self, msg, *args): self._log(logging.warning, msg, args) 413551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) def error(self, msg, *args): self._log(logging.error, msg, args) 423551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 433551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) def _log(self, logfn, msg, args): 443551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) try: 453551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) logfn('cron: %s' % msg, *args) 463551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) finally: 473551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) logservice.flush() 483551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 493551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)_cronlog = _CronLogger() 503551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 513551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)def _RequestEachItem(title, items, request_callback): 523551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) '''Runs a task |request_callback| named |title| for each item in |items|. 533551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) |request_callback| must take an item and return a servlet response. 543551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) Returns true if every item was successfully run, false if any return a 553551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) non-200 response or raise an exception. 563551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) ''' 573551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) _cronlog.info('%s: starting', title) 583551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) success_count, failure_count = 0, 0 59f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) timer = Timer() 603551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) try: 613551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) for i, item in enumerate(items): 623551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) def error_message(detail): 633551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) return '%s: error rendering %s (%s of %s): %s' % ( 643551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) title, item, i + 1, len(items), detail) 653551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) try: 663551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) response = request_callback(item) 673551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) if response.status == 200: 683551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) success_count += 1 693551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) else: 704e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) _cronlog.error(error_message('response status %s' % response.status)) 713551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) failure_count += 1 723551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) except Exception as e: 733551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) _cronlog.error(error_message(traceback.format_exc())) 743551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) failure_count += 1 753551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) if IsDeadlineExceededError(e): raise 763551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) finally: 77f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) _cronlog.info('%s: rendered %s of %s with %s failures in %s', 78f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) title, success_count, len(items), failure_count, 79f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) timer.Stop().FormatElapsed()) 803551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) return success_count == len(items) 813551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)class CronServlet(Servlet): 83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) '''Servlet which runs a cron job. 84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) ''' 85b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def __init__(self, request, delegate_for_test=None): 86b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) Servlet.__init__(self, request) 87b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) self._delegate = delegate_for_test or CronServlet.Delegate() 88b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 89b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) class Delegate(object): 90a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) '''CronServlet's runtime dependencies. Override for testing. 91b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) ''' 92b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def CreateBranchUtility(self, object_store_creator): 93b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return BranchUtility.Create(object_store_creator) 94b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) def CreateHostFileSystemProvider(self, 964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) object_store_creator, 974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) max_trunk_revision=None): 984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) return HostFileSystemProvider(object_store_creator, 994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) max_trunk_revision=max_trunk_revision) 100b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 1011e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) def CreateGithubFileSystemProvider(self, object_store_creator): 1021e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return GithubFileSystemProvider(object_store_creator) 103b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 104a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) def GetAppVersion(self): 105a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) return GetAppVersion() 106a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) def Get(self): 1083551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # Crons often time out, and if they do we need to make sure to flush the 1093551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # logs before the process gets killed (Python gives us a couple of 1103551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # seconds). 1113551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # 1123551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # So, manually flush logs at the end of the cron run. However, sometimes 1133551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # even that isn't enough, which is why in this file we use _cronlog and 1143551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # make it flush the log every time its used. 115c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) logservice.AUTOFLUSH_ENABLED = False 116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) try: 117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return self._GetImpl() 1183551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) except BaseException: 1193551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) _cronlog.error('Caught top-level exception! %s', traceback.format_exc()) 120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) finally: 121c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) logservice.flush() 122c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 123c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) def _GetImpl(self): 124c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) # Cron strategy: 125c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) # 126c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) # Find all public template files and static files, and render them. Most of 127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) # the time these won't have changed since the last cron run, so it's a 128c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) # little wasteful, but hopefully rendering is really fast (if it isn't we 129c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) # have a problem). 1303551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) _cronlog.info('starting') 131c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 132b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # This is returned every time RenderServlet wants to create a new 133b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # ServerInstance. 1343551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # 1353551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # TODO(kalman): IMPORTANT. This sometimes throws an exception, breaking 1363551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # everything. Need retry logic at the fetcher level. 137a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) server_instance = self._GetSafeServerInstance() 1384e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) trunk_fs = server_instance.host_file_system_provider.GetTrunk() 139b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 1403551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) def render(path): 141eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch request = Request(path, self._request.host, self._request.headers) 142eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch delegate = _SingletonRenderServletDelegate(server_instance) 143eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch return RenderServlet(request, delegate).Get() 144c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 1453551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) def request_files_in_dir(path, prefix=''): 1463551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) '''Requests every file found under |path| in this host file system, with 1473551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) a request prefix of |prefix|. 1483551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) ''' 1494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) files = [name for name, _ in CreateURLsFromPaths(trunk_fs, path, prefix)] 1503551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) return _RequestEachItem(path, files, render) 1513551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 1523551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) results = [] 153a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 1543551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) try: 155f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) # Start running the hand-written Cron methods first; they can be run in 156f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) # parallel. They are resolved at the end. 157f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def run_cron_for_future(target): 158f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) title = target.__class__.__name__ 159f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) future, init_timer = TimerClosure(target.Cron) 160f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) assert isinstance(future, Future), ( 161f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) '%s.Cron() did not return a Future' % title) 162f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def resolve(): 163f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) resolve_timer = Timer() 164f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) try: 165f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) future.Get() 166f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) except Exception as e: 167f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) _cronlog.error('%s: error %s' % (title, traceback.format_exc())) 168f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) results.append(False) 169f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if IsDeadlineExceededError(e): raise 170f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) finally: 171f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) resolve_timer.Stop() 172f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) _cronlog.info('%s took %s: %s to initialize and %s to resolve' % 173f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) (title, 174f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) init_timer.With(resolve_timer).FormatElapsed(), 175f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) init_timer.FormatElapsed(), 176f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) resolve_timer.FormatElapsed())) 177f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return Future(delegate=Gettable(resolve)) 178f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 179f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) targets = (CreateDataSources(server_instance).values() + 180f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) [server_instance.content_providers]) 181f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) title = 'initializing %s parallel Cron targets' % len(targets) 182f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) _cronlog.info(title) 183f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) timer = Timer() 184f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) try: 185f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) cron_futures = [run_cron_for_future(target) for target in targets] 186f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) finally: 187f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) _cronlog.info('%s took %s' % (title, timer.Stop().FormatElapsed())) 188f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 1893551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # Rendering the public templates will also pull in all of the private 1903551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # templates. 191f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) results.append(request_files_in_dir(PUBLIC_TEMPLATES)) 192b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 1933551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # Rendering the public templates will have pulled in the .js and 1943551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # manifest.json files (for listing examples on the API reference pages), 1953551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # but there are still images, CSS, etc. 196f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) results.append(request_files_in_dir(STATIC_DOCS, prefix='static')) 197b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 1983551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # Samples are too expensive to run on the dev server, where there is no 1993551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # parallel fetch. 200a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) if not IsDevServer(): 2013551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # Fetch each individual sample file. 202f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) results.append(request_files_in_dir(EXAMPLES, 203f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) prefix='extensions/examples')) 2043551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 2053551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # Fetch the zip file of each example (contains all the individual 2063551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # files). 207a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) example_zips = [] 208f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) for root, _, files in trunk_fs.Walk(EXAMPLES): 209a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) example_zips.extend( 2103551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) root + '.zip' for name in files if name == 'manifest.json') 2113551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) results.append(_RequestEachItem( 2123551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 'example zips', 2133551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) example_zips, 2143551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) lambda path: render('extensions/examples/' + path))) 2153551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 216f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) # Resolve the hand-written Cron method futures. 217f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) title = 'resolving %s parallel Cron targets' % len(targets) 218f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) _cronlog.info(title) 219f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) timer = Timer() 220f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) try: 221f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) for future in cron_futures: 222f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) future.Get() 223f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) finally: 224f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) _cronlog.info('%s took %s' % (title, timer.Stop().FormatElapsed())) 225eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 226d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) except: 2273551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) results.append(False) 2283551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # This should never actually happen (each cron step does its own 2293551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # conservative error checking), so re-raise no matter what it is. 230d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) _cronlog.error('uncaught error: %s' % traceback.format_exc()) 2313551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) raise 2323551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) finally: 2333551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) success = all(results) 2343551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) _cronlog.info('finished (%s)', 'success' if success else 'FAILED') 2353551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) return (Response.Ok('Success') if success else 2363551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) Response.InternalError('Failure')) 237a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 238a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) def _GetSafeServerInstance(self): 239a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) '''Returns a ServerInstance with a host file system at a safe revision, 240a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) meaning the last revision that the current running version of the server 241a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) existed. 242a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) ''' 243a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) delegate = self._delegate 2444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 2454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) # IMPORTANT: Get a ServerInstance pinned to the most recent revision, not 2464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) # HEAD. These cron jobs take a while and run very frequently such that 2474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) # there is usually one running at any given time, and eventually a file 2484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) # that we're dealing with will change underneath it, putting the server in 2494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) # an undefined state. 2504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) server_instance_near_head = self._CreateServerInstance( 2514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self._GetMostRecentRevision()) 252a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 253a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) app_yaml_handler = AppYamlHelper( 2544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) server_instance_near_head.object_store_creator, 2554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) server_instance_near_head.host_file_system_provider) 256a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 257a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) if app_yaml_handler.IsUpToDate(delegate.GetAppVersion()): 2584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) return server_instance_near_head 259a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 260a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) # The version in app.yaml is greater than the currently running app's. 261a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) # The safe version is the one before it changed. 262a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) safe_revision = app_yaml_handler.GetFirstRevisionGreaterThan( 263a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) delegate.GetAppVersion()) - 1 264a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 2653551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) _cronlog.info('app version %s is out of date, safe is %s', 2663551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) delegate.GetAppVersion(), safe_revision) 267a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 268ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch return self._CreateServerInstance(safe_revision) 269a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 2704e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) def _GetMostRecentRevision(self): 2714e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) '''Gets the revision of the most recent patch submitted to the host file 2724e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) system. This is similar to HEAD but it's a concrete revision so won't 2734e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) change as the cron runs. 2744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) ''' 2754e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) head_fs = ( 2764e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self._CreateServerInstance(None).host_file_system_provider.GetTrunk()) 277f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return head_fs.Stat('').version 2784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 279ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch def _CreateServerInstance(self, revision): 2804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) '''Creates a ServerInstance pinned to |revision|, or HEAD if None. 2814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) NOTE: If passed None it's likely that during the cron run patches will be 2824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) submitted at HEAD, which may change data underneath the cron run. 2834e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) ''' 284ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch object_store_creator = ObjectStoreCreator(start_empty=True) 2857dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch branch_utility = self._delegate.CreateBranchUtility(object_store_creator) 2864e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) host_file_system_provider = self._delegate.CreateHostFileSystemProvider( 2874e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) object_store_creator, max_trunk_revision=revision) 2881e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) github_file_system_provider = self._delegate.CreateGithubFileSystemProvider( 289a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) object_store_creator) 290ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch return ServerInstance(object_store_creator, 2914e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) CompiledFileSystem.Factory(object_store_creator), 2927dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch branch_utility, 2931e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) host_file_system_provider, 2941e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) github_file_system_provider) 295