← Back to team overview

yellow team mailing list archive

[Merge] lp:~frankban/lpsetup/yellow-ppa into lp:lpsetup

 

Francesco Banconi has proposed merging lp:~frankban/lpsetup/yellow-ppa into lp:lpsetup.

Requested reviews:
  Launchpad Yellow Squad (yellow)

For more details, see:
https://code.launchpad.net/~frankban/lpsetup/yellow-ppa/+merge/100102

== Changes ==

Updated `lxc-install` subcommand to add the yellow ppa repository in the container.
This is required because we want to install lpsetup (and python-shell-toolbox as a dependency) inside the container.

The helper function `apt_get_install` is now called passing caller=call. This way the output is actually printed to the terminal.

Added a dynamic dispatcher to StepsBasedSubCommand. If a sub command defines a step `mystep`, the dispatcher tries to run the step invoking `subcommand.call_mystep(namespace, step, args)`. If a method named `call_[step name]` is not defined, a default step runner is used. This way, a sub command can easily wrap or override the provided steps. This can be useful in the future, for example, having a step that can or can not be run based on a sub command option.

== Tests ==

$ bin/test 
Running zope.testrunner.layer.UnitTests tests:
  Set up zope.testrunner.layer.UnitTests in 0.000 seconds.
  Ran 39 tests with 0 failures and 0 errors in 0.026 seconds.
Tearing down left over layers:
  Tear down zope.testrunner.layer.UnitTests in 0.000 seconds.
-- 
https://code.launchpad.net/~frankban/lpsetup/yellow-ppa/+merge/100102
Your team Launchpad Yellow Squad is requested to review the proposed merge of lp:~frankban/lpsetup/yellow-ppa into lp:lpsetup.
=== modified file 'lpsetup/argparser.py'
--- lpsetup/argparser.py	2012-03-22 10:39:05 +0000
+++ lpsetup/argparser.py	2012-03-30 11:06:25 +0000
@@ -394,6 +394,24 @@
 
         >>> trace
         ['step1 received eggs']
+
+    A dynamic dispatcher takes care of running the steps. This way, a
+    sub command can easily wrap or override the provided steps, defining a
+    method named 'call_[step name]'::
+
+        >>> trace = []
+
+        >>> class DynamicSubCommand(SubCommand):
+        ...     def call_step1(self, namespace, step, args):
+        ...         trace.append('running step')
+        ...         step(*args)
+
+        >>> parser = ArgumentParser()
+        >>> _ = parser.register_subcommand('sub', DynamicSubCommand)
+        >>> namespace = parser.parse_args('sub --foo eggs --bar spam'.split())
+        >>> namespace.main(namespace)
+        >>> trace
+        ['running step', 'step1 received eggs', 'step2 received eggs and spam']
     """
 
     steps = ()
@@ -441,15 +459,22 @@
             '--skip-steps', nargs='+', choices=self._step_names,
             help='Skip one or more internal functions.')
 
+    def _call_step(self, namespace, step, args):
+        """Default callable used to run a `step`, using given `args`."""
+        return step(*args)
+
     def handle(self, namespace):
         skip_steps = namespace.skip_steps or []
         step_names = filter(
             lambda step_name: step_name not in skip_steps,
             namespace.steps or self._step_names)
+        default_step_runner = self._call_step
         for step_name in step_names:
             step, arg_names = self._steps[step_name]
             args = [getattr(namespace, i) for i in arg_names]
+            step_runner = getattr(
+                self, 'call_' + step_name, default_step_runner)
             try:
-                step(*args)
+                step_runner(namespace, step, args)
             except subprocess.CalledProcessError as err:
                 return err

=== modified file 'lpsetup/settings.py'
--- lpsetup/settings.py	2012-03-16 17:03:20 +0000
+++ lpsetup/settings.py	2012-03-30 11:06:25 +0000
@@ -56,6 +56,7 @@
     )
 LP_SOURCE_DEPS = (
     'http://bazaar.launchpad.net/~launchpad/lp-source-dependencies/trunk')
+LPSETUP_PPA = 'ppa:yellow/ppa'
 LXC_CONFIG_TEMPLATE = '/etc/lxc/local.conf'
 LXC_GUEST_ARCH = 'i386'
 LXC_GUEST_CHOICES = ('lucid', 'oneiric', 'precise')

=== modified file 'lpsetup/subcommands/install.py'
--- lpsetup/subcommands/install.py	2012-03-22 10:48:50 +0000
+++ lpsetup/subcommands/install.py	2012-03-30 11:06:25 +0000
@@ -118,7 +118,7 @@
     """Initialize host machine."""
     # Install necessary deb packages.  This requires Oneiric or later.
     call('apt-get', 'update')
-    apt_get_install(*BASE_PACKAGES)
+    apt_get_install(*BASE_PACKAGES, caller=call)
     # Create the user (if he does not exist).
     if not user_exists(user):
         call('useradd', '-m', '-s', '/bin/bash', '-U', user)
@@ -181,7 +181,7 @@
         call("echo 'mountall hold' | dpkg --set-selections", shell=True)
     call('apt-get', 'update')
     # Install base and Launchpad deb packages.
-    apt_get_install(*LP_PACKAGES, LANG='C')
+    apt_get_install(*LP_PACKAGES, LANG='C', caller=call)
 
 
 def setup_launchpad(user, dependencies_dir, directory, valid_ssh_keys):

=== modified file 'lpsetup/subcommands/lxcinstall.py'
--- lpsetup/subcommands/lxcinstall.py	2012-03-22 17:28:04 +0000
+++ lpsetup/subcommands/lxcinstall.py	2012-03-30 11:06:25 +0000
@@ -31,6 +31,7 @@
 from lpsetup.settings import (
     BASE_PACKAGES,
     DHCP_FILE,
+    LPSETUP_PPA,
     LXC_CONFIG_TEMPLATE,
     LXC_GUEST_ARCH,
     LXC_GUEST_CHOICES,
@@ -57,7 +58,7 @@
     for parallel testing using ephemeral instances.
     """
     # Install necessary deb packages.
-    apt_get_install(*LXC_PACKAGES)
+    apt_get_install(*LXC_PACKAGES, caller=call)
     # XXX 2012-02-02 gmb bug=925024:
     #     These calls need to be removed once the lxc vs. apparmor bug
     #     is resolved, since having apparmor enabled for lxc is very
@@ -116,10 +117,16 @@
             break
 
 
-def initialize_lxc(lxc_name, ssh_key_path):
+def initialize_lxc(lxc_name, ssh_key_path, lxc_os):
     """Initialize LXC container."""
     base_packages = list(BASE_PACKAGES) + ['lpsetup']
     sshcall = ssh(lxc_name, key=ssh_key_path)
+    args = {
+        'assume_yes': '' if lxc_os == 'lucid' else '-y',
+        'repository': LPSETUP_PPA,
+        }
+    sshcall('apt-add-repository {assume_yes} {repository}'.format(**args))
+    sshcall('apt-get clean && apt-get update')
     sshcall(
         'DEBIAN_FRONTEND=noninteractive '
         'apt-get install -y ' + ' '.join(base_packages))
@@ -158,7 +165,7 @@
         (wait_for_lxc,
          'lxc_name', 'ssh_key_path'),
         (initialize_lxc,
-         'lxc_name', 'ssh_key_path'),
+         'lxc_name', 'ssh_key_path', 'lxc_os'),
         (setup_launchpad_lxc,
          'user', 'dependencies_dir', 'directory',
          'valid_ssh_keys', 'ssh_key_path', 'lxc_name'),

=== modified file 'lpsetup/tests/examples.py'
--- lpsetup/tests/examples.py	2012-03-22 10:56:14 +0000
+++ lpsetup/tests/examples.py	2012-03-30 11:06:25 +0000
@@ -67,3 +67,12 @@
         (bad_step, 'foo'),
         (step2, 'foo', 'bar'),
         )
+
+
+class DynamicStepsBasedSubCommand(StepsBasedSubCommand):
+    """An example steps based sub command (using internal step dispatcher)."""
+
+    def call_step1(self, namespace, step, args):
+        print 'running step1 with {args} while bar is {bar}'.format(
+            args=','.join(args), bar=namespace.bar)
+        step(*args)

=== modified file 'lpsetup/tests/test_argparser.py'
--- lpsetup/tests/test_argparser.py	2012-03-22 10:56:14 +0000
+++ lpsetup/tests/test_argparser.py	2012-03-30 11:06:25 +0000
@@ -188,3 +188,20 @@
             error = self.parse_and_call_main('--foo eggs')
         self.assertEqual(1, error.returncode)
         self.check_output(['step1 received eggs'], output)
+
+
+class DynamicStepsBasedSubCommandTest(SubCommandTestMixin, unittest.TestCase):
+
+    sub_command_class = examples.DynamicStepsBasedSubCommand
+
+    def test_dynamic_dispatcher(self):
+        # The test runner calls a function named 'call_[step name]' if it is
+        # defined.
+        with capture_output() as output:
+            self.parse_and_call_main('--foo eggs --bar spam')
+        expected = [
+            'running step1 with eggs while bar is spam',
+            'step1 received eggs',
+            'step2 received eggs and spam'
+            ]
+        self.check_output(expected, output)