yellow team mailing list archive
-
yellow team
-
Mailing list archive
-
Message #00602
[Merge] lp:~frankban/lpsetup/ssh-keys into lp:lpsetup
Francesco Banconi has proposed merging lp:~frankban/lpsetup/ssh-keys into lp:lpsetup.
Requested reviews:
Launchpad Yellow Squad (yellow)
For more details, see:
https://code.launchpad.net/~frankban/lpsetup/ssh-keys/+merge/98175
== Changes ==
- Updated the install and lxc-install subcommans to support a custom ssh key name.
- The root ssh key is no longer needed, so it is not created.
--
https://code.launchpad.net/~frankban/lpsetup/ssh-keys/+merge/98175
Your team Launchpad Yellow Squad is requested to review the proposed merge of lp:~frankban/lpsetup/ssh-keys into lp:lpsetup.
=== modified file 'lpsetup/handlers.py'
--- lpsetup/handlers.py 2012-03-16 10:27:22 +0000
+++ lpsetup/handlers.py 2012-03-19 10:05:30 +0000
@@ -139,7 +139,8 @@
>>> private = r'PRIVATE\nKEY'
>>> public = r'PUBLIC\nKEY'
>>> namespace = argparse.Namespace(
- ... private_key=private, public_key=public)
+ ... private_key=private, public_key=public,
+ ... ssh_key_name='id_rsa', home_dir='/tmp/')
>>> handle_ssh_keys(namespace)
>>> namespace.private_key == private.decode('string-escape')
True
@@ -148,11 +149,18 @@
>>> namespace.valid_ssh_keys
True
+ After this handler is called, the ssh key path is present as an attribute
+ of the namespace::
+
+ >>> namespace.ssh_key_path
+ '/tmp/.ssh/id_rsa'
+
Keys are None if they are not provided and can not be found in the
current home directory::
>>> namespace = argparse.Namespace(
- ... private_key=None, home_dir='/tmp/__does_not_exist__')
+ ... private_key=None, public_key=None, ssh_key_name='id_rsa',
+ ... home_dir='/tmp/__does_not_exists__')
>>> handle_ssh_keys(namespace) # doctest: +ELLIPSIS
>>> print namespace.private_key
None
@@ -165,21 +173,22 @@
ValidationError will be raised.
>>> namespace = argparse.Namespace(
- ... private_key=private, public_key=None,
- ... home_dir='/tmp/__does_not_exist__')
+ ... private_key=private, public_key=None, ssh_key_name='id_rsa',
+ ... home_dir='/tmp/__does_not_exists__')
>>> handle_ssh_keys(namespace) # doctest: +ELLIPSIS
Traceback (most recent call last):
ValidationError: arguments private-key...
"""
namespace.valid_ssh_keys = True
- for attr, filename in (
- ('private_key', 'id_rsa'),
- ('public_key', 'id_rsa.pub')):
+ namespace.ssh_key_path = os.path.join(
+ namespace.home_dir, '.ssh', namespace.ssh_key_name)
+ for attr, path in (
+ ('private_key', namespace.ssh_key_path),
+ ('public_key', namespace.ssh_key_path + '.pub')):
value = getattr(namespace, attr, None)
if value:
setattr(namespace, attr, value.decode('string-escape'))
else:
- path = os.path.join(namespace.home_dir, '.ssh', filename)
try:
value = open(path).read()
except IOError:
=== modified file 'lpsetup/settings.py'
--- lpsetup/settings.py 2012-03-16 14:49:20 +0000
+++ lpsetup/settings.py 2012-03-19 10:05:30 +0000
@@ -69,3 +69,4 @@
LXC_PACKAGES = ['lxc', 'libvirt-bin']
LXC_PATH = '/var/lib/lxc/'
RESOLV_FILE = '/etc/resolv.conf'
+SSH_KEY_NAME = 'id_rsa'
=== modified file 'lpsetup/subcommands/install.py'
--- lpsetup/subcommands/install.py 2012-03-16 14:49:20 +0000
+++ lpsetup/subcommands/install.py 2012-03-19 10:05:30 +0000
@@ -50,6 +50,7 @@
LP_PACKAGES,
LP_REPOS,
LP_SOURCE_DEPS,
+ SSH_KEY_NAME,
)
from lpsetup.utils import call
@@ -111,10 +112,9 @@
call('make', '-C', checkout_dir, 'install')
-
def initialize(
user, full_name, email, lpuser, private_key, public_key, valid_ssh_keys,
- dependencies_dir, directory):
+ ssh_key_path, dependencies_dir, directory):
"""Initialize host machine."""
# Install necessary deb packages. This requires Oneiric or later.
call('apt-get', 'update')
@@ -122,35 +122,31 @@
# Create the user (if he does not exist).
if not user_exists(user):
call('useradd', '-m', '-s', '/bin/bash', '-U', user)
- # Generate root ssh keys if they do not exist.
- if not os.path.exists('/root/.ssh/id_rsa.pub'):
- generate_ssh_keys('/root/.ssh/')
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_dir)
- private_key = open(os.path.join(ssh_dir, 'id_rsa')).read()
- public_key = open(os.path.join(ssh_dir, 'id_rsa.pub')).read()
- priv_file = os.path.join(ssh_dir, 'id_rsa')
- pub_file = os.path.join(ssh_dir, 'id_rsa.pub')
+ 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 = run(
- 'ssh-keyscan', '-t', 'rsa', 'bazaar.launchpad.net')
+ known_host_content = subprocess.check_output([
+ 'ssh-keyscan', '-t', 'rsa', 'bazaar.launchpad.net'])
for filename, contents, mode in [
- (priv_file, private_key, 'w'),
- (pub_file, public_key, 'w'),
+ (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(priv_file, 0600)
+ os.chmod(ssh_key_path, 0600)
# Set up bzr and Launchpad authentication.
call('bzr', 'whoami', formataddr([full_name, email]))
if valid_ssh_keys:
@@ -220,14 +216,17 @@
actions = (
(initialize,
- 'user', 'full_name', 'email', 'lpuser', 'private_key',
- 'public_key', 'valid_ssh_keys', 'dependencies_dir', 'directory'),
- (setup_apt, 'no_repositories'),
+ 'user', 'full_name', 'email', 'lpuser',
+ 'private_key', 'public_key', 'valid_ssh_keys', 'ssh_key_path',
+ 'dependencies_dir', 'directory'),
+ (setup_apt,
+ 'no_repositories'),
(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,
@@ -284,5 +283,9 @@
'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.')
=== modified file 'lpsetup/subcommands/lxcinstall.py'
--- lpsetup/subcommands/lxcinstall.py 2012-03-09 10:14:32 +0000
+++ lpsetup/subcommands/lxcinstall.py 2012-03-19 10:05:30 +0000
@@ -91,8 +91,6 @@
# Set up root ssh key.
user_authorized_keys = os.path.expanduser(
'~' + user + '/.ssh/authorized_keys')
- with open(user_authorized_keys, 'a') as f:
- f.write(open('/root/.ssh/id_rsa.pub').read())
dst = get_container_path(lxc_name, '/root/.ssh/')
mkdirs(dst)
shutil.copy(user_authorized_keys, dst)
@@ -103,9 +101,9 @@
call('lxc-start', '-n', lxc_name, '-d')
-def wait_for_lxc(lxc_name, trials=60, sleep_seconds=1):
+def wait_for_lxc(lxc_name, ssh_key_path, trials=60, sleep_seconds=1):
"""Try to ssh as `user` into the LXC container named `lxc_name`."""
- sshcall = ssh(lxc_name)
+ sshcall = ssh(lxc_name, key=ssh_key_path)
while True:
trials -= 1
try:
@@ -118,19 +116,20 @@
break
-def initialize_lxc(lxc_name, lxc_os):
+def initialize_lxc(lxc_name, lxc_os, ssh_key_path):
"""Initialize LXC container."""
base_packages = list(BASE_PACKAGES)
if lxc_os == 'lucid':
# Install argparse to be able to run this script inside a lucid lxc.
base_packages.append('python-argparse')
- ssh(lxc_name)(
+ sshcall = ssh(lxc_name, key=ssh_key_path)
+ sshcall(
'DEBIAN_FRONTEND=noninteractive '
'apt-get install -y ' + ' '.join(base_packages))
def setup_launchpad_lxc(
- user, dependencies_dir, directory, valid_ssh_keys, lxc_name):
+ user, dependencies_dir, directory, valid_ssh_keys, ssh_key_path, lxc_name):
"""Set up the Launchpad environment inside an LXC."""
# Use ssh to call this script from inside the container.
args = [
@@ -138,12 +137,12 @@
'-d', dependencies_dir, '-c', directory
]
cmd = this_command(directory, args)
- ssh(lxc_name)(cmd)
-
-
-def stop_lxc(lxc_name):
+ ssh(lxc_name, key=ssh_key_path)(cmd)
+
+
+def stop_lxc(lxc_name, ssh_key_path):
"""Stop the lxc instance named `lxc_name`."""
- ssh(lxc_name)('poweroff')
+ ssh(lxc_name, key=ssh_key_path)('poweroff')
if not lxc_stopped(lxc_name):
subprocess.call(['lxc-stop', '-n', lxc_name])
@@ -157,16 +156,21 @@
'public_key', 'valid_ssh_keys', 'dependencies_dir', 'directory'),
(create_lxc,
'user', 'lxc_name', 'lxc_arch', 'lxc_os'),
- (start_lxc, 'lxc_name'),
- (wait_for_lxc, 'lxc_name'),
+ (start_lxc,
+ 'lxc_name'),
+ (wait_for_lxc,
+ 'lxc_name', 'ssh_key_path'),
(initialize_lxc,
- 'lxc_name', 'lxc_os'),
+ 'lxc_name', 'lxc_os', 'ssh_key_path'),
(setup_launchpad_lxc,
- 'user', 'dependencies_dir', 'directory', 'valid_ssh_keys',
- 'lxc_name'),
- (stop_lxc, 'lxc_name'),
+ 'user', 'dependencies_dir', 'directory',
+ 'valid_ssh_keys', 'ssh_key_path', 'lxc_name'),
+ (stop_lxc,
+ 'lxc_name', 'ssh_key_path'),
)
help = __doc__
+ ssh_key_name_help = ('The ssh key name used to connect to Launchpad '
+ 'and to to the LXC container.')
def add_arguments(self, parser):
super(SubCommand, self).add_arguments(parser)
=== modified file 'lpsetup/subcommands/update.py'
--- lpsetup/subcommands/update.py 2012-03-09 10:14:32 +0000
+++ lpsetup/subcommands/update.py 2012-03-19 10:05:30 +0000
@@ -56,7 +56,6 @@
call(cmd, '--parent', dependencies_dir, '--target', branch)
-
class SubCommand(argparser.ActionsBasedSubCommand):
"""Update the Launchpad environment to latest version."""
@@ -64,7 +63,7 @@
(update_launchpad,
'user', 'valid_ssh_keys', 'dependencies_dir', 'directory', 'apt'),
(link_sourcecode_in_branches,
- 'user', 'dependencies_dir', 'directory'),
+ 'user', 'dependencies_dir', 'directory'),
)
help = __doc__
validators = (
=== modified file 'lpsetup/tests/test_handlers.py'
--- lpsetup/tests/test_handlers.py 2012-03-16 14:06:12 +0000
+++ lpsetup/tests/test_handlers.py 2012-03-19 10:05:30 +0000
@@ -101,13 +101,16 @@
class HandleSSHKeysTest(HandlersTestMixin, unittest.TestCase):
+ home_dir = '/tmp/__does_not_exist__'
private = r'PRIVATE\nKEY'
public = r'PUBLIC\nKEY'
+ ssh_key_name = 'id_rsa'
def test_key_escaping(self):
# Ensure the keys contained in the namespace are correctly escaped.
namespace = argparse.Namespace(
- private_key=self.private, public_key=self.public)
+ private_key=self.private, public_key=self.public,
+ ssh_key_name=self.ssh_key_name, home_dir=self.home_dir)
handle_ssh_keys(namespace)
self.assertEqual(
self.private.decode('string-escape'),
@@ -117,11 +120,22 @@
namespace.public_key)
self.assertTrue(namespace.valid_ssh_keys)
+ def test_ssh_key_path_in_namespace(self):
+ # After the handler is called, the ssh key path is present
+ # as an attribute of the namespace.
+ namespace = argparse.Namespace(
+ private_key=self.private, public_key=self.public,
+ ssh_key_name=self.ssh_key_name, home_dir=self.home_dir)
+ handle_ssh_keys(namespace)
+ expected = self.home_dir + '/.ssh/id_rsa'
+ self.assertEqual(expected, namespace.ssh_key_path)
+
def test_no_keys(self):
# Keys are None if they are not provided and can not be found in the
# current home directory.
namespace = argparse.Namespace(
- private_key=None, home_dir='/tmp/__does_not_exist__')
+ private_key=None, ssh_key_name=self.ssh_key_name,
+ home_dir=self.home_dir)
handle_ssh_keys(namespace)
self.assertIsNone(namespace.private_key)
self.assertIsNone(namespace.public_key)
@@ -131,7 +145,7 @@
# Ensure a `ValidationError` is raised if only one key is provided.
namespace = argparse.Namespace(
private_key=self.private, public_key=None,
- home_dir='/tmp/__does_not_exist__')
+ ssh_key_name=self.ssh_key_name, home_dir=self.home_dir)
with self.assertNotValid('private-key'):
handle_ssh_keys(namespace)