OLD | NEW |
---|---|
(Empty) | |
1 # Copyright 2015 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 import cPickle | |
6 import copy | |
7 import getopt | |
8 import json | |
9 import logging | |
10 import os.path | |
11 import sys | |
12 import traceback | |
13 | |
14 from branch_utility import BranchUtility | |
15 from commit_tracker import CommitTracker | |
16 from compiled_file_system import CompiledFileSystem | |
17 from data_source_registry import CreateDataSources | |
18 from environment import GetAppVersion | |
19 from environment_wrappers import CreateUrlFetcher, GetAccessToken | |
20 from future import All | |
21 from gcs_file_system_provider import CloudStorageFileSystemProvider | |
22 from host_file_system_provider import HostFileSystemProvider | |
23 from local_git_util import ParseRevision | |
24 from object_store_creator import ObjectStoreCreator | |
25 from persistent_object_store_fake import PersistentObjectStoreFake | |
26 from render_refresher import RenderRefresher | |
27 from server_instance import ServerInstance | |
28 from timer import Timer | |
29 | |
30 | |
31 # The path template to use for flushing the memcached. Should be formatted with | |
32 # with the app version. | |
33 _FLUSH_MEMCACHE_PATH = ('https://%s-dot-chrome-apps-doc.appspot.com/' | |
34 '_flush_memcache') | |
35 | |
36 | |
37 def _UpdateCommitId(commit_name, commit_id): | |
38 '''Sets the commit ID for a named commit. This is the final step performed | |
39 during update. Once all the appropriate datastore entries have been populated | |
40 for a new commit ID, the 'master' commit entry is updated to that ID and the | |
41 frontend will begin serving the new data. | |
42 | |
43 Note that this requires an access token identifying the main service account | |
44 for the chrome-apps-doc project. VM instances will get this automatically | |
45 from their environment, but if you want to do a local push to prod you will | |
46 need to set the DOCSERVER_ACCESS_TOKEN environment variable appropriately. | |
47 ''' | |
48 commit_tracker = CommitTracker( | |
49 ObjectStoreCreator(store_type=PersistentObjectStoreFake, | |
50 start_empty=False)) | |
51 commit_tracker.Set(commit_name, commit_id).Get() | |
52 logging.info('Commit "%s" updated to %s.' % (commit_name, commit_id)) | |
53 | |
54 | |
55 def _GetCachedCommitId(commit_name): | |
56 '''Determines which commit ID was last cached. | |
57 ''' | |
58 commit_tracker = CommitTracker( | |
59 ObjectStoreCreator(store_type=PersistentObjectStoreFake, | |
60 start_empty=False)) | |
61 return commit_tracker.Get(commit_name).Get() | |
62 | |
63 | |
64 def _FlushMemcache(): | |
65 '''Requests that the frontend flush its memcached to avoid serving stale data. | |
66 ''' | |
67 flush_url = _FLUSH_MEMCACHE_PATH % GetAppVersion() | |
68 headers = { 'Authorization': 'Bearer %s' % GetAccessToken() } | |
69 response = CreateUrlFetcher().Fetch(flush_url, headers) | |
70 if response.status_code != 200: | |
71 logging.error('Unable to flush memcache: HTTP %s (%s)' % | |
72 (response.status_code, response.content)) | |
73 else: | |
74 logging.info('Memcache flushed.') | |
75 | |
76 | |
77 def _CreateServerInstance(commit): | |
78 '''Creates a ServerInstance based on origin/master. | |
79 ''' | |
80 object_store_creator = ObjectStoreCreator( | |
81 start_empty=False, store_type=PersistentObjectStoreFake) | |
82 branch_utility = BranchUtility.Create(object_store_creator) | |
83 host_file_system_provider = HostFileSystemProvider(object_store_creator, | |
84 pinned_commit=commit) | |
85 gcs_file_system_provider = CloudStorageFileSystemProvider( | |
86 object_store_creator) | |
87 return ServerInstance(object_store_creator, | |
88 CompiledFileSystem.Factory(object_store_creator), | |
89 branch_utility, | |
90 host_file_system_provider, | |
91 gcs_file_system_provider) | |
92 | |
93 | |
94 def _UpdateDataSource(name, data_source): | |
95 try: | |
96 class_name = data_source.__class__.__name__ | |
97 timer = Timer() | |
98 logging.info('Updating %s...' % name) | |
99 data_source.Refresh().Get() | |
100 except Exception as e: | |
101 logging.error('%s: error %s' % (class_name, traceback.format_exc())) | |
102 raise e | |
103 finally: | |
104 logging.info('Updating %s took %s' % (name, timer.Stop().FormatElapsed())) | |
105 | |
106 | |
107 def UpdateCache(single_data_source=None, commit=None): | |
108 '''Attempts to populate the datastore with a bunch of information derived from | |
109 a given commit. | |
110 ''' | |
111 server_instance = _CreateServerInstance(commit) | |
112 | |
113 # This is the guy that would be responsible for refreshing the cache of | |
114 # examples. Here for posterity, hopefully it will be added to the targets | |
115 # below someday. | |
116 # render_refresher = RenderRefresher(server_instance, self._request) | |
117 | |
118 data_sources = CreateDataSources(server_instance) | |
119 data_sources['content_providers'] = server_instance.content_providers | |
120 data_sources['platform_bundle'] = server_instance.platform_bundle | |
121 if single_data_source: | |
122 _UpdateDataSource(single_data_source, data_sources[single_data_source]) | |
123 else: | |
124 for name, source in data_sources.iteritems(): | |
125 _UpdateDataSource(name, source) | |
126 | |
127 | |
128 def _Main(argv): | |
129 try: | |
130 opts = dict((name[2:], value) for name, value in | |
131 getopt.getopt(argv, '', | |
132 ['load-file=', 'data-source=', 'commit=', | |
133 'no-refresh', 'no-push', 'save-file=', | |
134 'no-master-update', 'push-all', 'force'])[0]) | |
135 except getopt.GetoptError as e: | |
136 print '%s\n' % e | |
137 print ( | |
138 'Usage: update_cache.py [options]\n\n' | |
139 'Options:\n' | |
140 ' --data-source=NAME Limit update to a single data source.\n' | |
not at google - send to devlin
2015/06/04 22:40:45
isn't the opt library meant to generate this sort
Ken Rockot(use gerrit already)
2015/06/05 00:21:50
Ah you're right. Well sort of. It looks like argpa
| |
141 ' --load-file=FILE Load object store data from FILE before\n' | |
142 ' starting the update.\n' | |
143 ' --save-file=FILE Save object store data to FILE after running\n' | |
144 ' the update.\n' | |
145 ' --no-refresh Do not attempt to update any data sources.\n' | |
146 ' --no-push Do not push to Datastore.\n' | |
147 ' --commit=REV Commit ID to use for master update.\n' | |
148 ' --no-master-update Do not update the master commit ID.\n' | |
149 ' --push-all Push all entities to the Datastore even if\n' | |
150 ' they do not differ from the loaded cache.\n\n' | |
151 ' --force Force an update even if the latest commit is' | |
152 ' already cached.\n') | |
153 exit(1) | |
154 | |
155 logging.getLogger().setLevel(logging.INFO) | |
156 | |
157 data_source = opts.get('data-source', None) | |
158 load_file = opts.get('load-file', None) | |
159 save_file = opts.get('save-file', None) | |
160 do_refresh = 'no-refresh' not in opts | |
161 do_push = 'no-push' not in opts | |
162 do_master_update = 'no-master-update' not in opts | |
163 push_all = do_push and ('push-all' in opts) | |
164 commit = ParseRevision(opts.get('commit', 'origin/HEAD')) | |
165 force_update = 'force' in opts | |
166 | |
167 original_data = {} | |
168 if load_file: | |
169 logging.info('Loading cache...') | |
170 PersistentObjectStoreFake.LoadFromFile(load_file) | |
171 if not push_all: | |
172 original_data = copy.deepcopy(PersistentObjectStoreFake.DATA) | |
173 | |
174 last_commit = _GetCachedCommitId('master') | |
175 if ParseRevision(commit) == last_commit and not force_update: | |
176 logging.info('Latest cache (revision %s) is up to date. Bye.' % commit) | |
177 exit(0) | |
178 | |
179 timer = Timer() | |
180 if do_refresh: | |
181 logging.info('Starting refresh from commit %s...' % ParseRevision(commit)) | |
182 if data_source: | |
183 UpdateCache(single_data_source=data_source, | |
184 commit=commit) | |
185 else: | |
186 UpdateCache(commit=commit) | |
187 | |
188 if do_push: | |
189 from datastore_util import PushData | |
190 if do_master_update: | |
191 _UpdateCommitId('master', commit) | |
192 push_timer = Timer() | |
193 logging.info('Pushing data into datastore...') | |
194 PushData(PersistentObjectStoreFake.DATA, original_data=original_data) | |
195 logging.info('Done. Datastore push took %s' % | |
196 push_timer.Stop().FormatElapsed()) | |
197 _FlushMemcache() | |
198 if save_file: | |
199 PersistentObjectStoreFake.SaveToFile(save_file) | |
200 | |
201 logging.info('Update completed in %s' % timer.Stop().FormatElapsed()) | |
202 | |
203 | |
204 if __name__ == '__main__': | |
205 _Main(sys.argv[1:]) | |
206 | |
OLD | NEW |