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