Merge lp:~raharper/curtin/trunk.vmtest-reuse-output into lp:~curtin-dev/curtin/trunk

Proposed by Ryan Harper
Status: Merged
Merged at revision: 500
Proposed branch: lp:~raharper/curtin/trunk.vmtest-reuse-output
Merge into: lp:~curtin-dev/curtin/trunk
Diff against target: 220 lines (+62/-37)
3 files modified
doc/topics/integration-testing.rst (+9/-0)
tests/vmtests/__init__.py (+46/-34)
tools/jenkins-runner (+7/-3)
To merge this branch: bzr merge lp:~raharper/curtin/trunk.vmtest-reuse-output
Reviewer Review Type Date Requested Status
Scott Moser (community) Approve
Server Team CI bot continuous-integration Approve
Review via email: mp+318150@code.launchpad.net

Description of the change

Allow re-use of vmtest output

When developing or debugging vmtest unittest failures of collected data
it's useful to skip the install/boot/collect phases and just re-run the
unittest against a directory of collected output.

Introduce CURTIN_VMTEST_REUSE_TOPDIR setting to enable this. If the testcase directory is present then vmtest will skip vmlaunch/boot/collect
stages. If the directory is missing, the test runs as usual.

CURTIN_VMTEST_REUSE_TOPDIR=1 ./tools/jenkins-runner \
  tests/vmtests/test_basic.py:XenialTestBasic

To post a comment you must log in.
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 :

add documentation of the new environment variable (as you have in the commit message)

the other question is that this change possibly hides failures ...
I'm just reading the diff in line and not thinking too much, but if an error causaed there to be no grub_config or reporting_config, it looks like we'd now just go on, where before we would fail a test.

review: Needs Fixing
Revision history for this message
Ryan Harper (raharper) wrote :

On Thu, Feb 23, 2017 at 2:18 PM, Scott Moser <email address hidden> wrote:

> Review: Needs Fixing
>
> add documentation of the new environment variable (as you have in the
> commit message)
>

ACK

>
> the other question is that this change possibly hides failures ...
> I'm just reading the diff in line and not thinking too much, but if an
> error causaed there to be no grub_config or reporting_config, it looks like
> we'd now just go on, where before we would fail a test.
>

we avoid *rewriting* files if they exist; I don't think that skips any
errors;

The basic concept is, if the tempdir has a cls.__name__ directory, to
rebuild the cls and TempDir paths to files that may get referenced in the
unittests;
however, if we're re-using, we don't want to rewrite the contents a second
time; so all of those checks are if not os.path.exists(foo): write_foo().

>
> --
> https://code.launchpad.net/~raharper/curtin/trunk.vmtest-
> reuse-output/+merge/318150
> You are the owner of lp:~raharper/curtin/trunk.vmtest-reuse-output.
>

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

On Thu, 23 Feb 2017, Ryan Harper wrote:

> On Thu, Feb 23, 2017 at 2:18 PM, Scott Moser <email address hidden> wrote:
>
> > Review: Needs Fixing
> >
> > add documentation of the new environment variable (as you have in the
> > commit message)
> >
>
> ACK
>
>
> >
> > the other question is that this change possibly hides failures ...
> > I'm just reading the diff in line and not thinking too much, but if an
> > error causaed there to be no grub_config or reporting_config, it looks like
> > we'd now just go on, where before we would fail a test.
> >
>
> we avoid *rewriting* files if they exist; I don't think that skips any
> errors;
>
> The basic concept is, if the tempdir has a cls.__name__ directory, to
> rebuild the cls and TempDir paths to files that may get referenced in the
> unittests;
> however, if we're re-using, we don't want to rewrite the contents a second
> time; so all of those checks are if not os.path.exists(foo): write_foo().
>

Thats sane then.
As I said, i just looked quick an thought maybe you were just going to
skip over errors.

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)
473. By Ryan Harper

merge from trunk

474. By Ryan Harper

Do not attempt to re-write launch configs when reusing result data

475. By Ryan Harper

merge from trunk

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
476. By Ryan Harper

merge from trunk

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
477. By Ryan Harper

merge from trunk

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 :

Over all this looks really good. I have some minor nits.
I do think we should respect/expect CURTIN_VMTEST_REUSE_TOPDIR in python in addition to tools/jenkins-runner.

Revision history for this message
Ryan Harper (raharper) wrote :

Thanks for the review, I'll clean up the mkdir stuff, make sure to use the environment variable to set cls.restored value instead of just the presence of the tempdir.

478. By Ryan Harper

merge from trunk

479. By Ryan Harper

Use environment variable to set restored class attribute

480. By Ryan Harper

Fix exception message (include the tmpdir value)

481. By Ryan Harper

Reorder exception message

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 :

one tiny nit. fix that and i approve.

review: Approve
482. By Ryan Harper

bool() REUSE_TOPDIR integer

483. By Ryan Harper

Add documentation of CURTIN_VMTEST_REUSE_TOPDIR

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'doc/topics/integration-testing.rst'
2--- doc/topics/integration-testing.rst 2017-04-26 16:27:20 +0000
3+++ doc/topics/integration-testing.rst 2017-05-19 20:26:52 +0000
4@@ -169,6 +169,15 @@
5 If you set this value, you must ensure that the directory is either
6 non-existent or clean.
7
8+- ``CURTIN_VMTEST_REUSE_TOPDIR``: default 0
9+
10+ If this variable is set to 1, then vmtest will detect if the test
11+ specified already exists in the configured ``CURTIN_VMTEST_TOPDIR``
12+ location. If present, vmtest will skip executing the install and
13+ boot phase of vmtest, and install just execute the unittests
14+ specified. This allows developers to re-run unittests on existing
15+ data that's already been collected.
16+
17 - ``CURTIN_VMTEST_LOG``: default $TMPDIR/vmtest-<timestamp>.log
18
19 Vmtest writes extended log information to this file.
20
21=== modified file 'tests/vmtests/__init__.py'
22--- tests/vmtests/__init__.py 2017-05-19 15:23:30 +0000
23+++ tests/vmtests/__init__.py 2017-05-19 20:26:52 +0000
24@@ -44,6 +44,7 @@
25 OUTPUT_DISK_NAME = 'output_disk.img'
26 BOOT_TIMEOUT = int(os.environ.get("CURTIN_VMTEST_BOOT_TIMEOUT", 300))
27 INSTALL_TIMEOUT = int(os.environ.get("CURTIN_VMTEST_INSTALL_TIMEOUT", 3000))
28+REUSE_TOPDIR = bool(int(os.environ.get("CURTIN_VMTEST_REUSE_TOPDIR", 0)))
29
30 _TOPDIR = None
31
32@@ -260,16 +261,9 @@
33 output_disk = None
34
35 def __init__(self, name, user_data):
36+ self.restored = REUSE_TOPDIR
37 # Create tmpdir
38 self.tmpdir = os.path.join(_topdir(), name)
39- try:
40- os.mkdir(self.tmpdir)
41- except OSError as e:
42- if e.errno == errno.EEXIST:
43- raise ValueError("name '%s' already exists in %s" %
44- (name, _topdir))
45- else:
46- raise e
47
48 # make subdirs
49 self.collect = os.path.join(self.tmpdir, "collect")
50@@ -280,12 +274,25 @@
51
52 self.dirs = (self.collect, self.install, self.boot, self.logs,
53 self.disks)
54+
55+ self.success_file = os.path.join(self.logs, "success")
56+ self.errors_file = os.path.join(self.logs, "errors.json")
57+ self.target_disk = os.path.join(self.disks, "install_disk.img")
58+ self.seed_disk = os.path.join(self.boot, "seed.img")
59+ self.output_disk = os.path.join(self.boot, OUTPUT_DISK_NAME)
60+
61+ if self.restored:
62+ if os.path.exists(self.tmpdir):
63+ logger.info('Reusing existing TempDir: %s', self.tmpdir)
64+ return
65+ else:
66+ raise ValueError("REUSE_TOPDIR set, but TempDir not found:"
67+ " %s" % self.tmpdir)
68+
69+ os.mkdir(self.tmpdir)
70 for d in self.dirs:
71 os.mkdir(d)
72
73- self.success_file = os.path.join(self.logs, "success")
74- self.errors_file = os.path.join(self.logs, "errors.json")
75-
76 # write cloud-init for installed system
77 meta_data_file = os.path.join(self.install, "meta-data")
78 with open(meta_data_file, "w") as fp:
79@@ -296,21 +303,18 @@
80
81 # create target disk
82 logger.debug('Creating target disk')
83- self.target_disk = os.path.join(self.disks, "install_disk.img")
84 subprocess.check_call(["qemu-img", "create", "-f", TARGET_IMAGE_FORMAT,
85 self.target_disk, "10G"],
86 stdout=DEVNULL, stderr=subprocess.STDOUT)
87
88 # create seed.img for installed system's cloud init
89 logger.debug('Creating seed disk')
90- self.seed_disk = os.path.join(self.boot, "seed.img")
91 subprocess.check_call(["cloud-localds", self.seed_disk,
92 user_data_file, meta_data_file],
93 stdout=DEVNULL, stderr=subprocess.STDOUT)
94
95 # create output disk, mount ro
96 logger.debug('Creating output disk')
97- self.output_disk = os.path.join(self.boot, OUTPUT_DISK_NAME)
98 subprocess.check_call(["qemu-img", "create", "-f", TARGET_IMAGE_FORMAT,
99 self.output_disk, "10M"],
100 stdout=DEVNULL, stderr=subprocess.STDOUT)
101@@ -549,7 +553,6 @@
102 cls.boot_log = os.path.join(cls.td.logs, 'boot-serial.log')
103 logger.debug('Install console log: {}'.format(cls.install_log))
104 logger.debug('Boot console log: {}'.format(cls.boot_log))
105- ftypes = cls.get_test_files()
106
107 # if interactive, launch qemu without 'background & wait'
108 if cls.interactive:
109@@ -569,6 +572,7 @@
110 cmd.extend(["--append=" + cls.extra_kern_args])
111
112 # publish the root tarball
113+ ftypes = cls.get_test_files()
114 install_src = "PUBURL/" + os.path.basename(ftypes[cls.target_ftype])
115 if cls.target_ftype == 'vmtest.root-tgz':
116 cmd.append("--publish=%s" % ftypes['vmtest.root-tgz'])
117@@ -652,10 +656,11 @@
118 # proxy config
119 configs = [cls.conf_file]
120 cls.proxy = get_apt_proxy()
121- if cls.proxy is not None:
122+ if cls.proxy is not None and not cls.td.restored:
123 proxy_config = os.path.join(cls.td.install, 'proxy.cfg')
124- with open(proxy_config, "w") as fp:
125- fp.write(json.dumps({'apt_proxy': cls.proxy}) + "\n")
126+ if not os.path.exists(proxy_config):
127+ with open(proxy_config, "w") as fp:
128+ fp.write(json.dumps({'apt_proxy': cls.proxy}) + "\n")
129 configs.append(proxy_config)
130
131 uefi_flags = []
132@@ -666,8 +671,9 @@
133
134 # always attempt to update target nvram (via grub)
135 grub_config = os.path.join(cls.td.install, 'grub.cfg')
136- with open(grub_config, "w") as fp:
137- fp.write(json.dumps({'grub': {'update_nvram': True}}))
138+ if not os.path.exists(grub_config):
139+ with open(grub_config, "w") as fp:
140+ fp.write(json.dumps({'grub': {'update_nvram': True}}))
141 configs.append(grub_config)
142
143 if cls.dirty_disks and storage_config:
144@@ -690,20 +696,21 @@
145 reporting_config = os.path.join(cls.td.install, 'reporting.cfg')
146 localhost_url = ('http://' + get_lan_ip() +
147 ':{:d}/'.format(reporting_logger.port))
148- with open(reporting_config, 'w') as fp:
149- fp.write(json.dumps({
150- 'install': {
151- 'log_file': '/tmp/install.log',
152- 'post_files': ['/tmp/install.log'],
153- },
154- 'reporting': {
155- 'maas': {
156- 'level': 'DEBUG',
157- 'type': 'webhook',
158- 'endpoint': localhost_url,
159- },
160- },
161- }))
162+ if not os.path.exists(reporting_config) and not cls.td.restored:
163+ with open(reporting_config, 'w') as fp:
164+ fp.write(json.dumps({
165+ 'install': {
166+ 'log_file': '/tmp/install.log',
167+ 'post_files': ['/tmp/install.log'],
168+ },
169+ 'reporting': {
170+ 'maas': {
171+ 'level': 'DEBUG',
172+ 'type': 'webhook',
173+ 'endpoint': localhost_url,
174+ },
175+ },
176+ }))
177 configs.append(reporting_config)
178
179 cmd.extend(uefi_flags + netdevs + disks +
180@@ -713,6 +720,11 @@
181 ["--config=%s" % f for f in configs] +
182 [install_src])
183
184+ # don't run the vm stages if we're just re-running testcases on output
185+ if cls.td.restored:
186+ logger.info('Using existing outputdata, skipping install/boot')
187+ return
188+
189 # run vm with installer
190 lout_path = os.path.join(cls.td.logs, "install-launch.log")
191 logger.info('Running curtin installer: {}'.format(cls.install_log))
192
193=== modified file 'tools/jenkins-runner'
194--- tools/jenkins-runner 2017-02-17 21:02:20 +0000
195+++ tools/jenkins-runner 2017-05-19 20:26:52 +0000
196@@ -3,6 +3,8 @@
197 topdir="${CURTIN_VMTEST_TOPDIR:-${WORKSPACE:-$PWD}/output}"
198 pkeep=${CURTIN_VMTEST_KEEP_DATA_PASS:-logs,collect}
199 fkeep=${CURTIN_VMTEST_KEEP_DATA_FAIL:-logs,collect}
200+reuse=${CURTIN_VMTEST_REUSE_TOPDIR:-0}
201+export CURTIN_VMTEST_REUSE_TOPDIR=$reuse
202 export CURTIN_VMTEST_IMAGE_SYNC=${CURTIN_VMTEST_IMAGE_SYNC:-0}
203 export CURTIN_VMTEST_KEEP_DATA_PASS=$pkeep
204 export CURTIN_VMTEST_KEEP_DATA_FAIL=$fkeep
205@@ -13,10 +15,12 @@
206
207 fail() { echo "$@" 1>&2; exit 1; }
208
209-if [ -d "$topdir" ]; then
210- fail "topdir '$topdir' existed."
211+if [ "$reuse" != "1" ]; then
212+ if [ -d "$topdir" ]; then
213+ fail "topdir '$topdir' existed."
214+ fi
215+ mkdir -p "$topdir" || fail "failed mkdir $topdir"
216 fi
217-mkdir -p "$topdir" || fail "failed mkdir $topdir"
218
219 start_s=$(date +%s)
220 parallel=${CURTIN_VMTEST_PARALLEL}

Subscribers

People subscribed via source and target branches