Merge ~smoser/curtin:ubuntu/devel-20180607 into curtin:ubuntu/devel

Proposed by Scott Moser
Status: Merged
Merged at revision: b78e920506bdd55e374a8337910301fe10a704f7
Proposed branch: ~smoser/curtin:ubuntu/devel-20180607
Merge into: curtin:ubuntu/devel
Diff against target: 560 lines (+234/-38)
15 files modified
curtin/commands/curthooks.py (+2/-2)
curtin/commands/extract.py (+1/-1)
curtin/commands/install.py (+16/-5)
curtin/url_helper.py (+1/-1)
curtin/util.py (+9/-4)
debian/changelog (+16/-0)
tests/unittests/test_commands_collect_logs.py (+26/-14)
tests/unittests/test_commands_extract.py (+72/-0)
tests/unittests/test_commands_install.py (+40/-0)
tests/unittests/test_util.py (+1/-1)
tests/vmtests/__init__.py (+1/-1)
tests/vmtests/image_sync.py (+1/-1)
tools/curtainer (+21/-6)
tools/vmtest-sync-images (+0/-1)
tox.ini (+27/-1)
Reviewer Review Type Date Requested Status
Ryan Harper (community) Approve
Server Team CI bot continuous-integration Approve
Review via email: mp+347635@code.launchpad.net

Commit message

+curtin (18.1-25-g9d0e557e-0ubuntu1) UNRELEASED; urgency=medium
+
+ * New upstream snapshot.
+ - Fix WorkingDir class to support already existing target directory.
+ (LP: #1775622)
+ - Fix extraction of local filesystem image. (LP: #1775630)
+ - Fix tip-pyflakes imported but unused call to util.get_platform_arch
+ - subp: update return value of subp with combine_capture=True.
+ - tox: add a xenial environments, default envlist changes.
+ - tests: Fix race on utcnow during timestamped curtin-log dir creation
+ (LP: #1772481)
+ - curtainer: patch source version from --source.
+ - pyflakes: fix unused variable references identified by pyflakes 2.0.0.
+
+ -- Scott Moser <email address hidden> Thu, 07 Jun 2018 14:59:20 -0400

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
Ryan Harper (raharper) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/curtin/commands/curthooks.py b/curtin/commands/curthooks.py
2index d45c3a8..a514560 100644
3--- a/curtin/commands/curthooks.py
4+++ b/curtin/commands/curthooks.py
5@@ -292,7 +292,7 @@ def setup_grub(cfg, target):
6 storage_cfg_odict = None
7 try:
8 storage_cfg_odict = extract_storage_ordered_dict(cfg)
9- except ValueError as e:
10+ except ValueError:
11 pass
12
13 if storage_cfg_odict:
14@@ -324,7 +324,7 @@ def setup_grub(cfg, target):
15 try:
16 (blockdev, part) = block.get_blockdev_for_partition(maybepart)
17 blockdevs.add(blockdev)
18- except ValueError as e:
19+ except ValueError:
20 # if there is no syspath for this device such as a lvm
21 # or raid device, then a ValueError is raised here.
22 LOG.debug("failed to find block device for %s", maybepart)
23diff --git a/curtin/commands/extract.py b/curtin/commands/extract.py
24index 69a9d18..ec7a791 100644
25--- a/curtin/commands/extract.py
26+++ b/curtin/commands/extract.py
27@@ -59,7 +59,7 @@ def extract_root_tgz_url(url, target):
28 def extract_root_fsimage_url(url, target):
29 path = _path_from_file_url(url)
30 if path != url or os.path.isfile(path):
31- return _extract_root_fsimage(path(url), target)
32+ return _extract_root_fsimage(path, target)
33
34 wfp = tempfile.NamedTemporaryFile(suffix=".img", delete=False)
35 wfp.close()
36diff --git a/curtin/commands/install.py b/curtin/commands/install.py
37index a8c4cf9..9e5406c 100644
38--- a/curtin/commands/install.py
39+++ b/curtin/commands/install.py
40@@ -111,12 +111,22 @@ class WorkingDir(object):
41 def __init__(self, config):
42 top_d = tempfile.mkdtemp()
43 state_d = os.path.join(top_d, 'state')
44+ scratch_d = os.path.join(top_d, 'scratch')
45+ for p in (state_d, scratch_d):
46+ os.mkdir(p)
47+
48 target_d = config.get('install', {}).get('target')
49 if not target_d:
50 target_d = os.path.join(top_d, 'target')
51- scratch_d = os.path.join(top_d, 'scratch')
52- for p in (state_d, target_d, scratch_d):
53- os.mkdir(p)
54+ try:
55+ util.ensure_dir(target_d)
56+ except OSError as e:
57+ raise ValueError(
58+ "Unable to create target directory '%s': %s" %
59+ (target_d, e))
60+ if os.listdir(target_d) != []:
61+ raise ValueError(
62+ "Provided target dir '%s' was not empty." % target_d)
63
64 netconf_f = os.path.join(state_d, 'network_config')
65 netstate_f = os.path.join(state_d, 'network_state')
66@@ -429,6 +439,7 @@ def cmd_install(args):
67
68 writeline_and_stdout(logfile, INSTALL_START_MSG)
69 args.reportstack.post_files = post_files
70+ workingd = None
71 try:
72 workingd = WorkingDir(cfg)
73 dd_images = util.get_dd_images(cfg.get('sources', {}))
74@@ -469,12 +480,12 @@ def cmd_install(args):
75 raise e
76 finally:
77 log_target_path = instcfg.get('save_install_log', SAVE_INSTALL_LOG)
78- if log_target_path:
79+ if log_target_path and workingd:
80 copy_install_log(logfile, workingd.target, log_target_path)
81
82 if instcfg.get('unmount', "") == "disabled":
83 LOG.info('Skipping unmount: config disabled target unmounting')
84- else:
85+ elif workingd:
86 # unmount everything (including iscsi disks)
87 util.do_umount(workingd.target, recursive=True)
88
89diff --git a/curtin/url_helper.py b/curtin/url_helper.py
90index d4d43a9..43c5c36 100644
91--- a/curtin/url_helper.py
92+++ b/curtin/url_helper.py
93@@ -227,7 +227,7 @@ def geturl(url, headers=None, headers_cb=None, exception_cb=None,
94 try:
95 return _geturl(url=url, headers=headers, headers_cb=headers_cb,
96 exception_cb=exception_cb, data=data)
97- except _ReRaisedException as e:
98+ except _ReRaisedException:
99 raise curexc.exc
100 except Exception as e:
101 curexc = e
102diff --git a/curtin/util.py b/curtin/util.py
103index de0eb88..7d06c09 100644
104--- a/curtin/util.py
105+++ b/curtin/util.py
106@@ -103,10 +103,11 @@ def _subp(args, data=None, rcs=None, env=None, capture=False,
107 (out, err) = sp.communicate(data)
108
109 # Just ensure blank instead of none.
110- if not out and capture:
111- out = b''
112- if not err and capture:
113- err = b''
114+ if capture or combine_capture:
115+ if not out:
116+ out = b''
117+ if not err:
118+ err = b''
119 if decode:
120 def ldecode(data, m='utf-8'):
121 if not isinstance(data, bytes):
122@@ -206,6 +207,8 @@ def subp(*args, **kwargs):
123 boolean indicating if stderr should be redirected to stdout. When True,
124 interleaved stderr and stdout will be returned as the first element of
125 a tuple.
126+ if combine_capture is True, then output is captured independent of
127+ the value of capture.
128 :param log_captured:
129 boolean indicating if output should be logged on capture. If
130 True, then stderr and stdout will be logged at DEBUG level. If
131@@ -521,6 +524,8 @@ def do_umount(mountpoint, recursive=False):
132
133
134 def ensure_dir(path, mode=None):
135+ if path == "":
136+ path = "."
137 try:
138 os.makedirs(path)
139 except OSError as e:
140diff --git a/debian/changelog b/debian/changelog
141index d7e9a49..dfd5560 100644
142--- a/debian/changelog
143+++ b/debian/changelog
144@@ -1,3 +1,19 @@
145+curtin (18.1-25-g9d0e557e-0ubuntu1) cosmic; urgency=medium
146+
147+ * New upstream snapshot.
148+ - Fix WorkingDir class to support already existing target directory.
149+ (LP: #1775622)
150+ - Fix extraction of local filesystem image. (LP: #1775630)
151+ - Fix tip-pyflakes imported but unused call to util.get_platform_arch
152+ - subp: update return value of subp with combine_capture=True.
153+ - tox: add a xenial environments, default envlist changes.
154+ - tests: Fix race on utcnow during timestamped curtin-log dir creation
155+ (LP: #1772481)
156+ - curtainer: patch source version from --source.
157+ - pyflakes: fix unused variable references identified by pyflakes 2.0.0.
158+
159+ -- Scott Moser <smoser@ubuntu.com> Thu, 07 Jun 2018 14:59:20 -0400
160+
161 curtin (18.1-17-gae48e86f-0ubuntu1) cosmic; urgency=medium
162
163 * New upstream snapshot.
164diff --git a/tests/unittests/test_commands_collect_logs.py b/tests/unittests/test_commands_collect_logs.py
165index bab36ae..6b958f4 100644
166--- a/tests/unittests/test_commands_collect_logs.py
167+++ b/tests/unittests/test_commands_collect_logs.py
168@@ -56,11 +56,12 @@ class TestCollectLogs(CiTestCase):
169 write_file(savefile, 'install:\n log_file: /tmp/savefile.log\n')
170 packdir = self.tmp_path('configs', _dir=self.new_root)
171 ensure_dir(packdir)
172- date = datetime.utcnow().strftime('%Y-%m-%d-%H-%M')
173 unusedcfg = os.path.join(packdir, 'config-001.yaml')
174 write_file(unusedcfg, 'install:\n log_file: /tmp/unused.log\n')
175- curtin_config = self.tmp_path(
176- 'curtin-logs-%s/curtin-config' % date, _dir=self.tmpdir)
177+ utcnow = datetime.utcnow()
178+ datestr = utcnow.strftime('%Y-%m-%d-%H-%M')
179+ tardir = self.tmp_path('curtin-logs-%s' % datestr, _dir=self.tmpdir)
180+ curtin_config = self.tmp_path('curtin-config', _dir=tardir)
181
182 self.assertEqual(
183 '/curtin/configs', collect_logs.CURTIN_PACK_CONFIG_DIR)
184@@ -72,8 +73,10 @@ class TestCollectLogs(CiTestCase):
185 new=packdir, autospec=None)
186 self.add_patch('shutil.rmtree', 'm_rmtree')
187 with mock.patch('sys.stderr'):
188- with self.assertRaises(SystemExit) as context_manager:
189- collect_logs.collect_logs_main(FakeArgs('my.tar'))
190+ with mock.patch('curtin.commands.collect_logs.datetime') as m_dt:
191+ with self.assertRaises(SystemExit) as context_manager:
192+ m_dt.utcnow.return_value = utcnow
193+ collect_logs.collect_logs_main(FakeArgs('my.tar'))
194 self.assertEqual('0', str(context_manager.exception))
195 expected_cfg = {'install': {'log_file': '/tmp/savefile.log'}}
196 with open(curtin_config, 'r') as f:
197@@ -83,8 +86,9 @@ class TestCollectLogs(CiTestCase):
198 """collect_logs_main sources all configs from /curtin/configs dir."""
199 savefile = self.tmp_path('absentinstall.log', _dir=self.new_root)
200 packdir = self.tmp_path('configs', _dir=self.new_root)
201- date = datetime.utcnow().strftime('%Y-%m-%d-%H-%M')
202- tardir = self.tmp_path('curtin-logs-%s' % date, _dir=self.tmpdir)
203+ utcnow = datetime.utcnow()
204+ datestr = utcnow.strftime('%Y-%m-%d-%H-%M')
205+ tardir = self.tmp_path('curtin-logs-%s' % datestr, _dir=self.tmpdir)
206 ensure_dir(packdir)
207 cfg1 = os.path.join(packdir, 'config-001.yaml')
208 cfg2 = os.path.join(packdir, 'config-002.yaml')
209@@ -101,8 +105,10 @@ class TestCollectLogs(CiTestCase):
210 new=packdir, autospec=None)
211 self.add_patch('shutil.rmtree', 'm_rmtree')
212 with mock.patch('sys.stderr'):
213- with self.assertRaises(SystemExit) as context_manager:
214- collect_logs.collect_logs_main(FakeArgs('my.tar'))
215+ with mock.patch('curtin.commands.collect_logs.datetime') as m_dt:
216+ with self.assertRaises(SystemExit) as context_manager:
217+ m_dt.utcnow.return_value = utcnow
218+ collect_logs.collect_logs_main(FakeArgs('my.tar'))
219 self.assertEqual('0', str(context_manager.exception))
220 self.assertEqual(['config-001.yaml', 'config-002.yaml'],
221 sorted(os.listdir(packdir)))
222@@ -232,8 +238,8 @@ class TestCreateTar(CiTestCase):
223 def setUp(self):
224 super(TestCreateTar, self).setUp()
225 self.new_root = self.tmp_dir()
226- date = datetime.utcnow().strftime('%Y-%m-%d-%H-%M')
227- self.tardir = 'curtin-logs-%s' % date
228+ self.utcnow = datetime.utcnow()
229+ self.tardir = 'curtin-logs-%s' % self.utcnow.strftime('%Y-%m-%d-%H-%M')
230 self.add_patch(
231 'curtin.commands.collect_logs._collect_system_info', 'm_sys_info')
232 self.tmpdir = self.tmp_path('mytemp', _dir=self.new_root)
233@@ -247,7 +253,9 @@ class TestCreateTar(CiTestCase):
234 self.add_patch('curtin.util.subp', 'mock_subp')
235 self.mock_subp.return_value = ('', '')
236 with mock.patch('sys.stderr'):
237- collect_logs.create_log_tarfile(tarfile, config={})
238+ with mock.patch('curtin.commands.collect_logs.datetime') as m_dt:
239+ m_dt.utcnow.return_value = self.utcnow
240+ collect_logs.create_log_tarfile(tarfile, config={})
241 self.assertIn(
242 mock.call(['tar', '-cvf', tarfile, self.tardir],
243 capture=True),
244@@ -264,7 +272,9 @@ class TestCreateTar(CiTestCase):
245 self.add_patch('curtin.util.subp', 'mock_subp')
246 self.mock_subp.return_value = ('', '')
247 with mock.patch('sys.stderr'):
248- collect_logs.create_log_tarfile(tarfile, config={})
249+ with mock.patch('curtin.commands.collect_logs.datetime') as m_dt:
250+ m_dt.utcnow.return_value = self.utcnow
251+ collect_logs.create_log_tarfile(tarfile, config={})
252 self.assertIn(
253 mock.call(['tar', '-cvf', tarfile, self.tardir],
254 capture=True),
255@@ -311,7 +321,9 @@ class TestCreateTar(CiTestCase):
256 'maas': {'consumer_key': 'ckey',
257 'token_key': 'tkey', 'token_secret': 'tsecret'}}}
258 with mock.patch('sys.stderr'):
259- collect_logs.create_log_tarfile(tarfile, config=config)
260+ with mock.patch('curtin.commands.collect_logs.datetime') as m_dt:
261+ m_dt.utcnow.return_value = self.utcnow
262+ collect_logs.create_log_tarfile(tarfile, config=config)
263 self.assertEqual(
264 [mock.call(self.tardir, ['ckey', 'tkey', 'tsecret'])],
265 self.m_redact.call_args_list)
266diff --git a/tests/unittests/test_commands_extract.py b/tests/unittests/test_commands_extract.py
267new file mode 100644
268index 0000000..e604d7f
269--- /dev/null
270+++ b/tests/unittests/test_commands_extract.py
271@@ -0,0 +1,72 @@
272+# This file is part of curtin. See LICENSE file for copyright and license info.
273+import os
274+
275+from .helpers import CiTestCase
276+
277+from curtin import util
278+from curtin.commands.extract import extract_root_fsimage_url
279+
280+
281+class TestExtractRootFsImageUrl(CiTestCase):
282+ """Test extract_root_fsimage_url."""
283+ def _fake_download(self, url, path):
284+ self.downloads.append(os.path.abspath(path))
285+ with open(path, "w") as fp:
286+ fp.write("fake content from " + url + "\n")
287+
288+ def setUp(self):
289+ super(TestExtractRootFsImageUrl, self).setUp()
290+ self.downloads = []
291+ self.add_patch("curtin.commands.extract.url_helper.download",
292+ "m_download", side_effect=self._fake_download)
293+ self.add_patch("curtin.commands.extract._extract_root_fsimage",
294+ "m__extract_root_fsimage")
295+
296+ def test_relative_file_url(self):
297+ """extract_root_fsimage_url supports relative file:// urls."""
298+ tmpd = self.tmp_dir()
299+ target = self.tmp_path("target_d", tmpd)
300+ startdir = os.getcwd()
301+ try:
302+ fname = "my.img"
303+ util.write_file(fname, fname + " data\n")
304+ extract_root_fsimage_url("file://" + fname, target)
305+ finally:
306+ os.chdir(startdir)
307+ self.assertEqual(1, self.m__extract_root_fsimage.call_count)
308+ self.assertEqual(0, self.m_download.call_count)
309+
310+ def test_absolute_file_url(self):
311+ """extract_root_fsimage_url supports absolute file:/// urls."""
312+ tmpd = self.tmp_dir()
313+ target = self.tmp_path("target_d", tmpd)
314+ fpath = self.tmp_path("my.img", tmpd)
315+ util.write_file(fpath, fpath + " data\n")
316+ extract_root_fsimage_url("file://" + fpath, target)
317+ self.assertEqual(1, self.m__extract_root_fsimage.call_count)
318+ self.assertEqual(0, self.m_download.call_count)
319+
320+ def test_http_url(self):
321+ """extract_root_fsimage_url supports http:// urls."""
322+ tmpd = self.tmp_dir()
323+ target = self.tmp_path("target_d", tmpd)
324+ myurl = "http://bogus.example.com/my.img"
325+ extract_root_fsimage_url(myurl, target)
326+ self.assertEqual(1, self.m__extract_root_fsimage.call_count)
327+ self.assertEqual(1, self.m_download.call_count)
328+ # ensure the file got cleaned up.
329+ self.assertEqual(1, len(self.downloads))
330+ self.assertEqual([], [f for f in self.downloads if os.path.exists(f)])
331+
332+ def test_file_path_not_url(self):
333+ """extract_root_fsimage_url supports normal file path without file:."""
334+ tmpd = self.tmp_dir()
335+ target = self.tmp_path("target_d", tmpd)
336+ fpath = self.tmp_path("my.img", tmpd)
337+ util.write_file(fpath, fpath + " data\n")
338+ extract_root_fsimage_url(os.path.abspath(fpath), target)
339+ self.assertEqual(1, self.m__extract_root_fsimage.call_count)
340+ self.assertEqual(0, self.m_download.call_count)
341+
342+
343+# vi: ts=4 expandtab syntax=python
344diff --git a/tests/unittests/test_commands_install.py b/tests/unittests/test_commands_install.py
345index 47f4497..9a64a79 100644
346--- a/tests/unittests/test_commands_install.py
347+++ b/tests/unittests/test_commands_install.py
348@@ -189,3 +189,43 @@ class TestCmdInstall(CiTestCase):
349 self.assertEqual(
350 [mock.call(self.logfile, target_dir, '/root/curtin-install.log')],
351 self.m_copy_log.call_args_list)
352+
353+
354+class TestWorkingDir(CiTestCase):
355+ def test_target_dir_may_exist(self):
356+ """WorkingDir supports existing empty target directory."""
357+ tmp_d = self.tmp_dir()
358+ work_d = self.tmp_path("work_d", tmp_d)
359+ target_d = self.tmp_path("target_d", tmp_d)
360+ ensure_dir(work_d)
361+ ensure_dir(target_d)
362+ with mock.patch("curtin.commands.install.tempfile.mkdtemp",
363+ return_value=work_d) as m_mkdtemp:
364+ workingdir = install.WorkingDir({'install': {'target': target_d}})
365+ self.assertEqual(1, m_mkdtemp.call_count)
366+ self.assertEqual(target_d, workingdir.target)
367+ self.assertEqual(target_d, workingdir.env().get('TARGET_MOUNT_POINT'))
368+
369+ def test_target_dir_with_content_raises_error(self):
370+ """WorkingDir raises ValueError on populated target_d."""
371+ tmp_d = self.tmp_dir()
372+ work_d = self.tmp_path("work_d", tmp_d)
373+ target_d = self.tmp_path("target_d", tmp_d)
374+ ensure_dir(work_d)
375+ ensure_dir(target_d)
376+ write_file(self.tmp_path("somefile.txt", target_d), "sometext")
377+ with mock.patch("curtin.commands.install.tempfile.mkdtemp",
378+ return_value=work_d):
379+ with self.assertRaises(ValueError):
380+ install.WorkingDir({'install': {'target': target_d}})
381+
382+ def test_target_dir_by_default_is_under_workd(self):
383+ """WorkingDir does not require target in config."""
384+ tmp_d = self.tmp_dir()
385+ work_d = self.tmp_path("work_d", tmp_d)
386+ ensure_dir(work_d)
387+ with mock.patch("curtin.commands.install.tempfile.mkdtemp",
388+ return_value=work_d) as m_mkdtemp:
389+ wd = install.WorkingDir({})
390+ self.assertEqual(1, m_mkdtemp.call_count)
391+ self.assertTrue(wd.target.startswith(work_d + "/"))
392diff --git a/tests/unittests/test_util.py b/tests/unittests/test_util.py
393index 65175c5..483cd5d 100644
394--- a/tests/unittests/test_util.py
395+++ b/tests/unittests/test_util.py
396@@ -260,7 +260,7 @@ class TestSubp(CiTestCase):
397 data = b'hello world'
398 (out, err) = util.subp(self.stdin2err, combine_capture=True,
399 decode=False, data=data)
400- self.assertIsNone(err)
401+ self.assertEqual(err, b'')
402 self.assertEqual(out, data)
403
404 def test_returns_none_if_no_capture(self):
405diff --git a/tests/vmtests/__init__.py b/tests/vmtests/__init__.py
406index 5c30a83..0c3d17e 100644
407--- a/tests/vmtests/__init__.py
408+++ b/tests/vmtests/__init__.py
409@@ -1091,7 +1091,7 @@ class VMBaseClass(TestCase):
410 # mount output disk
411 try:
412 cls.collect_output()
413- except Exception as e:
414+ except Exception:
415 cls.tearDownClass()
416 raise
417
418diff --git a/tests/vmtests/image_sync.py b/tests/vmtests/image_sync.py
419index bbe523a..e2cedc1 100644
420--- a/tests/vmtests/image_sync.py
421+++ b/tests/vmtests/image_sync.py
422@@ -174,7 +174,7 @@ class CurtinVmTestMirror(mirrors.ObjectFilterMirror):
423 except IOError as e:
424 if e.errno != errno.ENOENT:
425 raise
426- except JSONDecodeError as e:
427+ except JSONDecodeError:
428 jsonfile = os.path.join(self.out_d, dpath)
429 sys.stderr.write("Decode error in:\n "
430 "content_id=%s\n "
431diff --git a/tools/curtainer b/tools/curtainer
432index ca698c6..466d719 100755
433--- a/tools/curtainer
434+++ b/tools/curtainer
435@@ -17,10 +17,13 @@ Usage: ${0##*/} [ options ] <image> name
436 start a container of image (ubuntu-daily:xenial) and install curtin.
437
438 options:
439- --proposed enable proposed
440- --daily enable daily curtin archive
441- --source D grab the source deb, unpack inside, and copy unpacked
442- source out to 'D'
443+ --no-patch-version do not patch source dir/curtin/version.py
444+ by default, --source will have version.py updated
445+ with dpkg's version.
446+ --proposed enable proposed
447+ --daily enable daily curtin archive
448+ --source D grab the source deb, unpack inside, and copy unpacked
449+ source out to 'D'
450 EOF
451 }
452
453@@ -114,7 +117,7 @@ get_source() {
454
455 main() {
456 local short_opts="hv"
457- local long_opts="help,daily,proposed,source:,verbose"
458+ local long_opts="help,daily,no-patch-version,proposed,source:,verbose"
459 local getopt_out=""
460 getopt_out=$(getopt --name "${0##*/}" \
461 --options "${short_opts}" --long "${long_opts}" -- "$@") &&
462@@ -123,13 +126,14 @@ main() {
463
464 local cur="" next=""
465 local proposed=false daily=false src="" name="" maxwait=30
466- local eatmydata="eatmydata" getsource="none"
467+ local eatmydata="eatmydata" getsource="none" patch_version=true
468
469 while [ $# -ne 0 ]; do
470 cur="$1"; next="$2";
471 case "$cur" in
472 -h|--help) Usage ; exit 0;;
473 --source) getsource="$next"; shift;;
474+ --no-patch-version) patch_version=false;;
475 --proposed) proposed=true;;
476 --daily) daily=true;;
477 -v|--verbose) VERBOSITY=$((${VERBOSITY}+1));;
478@@ -147,6 +151,7 @@ main() {
479 if [ "$getsource" != "none" ]; then
480 [ ! -e "$getsource" ] || fail "source output '$getsource' exists."
481 fi
482+ getsource="${getsource%/}"
483
484 lxc launch "$src" "$name" || fail "failed lxc launch $src $name"
485 CONTAINER=$name
486@@ -218,6 +223,16 @@ main() {
487 if [ "$src_ver" != "$pkg_ver" ]; then
488 fail "source version ($src_ver) != package version ($pkg_ver)"
489 fi
490+ if "${patch_version}"; then
491+ local verfile="$getsource/curtin/version.py"
492+ grep -q "@@PACKAGED_VERSION@@" "$verfile" ||
493+ fail "failed patching version: " \
494+ "@@PACKAGED_VERSION@@ not found in $verfile"
495+ sed -i.curtainer-dist \
496+ "s,@@PACKAGED_VERSION@@,${pkg_ver}," "$verfile" ||
497+ fail "failed modifying $verfile"
498+ debug 1 "patched $verfile pkg version to $pkg_ver."
499+ fi
500 inside "$name" rm -Rf "$isrcd" ||
501 fail "failed removal of extract dir"
502 debug 1 "put source for curtin at $src_ver in $getsource"
503diff --git a/tools/vmtest-sync-images b/tools/vmtest-sync-images
504index 3d82b62..762702b 100755
505--- a/tools/vmtest-sync-images
506+++ b/tools/vmtest-sync-images
507@@ -18,7 +18,6 @@ from tests.vmtests import (
508 IMAGE_DIR, IMAGE_SRC_URL, sync_images)
509 from tests.vmtests.image_sync import ITEM_NAME_FILTERS
510 from tests.vmtests.helpers import (find_arches, find_releases_by_distro)
511-from curtin.util import get_platform_arch
512
513
514 def _fmt_list_filter(filter_name, matches):
515diff --git a/tox.ini b/tox.ini
516index c1748c3..6f14381 100644
517--- a/tox.ini
518+++ b/tox.ini
519@@ -1,7 +1,14 @@
520 [tox]
521 minversion = 1.6
522 skipsdist = True
523-envlist = py27, py3, py3-flake8, py3-pylint, py27-pylint, trusty-check, trusty-py27, trusty-py3
524+envlist =
525+ py3-flake8,
526+ py27,
527+ py3,
528+ py3-pylint,
529+ py27-pylint,
530+ trusty-py27,
531+ xenial-py3
532
533 [tox:jenkins]
534 downloadcache = ~/cache/pip
535@@ -91,6 +98,25 @@ basepython = python3
536 commands = {envpython} {toxinidir}/tools/noproxy nosetests \
537 {posargs:tests/unittests}
538
539+[testenv:xenial]
540+deps =
541+ mock==1.3.0
542+ nose==1.3.7
543+ pyyaml==3.11
544+ oauthlib==1.0.3
545+
546+[testenv:xenial-py27]
547+basepython = python27
548+deps = {[testenv:xenial]deps}
549+commands = {envpython} {toxinidir}/tools/noproxy nosetests \
550+ {posargs:tests/unittests}
551+
552+[testenv:xenial-py3]
553+basepython = python3
554+deps = {[testenv:xenial]deps}
555+commands = {envpython} {toxinidir}/tools/noproxy nosetests \
556+ {posargs:tests/unittests}
557+
558 [testenv:tip-pycodestyle]
559 commands = {envpython} -m pycodestyle {posargs:curtin/ tests/ tools/}
560 deps = pycodestyle

Subscribers

People subscribed via source and target branches