aurora-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mchucarr...@apache.org
Subject [1/2] git commit: Extend the client configuration plugin architecture.
Date Wed, 23 Apr 2014 17:16:48 GMT
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 <mchucarroll@twopensource.com>
Authored: Wed Apr 23 10:09:22 2014 -0400
Committer: Mark Chu-Carroll <mchucarroll@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'):


Mime
View raw message