Merge lp:~bac/lpsetup/lxc-integration into lp:lpsetup

Proposed by Brad Crittenden
Status: Merged
Approved by: Brad Crittenden
Approved revision: 81
Merged at revision: 57
Proposed branch: lp:~bac/lpsetup/lxc-integration
Merge into: lp:lpsetup
Diff against target: 876 lines (+368/-171)
15 files modified
README.rst (+6/-2)
commands.rst (+4/-4)
lpsetup/handlers.py (+13/-7)
lpsetup/settings.py (+0/-3)
lpsetup/subcommands/finish_inithost.py (+18/-11)
lpsetup/subcommands/initrepo.py (+24/-12)
lpsetup/subcommands/install_lxc.py (+13/-10)
lpsetup/subcommands/update.py (+32/-22)
lpsetup/tests/integration/common.py (+63/-0)
lpsetup/tests/integration/lxc.py (+106/-0)
lpsetup/tests/integration/non-lxc.py (+72/-89)
lpsetup/tests/subcommands/test_finish_inithost.py (+4/-2)
lpsetup/tests/subcommands/test_install_lxc.py (+3/-1)
lpsetup/tests/subcommands/test_update.py (+8/-6)
pre-commit.sh (+2/-2)
To merge this branch: bzr merge lp:~bac/lpsetup/lxc-integration
Reviewer Review Type Date Requested Status
Benji York (community) code Approve
Review via email: mp+115804@code.launchpad.net

Commit message

Add lxc-based integration test. Fix some idepotent problems.

Description of the change

Add lxc-based integration test.

Discovered a problem with the handling of some command-line options
(working_dir and code_dir) which required some rejiggering. They were
deleted in favor of specifying the repository and checkout-name
separately.

Refactored the test non-lxc.py in order to share code and structure
with lxc.py.

Fun with bzrlib. In retrospect the win over just shelling out to
'bzr' was miniscule.

To post a comment you must log in.
Revision history for this message
Brad Crittenden (bac) wrote :

The LXC-base test can re-use an existing launchpad repository (~/launchpad-testing) if it exists, which speeds things up considerably. With an existing repository the test took 9m13.

Revision history for this message
Benji York (benji) wrote :

The branch looks good, there were a couple of small issues that need to
be addressed and then it will be ready to land.

In lpsetup/tests/integration/lxc.py there are some multi-line imports
that can be single-line instead (on and about line 518 of the diff).

In lpsetup/tests/integration/common.py the time module is imported but
not used and there should be two blank lines before the definition of
IntegrationTestBase.

Also, I /think/ the "help" parameter to the first add_argument in
IntegrationTestBase exceeds our prescribed line width.

As we discussed on the call, I'm not happy with the new OOP and
subclassing -- especially since there is more-or-less no state managed
by the object -- the modules themselves would have been sufficient
organization. That being said, it's not the end of the world.

We also need to get the new non-LXC test to use Juju so the test is run
in an isolated and throw-away environment. We decided that landing the
branch as-is and following up with Juju-ification would be fine.

review: Approve (code)
Revision history for this message
Launchpad QA Bot (lpqabot) wrote :

There are additional revisions which have not been approved in review. Please seek review and approval of these new revisions.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'README.rst'
--- README.rst 2012-07-11 16:33:16 +0000
+++ README.rst 2012-07-19 21:00:25 +0000
@@ -59,11 +59,15 @@
59Integration tests can be found in lpsetup/tests/integration. They59Integration tests can be found in lpsetup/tests/integration. They
60require the "ubuntu" juju charm which, at the time of this writing, is60require the "ubuntu" juju charm which, at the time of this writing, is
61not available in the charm store. Check it out into the local juju61not available in the charm store. Check it out into the local juju
62charm repository from lp:~charmers/charms/precise/ubuntu/trunk.62charm repository from lp:~charmers/charms/precise/ubuntu/trunk. The
63test expects your Juju repository to be at ~/juju-charms
6364
64The integration tests use a juju environment named "lpsetup-testing" so65The integration tests use a juju environment named "lpsetup-testing" so
65you must create an appropriately configured juju environment with that66you must create an appropriately configured juju environment with that
66name before running the tests.67name before running the tests in ~/.juju/environments.yaml.
68
69The tests bootstrap the environment for you and fail if it is already
70running.
6771
68You may also want to add this to your SSH .config file to suppress the72You may also want to add this to your SSH .config file to suppress the
69many yes/no prompts:73many yes/no prompts:
7074
=== modified file 'commands.rst'
--- commands.rst 2012-07-10 19:06:12 +0000
+++ commands.rst 2012-07-19 21:00:25 +0000
@@ -51,13 +51,13 @@
51Completely sets up an LXC environment with Launchpad using the sandbox51Completely sets up an LXC environment with Launchpad using the sandbox
52development model:52development model:
5353
54~/launchpad54~/launchpad/lp-branches
55 bzr repository with --no-trees option.55 bzr repository with --no-trees option.
56~/launchpad/devel56~/launchpad/lp-branches/devel
57 branch with no trees of trunk.57 branch with no trees of trunk.
58~/launchpad/sandbox58~/launchpad/lp-branches/sandbox
59 lightweight checkout with a tree of ../devel.59 lightweight checkout with a tree of ../devel.
60~/launchpad/bug-xyz60~/launchpad/lp-branches/bug-xyz
61 branch with no trees of trunk where bug work is done via switching61 branch with no trees of trunk where bug work is done via switching
62 inside sandbox.62 inside sandbox.
6363
6464
=== modified file 'lpsetup/handlers.py'
--- lpsetup/handlers.py 2012-07-16 16:43:34 +0000
+++ lpsetup/handlers.py 2012-07-19 21:00:25 +0000
@@ -6,6 +6,7 @@
66
7__metaclass__ = type7__metaclass__ = type
8__all__ = [8__all__ = [
9 'handle_code_dir',
9 'handle_directories',10 'handle_directories',
10 'handle_lpuser_as_username',11 'handle_lpuser_as_username',
11 'handle_lpuser_from_lplogin',12 'handle_lpuser_from_lplogin',
@@ -221,13 +222,13 @@
221222
222223
223def handle_directories(namespace):224def handle_directories(namespace):
224 """Handle repository, code_dir, and dependencies directories.225 """Handle repository and dependencies directories.
225226
226 - The ~ construction is automatically expanded.227 - The ~ construction is automatically expanded.
227 - The validation fails for directories not residing inside the home.228 - The validation fails for directories not residing inside the home.
228 - The validation fails if the directory contains spaces.229 - The validation fails if the directory contains spaces.
229 """230 """
230 for attr in ('repository', 'code_dir', 'dependencies_dir'):231 for attr in ('repository', 'dependencies_dir'):
231 directory = getattr(namespace, attr, None)232 directory = getattr(namespace, attr, None)
232 if directory is None:233 if directory is None:
233 continue234 continue
@@ -285,11 +286,6 @@
285 return os.path.abspath(os.path.expanduser(path))286 return os.path.abspath(os.path.expanduser(path))
286287
287288
288def handle_working_dir(namespace):
289 """Handle path to the working directory."""
290 namespace.working_dir = normalize_path(namespace.working_dir)
291
292
293def handle_branch_and_checkout(namespace):289def handle_branch_and_checkout(namespace):
294 """Handle branch and checkout names.290 """Handle branch and checkout names.
295291
@@ -303,6 +299,7 @@
303299
304 - branch_name300 - branch_name
305 - checkout_name301 - checkout_name
302 - repository
306303
307 This handler does not modify the namespace.304 This handler does not modify the namespace.
308 """305 """
@@ -318,3 +315,12 @@
318 raise ValidationError(315 raise ValidationError(
319 'branch and checkout: can not use the same name ({0}).'.format(316 'branch and checkout: can not use the same name ({0}).'.format(
320 checkout_name))317 checkout_name))
318
319
320def handle_code_dir(namespace):
321 """Handle the computed value for `code_dir`.
322
323 It must be invoked after handle_directories.
324 """
325 namespace.code_dir = os.path.join(
326 namespace.repository, namespace.checkout_name)
321327
=== modified file 'lpsetup/settings.py'
--- lpsetup/settings.py 2012-07-17 17:55:38 +0000
+++ lpsetup/settings.py 2012-07-19 21:00:25 +0000
@@ -3,8 +3,6 @@
3# GNU Affero General Public License version 3 (see the file LICENSE).3# GNU Affero General Public License version 3 (see the file LICENSE).
44
5"""Global settings and defaults for lpsetup."""5"""Global settings and defaults for lpsetup."""
6import os.path
7
86
9APT_REPOSITORIES = (7APT_REPOSITORIES = (
10 'deb http://archive.ubuntu.com/ubuntu {distro} multiverse',8 'deb http://archive.ubuntu.com/ubuntu {distro} multiverse',
@@ -56,7 +54,6 @@
56LP_BRANCH_NAME = 'devel'54LP_BRANCH_NAME = 'devel'
57LP_CHECKOUT_NAME = 'sandbox'55LP_CHECKOUT_NAME = 'sandbox'
58LP_REPOSITORY_DIR = '~/launchpad/lp-branches'56LP_REPOSITORY_DIR = '~/launchpad/lp-branches'
59LP_CODE_DIR = os.path.join(LP_REPOSITORY_DIR, LP_CHECKOUT_NAME)
60LP_PACKAGES = [57LP_PACKAGES = [
61 # "launchpad-database-dependencies-9.1" can be removed once 8.x is58 # "launchpad-database-dependencies-9.1" can be removed once 8.x is
62 # no longer an option in "launchpad-developer-dependencies.59 # no longer an option in "launchpad-developer-dependencies.
6360
=== modified file 'lpsetup/subcommands/finish_inithost.py'
--- lpsetup/subcommands/finish_inithost.py 2012-07-16 08:17:05 +0000
+++ lpsetup/subcommands/finish_inithost.py 2012-07-19 21:00:25 +0000
@@ -29,10 +29,15 @@
2929
30from lpsetup import argparser30from lpsetup import argparser
31from lpsetup.handlers import (31from lpsetup.handlers import (
32 handle_code_dir,
32 handle_directories,33 handle_directories,
33 handle_user,34 handle_user,
34 )35 )
35from lpsetup.settings import LP_CODE_DIR36from lpsetup.settings import (
37 LP_CHECKOUT_NAME,
38 LP_REPOSITORY_DIR,
39 )
40
36from lpsetup.utils import call41from lpsetup.utils import call
3742
3843
@@ -75,22 +80,24 @@
75 handlers = (80 handlers = (
76 handle_user,81 handle_user,
77 handle_directories,82 handle_directories,
83 handle_code_dir,
78 )84 )
7985
80 @staticmethod
81 def add_common_arguments(parser):
82 parser.add_argument(
83 '-c', '--code-dir', default=LP_CODE_DIR,
84 help='The directory of the Launchpad code checkout. '
85 'The directory must reside under the home directory of the '
86 'given user (see -u argument). '
87 '[DEFAULT={0}]'.format(LP_CODE_DIR))
88
89 def add_arguments(self, parser):86 def add_arguments(self, parser):
90 super(SubCommand, self).add_arguments(parser)87 super(SubCommand, self).add_arguments(parser)
91 self.add_common_arguments(parser)
92 parser.add_argument(88 parser.add_argument(
93 '-u', '--user',89 '-u', '--user',
94 help='The name of the system user. '90 help='The name of the system user. '
95 'The current user is used if this script is not run as '91 'The current user is used if this script is not run as '
96 'root and this argument is omitted.')92 'root and this argument is omitted.')
93 parser.add_argument(
94 '--checkout-name', default=LP_CHECKOUT_NAME,
95 help='Create a checkout with the given name. '
96 'Ignored if --no-checkout is specified. '
97 'Defaults to {0}.'.format(LP_CHECKOUT_NAME))
98 parser.add_argument(
99 '-r', '--repository', default=LP_REPOSITORY_DIR,
100 help='The directory of the Launchpad repository to be created. '
101 'The directory must reside under the home directory of the '
102 'given user (see -u argument). '
103 '[DEFAULT={0}]'.format(LP_REPOSITORY_DIR))
97104
=== modified file 'lpsetup/subcommands/initrepo.py'
--- lpsetup/subcommands/initrepo.py 2012-07-16 10:09:48 +0000
+++ lpsetup/subcommands/initrepo.py 2012-07-19 21:00:25 +0000
@@ -49,27 +49,39 @@
49 is_lightweight = not no_checkout49 is_lightweight = not no_checkout
50 no_trees = '--no-trees' if is_lightweight else None50 no_trees = '--no-trees' if is_lightweight else None
51 # Initialize the repository.51 # Initialize the repository.
52 try:52
53 call('bzr', 'init-repo', '--quiet', repository, no_trees)53 # Skip creating the repository if it already exists. There is the
54 except subprocess.CalledProcessError as err:54 # risk that it is not made with the same `no_trees` option as
55 msg = 'Error: unable to initialize the repository: '55 # specified here.
56 raise exceptions.ExecutionError(msg + err.output)56 if not os.path.exists(os.path.join(repository, '.bzr')):
57 try:
58 call('bzr', 'init-repo', '--quiet', repository, no_trees)
59 except subprocess.CalledProcessError as err:
60 msg = 'Error: unable to initialize the repository: '
61 raise exceptions.ExecutionError(msg + err.output)
57 # Set up the codebase.62 # Set up the codebase.
58 branch_dir = os.path.join(repository, branch_name)63 branch_dir = os.path.join(repository, branch_name)
59 # Retrieve the branch.64 # Retrieve the branch.
65 if os.path.exists(branch_dir):
66 # The branch already exists, so we pull to refresh it rather than
67 # creating it.
68 cmd = ['bzr', 'pull', '--overwrite', '-d', branch_dir, source]
69 else:
70 cmd = ['bzr', 'branch', source, branch_dir]
60 try:71 try:
61 call('bzr', 'branch', source, branch_dir)72 call(*cmd)
62 except subprocess.CalledProcessError as err:73 except subprocess.CalledProcessError as err:
63 msg = 'Error: unable to branch source: '74 msg = 'Error: unable to branch source: '
64 raise exceptions.ExecutionError(msg + err.output)75 raise exceptions.ExecutionError(msg + err.output)
65 if is_lightweight:76 if is_lightweight:
66 checkout_dir = os.path.join(repository, checkout_name)77 checkout_dir = os.path.join(repository, checkout_name)
67 # Create a lightweight checkout.78 # Create a lightweight checkout if it doesn't exist.
68 try:79 if not os.path.exists(checkout_dir):
69 call('bzr', 'co', '--lightweight', branch_dir, checkout_dir)80 try:
70 except subprocess.CalledProcessError as err:81 call('bzr', 'co', '--lightweight', branch_dir, checkout_dir)
71 msg = 'Error: unable to create the lightweight checkout: '82 except subprocess.CalledProcessError as err:
72 raise exceptions.ExecutionError(msg + err.output)83 msg = 'Error: unable to create the lightweight checkout: '
84 raise exceptions.ExecutionError(msg + err.output)
7385
7486
75def setup_bzr_locations(87def setup_bzr_locations(
7688
=== modified file 'lpsetup/subcommands/install_lxc.py'
--- lpsetup/subcommands/install_lxc.py 2012-07-13 14:51:08 +0000
+++ lpsetup/subcommands/install_lxc.py 2012-07-19 21:00:25 +0000
@@ -13,6 +13,7 @@
13import os13import os
1414
15from lpsetup.handlers import (15from lpsetup.handlers import (
16 handle_code_dir,
16 handle_directories,17 handle_directories,
17 handle_source,18 handle_source,
18 )19 )
@@ -21,7 +22,6 @@
21 SCRIPTS,22 SCRIPTS,
22 )23 )
23from lpsetup.subcommands import (24from lpsetup.subcommands import (
24 finish_inithost,
25 initlxc,25 initlxc,
26 initrepo,26 initrepo,
27 update,27 update,
@@ -69,11 +69,9 @@
6969
7070
71def cmd_in_lxc(lxc_name, ssh_key_path, home_dir, args, as_user=None):71def cmd_in_lxc(lxc_name, ssh_key_path, home_dir, args, as_user=None):
72 print "args = ", args
73 cmd = this_command(home_dir, args)72 cmd = this_command(home_dir, args)
74 if as_user is not None:73 if as_user is not None:
75 cmd = "su {} -c '{}'".format(as_user, cmd)74 cmd = "su {} -c '{}'".format(as_user, cmd)
76 print "CMD = ", cmd
77 ssh(lxc_name, cmd, key=ssh_key_path)75 ssh(lxc_name, cmd, key=ssh_key_path)
7876
7977
@@ -91,18 +89,20 @@
9189
92def update_in_lxc(lxc_name, ssh_key_path, home_dir, user, external_path,90def update_in_lxc(lxc_name, ssh_key_path, home_dir, user, external_path,
93 use_http, checkout_name, repository):91 use_http, checkout_name, repository):
94 working_dir = os.path.join(repository, checkout_name)
95 args = [92 args = [
96 'update', '--external-path', external_path, '-W', working_dir,93 'update', '--external-path', external_path,
94 '-r', repository, '--checkout-name', checkout_name,
97 ]95 ]
98 if use_http:96 if use_http:
99 args.append('--use-http')97 args.append('--use-http')
100 cmd_in_lxc(lxc_name, ssh_key_path, home_dir, args, as_user=user)98 cmd_in_lxc(lxc_name, ssh_key_path, home_dir, args, as_user=user)
10199
102100
103def finish_inithost_in_lxc(lxc_name, ssh_key_path, home_dir, user, code_dir):101def finish_inithost_in_lxc(lxc_name, ssh_key_path, home_dir, user,
102 repository, checkout_name):
104 args = [103 args = [
105 'finish-init-host', '--user', user, '--code-dir', code_dir,104 'finish-init-host', '--user', user, '--repository', repository,
105 '--checkout-name', checkout_name,
106 ]106 ]
107 cmd_in_lxc(lxc_name, ssh_key_path, home_dir, args)107 cmd_in_lxc(lxc_name, ssh_key_path, home_dir, args)
108108
@@ -121,14 +121,18 @@
121 (update_in_lxc, 'lxc_name', 'ssh_key_path', 'home_dir', 'user',121 (update_in_lxc, 'lxc_name', 'ssh_key_path', 'home_dir', 'user',
122 'external_path', 'use_http', 'checkout_name', 'repository'),122 'external_path', 'use_http', 'checkout_name', 'repository'),
123 (finish_inithost_in_lxc, 'lxc_name', 'ssh_key_path', 'home_dir',123 (finish_inithost_in_lxc, 'lxc_name', 'ssh_key_path', 'home_dir',
124 'user', 'code_dir'),124 'user', 'repository', 'checkout_name'),
125 )125 )
126126
127 help = __doc__127 help = __doc__
128128
129 def get_handlers(self, namespace):129 def get_handlers(self, namespace):
130 handlers = super(SubCommand, self).get_handlers(namespace)130 handlers = super(SubCommand, self).get_handlers(namespace)
131 return handlers + (handle_directories, handle_source)131 return handlers + (
132 handle_directories,
133 handle_code_dir,
134 handle_source,
135 )
132136
133 def call_create_scripts(self, namespace, step, args):137 def call_create_scripts(self, namespace, step, args):
134 """Run the `create_scripts` step only if the related flag is set."""138 """Run the `create_scripts` step only if the related flag is set."""
@@ -140,7 +144,6 @@
140 # Inherit arguments from subcommands we depend upon.144 # Inherit arguments from subcommands we depend upon.
141 initrepo.SubCommand.add_common_arguments(parser)145 initrepo.SubCommand.add_common_arguments(parser)
142 update.SubCommand.add_common_arguments(parser)146 update.SubCommand.add_common_arguments(parser)
143 finish_inithost.SubCommand.add_common_arguments(parser)
144 # Add parallel testing related arguments.147 # Add parallel testing related arguments.
145 parser.add_argument(148 parser.add_argument(
146 '-C', '--create-scripts', action='store_true',149 '-C', '--create-scripts', action='store_true',
147150
=== modified file 'lpsetup/subcommands/update.py'
--- lpsetup/subcommands/update.py 2012-07-13 15:55:40 +0000
+++ lpsetup/subcommands/update.py 2012-07-19 21:00:25 +0000
@@ -19,48 +19,49 @@
19from lpsetup import argparser19from lpsetup import argparser
20from lpsetup import handlers20from lpsetup import handlers
21from lpsetup.settings import (21from lpsetup.settings import (
22 LP_CODE_DIR,22 LP_CHECKOUT_NAME,
23 LP_REPOSITORY_DIR,
23 LP_SOURCE_DEPS,24 LP_SOURCE_DEPS,
24 )25 )
2526
2627
27def initialize_directories(working_dir, external_path):28def initialize_directories(code_dir, external_path):
28 """Initialize the eggs, yui, and sourcecode directories.29 """Initialize the eggs, yui, and sourcecode directories.
2930
30 Create them if necessary.31 Create them if necessary.
31 """32 """
32 for dir_ in ['eggs', 'yui', 'sourcecode']:33 for dir_ in ['eggs', 'yui', 'sourcecode']:
33 mkdirs(os.path.join(working_dir, external_path, dir_))34 mkdirs(os.path.join(code_dir, external_path, dir_))
3435
3536
36def update_dependencies(working_dir, external_path, use_http):37def update_dependencies(code_dir, external_path, use_http):
37 """Update the external dependencies."""38 """Update the external dependencies."""
38 use_http_param = '--use-http' if use_http else None39 use_http_param = '--use-http' if use_http else None
39 cmd = os.path.join(working_dir, 'utilities', 'update-sourcecode')40 cmd = os.path.join(code_dir, 'utilities', 'update-sourcecode')
40 abs_external_path = os.path.abspath(41 abs_external_path = os.path.abspath(
41 os.path.join(working_dir, external_path))42 os.path.join(code_dir, external_path))
42 source_path = os.path.join(abs_external_path, 'sourcecode')43 source_path = os.path.join(abs_external_path, 'sourcecode')
43 run(cmd, use_http_param, source_path)44 run(cmd, use_http_param, source_path)
4445
45 # Update the download cache.46 # Update the download cache.
46 download_cache = os.path.join(working_dir, 'download-cache')47 download_cache = os.path.join(code_dir, 'download-cache')
47 if os.path.exists(download_cache):48 if os.path.exists(download_cache):
48 run('bzr', 'up', download_cache)49 run('bzr', 'up', download_cache)
49 else:50 else:
50 run('bzr', 'co', '-v', '--lightweight', LP_SOURCE_DEPS, download_cache)51 run('bzr', 'co', '-v', '--lightweight', LP_SOURCE_DEPS, download_cache)
5152
52 # Link to the external sourcecode.53 # Link to the external sourcecode.
53 if abs_external_path != working_dir:54 if abs_external_path != code_dir:
54 cmd = os.path.join(55 cmd = os.path.join(
55 working_dir, 'utilities', 'link-external-sourcecode')56 code_dir, 'utilities', 'link-external-sourcecode')
56 run(cmd,57 run(cmd,
57 '--target', working_dir,58 '--target', code_dir,
58 '--parent', external_path)59 '--parent', external_path)
5960
6061
61def update_tree(working_dir):62def update_tree(code_dir):
62 """Update the tree at working_dir with the latest LP code."""63 """Update the tree at code_dir with the latest LP code."""
63 with cd(working_dir):64 with cd(code_dir):
64 run('bzr', 'pull')65 run('bzr', 'pull')
6566
6667
@@ -71,14 +72,16 @@
71 """72 """
7273
73 steps = (74 steps = (
74 (initialize_directories, 'working_dir', 'external_path'),75 (initialize_directories, 'code_dir', 'external_path'),
75 (update_dependencies, 'working_dir', 'external_path', 'use_http'),76 (update_dependencies, 'code_dir', 'external_path', 'use_http'),
76 (update_tree, 'working_dir'),77 (update_tree, 'code_dir'),
77 )78 )
78 help = __doc__79 help = __doc__
79 handlers = (80 handlers = (
80 # Normalize paths and default to cwd if none exists.81 # Normalize paths and default to cwd if none exists.
81 handlers.handle_working_dir,82 handlers.handle_user,
83 handlers.handle_directories,
84 handlers.handle_code_dir,
82 )85 )
8386
84 @staticmethod87 @staticmethod
@@ -97,6 +100,13 @@
97 help='Force bzr to use http to get the sourcecode '100 help='Force bzr to use http to get the sourcecode '
98 'branches rather than using bzr+ssh.')101 'branches rather than using bzr+ssh.')
99 parser.add_argument(102 parser.add_argument(
100 '-W', '--working-dir', default=LP_CODE_DIR,103 '--checkout-name', default=LP_CHECKOUT_NAME,
101 help='Path to branch to update. '104 help='Create a checkout with the given name. '
102 '[DEFAULT={0}]'.format(LP_CODE_DIR))105 'Ignored if --no-checkout is specified. '
106 'Defaults to {0}.'.format(LP_CHECKOUT_NAME))
107 parser.add_argument(
108 '-r', '--repository', default=LP_REPOSITORY_DIR,
109 help='The directory of the Launchpad repository to be created. '
110 'The directory must reside under the home directory of the '
111 'given user (see -u argument). '
112 '[DEFAULT={0}]'.format(LP_REPOSITORY_DIR))
103113
=== added file 'lpsetup/tests/integration/common.py'
--- lpsetup/tests/integration/common.py 1970-01-01 00:00:00 +0000
+++ lpsetup/tests/integration/common.py 2012-07-19 21:00:25 +0000
@@ -0,0 +1,63 @@
1#!/usr/bin/env python
2# Copyright 2012 Canonical Ltd. This software is licensed under the
3# GNU Affero General Public License version 3 (see the file LICENSE).
4
5"""Common resources for integration tests."""
6
7__metaclass__ = type
8__all__ = [
9 'IntegrationTestBase',
10 ]
11
12import argparse
13from datetime import datetime
14
15
16class IntegrationTestBase:
17
18 banner_width = 70
19 test_type = None
20
21 def __init__(self):
22 self.parser = argparse.ArgumentParser()
23 self.parser.add_argument(
24 '--no-tear-down', action='store_true', default=False,
25 help='Do not run the tear down method for debugging.')
26 self.args = self.parser.parse_args()
27
28 def banner(self, msg, width=banner_width):
29 print '*' * width
30 print '* ' + msg.ljust(width - 4) + ' *'
31 print '*' * width
32
33 def set_up(self):
34 self.banner(
35 'Setting up the test environment for {}.'.format(self.test_type))
36
37 def tear_down(self):
38 self.banner('Cleaning up.')
39
40 def check_environment(self):
41 self.banner('Checking test environment.')
42
43 def do_test(self):
44 self.banner('Running integration tests for {}.'.format(self.test_type))
45
46 def run(self):
47 start_time = datetime.now()
48 self.check_environment()
49 try:
50 self.set_up()
51 try:
52 self.do_test()
53 except Exception as err:
54 self.banner('Test failed. Sorry.')
55 print err
56 return 1
57 else:
58 self.banner('Test succeeded.')
59 return 0
60 finally:
61 if not self.args.no_tear_down:
62 self.tear_down()
63 print "Run time: ", datetime.now() - start_time
064
=== added file 'lpsetup/tests/integration/lxc.py'
--- lpsetup/tests/integration/lxc.py 1970-01-01 00:00:00 +0000
+++ lpsetup/tests/integration/lxc.py 2012-07-19 21:00:25 +0000
@@ -0,0 +1,106 @@
1#!/usr/bin/env python
2# Copyright 2012 Canonical Ltd. This software is licensed under the
3# GNU Affero General Public License version 3 (see the file LICENSE).
4
5"""A simple, end-to-end integration test."""
6
7from bzrlib.branch import Branch
8from bzrlib.errors import NotBranchError
9from bzrlib.missing import find_unmerged
10from bzrlib.status import show_tree_status
11from bzrlib.workingtree import WorkingTree
12from StringIO import StringIO
13from subprocess import check_call
14import sys
15
16from shelltoolbox import run
17
18from common import IntegrationTestBase
19
20
21LXC_NAME = 'lpsetup-testing'
22
23
24class LXCIntegrationTest(IntegrationTestBase):
25
26 test_type = 'LXC container'
27 repo = '~/launchpad-testing/lp-branches'
28
29 def get_branch(self, dir='.'):
30 branch, _ = Branch.open_containing(dir)
31 return branch
32
33 def get_push_location(self, branch=None):
34 if branch is None:
35 branch = self.get_branch()
36 return branch.get_push_location()
37
38 def check_environment(self):
39 """Be sure the test environment doesn't exist."""
40 # We want to be really sure we do not clobber an existing LXC
41 # container, therefore we make sure one doesn't exist before
42 # proceeding.
43 super(LXCIntegrationTest, self).check_environment()
44 cmd = 'sudo lxc-info -n {}'.format(LXC_NAME)
45 results = run(*cmd.split())
46 if results.split()[1] == 'RUNNING':
47 # The container should not be active.
48 raise RuntimeError(
49 "An LXC container named '{}' is unexpectedly "
50 "running.".format(LXC_NAME))
51 # Ensure the local branch has been pushed.
52 # For some reason 'bzr missing' cannot use a 'lp:' URL. http or
53 # bzr+ssh URLs work.
54 self.branch = self.get_branch()
55 self.push_location = self.get_push_location(self.branch)
56 if self.push_location.startswith('lp:'):
57 raise RuntimeError(
58 "Push location must use http or bzr+ssh "
59 "protocol: {}".format(self.push_location))
60 # Ensure the branch has been pushed and has no missing revisions.
61 try:
62 push_branch = self.get_branch(self.push_location)
63 except NotBranchError:
64 raise RuntimeError(
65 'The branch has never been pushed to Launchpad.')
66 tree, _ = WorkingTree.open_containing_paths(None)
67 stream = StringIO()
68 show_tree_status(tree, to_file=stream)
69 if stream.getvalue():
70 raise RuntimeError(
71 'The tree has uncommitted changes. Check them in '
72 'and push to Launchpad.')
73 near, far = find_unmerged(self.branch, push_branch)
74 if near or far:
75 raise RuntimeError('The local branch and the pushed version '
76 'have diverged.')
77
78 def set_up(self):
79 """Set up the LXC container environment."""
80 super(LXCIntegrationTest, self).set_up()
81 cmd = 'sudo python setup.py install'
82 check_call(cmd.split())
83
84 def do_test(self):
85 """Run an end-to-end integration tests of the non-LXC lpsetup story."""
86 super(LXCIntegrationTest, self).do_test()
87 cmd = 'lp-setup install-lxc -B {} -r {} {}'.format(
88 self.push_location, self.repo, LXC_NAME)
89 check_call(cmd.split())
90
91 def tear_down(self):
92 super(LXCIntegrationTest, self).tear_down()
93
94 def lxc_cmd(cmd_name):
95 cmd = 'sudo {} -n {}'.format(cmd_name, LXC_NAME)
96 check_call(cmd.split())
97
98 try:
99 lxc_cmd('lxc-stop')
100 lxc_cmd('lxc-destroy')
101 except:
102 pass
103
104
105if __name__ == '__main__':
106 sys.exit(LXCIntegrationTest().run())
0107
=== modified file 'lpsetup/tests/integration/non-lxc.py'
--- lpsetup/tests/integration/non-lxc.py 2012-07-10 19:58:56 +0000
+++ lpsetup/tests/integration/non-lxc.py 2012-07-19 21:00:25 +0000
@@ -14,96 +14,79 @@
14 PIPE,14 PIPE,
15 )15 )
1616
1717from common import IntegrationTestBase
18def banner(s):18
19 width = 7019
20 print '*' * width20class NonLXCIntegrationTest(IntegrationTestBase):
21 print '* ' + s.ljust(width - 4) + ' *'21
22 print '*' * width22 test_type = 'non-LXC target'
2323
2424 def on_remote(self, args):
25def on_remote(args):25 if type(args) == str:
26 if type(args) == str:26 args = args.split()
27 args = args.split()27
2828 check_call('juju ssh 1 -o StrictHostKeyChecking=no'
29 check_call('juju ssh 1 -o StrictHostKeyChecking=no'29 ' -o UserKnownHostsFile=/dev/null -e lpsetup-testing'.split()
30 ' -o UserKnownHostsFile=/dev/null -e lpsetup-testing'.split()30 + list(args))
31 + list(args))31
3232 def check_environment(self):
3333 """Be sure the test environment doesn't exist."""
34def check_environment():34 # We want to be really sure we do not clobber an already-existing juju
35 """Be sure the test environment doesn't exist."""35 # environment. Therefore we make sure one doesn't exist before
36 banner('Checking test environment.')36 # bootstrapping.
37 # We want to be really sure we do not clobber an already-existing juju37 super(NonLXCIntegrationTest, self).check_environment()
38 # environment. Therefore we make sure one doesn't exist before38 code = os.system('juju status -e lpsetup-testing')
39 # bootstrapping.39 if code == 0:
40 code = os.system('juju status -e lpsetup-testing')40 # The "juju status" should have failed.
41 if code == 0:41 raise RuntimeError('A juju environment unexpectedly exists.')
42 # The "juju status" should have failed.42
43 raise RuntimeError('A juju environment unexpectedly exists.')43 def set_up(self):
4444 """Set up a juju-managed instance to run the tests on."""
4545 super(NonLXCIntegrationTest, self).set_up()
46def set_up():46 check_call('juju bootstrap -e lpsetup-testing'.split())
47 """Set up a juju-managed instance to run the tests on."""47 # XXX The "ubuntu" charm is broken, so it has to be loaded from the
48 banner('Setting up the test environment.')48 # local repository. Get it from
49 check_call('juju bootstrap -e lpsetup-testing'.split())49 # lp:~charmers/charms/precise/ubuntu/trunk.
50 # XXX The "ubuntu" charm is broken, so it has to be loaded from the local50 check_call('juju deploy local:ubuntu --repository ~/juju-charms'
51 # repository. Get it from lp:~charmers/charms/precise/ubuntu/trunk.51 ' -e lpsetup-testing --constraints instance-type=m1.large'.split())
52 check_call('juju deploy local:ubuntu --repository ~/juju-charms'52
53 ' -e lpsetup-testing --constraints instance-type=m1.large'.split())53 start_time = time.time()
5454 while time.time() - start_time < 600:
55 start_time = time.time()55 process = Popen(
56 while time.time() - start_time < 600:56 'juju status -e lpsetup-testing'.split(), stdout=PIPE)
57 process = Popen('juju status -e lpsetup-testing'.split(), stdout=PIPE)57 stdout, stderr = process.communicate()
58 stdout, stderr = process.communicate()58 if 'instance-state: running' in stdout:
59 if 'instance-state: running' in stdout:59 break
60 break
61 else:
62 raise RuntimeError('starting the instance took too long')
63
64 # Even though the instance-state is "running", it's still not ready, so
65 # wait a little while before continuing.
66 time.sleep(30)
67
68 on_remote('sudo apt-add-repository --yes ppa:yellow/ppa')
69 on_remote('sudo apt-get update')
70 on_remote('sudo apt-get install python-shelltoolbox')
71 check_call('juju scp -o StrictHostKeyChecking=no'
72 ' -o UserKnownHostsFile=/dev/null -e lpsetup-testing'
73 ' -r . 1:lpsetup'.split())
74
75
76def do_test():
77 """Run an end-to-end integration tests of the non-LXC lpsetup story."""
78 banner('Running (non-LXC) integration test.')
79 # Since the most common scenario is to have bzr whoami setup, we do that
80 # instead of providing the arguments directly to lpsetup.
81 on_remote(('bzr', 'whoami', '"Not A Real Person <no@example.com>"'))
82 on_remote('cd lpsetup; ./lp-setup init-host'.split())
83
84
85def tear_down():
86 banner('Cleaning up.')
87 code = os.system('echo y | juju destroy-environment -e lpsetup-testing')
88 if code != 0:
89 raise RuntimeError('Destroying the test juju environment failed.')
90
91
92def main():
93 check_environment()
94 try:
95 set_up()
96 try:
97 do_test()
98 except:
99 banner('Test failed. Sorry.')
100 return 1
101 else:60 else:
102 banner('Test succeeded.')61 raise RuntimeError('starting the instance took too long')
103 return 062
104 finally:63 # Even though the instance-state is "running", it's still not ready, so
105 tear_down()64 # wait a little while before continuing.
65 time.sleep(30)
66
67 self.on_remote('sudo apt-add-repository --yes ppa:yellow/ppa')
68 self.on_remote('sudo apt-get update')
69 self.on_remote('sudo apt-get install python-shelltoolbox')
70 check_call('juju scp -o StrictHostKeyChecking=no'
71 ' -o UserKnownHostsFile=/dev/null -e lpsetup-testing'
72 ' -r . 1:lpsetup'.split())
73
74 def do_test(self):
75 """Run an end-to-end integration tests of the non-LXC lpsetup story."""
76 # Since the most common scenario is to have bzr whoami setup, we do
77 # that instead of providing the arguments directly to lpsetup.
78 super(NonLXCIntegrationTest, self).do_test()
79 self.on_remote(
80 ('bzr', 'whoami', '"Not A Real Person <no@example.com>"'))
81 self.on_remote('cd lpsetup; ./lp-setup init-host'.split())
82
83 def tear_down(self):
84 super(NonLXCIntegrationTest, self).tear_down()
85 code = os.system(
86 'echo y | juju destroy-environment -e lpsetup-testing')
87 if code != 0:
88 raise RuntimeError('Destroying the test juju environment failed.')
10689
10790
108if __name__ == '__main__':91if __name__ == '__main__':
109 sys.exit(main())92 sys.exit(NonLXCIntegrationTest().run())
11093
=== modified file 'lpsetup/tests/subcommands/test_finish_inithost.py'
--- lpsetup/tests/subcommands/test_finish_inithost.py 2012-07-12 18:23:37 +0000
+++ lpsetup/tests/subcommands/test_finish_inithost.py 2012-07-19 21:00:25 +0000
@@ -19,9 +19,10 @@
1919
2020
21def get_arguments():21def get_arguments():
22 code_dir = '~/' + get_random_string()22 repo = '~/' + get_random_string()
23 checkout = get_random_string()
23 user = get_random_string()24 user = get_random_string()
24 return ('-c', code_dir, '-u', user)25 return ('-r', repo, '--checkout-name', checkout, '-u', user)
2526
2627
27class FinishInitHostTest(StepsBasedSubCommandTestMixin, unittest.TestCase):28class FinishInitHostTest(StepsBasedSubCommandTestMixin, unittest.TestCase):
@@ -31,6 +32,7 @@
31 expected_handlers = (32 expected_handlers = (
32 handlers.handle_user,33 handlers.handle_user,
33 handlers.handle_directories,34 handlers.handle_directories,
35 handlers.handle_code_dir,
34 )36 )
3537
36 @property38 @property
3739
=== modified file 'lpsetup/tests/subcommands/test_install_lxc.py'
--- lpsetup/tests/subcommands/test_install_lxc.py 2012-07-12 22:12:09 +0000
+++ lpsetup/tests/subcommands/test_install_lxc.py 2012-07-19 21:00:25 +0000
@@ -34,7 +34,8 @@
3434
35finish_inithost_in_lxc_step = (35finish_inithost_in_lxc_step = (
36 install_lxc.finish_inithost_in_lxc, [36 install_lxc.finish_inithost_in_lxc, [
37 'lxc_name', 'ssh_key_path', 'home_dir', 'user', 'code_dir',37 'lxc_name', 'ssh_key_path', 'home_dir', 'user', 'repository',
38 'checkout_name',
38 ])39 ])
3940
4041
@@ -52,6 +53,7 @@
52 expected_arguments = get_arguments()53 expected_arguments = get_arguments()
53 expected_handlers = test_initlxc.InitLxcTest.expected_handlers + (54 expected_handlers = test_initlxc.InitLxcTest.expected_handlers + (
54 handlers.handle_directories,55 handlers.handle_directories,
56 handlers.handle_code_dir,
55 handlers.handle_source,57 handlers.handle_source,
56 )58 )
57 expected_steps = test_initlxc.InitLxcTest.expected_steps + (59 expected_steps = test_initlxc.InitLxcTest.expected_steps + (
5860
=== modified file 'lpsetup/tests/subcommands/test_update.py'
--- lpsetup/tests/subcommands/test_update.py 2012-07-13 15:55:40 +0000
+++ lpsetup/tests/subcommands/test_update.py 2012-07-19 21:00:25 +0000
@@ -18,15 +18,15 @@
18 return (18 return (
19 '--external-path', get_random_string(),19 '--external-path', get_random_string(),
20 '--use-http',20 '--use-http',
21 '-e', '../devel',21 '--repository', get_random_string(),
22 '-W', '~/' + get_random_string(),22 '--checkout-name', get_random_string(),
23 )23 )
2424
25init_dir_step = (25init_dir_step = (
26 update.initialize_directories, ['working_dir', 'external_path'])26 update.initialize_directories, ['code_dir', 'external_path'])
27update_dep_step = (27update_dep_step = (
28 update.update_dependencies, ['working_dir', 'external_path', 'use_http'])28 update.update_dependencies, ['code_dir', 'external_path', 'use_http'])
29update_tree_step = (update.update_tree, ['working_dir'])29update_tree_step = (update.update_tree, ['code_dir'])
3030
3131
32class UpdateTest(StepsBasedSubCommandTestMixin, unittest.TestCase):32class UpdateTest(StepsBasedSubCommandTestMixin, unittest.TestCase):
@@ -35,7 +35,9 @@
35 sub_command_class = update.SubCommand35 sub_command_class = update.SubCommand
36 expected_arguments = get_arguments()36 expected_arguments = get_arguments()
37 expected_handlers = (37 expected_handlers = (
38 handlers.handle_working_dir,38 handlers.handle_user,
39 handlers.handle_directories,
40 handlers.handle_code_dir,
39 )41 )
40 expected_steps = (42 expected_steps = (
41 init_dir_step,43 init_dir_step,
4244
=== modified file 'pre-commit.sh'
--- pre-commit.sh 2012-07-11 15:50:01 +0000
+++ pre-commit.sh 2012-07-19 21:00:25 +0000
@@ -1,4 +1,4 @@
1#!/bin/bash1#!/bin/bash
22
3pyfiles=`find . -name "*.py" | grep -v distribute_setup.py`3pyfiles=`find . -name build -prune -o -name "*.py" | grep -v distribute_setup.py`
4pocketlint $pyfiles && pep8 $pyfiles && nosetests4pocketlint $pyfiles && pep8 --exclude=build $pyfiles && nosetests

Subscribers

People subscribed via source and target branches