Merge ~chad.smith/cloud-init:ubuntu/devel into cloud-init:ubuntu/devel
- Git
- lp:~chad.smith/cloud-init
- ubuntu/devel
- Merge into ubuntu/devel
Proposed by
Chad Smith
Status: | Merged | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Merged at revision: | 04abd70b222976007b07afc5b1fa70c0a4de882e | ||||||||||||
Proposed branch: | ~chad.smith/cloud-init:ubuntu/devel | ||||||||||||
Merge into: | cloud-init:ubuntu/devel | ||||||||||||
Diff against target: |
814 lines (+371/-90) 15 files modified
ChangeLog (+52/-0) cloudinit/cmd/tests/test_clean.py (+2/-1) cloudinit/cmd/tests/test_status.py (+2/-1) cloudinit/sources/DataSourceOVF.py (+16/-5) cloudinit/tests/helpers.py (+9/-11) cloudinit/version.py (+1/-1) config/cloud.cfg.tmpl (+2/-0) debian/changelog (+16/-0) tests/cloud_tests/collect.py (+3/-2) tests/cloud_tests/platforms/lxd/instance.py (+106/-26) tests/unittests/test_ds_identify.py (+40/-3) tests/unittests/test_handler/test_schema.py (+7/-5) tools/ds-identify (+34/-19) tools/run-centos (+78/-13) tox.ini (+3/-3) |
||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Server Team CI bot | continuous-integration | Approve | |
Scott Moser | Pending | ||
Review via email: mp+338591@code.launchpad.net |
Commit message
Description of the change
Upstream snapshot for cloud-init master to sync 18.1 release to bionic
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)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/ChangeLog b/ChangeLog |
2 | index 31c2dcb..be4c357 100644 |
3 | --- a/ChangeLog |
4 | +++ b/ChangeLog |
5 | @@ -1,3 +1,55 @@ |
6 | +18.1: |
7 | + - OVF: Fix VMware support for 64-bit platforms. [Sankar Tanguturi] |
8 | + - ds-identify: Fix searching for iso9660 OVF cdroms. (LP: #1749980) |
9 | + - SUSE: Fix groups used for ownership of cloud-init.log [Robert Schweikert] |
10 | + - ds-identify: check /writable/system-data/ for nocloud seed. |
11 | + (LP: #1747070) |
12 | + - tests: run nosetests in cloudinit/ directory, fix py26 fallout. |
13 | + - tools: run-centos: git clone rather than tar. |
14 | + - tests: add support for logs with lxd from snap and future lxd 3. |
15 | + (LP: #1745663) |
16 | + - EC2: Fix get_instance_id called against cached datasource pickle. |
17 | + (LP: #1748354) |
18 | + - cli: fix cloud-init status to report running when before result.json |
19 | + (LP: #1747965) |
20 | + - net: accept network-config in netplan format for renaming interfaces |
21 | + (LP: #1709715) |
22 | + - Fix ssh keys validation in ssh_util [Tatiana Kholkina] |
23 | + - docs: Update RTD content for cloud-init subcommands. |
24 | + - OVF: Extend well-known labels to include OVFENV. (LP: #1698669) |
25 | + - Fix potential cases of uninitialized variables. (LP: #1744796) |
26 | + - tests: Collect script output as binary, collect systemd journal, fix lxd. |
27 | + - HACKING.rst: mention setting user name and email via git config. |
28 | + - Azure VM Preprovisioning support. [Douglas Jordan] (LP: #1734991) |
29 | + - tools/read-version: Fix read-version when in a git worktree. |
30 | + - docs: Fix typos in docs and one debug message. [Florian Grignon] |
31 | + - btrfs: support resizing if root is mounted ro. |
32 | + [Robert Schweikert] (LP: #1734787) |
33 | + - OpenNebula: Improve network configuration support. |
34 | + [Akihiko Ota] (LP: #1719157, #1716397, #1736750) |
35 | + - tests: Fix EC2 Platform to return console output as bytes. |
36 | + - tests: Fix attempted use of /run in a test case. |
37 | + - GCE: Improvements and changes to ssh key behavior for default user. |
38 | + [Max Illfelder] (LP: #1670456, #1707033, #1707037, #1707039) |
39 | + - subp: make ProcessExecutionError have expected types in stderr, stdout. |
40 | + - tests: when querying ntp server, do not do dns resolution. |
41 | + - Recognize uppercase vfat disk labels [James Penick] (LP: #1598783) |
42 | + - tests: remove zesty as supported OS to test [Joshua Powers] |
43 | + - Do not log warning on config files that represent None. (LP: #1742479) |
44 | + - tests: Use git hash pip dependency format for pylxd. |
45 | + - tests: add integration requirements text file [Joshua Powers] |
46 | + - MAAS: add check_instance_id based off oauth tokens. (LP: #1712680) |
47 | + - tests: update apt sources list test [Joshua Powers] |
48 | + - tests: clean up image properties [Joshua Powers] |
49 | + - tests: rename test ssh keys to avoid appearance of leaking private keys. |
50 | + [Joshua Powers] |
51 | + - tests: Enable AWS EC2 Integration Testing [Joshua Powers] |
52 | + - cli: cloud-init clean handles symlinks (LP: #1741093) |
53 | + - SUSE: Add a basic test of network config rendering. [Robert Schweikert] |
54 | + - Azure: Only bounce network when necessary. (LP: #1722668) |
55 | + - lint: Fix lints seen by pylint version 1.8.1. |
56 | + - cli: Fix error in cloud-init modules --mode=init. (LP: #1736600) |
57 | + |
58 | 17.2: |
59 | - ds-identify: failure in NoCloud due to unset variable usage. |
60 | (LP: #1737704) |
61 | diff --git a/cloudinit/cmd/tests/test_clean.py b/cloudinit/cmd/tests/test_clean.py |
62 | index 6713af4..5a3ec3b 100644 |
63 | --- a/cloudinit/cmd/tests/test_clean.py |
64 | +++ b/cloudinit/cmd/tests/test_clean.py |
65 | @@ -165,10 +165,11 @@ class TestClean(CiTestCase): |
66 | wrap_and_call( |
67 | 'cloudinit.cmd.clean', |
68 | {'Init': {'side_effect': self.init_class}, |
69 | + 'sys.exit': {'side_effect': self.sys_exit}, |
70 | 'sys.argv': {'new': ['clean', '--logs']}}, |
71 | clean.main) |
72 | |
73 | - self.assertRaisesCodeEqual(0, context_manager.exception.code) |
74 | + self.assertEqual(0, context_manager.exception.code) |
75 | self.assertFalse( |
76 | os.path.exists(self.log1), 'Unexpected log {0}'.format(self.log1)) |
77 | |
78 | diff --git a/cloudinit/cmd/tests/test_status.py b/cloudinit/cmd/tests/test_status.py |
79 | index 4a5a8c0..37a8993 100644 |
80 | --- a/cloudinit/cmd/tests/test_status.py |
81 | +++ b/cloudinit/cmd/tests/test_status.py |
82 | @@ -380,10 +380,11 @@ class TestStatus(CiTestCase): |
83 | wrap_and_call( |
84 | 'cloudinit.cmd.status', |
85 | {'sys.argv': {'new': ['status']}, |
86 | + 'sys.exit': {'side_effect': self.sys_exit}, |
87 | '_is_cloudinit_disabled': (False, ''), |
88 | 'Init': {'side_effect': self.init_class}}, |
89 | status.main) |
90 | - self.assertRaisesCodeEqual(0, context_manager.exception.code) |
91 | + self.assertEqual(0, context_manager.exception.code) |
92 | self.assertEqual('status: running\n', m_stdout.getvalue()) |
93 | |
94 | # vi: ts=4 expandtab syntax=python |
95 | diff --git a/cloudinit/sources/DataSourceOVF.py b/cloudinit/sources/DataSourceOVF.py |
96 | index 6e62f98..dc914a7 100644 |
97 | --- a/cloudinit/sources/DataSourceOVF.py |
98 | +++ b/cloudinit/sources/DataSourceOVF.py |
99 | @@ -95,11 +95,20 @@ class DataSourceOVF(sources.DataSource): |
100 | "VMware Customization support") |
101 | elif not util.get_cfg_option_bool( |
102 | self.sys_cfg, "disable_vmware_customization", True): |
103 | - deployPkgPluginPath = search_file("/usr/lib/vmware-tools", |
104 | - "libdeployPkgPlugin.so") |
105 | - if not deployPkgPluginPath: |
106 | - deployPkgPluginPath = search_file("/usr/lib/open-vm-tools", |
107 | - "libdeployPkgPlugin.so") |
108 | + |
109 | + search_paths = ( |
110 | + "/usr/lib/vmware-tools", "/usr/lib64/vmware-tools", |
111 | + "/usr/lib/open-vm-tools", "/usr/lib64/open-vm-tools") |
112 | + |
113 | + plugin = "libdeployPkgPlugin.so" |
114 | + deployPkgPluginPath = None |
115 | + for path in search_paths: |
116 | + deployPkgPluginPath = search_file(path, plugin) |
117 | + if deployPkgPluginPath: |
118 | + LOG.debug("Found the customization plugin at %s", |
119 | + deployPkgPluginPath) |
120 | + break |
121 | + |
122 | if deployPkgPluginPath: |
123 | # When the VM is powered on, the "VMware Tools" daemon |
124 | # copies the customization specification file to |
125 | @@ -111,6 +120,8 @@ class DataSourceOVF(sources.DataSource): |
126 | msg="waiting for configuration file", |
127 | func=wait_for_imc_cfg_file, |
128 | args=("cust.cfg", max_wait)) |
129 | + else: |
130 | + LOG.debug("Did not find the customization plugin.") |
131 | |
132 | if vmwareImcConfigFilePath: |
133 | LOG.debug("Found VMware Customization Config File at %s", |
134 | diff --git a/cloudinit/tests/helpers.py b/cloudinit/tests/helpers.py |
135 | index 0080c72..41d9a8e 100644 |
136 | --- a/cloudinit/tests/helpers.py |
137 | +++ b/cloudinit/tests/helpers.py |
138 | @@ -173,17 +173,15 @@ class CiTestCase(TestCase): |
139 | dir = self.tmp_dir() |
140 | return os.path.normpath(os.path.abspath(os.path.join(dir, path))) |
141 | |
142 | - def assertRaisesCodeEqual(self, expected, found): |
143 | - """Handle centos6 having different context manager for assertRaises. |
144 | - with assertRaises(Exception) as e: |
145 | - raise Exception("BOO") |
146 | - |
147 | - centos6 will have e.exception as an integer. |
148 | - anything nwere will have it as something with a '.code'""" |
149 | - if isinstance(found, int): |
150 | - self.assertEqual(expected, found) |
151 | - else: |
152 | - self.assertEqual(expected, found.code) |
153 | + def sys_exit(self, code): |
154 | + """Provide a wrapper around sys.exit for python 2.6 |
155 | + |
156 | + In 2.6, this code would produce 'cm.exception' with value int(2) |
157 | + rather than the SystemExit that was raised by sys.exit(2). |
158 | + with assertRaises(SystemExit) as cm: |
159 | + sys.exit(2) |
160 | + """ |
161 | + raise SystemExit(code) |
162 | |
163 | |
164 | class ResourceUsingTestCase(CiTestCase): |
165 | diff --git a/cloudinit/version.py b/cloudinit/version.py |
166 | index be6262d..4a682ad 100644 |
167 | --- a/cloudinit/version.py |
168 | +++ b/cloudinit/version.py |
169 | @@ -4,7 +4,7 @@ |
170 | # |
171 | # This file is part of cloud-init. See LICENSE file for license information. |
172 | |
173 | -__VERSION__ = "17.2" |
174 | +__VERSION__ = "18.1" |
175 | |
176 | FEATURES = [ |
177 | # supports network config version 1 |
178 | diff --git a/config/cloud.cfg.tmpl b/config/cloud.cfg.tmpl |
179 | index 32de9c9..fad1184 100644 |
180 | --- a/config/cloud.cfg.tmpl |
181 | +++ b/config/cloud.cfg.tmpl |
182 | @@ -4,6 +4,8 @@ |
183 | |
184 | {% if variant in ["freebsd"] %} |
185 | syslog_fix_perms: root:wheel |
186 | +{% elif variant in ["suse"] %} |
187 | +syslog_fix_perms: root:root |
188 | {% endif %} |
189 | # A set of users which may be applied and/or used by various modules |
190 | # when a 'default' entry is found it will reference the 'default_user' |
191 | diff --git a/debian/changelog b/debian/changelog |
192 | index abbd5a9..2552bb6 100644 |
193 | --- a/debian/changelog |
194 | +++ b/debian/changelog |
195 | @@ -1,3 +1,19 @@ |
196 | +cloud-init (18.1-0ubuntu1) bionic; urgency=medium |
197 | + |
198 | + * New upstream snapshot. |
199 | + - release 18.1 (LP: #1751145) |
200 | + - OVF: Fix VMware support for 64-bit platforms. [Sankar Tanguturi] |
201 | + - ds-identify: Fix searching for iso9660 OVF cdroms. (LP: #1749980) |
202 | + - SUSE: Fix groups used for ownership of cloud-init.log [Robert Schweikert] |
203 | + - ds-identify: check /writable/system-data/ for nocloud seed. |
204 | + (LP: #1747070) |
205 | + - tests: run nosetests in cloudinit/ directory, fix py26 fallout. |
206 | + - tools: run-centos: git clone rather than tar. |
207 | + - tests: add support for logs with lxd from snap and future lxd 3. |
208 | + (LP: #1745663) |
209 | + |
210 | + -- Chad Smith <chad.smith@canonical.com> Thu, 22 Feb 2018 15:42:11 -0700 |
211 | + |
212 | cloud-init (17.2-34-g644048e3-0ubuntu1) bionic; urgency=medium |
213 | |
214 | * New upstream snapshot. |
215 | diff --git a/tests/cloud_tests/collect.py b/tests/cloud_tests/collect.py |
216 | index 5ea88e5..d4f9135 100644 |
217 | --- a/tests/cloud_tests/collect.py |
218 | +++ b/tests/cloud_tests/collect.py |
219 | @@ -44,8 +44,9 @@ def collect_console(instance, base_dir): |
220 | LOG.debug('getting console log for %s to %s', instance, logfile) |
221 | try: |
222 | data = instance.console_log() |
223 | - except NotImplementedError: |
224 | - data = b'instance.console_log: not implemented' |
225 | + except NotImplementedError as e: |
226 | + # args[0] is hacky, but thats all I see to get at the message. |
227 | + data = b'NotImplementedError:' + e.args[0].encode() |
228 | with open(logfile, "wb") as fp: |
229 | fp.write(data) |
230 | |
231 | diff --git a/tests/cloud_tests/platforms/lxd/instance.py b/tests/cloud_tests/platforms/lxd/instance.py |
232 | index d2d2a1f..0488da5 100644 |
233 | --- a/tests/cloud_tests/platforms/lxd/instance.py |
234 | +++ b/tests/cloud_tests/platforms/lxd/instance.py |
235 | @@ -6,7 +6,9 @@ import os |
236 | import shutil |
237 | from tempfile import mkdtemp |
238 | |
239 | -from cloudinit.util import subp, ProcessExecutionError |
240 | +from cloudinit.util import load_yaml, subp, ProcessExecutionError, which |
241 | +from tests.cloud_tests import LOG |
242 | +from tests.cloud_tests.util import PlatformError |
243 | |
244 | from ..instances import Instance |
245 | |
246 | @@ -15,6 +17,8 @@ class LXDInstance(Instance): |
247 | """LXD container backed instance.""" |
248 | |
249 | platform_name = "lxd" |
250 | + _console_log_method = None |
251 | + _console_log_file = None |
252 | |
253 | def __init__(self, platform, name, properties, config, features, |
254 | pylxd_container): |
255 | @@ -30,8 +34,8 @@ class LXDInstance(Instance): |
256 | super(LXDInstance, self).__init__( |
257 | platform, name, properties, config, features) |
258 | self.tmpd = mkdtemp(prefix="%s-%s" % (type(self).__name__, name)) |
259 | - self._setup_console_log() |
260 | self.name = name |
261 | + self._setup_console_log() |
262 | |
263 | @property |
264 | def pylxd_container(self): |
265 | @@ -39,21 +43,6 @@ class LXDInstance(Instance): |
266 | self._pylxd_container.sync() |
267 | return self._pylxd_container |
268 | |
269 | - def _setup_console_log(self): |
270 | - logf = os.path.join(self.tmpd, "console.log") |
271 | - |
272 | - # doing this ensures we can read it. Otherwise it ends up root:root. |
273 | - with open(logf, "w") as fp: |
274 | - fp.write("# %s\n" % self.name) |
275 | - |
276 | - cfg = "lxc.console.logfile=%s" % logf |
277 | - orig = self._pylxd_container.config.get('raw.lxc', "") |
278 | - if orig: |
279 | - orig += "\n" |
280 | - self._pylxd_container.config['raw.lxc'] = orig + cfg |
281 | - self._pylxd_container.save() |
282 | - self._console_log_file = logf |
283 | - |
284 | def _execute(self, command, stdin=None, env=None): |
285 | if env is None: |
286 | env = {} |
287 | @@ -97,19 +86,80 @@ class LXDInstance(Instance): |
288 | """ |
289 | self.pylxd_container.files.put(remote_path, data) |
290 | |
291 | + @property |
292 | + def console_log_method(self): |
293 | + if self._console_log_method is not None: |
294 | + return self._console_log_method |
295 | + |
296 | + client = which('lxc') |
297 | + if not client: |
298 | + raise PlatformError("No 'lxc' client.") |
299 | + |
300 | + elif _has_proper_console_support(): |
301 | + self._console_log_method = 'show-log' |
302 | + elif client.startswith("/snap"): |
303 | + self._console_log_method = 'logfile-snap' |
304 | + else: |
305 | + self._console_log_method = 'logfile-tmp' |
306 | + |
307 | + LOG.debug("Set console log method to %s", self._console_log_method) |
308 | + return self._console_log_method |
309 | + |
310 | + def _setup_console_log(self): |
311 | + method = self.console_log_method |
312 | + if not method.startswith("logfile-"): |
313 | + return |
314 | + |
315 | + if method == "logfile-snap": |
316 | + log_dir = "/var/snap/lxd/common/consoles" |
317 | + if not os.path.exists(log_dir): |
318 | + raise PlatformError( |
319 | + "Unable to log with snap lxc. Please run:\n" |
320 | + " sudo mkdir --mode=1777 -p %s" % log_dir) |
321 | + elif method == "logfile-tmp": |
322 | + log_dir = "/tmp" |
323 | + else: |
324 | + raise PlatformError( |
325 | + "Unexpected value for console method: %s" % method) |
326 | + |
327 | + # doing this ensures we can read it. Otherwise it ends up root:root. |
328 | + log_file = os.path.join(log_dir, self.name) |
329 | + with open(log_file, "w") as fp: |
330 | + fp.write("# %s\n" % self.name) |
331 | + |
332 | + cfg = "lxc.console.logfile=%s" % log_file |
333 | + orig = self._pylxd_container.config.get('raw.lxc', "") |
334 | + if orig: |
335 | + orig += "\n" |
336 | + self._pylxd_container.config['raw.lxc'] = orig + cfg |
337 | + self._pylxd_container.save() |
338 | + self._console_log_file = log_file |
339 | + |
340 | def console_log(self): |
341 | """Console log. |
342 | |
343 | - @return_value: bytes of this instance’s console |
344 | + @return_value: bytes of this instance's console |
345 | """ |
346 | - if not os.path.exists(self._console_log_file): |
347 | - raise NotImplementedError( |
348 | - "Console log '%s' does not exist. If this is a remote " |
349 | - "lxc, then this is really NotImplementedError. If it is " |
350 | - "A local lxc, then this is a RuntimeError." |
351 | - "https://github.com/lxc/lxd/issues/1129") |
352 | - with open(self._console_log_file, "rb") as fp: |
353 | - return fp.read() |
354 | + |
355 | + if self._console_log_file: |
356 | + if not os.path.exists(self._console_log_file): |
357 | + raise NotImplementedError( |
358 | + "Console log '%s' does not exist. If this is a remote " |
359 | + "lxc, then this is really NotImplementedError. If it is " |
360 | + "A local lxc, then this is a RuntimeError." |
361 | + "https://github.com/lxc/lxd/issues/1129") |
362 | + with open(self._console_log_file, "rb") as fp: |
363 | + return fp.read() |
364 | + |
365 | + try: |
366 | + stdout, stderr = subp( |
367 | + ['lxc', 'console', '--show-log', self.name], decode=False) |
368 | + return stdout |
369 | + except ProcessExecutionError as e: |
370 | + raise PlatformError( |
371 | + "console log", |
372 | + "Console log failed [%d]: stdout=%s stderr=%s" % ( |
373 | + e.exit_code, e.stdout, e.stderr)) |
374 | |
375 | def reboot(self, wait=True): |
376 | """Reboot instance.""" |
377 | @@ -146,7 +196,37 @@ class LXDInstance(Instance): |
378 | if self.platform.container_exists(self.name): |
379 | raise OSError('container {} was not properly removed' |
380 | .format(self.name)) |
381 | + if self._console_log_file and os.path.exists(self._console_log_file): |
382 | + os.unlink(self._console_log_file) |
383 | shutil.rmtree(self.tmpd) |
384 | super(LXDInstance, self).destroy() |
385 | |
386 | + |
387 | +def _has_proper_console_support(): |
388 | + stdout, _ = subp(['lxc', 'info']) |
389 | + info = load_yaml(stdout) |
390 | + reason = None |
391 | + if 'console' not in info.get('api_extensions', []): |
392 | + reason = "LXD server does not support console api extension" |
393 | + else: |
394 | + dver = info.get('environment', {}).get('driver_version', "") |
395 | + if dver.startswith("2.") or dver.startwith("1."): |
396 | + reason = "LXD Driver version not 3.x+ (%s)" % dver |
397 | + else: |
398 | + try: |
399 | + stdout, stderr = subp(['lxc', 'console', '--help'], |
400 | + decode=False) |
401 | + if not (b'console' in stdout and b'log' in stdout): |
402 | + reason = "no '--log' in lxc console --help" |
403 | + except ProcessExecutionError as e: |
404 | + reason = "no 'console' command in lxc client" |
405 | + |
406 | + if reason: |
407 | + LOG.debug("no console-support: %s", reason) |
408 | + return False |
409 | + else: |
410 | + LOG.debug("console-support looks good") |
411 | + return True |
412 | + |
413 | + |
414 | # vi: ts=4 expandtab |
415 | diff --git a/tests/unittests/test_ds_identify.py b/tests/unittests/test_ds_identify.py |
416 | index 31cc622..9be3f96 100644 |
417 | --- a/tests/unittests/test_ds_identify.py |
418 | +++ b/tests/unittests/test_ds_identify.py |
419 | @@ -337,6 +337,16 @@ class TestDsIdentify(CiTestCase): |
420 | """OVF is identified when vmware customization is enabled.""" |
421 | self._test_ds_found('OVF-vmware-customization') |
422 | |
423 | + def test_ovf_on_vmware_iso_found_open_vm_tools_64(self): |
424 | + """OVF is identified when open-vm-tools installed in /usr/lib64.""" |
425 | + cust64 = copy.deepcopy(VALID_CFG['OVF-vmware-customization']) |
426 | + p32 = 'usr/lib/vmware-tools/plugins/vmsvc/libdeployPkgPlugin.so' |
427 | + open64 = 'usr/lib64/open-vm-tools/plugins/vmsvc/libdeployPkgPlugin.so' |
428 | + cust64['files'][open64] = cust64['files'][p32] |
429 | + del cust64['files'][p32] |
430 | + return self._check_via_dict( |
431 | + cust64, RC_FOUND, dslist=[cust64.get('ds'), DS_NONE]) |
432 | + |
433 | def test_ovf_on_vmware_iso_found_by_cdrom_with_matching_fs_label(self): |
434 | """OVF is identified by well-known iso9660 labels.""" |
435 | ovf_cdrom_by_label = copy.deepcopy(VALID_CFG['OVF']) |
436 | @@ -350,8 +360,10 @@ class TestDsIdentify(CiTestCase): |
437 | "OVFENV", "ovfenv"] |
438 | for valid_ovf_label in valid_ovf_labels: |
439 | ovf_cdrom_by_label['mocks'][0]['out'] = blkid_out([ |
440 | + {'DEVNAME': 'sda1', 'TYPE': 'ext4', 'LABEL': 'rootfs'}, |
441 | {'DEVNAME': 'sr0', 'TYPE': 'iso9660', |
442 | - 'LABEL': valid_ovf_label}]) |
443 | + 'LABEL': valid_ovf_label}, |
444 | + {'DEVNAME': 'vda1', 'TYPE': 'ntfs', 'LABEL': 'data'}]) |
445 | self._check_via_dict( |
446 | ovf_cdrom_by_label, rc=RC_FOUND, dslist=['OVF', DS_NONE]) |
447 | |
448 | @@ -359,6 +371,14 @@ class TestDsIdentify(CiTestCase): |
449 | """NoCloud is found with iso9660 filesystem on non-cdrom disk.""" |
450 | self._test_ds_found('NoCloud') |
451 | |
452 | + def test_nocloud_seed(self): |
453 | + """Nocloud seed directory.""" |
454 | + self._test_ds_found('NoCloud-seed') |
455 | + |
456 | + def test_nocloud_seed_ubuntu_core_writable(self): |
457 | + """Nocloud seed directory ubuntu core writable""" |
458 | + self._test_ds_found('NoCloud-seed-ubuntu-core') |
459 | + |
460 | |
461 | def blkid_out(disks=None): |
462 | """Convert a list of disk dictionaries into blkid content.""" |
463 | @@ -454,6 +474,22 @@ VALID_CFG = { |
464 | 'dev/vdb': 'pretend iso content for cidata\n', |
465 | } |
466 | }, |
467 | + 'NoCloud-seed': { |
468 | + 'ds': 'NoCloud', |
469 | + 'files': { |
470 | + os.path.join(P_SEED_DIR, 'nocloud', 'user-data'): 'ud\n', |
471 | + os.path.join(P_SEED_DIR, 'nocloud', 'meta-data'): 'md\n', |
472 | + } |
473 | + }, |
474 | + 'NoCloud-seed-ubuntu-core': { |
475 | + 'ds': 'NoCloud', |
476 | + 'files': { |
477 | + os.path.join('writable/system-data', P_SEED_DIR, |
478 | + 'nocloud-net', 'user-data'): 'ud\n', |
479 | + os.path.join('writable/system-data', P_SEED_DIR, |
480 | + 'nocloud-net', 'meta-data'): 'md\n', |
481 | + } |
482 | + }, |
483 | 'OpenStack': { |
484 | 'ds': 'OpenStack', |
485 | 'files': {P_PRODUCT_NAME: 'OpenStack Nova\n'}, |
486 | @@ -489,8 +525,9 @@ VALID_CFG = { |
487 | 'mocks': [ |
488 | {'name': 'blkid', 'ret': 0, |
489 | 'out': blkid_out( |
490 | - [{'DEVNAME': 'vda1', 'TYPE': 'vfat', 'PARTUUID': uuid4()}, |
491 | - {'DEVNAME': 'sr0', 'TYPE': 'iso9660', 'LABEL': ''}]) |
492 | + [{'DEVNAME': 'sr0', 'TYPE': 'iso9660', 'LABEL': ''}, |
493 | + {'DEVNAME': 'sr1', 'TYPE': 'iso9660', 'LABEL': 'ignoreme'}, |
494 | + {'DEVNAME': 'vda1', 'TYPE': 'vfat', 'PARTUUID': uuid4()}]), |
495 | }, |
496 | MOCK_VIRT_IS_VMWARE, |
497 | ], |
498 | diff --git a/tests/unittests/test_handler/test_schema.py b/tests/unittests/test_handler/test_schema.py |
499 | index 648573f..df67a0e 100644 |
500 | --- a/tests/unittests/test_handler/test_schema.py |
501 | +++ b/tests/unittests/test_handler/test_schema.py |
502 | @@ -336,11 +336,13 @@ class MainTest(CiTestCase): |
503 | |
504 | def test_main_missing_args(self): |
505 | """Main exits non-zero and reports an error on missing parameters.""" |
506 | - with mock.patch('sys.argv', ['mycmd']): |
507 | - with mock.patch('sys.stderr', new_callable=StringIO) as m_stderr: |
508 | - with self.assertRaises(SystemExit) as context_manager: |
509 | - main() |
510 | - self.assertEqual('1', str(context_manager.exception)) |
511 | + with mock.patch('sys.exit', side_effect=self.sys_exit): |
512 | + with mock.patch('sys.argv', ['mycmd']): |
513 | + with mock.patch('sys.stderr', new_callable=StringIO) as \ |
514 | + m_stderr: |
515 | + with self.assertRaises(SystemExit) as context_manager: |
516 | + main() |
517 | + self.assertEqual(1, context_manager.exception.code) |
518 | self.assertEqual( |
519 | 'Expected either --config-file argument or --doc\n', |
520 | m_stderr.getvalue()) |
521 | diff --git a/tools/ds-identify b/tools/ds-identify |
522 | index cd26824..ec368d5 100755 |
523 | --- a/tools/ds-identify |
524 | +++ b/tools/ds-identify |
525 | @@ -186,7 +186,8 @@ block_dev_with_label() { |
526 | read_fs_info() { |
527 | cached "${DI_BLKID_OUTPUT}" && return 0 |
528 | # do not rely on links in /dev/disk which might not be present yet. |
529 | - # note that older blkid versions do not report DEVNAME in 'export' output. |
530 | + # Note that blkid < 2.22 (centos6, trusty) do not output DEVNAME. |
531 | + # that means that DI_ISO9660_DEVS will not be set. |
532 | if is_container; then |
533 | # blkid will in a container, or at least currently in lxd |
534 | # not provide useful information. |
535 | @@ -203,21 +204,26 @@ read_fs_info() { |
536 | DI_ISO9660_DEVS="$UNAVAILABLE:error" |
537 | return $ret |
538 | } |
539 | - IFS="$CR" |
540 | - set -- $out |
541 | - IFS="$oifs" |
542 | - for line in "$@" ""; do |
543 | + # 'set --' will collapse multiple consecutive entries in IFS for |
544 | + # whitespace characters (\n, tab, " ") so we cannot rely on getting |
545 | + # empty lines in "$@" below. |
546 | + IFS="$CR"; set -- $out; IFS="$oifs" |
547 | + |
548 | + for line in "$@"; do |
549 | case "${line}" in |
550 | - DEVNAME=*) dev=${line#DEVNAME=};; |
551 | + DEVNAME=*) |
552 | + [ -n "$dev" -a "$ftype" = "iso9660" ] && |
553 | + isodevs="${isodevs} ${dev}=$label" |
554 | + ftype=""; dev=""; label=""; |
555 | + dev=${line#DEVNAME=};; |
556 | LABEL=*) label="${line#LABEL=}"; |
557 | labels="${labels}${line#LABEL=}${delim}";; |
558 | TYPE=*) ftype=${line#TYPE=};; |
559 | - "") if [ "$ftype" = "iso9660" ]; then |
560 | - isodevs="${isodevs} ${dev}=$label" |
561 | - fi |
562 | - ftype=""; devname=""; label=""; |
563 | esac |
564 | done |
565 | + [ -n "$dev" -a "$ftype" = "iso9660" ] && |
566 | + isodevs="${isodevs} ${dev}=$label" |
567 | + |
568 | DI_FS_LABELS="${labels%${delim}}" |
569 | DI_ISO9660_DEVS="${isodevs# }" |
570 | } |
571 | @@ -470,6 +476,16 @@ check_seed_dir() { |
572 | return 0 |
573 | } |
574 | |
575 | +check_writable_seed_dir() { |
576 | + # ubuntu core bind-mounts /writable/system-data/var/lib/cloud |
577 | + # over the top of /var/lib/cloud, but the mount might not be done yet. |
578 | + local wdir="/writable/system-data" |
579 | + [ -d "${PATH_ROOT}$wdir" ] || return 1 |
580 | + local sdir="${PATH_ROOT}$wdir${PATH_VAR_LIB_CLOUD#${PATH_ROOT}}" |
581 | + local PATH_VAR_LIB_CLOUD="$sdir" |
582 | + check_seed_dir "$@" |
583 | +} |
584 | + |
585 | probe_floppy() { |
586 | cached "${STATE_FLOPPY_PROBED}" && return "${STATE_FLOPPY_PROBED}" |
587 | local fpath=/dev/floppy |
588 | @@ -569,6 +585,7 @@ dscheck_NoCloud() { |
589 | esac |
590 | for d in nocloud nocloud-net; do |
591 | check_seed_dir "$d" meta-data user-data && return ${DS_FOUND} |
592 | + check_writable_seed_dir "$d" meta-data user-data && return ${DS_FOUND} |
593 | done |
594 | if has_fs_with_label "${fslabel}"; then |
595 | return ${DS_FOUND} |
596 | @@ -633,8 +650,9 @@ ovf_vmware_guest_customization() { |
597 | |
598 | # we have to have the plugin to do vmware customization |
599 | local found="" pkg="" pre="${PATH_ROOT}/usr/lib" |
600 | + local ppath="plugins/vmsvc/libdeployPkgPlugin.so" |
601 | for pkg in vmware-tools open-vm-tools; do |
602 | - if [ -f "$pre/$pkg/plugins/vmsvc/libdeployPkgPlugin.so" ]; then |
603 | + if [ -f "$pre/$pkg/$ppath" -o -f "${pre}64/$pkg/$ppath" ]; then |
604 | found="$pkg"; break; |
605 | fi |
606 | done |
607 | @@ -685,15 +703,12 @@ dscheck_OVF() { |
608 | # Azure provides ovf. Skip false positive by dis-allowing. |
609 | is_azure_chassis && return $DS_NOT_FOUND |
610 | |
611 | - local isodevs="${DI_ISO9660_DEVS}" |
612 | - case "$isodevs" in |
613 | - ""|$UNAVAILABLE:*) return ${DS_NOT_FOUND};; |
614 | - esac |
615 | - |
616 | # DI_ISO9660_DEVS is <device>=label, like /dev/sr0=OVF-TRANSPORT |
617 | - for tok in $isodevs; do |
618 | - is_cdrom_ovf "${tok%%=*}" "${tok#*=}" && return $DS_FOUND |
619 | - done |
620 | + if [ "${DI_ISO9660_DEVS#${UNAVAILABLE}:}" = "${DI_ISO9660_DEVS}" ]; then |
621 | + for tok in ${DI_ISO9660_DEVS}; do |
622 | + is_cdrom_ovf "${tok%%=*}" "${tok#*=}" && return $DS_FOUND |
623 | + done |
624 | + fi |
625 | |
626 | if ovf_vmware_guest_customization; then |
627 | return ${DS_FOUND} |
628 | diff --git a/tools/run-centos b/tools/run-centos |
629 | index d58ef3e..cb241ee 100755 |
630 | --- a/tools/run-centos |
631 | +++ b/tools/run-centos |
632 | @@ -23,6 +23,9 @@ Usage: ${0##*/} [ options ] version |
633 | |
634 | options: |
635 | -a | --artifact keep .rpm artifacts |
636 | + --dirty apply local changes before running tests. |
637 | + If not provided, a clean checkout of branch is tested. |
638 | + Inside container, changes are in local-changes.diff. |
639 | -k | --keep keep container after tests |
640 | -r | --rpm build .rpm |
641 | -s | --srpm build .src.rpm |
642 | @@ -80,25 +83,84 @@ inside() { |
643 | inject_cloud_init(){ |
644 | # take current cloud-init git dir and put it inside $name at |
645 | # ~$user/cloud-init. |
646 | - local name="$1" user="$2" top_d="" dname="" pstat="" |
647 | - top_d=$(git rev-parse --show-toplevel) || { |
648 | - errorrc "Failed to get git top level in $PWD"; |
649 | + local name="$1" user="$2" dirty="$3" |
650 | + local changes="" top_d="" dname="cloud-init" pstat="" |
651 | + local gitdir="" commitish="" |
652 | + gitdir=$(git rev-parse --git-dir) || { |
653 | + errorrc "Failed to get git dir in $PWD"; |
654 | return |
655 | } |
656 | - dname=$(basename "${top_d}") || return |
657 | - debug 1 "collecting ${top_d} ($dname) into user $user in $name." |
658 | - tar -C "${top_d}/.." -cpf - "$dname" | |
659 | + local t=${gitdir%/*} |
660 | + case "$t" in |
661 | + */worktrees) |
662 | + if [ -f "${t%worktrees}/config" ]; then |
663 | + gitdir="${t%worktrees}" |
664 | + fi |
665 | + esac |
666 | + |
667 | + # attempt to get branch name. |
668 | + commitish=$(git rev-parse --abbrev-ref HEAD) || { |
669 | + errorrc "Failed git rev-parse --abbrev-ref HEAD" |
670 | + return |
671 | + } |
672 | + if [ "$commitish" = "HEAD" ]; then |
673 | + # detached head |
674 | + commitish=$(git rev-parse HEAD) || { |
675 | + errorrc "failed git rev-parse HEAD" |
676 | + return |
677 | + } |
678 | + fi |
679 | + |
680 | + local local_changes=false |
681 | + if ! git diff --quiet "$commitish"; then |
682 | + # there are local changes not committed. |
683 | + local_changes=true |
684 | + if [ "$dirty" = "false" ]; then |
685 | + error "WARNING: You had uncommitted changes. Those changes will " |
686 | + error "be put into 'local-changes.diff' inside the container. " |
687 | + error "To test these changes you must pass --dirty." |
688 | + fi |
689 | + fi |
690 | + |
691 | + debug 1 "collecting ${gitdir} ($dname) into user $user in $name." |
692 | + tar -C "${gitdir}" -cpf - . | |
693 | inside_as "$name" "$user" sh -ec ' |
694 | dname=$1 |
695 | + commitish=$2 |
696 | rm -Rf "$dname" |
697 | + mkdir -p $dname/.git |
698 | + cd $dname/.git |
699 | tar -xpf - |
700 | - [ "$dname" = "cloud-init" ] || mv "$dname" cloud-init' \ |
701 | - extract "$dname" |
702 | + cd .. |
703 | + git config core.bare false |
704 | + out=$(git checkout $commitish 2>&1) || |
705 | + { echo "failed git checkout $commitish: $out" 1>&2; exit 1; } |
706 | + out=$(git checkout . 2>&1) || |
707 | + { echo "failed git checkout .: $out" 1>&2; exit 1; } |
708 | + ' extract "$dname" "$commitish" |
709 | [ "${PIPESTATUS[*]}" = "0 0" ] || { |
710 | - error "Failed to push tarball of '$top_d' into $name" \ |
711 | + error "Failed to push tarball of '$gitdir' into $name" \ |
712 | " for user $user (dname=$dname)" |
713 | return 1 |
714 | } |
715 | + |
716 | + echo "local_changes=$local_changes dirty=$dirty" |
717 | + if [ "$local_changes" = "true" ]; then |
718 | + git diff "$commitish" | |
719 | + inside_as "$name" "$user" sh -exc ' |
720 | + cd "$1" |
721 | + if [ "$2" = "true" ]; then |
722 | + git apply |
723 | + else |
724 | + cat > local-changes.diff |
725 | + fi |
726 | + ' insert_changes "$dname" "$dirty" |
727 | + [ "${PIPESTATUS[*]}" = "0 0" ] || { |
728 | + error "Failed to apply local changes." |
729 | + return 1 |
730 | + } |
731 | + fi |
732 | + |
733 | return 0 |
734 | } |
735 | |
736 | @@ -179,7 +241,7 @@ delete_container() { |
737 | |
738 | main() { |
739 | local short_opts="ahkrsuv" |
740 | - local long_opts="artifact,help,keep,rpm,srpm,unittest,verbose" |
741 | + local long_opts="artifact,dirty,help,keep,rpm,srpm,unittest,verbose" |
742 | local getopt_out="" |
743 | getopt_out=$(getopt --name "${0##*/}" \ |
744 | --options "${short_opts}" --long "${long_opts}" -- "$@") && |
745 | @@ -188,11 +250,13 @@ main() { |
746 | |
747 | local cur="" next="" |
748 | local artifact="" keep="" rpm="" srpm="" unittest="" version="" |
749 | + local dirty=false |
750 | |
751 | while [ $# -ne 0 ]; do |
752 | cur="${1:-}"; next="${2:-}"; |
753 | case "$cur" in |
754 | -a|--artifact) artifact=1;; |
755 | + --dirty) dirty=true;; |
756 | -h|--help) Usage ; exit 0;; |
757 | -k|--keep) KEEP=true;; |
758 | -r|--rpm) rpm=1;; |
759 | @@ -231,7 +295,7 @@ main() { |
760 | inside "$name" useradd "$user" |
761 | |
762 | debug 1 "inserting cloud-init" |
763 | - inject_cloud_init "$name" "$user" || { |
764 | + inject_cloud_init "$name" "$user" "$dirty" || { |
765 | errorrc "FAIL: injecting cloud-init into $name failed." |
766 | return |
767 | } |
768 | @@ -244,12 +308,13 @@ main() { |
769 | |
770 | local errors=0 |
771 | inside_as_cd "$name" "$user" "$cdir" \ |
772 | - sh -ec "git checkout .; git status" || |
773 | + sh -ec "git status" || |
774 | { errorrc "git checkout failed."; errors=$(($errors+1)); } |
775 | |
776 | if [ -n "$unittest" ]; then |
777 | debug 1 "running unit tests." |
778 | - inside_as_cd "$name" "$user" "$cdir" nosetests tests/unittests || |
779 | + inside_as_cd "$name" "$user" "$cdir" \ |
780 | + nosetests tests/unittests cloudinit || |
781 | { errorrc "nosetests failed."; errors=$(($errors+1)); } |
782 | fi |
783 | |
784 | diff --git a/tox.ini b/tox.ini |
785 | index bb74853..1f990af 100644 |
786 | --- a/tox.ini |
787 | +++ b/tox.ini |
788 | @@ -45,7 +45,7 @@ deps = -r{toxinidir}/test-requirements.txt |
789 | |
790 | [testenv:py26] |
791 | deps = -r{toxinidir}/test-requirements.txt |
792 | -commands = nosetests {posargs:tests/unittests} |
793 | +commands = nosetests {posargs:tests/unittests cloudinit} |
794 | setenv = |
795 | LC_ALL = C |
796 | |
797 | @@ -83,7 +83,7 @@ deps = |
798 | |
799 | [testenv:centos6] |
800 | basepython = python2.6 |
801 | -commands = nosetests {posargs:tests/unittests} |
802 | +commands = nosetests {posargs:tests/unittests cloudinit} |
803 | deps = |
804 | # requirements |
805 | argparse==1.2.1 |
806 | @@ -98,7 +98,7 @@ deps = |
807 | |
808 | [testenv:opensusel42] |
809 | basepython = python2.7 |
810 | -commands = nosetests {posargs:tests/unittests} |
811 | +commands = nosetests {posargs:tests/unittests cloudinit} |
812 | deps = |
813 | # requirements |
814 | argparse==1.3.0 |
PASSED: Continuous integration, rev:04abd70b222 976007b07afc5b1 fa70c0a4de882e /jenkins. ubuntu. com/server/ job/cloud- init-ci/ 785/
https:/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Unit & Style Tests
SUCCESS: Ubuntu LTS: Build
SUCCESS: Ubuntu LTS: Integration
SUCCESS: MAAS Compatability Testing
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild: /jenkins. ubuntu. com/server/ job/cloud- init-ci/ 785/rebuild
https:/