OLD | NEW |
---|---|
1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 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 from appengine_wrappers import taskqueue | 5 import json |
6 import logging | |
7 | |
8 from appengine_wrappers import memcache | |
6 from commit_tracker import CommitTracker | 9 from commit_tracker import CommitTracker |
10 from environment import IsDevServer | |
11 from environment_wrappers import CreateUrlFetcher | |
7 from future import All | 12 from future import All |
8 from object_store_creator import ObjectStoreCreator | 13 from object_store_creator import ObjectStoreCreator |
9 from refresh_tracker import RefreshTracker | |
10 from servlet import Servlet, Response | 14 from servlet import Servlet, Response |
11 | 15 |
12 | 16 |
13 class EnqueueServlet(Servlet): | 17 _SERVICE_ACCOUNT_NAME = '636061184119-compute@developer.gserviceaccount.com' |
not at google - send to devlin
2015/06/04 22:40:45
Can you quickly describe what these are / where th
Ken Rockot(use gerrit already)
2015/06/05 00:21:50
Done.
| |
14 '''This Servlet can be used to manually enqueue tasks on the default | 18 _ACCOUNT_INFO_URL = ('https://www.googleapis.com/oauth2/v1/userinfo?' |
15 taskqueue. Useful for when an admin wants to manually force a specific | 19 'access_token=%s') |
16 DataSource refresh, but the refresh operation takes longer than the 60 sec | |
17 timeout of a non-taskqueue request. For example, you might query | |
18 | |
19 /_enqueue/_refresh/content_providers/cr-native-client?commit=123ff65468dcafff0 | |
20 | |
21 which will enqueue a task (/_refresh/content_providers/cr-native-client) to | |
22 refresh the NaCl documentation cache for commit 123ff65468dcafff0. | |
23 | |
24 Access to this servlet should always be restricted to administrative users. | |
25 ''' | |
26 def __init__(self, request): | |
27 Servlet.__init__(self, request) | |
28 | |
29 def Get(self): | |
30 queue = taskqueue.Queue() | |
31 queue.add(taskqueue.Task(url='/%s' % self._request.path, | |
32 params=self._request.arguments)) | |
33 return Response.Ok('Task enqueued.') | |
34 | 20 |
35 | 21 |
36 class QueryCommitServlet(Servlet): | 22 class QueryCommitServlet(Servlet): |
37 '''Provides read access to the commit ID cache within the server. For example: | 23 '''Provides read access to the commit ID cache within the server. For example: |
38 | 24 |
39 /_query_commit/master | 25 /_query_commit/master |
40 | 26 |
41 will return the commit ID stored under the commit key "master" within the | 27 will return the commit ID stored under the commit key "master" within the |
42 commit cache. Currently "master" is the only named commit we cache, and it | 28 commit cache. Currently "master" is the only named commit we cache, and it |
43 corresponds to the commit ID whose data currently populates the data cache | 29 corresponds to the commit ID whose data currently populates the data cache |
(...skipping 14 matching lines...) Expand all Loading... | |
58 commit_id, history_log) | 44 commit_id, history_log) |
59 return response | 45 return response |
60 | 46 |
61 commit_name = self._request.path | 47 commit_name = self._request.path |
62 id_future = commit_tracker.Get(commit_name) | 48 id_future = commit_tracker.Get(commit_name) |
63 history_future = commit_tracker.GetHistory(commit_name) | 49 history_future = commit_tracker.GetHistory(commit_name) |
64 return Response.Ok( | 50 return Response.Ok( |
65 All((id_future, history_future)).Then(generate_response).Get()) | 51 All((id_future, history_future)).Then(generate_response).Get()) |
66 | 52 |
67 | 53 |
68 class DumpRefreshServlet(Servlet): | 54 class FlushMemcacheServlet(Servlet): |
69 def __init__(self, request): | 55 '''Flushes the entire memcache. |
70 Servlet.__init__(self, request) | |
71 | 56 |
72 def Get(self): | 57 This requires an access token for the project's main service account. Without |
73 object_store_creator = ObjectStoreCreator(start_empty=False) | 58 said token, the request is considered invalid. |
74 refresh_tracker = RefreshTracker(object_store_creator) | |
75 commit_id = self._request.path | |
76 work_order = refresh_tracker._GetWorkOrder(commit_id).Get() | |
77 task_names = ['%s@%s' % (commit_id, task) for task in work_order.tasks] | |
78 completions = refresh_tracker._task_completions.GetMulti(task_names).Get() | |
79 missing = [] | |
80 for task in task_names: | |
81 if task not in completions: | |
82 missing.append(task) | |
83 response = 'Missing:<br>%s' % ''.join('%s<br>' % task for task in missing) | |
84 return Response.Ok(response) | |
85 | |
86 class ResetCommitServlet(Servlet): | |
87 '''Writes a new commit ID to the commit cache. For example: | |
88 | |
89 /_reset_commit/master/123456 | |
90 | |
91 will reset the 'master' commit ID to '123456'. The provided commit MUST be | |
92 in the named commit's recent history or it will be ignored. | |
93 ''' | 59 ''' |
94 | 60 |
95 class Delegate(object): | 61 class Delegate(object): |
96 def CreateCommitTracker(self): | 62 def IsAuthorized(self, access_token): |
97 return CommitTracker(ObjectStoreCreator(start_empty=False)) | 63 '''Verifies that a given access_token represents the main service account. |
64 ''' | |
65 fetcher = CreateUrlFetcher() | |
66 response = fetcher.Fetch(_ACCOUNT_INFO_URL % access_token) | |
67 if response.status_code != 200: | |
68 return False | |
69 try: | |
70 info = json.loads(response.content) | |
71 except: | |
72 return False | |
73 return info['email'] == _SERVICE_ACCOUNT_NAME | |
98 | 74 |
99 def __init__(self, request, delegate=Delegate()): | 75 def __init__(self, request, delegate=Delegate()): |
100 Servlet.__init__(self, request) | 76 Servlet.__init__(self, request) |
101 self._delegate = delegate | 77 self._delegate = delegate |
102 | 78 |
79 def GetAccessToken(self): | |
80 auth_header = self._request.headers.get('Authorization') | |
81 if not auth_header: | |
82 return None | |
83 try: | |
84 method, token = auth_header.split(' ', 1) | |
85 except: | |
86 return None | |
87 if method != 'Bearer': | |
88 return None | |
89 return token | |
90 | |
103 def Get(self): | 91 def Get(self): |
104 commit_tracker = self._delegate.CreateCommitTracker() | 92 access_token = self.GetAccessToken() |
105 commit_name, commit_id = self._request.path.split('/', 1) | 93 if not access_token: |
106 history = commit_tracker.GetHistory(commit_name).Get() | 94 return Response.Unauthorized('Unauthorized', 'Bearer', 'update') |
107 if not any(entry.commit_id == commit_id for entry in history): | 95 if not self._delegate.IsAuthorized(access_token): |
108 return Response.BadRequest('Commit %s not cached.' % commit_id) | 96 return Response.Forbidden('Forbidden') |
109 commit_tracker.Set(commit_name, commit_id).Get() | 97 result = memcache.flush_all() |
110 return Response.Ok('Commit "%s" updated to %s' % (commit_name, commit_id)) | 98 return Response.Ok('Flushed: %s' % result) |
111 | 99 |
100 | |
101 class UpdateCacheServlet(Servlet): | |
102 '''Devserver-only servlet for pushing local file data into the datastore. | |
103 This is useful if you've used update_cache.py to build a local datastore | |
104 for testing. Query: | |
105 | |
106 /_update_cache/FOO_DATA | |
107 | |
108 to make the devserver read FOO_DATA from its pwd and push all the data into | |
109 datastore. | |
110 ''' | |
111 def __init__(self, request): | |
112 Servlet.__init__(self, request) | |
113 | |
114 def Get(self): | |
115 if not IsDevServer(): | |
116 return Response.BadRequest('') | |
117 import cPickle | |
118 from persistent_object_store_appengine import PersistentObjectStoreAppengine | |
119 with open(self._request.path, 'r') as f: | |
120 data = cPickle.load(f) | |
121 for namespace, contents in data.iteritems(): | |
122 store = PersistentObjectStoreAppengine(namespace) | |
123 for k, v in cPickle.loads(contents).iteritems(): | |
124 try: | |
125 store.Set(k, v).Get() | |
126 except: | |
127 logging.warn('Skipping entry %s because of errors.' % k) | |
128 return Response.Ok('Data pushed!') | |
OLD | NEW |