Index: appengine/swarming/handlers_endpoints_test.py |
diff --git a/appengine/swarming/handlers_endpoints_test.py b/appengine/swarming/handlers_endpoints_test.py |
index 5c913d139d6a6caaa96b8315a18df8ed9214b144..dbfee2445f4137672e68355aaf20cca0dd3483ec 100755 |
--- a/appengine/swarming/handlers_endpoints_test.py |
+++ b/appengine/swarming/handlers_endpoints_test.py |
@@ -63,6 +63,7 @@ class BaseTest(test_env_handlers.AppTestBase, test_case.EndpointsTestCase): |
lambda *args, **kwargs: self.fail('%s, %s' % (args, kwargs))) |
# Client API test cases run by default as user. |
self.set_as_user() |
+ self.pubsub_tasks = [] |
self.mock(utils, 'enqueue_task', self._enqueue_task) |
@ndb.non_transactional |
@@ -72,6 +73,9 @@ class BaseTest(test_env_handlers.AppTestBase, test_case.EndpointsTestCase): |
handlers_backend.TaskDimensionsHandler.tidy_stale(kwargs['payload']) |
return True |
if queue_name == 'pubsub': |
+ task = {'url': url, 'queue_name': queue_name} |
+ task.update(kwargs) |
+ self.pubsub_tasks.append(task) |
return True |
self.fail(url) |
@@ -625,6 +629,107 @@ class TasksApiTest(BaseTest): |
expected, |
self.call_api('requests', body=message_to_dict(request)).json) |
+ def test_new_ok_deduped_by_transaction_id(self): |
+ """Asserts that new returns task result for deduped.""" |
+ # Run a task to completion. |
+ self.mock(random, 'getrandbits', lambda _: 0x88) |
+ now = self.mock_now(datetime.datetime(2010, 1, 2, 3, 4, 5)) |
+ str_now = unicode(now.strftime(self.DATETIME_NO_MICRO)) |
+ new_req, _, task_id = self.client_create_task_raw( |
+ name='task', tags=['project:yay', 'commit:post'], |
+ properties=dict(idempotent=True), |
+ transaction_id='txn') |
+ self.set_as_bot() |
+ self.bot_run_task() |
+ |
+ self.mock(random, 'getrandbits', lambda _: 0x66) |
+ self.set_as_user() |
+ |
+ expected = { |
+ u'request': { |
+ u'authenticated': u'user:user@example.com', |
+ u'created_ts': str_now, |
+ u'expiration_secs': u'86400', |
+ u'name': u'task', |
+ u'priority': u'100', |
+ u'properties': { |
+ u'cipd_input': { |
+ u'client_package': { |
+ u'package_name': u'infra/tools/cipd/${platform}', |
+ u'version': u'git_revision:deadbeef', |
+ }, |
+ u'packages': [{ |
+ u'package_name': u'rm', |
+ u'path': u'bin', |
+ u'version': u'git_revision:deadbeef', |
+ }], |
+ u'server': u'https://chrome-infra-packages.appspot.com', |
+ }, |
+ u'command': [u'python', u'run_test.py'], |
+ u'dimensions': [ |
+ {u'key': u'os', u'value': u'Amiga'}, |
+ {u'key': u'pool', u'value': u'default'}, |
+ ], |
+ u'execution_timeout_secs': u'3600', |
+ u'grace_period_secs': u'30', |
+ u'idempotent': True, |
+ u'io_timeout_secs': u'1200', |
+ u'outputs': [u'foo', u'path/to/foobar'], |
+ }, |
+ u'service_account': u'none', |
+ u'tags': [ |
+ u'commit:post', |
+ u'os:Amiga', |
+ u'pool:default', |
+ u'priority:100', |
+ u'project:yay', |
+ u'service_account:none', |
+ u'user:joe@localhost', |
+ ], |
+ u'user': u'joe@localhost', |
+ }, |
+ u'task_id': unicode(task_id), |
+ u'task_result': { |
+ u'bot_dimensions': [ |
+ {u'key': u'id', u'value': [u'bot1']}, |
+ {u'key': u'os', u'value': [u'Amiga']}, |
+ {u'key': u'pool', u'value': [u'default']}, |
+ ], |
+ u'bot_id': u'bot1', |
+ u'bot_version': self.bot_version, |
+ u'completed_ts': str_now, |
+ u'costs_usd': [0.1], |
+ u'created_ts': str_now, |
+ u'duration': 0.1, |
+ u'exit_code': u'0', |
+ u'failure': False, |
+ u'internal_failure': False, |
+ u'modified_ts': str_now, |
+ u'name': u'task', |
+ u'properties_hash': ( |
+ u'd35fe05074cbd9a2356c77c9983c71476f3a5f9415c5c5b1f3e2cdf7826b7261' |
+ ), |
+ u'run_id': u'5cee488008811', |
+ u'server_versions': [u'v1a'], |
+ u'started_ts': str_now, |
+ u'state': u'COMPLETED', |
+ u'tags': [ |
+ u'commit:post', |
+ u'os:Amiga', |
+ u'pool:default', |
+ u'priority:100', |
+ u'project:yay', |
+ u'service_account:none', |
+ u'user:joe@localhost', |
+ ], |
+ u'task_id': unicode(task_id), |
+ u'try_number': u'1', |
+ u'user': u'joe@localhost', |
+ }, |
+ } |
+ response = self.call_api('new', body=message_to_dict(new_req)) |
+ self.assertEqual(expected, response.json) |
+ |
def test_new_ok_isolated(self): |
"""Asserts that new generates appropriate metadata.""" |
self.mock(random, 'getrandbits', lambda _: 0x88) |
@@ -850,12 +955,6 @@ class TasksApiTest(BaseTest): |
self.assertEqual(expected, response.json) |
def test_mass_cancel(self): |
- notifies = [] |
- def enqueue_task_mock(**kwargs): |
- notifies.append(kwargs) |
- return True |
- self.mock(utils, 'enqueue_task', enqueue_task_mock) |
- |
# Create two tasks. |
self.mock(random, 'getrandbits', lambda _: 0x88) |
first, second, _, _, _, now_120 = self._gen_three_pending_tasks() |
@@ -880,7 +979,57 @@ class TasksApiTest(BaseTest): |
response = self.call_api('cancel', body={u'tags': [u'os:Win']}) |
self.assertEqual(expected, response.json) |
+ def test_cancel_by_transaction_id_ok(self): |
+ """Asserts that task cancellation goes smoothly.""" |
+ # Create and cancel a task as a non-privileged user. |
+ self.mock(random, 'getrandbits', lambda _: 0x88) |
+ now = datetime.datetime(2010, 1, 2, 3, 4, 5) |
+ self.mock_now(now) |
+ _, _, task_id = self.client_create_task_raw( |
+ pubsub_topic='projects/abc/topics/def', |
+ pubsub_userdata='blah', |
+ transaction_id='txn') |
+ expected = { |
+ u'task_id': task_id, |
+ u'ok': True, |
+ u'was_running': False |
+ } |
+ response = self.call_api( |
+ 'cancel_by_transaction_id', body={'transaction_id': 'txn'}) |
+ self.assertEqual(expected, response.json) |
+ |
+ _, result = handlers_endpoints.get_request_and_result(task_id) |
+ self.assertEqual(result.state, task_result.State.CANCELED) |
+ |
+ expected = [ |
+ { |
+ 'payload': '{"auth_token":null,"task_id":"5cee488008810",' |
+ '"topic":"projects/abc/topics/def","userdata":"blah"}', |
+ 'queue_name': 'pubsub', |
+ 'transactional': True, |
+ 'url': '/internal/taskqueue/pubsub/5cee488008810', |
+ }, |
+ ] |
+ self.assertEqual(expected, self.pubsub_tasks) |
+ |
+ def test_cancel_by_transaction_id_forbidden(self): |
+ """Asserts that non-privileged non-owner can't cancel tasks.""" |
+ # Create a task as an admin. |
+ self.mock(random, 'getrandbits', lambda _: 0x88) |
+ now = datetime.datetime(2010, 1, 2, 3, 4, 5) |
+ self.mock_now(now) |
+ self.set_as_admin() |
+ self.client_create_task_raw( |
+ pubsub_topic='projects/abc/topics/def', |
+ pubsub_userdata='blah', |
+ transaction_id='txn') |
+ # Attempt to cancel as non-privileged user -> HTTP 403. |
+ self.set_as_user() |
+ self.call_api( |
+ 'cancel_by_transaction_id', body={'transaction_id': 'txn'}, status=403) |
+ |
+ self.assertEqual([], self.pubsub_tasks) |
def test_list_ok(self): |
"""Asserts that list requests all TaskResultSummaries.""" |
@@ -1066,7 +1215,7 @@ class TasksApiTest(BaseTest): |
str_now = unicode(now.strftime(self.DATETIME_NO_MICRO)) |
self.mock_now(now) |
self.mock(random, 'getrandbits', lambda _: 0x66) |
- _, first_id = self.client_create_task_raw( |
+ _, _, first_id = self.client_create_task_raw( |
name='first', tags=['project:yay', 'commit:post', 'os:Win'], |
properties=dict(idempotent=True)) |
self.set_as_bot() |
@@ -1193,7 +1342,7 @@ class TasksApiTest(BaseTest): |
now = datetime.datetime(2010, 1, 2, 3, 4, 5) |
self.mock_now(now) |
self.mock(random, 'getrandbits', lambda _: 0x66) |
- _, first_id = self.client_create_task_raw( |
+ _, _, first_id = self.client_create_task_raw( |
name='first', tags=['project:yay', 'commit:abcd', 'os:Win'], |
pubsub_topic='projects/abc/topics/def', |
pubsub_userdata='1234', |
@@ -1201,7 +1350,7 @@ class TasksApiTest(BaseTest): |
now_60 = self.mock_now(now, 60) |
self.mock(random, 'getrandbits', lambda _: 0x88) |
- _, second_id = self.client_create_task_raw( |
+ _, _, second_id = self.client_create_task_raw( |
name='second', user='jack@localhost', |
pubsub_topic='projects/abc/topics/def', |
pubsub_userdata='5678', |
@@ -1209,7 +1358,7 @@ class TasksApiTest(BaseTest): |
properties=dict(idempotent=True)) |
now_120 = self.mock_now(now, 120) |
- _, third_id = self.client_create_task_raw( |
+ _, _, third_id = self.client_create_task_raw( |
name='third', user='jack@localhost', |
pubsub_topic='projects/abc/topics/def', |
pubsub_userdata='9000', |
@@ -1229,13 +1378,12 @@ class TaskApiTest(BaseTest): |
def test_cancel_ok(self): |
"""Asserts that task cancellation goes smoothly.""" |
- # catch PubSub notification |
# Create and cancel a task as a non-privileged user. |
self.mock(random, 'getrandbits', lambda _: 0x88) |
now = datetime.datetime(2010, 1, 2, 3, 4, 5) |
self.mock_now(now) |
str_now = unicode(now.strftime(self.DATETIME_NO_MICRO)) |
- _, task_id = self.client_create_task_raw( |
+ _, _, task_id = self.client_create_task_raw( |
pubsub_topic='projects/abc/topics/def', |
pubsub_userdata='blah') |
expected = {u'ok': True, u'was_running': False} |
@@ -1274,22 +1422,16 @@ class TaskApiTest(BaseTest): |
'url': '/internal/taskqueue/pubsub/5cee488008810', |
}, |
] |
+ self.assertEqual(expected, self.pubsub_tasks) |
def test_cancel_forbidden(self): |
"""Asserts that non-privileged non-owner can't cancel tasks.""" |
- # catch PubSub notification |
- notifies = [] |
- def enqueue_task_mock(**kwargs): |
- notifies.append(kwargs) |
- return True |
- self.mock(utils, 'enqueue_task', enqueue_task_mock) |
- |
# Create a task as an admin. |
self.mock(random, 'getrandbits', lambda _: 0x88) |
now = datetime.datetime(2010, 1, 2, 3, 4, 5) |
self.mock_now(now) |
self.set_as_admin() |
- _, task_id = self.client_create_task_raw( |
+ _, _, task_id = self.client_create_task_raw( |
pubsub_topic='projects/abc/topics/def', |
pubsub_userdata='blah') |
@@ -1297,12 +1439,14 @@ class TaskApiTest(BaseTest): |
self.set_as_user() |
self.call_api('cancel', body={'task_id': task_id}, status=403) |
+ self.assertEqual([], self.pubsub_tasks) |
+ |
def test_task_canceled(self): |
self.mock(random, 'getrandbits', lambda _: 0x88) |
now = datetime.datetime(2010, 1, 2, 3, 4, 5) |
self.mock_now(now) |
str_now = unicode(now.strftime(self.DATETIME_NO_MICRO)) |
- _, task_id = self.client_create_task_raw( |
+ _, _, task_id = self.client_create_task_raw( |
properties=dict(command=['python', 'runtest.py'])) |
self.set_as_bot() |
@@ -1387,7 +1531,7 @@ class TaskApiTest(BaseTest): |
now = datetime.datetime(2010, 1, 2, 3, 4, 5) |
self.mock_now(now) |
str_now = unicode(now.strftime(self.DATETIME_NO_MICRO)) |
- _, task_id = self.client_create_task_raw() |
+ _, _, task_id = self.client_create_task_raw() |
response = self.call_api('result', body={'task_id': task_id}) |
expected = { |
u'created_ts': str_now, |
@@ -1519,7 +1663,7 @@ class TaskApiTest(BaseTest): |
def test_stdout_empty(self): |
"""Asserts that incipient tasks produce no output.""" |
- _, task_id = self.client_create_task_raw() |
+ _, _, task_id = self.client_create_task_raw() |
response = self.call_api('stdout', body={'task_id': task_id}) |
self.assertEqual({}, response.json) |
@@ -1528,13 +1672,14 @@ class TaskApiTest(BaseTest): |
def test_result_run_not_found(self): |
"""Asserts that getting results from incipient tasks raises 404.""" |
- _, task_id = self.client_create_task_raw() |
+ _, _, task_id = self.client_create_task_raw() |
run_id = task_id[:-1] + '1' |
self.call_api('stdout', body={'task_id': run_id}, status=404) |
def test_task_deduped(self): |
"""Asserts that task deduplication works as expected.""" |
- _, task_id_1 = self.client_create_task_raw(properties=dict(idempotent=True)) |
+ _, _, task_id_1 = self.client_create_task_raw( |
+ properties=dict(idempotent=True)) |
self.set_as_bot() |
task_id_bot = self.bot_run_task() |
@@ -1543,7 +1688,7 @@ class TaskApiTest(BaseTest): |
# second task; this one's results should be returned immediately |
self.set_as_user() |
- _, task_id_2 = self.client_create_task_raw( |
+ _, _, task_id_2 = self.client_create_task_raw( |
name='second', user='jack@localhost', properties=dict(idempotent=True)) |
self.set_as_bot() |
@@ -1564,7 +1709,7 @@ class TaskApiTest(BaseTest): |
"""Asserts that request produces a task request.""" |
now = datetime.datetime(2010, 1, 2, 3, 4, 5, 6) |
self.mock_now(now) |
- _, task_id = self.client_create_task_raw() |
+ _, _, task_id = self.client_create_task_raw() |
response = self.call_api('request', body={'task_id': task_id}) |
expected = { |
u'authenticated': u'user:user@example.com', |