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 65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import posixpath 7c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import traceback 8c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 9a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)from app_yaml_helper import AppYamlHelper 10cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)from appengine_wrappers import 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 14cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)from environment import GetAppVersion, IsDevServer 155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from extensions_paths import EXAMPLES, PUBLIC_TEMPLATES, STATIC_DOCS 16ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochfrom file_system_util import CreateURLsFromPaths 17effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochfrom future import Future 185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from gcs_file_system_provider import CloudStorageFileSystemProvider 191e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)from github_file_system_provider import GithubFileSystemProvider 204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)from host_file_system_provider import HostFileSystemProvider 21b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)from object_store_creator import ObjectStoreCreator 22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)from render_servlet import RenderServlet 23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)from server_instance import ServerInstance 24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)from servlet import Servlet, Request, Response 255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from special_paths import SITE_VERIFICATION_FILE 26f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)from timer import Timer, TimerClosure 27f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 28b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 29b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)class _SingletonRenderServletDelegate(RenderServlet.Delegate): 30b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def __init__(self, server_instance): 31b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) self._server_instance = server_instance 32b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 33ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch def CreateServerInstance(self): 34b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return self._server_instance 35c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 363551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)class _CronLogger(object): 373551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) '''Wraps the logging.* methods to prefix them with 'cron' and flush 383551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) immediately. The flushing is important because often these cron runs time 393551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) out and we lose the logs. 403551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) ''' 413551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) def info(self, msg, *args): self._log(logging.info, msg, args) 423551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) def warning(self, msg, *args): self._log(logging.warning, msg, args) 433551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) def error(self, msg, *args): self._log(logging.error, msg, args) 443551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 453551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) def _log(self, logfn, msg, args): 463551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) try: 473551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) logfn('cron: %s' % msg, *args) 483551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) finally: 493551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) logservice.flush() 503551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 513551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)_cronlog = _CronLogger() 523551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 533551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)def _RequestEachItem(title, items, request_callback): 543551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) '''Runs a task |request_callback| named |title| for each item in |items|. 553551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) |request_callback| must take an item and return a servlet response. 563551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) Returns true if every item was successfully run, false if any return a 573551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) non-200 response or raise an exception. 583551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) ''' 593551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) _cronlog.info('%s: starting', title) 603551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) success_count, failure_count = 0, 0 61f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) timer = Timer() 623551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) try: 633551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) for i, item in enumerate(items): 643551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) def error_message(detail): 653551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) return '%s: error rendering %s (%s of %s): %s' % ( 663551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) title, item, i + 1, len(items), detail) 673551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) try: 683551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) response = request_callback(item) 693551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) if response.status == 200: 703551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) success_count += 1 713551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) else: 724e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) _cronlog.error(error_message('response status %s' % response.status)) 733551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) failure_count += 1 743551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) except Exception as e: 753551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) _cronlog.error(error_message(traceback.format_exc())) 763551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) failure_count += 1 773551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) if IsDeadlineExceededError(e): raise 783551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) finally: 79f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) _cronlog.info('%s: rendered %s of %s with %s failures in %s', 80f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) title, success_count, len(items), failure_count, 81f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) timer.Stop().FormatElapsed()) 823551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) return success_count == len(items) 833551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)class CronServlet(Servlet): 85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) '''Servlet which runs a cron job. 86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) ''' 87b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def __init__(self, request, delegate_for_test=None): 88b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) Servlet.__init__(self, request) 89b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) self._delegate = delegate_for_test or CronServlet.Delegate() 90b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 91b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) class Delegate(object): 92a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) '''CronServlet's runtime dependencies. Override for testing. 93b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) ''' 94b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def CreateBranchUtility(self, object_store_creator): 95b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return BranchUtility.Create(object_store_creator) 96b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) def CreateHostFileSystemProvider(self, 984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) object_store_creator, 994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) max_trunk_revision=None): 1004e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) return HostFileSystemProvider(object_store_creator, 1014e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) max_trunk_revision=max_trunk_revision) 102b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 1031e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) def CreateGithubFileSystemProvider(self, object_store_creator): 1041e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return GithubFileSystemProvider(object_store_creator) 105b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) def CreateGCSFileSystemProvider(self, object_store_creator): 1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return CloudStorageFileSystemProvider(object_store_creator) 1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 109a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) def GetAppVersion(self): 110a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) return GetAppVersion() 111a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) def Get(self): 1133551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # Crons often time out, and if they do we need to make sure to flush the 1143551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # logs before the process gets killed (Python gives us a couple of 1153551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # seconds). 1163551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # 1173551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # So, manually flush logs at the end of the cron run. However, sometimes 1183551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # even that isn't enough, which is why in this file we use _cronlog and 1193551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # make it flush the log every time its used. 120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) logservice.AUTOFLUSH_ENABLED = False 121c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) try: 122c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return self._GetImpl() 1233551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) except BaseException: 1243551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) _cronlog.error('Caught top-level exception! %s', traceback.format_exc()) 125c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) finally: 126c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) logservice.flush() 127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 128c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) def _GetImpl(self): 129c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) # Cron strategy: 130c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) # 131c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) # Find all public template files and static files, and render them. Most of 132c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) # the time these won't have changed since the last cron run, so it's a 133c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) # little wasteful, but hopefully rendering is really fast (if it isn't we 134c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) # have a problem). 1353551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) _cronlog.info('starting') 136c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 137b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # This is returned every time RenderServlet wants to create a new 138b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # ServerInstance. 1393551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # 1403551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # TODO(kalman): IMPORTANT. This sometimes throws an exception, breaking 1413551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # everything. Need retry logic at the fetcher level. 142a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) server_instance = self._GetSafeServerInstance() 1434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) trunk_fs = server_instance.host_file_system_provider.GetTrunk() 144b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 1453551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) def render(path): 146eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch request = Request(path, self._request.host, self._request.headers) 147eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch delegate = _SingletonRenderServletDelegate(server_instance) 148eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch return RenderServlet(request, delegate).Get() 149c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) def request_files_in_dir(path, prefix='', strip_ext=None): 1513551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) '''Requests every file found under |path| in this host file system, with 1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) a request prefix of |prefix|. |strip_ext| is an optional list of file 1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) extensions that should be stripped from paths before requesting. 1543551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) ''' 1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) def maybe_strip_ext(name): 1565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if name == SITE_VERIFICATION_FILE or not strip_ext: 1575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return name 1585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base, ext = posixpath.splitext(name) 1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return base if ext in strip_ext else name 1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) files = [maybe_strip_ext(name) 1615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) for name, _ in CreateURLsFromPaths(trunk_fs, path, prefix)] 1623551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) return _RequestEachItem(path, files, render) 1633551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 1643551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) results = [] 165a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 1663551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) try: 167f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) # Start running the hand-written Cron methods first; they can be run in 168f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) # parallel. They are resolved at the end. 169f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def run_cron_for_future(target): 170f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) title = target.__class__.__name__ 171f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) future, init_timer = TimerClosure(target.Cron) 172f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) assert isinstance(future, Future), ( 173f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) '%s.Cron() did not return a Future' % title) 174f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def resolve(): 175f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) resolve_timer = Timer() 176f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) try: 177f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) future.Get() 178f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) except Exception as e: 179f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) _cronlog.error('%s: error %s' % (title, traceback.format_exc())) 180f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) results.append(False) 181f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if IsDeadlineExceededError(e): raise 182f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) finally: 183f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) resolve_timer.Stop() 184f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) _cronlog.info('%s took %s: %s to initialize and %s to resolve' % 185f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) (title, 186f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) init_timer.With(resolve_timer).FormatElapsed(), 187f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) init_timer.FormatElapsed(), 188f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) resolve_timer.FormatElapsed())) 189effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return Future(callback=resolve) 190f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 191f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) targets = (CreateDataSources(server_instance).values() + 1920529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch [server_instance.content_providers, 1930529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch server_instance.api_models]) 194f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) title = 'initializing %s parallel Cron targets' % len(targets) 195f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) _cronlog.info(title) 196f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) timer = Timer() 197f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) try: 198f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) cron_futures = [run_cron_for_future(target) for target in targets] 199f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) finally: 200f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) _cronlog.info('%s took %s' % (title, timer.Stop().FormatElapsed())) 201f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 2023551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # Samples are too expensive to run on the dev server, where there is no 2033551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # parallel fetch. 204effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch # 205effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch # XXX(kalman): Currently samples are *always* too expensive to fetch, so 206effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch # disabling them for now. It won't break anything so long as we're still 207effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch # not enforcing that everything gets cached for normal instances. 208effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if False: # should be "not IsDevServer()": 2093551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # Fetch each individual sample file. 210f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) results.append(request_files_in_dir(EXAMPLES, 211f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) prefix='extensions/examples')) 2123551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 213f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) # Resolve the hand-written Cron method futures. 214f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) title = 'resolving %s parallel Cron targets' % len(targets) 215f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) _cronlog.info(title) 216f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) timer = Timer() 217f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) try: 218f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) for future in cron_futures: 219f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) future.Get() 220f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) finally: 221f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) _cronlog.info('%s took %s' % (title, timer.Stop().FormatElapsed())) 222eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 223d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) except: 2243551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) results.append(False) 2253551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # This should never actually happen (each cron step does its own 2263551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) # conservative error checking), so re-raise no matter what it is. 227d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) _cronlog.error('uncaught error: %s' % traceback.format_exc()) 2283551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) raise 2293551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) finally: 2303551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) success = all(results) 2313551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) _cronlog.info('finished (%s)', 'success' if success else 'FAILED') 2323551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) return (Response.Ok('Success') if success else 2333551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) Response.InternalError('Failure')) 234a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 235a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) def _GetSafeServerInstance(self): 236a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) '''Returns a ServerInstance with a host file system at a safe revision, 237a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) meaning the last revision that the current running version of the server 238a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) existed. 239a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) ''' 240a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) delegate = self._delegate 2414e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 2424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) # IMPORTANT: Get a ServerInstance pinned to the most recent revision, not 2434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) # HEAD. These cron jobs take a while and run very frequently such that 2444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) # there is usually one running at any given time, and eventually a file 2454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) # that we're dealing with will change underneath it, putting the server in 2464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) # an undefined state. 2474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) server_instance_near_head = self._CreateServerInstance( 2484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self._GetMostRecentRevision()) 249a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 250a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) app_yaml_handler = AppYamlHelper( 2514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) server_instance_near_head.object_store_creator, 2524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) server_instance_near_head.host_file_system_provider) 253a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 254a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) if app_yaml_handler.IsUpToDate(delegate.GetAppVersion()): 2554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) return server_instance_near_head 256a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 257a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) # The version in app.yaml is greater than the currently running app's. 258a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) # The safe version is the one before it changed. 259a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) safe_revision = app_yaml_handler.GetFirstRevisionGreaterThan( 260a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) delegate.GetAppVersion()) - 1 261a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 2623551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) _cronlog.info('app version %s is out of date, safe is %s', 2633551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) delegate.GetAppVersion(), safe_revision) 264a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 265ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch return self._CreateServerInstance(safe_revision) 266a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 2674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) def _GetMostRecentRevision(self): 2684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) '''Gets the revision of the most recent patch submitted to the host file 2694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) system. This is similar to HEAD but it's a concrete revision so won't 2704e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) change as the cron runs. 2714e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) ''' 2724e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) head_fs = ( 2734e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self._CreateServerInstance(None).host_file_system_provider.GetTrunk()) 274f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return head_fs.Stat('').version 2754e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 276ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch def _CreateServerInstance(self, revision): 2774e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) '''Creates a ServerInstance pinned to |revision|, or HEAD if None. 2784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) NOTE: If passed None it's likely that during the cron run patches will be 2794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) submitted at HEAD, which may change data underneath the cron run. 2804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) ''' 281ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch object_store_creator = ObjectStoreCreator(start_empty=True) 2827dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch branch_utility = self._delegate.CreateBranchUtility(object_store_creator) 2834e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) host_file_system_provider = self._delegate.CreateHostFileSystemProvider( 2844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) object_store_creator, max_trunk_revision=revision) 2851e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) github_file_system_provider = self._delegate.CreateGithubFileSystemProvider( 286a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) object_store_creator) 2875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gcs_file_system_provider = self._delegate.CreateGCSFileSystemProvider( 2885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) object_store_creator) 289ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch return ServerInstance(object_store_creator, 2904e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) CompiledFileSystem.Factory(object_store_creator), 2917dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch branch_utility, 2921e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) host_file_system_provider, 2935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) github_file_system_provider, 2945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gcs_file_system_provider) 295