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/04/23 19:16:48 UTC

[1/2] git commit: Extend the client configuration plugin architecture.

Repository: incubator-aurora
Updated Branches:
  refs/heads/master 56649c0be -> 760e5d3c0


Extend the client configuration plugin architecture.

Plugins can now run code at three key points:
- Before arguments are processed and execution is dispatched to a command.
- After argument processing and dispatch, but before execution.
- After execution.

This allows plugins to perform initialization required for argument processing,
and for post-execution cleanups and synchronizations.

Bugs closed: aurora-332

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


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

Branch: refs/heads/master
Commit: 1a4579fec5578d8b226174e3280391b7b62d7655
Parents: 56649c0
Author: Mark Chu-Carroll <mc...@twopensource.com>
Authored: Wed Apr 23 10:09:22 2014 -0400
Committer: Mark Chu-Carroll <mc...@twitter.com>
Committed: Wed Apr 23 10:09:22 2014 -0400

----------------------------------------------------------------------
 .../python/apache/aurora/client/cli/__init__.py | 55 ++++++++++++++++----
 .../apache/aurora/client/cli/test_plugins.py    | 24 ++++++++-
 2 files changed, 66 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/1a4579fe/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 f1165a0..10b1d1d 100644
--- a/src/main/python/apache/aurora/client/cli/__init__.py
+++ b/src/main/python/apache/aurora/client/cli/__init__.py
@@ -120,12 +120,7 @@ class Context(object):
 
 
 class ConfigurationPlugin(object):
-  """A component that can be plugged in to a command-line.
-  The component can add a set of options to the command-line.
-  After a context is created, but before a call is dispatched
-  to the noun/verb for execution, the plugin will have the opportunity
-  to process its parameters and perform whatever initialization it
-  requires on the context.
+  """A component that can be plugged in to a command-line to add new configuration options.
 
   For example, if a production environment is protected behind some
   kind of gateway, a ConfigurationPlugin could be created that
@@ -133,13 +128,39 @@ class ConfigurationPlugin(object):
   attempt to communicate with the environment.
   """
 
+  class Error(Exception):
+    def __init__(self, msg, code=0):
+      super(ConfigurationPlugin.Error, self).__init__(msg)
+      self.code = code
+      self.msg = msg
+
   @abstractmethod
   def get_options(self):
     """Return the set of options processed by this plugin"""
 
   @abstractmethod
-  def execute(self, context):
-    """Run the context/command line initialization code for this plugin."""
+  def before_dispatch(self, raw_args):
+    """Run some code before dispatching to the client.
+    Returns a potentially modified version of the command line arguments.
+    If a ConfigurationPlugin.Error exception is thrown, it aborts command execution.
+    """
+
+  @abstractmethod
+  def before_execution(self, context):
+    """Run the context/command line initialization code for this plugin before
+    invoking the command verb.
+    The before_execution method behaves as if it's part of the implementation of the
+    verb being invoked. It has access to the same context that will be used by the command.
+    Any errors that occur during the execution should be signalled using ConfigurationPlugin.Error.
+    """
+
+  @abstractmethod
+  def after_execution(self, context, result_code):
+    """Run cleanup code after the execution of the verb has completed.
+    This code should *not* ever throw exceptions. It's just cleanup code, and it shouldn't
+    ever change the result of invoking a verb. This can throw a ConfigurationPlugin.Error,
+    which will generate log records, but will not otherwise effect the execution of the command.
+    """
 
 
 class AuroraCommand(object):
@@ -282,6 +303,13 @@ class CommandLine(object):
     """
     print_aurora_log(logging.INFO, 'Command=(%s)', args)
     nouns = self.registered_nouns
+    try:
+      for plugin in self.plugins:
+        args = plugin.before_dispatch(args)
+    except ConfigurationPlugin.Error as e:
+      print('Error in configuration plugin before dispatch: %s' % e.msg, file=sys.stderr)
+      return e.code
+
     if args[0] == 'help':
       return self.help_cmd(args[1:])
     self.setup_options_parser()
@@ -293,9 +321,9 @@ class CommandLine(object):
     context.set_options(options)
     try:
       for plugin in self.plugins:
-        plugin.execute(context)
-    except Context.CommandError as c:
-      print('Error in configuration plugin: %s' % c.msg, file=sys.stderr)
+        plugin.before_execution(context)
+    except ConfigurationPlugin.Error as e:
+      print('Error in configuration plugin before execution: %s' % c.msg, file=sys.stderr)
       return c.code
     try:
       result = noun.execute(context)
@@ -303,6 +331,11 @@ class CommandLine(object):
         print_aurora_log(logging.INFO, 'Command terminated successfully')
       else:
         print_aurora_log(logging.INFO, 'Commmand terminated with error code %s', result)
+      for plugin in self.plugins:
+        try:
+          plugin.after_execution(context, result)
+        except ConfigurationPlugin.Error as e:
+          print_aurora_log(logging.INFO, 'Error executing post-execution extension hook:  %s', e.msg)
       return result
     except Context.CommandError as c:
       print_aurora_log(logging.INFO, 'Error executing command: %s', c.msg)

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/1a4579fe/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 7cacb02..64bde45 100644
--- a/src/test/python/apache/aurora/client/cli/test_plugins.py
+++ b/src/test/python/apache/aurora/client/cli/test_plugins.py
@@ -43,12 +43,30 @@ from mock import Mock, patch
 
 
 class BogusPlugin(ConfigurationPlugin):
+  """A test plugin, which uses all three plugin methods:
+  - before_dispatch changes the command-line arguments to remove "bogus_bogus" if it's found.
+    If it didn't work, we'll get an invalid parameter exception executing the test.
+  - before_execution processes the "bogosity" argument.
+  - after_execution sets a flag.
+  """
+
   def get_options(self):
     return [CommandOption('--bogosity', type=str, help='Permitted bogosity level')]
 
-  def execute(self, context):
+  def before_dispatch(self, args):
+    if args[0] == '--bogus_bogus':
+       args = args[1:]
+    return args
+
+
+  def before_execution(self, context):
     context.bogosity = context.options.bogosity
 
+  def after_execution(self, context, return_value):
+    context.after = True
+    raise self.Error("Oops")
+
+
 class TestPlugins(AuroraClientCommandTest):
 
   @classmethod
@@ -123,7 +141,7 @@ class TestPlugins(AuroraClientCommandTest):
         fp.flush()
         cmd = AuroraCommandLine()
         cmd.register_plugin(BogusPlugin())
-        cmd.execute(['job', 'create', '--bogosity=maximum', '--wait-until=RUNNING',
+        cmd.execute(['--bogus_bogus', 'job', 'create', '--bogosity=maximum', '--wait-until=RUNNING',
             'west/bozo/test/hello', fp.name])
 
       # Now check that the right API calls got made.
@@ -132,6 +150,8 @@ class TestPlugins(AuroraClientCommandTest):
       self.assert_scheduler_called(api, mock_query, 2)
       # Check that the plugin did its job.
       assert mock_context.bogosity == "maximum"
+      assert mock_context.after == True
+
 
   def mock_print(self, str):
     for str in str.split('\n'):


[2/2] git commit: Add a "get version" API method. (This is needed for a logging-related change for twitter.)

Posted by mc...@apache.org.
Add a "get version" API method. (This is needed for a logging-related change for twitter.)

While I'm in there, fix a bug in "job list".

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


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

Branch: refs/heads/master
Commit: 760e5d3c0e2f1db5e29ee891423aea8c60043fcf
Parents: 1a4579f
Author: Mark Chu-Carroll <mc...@twopensource.com>
Authored: Wed Apr 23 13:13:04 2014 -0400
Committer: Mark Chu-Carroll <mc...@twitter.com>
Committed: Wed Apr 23 13:13:04 2014 -0400

----------------------------------------------------------------------
 src/main/python/apache/aurora/client/cli/BUILD        |  1 +
 src/main/python/apache/aurora/client/cli/__init__.py  | 13 ++++++++++++-
 src/main/python/apache/aurora/client/cli/jobs.py      |  2 --
 src/test/python/apache/aurora/client/cli/test_help.py |  2 +-
 4 files changed, 14 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/760e5d3c/src/main/python/apache/aurora/client/cli/BUILD
----------------------------------------------------------------------
diff --git a/src/main/python/apache/aurora/client/cli/BUILD b/src/main/python/apache/aurora/client/cli/BUILD
index 02e061c..17cdc28 100644
--- a/src/main/python/apache/aurora/client/cli/BUILD
+++ b/src/main/python/apache/aurora/client/cli/BUILD
@@ -33,6 +33,7 @@ python_library(
   dependencies = [
     pants(':cli'),
     pants(':bridge'),
+    pants('3rdparty/python:twitter.common.python'),
     pants('src/main/python/apache/aurora/client/bin:aurora_client_lib')
   ]
 )

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/760e5d3c/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 10b1d1d..5a10328 100644
--- a/src/main/python/apache/aurora/client/cli/__init__.py
+++ b/src/main/python/apache/aurora/client/cli/__init__.py
@@ -39,6 +39,8 @@ import logging
 import sys
 from uuid import uuid1
 
+from twitter.common.python.pex import PexInfo
+from twitter.common.python.dirwrapper import PythonDirectoryWrapper
 
 # Constants for standard return codes.
 EXIT_OK = 0
@@ -70,6 +72,15 @@ def print_aurora_log(sev, msg, *args, **kwargs):
   kwargs['extra'] = extra
   logger.log(sev, msg, *args, **kwargs)
 
+def get_client_version():
+  try:
+    pexpath = sys.argv[0]
+    pex_info = PexInfo.from_pex(PythonDirectoryWrapper.get(pexpath))
+    return ("%s@%s" % (pex_info.build_properties.get('sha', 'unknown'),
+        pex_info.build_properties.get('date', 'unknown')))
+  except (IOError, PythonDirectoryWrapper.Error):
+    return "VersionUnknown"
+
 
 class Context(object):
   class Error(Exception): pass
@@ -266,7 +277,7 @@ class CommandLine(object):
   @property
   def composed_help(self):
     """Get a fully composed, well-formatted help message"""
-    result = ["Usage:"]
+    result = ["Aurora Client version %s" % get_client_version(), "Usage:"]
     for noun in self.registered_nouns:
       result += ["==Commands for %ss" % noun]
       result += ["  %s" % s for s in self.nouns[noun].usage] + [""]

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/760e5d3c/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 0793a1a..0534bdf 100644
--- a/src/main/python/apache/aurora/client/cli/jobs.py
+++ b/src/main/python/apache/aurora/client/cli/jobs.py
@@ -390,8 +390,6 @@ class ListJobsCommand(Verb):
     jobs = context.get_jobs_matching_key(context.options.jobspec)
     for j in jobs:
       context.print_out('%s/%s/%s/%s' % (j.cluster, j.role, j.env, j.name))
-    result = self.get_status_for_jobs(jobs, context)
-    context.print_out(result)
     return EXIT_OK
 
 class OpenCommand(Verb):

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/760e5d3c/src/test/python/apache/aurora/client/cli/test_help.py
----------------------------------------------------------------------
diff --git a/src/test/python/apache/aurora/client/cli/test_help.py b/src/test/python/apache/aurora/client/cli/test_help.py
index 6876bb7..12d8671 100644
--- a/src/test/python/apache/aurora/client/cli/test_help.py
+++ b/src/test/python/apache/aurora/client/cli/test_help.py
@@ -44,7 +44,7 @@ class TestHelp(unittest.TestCase):
         side_effect=self.mock_print):
       self.cmd.execute(['help'])
       assert len(self.transcript) > 10
-      assert self.transcript[0] == 'Usage:'
+      assert self.transcript[1] == 'Usage:'
       assert '==Commands for jobs' in self.transcript
       assert '==Commands for quotas' in self.transcript