← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~benji/lpsetup/add-hostinit into lp:lpsetup

 

Benji York has proposed merging lp:~benji/lpsetup/add-hostinit into lp:lpsetup.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~benji/lpsetup/add-hostinit/+merge/111429

This branch moves much of the code out of the "initialize" subcommand
into an "inithost" subcommand per the lpsetup LEP.

The subcommand name "hostinit" was chosen as it better communicates the
function of the command but can be easily changed to the name used in
the LEP ("init") if desired.

The initialize command was refactored as a subclass of inithost that
reuses the inithost steps and adds steps.  It is expected that the
remaining code included in the initialize command will eventually be
moved into another command still and only a small backward-compatible
subcommand will be left.  That subcommand will be removed when the need
for backward compatibility goes away.

The existing tests continue to pass, but few of them exercised the
initialize command.  Manual testing suggests that the new and
pre-existing commands continue to function.

-- 
https://code.launchpad.net/~benji/lpsetup/add-hostinit/+merge/111429
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~benji/lpsetup/add-hostinit into lp:lpsetup.
=== modified file '.bzrignore'
--- .bzrignore	2012-04-30 09:26:07 +0000
+++ .bzrignore	2012-06-21 15:45:26 +0000
@@ -9,4 +9,4 @@
 lpsetup.egg-info
 MANIFEST
 parts
-
+tags

=== modified file 'lpsetup/cli.py'
--- lpsetup/cli.py	2012-05-22 09:36:57 +0000
+++ lpsetup/cli.py	2012-06-21 15:45:26 +0000
@@ -16,6 +16,7 @@
     )
 from lpsetup.subcommands import (
     branch,
+    inithost,
     install,
     lxcinstall,
     update,
@@ -26,6 +27,7 @@
 def main():
     parser = argparser.ArgumentParser(description=description)
     parser.register_subcommand('install', install.SubCommand)
+    parser.register_subcommand('inithost', inithost.SubCommand)
     parser.register_subcommand('update', update.SubCommand)
     parser.register_subcommand('lxc-install', lxcinstall.SubCommand)
     parser.register_subcommand('branch', branch.SubCommand)

=== added file 'lpsetup/subcommands/inithost.py'
--- lpsetup/subcommands/inithost.py	1970-01-01 00:00:00 +0000
+++ lpsetup/subcommands/inithost.py	2012-06-21 15:45:26 +0000
@@ -0,0 +1,212 @@
+#!/usr/bin/env python
+# Copyright 2012 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""hostinit subcommand: prepare a host machine to run Launchpad."""
+
+__metaclass__ = type
+__all__ = [
+    'initialize',
+    'setup_apt',
+    'setup_bzr_locations',
+    'SubCommand',
+    ]
+
+from email.Utils import formataddr
+import os
+import subprocess
+
+from shelltoolbox import (
+    apt_get_install,
+    generate_ssh_keys,
+    mkdirs,
+    run,
+    su,
+    user_exists,
+    )
+
+from lpsetup import (
+    argparser,
+    handlers,
+    )
+from lpsetup.settings import (
+    APT_REPOSITORIES,
+    BASE_PACKAGES,
+    CHECKOUT_DIR,
+    DEPENDENCIES_DIR,
+    LP_BZR_LOCATIONS,
+    LP_CHECKOUT,
+    LP_PACKAGES,
+    SSH_KEY_NAME,
+    )
+from lpsetup.utils import (
+    call,
+    ConfigParser,
+    )
+
+
+def initialize(
+    user, full_name, email, lpuser, private_key, public_key, valid_ssh_keys,
+    ssh_key_path, feed_random, dependencies_dir, directory):
+    """Initialize host machine."""
+    # Install necessary deb packages.  This requires Oneiric or later.
+    call('apt-get', 'update')
+    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)
+    with su(user) as env:
+        # Set up the user's ssh directory.  The ssh key must be associated
+        # with the lpuser's Launchpad account.
+        ssh_dir = os.path.join(env.home, '.ssh')
+        mkdirs(ssh_dir)
+        # Generate user ssh keys if none are supplied.
+        pub_key_path = ssh_key_path + '.pub'
+        if not valid_ssh_keys:
+            generate_ssh_keys(ssh_key_path)
+            private_key = open(ssh_key_path).read()
+            public_key = open(pub_key_path).read()
+        auth_file = os.path.join(ssh_dir, 'authorized_keys')
+        known_hosts = os.path.join(ssh_dir, 'known_hosts')
+        known_host_content = subprocess.check_output([
+            'ssh-keyscan', '-t', 'rsa', 'bazaar.launchpad.net'])
+        for filename, contents, mode in [
+            (ssh_key_path, private_key, 'w'),
+            (pub_key_path, public_key, 'w'),
+            (auth_file, public_key, 'a'),
+            (known_hosts, known_host_content, 'a'),
+            ]:
+            with open(filename, mode) as f:
+                f.write(contents + '\n')
+            os.chmod(filename, 0644)
+        os.chmod(ssh_key_path, 0600)
+        # Set up bzr and Launchpad authentication.
+        call('bzr', 'whoami', formataddr([full_name, email]))
+        if valid_ssh_keys:
+            subprocess.call(['bzr', 'lp-login', lpuser])
+    # haveged is used to fill /dev/random, avoiding
+    # entropy exhaustion during automated parallel tests.
+    if feed_random:
+        apt_get_install('haveged', caller=call)
+
+
+def setup_bzr_locations(user, lpuser, directory, template=LP_BZR_LOCATIONS):
+    """Set up bazaar locations."""
+    context = {
+        'checkout_dir': os.path.join(directory, LP_CHECKOUT),
+        'directory': directory,
+        'lpuser': lpuser,
+        }
+    with su(user) as env:
+        bazaar_dir = os.path.join(env.home, '.bazaar')
+        mkdirs(bazaar_dir)
+        path = os.path.join(bazaar_dir, 'locations.conf')
+        parser = ConfigParser()
+        parser.read(path)
+        for section_template, options in template.items():
+            section = section_template.format(**context)
+            if not parser.has_section(section):
+                parser.add_section(section)
+            for option, value in options.items():
+                parser.set(section, option, value.format(**context))
+        with open(path, 'w') as f:
+            parser.write(f)
+
+
+def setup_apt(no_repositories=True):
+    """Setup, update and upgrade deb packages."""
+    if not no_repositories:
+        distro = run('lsb_release', '-cs').strip()
+        # APT repository update.
+        for reposirory in APT_REPOSITORIES:
+            assume_yes = None if distro == 'lucid' else '-y'
+            call('add-apt-repository', assume_yes,
+                 reposirory.format(distro=distro))
+    call('apt-get', 'update')
+    # Install base and Launchpad deb packages.
+    apt_get_install(*LP_PACKAGES, LC_ALL='C', caller=call)
+
+
+class SubCommand(argparser.StepsBasedSubCommand):
+    """Prepare a host machine to run Launchpad."""
+    initialize_step = (initialize,
+         'user', 'full_name', 'email', 'lpuser',
+         'private_key', 'public_key', 'valid_ssh_keys', 'ssh_key_path',
+         'feed_random', 'dependencies_dir', 'directory')
+
+    setup_apt_step = (setup_apt,
+         'no_repositories')
+    setup_bzr_locations_step = (setup_bzr_locations,
+        'user', 'lpuser', 'directory')
+
+    steps = (initialize_step, setup_bzr_locations_step, setup_apt_step)
+
+    help = __doc__
+    needs_root = True
+    ssh_key_name_help = 'The ssh key name used to connect to Launchpad.'
+    validators = (
+        handlers.handle_user,
+        handlers.handle_lpuser,
+        handlers.handle_userdata,
+        handlers.handle_ssh_keys,
+        handlers.handle_directories,
+        )
+
+    def add_arguments(self, parser):
+        super(SubCommand, self).add_arguments(parser)
+        parser.add_argument(
+            '-u', '--user',
+            help='The name of the system user to be created or updated. '
+                 'The current user is used if this script is not run as '
+                 'root and this argument is omitted.')
+        parser.add_argument(
+            '-e', '--email',
+            help='The email of the user, used for bzr whoami. This argument '
+                 'can be omitted if a bzr id exists for current user.')
+        parser.add_argument(
+            '-f', '--full-name',
+            help='The full name of the user, used for bzr whoami. '
+                 'This argument can be omitted if a bzr id exists for '
+                 'current user.')
+        parser.add_argument(
+            '-l', '--lpuser',
+            help='The name of the Launchpad user that will be used to '
+                 'check out dependencies. If not provided, the system '
+                 'user name is used.')
+        parser.add_argument(
+            '-v', '--private-key',
+            help='The SSH private key for the Launchpad user (without '
+                 'passphrase). If this argument is omitted and a keypair is '
+                 'not found in the home directory of the system user a new '
+                 'SSH keypair will be generated and the checkout of the '
+                 'Launchpad code will use HTTP rather than bzr+ssh.')
+        parser.add_argument(
+            '-b', '--public-key',
+            help='The SSH public key for the Launchpad user. '
+                 'If this argument is omitted and a keypair is not found '
+                 'in the home directory of the system user a new SSH '
+                 'keypair will be generated and the checkout of the '
+                 'Launchpad code will use HTTP rather than bzr+ssh.')
+        parser.add_argument(
+            '-d', '--dependencies-dir', default=DEPENDENCIES_DIR,
+            help='The directory of the Launchpad dependencies to be created. '
+                 'The directory must reside under the home directory of the '
+                 'given user (see -u argument). '
+                 '[DEFAULT={0}]'.format(DEPENDENCIES_DIR))
+        parser.add_argument(
+            '-c', '--directory', default=CHECKOUT_DIR,
+            help='The directory of the Launchpad repository to be created. '
+                 'The directory must reside under the home directory of the '
+                 'given user (see -u argument). '
+                 '[DEFAULT={0}]'.format(CHECKOUT_DIR))
+        parser.add_argument(
+            '-S', '--ssh-key-name', default=SSH_KEY_NAME,
+            help='{0} [DEFAULT={1}]'.format(
+                self.ssh_key_name_help, SSH_KEY_NAME))
+        parser.add_argument(
+            '-N', '--no-repositories', action='store_true',
+            help='Do not add APT repositories.')
+        parser.add_argument(
+            '--feed-random', action='store_true',
+            help='Use haveged to maintain a pool of random bytes used to '
+                 'fill /dev/random and avoid entropy exhaustion.')

=== modified file 'lpsetup/subcommands/install.py'
--- lpsetup/subcommands/install.py	2012-06-18 09:16:22 +0000
+++ lpsetup/subcommands/install.py	2012-06-21 15:45:26 +0000
@@ -7,55 +7,37 @@
 __metaclass__ = type
 __all__ = [
     'make_launchpad',
-    'initialize',
     'SubCommand',
-    'setup_apt',
     'setup_codebase',
     'setup_external_sourcecode',
     'setup_launchpad',
     ]
 
-from email.Utils import formataddr
 import os
 import pwd
 import subprocess
 
 from shelltoolbox import (
-    apt_get_install,
     cd,
-    generate_ssh_keys,
     get_su_command,
     file_append,
     mkdirs,
     run,
     su,
-    user_exists,
     )
 
-from lpsetup import (
-    argparser,
-    handlers,
-    )
+from lpsetup import argparser
+from lpsetup.subcommands import inithost
 from lpsetup.settings import (
-    APT_REPOSITORIES,
-    BASE_PACKAGES,
-    CHECKOUT_DIR,
-    DEPENDENCIES_DIR,
     HOSTS_CONTENT,
     HOSTS_FILE,
     LP_APACHE_MODULES,
     LP_APACHE_ROOTS,
-    LP_BZR_LOCATIONS,
     LP_CHECKOUT,
-    LP_PACKAGES,
     LP_REPOS,
     LP_SOURCE_DEPS,
-    SSH_KEY_NAME,
-    )
-from lpsetup.utils import (
-    call,
-    ConfigParser,
-    )
+    )
+from lpsetup.utils import call
 
 
 def setup_codebase(user, checkout_dir, dependencies_dir, valid_ssh_keys=True):
@@ -115,92 +97,15 @@
         call('make', '-C', checkout_dir, 'install')
 
 
-def initialize(
-    user, full_name, email, lpuser, private_key, public_key, valid_ssh_keys,
-    ssh_key_path, feed_random, dependencies_dir, directory):
-    """Initialize host machine."""
-    # Install necessary deb packages.  This requires Oneiric or later.
-    call('apt-get', 'update')
-    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)
+def fetch(user, directory, dependencies_dir, valid_ssh_keys):
+    """Create a repo for the Launchpad code and retrieve it."""
     with su(user) as env:
-        # Set up the user's ssh directory.  The ssh key must be associated
-        # with the lpuser's Launchpad account.
-        ssh_dir = os.path.join(env.home, '.ssh')
-        mkdirs(ssh_dir)
-        # Generate user ssh keys if none are supplied.
-        pub_key_path = ssh_key_path + '.pub'
-        if not valid_ssh_keys:
-            generate_ssh_keys(ssh_key_path)
-            private_key = open(ssh_key_path).read()
-            public_key = open(pub_key_path).read()
-        auth_file = os.path.join(ssh_dir, 'authorized_keys')
-        known_hosts = os.path.join(ssh_dir, 'known_hosts')
-        known_host_content = subprocess.check_output([
-            'ssh-keyscan', '-t', 'rsa', 'bazaar.launchpad.net'])
-        for filename, contents, mode in [
-            (ssh_key_path, private_key, 'w'),
-            (pub_key_path, public_key, 'w'),
-            (auth_file, public_key, 'a'),
-            (known_hosts, known_host_content, 'a'),
-            ]:
-            with open(filename, mode) as f:
-                f.write(contents + '\n')
-            os.chmod(filename, 0644)
-        os.chmod(ssh_key_path, 0600)
-        # Set up bzr and Launchpad authentication.
-        call('bzr', 'whoami', formataddr([full_name, email]))
-        if valid_ssh_keys:
-            subprocess.call(['bzr', 'lp-login', lpuser])
         # Set up the repository.
         mkdirs(directory)
         call('bzr', 'init-repo', '--2a', directory)
     # Set up the codebase.
     checkout_dir = os.path.join(directory, LP_CHECKOUT)
     setup_codebase(user, checkout_dir, dependencies_dir, valid_ssh_keys)
-    # haveged is used to fill /dev/random, avoiding
-    # entropy exhaustion during automated parallel tests.
-    if feed_random:
-        apt_get_install('haveged', caller=call)
-
-
-def setup_bzr_locations(user, lpuser, directory, template=LP_BZR_LOCATIONS):
-    """Set up bazaar locations."""
-    context = {
-        'checkout_dir': os.path.join(directory, LP_CHECKOUT),
-        'directory': directory,
-        'lpuser': lpuser,
-        }
-    with su(user) as env:
-        bazaar_dir = os.path.join(env.home, '.bazaar')
-        mkdirs(bazaar_dir)
-        path = os.path.join(bazaar_dir, 'locations.conf')
-        parser = ConfigParser()
-        parser.read(path)
-        for section_template, options in template.items():
-            section = section_template.format(**context)
-            if not parser.has_section(section):
-                parser.add_section(section)
-            for option, value in options.items():
-                parser.set(section, option, value.format(**context))
-        with open(path, 'w') as f:
-            parser.write(f)
-
-
-def setup_apt(no_repositories=True):
-    """Setup, update and upgrade deb packages."""
-    if not no_repositories:
-        distro = run('lsb_release', '-cs').strip()
-        # APT repository update.
-        for reposirory in APT_REPOSITORIES:
-            assume_yes = None if distro == 'lucid' else '-y'
-            call('add-apt-repository', assume_yes,
-                 reposirory.format(distro=distro))
-    call('apt-get', 'update')
-    # Install base and Launchpad deb packages.
-    apt_get_install(*LP_PACKAGES, LC_ALL='C', caller=call)
 
 
 def setup_launchpad(user, dependencies_dir, directory, valid_ssh_keys):
@@ -232,87 +137,16 @@
         file_append(HOSTS_FILE, line)
 
 
-class SubCommand(argparser.StepsBasedSubCommand):
+class SubCommand(inithost.SubCommand):
     """Install the Launchpad environment."""
 
+    # The steps for "install" are a superset of the steps for "inithost".
     steps = (
-        (initialize,
-         'user', 'full_name', 'email', 'lpuser',
-         'private_key', 'public_key', 'valid_ssh_keys', 'ssh_key_path',
-         'feed_random', 'dependencies_dir', 'directory'),
-        (setup_bzr_locations,
-         'user', 'lpuser', 'directory'),
-        (setup_apt,
-         'no_repositories'),
+        inithost.SubCommand.initialize_step,
+        (fetch,
+         'user', 'directory', 'dependencies_dir', 'valid_ssh_keys'),
+        inithost.SubCommand.setup_bzr_locations_step,
+        inithost.SubCommand.setup_apt_step,
         (setup_launchpad,
          'user', 'dependencies_dir', 'directory', 'valid_ssh_keys'),
         )
-    help = __doc__
-    needs_root = True
-    ssh_key_name_help = 'The ssh key name used to connect to Launchpad.'
-    validators = (
-        handlers.handle_user,
-        handlers.handle_lpuser,
-        handlers.handle_userdata,
-        handlers.handle_ssh_keys,
-        handlers.handle_directories,
-        )
-
-    def add_arguments(self, parser):
-        super(SubCommand, self).add_arguments(parser)
-        parser.add_argument(
-            '-u', '--user',
-            help='The name of the system user to be created or updated. '
-                 'The current user is used if this script is not run as '
-                 'root and this argument is omitted.')
-        parser.add_argument(
-            '-e', '--email',
-            help='The email of the user, used for bzr whoami. This argument '
-                 'can be omitted if a bzr id exists for current user.')
-        parser.add_argument(
-            '-f', '--full-name',
-            help='The full name of the user, used for bzr whoami. '
-                 'This argument can be omitted if a bzr id exists for '
-                 'current user.')
-        parser.add_argument(
-            '-l', '--lpuser',
-            help='The name of the Launchpad user that will be used to '
-                 'check out dependencies. If not provided, the system '
-                 'user name is used.')
-        parser.add_argument(
-            '-v', '--private-key',
-            help='The SSH private key for the Launchpad user (without '
-                 'passphrase). If this argument is omitted and a keypair is '
-                 'not found in the home directory of the system user a new '
-                 'SSH keypair will be generated and the checkout of the '
-                 'Launchpad code will use HTTP rather than bzr+ssh.')
-        parser.add_argument(
-            '-b', '--public-key',
-            help='The SSH public key for the Launchpad user. '
-                 'If this argument is omitted and a keypair is not found '
-                 'in the home directory of the system user a new SSH '
-                 'keypair will be generated and the checkout of the '
-                 'Launchpad code will use HTTP rather than bzr+ssh.')
-        parser.add_argument(
-            '-d', '--dependencies-dir', default=DEPENDENCIES_DIR,
-            help='The directory of the Launchpad dependencies to be created. '
-                 'The directory must reside under the home directory of the '
-                 'given user (see -u argument). '
-                 '[DEFAULT={0}]'.format(DEPENDENCIES_DIR))
-        parser.add_argument(
-            '-c', '--directory', default=CHECKOUT_DIR,
-            help='The directory of the Launchpad repository to be created. '
-                 'The directory must reside under the home directory of the '
-                 'given user (see -u argument). '
-                 '[DEFAULT={0}]'.format(CHECKOUT_DIR))
-        parser.add_argument(
-            '-S', '--ssh-key-name', default=SSH_KEY_NAME,
-            help='{0} [DEFAULT={1}]'.format(
-                self.ssh_key_name_help, SSH_KEY_NAME))
-        parser.add_argument(
-            '-N', '--no-repositories', action='store_true',
-            help='Do not add APT repositories.')
-        parser.add_argument(
-            '--feed-random', action='store_true',
-            help='Use haveged to maintain a pool of random bytes used to '
-                 'fill /dev/random and avoid entropy exhaustion.')

=== modified file 'lpsetup/subcommands/lxcinstall.py'
--- lpsetup/subcommands/lxcinstall.py	2012-05-22 09:58:51 +0000
+++ lpsetup/subcommands/lxcinstall.py	2012-06-21 15:45:26 +0000
@@ -42,7 +42,7 @@
     LXC_PACKAGES,
     SCRIPTS,
     )
-from lpsetup.subcommands import install
+from lpsetup.subcommands import inithost
 from lpsetup.utils import (
     call,
     get_container_path,
@@ -198,15 +198,15 @@
         subprocess.call(['lxc-stop', '-n', lxc_name])
 
 
-class SubCommand(install.SubCommand):
+class SubCommand(inithost.SubCommand):
     """Install the Launchpad environment inside an LXC."""
 
     steps = (
-        (install.initialize,
+        (inithost.initialize,
          'user', 'full_name', 'email', 'lpuser',
          'private_key', 'public_key', 'valid_ssh_keys', 'ssh_key_path',
          'feed_random', 'dependencies_dir', 'directory'),
-        (install.setup_bzr_locations,
+        (inithost.setup_bzr_locations,
          'user', 'lpuser', 'directory'),
         (create_scripts,
          'user', 'lxc_name', 'ssh_key_path'),