Merge ~wesley-wiedenmeier/cloud-init:integration-testing-invocation-cleanup into cloud-init:master

Proposed by Wesley Wiedenmeier
Status: Merged
Merged at revision: 76d58265e34851b78e952a7f275340863c90a9f5
Proposed branch: ~wesley-wiedenmeier/cloud-init:integration-testing-invocation-cleanup
Merge into: cloud-init:master
Diff against target: 600 lines (+349/-36)
8 files modified
doc/rtd/topics/tests.rst (+67/-8)
tests/cloud_tests/__init__.py (+1/-0)
tests/cloud_tests/__main__.py (+5/-26)
tests/cloud_tests/args.py (+53/-1)
tests/cloud_tests/bddeb.py (+124/-0)
tests/cloud_tests/instances/base.py (+1/-1)
tests/cloud_tests/run_funcs.py (+65/-0)
tests/cloud_tests/util.py (+33/-0)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve
Joshua Powers (community) Approve
cloud-init Commiters Pending
Review via email: mp+314496@code.launchpad.net

Description of the change

Integration Testing: provide commands to run tests from current tree

 - Add 'bddeb' command to build a deb from the current working tree
   cleanly in a container, so deps don't have to be installed on host
 - Add 'tree_collect' and 'tree_run' commands, to build deb from
   current working tree and call collect or run with deb
 - Fix bug in instance.pull_file()

To post a comment you must log in.
Revision history for this message
Joshua Powers (powersj) wrote :

LGTM with two comments:

1) Had to increase the timeout as 120 seconds was not enough. I bumped to 600.
2) the tox target "cittest_run" actually does a "tree_run" In order to not be confused between run versus tree_run I think this should really be "cittest_tree_run"

Tested by running the following on my local system with pylxd 2.1.3:

# lint
$ tox
# runs with options
$ python3 -m tests.cloud_tests run -v -n trusty
$ python3 -m tests.cloud_tests run -v -n xenial
$ python3 -m tests.cloud_tests run -v -n yakkety --repo 'deb http://archive.ubuntu.com/ubuntu/ yakkety main'
$ python3 -m tests.cloud_tests run -v -n xenial --deb cloud-init_0.7.9-0ubuntu1_all.deb
# new tox based runs + tree_run
$ tox -e citest_run
$ tox -e citest -- tree_run -n zesty -t tests/cloud_tests/configs/modules/write_files.yaml
$ python3 -m tests.cloud_tests tree_run -v -n zesty -t tests/cloud_tests/configs/modules/timezone.yaml

lxc list was clean afterwards as expected. I believe I only missed a ppa test.

The only warning message I got other than those for pylxd's 2.2 deprecation was:
/home/powersj/Work/repos/cloud-init-wesley/tests/cloud_tests/collect.py:48: DeprecationWarning: The 'warn' method is deprecated, use 'warning' instead

review: Approve
Revision history for this message
Wesley Wiedenmeier (wesley-wiedenmeier) wrote :

Thanks for reviewing

> 1) Had to increase the timeout as 120 seconds was not enough. I bumped to 600.
I'll go ahead and increase the timout a bit, or possibly add the option for the call to snapshot.launch() in bddeb to specify a longer timeout that normal, since the bddeb instance should be expected to take longer to boot than other instances. It would also work to not use cloud-init to install devscripts, and just apt-get it during the build process, that may be cleaner, and would allow finer grained logging messages.

> 2) the tox target "cittest_run" actually does a "tree_run" In order to not be
> confused between run versus tree_run I think this should really be
> "cittest_tree_run"
Good point, citest_run = tree_run may be confusing, I'll get the name switched over.

> lxc list was clean afterwards as expected. I believe I only missed a ppa test.
I think that is plenty of testing as the changes shouldn't have affected anything other than bddeb and adding the new commands. I'll run through with --ppa once to be sure though.

> The only warning message I got other than those for pylxd's 2.2 deprecation
> was:
> /home/powersj/Work/repos/cloud-init-wesley/tests/cloud_tests/collect.py:48:
> DeprecationWarning: The 'warn' method is deprecated, use 'warning' instead
I always forget whether its LOG.warn or LOG.warning that is depricated, got it wrong here, good catch. I'll fix here and grep through to check if its wrong anywhere else.

I should have all of that done at some point tonight, should be ready for merge tomorrow

Revision history for this message
Wesley Wiedenmeier (wesley-wiedenmeier) wrote :

Timeouts will no longer be an issue for bddeb as dep installation is now handled after boot. I think this should be ready to merge, I've tested again with tree_collect, tree_run, and just with a normal 'run --ppa'

Revision history for this message
Joshua Powers (powersj) wrote :

LGTM

Thanks for the changes! Ran the following successfully with no changes to timeouts:

$ tox
$ tox -e citest_tree_run
$ tox -e citest -- tree_run -n zesty -t tests/cloud_tests/configs/modules/write_files.yaml
$ python3 -m tests.cloud_tests tree_run -v -n zesty -t tests/cloud_tests/configs/modules/timezone.yaml

review: Approve
Revision history for this message
Wesley Wiedenmeier (wesley-wiedenmeier) wrote :

This merge proposal is no longer required, as everything here is included in the main one at:
https://code.launchpad.net/~wesley-wiedenmeier/cloud-init/+git/cloud-init/+merge/308218

Revision history for this message
Wesley Wiedenmeier (wesley-wiedenmeier) wrote :
Download full text (3.8 KiB)

Since there is additional work that can be done to improve the build system, this branch and this merge proposal will be used for development. Below is some of the discussion on bddeb for reference.

> >> * creating the tarball..
> >> I think what we want to do here is:
> >> a.) make-tarball on "this side"
> >> b.) modify bddeb to be able to just create a debian/ dir
> >> either
> >> i.) have it create the orig tarball and a debian.tar.gz
> >> into a provided (empty) output dir.
> >> ii.) have it just create the debian.tar.gz and we create
> >> the tarball example of doing that
> >> http://paste.ubuntu.com/23870964/
> >> c.) move the tarball and debian dir over, extract tarball,
> >> extract debian/
> >> d.) mk-build-deps --install .... debian/control
> >> e.) dpkg-buildpackage
> >>
> >> doing this should mean we do not have to 'install additional
> >> build deps' but rather just have mk-build-deps do the right
> >> thing.
>
> >I had been trying to avoid modifying anything outside cloud_tests,
> >but adding the --debian-tar-only option in bddeb definitely does
> >simplify this. This may be a useful feature in general for debugging
> >cloud-init builds that do not work or doing custom deb builds, as
> >going through the template system can be inconvenient.
>
> >This also avoids having to go through a source repo in case the
> >current version of cloud-init in tree has different deps from the
> >one in the source repo.
>
> > Using dpkg-buildpackage instead of debuild may be quicker too.
>
> Yeah, it makes the build much simpler in the container. I'm tempted
> to further improve bddeb to list what packages need installing even,
> then we could drop the mk-build-deps and equivs ... but that is not
> necessary now. The complexity in that comes really from the obnoxious
> 'python3-pyflakes | pyflakes (<< 1.1.0-2)'
>
> in package/debian/control.in

Yeah, I kinda like the idea of bddeb to handle build deps entirely. That may
make things simpler for users who want to build as well since there have been
lots of questions on irc about how to build packages. We should probably add
more documentation on cloud-inits build system at some point as well.

> >> * when building with dpkg-buildpackage or debuild, good to
> >> allow setting DEB_BUILD_OPTIONS=nocheck (which will not run pep8 and
> >> nosetests)
>
> >Nice, I didn't know that was an option. Lets us avoid the additional
> >build deps step easily, since that was mostly to avoid failing because
> >testing stuff wasn't there.
>
> There is info at https://bugs.launchpad.net/cloud-init/+bug/1652329
> on what the plan here is... ultimately, package build wont run pep8, but
> would still run nosetests and useful to be able to not run them.

I do like the idea of dropping pep8 in the build, since the actual source used
for the builds whill have already been checked.

> >> * i think the above path means we do not depend on deb-src lines.
>
> >The switch back to ubuntu daily will handle that, but this also
> >avoids going through source repo at all which can be nice if a local
> >change adds a new build dep.
>
> And simply use of mk-build-deps would too..
> I know i'm kind of over-doing this, but i really lik...

Read more...

Revision history for this message
Wesley Wiedenmeier (wesley-wiedenmeier) wrote :

This branch should mostly be ready to merge in now. There is additonal work to be done on getting the cc_apt_configure issue resolved in debian jessie, and the hostnamectl issue resolved on centos70, but that can wait, as these are most likely not issues caused by the test suite. Support for package management on centos still needs to be switched to dnf, but since centos is blocked on the hostnamectl issue, that can wait as well.

The cleanup to system_ready_scripts has been done, and the ubuntu release config is back to using the ubuntu daily images. Ubuntu releases xenial -> zesty have been run with a couple of test configs successfully, both installing from ppa:cloud-init-dev/daily and from --deb with a deb of 0.7.9, and trusty has been run successfully with the version of cloud-init it ships with on modules/final_message. Additional testing of this merge may be good, but the tests currently being run by jenkins should work.

Revision history for this message
Wesley Wiedenmeier (wesley-wiedenmeier) wrote :

This branch is not yet ready to merge. The above comment was meant for the main merge proposal at https://code.launchpad.net/~wesley-wiedenmeier/cloud-init/+git/cloud-init/+merge/308218

The build system and the bddeb functionality for cloud-tests can be improved further.

Revision history for this message
Wesley Wiedenmeier (wesley-wiedenmeier) wrote :

This branch should be ready to merge into trunk now. There were some additional improvements to the build system that had been discussed, but it may be best to work on those later. If the build system is improved in the future, then this code can easily be updated as well.

The tree_run command that this branch provides is useful enough on its own that this is worth merging in on its own, although this is a lower priority than either the main integration-testing update or the distro feature flags branch that is being worked on right now.

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
51fa870... by Wesley Wiedenmeier

Integration Testing: Updated documentation

 - Added documentation on the tree_run and tree_collect commands.
 - Also made minor documentation updates for additional useful
   features in the test suite.

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Scott Moser (smoser) wrote :

I'm going to mark this 'work in progress'. Josh has integrated this branch and others into his merge proposal at https://code.launchpad.net/~powersj/cloud-init/+git/cloud-init/+merge/324136 . We do plan to pull that all soon.

Thanks for your work, Wesley!

Revision history for this message
Scott Moser (smoser) wrote :

Hi.
I've marked this 'merged' as I think it is now in trunk under 76d58265e34851b78e952a7f275340863c90a9f5.
If you disagree, please feel free to re-open.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/doc/rtd/topics/tests.rst b/doc/rtd/topics/tests.rst
index 0663811..a088e64 100644
--- a/doc/rtd/topics/tests.rst
+++ b/doc/rtd/topics/tests.rst
@@ -47,20 +47,23 @@ The test configuration is a YAML file such as *ntp_server.yaml* below:
47 cat /etc/ntp.conf | grep '^server'47 cat /etc/ntp.conf | grep '^server'
4848
4949
50There are two keys, 1 required and 1 optional, in the YAML file:50There are several keys, 1 required and some optional, in the YAML file:
5151
521. The required key is ``cloud_config``. This should be a string of valid521. The required key is ``cloud_config``. This should be a string of valid
53 YAML that is exactly what would normally be placed in a cloud-config file,53 YAML that is exactly what would normally be placed in a cloud-config file,
54 including the cloud-config header. This essentially sets up the scenario54 including the cloud-config header. This essentially sets up the scenario
55 under test.55 under test.
5656
572. The optional key is ``collect_scripts``. This key has one or more572. One optional key is ``collect_scripts``. This key has one or more
58 sub-keys containing strings of arbitrary commands to execute (e.g.58 sub-keys containing strings of arbitrary commands to execute (e.g.
59 ```cat /var/log/cloud-config-output.log```). In the example above the59 ```cat /var/log/cloud-config-output.log```). In the example above the
60 output of dpkg is captured, grep for ntp, and the number of lines60 output of dpkg is captured, grep for ntp, and the number of lines
61 reported. The name of the sub-key is important. The sub-key is used by61 reported. The name of the sub-key is important. The sub-key is used by
62 the verification script to recall the output of the commands ran.62 the verification script to recall the output of the commands ran.
6363
643. The optinal key ``enabled`` enables or disables the test case. By defaul
65 the test case will be enabled.
66
64Default Collect Scripts67Default Collect Scripts
65-----------------------68-----------------------
6669
@@ -109,15 +112,25 @@ Here is a breakdown of the unit test file:
109112
110* The import statement allows access to the output files.113* The import statement allows access to the output files.
111114
112* The class can be named anything, but must import the ``base.CloudTestCase``115* The class can be named anything, but must import the ``base.CloudTestCase``,
116 either directly or via another test class.
113117
114* There can be 1 to N number of functions with any name, however only118* There can be 1 to N number of functions with any name, however only
115 tests starting with ``test_*`` will be executed.119 tests starting with ``test_*`` will be executed.
116120
121* There can be 1 to N number of classes in a test module, however only classes
122 inheriting from ``base.CloudTestCase`` will be loaded.
123
117* Output from the commands can be accessed via124* Output from the commands can be accessed via
118 ``self.get_data_file('key')`` where key is the sub-key of125 ``self.get_data_file('key')`` where key is the sub-key of
119 ``collect_scripts`` above.126 ``collect_scripts`` above.
120127
128* The cloud config that the test ran with can be accessed via
129 ``self.cloud_config``, or any entry from the cloud config can be accessed via
130 ``self.get_config_entry('key')``.
131
132* See the base ``CloudTestCase`` for additional helper functions.
133
121Layout134Layout
122======135======
123136
@@ -144,6 +157,23 @@ The sub-folders of bugs, examples, main, and modules help organize the
144tests. View the README.md in each to understand in more detail each157tests. View the README.md in each to understand in more detail each
145directory.158directory.
146159
160Test Creation Helper
161====================
162
163The integration testing suite has a built in helper to aid in test development.
164Its help can be invoked via ``python3 -m tests.cloud_tests create --help``. It
165can create a template testcase config file with user data passed in from the
166command line, as well as a template testcase verifier module.
167
168The following would create a testcase named ``example`` under the ``modules``
169category with the given description, and cloud config data read in from
170``/tmp/user_data``.
171
172.. code-block:: bash
173
174 $ python3 -m tests.cloud_tests create modules/example \
175 -d "a simple example test case" -c "$(< /tmp/user_data)"
176
147177
148Development Checklist178Development Checklist
149=====================179=====================
@@ -171,7 +201,7 @@ Development Checklist
171Execution201Execution
172=========202=========
173203
174Executing tests has three options:204Executing tests has several options:
175205
176* ``run`` an alias to run both ``collect`` and ``verify``206* ``run`` an alias to run both ``collect`` and ``verify``
177207
@@ -182,6 +212,10 @@ Executing tests has three options:
182* ``verify`` given a directory of test data, run the Python unit tests on212* ``verify`` given a directory of test data, run the Python unit tests on
183 it to generate results.213 it to generate results.
184214
215* ``tree_run`` and ``tree_collect`` are helpers to run ``run`` or ``collect``
216 respectively using a deb built from the copy of cloud-init in the current
217 working tree.
218
185Run219Run
186---220---
187The first example will provide a complete end-to-end run of data221The first example will provide a complete end-to-end run of data
@@ -238,6 +272,29 @@ without the more lengthy collect process. This can be done by running:
238The above command will run the verify scripts on the data discovered in272The above command will run the verify scripts on the data discovered in
239`/tmp/collection`.273`/tmp/collection`.
240274
275TreeRun and TreeCollect
276-----------------------
277
278If working on a cloud-init feature or resolving a bug, it may be useful to run
279the current copy of cloud-init in the integration testing environment. The
280integration testing suite can automatically build a deb based on the current
281working tree of cloud-init and run the test suite using this deb.
282
283The ``tree_run`` and ``tree_collect`` commands take the same arguments as the
284``run`` and ``collect`` commands. These commands will build a deb and write it
285into a tempfile, then start the test suite and pass that deb in. To build a
286deb only, and not run the test suite, the ``bddeb`` command can be used.
287
288Note that code in the cloud-init working tree that has not been committed
289when the cloud-init deb is built will still be included. To build a cloud-init
290deb from or use the ``tree_run`` command using a copy of cloud-init located in
291a different directory, use the option ``--cloud-init /path/to/cloud-init``.
292
293.. code-block:: bash
294
295 $ python3 -m tests.cloud_tests tree_run -n xenial -n zesty \
296 -t modules/final_message -t modules/write_files -v -r /tmp/result.yaml
297
241Run via tox298Run via tox
242-----------299-----------
243In order to avoid the need for dependencies and ease the setup and300In order to avoid the need for dependencies and ease the setup and
@@ -256,11 +313,13 @@ arguments.
256Architecture313Architecture
257============314============
258315
259The following outlines the process flow during a complete end-to-end LXD-backed test.316The following outlines the process flow during a complete end-to-end
317LXD-backed test.
260318
2611. Configuration3191. Configuration
262 * The back end and specific OS releases are verified as supported320 * The back end and specific OS releases are verified as supported
263 * The test or tests that need to be run are determined either by directory or by individual yaml321 * The test or tests that need to be run are determined either by
322 directory or by individual yaml
264323
2652. Image Creation3242. Image Creation
266 * Acquire the daily LXD image325 * Acquire the daily LXD image
@@ -285,5 +344,5 @@ The following outlines the process flow during a complete end-to-end LXD-backed
285344
2865. Results3455. Results
287 * If any failures were detected the test suite returns a failure346 * If any failures were detected the test suite returns a failure
288347 * Results can be dumped in yaml format to a specified file using the
289348 ``-r <result_file_name>.yaml`` option
diff --git a/tests/cloud_tests/__init__.py b/tests/cloud_tests/__init__.py
index 099c357..7959bd9 100644
--- a/tests/cloud_tests/__init__.py
+++ b/tests/cloud_tests/__init__.py
@@ -6,6 +6,7 @@ import os
6BASE_DIR = os.path.dirname(os.path.abspath(__file__))6BASE_DIR = os.path.dirname(os.path.abspath(__file__))
7TESTCASES_DIR = os.path.join(BASE_DIR, 'testcases')7TESTCASES_DIR = os.path.join(BASE_DIR, 'testcases')
8TEST_CONF_DIR = os.path.join(BASE_DIR, 'configs')8TEST_CONF_DIR = os.path.join(BASE_DIR, 'configs')
9TREE_BASE = os.sep.join(BASE_DIR.split(os.sep)[:-2])
910
1011
11def _initialize_logging():12def _initialize_logging():
diff --git a/tests/cloud_tests/__main__.py b/tests/cloud_tests/__main__.py
index ef7d187..74f29f6 100644
--- a/tests/cloud_tests/__main__.py
+++ b/tests/cloud_tests/__main__.py
@@ -2,11 +2,9 @@
22
3import argparse3import argparse
4import logging4import logging
5import shutil
6import sys5import sys
7import tempfile
86
9from tests.cloud_tests import (args, collect, manage, verify)7from tests.cloud_tests import args, bddeb, collect, manage, run_funcs, verify
10from tests.cloud_tests import LOG8from tests.cloud_tests import LOG
119
1210
@@ -22,28 +20,6 @@ def configure_log(args):
22 LOG.setLevel(level)20 LOG.setLevel(level)
2321
2422
25def run(args):
26 """
27 run full test suite
28 """
29 failed = 0
30 args.data_dir = tempfile.mkdtemp(prefix='cloud_test_data_')
31 LOG.debug('using tmpdir %s', args.data_dir)
32 try:
33 failed += collect.collect(args)
34 failed += verify.verify(args)
35 except Exception:
36 failed += 1
37 raise
38 finally:
39 # TODO: make this configurable via environ or cmdline
40 if failed:
41 LOG.warn('some tests failed, leaving data in %s', args.data_dir)
42 else:
43 shutil.rmtree(args.data_dir)
44 return failed
45
46
47def main():23def main():
48 """24 """
49 entry point for cloud test suite25 entry point for cloud test suite
@@ -80,9 +56,12 @@ def main():
80 # run handler56 # run handler
81 LOG.debug('running with args: %s\n', parsed)57 LOG.debug('running with args: %s\n', parsed)
82 return {58 return {
59 'bddeb': bddeb.bddeb,
83 'collect': collect.collect,60 'collect': collect.collect,
84 'create': manage.create,61 'create': manage.create,
85 'run': run,62 'run': run_funcs.run,
63 'tree_collect': run_funcs.tree_collect,
64 'tree_run': run_funcs.tree_run,
86 'verify': verify.verify,65 'verify': verify.verify,
87 }[parsed.subcmd](parsed)66 }[parsed.subcmd](parsed)
8867
diff --git a/tests/cloud_tests/args.py b/tests/cloud_tests/args.py
index b68cc98..d051737 100644
--- a/tests/cloud_tests/args.py
+++ b/tests/cloud_tests/args.py
@@ -3,9 +3,24 @@
3import os3import os
44
5from tests.cloud_tests import config, util5from tests.cloud_tests import config, util
6from tests.cloud_tests import LOG6from tests.cloud_tests import LOG, TREE_BASE
77
8ARG_SETS = {8ARG_SETS = {
9 'BDDEB': (
10 (('--bddeb-args',),
11 {'help': 'args to pass through to bddeb',
12 'action': 'store', 'default': None, 'required': False}),
13 (('--build-os',),
14 {'help': 'OS to use as build system (default is xenial)',
15 'action': 'store', 'choices': config.list_enabled_distros(),
16 'default': 'xenial', 'required': False}),
17 (('--build-platform',),
18 {'help': 'platform to use for build system (default is lxd)',
19 'action': 'store', 'choices': config.list_enabled_platforms(),
20 'default': 'lxd', 'required': False}),
21 (('--cloud-init',),
22 {'help': 'path to base of cloud-init tree', 'metavar': 'DIR',
23 'action': 'store', 'required': False, 'default': TREE_BASE}),),
9 'COLLECT': (24 'COLLECT': (
10 (('-p', '--platform'),25 (('-p', '--platform'),
11 {'help': 'platform(s) to run tests on', 'metavar': 'PLATFORM',26 {'help': 'platform(s) to run tests on', 'metavar': 'PLATFORM',
@@ -42,6 +57,10 @@ ARG_SETS = {
42 (('-d', '--data-dir'),57 (('-d', '--data-dir'),
43 {'help': 'directory to store test data in',58 {'help': 'directory to store test data in',
44 'action': 'store', 'metavar': 'DIR', 'required': True}),),59 'action': 'store', 'metavar': 'DIR', 'required': True}),),
60 'OUTPUT_DEB': (
61 (('--deb',),
62 {'help': 'path to write output deb to', 'metavar': 'FILE',
63 'action': 'store', 'required': True}),),
45 'RESULT': (64 'RESULT': (
46 (('-r', '--result'),65 (('-r', '--result'),
47 {'help': 'file to write results to',66 {'help': 'file to write results to',
@@ -66,10 +85,16 @@ ARG_SETS = {
66}85}
6786
68SUBCMDS = {87SUBCMDS = {
88 'bddeb': ('build cloud-init deb from tree',
89 ('BDDEB', 'OUTPUT_DEB', 'INTERFACE')),
69 'collect': ('collect test data',90 'collect': ('collect test data',
70 ('COLLECT', 'INTERFACE', 'OUTPUT', 'RESULT', 'SETUP')),91 ('COLLECT', 'INTERFACE', 'OUTPUT', 'RESULT', 'SETUP')),
71 'create': ('create new test case', ('CREATE', 'INTERFACE')),92 'create': ('create new test case', ('CREATE', 'INTERFACE')),
72 'run': ('run test suite', ('COLLECT', 'INTERFACE', 'RESULT', 'SETUP')),93 'run': ('run test suite', ('COLLECT', 'INTERFACE', 'RESULT', 'SETUP')),
94 'tree_collect': ('collect using current working tree',
95 ('BDDEB', 'COLLECT', 'INTERFACE', 'OUTPUT', 'RESULT')),
96 'tree_run': ('run using current working tree',
97 ('BDDEB', 'COLLECT', 'INTERFACE', 'RESULT')),
73 'verify': ('verify test data', ('INTERFACE', 'OUTPUT', 'RESULT')),98 'verify': ('verify test data', ('INTERFACE', 'OUTPUT', 'RESULT')),
74}99}
75100
@@ -81,6 +106,20 @@ def _empty_normalizer(args):
81 return args106 return args
82107
83108
109def normalize_bddeb_args(args):
110 """
111 normalize BDDEB arguments
112 args: parsed args
113 return_value: updated args, or None if errors encountered
114 """
115 # make sure cloud-init dir is accessible
116 if not (args.cloud_init and os.path.isdir(args.cloud_init)):
117 LOG.error('invalid cloud-init tree path')
118 return None
119
120 return args
121
122
84def normalize_create_args(args):123def normalize_create_args(args):
85 """124 """
86 normalize CREATE arguments125 normalize CREATE arguments
@@ -185,6 +224,17 @@ def normalize_output_args(args):
185 return args224 return args
186225
187226
227def normalize_output_deb_args(args):
228 """
229 normalize OUTPUT_DEB arguments
230 args: parsed args
231 return_value: updated args, or None if erros occurred
232 """
233 # make sure to use abspath for deb
234 args.deb = os.path.abspath(args.deb)
235 return args
236
237
188def normalize_setup_args(args):238def normalize_setup_args(args):
189 """239 """
190 normalize SETUP arguments240 normalize SETUP arguments
@@ -210,10 +260,12 @@ def normalize_setup_args(args):
210260
211261
212NORMALIZERS = {262NORMALIZERS = {
263 'BDDEB': normalize_bddeb_args,
213 'COLLECT': normalize_collect_args,264 'COLLECT': normalize_collect_args,
214 'CREATE': normalize_create_args,265 'CREATE': normalize_create_args,
215 'INTERFACE': _empty_normalizer,266 'INTERFACE': _empty_normalizer,
216 'OUTPUT': normalize_output_args,267 'OUTPUT': normalize_output_args,
268 'OUTPUT_DEB': normalize_output_deb_args,
217 'RESULT': _empty_normalizer,269 'RESULT': _empty_normalizer,
218 'SETUP': normalize_setup_args,270 'SETUP': normalize_setup_args,
219}271}
diff --git a/tests/cloud_tests/bddeb.py b/tests/cloud_tests/bddeb.py
220new file mode 100644272new file mode 100644
index 0000000..3b79cd2
--- /dev/null
+++ b/tests/cloud_tests/bddeb.py
@@ -0,0 +1,124 @@
1# This file is part of cloud-init. See LICENSE file for license information.
2
3from tests.cloud_tests import (config, LOG)
4from tests.cloud_tests import (platforms, images, snapshots, instances)
5from tests.cloud_tests.stage import (PlatformComponent, run_stage, run_single)
6
7from cloudinit import util as c_util
8
9from functools import partial
10import os
11
12build_deps = ['devscripts', 'equivs', 'git', 'tar']
13
14
15def _out(cmd_res):
16 """
17 get clean output from cmd result
18 """
19 return cmd_res[0].strip()
20
21
22def build_deb(args, instance):
23 """
24 build deb on system and copy out to location at args.deb
25 args: cmdline arguments
26 return_value: tuple of results and fail count
27 """
28 # update remote system package list and install build deps
29 LOG.debug('installing build deps')
30 pkgs = ' '.join(build_deps)
31 cmd = 'apt-get update && apt-get install --yes {}'.format(pkgs)
32 instance.execute(['/bin/sh', '-c', cmd])
33 instance.execute(['mk-build-deps', '--install', '-t',
34 'apt-get --no-install-recommends --yes', 'cloud-init'])
35
36 # local tmpfile that must be deleted
37 local_tarball = _out(c_util.subp(['mktemp'], capture=True))
38
39 try:
40 # paths to use in remote system
41 remote_tarball = _out(instance.execute(['mktemp']))
42 extract_dir = _out(instance.execute(['mktemp', '--directory']))
43 bddeb_path = os.path.join(extract_dir, 'packages', 'bddeb')
44 output_link = '/cloud-init_all.deb'
45 git_env = {'GIT_DIR': os.path.join(extract_dir, '.git'),
46 'GIT_WORK_TREE': extract_dir}
47
48 # create a tarball of cloud init tree and copy to remote system
49 LOG.debug('creating tarball of cloud-init at: %s', local_tarball)
50 c_util.subp(['tar', 'cf', local_tarball, '--owner', 'root',
51 '--group', 'root', '-C', args.cloud_init, '.'])
52 LOG.debug('copying to remote system at: %s', remote_tarball)
53 instance.push_file(local_tarball, remote_tarball)
54
55 # extract tarball in remote system and commit anything uncommitted
56 LOG.debug('extracting tarball in remote system at: %s', extract_dir)
57 instance.execute(['tar', 'xf', remote_tarball, '-C', extract_dir])
58 instance.execute(['git', 'commit', '-a', '-m', 'tmp', '--allow-empty'],
59 env=git_env)
60
61 # build the deb, ignoring missing deps (flake8)
62 LOG.debug('building deb in remote system at: %s', output_link)
63 bddeb_args = args.bddeb_args.split() if args.bddeb_args else []
64 instance.execute([bddeb_path, '-d'] + bddeb_args, env=git_env)
65
66 # copy the deb back to the host system
67 LOG.debug('copying built deb to host at: %s', args.deb)
68 instance.pull_file(output_link, args.deb)
69
70 finally:
71 os.remove(local_tarball)
72
73
74def setup_build(args):
75 """
76 set build system up then run build
77 args: cmdline arguments
78 return_value: tuple of results and fail count
79 """
80 res = ({}, 1)
81
82 # set up platform
83 LOG.info('setting up platform: %s', args.build_platform)
84 platform_config = config.load_platform_config(args.build_platform)
85 platform_call = partial(platforms.get_platform, args.build_platform,
86 platform_config)
87 with PlatformComponent(platform_call) as platform:
88
89 # set up image
90 LOG.info('acquiring image for os: %s', args.build_os)
91 img_conf = config.load_os_config(args.build_os)
92 image_call = partial(images.get_image, platform, img_conf)
93 with PlatformComponent(image_call) as image:
94
95 # set up snapshot
96 snapshot_call = partial(snapshots.get_snapshot, image)
97 with PlatformComponent(snapshot_call) as snapshot:
98
99 # create instance with cloud-config to set it up
100 LOG.info('creating instance to build deb in')
101 empty_cloud_config = "#cloud-config\n{}"
102 instance_call = partial(
103 instances.get_instance, snapshot, empty_cloud_config,
104 use_desc='build cloud-init deb')
105 with PlatformComponent(instance_call) as instance:
106
107 # build the deb
108 res = run_single('build deb on system',
109 partial(build_deb, args, instance))
110
111 return res
112
113
114def bddeb(args):
115 """
116 entry point for build deb
117 args: cmdline arguments
118 return_value: fail count
119 """
120 LOG.info('preparing to build cloud-init deb')
121 (res, failed) = run_stage('build deb', [partial(setup_build, args)])
122 return failed
123
124# vi: ts=4 expandtab
diff --git a/tests/cloud_tests/instances/base.py b/tests/cloud_tests/instances/base.py
index 9559d28..1782c1d 100644
--- a/tests/cloud_tests/instances/base.py
+++ b/tests/cloud_tests/instances/base.py
@@ -51,7 +51,7 @@ class Instance(object):
51 copy file at 'remote_path', from instance to 'local_path'51 copy file at 'remote_path', from instance to 'local_path'
52 """52 """
53 with open(local_path, 'wb') as fp:53 with open(local_path, 'wb') as fp:
54 fp.write(self.read_data(remote_path), encode=True)54 fp.write(self.read_data(remote_path))
5555
56 def push_file(self, local_path, remote_path):56 def push_file(self, local_path, remote_path):
57 """57 """
diff --git a/tests/cloud_tests/run_funcs.py b/tests/cloud_tests/run_funcs.py
58new file mode 10064458new file mode 100644
index 0000000..683a3f6
--- /dev/null
+++ b/tests/cloud_tests/run_funcs.py
@@ -0,0 +1,65 @@
1# This file is part of cloud-init. See LICENSE file for license information.
2
3from tests.cloud_tests import bddeb, collect, util, verify
4
5import os
6
7
8def tree_collect(args):
9 """
10 collect data using deb build from current tree
11 args: cmdline args
12 return_value: fail count
13 """
14 failed = 0
15
16 with util.TempDir(args) as tmpdir:
17 args.deb = os.path.join(tmpdir, 'cloud-init.deb')
18 try:
19 failed += bddeb.bddeb(args)
20 failed += collect.collect(args)
21 except Exception:
22 failed += 1
23 raise
24
25 return failed
26
27
28def tree_run(args):
29 """
30 run test suite using deb build from current tree
31 args: cmdline args
32 return_value: fail count
33 """
34 failed = 0
35
36 with util.TempDir(args) as tmpdir:
37 args.deb = os.path.join(tmpdir, 'cloud-init.deb')
38 try:
39 failed += bddeb.bddeb(args)
40 failed += run(args)
41 except Exception:
42 failed += 1
43 raise
44
45 return failed
46
47
48def run(args):
49 """
50 run test suite
51 """
52 failed = 0
53
54 with util.TempDir(args) as tmpdir:
55 args.data_dir = tmpdir
56 try:
57 failed += collect.collect(args)
58 failed += verify.verify(args)
59 except Exception:
60 failed += 1
61 raise
62
63 return failed
64
65# vi: ts=4 expandtab
diff --git a/tests/cloud_tests/util.py b/tests/cloud_tests/util.py
index 64a8667..18f54b4 100644
--- a/tests/cloud_tests/util.py
+++ b/tests/cloud_tests/util.py
@@ -3,6 +3,7 @@
3import glob3import glob
4import os4import os
5import random5import random
6import shutil
6import string7import string
7import tempfile8import tempfile
8import yaml9import yaml
@@ -160,4 +161,36 @@ def write_file(*args, **kwargs):
160 """161 """
161 c_util.write_file(*args, **kwargs)162 c_util.write_file(*args, **kwargs)
162163
164
165class TempDir(object):
166 """
167 temporary directory like tempfile.TemporaryDirectory, but configurable
168 """
169
170 def __init__(self, args):
171 """
172 setup and store args
173 args: cmdline arguments
174 """
175 self.args = args
176 self.tmpdir = None
177
178 def __enter__(self):
179 """
180 create tempdir
181 return_value: tempdir path
182 """
183 self.tmpdir = tempfile.mkdtemp(prefix='cloud_test_')
184 LOG.debug('using tmpdir: %s', self.tmpdir)
185 return self.tmpdir
186
187 def __exit__(self, etype, value, trace):
188 """
189 destroy tempdir if no errors occurred
190 """
191 if etype:
192 LOG.warn('erros occurred, leaving data in %s', self.tmpdir)
193 else:
194 shutil.rmtree(self.tmpdir)
195
163# vi: ts=4 expandtab196# vi: ts=4 expandtab

Subscribers

People subscribed via source and target branches