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
1=== modified file 'README.rst'
2--- README.rst 2012-07-11 16:33:16 +0000
3+++ README.rst 2012-07-19 21:00:25 +0000
4@@ -59,11 +59,15 @@
5 Integration tests can be found in lpsetup/tests/integration. They
6 require the "ubuntu" juju charm which, at the time of this writing, is
7 not available in the charm store. Check it out into the local juju
8-charm repository from lp:~charmers/charms/precise/ubuntu/trunk.
9+charm repository from lp:~charmers/charms/precise/ubuntu/trunk. The
10+test expects your Juju repository to be at ~/juju-charms
11
12 The integration tests use a juju environment named "lpsetup-testing" so
13 you must create an appropriately configured juju environment with that
14-name before running the tests.
15+name before running the tests in ~/.juju/environments.yaml.
16+
17+The tests bootstrap the environment for you and fail if it is already
18+running.
19
20 You may also want to add this to your SSH .config file to suppress the
21 many yes/no prompts:
22
23=== modified file 'commands.rst'
24--- commands.rst 2012-07-10 19:06:12 +0000
25+++ commands.rst 2012-07-19 21:00:25 +0000
26@@ -51,13 +51,13 @@
27 Completely sets up an LXC environment with Launchpad using the sandbox
28 development model:
29
30-~/launchpad
31+~/launchpad/lp-branches
32 bzr repository with --no-trees option.
33-~/launchpad/devel
34+~/launchpad/lp-branches/devel
35 branch with no trees of trunk.
36-~/launchpad/sandbox
37+~/launchpad/lp-branches/sandbox
38 lightweight checkout with a tree of ../devel.
39-~/launchpad/bug-xyz
40+~/launchpad/lp-branches/bug-xyz
41 branch with no trees of trunk where bug work is done via switching
42 inside sandbox.
43
44
45=== modified file 'lpsetup/handlers.py'
46--- lpsetup/handlers.py 2012-07-16 16:43:34 +0000
47+++ lpsetup/handlers.py 2012-07-19 21:00:25 +0000
48@@ -6,6 +6,7 @@
49
50 __metaclass__ = type
51 __all__ = [
52+ 'handle_code_dir',
53 'handle_directories',
54 'handle_lpuser_as_username',
55 'handle_lpuser_from_lplogin',
56@@ -221,13 +222,13 @@
57
58
59 def handle_directories(namespace):
60- """Handle repository, code_dir, and dependencies directories.
61+ """Handle repository and dependencies directories.
62
63 - The ~ construction is automatically expanded.
64 - The validation fails for directories not residing inside the home.
65 - The validation fails if the directory contains spaces.
66 """
67- for attr in ('repository', 'code_dir', 'dependencies_dir'):
68+ for attr in ('repository', 'dependencies_dir'):
69 directory = getattr(namespace, attr, None)
70 if directory is None:
71 continue
72@@ -285,11 +286,6 @@
73 return os.path.abspath(os.path.expanduser(path))
74
75
76-def handle_working_dir(namespace):
77- """Handle path to the working directory."""
78- namespace.working_dir = normalize_path(namespace.working_dir)
79-
80-
81 def handle_branch_and_checkout(namespace):
82 """Handle branch and checkout names.
83
84@@ -303,6 +299,7 @@
85
86 - branch_name
87 - checkout_name
88+ - repository
89
90 This handler does not modify the namespace.
91 """
92@@ -318,3 +315,12 @@
93 raise ValidationError(
94 'branch and checkout: can not use the same name ({0}).'.format(
95 checkout_name))
96+
97+
98+def handle_code_dir(namespace):
99+ """Handle the computed value for `code_dir`.
100+
101+ It must be invoked after handle_directories.
102+ """
103+ namespace.code_dir = os.path.join(
104+ namespace.repository, namespace.checkout_name)
105
106=== modified file 'lpsetup/settings.py'
107--- lpsetup/settings.py 2012-07-17 17:55:38 +0000
108+++ lpsetup/settings.py 2012-07-19 21:00:25 +0000
109@@ -3,8 +3,6 @@
110 # GNU Affero General Public License version 3 (see the file LICENSE).
111
112 """Global settings and defaults for lpsetup."""
113-import os.path
114-
115
116 APT_REPOSITORIES = (
117 'deb http://archive.ubuntu.com/ubuntu {distro} multiverse',
118@@ -56,7 +54,6 @@
119 LP_BRANCH_NAME = 'devel'
120 LP_CHECKOUT_NAME = 'sandbox'
121 LP_REPOSITORY_DIR = '~/launchpad/lp-branches'
122-LP_CODE_DIR = os.path.join(LP_REPOSITORY_DIR, LP_CHECKOUT_NAME)
123 LP_PACKAGES = [
124 # "launchpad-database-dependencies-9.1" can be removed once 8.x is
125 # no longer an option in "launchpad-developer-dependencies.
126
127=== modified file 'lpsetup/subcommands/finish_inithost.py'
128--- lpsetup/subcommands/finish_inithost.py 2012-07-16 08:17:05 +0000
129+++ lpsetup/subcommands/finish_inithost.py 2012-07-19 21:00:25 +0000
130@@ -29,10 +29,15 @@
131
132 from lpsetup import argparser
133 from lpsetup.handlers import (
134+ handle_code_dir,
135 handle_directories,
136 handle_user,
137 )
138-from lpsetup.settings import LP_CODE_DIR
139+from lpsetup.settings import (
140+ LP_CHECKOUT_NAME,
141+ LP_REPOSITORY_DIR,
142+ )
143+
144 from lpsetup.utils import call
145
146
147@@ -75,22 +80,24 @@
148 handlers = (
149 handle_user,
150 handle_directories,
151+ handle_code_dir,
152 )
153
154- @staticmethod
155- def add_common_arguments(parser):
156- parser.add_argument(
157- '-c', '--code-dir', default=LP_CODE_DIR,
158- help='The directory of the Launchpad code checkout. '
159- 'The directory must reside under the home directory of the '
160- 'given user (see -u argument). '
161- '[DEFAULT={0}]'.format(LP_CODE_DIR))
162-
163 def add_arguments(self, parser):
164 super(SubCommand, self).add_arguments(parser)
165- self.add_common_arguments(parser)
166 parser.add_argument(
167 '-u', '--user',
168 help='The name of the system user. '
169 'The current user is used if this script is not run as '
170 'root and this argument is omitted.')
171+ parser.add_argument(
172+ '--checkout-name', default=LP_CHECKOUT_NAME,
173+ help='Create a checkout with the given name. '
174+ 'Ignored if --no-checkout is specified. '
175+ 'Defaults to {0}.'.format(LP_CHECKOUT_NAME))
176+ parser.add_argument(
177+ '-r', '--repository', default=LP_REPOSITORY_DIR,
178+ help='The directory of the Launchpad repository to be created. '
179+ 'The directory must reside under the home directory of the '
180+ 'given user (see -u argument). '
181+ '[DEFAULT={0}]'.format(LP_REPOSITORY_DIR))
182
183=== modified file 'lpsetup/subcommands/initrepo.py'
184--- lpsetup/subcommands/initrepo.py 2012-07-16 10:09:48 +0000
185+++ lpsetup/subcommands/initrepo.py 2012-07-19 21:00:25 +0000
186@@ -49,27 +49,39 @@
187 is_lightweight = not no_checkout
188 no_trees = '--no-trees' if is_lightweight else None
189 # Initialize the repository.
190- try:
191- call('bzr', 'init-repo', '--quiet', repository, no_trees)
192- except subprocess.CalledProcessError as err:
193- msg = 'Error: unable to initialize the repository: '
194- raise exceptions.ExecutionError(msg + err.output)
195+
196+ # Skip creating the repository if it already exists. There is the
197+ # risk that it is not made with the same `no_trees` option as
198+ # specified here.
199+ if not os.path.exists(os.path.join(repository, '.bzr')):
200+ try:
201+ call('bzr', 'init-repo', '--quiet', repository, no_trees)
202+ except subprocess.CalledProcessError as err:
203+ msg = 'Error: unable to initialize the repository: '
204+ raise exceptions.ExecutionError(msg + err.output)
205 # Set up the codebase.
206 branch_dir = os.path.join(repository, branch_name)
207 # Retrieve the branch.
208+ if os.path.exists(branch_dir):
209+ # The branch already exists, so we pull to refresh it rather than
210+ # creating it.
211+ cmd = ['bzr', 'pull', '--overwrite', '-d', branch_dir, source]
212+ else:
213+ cmd = ['bzr', 'branch', source, branch_dir]
214 try:
215- call('bzr', 'branch', source, branch_dir)
216+ call(*cmd)
217 except subprocess.CalledProcessError as err:
218 msg = 'Error: unable to branch source: '
219 raise exceptions.ExecutionError(msg + err.output)
220 if is_lightweight:
221 checkout_dir = os.path.join(repository, checkout_name)
222- # Create a lightweight checkout.
223- try:
224- call('bzr', 'co', '--lightweight', branch_dir, checkout_dir)
225- except subprocess.CalledProcessError as err:
226- msg = 'Error: unable to create the lightweight checkout: '
227- raise exceptions.ExecutionError(msg + err.output)
228+ # Create a lightweight checkout if it doesn't exist.
229+ if not os.path.exists(checkout_dir):
230+ try:
231+ call('bzr', 'co', '--lightweight', branch_dir, checkout_dir)
232+ except subprocess.CalledProcessError as err:
233+ msg = 'Error: unable to create the lightweight checkout: '
234+ raise exceptions.ExecutionError(msg + err.output)
235
236
237 def setup_bzr_locations(
238
239=== modified file 'lpsetup/subcommands/install_lxc.py'
240--- lpsetup/subcommands/install_lxc.py 2012-07-13 14:51:08 +0000
241+++ lpsetup/subcommands/install_lxc.py 2012-07-19 21:00:25 +0000
242@@ -13,6 +13,7 @@
243 import os
244
245 from lpsetup.handlers import (
246+ handle_code_dir,
247 handle_directories,
248 handle_source,
249 )
250@@ -21,7 +22,6 @@
251 SCRIPTS,
252 )
253 from lpsetup.subcommands import (
254- finish_inithost,
255 initlxc,
256 initrepo,
257 update,
258@@ -69,11 +69,9 @@
259
260
261 def cmd_in_lxc(lxc_name, ssh_key_path, home_dir, args, as_user=None):
262- print "args = ", args
263 cmd = this_command(home_dir, args)
264 if as_user is not None:
265 cmd = "su {} -c '{}'".format(as_user, cmd)
266- print "CMD = ", cmd
267 ssh(lxc_name, cmd, key=ssh_key_path)
268
269
270@@ -91,18 +89,20 @@
271
272 def update_in_lxc(lxc_name, ssh_key_path, home_dir, user, external_path,
273 use_http, checkout_name, repository):
274- working_dir = os.path.join(repository, checkout_name)
275 args = [
276- 'update', '--external-path', external_path, '-W', working_dir,
277+ 'update', '--external-path', external_path,
278+ '-r', repository, '--checkout-name', checkout_name,
279 ]
280 if use_http:
281 args.append('--use-http')
282 cmd_in_lxc(lxc_name, ssh_key_path, home_dir, args, as_user=user)
283
284
285-def finish_inithost_in_lxc(lxc_name, ssh_key_path, home_dir, user, code_dir):
286+def finish_inithost_in_lxc(lxc_name, ssh_key_path, home_dir, user,
287+ repository, checkout_name):
288 args = [
289- 'finish-init-host', '--user', user, '--code-dir', code_dir,
290+ 'finish-init-host', '--user', user, '--repository', repository,
291+ '--checkout-name', checkout_name,
292 ]
293 cmd_in_lxc(lxc_name, ssh_key_path, home_dir, args)
294
295@@ -121,14 +121,18 @@
296 (update_in_lxc, 'lxc_name', 'ssh_key_path', 'home_dir', 'user',
297 'external_path', 'use_http', 'checkout_name', 'repository'),
298 (finish_inithost_in_lxc, 'lxc_name', 'ssh_key_path', 'home_dir',
299- 'user', 'code_dir'),
300+ 'user', 'repository', 'checkout_name'),
301 )
302
303 help = __doc__
304
305 def get_handlers(self, namespace):
306 handlers = super(SubCommand, self).get_handlers(namespace)
307- return handlers + (handle_directories, handle_source)
308+ return handlers + (
309+ handle_directories,
310+ handle_code_dir,
311+ handle_source,
312+ )
313
314 def call_create_scripts(self, namespace, step, args):
315 """Run the `create_scripts` step only if the related flag is set."""
316@@ -140,7 +144,6 @@
317 # Inherit arguments from subcommands we depend upon.
318 initrepo.SubCommand.add_common_arguments(parser)
319 update.SubCommand.add_common_arguments(parser)
320- finish_inithost.SubCommand.add_common_arguments(parser)
321 # Add parallel testing related arguments.
322 parser.add_argument(
323 '-C', '--create-scripts', action='store_true',
324
325=== modified file 'lpsetup/subcommands/update.py'
326--- lpsetup/subcommands/update.py 2012-07-13 15:55:40 +0000
327+++ lpsetup/subcommands/update.py 2012-07-19 21:00:25 +0000
328@@ -19,48 +19,49 @@
329 from lpsetup import argparser
330 from lpsetup import handlers
331 from lpsetup.settings import (
332- LP_CODE_DIR,
333+ LP_CHECKOUT_NAME,
334+ LP_REPOSITORY_DIR,
335 LP_SOURCE_DEPS,
336 )
337
338
339-def initialize_directories(working_dir, external_path):
340+def initialize_directories(code_dir, external_path):
341 """Initialize the eggs, yui, and sourcecode directories.
342
343 Create them if necessary.
344 """
345 for dir_ in ['eggs', 'yui', 'sourcecode']:
346- mkdirs(os.path.join(working_dir, external_path, dir_))
347-
348-
349-def update_dependencies(working_dir, external_path, use_http):
350+ mkdirs(os.path.join(code_dir, external_path, dir_))
351+
352+
353+def update_dependencies(code_dir, external_path, use_http):
354 """Update the external dependencies."""
355 use_http_param = '--use-http' if use_http else None
356- cmd = os.path.join(working_dir, 'utilities', 'update-sourcecode')
357+ cmd = os.path.join(code_dir, 'utilities', 'update-sourcecode')
358 abs_external_path = os.path.abspath(
359- os.path.join(working_dir, external_path))
360+ os.path.join(code_dir, external_path))
361 source_path = os.path.join(abs_external_path, 'sourcecode')
362 run(cmd, use_http_param, source_path)
363
364 # Update the download cache.
365- download_cache = os.path.join(working_dir, 'download-cache')
366+ download_cache = os.path.join(code_dir, 'download-cache')
367 if os.path.exists(download_cache):
368 run('bzr', 'up', download_cache)
369 else:
370 run('bzr', 'co', '-v', '--lightweight', LP_SOURCE_DEPS, download_cache)
371
372 # Link to the external sourcecode.
373- if abs_external_path != working_dir:
374+ if abs_external_path != code_dir:
375 cmd = os.path.join(
376- working_dir, 'utilities', 'link-external-sourcecode')
377+ code_dir, 'utilities', 'link-external-sourcecode')
378 run(cmd,
379- '--target', working_dir,
380+ '--target', code_dir,
381 '--parent', external_path)
382
383
384-def update_tree(working_dir):
385- """Update the tree at working_dir with the latest LP code."""
386- with cd(working_dir):
387+def update_tree(code_dir):
388+ """Update the tree at code_dir with the latest LP code."""
389+ with cd(code_dir):
390 run('bzr', 'pull')
391
392
393@@ -71,14 +72,16 @@
394 """
395
396 steps = (
397- (initialize_directories, 'working_dir', 'external_path'),
398- (update_dependencies, 'working_dir', 'external_path', 'use_http'),
399- (update_tree, 'working_dir'),
400+ (initialize_directories, 'code_dir', 'external_path'),
401+ (update_dependencies, 'code_dir', 'external_path', 'use_http'),
402+ (update_tree, 'code_dir'),
403 )
404 help = __doc__
405 handlers = (
406 # Normalize paths and default to cwd if none exists.
407- handlers.handle_working_dir,
408+ handlers.handle_user,
409+ handlers.handle_directories,
410+ handlers.handle_code_dir,
411 )
412
413 @staticmethod
414@@ -97,6 +100,13 @@
415 help='Force bzr to use http to get the sourcecode '
416 'branches rather than using bzr+ssh.')
417 parser.add_argument(
418- '-W', '--working-dir', default=LP_CODE_DIR,
419- help='Path to branch to update. '
420- '[DEFAULT={0}]'.format(LP_CODE_DIR))
421+ '--checkout-name', default=LP_CHECKOUT_NAME,
422+ help='Create a checkout with the given name. '
423+ 'Ignored if --no-checkout is specified. '
424+ 'Defaults to {0}.'.format(LP_CHECKOUT_NAME))
425+ parser.add_argument(
426+ '-r', '--repository', default=LP_REPOSITORY_DIR,
427+ help='The directory of the Launchpad repository to be created. '
428+ 'The directory must reside under the home directory of the '
429+ 'given user (see -u argument). '
430+ '[DEFAULT={0}]'.format(LP_REPOSITORY_DIR))
431
432=== added file 'lpsetup/tests/integration/common.py'
433--- lpsetup/tests/integration/common.py 1970-01-01 00:00:00 +0000
434+++ lpsetup/tests/integration/common.py 2012-07-19 21:00:25 +0000
435@@ -0,0 +1,63 @@
436+#!/usr/bin/env python
437+# Copyright 2012 Canonical Ltd. This software is licensed under the
438+# GNU Affero General Public License version 3 (see the file LICENSE).
439+
440+"""Common resources for integration tests."""
441+
442+__metaclass__ = type
443+__all__ = [
444+ 'IntegrationTestBase',
445+ ]
446+
447+import argparse
448+from datetime import datetime
449+
450+
451+class IntegrationTestBase:
452+
453+ banner_width = 70
454+ test_type = None
455+
456+ def __init__(self):
457+ self.parser = argparse.ArgumentParser()
458+ self.parser.add_argument(
459+ '--no-tear-down', action='store_true', default=False,
460+ help='Do not run the tear down method for debugging.')
461+ self.args = self.parser.parse_args()
462+
463+ def banner(self, msg, width=banner_width):
464+ print '*' * width
465+ print '* ' + msg.ljust(width - 4) + ' *'
466+ print '*' * width
467+
468+ def set_up(self):
469+ self.banner(
470+ 'Setting up the test environment for {}.'.format(self.test_type))
471+
472+ def tear_down(self):
473+ self.banner('Cleaning up.')
474+
475+ def check_environment(self):
476+ self.banner('Checking test environment.')
477+
478+ def do_test(self):
479+ self.banner('Running integration tests for {}.'.format(self.test_type))
480+
481+ def run(self):
482+ start_time = datetime.now()
483+ self.check_environment()
484+ try:
485+ self.set_up()
486+ try:
487+ self.do_test()
488+ except Exception as err:
489+ self.banner('Test failed. Sorry.')
490+ print err
491+ return 1
492+ else:
493+ self.banner('Test succeeded.')
494+ return 0
495+ finally:
496+ if not self.args.no_tear_down:
497+ self.tear_down()
498+ print "Run time: ", datetime.now() - start_time
499
500=== added file 'lpsetup/tests/integration/lxc.py'
501--- lpsetup/tests/integration/lxc.py 1970-01-01 00:00:00 +0000
502+++ lpsetup/tests/integration/lxc.py 2012-07-19 21:00:25 +0000
503@@ -0,0 +1,106 @@
504+#!/usr/bin/env python
505+# Copyright 2012 Canonical Ltd. This software is licensed under the
506+# GNU Affero General Public License version 3 (see the file LICENSE).
507+
508+"""A simple, end-to-end integration test."""
509+
510+from bzrlib.branch import Branch
511+from bzrlib.errors import NotBranchError
512+from bzrlib.missing import find_unmerged
513+from bzrlib.status import show_tree_status
514+from bzrlib.workingtree import WorkingTree
515+from StringIO import StringIO
516+from subprocess import check_call
517+import sys
518+
519+from shelltoolbox import run
520+
521+from common import IntegrationTestBase
522+
523+
524+LXC_NAME = 'lpsetup-testing'
525+
526+
527+class LXCIntegrationTest(IntegrationTestBase):
528+
529+ test_type = 'LXC container'
530+ repo = '~/launchpad-testing/lp-branches'
531+
532+ def get_branch(self, dir='.'):
533+ branch, _ = Branch.open_containing(dir)
534+ return branch
535+
536+ def get_push_location(self, branch=None):
537+ if branch is None:
538+ branch = self.get_branch()
539+ return branch.get_push_location()
540+
541+ def check_environment(self):
542+ """Be sure the test environment doesn't exist."""
543+ # We want to be really sure we do not clobber an existing LXC
544+ # container, therefore we make sure one doesn't exist before
545+ # proceeding.
546+ super(LXCIntegrationTest, self).check_environment()
547+ cmd = 'sudo lxc-info -n {}'.format(LXC_NAME)
548+ results = run(*cmd.split())
549+ if results.split()[1] == 'RUNNING':
550+ # The container should not be active.
551+ raise RuntimeError(
552+ "An LXC container named '{}' is unexpectedly "
553+ "running.".format(LXC_NAME))
554+ # Ensure the local branch has been pushed.
555+ # For some reason 'bzr missing' cannot use a 'lp:' URL. http or
556+ # bzr+ssh URLs work.
557+ self.branch = self.get_branch()
558+ self.push_location = self.get_push_location(self.branch)
559+ if self.push_location.startswith('lp:'):
560+ raise RuntimeError(
561+ "Push location must use http or bzr+ssh "
562+ "protocol: {}".format(self.push_location))
563+ # Ensure the branch has been pushed and has no missing revisions.
564+ try:
565+ push_branch = self.get_branch(self.push_location)
566+ except NotBranchError:
567+ raise RuntimeError(
568+ 'The branch has never been pushed to Launchpad.')
569+ tree, _ = WorkingTree.open_containing_paths(None)
570+ stream = StringIO()
571+ show_tree_status(tree, to_file=stream)
572+ if stream.getvalue():
573+ raise RuntimeError(
574+ 'The tree has uncommitted changes. Check them in '
575+ 'and push to Launchpad.')
576+ near, far = find_unmerged(self.branch, push_branch)
577+ if near or far:
578+ raise RuntimeError('The local branch and the pushed version '
579+ 'have diverged.')
580+
581+ def set_up(self):
582+ """Set up the LXC container environment."""
583+ super(LXCIntegrationTest, self).set_up()
584+ cmd = 'sudo python setup.py install'
585+ check_call(cmd.split())
586+
587+ def do_test(self):
588+ """Run an end-to-end integration tests of the non-LXC lpsetup story."""
589+ super(LXCIntegrationTest, self).do_test()
590+ cmd = 'lp-setup install-lxc -B {} -r {} {}'.format(
591+ self.push_location, self.repo, LXC_NAME)
592+ check_call(cmd.split())
593+
594+ def tear_down(self):
595+ super(LXCIntegrationTest, self).tear_down()
596+
597+ def lxc_cmd(cmd_name):
598+ cmd = 'sudo {} -n {}'.format(cmd_name, LXC_NAME)
599+ check_call(cmd.split())
600+
601+ try:
602+ lxc_cmd('lxc-stop')
603+ lxc_cmd('lxc-destroy')
604+ except:
605+ pass
606+
607+
608+if __name__ == '__main__':
609+ sys.exit(LXCIntegrationTest().run())
610
611=== modified file 'lpsetup/tests/integration/non-lxc.py'
612--- lpsetup/tests/integration/non-lxc.py 2012-07-10 19:58:56 +0000
613+++ lpsetup/tests/integration/non-lxc.py 2012-07-19 21:00:25 +0000
614@@ -14,96 +14,79 @@
615 PIPE,
616 )
617
618-
619-def banner(s):
620- width = 70
621- print '*' * width
622- print '* ' + s.ljust(width - 4) + ' *'
623- print '*' * width
624-
625-
626-def on_remote(args):
627- if type(args) == str:
628- args = args.split()
629-
630- check_call('juju ssh 1 -o StrictHostKeyChecking=no'
631- ' -o UserKnownHostsFile=/dev/null -e lpsetup-testing'.split()
632- + list(args))
633-
634-
635-def check_environment():
636- """Be sure the test environment doesn't exist."""
637- banner('Checking test environment.')
638- # We want to be really sure we do not clobber an already-existing juju
639- # environment. Therefore we make sure one doesn't exist before
640- # bootstrapping.
641- code = os.system('juju status -e lpsetup-testing')
642- if code == 0:
643- # The "juju status" should have failed.
644- raise RuntimeError('A juju environment unexpectedly exists.')
645-
646-
647-def set_up():
648- """Set up a juju-managed instance to run the tests on."""
649- banner('Setting up the test environment.')
650- check_call('juju bootstrap -e lpsetup-testing'.split())
651- # XXX The "ubuntu" charm is broken, so it has to be loaded from the local
652- # repository. Get it from lp:~charmers/charms/precise/ubuntu/trunk.
653- check_call('juju deploy local:ubuntu --repository ~/juju-charms'
654- ' -e lpsetup-testing --constraints instance-type=m1.large'.split())
655-
656- start_time = time.time()
657- while time.time() - start_time < 600:
658- process = Popen('juju status -e lpsetup-testing'.split(), stdout=PIPE)
659- stdout, stderr = process.communicate()
660- if 'instance-state: running' in stdout:
661- break
662- else:
663- raise RuntimeError('starting the instance took too long')
664-
665- # Even though the instance-state is "running", it's still not ready, so
666- # wait a little while before continuing.
667- time.sleep(30)
668-
669- on_remote('sudo apt-add-repository --yes ppa:yellow/ppa')
670- on_remote('sudo apt-get update')
671- on_remote('sudo apt-get install python-shelltoolbox')
672- check_call('juju scp -o StrictHostKeyChecking=no'
673- ' -o UserKnownHostsFile=/dev/null -e lpsetup-testing'
674- ' -r . 1:lpsetup'.split())
675-
676-
677-def do_test():
678- """Run an end-to-end integration tests of the non-LXC lpsetup story."""
679- banner('Running (non-LXC) integration test.')
680- # Since the most common scenario is to have bzr whoami setup, we do that
681- # instead of providing the arguments directly to lpsetup.
682- on_remote(('bzr', 'whoami', '"Not A Real Person <no@example.com>"'))
683- on_remote('cd lpsetup; ./lp-setup init-host'.split())
684-
685-
686-def tear_down():
687- banner('Cleaning up.')
688- code = os.system('echo y | juju destroy-environment -e lpsetup-testing')
689- if code != 0:
690- raise RuntimeError('Destroying the test juju environment failed.')
691-
692-
693-def main():
694- check_environment()
695- try:
696- set_up()
697- try:
698- do_test()
699- except:
700- banner('Test failed. Sorry.')
701- return 1
702+from common import IntegrationTestBase
703+
704+
705+class NonLXCIntegrationTest(IntegrationTestBase):
706+
707+ test_type = 'non-LXC target'
708+
709+ def on_remote(self, args):
710+ if type(args) == str:
711+ args = args.split()
712+
713+ check_call('juju ssh 1 -o StrictHostKeyChecking=no'
714+ ' -o UserKnownHostsFile=/dev/null -e lpsetup-testing'.split()
715+ + list(args))
716+
717+ def check_environment(self):
718+ """Be sure the test environment doesn't exist."""
719+ # We want to be really sure we do not clobber an already-existing juju
720+ # environment. Therefore we make sure one doesn't exist before
721+ # bootstrapping.
722+ super(NonLXCIntegrationTest, self).check_environment()
723+ code = os.system('juju status -e lpsetup-testing')
724+ if code == 0:
725+ # The "juju status" should have failed.
726+ raise RuntimeError('A juju environment unexpectedly exists.')
727+
728+ def set_up(self):
729+ """Set up a juju-managed instance to run the tests on."""
730+ super(NonLXCIntegrationTest, self).set_up()
731+ check_call('juju bootstrap -e lpsetup-testing'.split())
732+ # XXX The "ubuntu" charm is broken, so it has to be loaded from the
733+ # local repository. Get it from
734+ # lp:~charmers/charms/precise/ubuntu/trunk.
735+ check_call('juju deploy local:ubuntu --repository ~/juju-charms'
736+ ' -e lpsetup-testing --constraints instance-type=m1.large'.split())
737+
738+ start_time = time.time()
739+ while time.time() - start_time < 600:
740+ process = Popen(
741+ 'juju status -e lpsetup-testing'.split(), stdout=PIPE)
742+ stdout, stderr = process.communicate()
743+ if 'instance-state: running' in stdout:
744+ break
745 else:
746- banner('Test succeeded.')
747- return 0
748- finally:
749- tear_down()
750+ raise RuntimeError('starting the instance took too long')
751+
752+ # Even though the instance-state is "running", it's still not ready, so
753+ # wait a little while before continuing.
754+ time.sleep(30)
755+
756+ self.on_remote('sudo apt-add-repository --yes ppa:yellow/ppa')
757+ self.on_remote('sudo apt-get update')
758+ self.on_remote('sudo apt-get install python-shelltoolbox')
759+ check_call('juju scp -o StrictHostKeyChecking=no'
760+ ' -o UserKnownHostsFile=/dev/null -e lpsetup-testing'
761+ ' -r . 1:lpsetup'.split())
762+
763+ def do_test(self):
764+ """Run an end-to-end integration tests of the non-LXC lpsetup story."""
765+ # Since the most common scenario is to have bzr whoami setup, we do
766+ # that instead of providing the arguments directly to lpsetup.
767+ super(NonLXCIntegrationTest, self).do_test()
768+ self.on_remote(
769+ ('bzr', 'whoami', '"Not A Real Person <no@example.com>"'))
770+ self.on_remote('cd lpsetup; ./lp-setup init-host'.split())
771+
772+ def tear_down(self):
773+ super(NonLXCIntegrationTest, self).tear_down()
774+ code = os.system(
775+ 'echo y | juju destroy-environment -e lpsetup-testing')
776+ if code != 0:
777+ raise RuntimeError('Destroying the test juju environment failed.')
778
779
780 if __name__ == '__main__':
781- sys.exit(main())
782+ sys.exit(NonLXCIntegrationTest().run())
783
784=== modified file 'lpsetup/tests/subcommands/test_finish_inithost.py'
785--- lpsetup/tests/subcommands/test_finish_inithost.py 2012-07-12 18:23:37 +0000
786+++ lpsetup/tests/subcommands/test_finish_inithost.py 2012-07-19 21:00:25 +0000
787@@ -19,9 +19,10 @@
788
789
790 def get_arguments():
791- code_dir = '~/' + get_random_string()
792+ repo = '~/' + get_random_string()
793+ checkout = get_random_string()
794 user = get_random_string()
795- return ('-c', code_dir, '-u', user)
796+ return ('-r', repo, '--checkout-name', checkout, '-u', user)
797
798
799 class FinishInitHostTest(StepsBasedSubCommandTestMixin, unittest.TestCase):
800@@ -31,6 +32,7 @@
801 expected_handlers = (
802 handlers.handle_user,
803 handlers.handle_directories,
804+ handlers.handle_code_dir,
805 )
806
807 @property
808
809=== modified file 'lpsetup/tests/subcommands/test_install_lxc.py'
810--- lpsetup/tests/subcommands/test_install_lxc.py 2012-07-12 22:12:09 +0000
811+++ lpsetup/tests/subcommands/test_install_lxc.py 2012-07-19 21:00:25 +0000
812@@ -34,7 +34,8 @@
813
814 finish_inithost_in_lxc_step = (
815 install_lxc.finish_inithost_in_lxc, [
816- 'lxc_name', 'ssh_key_path', 'home_dir', 'user', 'code_dir',
817+ 'lxc_name', 'ssh_key_path', 'home_dir', 'user', 'repository',
818+ 'checkout_name',
819 ])
820
821
822@@ -52,6 +53,7 @@
823 expected_arguments = get_arguments()
824 expected_handlers = test_initlxc.InitLxcTest.expected_handlers + (
825 handlers.handle_directories,
826+ handlers.handle_code_dir,
827 handlers.handle_source,
828 )
829 expected_steps = test_initlxc.InitLxcTest.expected_steps + (
830
831=== modified file 'lpsetup/tests/subcommands/test_update.py'
832--- lpsetup/tests/subcommands/test_update.py 2012-07-13 15:55:40 +0000
833+++ lpsetup/tests/subcommands/test_update.py 2012-07-19 21:00:25 +0000
834@@ -18,15 +18,15 @@
835 return (
836 '--external-path', get_random_string(),
837 '--use-http',
838- '-e', '../devel',
839- '-W', '~/' + get_random_string(),
840+ '--repository', get_random_string(),
841+ '--checkout-name', get_random_string(),
842 )
843
844 init_dir_step = (
845- update.initialize_directories, ['working_dir', 'external_path'])
846+ update.initialize_directories, ['code_dir', 'external_path'])
847 update_dep_step = (
848- update.update_dependencies, ['working_dir', 'external_path', 'use_http'])
849-update_tree_step = (update.update_tree, ['working_dir'])
850+ update.update_dependencies, ['code_dir', 'external_path', 'use_http'])
851+update_tree_step = (update.update_tree, ['code_dir'])
852
853
854 class UpdateTest(StepsBasedSubCommandTestMixin, unittest.TestCase):
855@@ -35,7 +35,9 @@
856 sub_command_class = update.SubCommand
857 expected_arguments = get_arguments()
858 expected_handlers = (
859- handlers.handle_working_dir,
860+ handlers.handle_user,
861+ handlers.handle_directories,
862+ handlers.handle_code_dir,
863 )
864 expected_steps = (
865 init_dir_step,
866
867=== modified file 'pre-commit.sh'
868--- pre-commit.sh 2012-07-11 15:50:01 +0000
869+++ pre-commit.sh 2012-07-19 21:00:25 +0000
870@@ -1,4 +1,4 @@
871 #!/bin/bash
872
873-pyfiles=`find . -name "*.py" | grep -v distribute_setup.py`
874-pocketlint $pyfiles && pep8 $pyfiles && nosetests
875+pyfiles=`find . -name build -prune -o -name "*.py" | grep -v distribute_setup.py`
876+pocketlint $pyfiles && pep8 --exclude=build $pyfiles && nosetests

Subscribers

People subscribed via source and target branches