Merge lp:~raharper/curtin/trunk.lp1635560 into lp:~curtin-dev/curtin/trunk
- trunk.lp1635560
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 446 |
Proposed branch: | lp:~raharper/curtin/trunk.lp1635560 |
Merge into: | lp:~curtin-dev/curtin/trunk |
Diff against target: |
349 lines (+112/-51) 8 files modified
curtin/block/__init__.py (+7/-3) curtin/commands/apply_net.py (+2/-2) curtin/util.py (+13/-3) tests/unittests/helpers.py (+24/-20) tests/unittests/test_apt_source.py (+1/-1) tests/unittests/test_block.py (+11/-10) tests/unittests/test_util.py (+32/-1) tests/vmtests/__init__.py (+22/-11) |
To merge this branch: | bzr merge lp:~raharper/curtin/trunk.lp1635560 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Server Team CI bot | continuous-integration | Needs Fixing | |
curtin developers | Pending | ||
Review via email: mp+314645@code.launchpad.net |
Commit message
content decoding in load_file, apply_net raise exception on errors
This patch series fixes two issues. First, subcommands of the apply_net
command were exiting non-zero but we failed to raise and exception
which let curtin hide an error. We've modified apply_net to re-raise
the exception when it occurs. Additionally update vmtest to look for
stack-traces in the installation log and mark a test failed if it
detects one; this should prevent future cases from re-occurring.
The second error is when loading a file with and encoding, load_file
did not handle this case. Merge in a version of load_file from
cloud-init which already handles this case. Introduce new
unittests to validate the function as it touches some block related
code where we read partition data directly.
Finally, the mock_open feature of unittest.mock only supports
binary data in version 2.0.0 or newer, so skip this unittest on
systems without a new-enough mock; note this does not affect
the function of the code on the same release, only the unittest.
Description of the change
content decoding in load_file, apply_net raise exception on errors
This patch series fixes two issues. First, subcommands of the apply_net
command were exiting non-zero but we failed to raise and exception
which let curtin hide an error. We've modified apply_net to re-raise
the exception when it occurs. Additionally update vmtest to look for
stack-traces in the installation log and mark a test failed if it
detects one; this should prevent future cases from re-occurring.
The second error is when loading a file with and encoding, load_file
did not handle this case. Merge in a version of load_file from
cloud-init which already handles this case. Introduce new
unittests to validate the function as it touches some block related
code where we read partition data directly.
Finally, the mock_open feature of unittest.mock only supports
binary data in version 2.0.0 or newer, so skip this unittest on
systems without a new-enough mock; note this does not affect
the function of the code on the same release, only the unittest.
- 435. By Ryan Harper
-
merge from trunk
Server Team CI bot (server-team-bot) wrote : | # |
- 436. By Ryan Harper
-
merge from trunk
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:436
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Scott Moser (smoser) wrote : | # |
I'm kind of worreid about a behavior change in default usage of load_file.
Previously load_file without a mode argument would open in 'r' mode. It would would always return a string, and would raise exception if the read in non-binary mode failed.
Now we're changing that to
open in rb mode
raise exception if decode() fails.
I'm afraid their might be subtle differences between what we had:
open(file, "r").read()
and the new:
open(file, "rb").read(
I don't have an example, but I'm afraid there might be differences.
Ryan Harper (raharper) wrote : | # |
On Fri, Jan 20, 2017 at 12:37 PM, Scott Moser <email address hidden> wrote:
> I'm kind of worreid about a behavior change in default usage of load_file.
> Previously load_file without a mode argument would open in 'r' mode. It
> would would always return a string, and would raise exception if the read
> in non-binary mode failed.
>
> Now we're changing that to
> open in rb mode
> raise exception if decode() fails.
>
> I'm afraid their might be subtle differences between what we had:
> open(file, "r").read()
> and the new:
> open(file, "rb").read(
>
> I don't have an example, but I'm afraid there might be differences.
>
Agreed, though this is how we do this in cloud-init.
I've found places in code where we do use load_file with the expected
non-decoding case
and have run vmtests to verify that all that still works.
>
> Diff comments:
>
> >
> > === modified file 'curtin/util.py'
> > --- curtin/util.py 2016-11-30 16:00:39 +0000
> > +++ curtin/util.py 2017-01-19 17:21:37 +0000
> > @@ -321,11 +321,23 @@
> > os.chmod(filename, mode)
> >
> >
> > -def load_file(path, mode="r", read_len=None, offset=0):
> > +def load_file(path, mode="rb", read_len=None, offset=0, decode=True):
> > with open(path, mode) as fp:
> > if offset:
> > fp.seek(offset)
> > - return fp.read(read_len) if read_len else fp.read()
> > + contents = fp.read(read_len) if read_len else fp.read()
> > +
> > + if decode:
>
> maybe:
> if decode and 'b' in mode
>
OK.
>
> > + return decode_
> > + else:
> > + return contents
> > +
> > +
>
> the decode_binary in cloud-init is someewhat of a hack, and i'd much
> rather not have it there.
> Instead, I'd rather have the caller know what they have and decode if they
> need it rather than the 'isinstance' test.
>
Well, the instance check is the workaround of not using six for byte vs.
string between python 2 and 3.
I think it makes as-is; the point of util is handle some of these
complexities to simplify the code elsewhere.
> I'm ok with decode_binary, but i'd prefer it not to do the isinstance
> chec,.
>
Hrm, I not sure this works without the check across py2 and py3 .
>
> > +def decode_binary(blob, encoding='utf-8', errors='replace'):
> > + # Converts a binary type into a text type using given encoding.
> > + if isinstance(blob, string_types):
> > + return blob
> > + return blob.decode(
> >
> >
> > def file_size(path):
>
>
> --
> https:/
> You are the owner of lp:~raharper/curtin/trunk.lp1635560.
>
- 437. By Ryan Harper
-
vmtest: make install-log error checking more obvious
- 438. By Ryan Harper
-
Modify known error message to not trip install failure detection
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:438
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
- 439. By Ryan Harper
-
util: only call decode_binary when needed, decode_binary only decodes binary
- 440. By Ryan Harper
-
merge from trunk
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:440
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
- 441. By Ryan Harper
-
merge from trunk
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:441
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Scott Moser (smoser) wrote : | # |
I guess I approve, with the one question inline.
I do think our load_file is kind of wierd still though..
cloud-init takes a 'decode' parameter, but does not take a open param.
Here, though, we take both mode and decode.
They seem to do mostly the same thing (and if they dont, i'd rather handle that specifically).
Ie, best case scenario is that:
load_file("file", mode="rb", decode=True) == load_file("file", mode="r", decode=False).
The worse cases is if that is subtly different.
Can we just drop the mode parameter and alway suse 'rb' ?
Then the caller gets a string if they pass decode=True, and gets bytes otherwise.
Ryan Harper (raharper) wrote : | # |
On Mon, Jan 30, 2017 at 10:26 AM, Scott Moser <email address hidden> wrote:
> I guess I approve, with the one question inline.
> I do think our load_file is kind of wierd still though..
> cloud-init takes a 'decode' parameter, but does not take a open param.
>
mode
> Here, though, we take both mode and decode.
>
> They seem to do mostly the same thing (and if they dont, i'd rather handle
> that specifically).
>
> Ie, best case scenario is that:
> load_file("file", mode="rb", decode=True) == load_file("file", mode="r",
> decode=False).
> The worse cases is if that is subtly different.
>
These are worth unittests to confirm; I'll add those;
>
> Can we just drop the mode parameter and alway suse 'rb' ?
>
> Then the caller gets a string if they pass decode=True, and gets bytes
> otherwise.
>
The one case, (for which we don't have a user at the moment) is if someone
wants to do
something with decoding differently than what the default is then they'll
need mode='rb'
and decode=False;
In general, what decision can we make when we get mode='r' and decode=True ?
Was that default or did the caller really specify that?
If we don't care about that specifically; and I don't know that we need to,
then it
does seem like we can use decode boolean and drop mode.
>
>
>
> Diff comments:
>
> > === modified file 'curtin/
> > --- curtin/
> > +++ curtin/
> > @@ -375,10 +375,10 @@
> > cmd = ['blockdev', '--rereadpt'] + devices
> > try:
> > util.subp(cmd, capture=True)
> > - except util.ProcessExe
> > + except util.ProcessExe
> > # FIXME: its less than ideal to swallow this error, but until
> > # we fix LP: #1489521 we kind of need to.
> > - LOG.warn(
> > + LOG.warn("Error rescanning devices, possibly known issue LP:
> #1489521")
>
> is there a reason not to log the error ?
>
Currently we have a comment that indicated that we don't care about the
error at this time
The message getting logged tripped the vmtest check_install_log regex which
indicated the
deployment failed. If we don't care about this error, then I think we can
skip the logging here.
> if you dont log the error, then the exit code and stderr and stdout get
> completely lost.
>
If we want to log the exit-code and stderr,stdout we can; it was the
python stacktrace
that got dumped via printing the exception that triggered the failure of a
test.
> >
> > udevadm_settle()
> >
>
>
> --
> https:/
> You are the owner of lp:~raharper/curtin/trunk.lp1635560.
>
- 442. By Ryan Harper
-
util.load_file: set mode based on decode parameter
- 443. By Ryan Harper
-
util.load_file: drop mode parameter, always use 'rb'
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:443
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Ryan Harper (raharper) wrote : | # |
Re-run vmtest on diglett after revno 443:
-------
Ran 1015 tests in 8278.084s
OK (SKIP=1)
Mon, 30 Jan 2017 14:18:28 -0600: vmtest end [0] in 8280s
- 444. By Ryan Harper
-
merge from trunk
- 445. By Ryan Harper
-
Add comment when logging expected error; don't pass decode=False in block_info
Server Team CI bot (server-team-bot) wrote : | # |
FAILED: Continuous integration, rev:445
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Preview Diff
1 | === modified file 'curtin/block/__init__.py' |
2 | --- curtin/block/__init__.py 2017-02-01 15:10:13 +0000 |
3 | +++ curtin/block/__init__.py 2017-02-06 19:55:25 +0000 |
4 | @@ -392,7 +392,11 @@ |
5 | except util.ProcessExecutionError as e: |
6 | # FIXME: its less than ideal to swallow this error, but until |
7 | # we fix LP: #1489521 we kind of need to. |
8 | - LOG.warn("rescanning devices failed: %s", e) |
9 | + LOG.warn("Error rescanning devices, possibly known issue LP: #1489521") |
10 | + # Reformatting the execption output so as to not trigger |
11 | + # vmtest scanning for Unexepected errors in install logfile |
12 | + LOG.warn("cmd: %s\nstdout:%s\nstderr:%s\nexit_code:%s", e.cmd, |
13 | + e.stdout, e.stderr, e.exit_code) |
14 | |
15 | udevadm_settle() |
16 | |
17 | @@ -702,7 +706,7 @@ |
18 | # this signature must be at 0x1fe |
19 | # https://en.wikipedia.org/wiki/Master_boot_record#Sector_layout |
20 | return (is_block_device(device) and util.file_size(device) >= 0x200 and |
21 | - (util.load_file(device, mode='rb', read_len=2, offset=0x1fe) == |
22 | + (util.load_file(device, decode=False, read_len=2, offset=0x1fe) == |
23 | b'\x55\xAA')) |
24 | |
25 | |
26 | @@ -720,7 +724,7 @@ |
27 | sector_size = get_blockdev_sector_size(device)[0] |
28 | return (is_block_device(device) and |
29 | util.file_size(device) >= 2 * sector_size and |
30 | - (util.load_file(device, mode='rb', read_len=8, |
31 | + (util.load_file(device, decode=False, read_len=8, |
32 | offset=sector_size) == b'EFI PART')) |
33 | |
34 | |
35 | |
36 | === modified file 'curtin/commands/apply_net.py' |
37 | --- curtin/commands/apply_net.py 2016-08-29 18:27:32 +0000 |
38 | +++ curtin/commands/apply_net.py 2017-02-06 19:55:25 +0000 |
39 | @@ -160,7 +160,7 @@ |
40 | except: |
41 | msg = bmsg + " %s exists, but could not be read." % cfg |
42 | LOG.exception(msg) |
43 | - return |
44 | + raise |
45 | |
46 | |
47 | def _maybe_remove_legacy_eth0(target, |
48 | @@ -194,7 +194,7 @@ |
49 | except: |
50 | msg = bmsg + " %s exists, but could not be read." % cfg |
51 | LOG.exception(msg) |
52 | - return |
53 | + raise |
54 | |
55 | LOG.warn(msg) |
56 | |
57 | |
58 | === modified file 'curtin/util.py' |
59 | --- curtin/util.py 2017-02-02 22:52:01 +0000 |
60 | +++ curtin/util.py 2017-02-06 19:55:25 +0000 |
61 | @@ -337,11 +337,21 @@ |
62 | os.chmod(filename, mode) |
63 | |
64 | |
65 | -def load_file(path, mode="r", read_len=None, offset=0): |
66 | - with open(path, mode) as fp: |
67 | +def load_file(path, read_len=None, offset=0, decode=True): |
68 | + with open(path, "rb") as fp: |
69 | if offset: |
70 | fp.seek(offset) |
71 | - return fp.read(read_len) if read_len else fp.read() |
72 | + contents = fp.read(read_len) if read_len else fp.read() |
73 | + |
74 | + if decode: |
75 | + return decode_binary(contents) |
76 | + else: |
77 | + return contents |
78 | + |
79 | + |
80 | +def decode_binary(blob, encoding='utf-8', errors='replace'): |
81 | + # Converts a binary type into a text type using given encoding. |
82 | + return blob.decode(encoding, errors=errors) |
83 | |
84 | |
85 | def file_size(path): |
86 | |
87 | === modified file 'tests/unittests/helpers.py' |
88 | --- tests/unittests/helpers.py 2016-09-16 18:54:28 +0000 |
89 | +++ tests/unittests/helpers.py 2017-02-06 19:55:25 +0000 |
90 | @@ -14,28 +14,32 @@ |
91 | # |
92 | # You should have received a copy of the GNU Affero General Public License |
93 | # along with Curtin. If not, see <http://www.gnu.org/licenses/>. |
94 | + |
95 | +import contextlib |
96 | +import imp |
97 | +import importlib |
98 | import mock |
99 | |
100 | |
101 | -class mocked_open(object): |
102 | - # older versions of mock can't really mock the builtin 'open' easily. |
103 | - def __init__(self): |
104 | - self.mocked = None |
105 | - |
106 | - def __enter__(self): |
107 | - if self.mocked: |
108 | - return self.mocked.start() |
109 | - |
110 | - py2_p = '__builtin__.open' |
111 | - py3_p = 'builtins.open' |
112 | +def builtin_module_name(): |
113 | + options = ('builtins', '__builtin__') |
114 | + for name in options: |
115 | try: |
116 | - self.mocked = mock.patch(py2_p, new_callable=mock.mock_open()) |
117 | - return self.mocked.start() |
118 | + imp.find_module(name) |
119 | except ImportError: |
120 | - self.mocked = mock.patch(py3_p, new_callable=mock.mock_open()) |
121 | - return self.mocked.start() |
122 | - |
123 | - def __exit__(self, etype, value, trace): |
124 | - if self.mocked: |
125 | - self.mocked.stop() |
126 | - self.mocked = None |
127 | + continue |
128 | + else: |
129 | + print('importing and returning: %s' % name) |
130 | + importlib.import_module(name) |
131 | + return name |
132 | + |
133 | + |
134 | +@contextlib.contextmanager |
135 | +def simple_mocked_open(content=None): |
136 | + if not content: |
137 | + content = '' |
138 | + m_open = mock.mock_open(read_data=content) |
139 | + mod_name = builtin_module_name() |
140 | + m_patch = '{}.open'.format(mod_name) |
141 | + with mock.patch(m_patch, m_open, create=True): |
142 | + yield m_open |
143 | |
144 | === modified file 'tests/unittests/test_apt_source.py' |
145 | --- tests/unittests/test_apt_source.py 2017-02-02 22:52:01 +0000 |
146 | +++ tests/unittests/test_apt_source.py 2017-02-06 19:55:25 +0000 |
147 | @@ -42,7 +42,7 @@ |
148 | load file and return content after decoding |
149 | """ |
150 | try: |
151 | - content = util.load_file(filename, mode="r") |
152 | + content = util.load_file(filename, decode=True) |
153 | except Exception as error: |
154 | print('failed to load file content for test: %s' % error) |
155 | raise |
156 | |
157 | === modified file 'tests/unittests/test_block.py' |
158 | --- tests/unittests/test_block.py 2017-02-01 00:37:15 +0000 |
159 | +++ tests/unittests/test_block.py 2017-02-06 19:55:25 +0000 |
160 | @@ -8,7 +8,7 @@ |
161 | |
162 | from collections import OrderedDict |
163 | |
164 | -from .helpers import mocked_open |
165 | +from .helpers import simple_mocked_open |
166 | from curtin import util |
167 | from curtin import block |
168 | |
169 | @@ -210,7 +210,8 @@ |
170 | myfile = self.tfile("def_zero") |
171 | util.write_file(myfile, flen * b'\1', omode="wb") |
172 | block.wipe_file(myfile) |
173 | - found = util.load_file(myfile, mode="rb") |
174 | + with open(myfile, mode="rb") as fh: |
175 | + found = fh.read() |
176 | self.assertEqual(found, flen * b'\0') |
177 | |
178 | def test_reader_used(self): |
179 | @@ -223,7 +224,8 @@ |
180 | # populate with nulls |
181 | util.write_file(myfile, flen * b'\0', omode="wb") |
182 | block.wipe_file(myfile, reader=reader, buflen=flen) |
183 | - found = util.load_file(myfile, mode="rb") |
184 | + with open(myfile, mode="rb") as fh: |
185 | + found = fh.read() |
186 | self.assertEqual(found, flen * b'\1') |
187 | |
188 | def test_reader_twice(self): |
189 | @@ -239,7 +241,8 @@ |
190 | myfile = self.tfile("reader_twice") |
191 | util.write_file(myfile, flen * b'\xff', omode="wb") |
192 | block.wipe_file(myfile, reader=reader, buflen=20) |
193 | - found = util.load_file(myfile, mode="rb") |
194 | + with open(myfile, mode="rb") as fh: |
195 | + found = fh.read() |
196 | self.assertEqual(found, expected) |
197 | |
198 | def test_reader_fhandle(self): |
199 | @@ -346,15 +349,13 @@ |
200 | |
201 | @mock.patch('curtin.block.wipe_file') |
202 | def test_wipe_zero(self, mock_wipe_file): |
203 | - with mocked_open() as mock_open: |
204 | + with simple_mocked_open(): |
205 | block.wipe_volume(self.dev, mode='zero') |
206 | mock_wipe_file.assert_called_with(self.dev) |
207 | - mock_open.return_value = mock.MagicMock() |
208 | |
209 | @mock.patch('curtin.block.wipe_file') |
210 | def test_wipe_random(self, mock_wipe_file): |
211 | - with mocked_open() as mock_open: |
212 | - mock_open.return_value = mock.MagicMock() |
213 | + with simple_mocked_open() as mock_open: |
214 | block.wipe_volume(self.dev, mode='random') |
215 | mock_open.assert_called_with('/dev/urandom', 'rb') |
216 | mock_wipe_file.assert_called_with( |
217 | @@ -436,8 +437,8 @@ |
218 | gpt_content_4k = b'\x00' * 0x800 + b'EFI PART' + b'\x00' * (0x800 - 8) |
219 | null_content = b'\x00' * 0xf00 |
220 | |
221 | - def _test_util_load_file(self, content, device, mode, read_len, offset): |
222 | - return (bytes if 'b' in mode else str)(content[offset:offset+read_len]) |
223 | + def _test_util_load_file(self, content, device, read_len, offset, decode): |
224 | + return (bytes if not decode else str)(content[offset:offset+read_len]) |
225 | |
226 | @mock.patch('curtin.block.check_dos_signature') |
227 | @mock.patch('curtin.block.check_efi_signature') |
228 | |
229 | === modified file 'tests/unittests/test_util.py' |
230 | --- tests/unittests/test_util.py 2016-08-29 21:32:21 +0000 |
231 | +++ tests/unittests/test_util.py 2017-02-06 19:55:25 +0000 |
232 | @@ -1,4 +1,4 @@ |
233 | -from unittest import TestCase |
234 | +from unittest import TestCase, skipIf |
235 | import mock |
236 | import os |
237 | import stat |
238 | @@ -6,6 +6,7 @@ |
239 | import tempfile |
240 | |
241 | from curtin import util |
242 | +from .helpers import simple_mocked_open |
243 | |
244 | |
245 | class TestLogTimer(TestCase): |
246 | @@ -460,4 +461,34 @@ |
247 | m_subp.assert_called_with(cmd, target=target) |
248 | |
249 | |
250 | +class TestLoadFile(TestCase): |
251 | + """Test utility 'load_file'""" |
252 | + |
253 | + def test_load_file_simple(self): |
254 | + fname = 'test.cfg' |
255 | + contents = "#curtin-config" |
256 | + with simple_mocked_open(content=contents) as m_open: |
257 | + loaded_contents = util.load_file(fname, decode=False) |
258 | + self.assertEqual(contents, loaded_contents) |
259 | + m_open.assert_called_with(fname, 'rb') |
260 | + |
261 | + @skipIf(mock.__version__ < '2.0.0', "mock version < 2.0.0") |
262 | + def test_load_file_handles_utf8(self): |
263 | + fname = 'test.cfg' |
264 | + contents = b'd\xc3\xa9j\xc8\xa7' |
265 | + with simple_mocked_open(content=contents) as m_open: |
266 | + with open(fname, 'rb') as f: |
267 | + self.assertEqual(f.read(), contents) |
268 | + m_open.assert_called_with(fname, 'rb') |
269 | + |
270 | + @skipIf(mock.__version__ < '2.0.0', "mock version < 2.0.0") |
271 | + @mock.patch('curtin.util.decode_binary') |
272 | + def test_load_file_respects_decode_false(self, mock_decode): |
273 | + fname = 'test.cfg' |
274 | + contents = b'start \xc3\xa9 end' |
275 | + with simple_mocked_open(contents): |
276 | + loaded_contents = util.load_file(fname, decode=False) |
277 | + self.assertEqual(type(loaded_contents), bytes) |
278 | + self.assertEqual(loaded_contents, contents) |
279 | + |
280 | # vi: ts=4 expandtab syntax=python |
281 | |
282 | === modified file 'tests/vmtests/__init__.py' |
283 | --- tests/vmtests/__init__.py 2017-01-27 16:13:17 +0000 |
284 | +++ tests/vmtests/__init__.py 2017-02-06 19:55:25 +0000 |
285 | @@ -546,10 +546,11 @@ |
286 | install_log = lfh.read().decode('utf-8', errors='replace') |
287 | errmsg, errors = check_install_log(install_log) |
288 | if errmsg: |
289 | + logger.error('Found error: ' + errmsg) |
290 | for e in errors: |
291 | - logger.error(e) |
292 | - logger.error(errmsg) |
293 | - raise Exception(cls.__name__ + ":" + errmsg) |
294 | + logger.error('Context:\n' + e) |
295 | + raise Exception(cls.__name__ + ":" + errmsg + |
296 | + '\n'.join(errors)) |
297 | else: |
298 | logger.info('Install OK') |
299 | else: |
300 | @@ -912,6 +913,14 @@ |
301 | raise exc |
302 | |
303 | |
304 | +def find_error_context(err_match, contents, nrchars=200): |
305 | + context_start = err_match.start() - nrchars |
306 | + context_end = err_match.end() + nrchars |
307 | + # extract contents, split into lines, drop the first and last partials |
308 | + # recombine and return |
309 | + return "\n".join(contents[context_start:context_end].splitlines()[1:-1]) |
310 | + |
311 | + |
312 | def check_install_log(install_log): |
313 | # look if install is OK via curtin 'Installation ok" |
314 | # if we dont find that, scan for known error messages and report |
315 | @@ -925,17 +934,18 @@ |
316 | 'Installation\ failed', |
317 | 'ImportError: No module named.*', |
318 | 'Unexpected error while running command', |
319 | - 'E: Unable to locate package.*'])) |
320 | + 'E: Unable to locate package.*', |
321 | + 'Traceback.*most recent call last.*:'])) |
322 | |
323 | install_is_ok = re.findall(install_pass, install_log) |
324 | + # always scan for errors |
325 | + found_errors = re.finditer(install_fail, install_log) |
326 | if len(install_is_ok) == 0: |
327 | - errors = re.findall(install_fail, install_log) |
328 | - if len(errors) > 0: |
329 | - for e in errors: |
330 | - logger.error(e) |
331 | - errmsg = ('Errors during curtin installer') |
332 | - else: |
333 | - errmsg = ('Failed to verify Installation is OK') |
334 | + errmsg = ('Failed to verify Installation is OK') |
335 | + |
336 | + for e in found_errors: |
337 | + errors.append(find_error_context(e, install_log)) |
338 | + errmsg = ('Errors during curtin installer') |
339 | |
340 | return errmsg, errors |
341 | |
342 | @@ -1011,6 +1021,7 @@ |
343 | { shutdown -P now "Shutting down on centos"; } |
344 | [ "$(lsb_release -sc)" = "precise" ] && |
345 | { shutdown -P now "Shutting down on precise"; } |
346 | + exit 0; |
347 | """) |
348 | |
349 | scripts = ([collect_prep] + collect_scripts + [collect_post] + |
PASSED: Continuous integration, rev:435 /jenkins. ubuntu. com/server/ job/curtin- ci/281/ /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= vm-amd64/ 281 /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= vm-i386/ 281 /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= vm-ppc64el/ 281 /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= vm-s390x/ 281
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild: /jenkins. ubuntu. com/server/ job/curtin- ci/281/ rebuild
https:/