You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aurora.apache.org by mc...@apache.org on 2014/07/02 00:07:11 UTC

git commit: Add configuration plugins to default aurora client.

Repository: incubator-aurora
Updated Branches:
  refs/heads/master 5665b43a0 -> f8cb56381


Add configuration plugins to default aurora client.

- Add a plugin to allow users to set logging levels.
- Add a plugin to allow users to configure whether or not unknown exceptions get
  caught or revealed.

Bugs closed: aurora-544, aurora-545

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


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

Branch: refs/heads/master
Commit: f8cb56381befd6e14170a518e414c347759a06bc
Parents: 5665b43
Author: Mark Chu-Carroll <mc...@twopensource.com>
Authored: Tue Jul 1 18:01:14 2014 -0400
Committer: Mark Chu-Carroll <mc...@twitter.com>
Committed: Tue Jul 1 18:01:14 2014 -0400

----------------------------------------------------------------------
 .../python/apache/aurora/client/cli/__init__.py | 21 ++++-
 .../aurora/client/cli/standalone_client.py      | 81 +++++++++++++++++++-
 .../aurora/client/cli/test_config_noun.py       |  4 +-
 .../apache/aurora/client/cli/test_create.py     |  2 +-
 .../apache/aurora/client/cli/test_plugins.py    | 37 +++++++++
 .../python/apache/aurora/client/cli/util.py     |  4 +-
 6 files changed, 143 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/f8cb5638/src/main/python/apache/aurora/client/cli/__init__.py
----------------------------------------------------------------------
diff --git a/src/main/python/apache/aurora/client/cli/__init__.py b/src/main/python/apache/aurora/client/cli/__init__.py
index 827bbb8..08590d0 100644
--- a/src/main/python/apache/aurora/client/cli/__init__.py
+++ b/src/main/python/apache/aurora/client/cli/__init__.py
@@ -102,7 +102,24 @@ class Context(object):
       super(Context.CommandError, self).__init__(msg)  # noqa:T800
       self.msg = msg
       self.code = code
-      self.options = None
+
+  REVEAL_ERRORS = False
+
+  def __init__(self):
+    self.options = None
+    self.logging_level = None
+
+  @classmethod
+  def reveal_errors(cls):
+    return cls.REVEAL_ERRORS
+
+  @classmethod
+  def enable_reveal_errors(cls):
+    cls.REVEAL_ERRORS = True
+
+  @classmethod
+  def disable_reveal_errors(cls):
+    cls.REVEAL_ERRORS = False
 
   @classmethod
   def exit(cls, code, msg):
@@ -409,6 +426,8 @@ class CommandLine(object):
       return EXIT_INTERRUPTED
     except Exception as e:
       print_aurora_log(logging.ERROR, "Unknown error: %s" % e)
+      if Context.reveal_errors():
+        raise
       return EXIT_UNKNOWN_ERROR
 
 

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/f8cb5638/src/main/python/apache/aurora/client/cli/standalone_client.py
----------------------------------------------------------------------
diff --git a/src/main/python/apache/aurora/client/cli/standalone_client.py b/src/main/python/apache/aurora/client/cli/standalone_client.py
index 5d1a4a5..b7c8de6 100644
--- a/src/main/python/apache/aurora/client/cli/standalone_client.py
+++ b/src/main/python/apache/aurora/client/cli/standalone_client.py
@@ -11,16 +11,94 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+from __future__ import print_function
+
 import logging
 import sys
 
-from apache.aurora.client.cli import CommandLine
+from apache.aurora.client.cli import CommandLine, ConfigurationPlugin
 from apache.aurora.client.cli.logsetup import setup_default_log_handlers
+from apache.aurora.client.cli.options import CommandOption
+
+
+class AuroraErrorHandlingPlugin(ConfigurationPlugin):
+  """Plugin for managing the REVEAL_ERRORS flag, which determines whether or not the aurora
+  command line supresses unknown excepts, or dumps the stack. The default behavior
+  should be to supress errors, but for debugging, we want to see the stack dump.
+  """
+
+  def get_options(self):
+    return [CommandOption("--reveal-errors", default=False, action="store_true",
+        help="If enabled, allow unknown errors to generate stack dumps")]
+
+  def before_dispatch(self, raw_args):
+    return raw_args
+
+  def before_execution(self, context):
+    if context.options.reveal_errors:
+      context.enable_reveal_errors()
+
+  def after_execution(self, context, result_code):
+    pass
+
+
+class AuroraLogConfigurationPlugin(ConfigurationPlugin):
+  """Plugin for configuring log level settings for the aurora client."""
+
+  def __init__(self):
+    super(AuroraLogConfigurationPlugin, self).__init__()
+    self.logging_level = None
+
+  def get_options(self):
+    return [
+        CommandOption("--verbose-logging", "-v", default=False, action="store_true",
+          help=("Show verbose logging, including all logs up to level INFO (equivalent to "
+              "--logging-level=20)")),
+        CommandOption("--logging-level", default=None, type=int,
+          help="Set logging to a specific numeric level.")
+    ]
+
+  def before_dispatch(self, raw_args):
+    # We need to process the loglevel arguments before dispatch, so that we can
+    # do logging during dispatch. That means that we need to cheat, and manually
+    # check for the logging arguments. (We still register them in get_options,
+    # so that they'll show up in the help message.)
+    loglevel = logging.WARN
+    for arg in raw_args:
+      if arg == "--verbose-logging" or arg == "-v":
+        if loglevel > logging.INFO:
+          loglevel = logging.INFO
+      if arg.startswith("--logging-level="):
+        arg_bits = arg.split("=")
+        # If the format here is wrong, argparse will generate error, so we just skip
+        # it if it's incorrect.
+        if len(arg_bits) == 2:
+          try:
+            loglevel = int(arg_bits[1])
+          except ValueError:
+            print("Invalid value for log level; must be an integer, but got %s" % arg_bits[1],
+                file=sys.stderr)
+            raise
+    setup_default_log_handlers(loglevel)
+    self.logging_level = loglevel
+    return raw_args
+
+  def before_execution(self, context):
+    context.logging_level = self.logging_level
+
+  def after_execution(self, context, result_code):
+    pass
 
 
 class AuroraCommandLine(CommandLine):
   """The CommandLine implementation for the Aurora client v2 command line."""
 
+  def __init__(self):
+    super(AuroraCommandLine, self).__init__()
+    self.register_plugin(AuroraLogConfigurationPlugin())
+    self.register_plugin(AuroraErrorHandlingPlugin())
+
+
   @classmethod
   def get_description(cls):
     return 'Aurora client command line'
@@ -43,7 +121,6 @@ class AuroraCommandLine(CommandLine):
 
 def proxy_main():
   client = AuroraCommandLine()
-  setup_default_log_handlers(logging.INFO)
   if len(sys.argv) == 1:
     sys.argv.append("help")
   client.execute(sys.argv[1:])

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/f8cb5638/src/test/python/apache/aurora/client/cli/test_config_noun.py
----------------------------------------------------------------------
diff --git a/src/test/python/apache/aurora/client/cli/test_config_noun.py b/src/test/python/apache/aurora/client/cli/test_config_noun.py
index c55c9fe..dfcbd72 100644
--- a/src/test/python/apache/aurora/client/cli/test_config_noun.py
+++ b/src/test/python/apache/aurora/client/cli/test_config_noun.py
@@ -17,6 +17,7 @@ import textwrap
 from mock import patch
 from twitter.common.contextutil import temporary_file
 
+from apache.aurora.client.cli import EXIT_COMMAND_FAILURE
 from apache.aurora.client.cli.client import AuroraCommandLine
 from apache.aurora.client.cli.util import AuroraClientCommandTest, FakeAuroraCommandContext
 
@@ -43,7 +44,8 @@ class TestClientCreateCommand(AuroraClientCommandTest):
         fp.write(self.get_invalid_config("blather=..."))
         fp.flush()
         cmd = AuroraCommandLine()
-        cmd.execute(['config', 'list', fp.name])
+        result = cmd.execute(['config', 'list', fp.name])
+        assert result == EXIT_COMMAND_FAILURE
         assert mock_context.out == []
         assert any(line.startswith("Error loading configuration file: invalid syntax") for line in
             mock_context.err)

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/f8cb5638/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 b9313be..cda95ce 100644
--- a/src/test/python/apache/aurora/client/cli/test_create.py
+++ b/src/test/python/apache/aurora/client/cli/test_create.py
@@ -206,7 +206,7 @@ class TestClientCreateCommand(AuroraClientCommandTest):
         assert api.create_job.call_count == 0
 
   def test_unknown_error(self):
-    mock_context = FakeAuroraCommandContext()
+    mock_context = FakeAuroraCommandContext(reveal=False)
     with contextlib.nested(
         patch('time.sleep'),
         patch('apache.aurora.client.cli.jobs.Job.create_context',

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/f8cb5638/src/test/python/apache/aurora/client/cli/test_plugins.py
----------------------------------------------------------------------
diff --git a/src/test/python/apache/aurora/client/cli/test_plugins.py b/src/test/python/apache/aurora/client/cli/test_plugins.py
index ce85318..3af6291 100644
--- a/src/test/python/apache/aurora/client/cli/test_plugins.py
+++ b/src/test/python/apache/aurora/client/cli/test_plugins.py
@@ -12,6 +12,8 @@
 # limitations under the License.
 #
 
+import logging
+
 from mock import Mock, patch
 from twitter.common.contextutil import temporary_file
 
@@ -192,3 +194,38 @@ class TestPlugins(AuroraClientCommandTest):
       assert not any('list' in t for t in self.transcript)
       assert "Options:" in self.transcript
       assert any('bogosity' in t for t in self.transcript)
+
+  def test_verbose_logging_plugin_in_create_job(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
+    tests on the result."""
+
+    # We'll patch out create_context, which will give us a fake context
+    # object, and everything can be stubbed through that.
+    mock_context = FakeAuroraCommandContext(reveal=False)
+    with patch('apache.aurora.client.cli.jobs.Job.create_context', return_value=mock_context):
+      # After making the client, create sets up a job monitor.
+      # The monitor uses TaskQuery to get the tasks. It's called at least twice:once before
+      # the job is created, and once after. So we need to set up mocks for the query results.
+      mock_query = self.create_mock_query()
+      mock_context.add_expected_status_query_result(
+        self.create_mock_status_query_result(ScheduleStatus.INIT))
+      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()
+
+      # 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()
+        cmd = AuroraCommandLine()
+        cmd.execute(['job', 'create', '-v', '--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, 1)
+      # Check that the plugin did its job.
+      assert mock_context.logging_level == logging.INFO

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/f8cb5638/src/test/python/apache/aurora/client/cli/util.py
----------------------------------------------------------------------
diff --git a/src/test/python/apache/aurora/client/cli/util.py b/src/test/python/apache/aurora/client/cli/util.py
index a2c9d09..65e679f 100644
--- a/src/test/python/apache/aurora/client/cli/util.py
+++ b/src/test/python/apache/aurora/client/cli/util.py
@@ -35,7 +35,7 @@ from gen.apache.aurora.api.ttypes import (
 
 
 class FakeAuroraCommandContext(AuroraCommandContext):
-  def __init__(self):
+  def __init__(self, reveal=True):
     super(FakeAuroraCommandContext, self).__init__()
     self.options = None
     self.status = []
@@ -44,6 +44,8 @@ class FakeAuroraCommandContext(AuroraCommandContext):
     self.showed_urls = []
     self.out = []
     self.err = []
+    if reveal:
+      self.enable_reveal_errors()
 
   def get_api(self, cluster):
     return self.fake_api