Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(256)

Side by Side Diff: appengine/cr-buildbucket/swarming/test/swarming_test.py

Issue 2213723002: swarmbucket: allow overriding config (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@sb-cfg-refactoring
Patch Set: security warning Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright 2015 The Chromium Authors. All rights reserved. 1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 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 import base64 5 import base64
6 import contextlib 6 import contextlib
7 import datetime 7 import datetime
8 import json 8 import json
9 import logging
9 10
10 from components import auth 11 from components import auth
11 from components import config as config_component 12 from components import config as config_component
12 from components import net 13 from components import net
13 from components import utils 14 from components import utils
14 from google.appengine.ext import ndb 15 from google.appengine.ext import ndb
15 from testing_utils import testing 16 from testing_utils import testing
16 from webob import exc 17 from webob import exc
17 import mock 18 import mock
18 import webapp2 19 import webapp2
19 20
20 from swarming import swarming 21 from swarming import swarming
21 from proto import project_config_pb2 22 from proto import project_config_pb2
22 import config 23 import config
23 import errors 24 import errors
24 import model 25 import model
25 26
26 27
27 def futuristic(result): 28 def futuristic(result):
28 f = ndb.Future() 29 f = ndb.Future()
29 f.set_result(result) 30 f.set_result(result)
30 return f 31 return f
31 32
32 33
33 class SwarmingTest(testing.AppengineTestCase): 34 class SwarmingTest(testing.AppengineTestCase):
34 def setUp(self): 35 def setUp(self):
35 super(SwarmingTest, self).setUp() 36 super(SwarmingTest, self).setUp()
36 self.mock(utils, 'utcnow', lambda: datetime.datetime(2015, 11, 30)) 37 self.mock(utils, 'utcnow', lambda: datetime.datetime(2015, 11, 30))
38
39 self.json_response = None
40 def json_request_async(*_, **__):
41 if self.json_response is not None:
42 return futuristic(self.json_response)
43 self.fail('unexpected outbound request') # pragma: no cover
44
45 self.mock(
46 net, 'json_request_async', mock.Mock(side_effect=json_request_async))
47
37 self.bucket_cfg = project_config_pb2.Bucket( 48 self.bucket_cfg = project_config_pb2.Bucket(
38 name='bucket', 49 name='bucket',
39 swarming=project_config_pb2.Swarming( 50 swarming=project_config_pb2.Swarming(
40 hostname='chromium-swarm.appspot.com', 51 hostname='chromium-swarm.appspot.com',
41 url_format='https://example.com/{swarming_hostname}/{task_id}', 52 url_format='https://example.com/{swarming_hostname}/{task_id}',
42 common_swarming_tags=['commontag:yes'], 53 common_swarming_tags=['commontag:yes'],
43 common_dimensions=['cores:8', 'pool:default', 'cpu:x86-64'], 54 common_dimensions=['cores:8', 'pool:default', 'cpu:x86-64'],
44 common_recipe=project_config_pb2.Swarming.Recipe( 55 common_recipe=project_config_pb2.Swarming.Recipe(
45 repository='https://example.com/repo', 56 repository='https://example.com/repo',
46 name='recipe', 57 name='recipe',
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
140 {'changes': [{'author': {'email': 0}}]}, 151 {'changes': [{'author': {'email': 0}}]},
141 {'changes': [{'author': {'email': ''}}]}, 152 {'changes': [{'author': {'email': ''}}]},
142 {'changes': [{'author': {'email': 'a@example.com'}, 'repo_url': 0}]}, 153 {'changes': [{'author': {'email': 'a@example.com'}, 'repo_url': 0}]},
143 {'bad key': 1}, 154 {'bad key': 1},
144 {'swarming': []}, 155 {'swarming': []},
145 {'swarming': {'junk': 1}}, 156 {'swarming': {'junk': 1}},
146 {'swarming': {'recipe': []}}, 157 {'swarming': {'recipe': []}},
147 {'swarming': {'recipe': {'junk': 1}}}, 158 {'swarming': {'recipe': {'junk': 1}}},
148 {'swarming': {'recipe': {'revision': 1}}}, 159 {'swarming': {'recipe': {'revision': 1}}},
149 {'swarming': {'canary_template': 'yes'}}, 160 {'swarming': {'canary_template': 'yes'}},
161 {'swarming': {'override_cfg': 0}},
162 {'swarming': {'override_cfg': {'swarming': 0}}},
163 {
164 'swarming': {
165 'override_cfg': {
166 'swarming': {'hostname': 0},
167 },
168 },
169 },
170 {
171 'swarming': {
172 'override_cfg': {
173 'swarming': {'builders': [{}]},
174 },
175 },
176 },
177 {
178 'swarming': {
179 'override_cfg': {
180 'swarming': {'common_dimensions': ['wrong_dimension']},
181 },
182 },
183 },
184 {'swarming': {'override_cfg': {'x': 0}}},
150 ] 185 ]
151 for p in bad: 186 for p in bad:
187 logging.info('testing %s', p)
152 p['builder_name'] = 'foo' 188 p['builder_name'] = 'foo'
153 with self.assertRaises(errors.InvalidInputError): 189 with self.assertRaises(errors.InvalidInputError):
154 swarming.validate_build_parameters(p['builder_name'], p) 190 swarming.validate_build_parameters(p['builder_name'], p)
155 191
156 def test_execution_timeout(self): 192 def test_execution_timeout(self):
157 self.bucket_cfg.swarming.common_execution_timeout_secs = 120 193 self.bucket_cfg.swarming.common_execution_timeout_secs = 120
158 builder_cfg = project_config_pb2.Swarming.Builder(name='fast-builder') 194 builder_cfg = project_config_pb2.Swarming.Builder(name='fast-builder')
159 195
160 build = model.Build( 196 build = model.Build(
161 bucket='bucket', 197 bucket='bucket',
(...skipping 27 matching lines...) Expand all
189 'properties': { 225 'properties': {
190 'a': 'b', 226 'a': 'b',
191 }, 227 },
192 'changes': [{ 228 'changes': [{
193 'author': {'email': 'bob@example.com'}, 229 'author': {'email': 'bob@example.com'},
194 'repo_url': 'https://chromium.googlesource.com/chromium/src', 230 'repo_url': 'https://chromium.googlesource.com/chromium/src',
195 }] 231 }]
196 }, 232 },
197 ) 233 )
198 234
199 self.mock(net, 'json_request_async', mock.Mock(return_value=futuristic({ 235 self.json_response = {
200 'task_id': 'deadbeef', 236 'task_id': 'deadbeef',
201 'request': { 237 'request': {
202 'properties': { 238 'properties': {
203 'dimensions': [ 239 'dimensions': [
204 {'key': 'cores', 'value': '8'}, 240 {'key': 'cores', 'value': '8'},
205 {'key': 'os', 'value': 'Linux'}, 241 {'key': 'os', 'value': 'Linux'},
206 {'key': 'pool', 'value': 'Chrome'}, 242 {'key': 'pool', 'value': 'Chrome'},
207 ], 243 ],
208 }, 244 },
209 'tags': [ 245 'tags': [
210 'builder:builder', 246 'builder:builder',
211 'buildertag:yes', 247 'buildertag:yes',
212 'commontag:yes', 248 'commontag:yes',
213 'master:master.bucket', 249 'master:master.bucket',
214 'priority:108', 250 'priority:108',
215 'recipe_name:recipe', 251 'recipe_name:recipe',
216 'recipe_repository:https://example.com/repo', 252 'recipe_repository:https://example.com/repo',
217 'recipe_revision:badcoffee', 253 'recipe_revision:badcoffee',
218 ] 254 ]
219 } 255 }
220 }))) 256 }
221 257
222 swarming.create_task_async(build).get_result() 258 swarming.create_task_async(build).get_result()
223 259
224 # Test swarming request. 260 # Test swarming request.
225 self.assertEqual( 261 self.assertEqual(
226 net.json_request_async.call_args[0][0], 262 net.json_request_async.call_args[0][0],
227 'https://chromium-swarm.appspot.com/_ah/api/swarming/v1/tasks/new') 263 'https://chromium-swarm.appspot.com/_ah/api/swarming/v1/tasks/new')
228 actual_task_def = net.json_request_async.call_args[1]['payload'] 264 actual_task_def = net.json_request_async.call_args[1]['payload']
229 del actual_task_def['pubsub_auth_token'] 265 del actual_task_def['pubsub_auth_token']
230 expected_task_def = { 266 expected_task_def = {
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
301 def test_create_task_async_canary_template(self): 337 def test_create_task_async_canary_template(self):
302 build = model.Build( 338 build = model.Build(
303 bucket='bucket', 339 bucket='bucket',
304 parameters={ 340 parameters={
305 'builder_name': 'builder', 341 'builder_name': 'builder',
306 'swarming': { 342 'swarming': {
307 'canary_template': True, 343 'canary_template': True,
308 } }, 344 } },
309 ) 345 )
310 346
311 self.mock(net, 'json_request_async', mock.Mock(return_value=futuristic({ 347 self.json_response = {
312 'task_id': 'deadbeef', 348 'task_id': 'deadbeef',
313 'request': { 349 'request': {
314 'properties': { 350 'properties': {
315 'dimensions': [ 351 'dimensions': [
316 {'key': 'cores', 'value': '8'}, 352 {'key': 'cores', 'value': '8'},
317 {'key': 'os', 'value': 'Linux'}, 353 {'key': 'os', 'value': 'Linux'},
318 {'key': 'pool', 'value': 'Chrome'}, 354 {'key': 'pool', 'value': 'Chrome'},
319 ], 355 ],
320 }, 356 },
321 'tags': [ 357 'tags': [
322 'builder:builder', 358 'builder:builder',
323 'buildertag:yes', 359 'buildertag:yes',
324 'commontag:yes', 360 'commontag:yes',
325 'master:master.bucket', 361 'master:master.bucket',
326 'priority:108', 362 'priority:108',
327 'recipe_name:recipe', 363 'recipe_name:recipe',
328 'recipe_repository:https://example.com/repo', 364 'recipe_repository:https://example.com/repo',
329 ] 365 ]
330 } 366 }
331 }))) 367 }
332 368
333 swarming.create_task_async(build).get_result() 369 swarming.create_task_async(build).get_result()
334 370
335 # Test swarming request. 371 # Test swarming request.
336 self.assertEqual( 372 self.assertEqual(
337 net.json_request_async.call_args[0][0], 373 net.json_request_async.call_args[0][0],
338 'https://chromium-swarm.appspot.com/_ah/api/swarming/v1/tasks/new') 374 'https://chromium-swarm.appspot.com/_ah/api/swarming/v1/tasks/new')
339 actual_task_def = net.json_request_async.call_args[1]['payload'] 375 actual_task_def = net.json_request_async.call_args[1]['payload']
340 del actual_task_def['pubsub_auth_token'] 376 del actual_task_def['pubsub_auth_token']
341 expected_task_def = { 377 expected_task_def = {
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
418 454
419 self.task_template_canary = None 455 self.task_template_canary = None
420 with self.assertRaises(errors.InvalidInputError): 456 with self.assertRaises(errors.InvalidInputError):
421 swarming.create_task_async(build).get_result() 457 swarming.create_task_async(build).get_result()
422 458
423 def test_create_task_async_no_canary_template_implicit(self): 459 def test_create_task_async_no_canary_template_implicit(self):
424 self.mock(swarming, 'should_use_canary_template', mock.Mock()) 460 self.mock(swarming, 'should_use_canary_template', mock.Mock())
425 swarming.should_use_canary_template.return_value = True 461 swarming.should_use_canary_template.return_value = True
426 self.task_template_canary = None 462 self.task_template_canary = None
427 463
428 self.mock(net, 'json_request_async', mock.Mock(return_value=futuristic({ 464 self.json_response = {
429 'task_id': 'deadbeef', 465 'task_id': 'deadbeef',
430 'request': { 466 'request': {
431 'properties': { 467 'properties': {
432 'dimensions': [ 468 'dimensions': [
433 {'key': 'cores', 'value': '8'}, 469 {'key': 'cores', 'value': '8'},
434 {'key': 'os', 'value': 'Linux'}, 470 {'key': 'os', 'value': 'Linux'},
435 {'key': 'pool', 'value': 'Chrome'}, 471 {'key': 'pool', 'value': 'Chrome'},
436 ], 472 ],
437 }, 473 },
438 'tags': [ 474 'tags': [
439 'builder:builder', 475 'builder:builder',
440 'buildertag:yes', 476 'buildertag:yes',
441 'commontag:yes', 477 'commontag:yes',
442 'master:master.bucket', 478 'master:master.bucket',
443 'priority:108', 479 'priority:108',
444 'recipe_name:recipe', 480 'recipe_name:recipe',
445 'recipe_repository:https://example.com/repo', 481 'recipe_repository:https://example.com/repo',
446 'recipe_revision:badcoffee', 482 'recipe_revision:badcoffee',
447 ] 483 ]
448 } 484 }
449 }))) 485 }
450 486
451 build = model.Build( 487 build = model.Build(
452 bucket='bucket', 488 bucket='bucket',
453 parameters={'builder_name': 'builder'}, 489 parameters={'builder_name': 'builder'},
454 ) 490 )
455 swarming.create_task_async(build).get_result() 491 swarming.create_task_async(build).get_result()
456 492
457 actual_task_def = net.json_request_async.call_args[1]['payload'] 493 actual_task_def = net.json_request_async.call_args[1]['payload']
458 self.assertIn('buildbucket_template_canary:false', actual_task_def['tags']) 494 self.assertIn('buildbucket_template_canary:false', actual_task_def['tags'])
459 495
496 def test_create_task_async_override_cfg(self):
497 build = model.Build(
498 bucket='bucket',
499 parameters={
500 'builder_name': 'builder',
501 'swarming': {
502 'override_builder_cfg': {
503 # Override cores dimension.
504 'dimensions': ['cores:16'],
505 },
506 }
507 },
508 )
509
510 self.json_response = {
511 'task_id': 'deadbeef',
512 'request': {
513 'properties': {
514 'dimensions': [
515 {'key': 'cores', 'value': '16'},
516 {'key': 'os', 'value': 'Linux'},
517 {'key': 'pool', 'value': 'Chrome'},
518 ],
519 },
520 'tags': [
521 'builder:builder',
522 'buildertag:yes',
523 'commontag:yes',
524 'master:master.bucket',
525 'priority:108',
526 'recipe_name:recipe',
527 'recipe_repository:https://example.com/repo',
528 'recipe_revision:badcoffee',
529 ]
530 }
531 }
532
533 swarming.create_task_async(build).get_result()
534
535 actual_task_def = net.json_request_async.call_args[1]['payload']
536 self.assertIn(
537 {'key': 'cores', 'value': '16'},
538 actual_task_def['properties']['dimensions'])
539
540 def test_create_task_async_override_cfg_malformed(self):
541 build = model.Build(
542 bucket='bucket',
543 parameters={
544 'builder_name': 'builder',
545 'swarming': {
546 'override_builder_cfg': [],
547 }
548 },
549 )
550 with self.assertRaises(errors.InvalidInputError):
551 swarming.create_task_async(build).get_result()
552
553 build = model.Build(
554 bucket='bucket',
555 parameters={
556 'builder_name': 'builder',
557 'swarming': {
558 'override_builder_cfg': {
559 'name': 'x',
560 },
561 }
562 },
563 )
564 with self.assertRaises(errors.InvalidInputError):
565 swarming.create_task_async(build).get_result()
566
567 build = model.Build(
568 bucket='bucket',
569 parameters={
570 'builder_name': 'builder',
571 'swarming': {
572 'override_builder_cfg': {
573 'blabla': 'x',
574 },
575 }
576 },
577 )
578 with self.assertRaises(errors.InvalidInputError):
579 swarming.create_task_async(build).get_result()
580
581 # Remove a required dimension.
582 build = model.Build(
583 bucket='bucket',
584 parameters={
585 'builder_name': 'builder',
586 'swarming': {
587 'override_builder_cfg': {
588 'dimensions': ['pool:'],
589 },
590 }
591 },
592 )
593 with self.assertRaises(errors.InvalidInputError):
594 swarming.create_task_async(build).get_result()
595
596
460 def test_create_task_async_on_leased_build(self): 597 def test_create_task_async_on_leased_build(self):
461 build = model.Build( 598 build = model.Build(
462 bucket='bucket', 599 bucket='bucket',
463 parameters={'builder_name': 'builder'}, 600 parameters={'builder_name': 'builder'},
464 lease_key=12345, 601 lease_key=12345,
465 ) 602 )
466 with self.assertRaises(errors.InvalidInputError): 603 with self.assertRaises(errors.InvalidInputError):
467 swarming.create_task_async(build).get_result() 604 swarming.create_task_async(build).get_result()
468 605
469 def test_cancel_task(self): 606 def test_cancel_task(self):
470 self.mock(net, 'json_request_async', mock.Mock(return_value=futuristic({}))) 607 self.json_response = {}
471 build = model.Build( 608 build = model.Build(
472 bucket='whatever', 609 bucket='whatever',
473 swarming_hostname='chromium-swarm.appspot.com', 610 swarming_hostname='chromium-swarm.appspot.com',
474 swarming_task_id='deadbeef', 611 swarming_task_id='deadbeef',
475 ) 612 )
476 swarming.cancel_task_async(build).get_result() 613 swarming.cancel_task_async(build).get_result()
477 net.json_request_async.assert_called_with( 614 net.json_request_async.assert_called_with(
478 ('https://chromium-swarm.appspot.com/' 615 ('https://chromium-swarm.appspot.com/'
479 '_ah/api/swarming/v1/task/deadbeef/cancel'), 616 '_ah/api/swarming/v1/task/deadbeef/cancel'),
480 method='POST', 617 method='POST',
(...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after
784 self.assertEqual(build.status, model.BuildStatus.COMPLETED) 921 self.assertEqual(build.status, model.BuildStatus.COMPLETED)
785 self.assertEqual(build.result, model.BuildResult.FAILURE) 922 self.assertEqual(build.result, model.BuildResult.FAILURE)
786 self.assertEqual(build.failure_reason, model.FailureReason.INFRA_FAILURE) 923 self.assertEqual(build.failure_reason, model.FailureReason.INFRA_FAILURE)
787 self.assertIsNotNone(build.result_details) 924 self.assertIsNotNone(build.result_details)
788 self.assertIsNone(build.lease_key) 925 self.assertIsNone(build.lease_key)
789 self.assertIsNotNone(build.complete_time) 926 self.assertIsNotNone(build.complete_time)
790 927
791 928
792 def b64json(data): 929 def b64json(data):
793 return base64.b64encode(json.dumps(data)) 930 return base64.b64encode(json.dumps(data))
OLDNEW
« no previous file with comments | « appengine/cr-buildbucket/swarming/swarmingcfg.py ('k') | appengine/cr-buildbucket/swarming/test/swarmingcfg_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698