OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2014 The LUCI Authors. All rights reserved. | 2 # Copyright 2014 The LUCI Authors. All rights reserved. |
3 # Use of this source code is governed under the Apache License, Version 2.0 | 3 # Use of this source code is governed under the Apache License, Version 2.0 |
4 # that can be found in the LICENSE file. | 4 # that can be found in the LICENSE file. |
5 | 5 |
6 import datetime | 6 import datetime |
7 import logging | 7 import logging |
8 import os | 8 import os |
9 import random | 9 import random |
10 import sys | 10 import sys |
(...skipping 29 matching lines...) Expand all Loading... |
40 from server import task_scheduler | 40 from server import task_scheduler |
41 from server import task_to_run | 41 from server import task_to_run |
42 from server.task_result import State | 42 from server.task_result import State |
43 | 43 |
44 from proto import config_pb2 | 44 from proto import config_pb2 |
45 | 45 |
46 | 46 |
47 # pylint: disable=W0212,W0612 | 47 # pylint: disable=W0212,W0612 |
48 | 48 |
49 | 49 |
50 def _gen_request(properties=None, **kwargs): | |
51 """Creates a TaskRequest.""" | |
52 props = { | |
53 'command': [u'command1'], | |
54 'dimensions': {u'pool': u'default'}, | |
55 'env': {}, | |
56 'execution_timeout_secs': 24*60*60, | |
57 'io_timeout_secs': None, | |
58 } | |
59 props.update(properties or {}) | |
60 now = utils.utcnow() | |
61 args = { | |
62 'created_ts': now, | |
63 'name': 'Request name', | |
64 'priority': 50, | |
65 'properties': task_request.TaskProperties(**props), | |
66 'expiration_ts': now + datetime.timedelta(seconds=60), | |
67 'tags': [u'tag:1'], | |
68 'user': 'Jesus', | |
69 } | |
70 args.update(kwargs) | |
71 ret = task_request.TaskRequest(**args) | |
72 task_request.init_new_request(ret, True, None) | |
73 return ret | |
74 | |
75 | |
76 def get_results(request_key): | 50 def get_results(request_key): |
77 """Fetches all task results for a specified TaskRequest ndb.Key. | 51 """Fetches all task results for a specified TaskRequest ndb.Key. |
78 | 52 |
79 Returns: | 53 Returns: |
80 tuple(TaskResultSummary, list of TaskRunResult that exist). | 54 tuple(TaskResultSummary, list of TaskRunResult that exist). |
81 """ | 55 """ |
82 result_summary_key = task_pack.request_key_to_result_summary_key(request_key) | 56 result_summary_key = task_pack.request_key_to_result_summary_key(request_key) |
83 result_summary = result_summary_key.get() | 57 result_summary = result_summary_key.get() |
84 # There's two way to look at it, either use a DB query or fetch all the | 58 # There's two way to look at it, either use a DB query or fetch all the |
85 # entities that could exist, at most 255. In general, there will be <3 | 59 # entities that could exist, at most 255. In general, there will be <3 |
86 # entities so just fetching them by key would be faster. This function is | 60 # entities so just fetching them by key would be faster. This function is |
87 # exclusively used in unit tests so it's not performance critical. | 61 # exclusively used in unit tests so it's not performance critical. |
88 q = task_result.TaskRunResult.query(ancestor=result_summary_key) | 62 q = task_result.TaskRunResult.query(ancestor=result_summary_key) |
89 q = q.order(task_result.TaskRunResult.key) | 63 q = q.order(task_result.TaskRunResult.key) |
90 return result_summary, q.fetch() | 64 return result_summary, q.fetch() |
91 | 65 |
92 | 66 |
93 def _quick_schedule(dims): | |
94 """Schedules a task.""" | |
95 request = _gen_request(properties={'dimensions': dims}) | |
96 task_request.init_new_request(request, True, None) | |
97 return task_scheduler.schedule_request(request, None) | |
98 | |
99 | |
100 def _register_bot(bot_dimensions): | |
101 """Registers the bot so the task queues knows there's a worker than can run | |
102 the task. | |
103 """ | |
104 bot_management.bot_event( | |
105 'bot_connected', bot_dimensions[u'id'][0], '1.2.3.4', 'joe@localhost', | |
106 bot_dimensions, {'state': 'real'}, '1234', False, None, None) | |
107 task_queues.assert_bot(bot_dimensions) | |
108 | |
109 | |
110 def _quick_reap(): | |
111 """Reaps a task.""" | |
112 _quick_schedule({u'os': u'Windows-3.1.1', u'pool': u'default'}) | |
113 bot_dimensions = { | |
114 u'id': [u'localhost'], | |
115 u'os': [u'Windows-3.1.1'], | |
116 u'pool': [u'default'], | |
117 } | |
118 _register_bot(bot_dimensions) | |
119 reaped_request, _, run_result = task_scheduler.bot_reap_task( | |
120 bot_dimensions, 'abc', None) | |
121 return run_result | |
122 | |
123 | |
124 class TaskSchedulerApiTest(test_env_handlers.AppTestBase): | 67 class TaskSchedulerApiTest(test_env_handlers.AppTestBase): |
125 def setUp(self): | 68 def setUp(self): |
126 super(TaskSchedulerApiTest, self).setUp() | 69 super(TaskSchedulerApiTest, self).setUp() |
127 self.now = datetime.datetime(2014, 1, 2, 3, 4, 5, 6) | 70 self.now = datetime.datetime(2014, 1, 2, 3, 4, 5, 6) |
128 self.mock_now(self.now) | 71 self.mock_now(self.now) |
129 self.mock(stats_framework, 'add_entry', self._parse_line) | 72 self.mock(stats_framework, 'add_entry', self._parse_line) |
130 auth_testing.mock_get_current_identity(self) | 73 auth_testing.mock_get_current_identity(self) |
131 event_mon_metrics.initialize() | 74 event_mon_metrics.initialize() |
132 # Setup the backend to handle task queues for 'task-dimensions'. | 75 # Setup the backend to handle task queues for 'task-dimensions'. |
133 self.app = webtest.TestApp( | 76 self.app = webtest.TestApp( |
(...skipping 26 matching lines...) Expand all Loading... |
160 self.assertFalse(self._pub_sub_mocked) | 103 self.assertFalse(self._pub_sub_mocked) |
161 self._pub_sub_mocked = True | 104 self._pub_sub_mocked = True |
162 calls = [] | 105 calls = [] |
163 def pubsub_publish(**kwargs): | 106 def pubsub_publish(**kwargs): |
164 if not self.publish_successful: | 107 if not self.publish_successful: |
165 raise pubsub.TransientError('Fail') | 108 raise pubsub.TransientError('Fail') |
166 calls.append(('directly', kwargs)) | 109 calls.append(('directly', kwargs)) |
167 self.mock(pubsub, 'publish', pubsub_publish) | 110 self.mock(pubsub, 'publish', pubsub_publish) |
168 return calls | 111 return calls |
169 | 112 |
| 113 def _gen_request(self, properties=None, **kwargs): |
| 114 """Creates a TaskRequest.""" |
| 115 props = { |
| 116 'command': [u'command1'], |
| 117 'dimensions': {u'pool': u'default'}, |
| 118 'env': {}, |
| 119 'execution_timeout_secs': 24*60*60, |
| 120 'io_timeout_secs': None, |
| 121 } |
| 122 props.update(properties or {}) |
| 123 now = utils.utcnow() |
| 124 args = { |
| 125 'created_ts': now, |
| 126 'name': 'Request name', |
| 127 'priority': 50, |
| 128 'properties': task_request.TaskProperties(**props), |
| 129 'expiration_ts': now + datetime.timedelta(seconds=60), |
| 130 'tags': [u'tag:1'], |
| 131 'user': 'Jesus', |
| 132 } |
| 133 args.update(kwargs) |
| 134 ret = task_request.TaskRequest(**args) |
| 135 task_request.init_new_request(ret, True, None) |
| 136 return ret |
| 137 |
| 138 def _quick_schedule(self, dims, nb_task=0): |
| 139 """Schedules a task.""" |
| 140 request = self._gen_request(properties={'dimensions': dims}) |
| 141 result_summary = task_scheduler.schedule_request(request, None) |
| 142 self.assertEqual(nb_task, self.execute_tasks()) |
| 143 return result_summary |
| 144 |
| 145 def _register_bot(self, bot_dimensions, nb_task=0): |
| 146 """Registers the bot so the task queues knows there's a worker than can run |
| 147 the task. |
| 148 """ |
| 149 bot_management.bot_event( |
| 150 'bot_connected', bot_dimensions[u'id'][0], '1.2.3.4', 'joe@localhost', |
| 151 bot_dimensions, {'state': 'real'}, '1234', False, None, None) |
| 152 task_queues.assert_bot(bot_dimensions) |
| 153 self.assertEqual(nb_task, self.execute_tasks()) |
| 154 |
| 155 def _quick_reap(self, nb_task=0): |
| 156 """Reaps a task.""" |
| 157 self._quick_schedule({u'os': u'Windows-3.1.1', u'pool': u'default'}) |
| 158 bot_dimensions = { |
| 159 u'id': [u'localhost'], |
| 160 u'os': [u'Windows-3.1.1'], |
| 161 u'pool': [u'default'], |
| 162 } |
| 163 self._register_bot(bot_dimensions, nb_task=nb_task) |
| 164 reaped_request, _, run_result = task_scheduler.bot_reap_task( |
| 165 bot_dimensions, 'abc', None) |
| 166 return run_result |
| 167 |
170 def test_all_apis_are_tested(self): | 168 def test_all_apis_are_tested(self): |
171 # Ensures there's a test for each public API. | 169 # Ensures there's a test for each public API. |
172 # TODO(maruel): Remove this once coverage is asserted. | 170 # TODO(maruel): Remove this once coverage is asserted. |
173 module = task_scheduler | 171 module = task_scheduler |
174 expected = set( | 172 expected = set( |
175 i for i in dir(module) | 173 i for i in dir(module) |
176 if i[0] != '_' and hasattr(getattr(module, i), 'func_name')) | 174 if i[0] != '_' and hasattr(getattr(module, i), 'func_name')) |
177 missing = expected - set(i[5:] for i in dir(self) if i.startswith('test_')) | 175 missing = expected - set(i[5:] for i in dir(self) if i.startswith('test_')) |
178 self.assertFalse(missing) | 176 self.assertFalse(missing) |
179 | 177 |
180 def test_bot_reap_task(self): | 178 def test_bot_reap_task(self): |
181 request = _gen_request( | 179 request = self._gen_request( |
182 properties={ | 180 properties={ |
183 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, | 181 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, |
184 }) | 182 }) |
185 task_request.init_new_request(request, True, None) | 183 task_request.init_new_request(request, True, None) |
186 _result_summary = task_scheduler.schedule_request(request, None) | 184 _result_summary = task_scheduler.schedule_request(request, None) |
187 bot_dimensions = { | 185 bot_dimensions = { |
188 u'foo': [u'bar'], | 186 u'foo': [u'bar'], |
189 u'id': [u'localhost'], | 187 u'id': [u'localhost'], |
190 u'os': [u'Windows', u'Windows-3.1.1'], | 188 u'os': [u'Windows', u'Windows-3.1.1'], |
191 u'pool': [u'default'], | 189 u'pool': [u'default'], |
192 } | 190 } |
193 _register_bot(bot_dimensions) | 191 self._register_bot(bot_dimensions) |
194 actual_request, _, run_result = task_scheduler.bot_reap_task( | 192 actual_request, _, run_result = task_scheduler.bot_reap_task( |
195 bot_dimensions, 'abc', None) | 193 bot_dimensions, 'abc', None) |
196 self.assertEqual(request, actual_request) | 194 self.assertEqual(request, actual_request) |
197 self.assertEqual('localhost', run_result.bot_id) | 195 self.assertEqual('localhost', run_result.bot_id) |
198 self.assertEqual(None, task_to_run.TaskToRun.query().get().queue_number) | 196 self.assertEqual(None, task_to_run.TaskToRun.query().get().queue_number) |
199 | 197 |
200 def test_bot_reap_task_not_enough_time(self): | 198 def test_bot_reap_task_not_enough_time(self): |
201 request = _gen_request( | 199 request = self._gen_request( |
202 properties={ | 200 properties={ |
203 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, | 201 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, |
204 }) | 202 }) |
205 task_request.init_new_request(request, True, None) | 203 task_request.init_new_request(request, True, None) |
206 _result_summary = task_scheduler.schedule_request(request, None) | 204 _result_summary = task_scheduler.schedule_request(request, None) |
207 bot_dimensions = { | 205 bot_dimensions = { |
208 u'foo': [u'bar'], | 206 u'foo': [u'bar'], |
209 u'id': [u'localhost'], | 207 u'id': [u'localhost'], |
210 u'os': [u'Windows', u'Windows-3.1.1'], | 208 u'os': [u'Windows', u'Windows-3.1.1'], |
211 u'pool': [u'default'], | 209 u'pool': [u'default'], |
212 } | 210 } |
213 _register_bot(bot_dimensions) | 211 self._register_bot(bot_dimensions) |
214 actual_request, _, run_result = task_scheduler.bot_reap_task( | 212 actual_request, _, run_result = task_scheduler.bot_reap_task( |
215 bot_dimensions, 'abc', datetime.datetime(1969, 1, 1)) | 213 bot_dimensions, 'abc', datetime.datetime(1969, 1, 1)) |
216 self.failIf(actual_request) | 214 self.failIf(actual_request) |
217 self.failIf(run_result) | 215 self.failIf(run_result) |
218 self.failUnless(task_to_run.TaskToRun.query().get().queue_number) | 216 self.failUnless(task_to_run.TaskToRun.query().get().queue_number) |
219 | 217 |
220 def test_bot_reap_task_enough_time(self): | 218 def test_bot_reap_task_enough_time(self): |
221 request = _gen_request( | 219 request = self._gen_request( |
222 properties={ | 220 properties={ |
223 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, | 221 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, |
224 }) | 222 }) |
225 task_request.init_new_request(request, True, None) | 223 task_request.init_new_request(request, True, None) |
226 _result_summary = task_scheduler.schedule_request(request, None) | 224 _result_summary = task_scheduler.schedule_request(request, None) |
227 bot_dimensions = { | 225 bot_dimensions = { |
228 u'foo': [u'bar'], | 226 u'foo': [u'bar'], |
229 u'id': [u'localhost'], | 227 u'id': [u'localhost'], |
230 u'os': [u'Windows', u'Windows-3.1.1'], | 228 u'os': [u'Windows', u'Windows-3.1.1'], |
231 u'pool': [u'default'], | 229 u'pool': [u'default'], |
232 } | 230 } |
233 _register_bot(bot_dimensions) | 231 self._register_bot(bot_dimensions) |
234 actual_request, _, run_result = task_scheduler.bot_reap_task( | 232 actual_request, _, run_result = task_scheduler.bot_reap_task( |
235 bot_dimensions, 'abc', datetime.datetime(3000, 1, 1)) | 233 bot_dimensions, 'abc', datetime.datetime(3000, 1, 1)) |
236 self.assertEqual(request, actual_request) | 234 self.assertEqual(request, actual_request) |
237 self.assertEqual('localhost', run_result.bot_id) | 235 self.assertEqual('localhost', run_result.bot_id) |
238 self.failIf(task_to_run.TaskToRun.query().get().queue_number) | 236 self.failIf(task_to_run.TaskToRun.query().get().queue_number) |
239 | 237 |
240 def test_exponential_backoff(self): | 238 def test_exponential_backoff(self): |
241 self.mock( | 239 self.mock( |
242 task_scheduler.random, 'random', | 240 task_scheduler.random, 'random', |
243 lambda: task_scheduler._PROBABILITY_OF_QUICK_COMEBACK) | 241 lambda: task_scheduler._PROBABILITY_OF_QUICK_COMEBACK) |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
279 }) | 277 }) |
280 self.assertEqual([ | 278 self.assertEqual([ |
281 { | 279 { |
282 'attributes': {'auth_token': 'token'}, | 280 'attributes': {'auth_token': 'token'}, |
283 'message': '{"task_id":"abcdef123","userdata":"userdata"}', | 281 'message': '{"task_id":"abcdef123","userdata":"userdata"}', |
284 'topic': 'projects/abc/topics/def', | 282 'topic': 'projects/abc/topics/def', |
285 }], calls) | 283 }], calls) |
286 | 284 |
287 def _task_ran_successfully(self): | 285 def _task_ran_successfully(self): |
288 """Runs a task successfully and returns the task_id.""" | 286 """Runs a task successfully and returns the task_id.""" |
289 request = _gen_request( | 287 request = self._gen_request( |
290 properties={ | 288 properties={ |
291 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, | 289 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, |
292 'idempotent': True, | 290 'idempotent': True, |
293 }) | 291 }) |
294 task_request.init_new_request(request, True, None) | 292 task_request.init_new_request(request, True, None) |
295 _result_summary = task_scheduler.schedule_request(request, None) | 293 _result_summary = task_scheduler.schedule_request(request, None) |
296 bot_dimensions = { | 294 bot_dimensions = { |
297 u'foo': [u'bar'], | 295 u'foo': [u'bar'], |
298 u'id': [u'localhost'], | 296 u'id': [u'localhost'], |
299 u'os': [u'Windows', u'Windows-3.1.1'], | 297 u'os': [u'Windows', u'Windows-3.1.1'], |
300 u'pool': [u'default'], | 298 u'pool': [u'default'], |
301 } | 299 } |
302 _register_bot(bot_dimensions) | 300 self._register_bot(bot_dimensions) |
303 actual_request, _, run_result = task_scheduler.bot_reap_task( | 301 actual_request, _, run_result = task_scheduler.bot_reap_task( |
304 bot_dimensions, 'abc', None) | 302 bot_dimensions, 'abc', None) |
305 self.assertEqual(request, actual_request) | 303 self.assertEqual(request, actual_request) |
306 self.assertEqual('localhost', run_result.bot_id) | 304 self.assertEqual('localhost', run_result.bot_id) |
307 self.assertEqual(None, task_to_run.TaskToRun.query().get().queue_number) | 305 self.assertEqual(None, task_to_run.TaskToRun.query().get().queue_number) |
308 # It's important to terminate the task with success. | 306 # It's important to terminate the task with success. |
309 self.assertEqual( | 307 self.assertEqual( |
310 task_result.State.COMPLETED, | 308 task_result.State.COMPLETED, |
311 task_scheduler.bot_update_task( | 309 task_scheduler.bot_update_task( |
312 run_result_key=run_result.key, | 310 run_result_key=run_result.key, |
313 bot_id='localhost', | 311 bot_id='localhost', |
314 cipd_pins=None, | 312 cipd_pins=None, |
315 output='Foo1', | 313 output='Foo1', |
316 output_chunk_start=0, | 314 output_chunk_start=0, |
317 exit_code=0, | 315 exit_code=0, |
318 duration=0.1, | 316 duration=0.1, |
319 hard_timeout=False, | 317 hard_timeout=False, |
320 io_timeout=False, | 318 io_timeout=False, |
321 cost_usd=0.1, | 319 cost_usd=0.1, |
322 outputs_ref=None, | 320 outputs_ref=None, |
323 performance_stats=None)) | 321 performance_stats=None)) |
324 return unicode(run_result.task_id) | 322 return unicode(run_result.task_id) |
325 | 323 |
326 def _task_deduped(self, new_ts, deduped_from, task_id, nb_queues, now=None): | 324 def _task_deduped(self, new_ts, deduped_from, task_id, nb_task=0, now=None): |
327 request = _gen_request( | 325 request = self._gen_request( |
328 name='yay', | 326 name='yay', |
329 user='Raoul', | 327 user='Raoul', |
330 properties={ | 328 properties={ |
331 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, | 329 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, |
332 'idempotent': True, | 330 'idempotent': True, |
333 }) | 331 }) |
334 task_request.init_new_request(request, True, None) | 332 task_request.init_new_request(request, True, None) |
335 _result_summary = task_scheduler.schedule_request(request, None) | 333 _result_summary = task_scheduler.schedule_request(request, None) |
336 bot_dimensions = { | 334 bot_dimensions = { |
337 u'foo': [u'bar'], | 335 u'foo': [u'bar'], |
338 u'id': [u'localhost'], | 336 u'id': [u'localhost'], |
339 u'os': [u'Windows', u'Windows-3.1.1'], | 337 u'os': [u'Windows', u'Windows-3.1.1'], |
340 u'pool': [u'default'], | 338 u'pool': [u'default'], |
341 } | 339 } |
342 self.assertEqual(None, task_to_run.TaskToRun.query().get().queue_number) | 340 self.assertEqual(None, task_to_run.TaskToRun.query().get().queue_number) |
343 _register_bot(bot_dimensions) | 341 self._register_bot(bot_dimensions, nb_task=nb_task) |
344 actual_request_2, _, run_result_2 = task_scheduler.bot_reap_task( | 342 actual_request_2, _, run_result_2 = task_scheduler.bot_reap_task( |
345 bot_dimensions, 'abc', None) | 343 bot_dimensions, 'abc', None) |
346 self.assertEqual(None, actual_request_2) | 344 self.assertEqual(None, actual_request_2) |
347 self.assertEqual(nb_queues, self.execute_tasks()) | |
348 result_summary_duped, run_results_duped = get_results(request.key) | 345 result_summary_duped, run_results_duped = get_results(request.key) |
349 expected = { | 346 expected = { |
350 'abandoned_ts': None, | 347 'abandoned_ts': None, |
351 'bot_dimensions': bot_dimensions, | 348 'bot_dimensions': bot_dimensions, |
352 'bot_id': u'localhost', | 349 'bot_id': u'localhost', |
353 'bot_version': u'abc', | 350 'bot_version': u'abc', |
354 'cipd_pins': None, | 351 'cipd_pins': None, |
355 'children_task_ids': [], | 352 'children_task_ids': [], |
356 'completed_ts': now or self.now, | 353 'completed_ts': now or self.now, |
357 'costs_usd': [], | 354 'costs_usd': [], |
(...skipping 28 matching lines...) Expand all Loading... |
386 } | 383 } |
387 self.assertEqual(expected, result_summary_duped.to_dict()) | 384 self.assertEqual(expected, result_summary_duped.to_dict()) |
388 self.assertEqual([], run_results_duped) | 385 self.assertEqual([], run_results_duped) |
389 | 386 |
390 def test_task_idempotent(self): | 387 def test_task_idempotent(self): |
391 # First task is idempotent. | 388 # First task is idempotent. |
392 task_id = self._task_ran_successfully() | 389 task_id = self._task_ran_successfully() |
393 | 390 |
394 # Second task is deduped against first task. | 391 # Second task is deduped against first task. |
395 new_ts = self.mock_now(self.now, config.settings().reusable_task_age_secs-1) | 392 new_ts = self.mock_now(self.now, config.settings().reusable_task_age_secs-1) |
396 self._task_deduped(new_ts, task_id, '1d8dc670a0008a10', 0) | 393 self._task_deduped(new_ts, task_id, '1d8dc670a0008a10') |
397 | 394 |
398 def test_task_idempotent_old(self): | 395 def test_task_idempotent_old(self): |
399 # First task is idempotent. | 396 # First task is idempotent. |
400 self._task_ran_successfully() | 397 self._task_ran_successfully() |
401 | 398 |
402 # Second task is scheduled, first task is too old to be reused. | 399 # Second task is scheduled, first task is too old to be reused. |
403 new_ts = self.mock_now(self.now, config.settings().reusable_task_age_secs) | 400 new_ts = self.mock_now(self.now, config.settings().reusable_task_age_secs) |
404 request = _gen_request( | 401 request = self._gen_request( |
405 name='yay', | 402 name='yay', |
406 user='Raoul', | 403 user='Raoul', |
407 properties={ | 404 properties={ |
408 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, | 405 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, |
409 'idempotent': True, | 406 'idempotent': True, |
410 }) | 407 }) |
411 task_request.init_new_request(request, True, None) | 408 task_request.init_new_request(request, True, None) |
412 _result_summary = task_scheduler.schedule_request(request, None) | 409 _result_summary = task_scheduler.schedule_request(request, None) |
413 # The task was enqueued for execution. | 410 # The task was enqueued for execution. |
414 self.assertNotEqual(None, task_to_run.TaskToRun.query().get().queue_number) | 411 self.assertNotEqual(None, task_to_run.TaskToRun.query().get().queue_number) |
415 | 412 |
416 def test_task_idempotent_three(self): | 413 def test_task_idempotent_three(self): |
417 # First task is idempotent. | 414 # First task is idempotent. |
418 task_id = self._task_ran_successfully() | 415 task_id = self._task_ran_successfully() |
419 | 416 |
420 # Second task is deduped against first task. | 417 # Second task is deduped against first task. |
421 new_ts = self.mock_now(self.now, config.settings().reusable_task_age_secs-1) | 418 new_ts = self.mock_now(self.now, config.settings().reusable_task_age_secs-1) |
422 self._task_deduped(new_ts, task_id, '1d8dc670a0008a10', 0) | 419 self._task_deduped(new_ts, task_id, '1d8dc670a0008a10') |
423 | 420 |
424 # Third task is scheduled, second task is not dedupable, first task is too | 421 # Third task is scheduled, second task is not dedupable, first task is too |
425 # old. | 422 # old. |
426 new_ts = self.mock_now(self.now, config.settings().reusable_task_age_secs) | 423 new_ts = self.mock_now(self.now, config.settings().reusable_task_age_secs) |
427 request = _gen_request( | 424 request = self._gen_request( |
428 name='yay', | 425 name='yay', |
429 user='Jesus', | 426 user='Jesus', |
430 properties={ | 427 properties={ |
431 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, | 428 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, |
432 'idempotent': True, | 429 'idempotent': True, |
433 }) | 430 }) |
434 task_request.init_new_request(request, True, None) | 431 task_request.init_new_request(request, True, None) |
435 _result_summary = task_scheduler.schedule_request(request, None) | 432 _result_summary = task_scheduler.schedule_request(request, None) |
436 # The task was enqueued for execution. | 433 # The task was enqueued for execution. |
437 self.assertNotEqual(None, task_to_run.TaskToRun.query().get().queue_number) | 434 self.assertNotEqual(None, task_to_run.TaskToRun.query().get().queue_number) |
(...skipping 12 matching lines...) Expand all Loading... |
450 second_ts = self.mock_now(self.now, 10) | 447 second_ts = self.mock_now(self.now, 10) |
451 task_id = self._task_ran_successfully() | 448 task_id = self._task_ran_successfully() |
452 | 449 |
453 # Now any of the 2 tasks could be reused. Assert the right one (the most | 450 # Now any of the 2 tasks could be reused. Assert the right one (the most |
454 # recent) is reused. | 451 # recent) is reused. |
455 cfg.reusable_task_age_secs = 100 | 452 cfg.reusable_task_age_secs = 100 |
456 | 453 |
457 # Third task is deduped against second task. That ensures ordering works | 454 # Third task is deduped against second task. That ensures ordering works |
458 # correctly. | 455 # correctly. |
459 third_ts = self.mock_now(self.now, 20) | 456 third_ts = self.mock_now(self.now, 20) |
460 self._task_deduped(third_ts, task_id, '1d69ba3ea8008b10', 0, second_ts) | 457 self._task_deduped(third_ts, task_id, '1d69ba3ea8008b10', now=second_ts) |
461 | 458 |
462 def test_task_parent_children(self): | 459 def test_task_parent_children(self): |
463 # Parent task creates a child task. | 460 # Parent task creates a child task. |
464 parent_id = self._task_ran_successfully() | 461 parent_id = self._task_ran_successfully() |
465 request = _gen_request( | 462 request = self._gen_request( |
466 parent_task_id=parent_id, | 463 parent_task_id=parent_id, |
467 properties={ | 464 properties={ |
468 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, | 465 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, |
469 }) | 466 }) |
470 task_request.init_new_request(request, True, None) | 467 task_request.init_new_request(request, True, None) |
471 result_summary = task_scheduler.schedule_request(request, None) | 468 result_summary = task_scheduler.schedule_request(request, None) |
472 self.assertEqual([], result_summary.children_task_ids) | 469 self.assertEqual([], result_summary.children_task_ids) |
473 self.assertEqual(parent_id, request.parent_task_id) | 470 self.assertEqual(parent_id, request.parent_task_id) |
474 | 471 |
475 parent_run_result_key = task_pack.unpack_run_result_key(parent_id) | 472 parent_run_result_key = task_pack.unpack_run_result_key(parent_id) |
476 parent_res_summary_key = task_pack.run_result_key_to_result_summary_key( | 473 parent_res_summary_key = task_pack.run_result_key_to_result_summary_key( |
477 parent_run_result_key) | 474 parent_run_result_key) |
478 expected = [result_summary.task_id] | 475 expected = [result_summary.task_id] |
479 self.assertEqual(expected, parent_run_result_key.get().children_task_ids) | 476 self.assertEqual(expected, parent_run_result_key.get().children_task_ids) |
480 self.assertEqual(expected, parent_res_summary_key.get().children_task_ids) | 477 self.assertEqual(expected, parent_res_summary_key.get().children_task_ids) |
481 | 478 |
482 def test_task_parent_isolated(self): | 479 def test_task_parent_isolated(self): |
483 request = _gen_request( | 480 request = self._gen_request( |
484 properties={ | 481 properties={ |
485 'command': [], | 482 'command': [], |
486 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, | 483 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, |
487 'inputs_ref': { | 484 'inputs_ref': { |
488 'isolated': '1' * 40, | 485 'isolated': '1' * 40, |
489 'isolatedserver': 'http://localhost:1', | 486 'isolatedserver': 'http://localhost:1', |
490 'namespace': 'default-gzip', | 487 'namespace': 'default-gzip', |
491 }, | 488 }, |
492 }) | 489 }) |
493 task_request.init_new_request(request, True, None) | 490 task_request.init_new_request(request, True, None) |
494 _result_summary = task_scheduler.schedule_request(request, None) | 491 _result_summary = task_scheduler.schedule_request(request, None) |
495 bot_dimensions = { | 492 bot_dimensions = { |
496 u'foo': [u'bar'], | 493 u'foo': [u'bar'], |
497 u'id': [u'localhost'], | 494 u'id': [u'localhost'], |
498 u'os': [u'Windows', u'Windows-3.1.1'], | 495 u'os': [u'Windows', u'Windows-3.1.1'], |
499 u'pool': [u'default'], | 496 u'pool': [u'default'], |
500 } | 497 } |
501 _register_bot(bot_dimensions) | 498 self._register_bot(bot_dimensions) |
502 actual_request, _, run_result = task_scheduler.bot_reap_task( | 499 actual_request, _, run_result = task_scheduler.bot_reap_task( |
503 bot_dimensions, 'abc', None) | 500 bot_dimensions, 'abc', None) |
504 self.assertEqual(request, actual_request) | 501 self.assertEqual(request, actual_request) |
505 self.assertEqual('localhost', run_result.bot_id) | 502 self.assertEqual('localhost', run_result.bot_id) |
506 self.assertEqual(None, task_to_run.TaskToRun.query().get().queue_number) | 503 self.assertEqual(None, task_to_run.TaskToRun.query().get().queue_number) |
507 # It's important to terminate the task with success. | 504 # It's important to terminate the task with success. |
508 self.assertEqual( | 505 self.assertEqual( |
509 task_result.State.COMPLETED, | 506 task_result.State.COMPLETED, |
510 task_scheduler.bot_update_task( | 507 task_scheduler.bot_update_task( |
511 run_result_key=run_result.key, | 508 run_result_key=run_result.key, |
512 bot_id='localhost', | 509 bot_id='localhost', |
513 cipd_pins=None, | 510 cipd_pins=None, |
514 output='Foo1', | 511 output='Foo1', |
515 output_chunk_start=0, | 512 output_chunk_start=0, |
516 exit_code=0, | 513 exit_code=0, |
517 duration=0.1, | 514 duration=0.1, |
518 hard_timeout=False, | 515 hard_timeout=False, |
519 io_timeout=False, | 516 io_timeout=False, |
520 cost_usd=0.1, | 517 cost_usd=0.1, |
521 outputs_ref=None, | 518 outputs_ref=None, |
522 performance_stats=None)) | 519 performance_stats=None)) |
523 | 520 |
524 parent_id = run_result.task_id | 521 parent_id = run_result.task_id |
525 request = _gen_request( | 522 request = self._gen_request( |
526 parent_task_id=parent_id, | 523 parent_task_id=parent_id, |
527 properties={ | 524 properties={ |
528 'dimensions':{u'os': u'Windows-3.1.1', u'pool': u'default'}, | 525 'dimensions':{u'os': u'Windows-3.1.1', u'pool': u'default'}, |
529 }) | 526 }) |
530 task_request.init_new_request(request, True, None) | 527 task_request.init_new_request(request, True, None) |
531 result_summary = task_scheduler.schedule_request(request, None) | 528 result_summary = task_scheduler.schedule_request(request, None) |
532 self.assertEqual([], result_summary.children_task_ids) | 529 self.assertEqual([], result_summary.children_task_ids) |
533 self.assertEqual(parent_id, request.parent_task_id) | 530 self.assertEqual(parent_id, request.parent_task_id) |
534 | 531 |
535 parent_run_result_key = task_pack.unpack_run_result_key(parent_id) | 532 parent_run_result_key = task_pack.unpack_run_result_key(parent_id) |
536 parent_res_summary_key = task_pack.run_result_key_to_result_summary_key( | 533 parent_res_summary_key = task_pack.run_result_key_to_result_summary_key( |
537 parent_run_result_key) | 534 parent_run_result_key) |
538 expected = [result_summary.task_id] | 535 expected = [result_summary.task_id] |
539 self.assertEqual(expected, parent_run_result_key.get().children_task_ids) | 536 self.assertEqual(expected, parent_run_result_key.get().children_task_ids) |
540 self.assertEqual(expected, parent_res_summary_key.get().children_task_ids) | 537 self.assertEqual(expected, parent_res_summary_key.get().children_task_ids) |
541 | 538 |
542 def test_get_results(self): | 539 def test_get_results(self): |
543 # TODO(maruel): Split in more focused tests. | 540 # TODO(maruel): Split in more focused tests. |
544 created_ts = self.now | 541 created_ts = self.now |
545 self.mock_now(created_ts) | 542 self.mock_now(created_ts) |
546 request = _gen_request( | 543 request = self._gen_request( |
547 properties={ | 544 properties={ |
548 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, | 545 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, |
549 }) | 546 }) |
550 task_request.init_new_request(request, True, None) | 547 task_request.init_new_request(request, True, None) |
551 _result_summary = task_scheduler.schedule_request(request, None) | 548 _result_summary = task_scheduler.schedule_request(request, None) |
552 | 549 |
553 # The TaskRequest was enqueued, the TaskResultSummary was created but no | 550 # The TaskRequest was enqueued, the TaskResultSummary was created but no |
554 # TaskRunResult exist yet since the task was not scheduled on any bot. | 551 # TaskRunResult exist yet since the task was not scheduled on any bot. |
555 result_summary, run_results = get_results(request.key) | 552 result_summary, run_results = get_results(request.key) |
556 expected = { | 553 expected = { |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
592 self.assertEqual([], run_results) | 589 self.assertEqual([], run_results) |
593 | 590 |
594 # A bot reaps the TaskToRun. | 591 # A bot reaps the TaskToRun. |
595 reaped_ts = self.now + datetime.timedelta(seconds=60) | 592 reaped_ts = self.now + datetime.timedelta(seconds=60) |
596 self.mock_now(reaped_ts) | 593 self.mock_now(reaped_ts) |
597 bot_dimensions = { | 594 bot_dimensions = { |
598 u'id': [u'localhost'], | 595 u'id': [u'localhost'], |
599 u'os': [u'Windows-3.1.1'], | 596 u'os': [u'Windows-3.1.1'], |
600 u'pool': [u'default'], | 597 u'pool': [u'default'], |
601 } | 598 } |
602 _register_bot(bot_dimensions) | 599 self._register_bot(bot_dimensions) |
603 reaped_request, _, run_result = task_scheduler.bot_reap_task( | 600 reaped_request, _, run_result = task_scheduler.bot_reap_task( |
604 bot_dimensions, 'abc', None) | 601 bot_dimensions, 'abc', None) |
605 self.assertEqual(request, reaped_request) | 602 self.assertEqual(request, reaped_request) |
606 self.assertTrue(run_result) | 603 self.assertTrue(run_result) |
607 result_summary, run_results = get_results(request.key) | 604 result_summary, run_results = get_results(request.key) |
608 expected = { | 605 expected = { |
609 'abandoned_ts': None, | 606 'abandoned_ts': None, |
610 'bot_dimensions': bot_dimensions, | 607 'bot_dimensions': bot_dimensions, |
611 'bot_id': u'localhost', | 608 'bot_id': u'localhost', |
612 'bot_version': u'abc', | 609 'bot_version': u'abc', |
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
779 }, | 776 }, |
780 'server_versions': [u'v1a'], | 777 'server_versions': [u'v1a'], |
781 'started_ts': reaped_ts, | 778 'started_ts': reaped_ts, |
782 'state': State.COMPLETED, | 779 'state': State.COMPLETED, |
783 'try_number': 1, | 780 'try_number': 1, |
784 }, | 781 }, |
785 ] | 782 ] |
786 self.assertEqual(expected, [t.to_dict() for t in run_results]) | 783 self.assertEqual(expected, [t.to_dict() for t in run_results]) |
787 | 784 |
788 def test_exit_code_failure(self): | 785 def test_exit_code_failure(self): |
789 request = _gen_request( | 786 request = self._gen_request( |
790 properties={ | 787 properties={ |
791 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, | 788 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, |
792 }) | 789 }) |
793 task_request.init_new_request(request, True, None) | 790 task_request.init_new_request(request, True, None) |
794 _result_summary = task_scheduler.schedule_request(request, None) | 791 _result_summary = task_scheduler.schedule_request(request, None) |
795 bot_dimensions = { | 792 bot_dimensions = { |
796 u'id': [u'localhost'], | 793 u'id': [u'localhost'], |
797 u'os': [u'Windows-3.1.1'], | 794 u'os': [u'Windows-3.1.1'], |
798 u'pool': [u'default'], | 795 u'pool': [u'default'], |
799 } | 796 } |
800 _register_bot(bot_dimensions) | 797 self._register_bot(bot_dimensions) |
801 reaped_request, _, run_result = task_scheduler.bot_reap_task( | 798 reaped_request, _, run_result = task_scheduler.bot_reap_task( |
802 bot_dimensions, 'abc', None) | 799 bot_dimensions, 'abc', None) |
803 self.assertEqual(request, reaped_request) | 800 self.assertEqual(request, reaped_request) |
804 self.assertEqual( | 801 self.assertEqual( |
805 task_result.State.COMPLETED, | 802 task_result.State.COMPLETED, |
806 task_scheduler.bot_update_task( | 803 task_scheduler.bot_update_task( |
807 run_result_key=run_result.key, | 804 run_result_key=run_result.key, |
808 bot_id='localhost', | 805 bot_id='localhost', |
809 cipd_pins=None, | 806 cipd_pins=None, |
810 output='Foo1', | 807 output='Foo1', |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
876 'started_ts': self.now, | 873 'started_ts': self.now, |
877 'state': State.COMPLETED, | 874 'state': State.COMPLETED, |
878 'try_number': 1, | 875 'try_number': 1, |
879 }, | 876 }, |
880 ] | 877 ] |
881 self.assertEqual(expected, [t.to_dict() for t in run_results]) | 878 self.assertEqual(expected, [t.to_dict() for t in run_results]) |
882 | 879 |
883 def test_schedule_request(self): | 880 def test_schedule_request(self): |
884 # It is tested indirectly in the other functions. | 881 # It is tested indirectly in the other functions. |
885 self.assertTrue( | 882 self.assertTrue( |
886 _quick_schedule({u'os': u'Windows-3.1.1', u'pool': u'default'})) | 883 self._quick_schedule({u'os': u'Windows-3.1.1', u'pool': u'default'})) |
887 | 884 |
888 def mock_dim_acls(self, mapping): | 885 def mock_dim_acls(self, mapping): |
889 self.mock(config, 'settings', lambda: config_pb2.SettingsCfg( | 886 self.mock(config, 'settings', lambda: config_pb2.SettingsCfg( |
890 dimension_acls=config_pb2.DimensionACLs(entry=[ | 887 dimension_acls=config_pb2.DimensionACLs(entry=[ |
891 config_pb2.DimensionACLs.Entry(dimension=[d], usable_by=g) | 888 config_pb2.DimensionACLs.Entry(dimension=[d], usable_by=g) |
892 for d, g in sorted(mapping.iteritems()) | 889 for d, g in sorted(mapping.iteritems()) |
893 ]), | 890 ]), |
894 )) | 891 )) |
895 | 892 |
896 def test_schedule_request_forbidden_dim(self): | 893 def test_schedule_request_forbidden_dim(self): |
897 self.mock_dim_acls({u'pool:bad': u'noone'}) | 894 self.mock_dim_acls({u'pool:bad': u'noone'}) |
898 _quick_schedule({u'pool': u'good'}) | 895 self._quick_schedule({u'pool': u'good'}) |
899 with self.assertRaises(auth.AuthorizationError): | 896 with self.assertRaises(auth.AuthorizationError): |
900 _quick_schedule({u'pool': u'bad'}) | 897 self._quick_schedule({u'pool': u'bad'}) |
901 | 898 |
902 def test_schedule_request_forbidden_dim_via_star(self): | 899 def test_schedule_request_forbidden_dim_via_star(self): |
903 self.mock_dim_acls({u'abc:*': u'noone'}) | 900 self.mock_dim_acls({u'abc:*': u'noone'}) |
904 _quick_schedule({u'pool': u'default'}) | 901 self._quick_schedule({u'pool': u'default'}) |
905 with self.assertRaises(auth.AuthorizationError): | 902 with self.assertRaises(auth.AuthorizationError): |
906 _quick_schedule({u'pool': u'default', u'abc': u'blah'}) | 903 self._quick_schedule({u'pool': u'default', u'abc': u'blah'}) |
907 | 904 |
908 def test_schedule_request_id_without_pool(self): | 905 def test_schedule_request_id_without_pool(self): |
909 self.mock_dim_acls({u'pool:good': u'mocked'}) | 906 self.mock_dim_acls({u'pool:good': u'mocked'}) |
910 with self.assertRaises(auth.AuthorizationError): | 907 with self.assertRaises(auth.AuthorizationError): |
911 _quick_schedule({u'id': u'abc'}) | 908 self._quick_schedule({u'id': u'abc'}) |
912 auth_testing.mock_is_admin(self) | 909 auth_testing.mock_is_admin(self) |
913 _quick_schedule({u'id': u'abc'}) | 910 self._quick_schedule({u'id': u'abc'}) |
914 | 911 |
915 def test_schedule_request_id_and_pool(self): | 912 def test_schedule_request_id_and_pool(self): |
916 self.mock_dim_acls({u'pool:good': u'mocked'}) | 913 self.mock_dim_acls({u'pool:good': u'mocked'}) |
917 self.mock_dim_acls({u'pool:bad': u'unknown'}) | 914 self.mock_dim_acls({u'pool:bad': u'unknown'}) |
918 | 915 |
919 def mocked_is_group_member(group, ident): | 916 def mocked_is_group_member(group, ident): |
920 if group == 'mocked' and ident == auth_testing.DEFAULT_MOCKED_IDENTITY: | 917 if group == 'mocked' and ident == auth_testing.DEFAULT_MOCKED_IDENTITY: |
921 return True | 918 return True |
922 return False | 919 return False |
923 self.mock(auth, 'is_group_member', mocked_is_group_member) | 920 self.mock(auth, 'is_group_member', mocked_is_group_member) |
924 | 921 |
925 _quick_schedule({u'id': u'abc', u'pool': u'unknown'}) | 922 self._quick_schedule({u'id': u'abc', u'pool': u'unknown'}) |
926 _quick_schedule({u'id': u'abc', u'pool': u'good'}) | 923 self._quick_schedule({u'id': u'abc', u'pool': u'good'}) |
927 with self.assertRaises(auth.AuthorizationError): | 924 with self.assertRaises(auth.AuthorizationError): |
928 _quick_schedule({u'id': u'abc', u'pool': u'bad'}) | 925 self._quick_schedule({u'id': u'abc', u'pool': u'bad'}) |
929 | 926 |
930 def test_bot_update_task(self): | 927 def test_bot_update_task(self): |
931 run_result = _quick_reap() | 928 run_result = self._quick_reap() |
932 self.assertEqual( | 929 self.assertEqual( |
933 task_result.State.RUNNING, | 930 task_result.State.RUNNING, |
934 task_scheduler.bot_update_task( | 931 task_scheduler.bot_update_task( |
935 run_result_key=run_result.key, | 932 run_result_key=run_result.key, |
936 bot_id='localhost', | 933 bot_id='localhost', |
937 cipd_pins=None, | 934 cipd_pins=None, |
938 output='hi', | 935 output='hi', |
939 output_chunk_start=0, | 936 output_chunk_start=0, |
940 exit_code=None, | 937 exit_code=None, |
941 duration=None, | 938 duration=None, |
(...skipping 13 matching lines...) Expand all Loading... |
955 exit_code=0, | 952 exit_code=0, |
956 duration=0.1, | 953 duration=0.1, |
957 hard_timeout=False, | 954 hard_timeout=False, |
958 io_timeout=False, | 955 io_timeout=False, |
959 cost_usd=0.1, | 956 cost_usd=0.1, |
960 outputs_ref=None, | 957 outputs_ref=None, |
961 performance_stats=None)) | 958 performance_stats=None)) |
962 self.assertEqual('hihey', run_result.key.get().get_output()) | 959 self.assertEqual('hihey', run_result.key.get().get_output()) |
963 | 960 |
964 def test_bot_update_task_new_overwrite(self): | 961 def test_bot_update_task_new_overwrite(self): |
965 run_result = _quick_reap() | 962 run_result = self._quick_reap() |
966 self.assertEqual( | 963 self.assertEqual( |
967 task_result.State.RUNNING, | 964 task_result.State.RUNNING, |
968 task_scheduler.bot_update_task( | 965 task_scheduler.bot_update_task( |
969 run_result_key=run_result.key, | 966 run_result_key=run_result.key, |
970 bot_id='localhost', | 967 bot_id='localhost', |
971 cipd_pins=None, | 968 cipd_pins=None, |
972 output='hi', | 969 output='hi', |
973 output_chunk_start=0, | 970 output_chunk_start=0, |
974 exit_code=None, | 971 exit_code=None, |
975 duration=None, | 972 duration=None, |
(...skipping 13 matching lines...) Expand all Loading... |
989 exit_code=None, | 986 exit_code=None, |
990 duration=None, | 987 duration=None, |
991 hard_timeout=False, | 988 hard_timeout=False, |
992 io_timeout=False, | 989 io_timeout=False, |
993 cost_usd=0.1, | 990 cost_usd=0.1, |
994 outputs_ref=None, | 991 outputs_ref=None, |
995 performance_stats=None)) | 992 performance_stats=None)) |
996 self.assertEqual('hhey', run_result.key.get().get_output()) | 993 self.assertEqual('hhey', run_result.key.get().get_output()) |
997 | 994 |
998 def test_bot_update_exception(self): | 995 def test_bot_update_exception(self): |
999 run_result = _quick_reap() | 996 run_result = self._quick_reap() |
1000 def r(*_): | 997 def r(*_): |
1001 raise datastore_utils.CommitError('Sorry!') | 998 raise datastore_utils.CommitError('Sorry!') |
1002 | 999 |
1003 self.mock(ndb, 'put_multi', r) | 1000 self.mock(ndb, 'put_multi', r) |
1004 self.assertEqual( | 1001 self.assertEqual( |
1005 None, | 1002 None, |
1006 task_scheduler.bot_update_task( | 1003 task_scheduler.bot_update_task( |
1007 run_result_key=run_result.key, | 1004 run_result_key=run_result.key, |
1008 bot_id='localhost', | 1005 bot_id='localhost', |
1009 cipd_pins=None, | 1006 cipd_pins=None, |
1010 output='hi', | 1007 output='hi', |
1011 output_chunk_start=0, | 1008 output_chunk_start=0, |
1012 exit_code=0, | 1009 exit_code=0, |
1013 duration=0.1, | 1010 duration=0.1, |
1014 hard_timeout=False, | 1011 hard_timeout=False, |
1015 io_timeout=False, | 1012 io_timeout=False, |
1016 cost_usd=0.1, | 1013 cost_usd=0.1, |
1017 outputs_ref=None, | 1014 outputs_ref=None, |
1018 performance_stats=None)) | 1015 performance_stats=None)) |
1019 | 1016 |
1020 def test_bot_update_pubsub_error(self): | 1017 def test_bot_update_pubsub_error(self): |
1021 pub_sub_calls = self.mock_pub_sub() | 1018 pub_sub_calls = self.mock_pub_sub() |
1022 request = _gen_request( | 1019 request = self._gen_request( |
1023 properties={ | 1020 properties={ |
1024 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, | 1021 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, |
1025 }, | 1022 }, |
1026 pubsub_topic='projects/abc/topics/def') | 1023 pubsub_topic='projects/abc/topics/def') |
1027 task_request.init_new_request(request, True, None) | 1024 task_request.init_new_request(request, True, None) |
1028 task_scheduler.schedule_request(request, None) | 1025 task_scheduler.schedule_request(request, None) |
1029 bot_dimensions = { | 1026 bot_dimensions = { |
1030 u'foo': [u'bar'], | 1027 u'foo': [u'bar'], |
1031 u'id': [u'localhost'], | 1028 u'id': [u'localhost'], |
1032 u'os': [u'Windows', u'Windows-3.1.1'], | 1029 u'os': [u'Windows', u'Windows-3.1.1'], |
1033 u'pool': [u'default'], | 1030 u'pool': [u'default'], |
1034 } | 1031 } |
1035 _register_bot(bot_dimensions) | 1032 self._register_bot(bot_dimensions) |
1036 _, _, run_result = task_scheduler.bot_reap_task( | 1033 _, _, run_result = task_scheduler.bot_reap_task( |
1037 bot_dimensions, 'abc', None) | 1034 bot_dimensions, 'abc', None) |
1038 self.assertEqual('localhost', run_result.bot_id) | 1035 self.assertEqual('localhost', run_result.bot_id) |
1039 self.assertEqual(1, self.execute_tasks()) | |
1040 | 1036 |
1041 # Attempt to terminate the task with success, but make PubSub call fail. | 1037 # Attempt to terminate the task with success, but make PubSub call fail. |
1042 self.publish_successful = False | 1038 self.publish_successful = False |
1043 self.assertEqual( | 1039 self.assertEqual( |
1044 None, | 1040 None, |
1045 task_scheduler.bot_update_task( | 1041 task_scheduler.bot_update_task( |
1046 run_result_key=run_result.key, | 1042 run_result_key=run_result.key, |
1047 bot_id='localhost', | 1043 bot_id='localhost', |
1048 cipd_pins=None, | 1044 cipd_pins=None, |
1049 output='Foo1', | 1045 output='Foo1', |
1050 output_chunk_start=0, | 1046 output_chunk_start=0, |
1051 exit_code=0, | 1047 exit_code=0, |
1052 duration=0.1, | 1048 duration=0.1, |
1053 hard_timeout=False, | 1049 hard_timeout=False, |
1054 io_timeout=False, | 1050 io_timeout=False, |
1055 cost_usd=0.1, | 1051 cost_usd=0.1, |
1056 outputs_ref=None, | 1052 outputs_ref=None, |
1057 performance_stats=None)) | 1053 performance_stats=None)) |
1058 self.assertEqual(0, self.execute_tasks()) | 1054 self.assertEqual(1, self.execute_tasks(status=500)) |
1059 | 1055 |
1060 # Bot retries bot_update, now PubSub works and notification is sent. | 1056 # Bot retries bot_update, now PubSub works and notification is sent. |
1061 self.publish_successful = True | 1057 self.publish_successful = True |
1062 self.assertEqual( | 1058 self.assertEqual( |
1063 task_result.State.COMPLETED, | 1059 task_result.State.COMPLETED, |
1064 task_scheduler.bot_update_task( | 1060 task_scheduler.bot_update_task( |
1065 run_result_key=run_result.key, | 1061 run_result_key=run_result.key, |
1066 bot_id='localhost', | 1062 bot_id='localhost', |
1067 cipd_pins=None, | 1063 cipd_pins=None, |
1068 output='Foo1', | 1064 output='Foo1', |
1069 output_chunk_start=0, | 1065 output_chunk_start=0, |
1070 exit_code=0, | 1066 exit_code=0, |
1071 duration=0.1, | 1067 duration=0.1, |
1072 hard_timeout=False, | 1068 hard_timeout=False, |
1073 io_timeout=False, | 1069 io_timeout=False, |
1074 cost_usd=0.1, | 1070 cost_usd=0.1, |
1075 outputs_ref=None, | 1071 outputs_ref=None, |
1076 performance_stats=None)) | 1072 performance_stats=None)) |
1077 self.assertEqual(0, self.execute_tasks()) | 1073 self.assertEqual(0, self.execute_tasks()) |
1078 self.assertEqual(2, len(pub_sub_calls)) # notification is sent | 1074 self.assertEqual(1, len(pub_sub_calls)) # notification is sent |
1079 | 1075 |
1080 def _bot_update_timeouts(self, hard, io): | 1076 def _bot_update_timeouts(self, hard, io): |
1081 request = _gen_request( | 1077 request = self._gen_request( |
1082 properties={ | 1078 properties={ |
1083 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, | 1079 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, |
1084 }) | 1080 }) |
1085 task_request.init_new_request(request, True, None) | 1081 task_request.init_new_request(request, True, None) |
1086 result_summary = task_scheduler.schedule_request(request, None) | 1082 result_summary = task_scheduler.schedule_request(request, None) |
1087 bot_dimensions = { | 1083 bot_dimensions = { |
1088 u'id': [u'localhost'], | 1084 u'id': [u'localhost'], |
1089 u'os': [u'Windows-3.1.1'], | 1085 u'os': [u'Windows-3.1.1'], |
1090 u'pool': [u'default'], | 1086 u'pool': [u'default'], |
1091 } | 1087 } |
1092 _register_bot(bot_dimensions) | 1088 self._register_bot(bot_dimensions) |
1093 reaped_request, _, run_result = task_scheduler.bot_reap_task( | 1089 reaped_request, _, run_result = task_scheduler.bot_reap_task( |
1094 bot_dimensions, 'abc', None) | 1090 bot_dimensions, 'abc', None) |
1095 self.assertEqual( | 1091 self.assertEqual( |
1096 task_result.State.TIMED_OUT, | 1092 task_result.State.TIMED_OUT, |
1097 task_scheduler.bot_update_task( | 1093 task_scheduler.bot_update_task( |
1098 run_result_key=run_result.key, | 1094 run_result_key=run_result.key, |
1099 bot_id='localhost', | 1095 bot_id='localhost', |
1100 cipd_pins=None, | 1096 cipd_pins=None, |
1101 output='hi', | 1097 output='hi', |
1102 output_chunk_start=0, | 1098 output_chunk_start=0, |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1169 | 1165 |
1170 def test_bot_update_hard_timeout(self): | 1166 def test_bot_update_hard_timeout(self): |
1171 self._bot_update_timeouts(True, False) | 1167 self._bot_update_timeouts(True, False) |
1172 | 1168 |
1173 def test_bot_update_io_timeout(self): | 1169 def test_bot_update_io_timeout(self): |
1174 self._bot_update_timeouts(False, True) | 1170 self._bot_update_timeouts(False, True) |
1175 | 1171 |
1176 def test_bot_kill_task(self): | 1172 def test_bot_kill_task(self): |
1177 pub_sub_calls = self.mock_pub_sub() | 1173 pub_sub_calls = self.mock_pub_sub() |
1178 dimensions = {u'os': u'Windows-3.1.1', u'pool': u'default'} | 1174 dimensions = {u'os': u'Windows-3.1.1', u'pool': u'default'} |
1179 request = _gen_request( | 1175 request = self._gen_request( |
1180 properties={'dimensions': dimensions}, | 1176 properties={'dimensions': dimensions}, |
1181 pubsub_topic='projects/abc/topics/def') | 1177 pubsub_topic='projects/abc/topics/def') |
1182 task_request.init_new_request(request, True, None) | 1178 task_request.init_new_request(request, True, None) |
1183 result_summary = task_scheduler.schedule_request(request, None) | 1179 result_summary = task_scheduler.schedule_request(request, None) |
1184 bot_dimensions = { | 1180 bot_dimensions = { |
1185 u'id': [u'localhost'], | 1181 u'id': [u'localhost'], |
1186 u'os': [u'Windows-3.1.1'], | 1182 u'os': [u'Windows-3.1.1'], |
1187 u'pool': [u'default'], | 1183 u'pool': [u'default'], |
1188 } | 1184 } |
1189 _register_bot(bot_dimensions) | 1185 self._register_bot(bot_dimensions) |
1190 reaped_request, _, run_result = task_scheduler.bot_reap_task( | 1186 reaped_request, _, run_result = task_scheduler.bot_reap_task( |
1191 bot_dimensions, 'abc', None) | 1187 bot_dimensions, 'abc', None) |
1192 self.assertEqual(1, self.execute_tasks()) | 1188 self.assertEqual(1, self.execute_tasks()) |
1193 self.assertEqual(1, len(pub_sub_calls)) # PENDING -> RUNNING | 1189 self.assertEqual(1, len(pub_sub_calls)) # PENDING -> RUNNING |
1194 | 1190 |
1195 self.assertEqual( | 1191 self.assertEqual( |
1196 None, task_scheduler.bot_kill_task(run_result.key, 'localhost')) | 1192 None, task_scheduler.bot_kill_task(run_result.key, 'localhost')) |
1197 expected = { | 1193 expected = { |
1198 'abandoned_ts': self.now, | 1194 'abandoned_ts': self.now, |
1199 'bot_dimensions': bot_dimensions, | 1195 'bot_dimensions': bot_dimensions, |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1249 'server_versions': [u'v1a'], | 1245 'server_versions': [u'v1a'], |
1250 'started_ts': self.now, | 1246 'started_ts': self.now, |
1251 'state': State.BOT_DIED, | 1247 'state': State.BOT_DIED, |
1252 'try_number': 1, | 1248 'try_number': 1, |
1253 } | 1249 } |
1254 self.assertEqual(expected, run_result.key.get().to_dict()) | 1250 self.assertEqual(expected, run_result.key.get().to_dict()) |
1255 self.assertEqual(1, self.execute_tasks()) | 1251 self.assertEqual(1, self.execute_tasks()) |
1256 self.assertEqual(2, len(pub_sub_calls)) # RUNNING -> BOT_DIED | 1252 self.assertEqual(2, len(pub_sub_calls)) # RUNNING -> BOT_DIED |
1257 | 1253 |
1258 def test_bot_kill_task_wrong_bot(self): | 1254 def test_bot_kill_task_wrong_bot(self): |
1259 request = _gen_request( | 1255 request = self._gen_request( |
1260 properties={ | 1256 properties={ |
1261 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, | 1257 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, |
1262 }) | 1258 }) |
1263 task_request.init_new_request(request, True, None) | 1259 task_request.init_new_request(request, True, None) |
1264 result_summary = task_scheduler.schedule_request(request, None) | 1260 result_summary = task_scheduler.schedule_request(request, None) |
1265 bot_dimensions = { | 1261 bot_dimensions = { |
1266 u'id': [u'localhost'], | 1262 u'id': [u'localhost'], |
1267 u'os': [u'Windows-3.1.1'], | 1263 u'os': [u'Windows-3.1.1'], |
1268 u'pool': [u'default'], | 1264 u'pool': [u'default'], |
1269 } | 1265 } |
1270 _register_bot(bot_dimensions) | 1266 self._register_bot(bot_dimensions) |
1271 reaped_request, _, run_result = task_scheduler.bot_reap_task( | 1267 reaped_request, _, run_result = task_scheduler.bot_reap_task( |
1272 bot_dimensions, 'abc', None) | 1268 bot_dimensions, 'abc', None) |
1273 expected = ( | 1269 expected = ( |
1274 'Bot bot1 sent task kill for task 1d69b9f088008911 owned by bot ' | 1270 'Bot bot1 sent task kill for task 1d69b9f088008911 owned by bot ' |
1275 'localhost') | 1271 'localhost') |
1276 self.assertEqual( | 1272 self.assertEqual( |
1277 expected, task_scheduler.bot_kill_task(run_result.key, 'bot1')) | 1273 expected, task_scheduler.bot_kill_task(run_result.key, 'bot1')) |
1278 | 1274 |
1279 def test_cancel_task(self): | 1275 def test_cancel_task(self): |
1280 request = _gen_request( | 1276 request = self._gen_request( |
1281 properties={ | 1277 properties={ |
1282 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, | 1278 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, |
1283 }, | 1279 }, |
1284 pubsub_topic='projects/abc/topics/def') | 1280 pubsub_topic='projects/abc/topics/def') |
1285 pub_sub_calls = self.mock_pub_sub() | 1281 pub_sub_calls = self.mock_pub_sub() |
1286 task_request.init_new_request(request, True, None) | 1282 task_request.init_new_request(request, True, None) |
1287 result_summary = task_scheduler.schedule_request(request, None) | 1283 result_summary = task_scheduler.schedule_request(request, None) |
1288 ok, was_running = task_scheduler.cancel_task(request, result_summary.key) | 1284 ok, was_running = task_scheduler.cancel_task(request, result_summary.key) |
1289 self.assertEqual(True, ok) | 1285 self.assertEqual(True, ok) |
1290 self.assertEqual(False, was_running) | 1286 self.assertEqual(False, was_running) |
1291 result_summary = result_summary.key.get() | 1287 result_summary = result_summary.key.get() |
1292 self.assertEqual(task_result.State.CANCELED, result_summary.state) | 1288 self.assertEqual(task_result.State.CANCELED, result_summary.state) |
1293 self.assertEqual(1, self.execute_tasks()) | 1289 self.assertEqual(1, self.execute_tasks()) |
1294 self.assertEqual(1, len(pub_sub_calls)) # sent completion notification | 1290 self.assertEqual(1, len(pub_sub_calls)) # sent completion notification |
1295 | 1291 |
1296 def test_cancel_task_running(self): | 1292 def test_cancel_task_running(self): |
1297 request = _gen_request( | 1293 request = self._gen_request( |
1298 properties={ | 1294 properties={ |
1299 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, | 1295 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, |
1300 }, | 1296 }, |
1301 pubsub_topic='projects/abc/topics/def') | 1297 pubsub_topic='projects/abc/topics/def') |
1302 pub_sub_calls = self.mock_pub_sub() | 1298 pub_sub_calls = self.mock_pub_sub() |
1303 task_request.init_new_request(request, True, None) | 1299 task_request.init_new_request(request, True, None) |
1304 result_summary = task_scheduler.schedule_request(request, None) | 1300 result_summary = task_scheduler.schedule_request(request, None) |
1305 bot_dimensions = { | 1301 bot_dimensions = { |
1306 u'id': [u'localhost'], | 1302 u'id': [u'localhost'], |
1307 u'os': [u'Windows-3.1.1'], | 1303 u'os': [u'Windows-3.1.1'], |
1308 u'pool': [u'default'], | 1304 u'pool': [u'default'], |
1309 } | 1305 } |
1310 _register_bot(bot_dimensions) | 1306 self._register_bot(bot_dimensions) |
1311 reaped_request, _, run_result = task_scheduler.bot_reap_task( | 1307 reaped_request, _, run_result = task_scheduler.bot_reap_task( |
1312 bot_dimensions, 'abc', None) | 1308 bot_dimensions, 'abc', None) |
1313 ok, was_running = task_scheduler.cancel_task(request, result_summary.key) | 1309 ok, was_running = task_scheduler.cancel_task(request, result_summary.key) |
1314 self.assertEqual(False, ok) | 1310 self.assertEqual(False, ok) |
1315 self.assertEqual(True, was_running) | 1311 self.assertEqual(True, was_running) |
1316 result_summary = result_summary.key.get() | 1312 result_summary = result_summary.key.get() |
1317 self.assertEqual(task_result.State.RUNNING, result_summary.state) | 1313 self.assertEqual(task_result.State.RUNNING, result_summary.state) |
1318 self.assertEqual(1, self.execute_tasks()) | 1314 self.assertEqual(1, self.execute_tasks()) |
1319 self.assertEqual(1, len(pub_sub_calls)) # PENDING -> RUNNING | 1315 self.assertEqual(1, len(pub_sub_calls)) # PENDING -> RUNNING |
1320 | 1316 |
1321 def test_cron_abort_expired_task_to_run(self): | 1317 def test_cron_abort_expired_task_to_run(self): |
1322 request = _gen_request( | 1318 request = self._gen_request( |
1323 properties={ | 1319 properties={ |
1324 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, | 1320 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, |
1325 }, | 1321 }, |
1326 pubsub_topic='projects/abc/topics/def') | 1322 pubsub_topic='projects/abc/topics/def') |
1327 task_request.init_new_request(request, True, None) | 1323 task_request.init_new_request(request, True, None) |
1328 pub_sub_calls = self.mock_pub_sub() | 1324 pub_sub_calls = self.mock_pub_sub() |
1329 result_summary = task_scheduler.schedule_request(request, None) | 1325 result_summary = task_scheduler.schedule_request(request, None) |
1330 abandoned_ts = self.mock_now(self.now, request.expiration_secs+1) | 1326 abandoned_ts = self.mock_now(self.now, request.expiration_secs+1) |
1331 self.assertEqual( | 1327 self.assertEqual( |
1332 ['1d69b9f088008910'], | 1328 ['1d69b9f088008910'], |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1367 'try_number': None, | 1363 'try_number': None, |
1368 'user': u'Jesus', | 1364 'user': u'Jesus', |
1369 } | 1365 } |
1370 self.assertEqual(expected, result_summary.key.get().to_dict()) | 1366 self.assertEqual(expected, result_summary.key.get().to_dict()) |
1371 self.assertEqual(1, self.execute_tasks()) | 1367 self.assertEqual(1, self.execute_tasks()) |
1372 self.assertEqual(1, len(pub_sub_calls)) # pubsub completion notification | 1368 self.assertEqual(1, len(pub_sub_calls)) # pubsub completion notification |
1373 | 1369 |
1374 def test_cron_abort_expired_task_to_run_retry(self): | 1370 def test_cron_abort_expired_task_to_run_retry(self): |
1375 pub_sub_calls = self.mock_pub_sub() | 1371 pub_sub_calls = self.mock_pub_sub() |
1376 now = utils.utcnow() | 1372 now = utils.utcnow() |
1377 request = _gen_request( | 1373 request = self._gen_request( |
1378 properties={ | 1374 properties={ |
1379 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, | 1375 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, |
1380 'idempotent': True, | 1376 'idempotent': True, |
1381 }, | 1377 }, |
1382 created_ts=now, | 1378 created_ts=now, |
1383 expiration_ts=now+datetime.timedelta(seconds=600), | 1379 expiration_ts=now+datetime.timedelta(seconds=600), |
1384 pubsub_topic='projects/abc/topics/def') | 1380 pubsub_topic='projects/abc/topics/def') |
1385 task_request.init_new_request(request, True, None) | 1381 task_request.init_new_request(request, True, None) |
1386 result_summary = task_scheduler.schedule_request(request, None) | 1382 result_summary = task_scheduler.schedule_request(request, None) |
1387 | 1383 |
1388 # Fake first try bot died. | 1384 # Fake first try bot died. |
1389 bot_dimensions = { | 1385 bot_dimensions = { |
1390 u'foo': [u'bar'], | 1386 u'foo': [u'bar'], |
1391 u'id': [u'localhost'], | 1387 u'id': [u'localhost'], |
1392 u'os': [u'Windows', u'Windows-3.1.1'], | 1388 u'os': [u'Windows', u'Windows-3.1.1'], |
1393 u'pool': [u'default'], | 1389 u'pool': [u'default'], |
1394 } | 1390 } |
1395 _register_bot(bot_dimensions) | 1391 self._register_bot(bot_dimensions) |
1396 _request, _, run_result = task_scheduler.bot_reap_task( | 1392 _request, _, run_result = task_scheduler.bot_reap_task( |
1397 bot_dimensions, 'abc', None) | 1393 bot_dimensions, 'abc', None) |
1398 self.assertEqual(1, self.execute_tasks()) | 1394 self.assertEqual(1, self.execute_tasks()) |
1399 self.assertEqual(1, len(pub_sub_calls)) # PENDING -> RUNNING | 1395 self.assertEqual(1, len(pub_sub_calls)) # PENDING -> RUNNING |
1400 now_1 = self.mock_now(self.now + task_result.BOT_PING_TOLERANCE, 1) | 1396 now_1 = self.mock_now(self.now + task_result.BOT_PING_TOLERANCE, 1) |
1401 self.assertEqual(([], 1, 0), task_scheduler.cron_handle_bot_died('f.local')) | 1397 self.assertEqual(([], 1, 0), task_scheduler.cron_handle_bot_died('f.local')) |
1402 self.assertEqual(task_result.State.BOT_DIED, run_result.key.get().state) | 1398 self.assertEqual(task_result.State.BOT_DIED, run_result.key.get().state) |
1403 self.assertEqual( | 1399 self.assertEqual( |
1404 task_result.State.PENDING, run_result.result_summary_key.get().state) | 1400 task_result.State.PENDING, run_result.result_summary_key.get().state) |
1405 self.assertEqual(1, self.execute_tasks()) | 1401 self.assertEqual(1, self.execute_tasks()) |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1449 self.assertEqual(expected, result_summary.key.get().to_dict()) | 1445 self.assertEqual(expected, result_summary.key.get().to_dict()) |
1450 | 1446 |
1451 self.assertEqual(1, self.execute_tasks()) | 1447 self.assertEqual(1, self.execute_tasks()) |
1452 self.assertEqual(3, len(pub_sub_calls)) # PENDING -> BOT_DIED | 1448 self.assertEqual(3, len(pub_sub_calls)) # PENDING -> BOT_DIED |
1453 | 1449 |
1454 def test_cron_handle_bot_died(self): | 1450 def test_cron_handle_bot_died(self): |
1455 pub_sub_calls = self.mock_pub_sub() | 1451 pub_sub_calls = self.mock_pub_sub() |
1456 | 1452 |
1457 # Test first retry, then success. | 1453 # Test first retry, then success. |
1458 now = utils.utcnow() | 1454 now = utils.utcnow() |
1459 request = _gen_request( | 1455 request = self._gen_request( |
1460 properties={ | 1456 properties={ |
1461 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, | 1457 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, |
1462 'idempotent': True, | 1458 'idempotent': True, |
1463 }, | 1459 }, |
1464 created_ts=now, | 1460 created_ts=now, |
1465 expiration_ts=now+datetime.timedelta(seconds=600), | 1461 expiration_ts=now+datetime.timedelta(seconds=600), |
1466 pubsub_topic='projects/abc/topics/def') | 1462 pubsub_topic='projects/abc/topics/def') |
1467 task_request.init_new_request(request, True, None) | 1463 task_request.init_new_request(request, True, None) |
1468 _result_summary = task_scheduler.schedule_request(request, None) | 1464 _result_summary = task_scheduler.schedule_request(request, None) |
1469 self.assertEqual(0, len(pub_sub_calls)) | 1465 self.assertEqual(0, len(pub_sub_calls)) |
1470 bot_dimensions = { | 1466 bot_dimensions = { |
1471 u'foo': [u'bar'], | 1467 u'foo': [u'bar'], |
1472 u'id': [u'localhost'], | 1468 u'id': [u'localhost'], |
1473 u'os': [u'Windows', u'Windows-3.1.1'], | 1469 u'os': [u'Windows', u'Windows-3.1.1'], |
1474 u'pool': [u'default'], | 1470 u'pool': [u'default'], |
1475 } | 1471 } |
1476 _register_bot(bot_dimensions) | 1472 self._register_bot(bot_dimensions) |
1477 request, _, run_result = task_scheduler.bot_reap_task( | 1473 request, _, run_result = task_scheduler.bot_reap_task( |
1478 bot_dimensions, 'abc', None) | 1474 bot_dimensions, 'abc', None) |
1479 self.assertEqual( | 1475 self.assertEqual( |
1480 task_result.State.RUNNING, run_result.result_summary_key.get().state) | 1476 task_result.State.RUNNING, run_result.result_summary_key.get().state) |
1481 self.assertEqual(1, self.execute_tasks()) | 1477 self.assertEqual(1, self.execute_tasks()) |
1482 self.assertEqual(1, len(pub_sub_calls)) # PENDING -> RUNNING | 1478 self.assertEqual(1, len(pub_sub_calls)) # PENDING -> RUNNING |
1483 self.assertEqual(1, run_result.try_number) | 1479 self.assertEqual(1, run_result.try_number) |
1484 self.assertEqual(task_result.State.RUNNING, run_result.state) | 1480 self.assertEqual(task_result.State.RUNNING, run_result.state) |
1485 now_1 = self.mock_now(self.now + task_result.BOT_PING_TOLERANCE, 1) | 1481 now_1 = self.mock_now(self.now + task_result.BOT_PING_TOLERANCE, 1) |
1486 self.assertEqual(([], 1, 0), task_scheduler.cron_handle_bot_died('f.local')) | 1482 self.assertEqual(([], 1, 0), task_scheduler.cron_handle_bot_died('f.local')) |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1544 ], | 1540 ], |
1545 'try_number': 1, | 1541 'try_number': 1, |
1546 'user': u'Jesus', | 1542 'user': u'Jesus', |
1547 } | 1543 } |
1548 self.assertEqual(expected, run_result.result_summary_key.get().to_dict()) | 1544 self.assertEqual(expected, run_result.result_summary_key.get().to_dict()) |
1549 | 1545 |
1550 # Task was retried. | 1546 # Task was retried. |
1551 now_2 = self.mock_now(self.now + task_result.BOT_PING_TOLERANCE, 2) | 1547 now_2 = self.mock_now(self.now + task_result.BOT_PING_TOLERANCE, 2) |
1552 bot_dimensions_second = bot_dimensions.copy() | 1548 bot_dimensions_second = bot_dimensions.copy() |
1553 bot_dimensions_second[u'id'] = [u'localhost-second'] | 1549 bot_dimensions_second[u'id'] = [u'localhost-second'] |
1554 _register_bot(bot_dimensions_second) | 1550 self._register_bot(bot_dimensions_second) |
1555 _request, _, run_result = task_scheduler.bot_reap_task( | 1551 _request, _, run_result = task_scheduler.bot_reap_task( |
1556 bot_dimensions_second, 'abc', None) | 1552 bot_dimensions_second, 'abc', None) |
1557 self.assertEqual(1, self.execute_tasks()) | 1553 self.assertEqual(1, self.execute_tasks()) |
1558 self.assertEqual(3, len(pub_sub_calls)) # PENDING -> RUNNING | 1554 self.assertEqual(3, len(pub_sub_calls)) # PENDING -> RUNNING |
1559 logging.info('%s', [t.to_dict() for t in task_to_run.TaskToRun.query()]) | 1555 logging.info('%s', [t.to_dict() for t in task_to_run.TaskToRun.query()]) |
1560 self.assertEqual(2, run_result.try_number) | 1556 self.assertEqual(2, run_result.try_number) |
1561 self.assertEqual( | 1557 self.assertEqual( |
1562 task_result.State.COMPLETED, | 1558 task_result.State.COMPLETED, |
1563 task_scheduler.bot_update_task( | 1559 task_scheduler.bot_update_task( |
1564 run_result_key=run_result.key, | 1560 run_result_key=run_result.key, |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1610 } | 1606 } |
1611 self.assertEqual(expected, run_result.result_summary_key.get().to_dict()) | 1607 self.assertEqual(expected, run_result.result_summary_key.get().to_dict()) |
1612 self.assertEqual(0.1, run_result.key.get().cost_usd) | 1608 self.assertEqual(0.1, run_result.key.get().cost_usd) |
1613 | 1609 |
1614 self.assertEqual(0, self.execute_tasks()) | 1610 self.assertEqual(0, self.execute_tasks()) |
1615 self.assertEqual(4, len(pub_sub_calls)) # RUNNING -> COMPLETED | 1611 self.assertEqual(4, len(pub_sub_calls)) # RUNNING -> COMPLETED |
1616 | 1612 |
1617 def test_cron_handle_bot_died_same_bot_denied(self): | 1613 def test_cron_handle_bot_died_same_bot_denied(self): |
1618 # Test first retry, then success. | 1614 # Test first retry, then success. |
1619 now = utils.utcnow() | 1615 now = utils.utcnow() |
1620 request = _gen_request( | 1616 request = self._gen_request( |
1621 properties={ | 1617 properties={ |
1622 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, | 1618 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, |
1623 'idempotent': True, | 1619 'idempotent': True, |
1624 }, | 1620 }, |
1625 created_ts=now, | 1621 created_ts=now, |
1626 expiration_ts=now+datetime.timedelta(seconds=600)) | 1622 expiration_ts=now+datetime.timedelta(seconds=600)) |
1627 task_request.init_new_request(request, True, None) | 1623 task_request.init_new_request(request, True, None) |
1628 _result_summary = task_scheduler.schedule_request(request, None) | 1624 _result_summary = task_scheduler.schedule_request(request, None) |
1629 bot_dimensions = { | 1625 bot_dimensions = { |
1630 u'foo': [u'bar'], | 1626 u'foo': [u'bar'], |
1631 u'id': [u'localhost'], | 1627 u'id': [u'localhost'], |
1632 u'os': [u'Windows', u'Windows-3.1.1'], | 1628 u'os': [u'Windows', u'Windows-3.1.1'], |
1633 u'pool': [u'default'], | 1629 u'pool': [u'default'], |
1634 } | 1630 } |
1635 _register_bot(bot_dimensions) | 1631 self._register_bot(bot_dimensions) |
1636 _request, _, run_result = task_scheduler.bot_reap_task( | 1632 _request, _, run_result = task_scheduler.bot_reap_task( |
1637 bot_dimensions, 'abc', None) | 1633 bot_dimensions, 'abc', None) |
1638 self.assertEqual(1, run_result.try_number) | 1634 self.assertEqual(1, run_result.try_number) |
1639 self.assertEqual(task_result.State.RUNNING, run_result.state) | 1635 self.assertEqual(task_result.State.RUNNING, run_result.state) |
1640 now_1 = self.mock_now(self.now + task_result.BOT_PING_TOLERANCE, 1) | 1636 now_1 = self.mock_now(self.now + task_result.BOT_PING_TOLERANCE, 1) |
1641 self.assertEqual(([], 1, 0), task_scheduler.cron_handle_bot_died('f.local')) | 1637 self.assertEqual(([], 1, 0), task_scheduler.cron_handle_bot_died('f.local')) |
1642 | 1638 |
1643 # Refresh and compare: | 1639 # Refresh and compare: |
1644 expected = { | 1640 expected = { |
1645 'abandoned_ts': now_1, | 1641 'abandoned_ts': now_1, |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1704 now_2 = self.mock_now(self.now + task_result.BOT_PING_TOLERANCE, 2) | 1700 now_2 = self.mock_now(self.now + task_result.BOT_PING_TOLERANCE, 2) |
1705 request, _, run_result = task_scheduler.bot_reap_task( | 1701 request, _, run_result = task_scheduler.bot_reap_task( |
1706 bot_dimensions, 'abc', None) | 1702 bot_dimensions, 'abc', None) |
1707 self.assertEqual(None, request) | 1703 self.assertEqual(None, request) |
1708 self.assertEqual(None, run_result) | 1704 self.assertEqual(None, run_result) |
1709 logging.info('%s', [t.to_dict() for t in task_to_run.TaskToRun.query()]) | 1705 logging.info('%s', [t.to_dict() for t in task_to_run.TaskToRun.query()]) |
1710 | 1706 |
1711 def test_cron_handle_bot_died_second(self): | 1707 def test_cron_handle_bot_died_second(self): |
1712 # Test two tries internal_failure's leading to a BOT_DIED status. | 1708 # Test two tries internal_failure's leading to a BOT_DIED status. |
1713 now = utils.utcnow() | 1709 now = utils.utcnow() |
1714 request = _gen_request( | 1710 request = self._gen_request( |
1715 properties={ | 1711 properties={ |
1716 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, | 1712 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, |
1717 'idempotent': True, | 1713 'idempotent': True, |
1718 }, | 1714 }, |
1719 created_ts=now, | 1715 created_ts=now, |
1720 expiration_ts=now+datetime.timedelta(seconds=600)) | 1716 expiration_ts=now+datetime.timedelta(seconds=600)) |
1721 task_request.init_new_request(request, True, None) | 1717 task_request.init_new_request(request, True, None) |
1722 _result_summary = task_scheduler.schedule_request(request, None) | 1718 _result_summary = task_scheduler.schedule_request(request, None) |
1723 bot_dimensions = { | 1719 bot_dimensions = { |
1724 u'foo': [u'bar'], | 1720 u'foo': [u'bar'], |
1725 u'id': [u'localhost'], | 1721 u'id': [u'localhost'], |
1726 u'os': [u'Windows', u'Windows-3.1.1'], | 1722 u'os': [u'Windows', u'Windows-3.1.1'], |
1727 u'pool': [u'default'], | 1723 u'pool': [u'default'], |
1728 } | 1724 } |
1729 _register_bot(bot_dimensions) | 1725 self._register_bot(bot_dimensions) |
1730 _request, _, run_result = task_scheduler.bot_reap_task( | 1726 _request, _, run_result = task_scheduler.bot_reap_task( |
1731 bot_dimensions, 'abc', None) | 1727 bot_dimensions, 'abc', None) |
1732 self.assertEqual(1, run_result.try_number) | 1728 self.assertEqual(1, run_result.try_number) |
1733 self.assertEqual(task_result.State.RUNNING, run_result.state) | 1729 self.assertEqual(task_result.State.RUNNING, run_result.state) |
1734 self.mock_now(self.now + task_result.BOT_PING_TOLERANCE, 1) | 1730 self.mock_now(self.now + task_result.BOT_PING_TOLERANCE, 1) |
1735 self.assertEqual(([], 1, 0), task_scheduler.cron_handle_bot_died('f.local')) | 1731 self.assertEqual(([], 1, 0), task_scheduler.cron_handle_bot_died('f.local')) |
1736 now_1 = self.mock_now(self.now + task_result.BOT_PING_TOLERANCE, 2) | 1732 now_1 = self.mock_now(self.now + task_result.BOT_PING_TOLERANCE, 2) |
1737 # It must be a different bot. | 1733 # It must be a different bot. |
1738 bot_dimensions_second = bot_dimensions.copy() | 1734 bot_dimensions_second = bot_dimensions.copy() |
1739 bot_dimensions_second[u'id'] = [u'localhost-second'] | 1735 bot_dimensions_second[u'id'] = [u'localhost-second'] |
1740 _register_bot(bot_dimensions_second) | 1736 self._register_bot(bot_dimensions_second) |
| 1737 # No task to run because the task dimensions were already seen. |
1741 _request, _, run_result = task_scheduler.bot_reap_task( | 1738 _request, _, run_result = task_scheduler.bot_reap_task( |
1742 bot_dimensions_second, 'abc', None) | 1739 bot_dimensions_second, 'abc', None) |
1743 now_2 = self.mock_now(self.now + 2 * task_result.BOT_PING_TOLERANCE, 3) | 1740 now_2 = self.mock_now(self.now + 2 * task_result.BOT_PING_TOLERANCE, 3) |
1744 self.assertEqual( | 1741 self.assertEqual( |
1745 (['1d69b9f088008912'], 0, 0), | 1742 (['1d69b9f088008912'], 0, 0), |
1746 task_scheduler.cron_handle_bot_died('f.local')) | 1743 task_scheduler.cron_handle_bot_died('f.local')) |
1747 self.assertEqual(([], 0, 0), task_scheduler.cron_handle_bot_died('f.local')) | 1744 self.assertEqual(([], 0, 0), task_scheduler.cron_handle_bot_died('f.local')) |
1748 expected = { | 1745 expected = { |
1749 'abandoned_ts': now_2, | 1746 'abandoned_ts': now_2, |
1750 'bot_dimensions': bot_dimensions_second, | 1747 'bot_dimensions': bot_dimensions_second, |
(...skipping 26 matching lines...) Expand all Loading... |
1777 u'tag:1', | 1774 u'tag:1', |
1778 u'user:Jesus', | 1775 u'user:Jesus', |
1779 ], | 1776 ], |
1780 'try_number': 2, | 1777 'try_number': 2, |
1781 'user': u'Jesus', | 1778 'user': u'Jesus', |
1782 } | 1779 } |
1783 self.assertEqual(expected, run_result.result_summary_key.get().to_dict()) | 1780 self.assertEqual(expected, run_result.result_summary_key.get().to_dict()) |
1784 | 1781 |
1785 def test_cron_handle_bot_died_ignored_expired(self): | 1782 def test_cron_handle_bot_died_ignored_expired(self): |
1786 now = utils.utcnow() | 1783 now = utils.utcnow() |
1787 request = _gen_request( | 1784 request = self._gen_request( |
1788 properties={ | 1785 properties={ |
1789 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, | 1786 'dimensions': {u'os': u'Windows-3.1.1', u'pool': u'default'}, |
1790 }, | 1787 }, |
1791 created_ts=now, | 1788 created_ts=now, |
1792 expiration_ts=now+datetime.timedelta(seconds=600)) | 1789 expiration_ts=now+datetime.timedelta(seconds=600)) |
1793 task_request.init_new_request(request, True, None) | 1790 task_request.init_new_request(request, True, None) |
1794 _result_summary = task_scheduler.schedule_request(request, None) | 1791 _result_summary = task_scheduler.schedule_request(request, None) |
1795 bot_dimensions = { | 1792 bot_dimensions = { |
1796 u'foo': [u'bar'], | 1793 u'foo': [u'bar'], |
1797 u'id': [u'localhost'], | 1794 u'id': [u'localhost'], |
1798 u'os': [u'Windows', u'Windows-3.1.1'], | 1795 u'os': [u'Windows', u'Windows-3.1.1'], |
1799 u'pool': [u'default'], | 1796 u'pool': [u'default'], |
1800 } | 1797 } |
1801 _register_bot(bot_dimensions) | 1798 self._register_bot(bot_dimensions) |
1802 _request, _, run_result = task_scheduler.bot_reap_task( | 1799 _request, _, run_result = task_scheduler.bot_reap_task( |
1803 bot_dimensions, 'abc', None) | 1800 bot_dimensions, 'abc', None) |
1804 self.assertEqual(1, run_result.try_number) | 1801 self.assertEqual(1, run_result.try_number) |
1805 self.assertEqual(task_result.State.RUNNING, run_result.state) | 1802 self.assertEqual(task_result.State.RUNNING, run_result.state) |
1806 self.mock_now(self.now + task_result.BOT_PING_TOLERANCE, 601) | 1803 self.mock_now(self.now + task_result.BOT_PING_TOLERANCE, 601) |
1807 self.assertEqual( | 1804 self.assertEqual( |
1808 (['1d69b9f088008911'], 0, 0), | 1805 (['1d69b9f088008911'], 0, 0), |
1809 task_scheduler.cron_handle_bot_died('f.local')) | 1806 task_scheduler.cron_handle_bot_died('f.local')) |
1810 | 1807 |
1811 | 1808 |
1812 if __name__ == '__main__': | 1809 if __name__ == '__main__': |
1813 if '-v' in sys.argv: | 1810 if '-v' in sys.argv: |
1814 unittest.TestCase.maxDiff = None | 1811 unittest.TestCase.maxDiff = None |
1815 logging.basicConfig( | 1812 logging.basicConfig( |
1816 level=logging.DEBUG if '-v' in sys.argv else logging.CRITICAL) | 1813 level=logging.DEBUG if '-v' in sys.argv else logging.CRITICAL) |
1817 unittest.main() | 1814 unittest.main() |
OLD | NEW |