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 # This is the service account ID associated with the chrome-apps-doc project in |
14 '''This Servlet can be used to manually enqueue tasks on the default | 18 # the Google Developers Console. |
15 taskqueue. Useful for when an admin wants to manually force a specific | 19 _SERVICE_ACCOUNT_NAME = '636061184119-compute@developer.gserviceaccount.com' |
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 | 20 |
19 /_enqueue/_refresh/content_providers/cr-native-client?commit=123ff65468dcafff0 | 21 # This is Google's OAuth2 service for retrieving user information from an access |
20 | 22 # token. It is used to authenticate an incoming access token in the metadata |
21 which will enqueue a task (/_refresh/content_providers/cr-native-client) to | 23 # flush servlet, ensuring that only requests from the above service account are |
22 refresh the NaCl documentation cache for commit 123ff65468dcafff0. | 24 # fulfilled. |
23 | 25 _ACCOUNT_INFO_URL = ('https://www.googleapis.com/oauth2/v1/userinfo?' |
24 Access to this servlet should always be restricted to administrative users. | 26 'access_token=%s') |
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 | 27 |
35 | 28 |
36 class QueryCommitServlet(Servlet): | 29 class QueryCommitServlet(Servlet): |
37 '''Provides read access to the commit ID cache within the server. For example: | 30 '''Provides read access to the commit ID cache within the server. For example: |
38 | 31 |
39 /_query_commit/master | 32 /_query_commit/master |
40 | 33 |
41 will return the commit ID stored under the commit key "master" within the | 34 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 | 35 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 | 36 corresponds to the commit ID whose data currently populates the data cache |
(...skipping 14 matching lines...) Expand all Loading... |
58 commit_id, history_log) | 51 commit_id, history_log) |
59 return response | 52 return response |
60 | 53 |
61 commit_name = self._request.path | 54 commit_name = self._request.path |
62 id_future = commit_tracker.Get(commit_name) | 55 id_future = commit_tracker.Get(commit_name) |
63 history_future = commit_tracker.GetHistory(commit_name) | 56 history_future = commit_tracker.GetHistory(commit_name) |
64 return Response.Ok( | 57 return Response.Ok( |
65 All((id_future, history_future)).Then(generate_response).Get()) | 58 All((id_future, history_future)).Then(generate_response).Get()) |
66 | 59 |
67 | 60 |
68 class DumpRefreshServlet(Servlet): | 61 class FlushMemcacheServlet(Servlet): |
69 def __init__(self, request): | 62 '''Flushes the entire memcache. |
70 Servlet.__init__(self, request) | |
71 | 63 |
72 def Get(self): | 64 This requires an access token for the project's main service account. Without |
73 object_store_creator = ObjectStoreCreator(start_empty=False) | 65 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 ''' | 66 ''' |
94 | 67 |
95 class Delegate(object): | 68 class Delegate(object): |
96 def CreateCommitTracker(self): | 69 def IsAuthorized(self, access_token): |
97 return CommitTracker(ObjectStoreCreator(start_empty=False)) | 70 '''Verifies that a given access_token represents the main service account. |
| 71 ''' |
| 72 fetcher = CreateUrlFetcher() |
| 73 response = fetcher.Fetch(_ACCOUNT_INFO_URL % access_token) |
| 74 if response.status_code != 200: |
| 75 return False |
| 76 try: |
| 77 info = json.loads(response.content) |
| 78 except: |
| 79 return False |
| 80 return info['email'] == _SERVICE_ACCOUNT_NAME |
98 | 81 |
99 def __init__(self, request, delegate=Delegate()): | 82 def __init__(self, request, delegate=Delegate()): |
100 Servlet.__init__(self, request) | 83 Servlet.__init__(self, request) |
101 self._delegate = delegate | 84 self._delegate = delegate |
102 | 85 |
| 86 def GetAccessToken(self): |
| 87 auth_header = self._request.headers.get('Authorization') |
| 88 if not auth_header: |
| 89 return None |
| 90 try: |
| 91 method, token = auth_header.split(' ', 1) |
| 92 except: |
| 93 return None |
| 94 if method != 'Bearer': |
| 95 return None |
| 96 return token |
| 97 |
103 def Get(self): | 98 def Get(self): |
104 commit_tracker = self._delegate.CreateCommitTracker() | 99 access_token = self.GetAccessToken() |
105 commit_name, commit_id = self._request.path.split('/', 1) | 100 if not access_token: |
106 history = commit_tracker.GetHistory(commit_name).Get() | 101 return Response.Unauthorized('Unauthorized', 'Bearer', 'update') |
107 if not any(entry.commit_id == commit_id for entry in history): | 102 if not self._delegate.IsAuthorized(access_token): |
108 return Response.BadRequest('Commit %s not cached.' % commit_id) | 103 return Response.Forbidden('Forbidden') |
109 commit_tracker.Set(commit_name, commit_id).Get() | 104 result = memcache.flush_all() |
110 return Response.Ok('Commit "%s" updated to %s' % (commit_name, commit_id)) | 105 return Response.Ok('Flushed: %s' % result) |
111 | 106 |
| 107 |
| 108 class UpdateCacheServlet(Servlet): |
| 109 '''Devserver-only servlet for pushing local file data into the datastore. |
| 110 This is useful if you've used update_cache.py to build a local datastore |
| 111 for testing. Query: |
| 112 |
| 113 /_update_cache/FOO_DATA |
| 114 |
| 115 to make the devserver read FOO_DATA from its pwd and push all the data into |
| 116 datastore. |
| 117 ''' |
| 118 def __init__(self, request): |
| 119 Servlet.__init__(self, request) |
| 120 |
| 121 def Get(self): |
| 122 if not IsDevServer(): |
| 123 return Response.BadRequest('') |
| 124 import cPickle |
| 125 from persistent_object_store_appengine import PersistentObjectStoreAppengine |
| 126 with open(self._request.path, 'r') as f: |
| 127 data = cPickle.load(f) |
| 128 for namespace, contents in data.iteritems(): |
| 129 store = PersistentObjectStoreAppengine(namespace) |
| 130 for k, v in cPickle.loads(contents).iteritems(): |
| 131 try: |
| 132 store.Set(k, v).Get() |
| 133 except: |
| 134 logging.warn('Skipping entry %s because of errors.' % k) |
| 135 return Response.Ok('Data pushed!') |
OLD | NEW |