aurora-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mchucarr...@apache.org
Subject git commit: Add command output tests for "job create", "job killall", "job kill"
Date Fri, 22 Aug 2014 15:08:32 GMT
Repository: incubator-aurora
Updated Branches:
  refs/heads/master 4bbf29bad -> 2639b3a33


Add command output tests for "job create", "job killall", "job kill"

Add tests to ensure that IO behavior of client commands is correct.

Improve IO behavior in the IO client, so that users get more information.
Formorely, many commands succeeded silently, when it would be more helpful to actually tell
the user that you did something. (This shouldn't affect scripting, because in these commands,
the only result that's needed by a scripter is succeess/failure, which is carried by the exit
code.)

Bugs closed: aurora-645

Reviewed at https://reviews.apache.org/r/24852/


Project: http://git-wip-us.apache.org/repos/asf/incubator-aurora/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-aurora/commit/2639b3a3
Tree: http://git-wip-us.apache.org/repos/asf/incubator-aurora/tree/2639b3a3
Diff: http://git-wip-us.apache.org/repos/asf/incubator-aurora/diff/2639b3a3

Branch: refs/heads/master
Commit: 2639b3a33d0e0c316c6c4386aabc00c8e3b4a311
Parents: 4bbf29b
Author: Mark Chu-Carroll <mchucarroll@twopensource.com>
Authored: Fri Aug 22 11:04:45 2014 -0400
Committer: Mark Chu-Carroll <mchucarroll@twitter.com>
Committed: Fri Aug 22 11:04:45 2014 -0400

----------------------------------------------------------------------
 .../python/apache/aurora/client/cli/context.py  |  4 ++
 .../python/apache/aurora/client/cli/jobs.py     | 16 ++++-
 .../apache/aurora/client/cli/test_create.py     | 50 ++++++++++++--
 .../apache/aurora/client/cli/test_kill.py       | 73 ++++++++++++++++++++
 .../apache/aurora/client/cli/test_restart.py    | 72 ++++++++++++++++++-
 5 files changed, 207 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/2639b3a3/src/main/python/apache/aurora/client/cli/context.py
----------------------------------------------------------------------
diff --git a/src/main/python/apache/aurora/client/cli/context.py b/src/main/python/apache/aurora/client/cli/context.py
index 0108d91..51c7d24 100644
--- a/src/main/python/apache/aurora/client/cli/context.py
+++ b/src/main/python/apache/aurora/client/cli/context.py
@@ -102,6 +102,10 @@ class AuroraCommandContext(Context):
     self.open_page(synthesize_url(api.scheduler_proxy.scheduler_client().url, jobkey.role,
         jobkey.env, jobkey.name))
 
+  def get_job_page(self, api, jobkey):
+    return synthesize_url(api.scheduler_proxy.scheduler_client().url, jobkey.role,
+        jobkey.env, jobkey.name)
+
   def open_scheduler_page(self, cluster, role, env, name):
     """Open a scheduler page"""
     api = self.get_api(cluster)

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/2639b3a3/src/main/python/apache/aurora/client/cli/jobs.py
----------------------------------------------------------------------
diff --git a/src/main/python/apache/aurora/client/cli/jobs.py b/src/main/python/apache/aurora/client/cli/jobs.py
index 109ce59..ebc22aa 100644
--- a/src/main/python/apache/aurora/client/cli/jobs.py
+++ b/src/main/python/apache/aurora/client/cli/jobs.py
@@ -117,8 +117,10 @@ class CreateJobCommand(Verb):
     resp = api.create_job(config)
     context.log_response(resp)
     if resp.responseCode == ResponseCode.INVALID_REQUEST:
+      context.print_err("job create failed because job not found")
       raise context.CommandError(EXIT_INVALID_PARAMETER, "Job not found")
     elif resp.responseCode == ResponseCode.ERROR:
+      context.print_err("job create failed because of scheduler error")
       raise context.CommandError(EXIT_COMMAND_FAILURE, "Error reported by scheduler; see
log for details")
     if context.options.open_browser:
       context.open_job_page(api, config)
@@ -126,6 +128,8 @@ class CreateJobCommand(Verb):
       JobMonitor(api.scheduler_proxy, config.job_key()).wait_until(JobMonitor.running_or_finished)
     elif context.options.wait_until == "FINISHED":
       JobMonitor(api.scheduler_proxy, config.job_key()).wait_until(JobMonitor.terminal)
+    context.print_out("job create succeeded: job url=%s" %
+        context.get_job_page(api, context.options.jobspec))
     return EXIT_OK
 
 
@@ -309,12 +313,14 @@ class AbstractKillCommand(Verb):
       context.log_response(resp)
       if resp.responseCode is not ResponseCode.OK or self.wait_kill_tasks(
           context, api.scheduler_proxy, job, batch) is not EXIT_OK:
-        context.print_log(logging.INFO,
-            "Kill of shards %s failed with error; see log for details" % batch)
+        context.print_err("Kill of shards %s failed with error; see log for details" % batch)
         errors += 1
         if errors > context.options.max_total_failures:
+          context.print_err("Exceeded maximum number of errors while killing instances")
           raise context.CommandError(EXIT_COMMAND_FAILURE,
                "Exceeded maximum number of errors while killing instances")
+      else:
+        context.print_out("Successfully killed shards %s" % batch)
     if errors > 0:
       context.print_err("Warning: Errors occurred during batch kill")
       raise context.CommandError(EXIT_COMMAND_FAILURE, "Errors occurred while killing instances")
@@ -352,6 +358,7 @@ class KillCommand(AbstractKillCommand):
       self.kill_in_batches(context, job, instances_arg)
     if context.options.open_browser:
       context.open_job_page(api, context.options.jobspec)
+    context.print_out("job kill succeeded")
     return EXIT_OK
 
 
@@ -380,6 +387,7 @@ class KillAllJobCommand(AbstractKillCommand):
       self.kill_in_batches(context, job, None)
     if context.options.open_browser:
       context.open_job_page(api, job)
+    context.print_out("job killall succeeded")
     return EXIT_OK
 
 
@@ -480,6 +488,10 @@ Restarts are fully controlled client-side, so aborting halts the restart."""
     resp = api.restart(job, instances, updater_config,
         context.options.healthcheck_interval_seconds, config=config)
 
+    if resp.responseCode != ResponseCode.OK:
+      context.print_err("Error restarting job %s; see log for details" % str(job))
+    else:
+      context.print_out("Job %s restarted successfully" % str(job))
     context.check_and_log_response(resp)
     if context.options.open_browser:
       context.open_job_page(api, context.options.jobspec)

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/2639b3a3/src/test/python/apache/aurora/client/cli/test_create.py
----------------------------------------------------------------------
diff --git a/src/test/python/apache/aurora/client/cli/test_create.py b/src/test/python/apache/aurora/client/cli/test_create.py
index b70363a..31fa56f 100644
--- a/src/test/python/apache/aurora/client/cli/test_create.py
+++ b/src/test/python/apache/aurora/client/cli/test_create.py
@@ -142,8 +142,6 @@ class TestClientCreateCommand(AuroraClientCommandTest):
         cmd = AuroraCommandLine()
         cmd.execute(['job', 'create', '--wait-until=RUNNING', 'west/bozo/test/hello',
             fp.name])
-        # Now check that the right API calls got made.
-        # Check that create_job was called exactly once, with an AuroraConfig parameter.
         self.assert_create_job_called(api)
         self.assert_scheduler_called(api, mock_query, 3)
 
@@ -157,7 +155,6 @@ class TestClientCreateCommand(AuroraClientCommandTest):
           self.create_mock_status_query_result(ScheduleStatus.INIT))
       api = mock_context.get_api('west')
       api.create_job.return_value = self.get_failed_createjob_response()
-      # This is the real test: invoke create as if it had been called by the command line.
       with temporary_file() as fp:
         fp.write(self.get_valid_config())
         fp.flush()
@@ -166,7 +163,6 @@ class TestClientCreateCommand(AuroraClientCommandTest):
             'west/bozo/test/hello', fp.name])
         assert result == EXIT_COMMAND_FAILURE
 
-      # Now check that the right API calls got made.
       # Check that create_job was called exactly once, with an AuroraConfig parameter.
       self.assert_create_job_called(api)
 
@@ -224,6 +220,52 @@ class TestClientCreateCommand(AuroraClientCommandTest):
         assert result == EXIT_UNKNOWN_ERROR
         assert api.create_job.call_count == 0
 
+  def test_simple_successful_create_job_output(self):
+    """Run a test of the "create" command against a mocked-out API:
+    Verifies that the creation command generates the correct output.
+    """
+    mock_context = FakeAuroraCommandContext()
+    with contextlib.nested(
+        patch('threading._Event.wait'),
+        patch('apache.aurora.client.cli.jobs.Job.create_context', return_value=mock_context)):
+      mock_context.add_expected_status_query_result(
+        self.create_mock_status_query_result(ScheduleStatus.PENDING))
+      mock_context.add_expected_status_query_result(
+        self.create_mock_status_query_result(ScheduleStatus.RUNNING))
+      api = mock_context.get_api('west')
+      api.create_job.return_value = self.get_createjob_response()
+
+      with temporary_file() as fp:
+        fp.write(self.get_valid_config())
+        fp.flush()
+        cmd = AuroraCommandLine()
+        cmd.execute(['job', 'create', '--wait-until=RUNNING', 'west/bozo/test/hello',
+            fp.name])
+      assert mock_context.get_out() == [
+          "job create succeeded: job url=http://something_or_other/scheduler/bozo/test/hello"]
+      assert mock_context.get_err() == []
+
+  def test_create_job_failed_output(self):
+    """Test that a failed create generates the correct error messages"""
+    mock_context = FakeAuroraCommandContext()
+    with patch('apache.aurora.client.cli.jobs.Job.create_context', return_value=mock_context):
+      mock_context.add_expected_status_query_result(
+          self.create_mock_status_query_result(ScheduleStatus.INIT))
+      api = mock_context.get_api('west')
+      api.create_job.return_value = self.get_failed_createjob_response()
+      with temporary_file() as fp:
+        fp.write(self.get_valid_config())
+        fp.flush()
+        cmd = AuroraCommandLine()
+        result = cmd.execute(['job', 'create', '--wait-until=RUNNING',
+            'west/bozo/test/hello', fp.name])
+        assert result == EXIT_COMMAND_FAILURE
+
+      # Check that create_job was called exactly once, with an AuroraConfig parameter.
+      print("Out=%s\nErr=%s" % (mock_context.get_out(), mock_context.get_err()))
+      assert mock_context.get_out() == []
+      assert mock_context.get_err() == ["job create failed because of scheduler error"]
+
   def test_simple_successful_create_job_with_bindings(self):
     """Run a test of the "create" command against a mocked-out API:
     Verifies that the creation command sends the right API RPCs, and performs the correct

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/2639b3a3/src/test/python/apache/aurora/client/cli/test_kill.py
----------------------------------------------------------------------
diff --git a/src/test/python/apache/aurora/client/cli/test_kill.py b/src/test/python/apache/aurora/client/cli/test_kill.py
index ee64908..e3a366b 100644
--- a/src/test/python/apache/aurora/client/cli/test_kill.py
+++ b/src/test/python/apache/aurora/client/cli/test_kill.py
@@ -332,3 +332,76 @@ class TestClientKillCommand(AuroraClientCommandTest):
       mock_scheduler_proxy.killTasks.assert_called_with(
         TaskQuery(jobName='hello', environment='test', instanceIds=frozenset([0, 2, 4, 5,
6]),
             owner=Identity(role='bozo')), None)
+
+  def test_killall_job_output(self):
+    """Test kill output."""
+    mock_context = FakeAuroraCommandContext()
+    mock_scheduler_proxy = Mock()
+    with contextlib.nested(
+        patch('threading._Event.wait'),
+        patch('apache.aurora.client.cli.jobs.Job.create_context', return_value=mock_context),
+        patch('apache.aurora.client.factory.CLUSTERS', new=self.TEST_CLUSTERS)):
+
+      api = mock_context.get_api('west')
+      mock_scheduler_proxy.getTasksWithoutConfigs.return_value = self.create_status_call_result()
+      api.kill_job.return_value = self.get_kill_job_response()
+      mock_scheduler_proxy.killTasks.return_value = self.get_kill_job_response()
+      mock_context.add_expected_status_query_result(self.create_status_call_result(
+          self.create_mock_task(ScheduleStatus.KILLED)))
+      with temporary_file() as fp:
+        fp.write(self.get_valid_config())
+        fp.flush()
+        cmd = AuroraCommandLine()
+        cmd.execute(['job', 'killall', '--no-batching', '--config=%s' % fp.name,
+            'west/bozo/test/hello'])
+      assert mock_context.get_out() == ['job killall succeeded']
+      assert mock_context.get_err() == []
+
+  def test_kill_job_with_instances_batched_output(self):
+    """Test kill client-side API logic."""
+    mock_context = FakeAuroraCommandContext()
+    with contextlib.nested(
+        patch('threading._Event.wait'),
+        patch('apache.aurora.client.cli.jobs.Job.create_context', return_value=mock_context),
+        patch('apache.aurora.client.factory.CLUSTERS', new=self.TEST_CLUSTERS)):
+      api = mock_context.get_api('west')
+      status_result = self.create_status_call_result()
+      mock_context.add_expected_status_query_result(status_result)
+      api.kill_job.return_value = self.get_kill_job_response()
+      mock_context.add_expected_status_query_result(self.create_status_call_result(
+          self.create_mock_task(ScheduleStatus.KILLED)))
+
+      with temporary_file() as fp:
+        fp.write(self.get_valid_config())
+        fp.flush()
+        cmd = AuroraCommandLine()
+        cmd.execute(['job', 'kill', '--config=%s' % fp.name, 'west/bozo/test/hello/0,2,4-6'])
+
+    assert mock_context.get_out() == ['Successfully killed shards [0, 2, 4, 5, 6]',
+        'job kill succeeded']
+    assert mock_context.get_err() == []
+
+  def test_kill_job_with_instances_batched_maxerrors_output(self):
+    """Test kill client-side API logic."""
+    mock_context = FakeAuroraCommandContext()
+    with contextlib.nested(
+        patch('apache.aurora.client.cli.jobs.Job.create_context', return_value=mock_context),
+        patch('apache.aurora.client.factory.CLUSTERS', new=self.TEST_CLUSTERS)):
+      api = mock_context.get_api('west')
+      status_result = self.create_status_call_result()
+      mock_context.add_expected_status_query_result(status_result)
+      api.kill_job.return_value = self.create_error_response()
+
+      with temporary_file() as fp:
+        fp.write(self.get_valid_config())
+        fp.flush()
+        cmd = AuroraCommandLine()
+        cmd.execute(['job', 'kill', '--max-total-failures=1', '--config=%s' % fp.name,
+            'west/bozo/test/hello/0,2,4-13'])
+
+      assert mock_context.get_out() == []
+      print(mock_context.get_err())
+      assert mock_context.get_err() == [
+          'Kill of shards [0, 2, 4, 5, 6] failed with error; see log for details',
+          'Kill of shards [7, 8, 9, 10, 11] failed with error; see log for details',
+          'Exceeded maximum number of errors while killing instances']

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/2639b3a3/src/test/python/apache/aurora/client/cli/test_restart.py
----------------------------------------------------------------------
diff --git a/src/test/python/apache/aurora/client/cli/test_restart.py b/src/test/python/apache/aurora/client/cli/test_restart.py
index 14a08e8..a1e7a5a 100644
--- a/src/test/python/apache/aurora/client/cli/test_restart.py
+++ b/src/test/python/apache/aurora/client/cli/test_restart.py
@@ -156,8 +156,6 @@ class TestRestartCommand(AuroraClientCommandTest):
         mock_log.assert_called_with(20, 'Error executing command: %s', 'Damn')
 
   def test_restart_failed_restart(self):
-    # Test the client-side updater logic in its simplest case: everything succeeds, and no
rolling
-    # updates.
     (mock_api, mock_scheduler_proxy) = self.create_mock_api()
     mock_health_check = self.setup_health_checks(mock_api)
     self.setup_mock_scheduler_for_simple_restart(mock_api)
@@ -179,3 +177,73 @@ class TestRestartCommand(AuroraClientCommandTest):
         mock_scheduler_proxy.restartShards.assert_called_with(JobKey(environment=self.TEST_ENV,
             role=self.TEST_ROLE, name=self.TEST_JOB), [0, 1, 2, 3, 4], None)
         assert result == EXIT_API_ERROR
+
+  MOCK_OUT = []
+  MOCK_ERR = []
+
+  @classmethod
+  def reset_mock_io(cls):
+    cls.MOCK_OUT = []
+    cls.MOCK_ERR = []
+
+  @classmethod
+  def mock_print_out(cls, msg, indent=0):
+    indent_str = " " * indent
+    cls.MOCK_OUT.append("%s%s" % (indent_str, msg))
+
+  @classmethod
+  def mock_print_err(cls, msg, indent=0):
+    indent_str = " " * indent
+    cls.MOCK_ERR.append("%s%s" % (indent_str, msg))
+
+  def test_restart_simple_output(self):
+    self.reset_mock_io()
+    # Test the client-side restart logic in its simplest case: everything succeeds
+    (mock_api, mock_scheduler_proxy) = self.create_mock_api()
+    mock_health_check = self.setup_health_checks(mock_api)
+    self.setup_mock_scheduler_for_simple_restart(mock_api)
+    with contextlib.nested(
+        patch('apache.aurora.client.api.SchedulerProxy', return_value=mock_scheduler_proxy),
+        patch('apache.aurora.client.factory.CLUSTERS', new=self.TEST_CLUSTERS),
+        patch('apache.aurora.client.api.instance_watcher.StatusHealthCheck',
+            return_value=mock_health_check),
+        patch('time.time', side_effect=functools.partial(self.fake_time, self)),
+        patch('threading._Event.wait'),
+        patch('apache.aurora.client.cli.context.AuroraCommandContext.print_out',
+            side_effect=self.mock_print_out),
+        patch('apache.aurora.client.cli.context.AuroraCommandContext.print_err',
+            side_effect=self.mock_print_err)
+    ):
+      with temporary_file() as fp:
+        fp.write(self.get_valid_config())
+        fp.flush()
+        cmd = AuroraCommandLine()
+        cmd.execute(['job', 'restart', '--batch-size=5', 'west/bozo/test/hello', fp.name])
+        print(self.MOCK_OUT)
+        assert self.MOCK_OUT == ['Job west/bozo/test/hello restarted successfully']
+        assert self.MOCK_ERR == []
+
+  def test_restart_failed_restart_output(self):
+    self.reset_mock_io()
+    (mock_api, mock_scheduler_proxy) = self.create_mock_api()
+    mock_health_check = self.setup_health_checks(mock_api)
+    self.setup_mock_scheduler_for_simple_restart(mock_api)
+    mock_scheduler_proxy.restartShards.return_value = self.create_error_response()
+    with contextlib.nested(
+        patch('apache.aurora.client.api.SchedulerProxy', return_value=mock_scheduler_proxy),
+        patch('apache.aurora.client.factory.CLUSTERS', new=self.TEST_CLUSTERS),
+        patch('apache.aurora.client.api.instance_watcher.StatusHealthCheck',
+            return_value=mock_health_check),
+        patch('time.time', side_effect=functools.partial(self.fake_time, self)),
+        patch('apache.aurora.client.cli.context.AuroraCommandContext.print_out',
+            side_effect=self.mock_print_out),
+        patch('apache.aurora.client.cli.context.AuroraCommandContext.print_err',
+            side_effect=self.mock_print_err),
+        patch('threading._Event.wait')):
+      with temporary_file() as fp:
+        fp.write(self.get_valid_config())
+        fp.flush()
+        cmd = AuroraCommandLine()
+        cmd.execute(['job', 'restart', '--batch-size=5', 'west/bozo/test/hello', fp.name])
+      assert self.MOCK_OUT == []
+      assert self.MOCK_ERR == ['Error restarting job west/bozo/test/hello; see log for details']


Mime
View raw message