Merge ~raharper/curtin:fix/unittest-prevent-subp-by-default into curtin:master
- Git
- lp:~raharper/curtin
- fix/unittest-prevent-subp-by-default
- Merge into master
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Ryan Harper | ||||
Approved revision: | 56df4215c9d7b4671f435ca84284f1bd52f89705 | ||||
Merge reported by: | Server Team CI bot | ||||
Merged at revision: | not available | ||||
Proposed branch: | ~raharper/curtin:fix/unittest-prevent-subp-by-default | ||||
Merge into: | curtin:master | ||||
Diff against target: |
457 lines (+165/-77) 10 files modified
tests/unittests/helpers.py (+52/-3) tests/unittests/test_apt_custom_sources_list.py (+2/-0) tests/unittests/test_apt_source.py (+2/-0) tests/unittests/test_block.py (+11/-7) tests/unittests/test_block_zfs.py (+24/-20) tests/unittests/test_clear_holders.py (+5/-2) tests/unittests/test_commands_block_meta.py (+4/-0) tests/unittests/test_commands_collect_logs.py (+2/-0) tests/unittests/test_gpg.py (+50/-44) tests/unittests/test_util.py (+13/-1) |
||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Server Team CI bot | continuous-integration | Approve | |
Dan Watkins (community) | Approve | ||
Review via email: mp+382604@code.launchpad.net |
Commit message
unittest: do not allow util.subp by default
Bring over our util.subp mock from cloud-init. Rework unittest to
enable subp as needed.
LP: #1873913
Description of the change
Server Team CI bot (server-team-bot) wrote : | # |
Dan Watkins (oddbloke) wrote : | # |
Good thinking to pull this over, I have some comments inline.
Ryan Harper (raharper) wrote : | # |
Let me drop the logging bits; I replied in line; I think the per-test enablement with a tearDown() is reasonable. Let me see if I can sub-class the main test class and put the subp_enabled ones within that; hoping to avoid duplicating code from one class to another when all we need is to enable/disable subp.
Dan Watkins (oddbloke) wrote : | # |
On Mon, Apr 20, 2020 at 08:27:59PM -0000, Ryan Harper wrote:
> Let me drop the logging bits
Thanks!
> > diff --git a/tests/
> > index 3508d4b..2be7c41 100644
> > --- a/tests/
> > +++ b/tests/
> > @@ -425,6 +425,9 @@ class TestAssertZfsSu
> > def setUp(self):
> > super(TestAsser
> >
> > + def tearDown(self):
> > + self.allowed_subp = False
>
> I think this is reasonable safe. It's off by default. The test-cases
> that know they need it set it at the start of their test_*; this just
> retains the default off unless you need it and I think help reduce the
> scope of when subp is enabled.
Yep, I think the case I was worried about isn't applicable here, so
let's stick with this.
Dan Watkins (oddbloke) wrote : | # |
Sorry, noticed one more thing! Other than that, this looks good now, thanks for the refactors!
Ryan Harper (raharper) wrote : | # |
Fixing
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:86771d11f41
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Server Team CI bot (server-team-bot) wrote : | # |
Commit message lints:
- Line #2 has 15 too many characters. Line starts with: "Bring over our util.subp"...
Server Team CI bot (server-team-bot) wrote : | # |
Autolanding: FAILED
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Server Team CI bot (server-team-bot) wrote : | # |
Autolanding: FAILED
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Server Team CI bot (server-team-bot) wrote : | # |
Autolanding: FAILED
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Server Team CI bot (server-team-bot) wrote : | # |
Autolanding: FAILED
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Server Team CI bot (server-team-bot) wrote : | # |
Autolanding: FAILED
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
- 56df421... by Ryan Harper
-
Fix new calls of util.subp and multipath.
is_mpath_ member
Ryan Harper (raharper) wrote : | # |
wait for CI to say yes, then mark approve
Server Team CI bot (server-team-bot) wrote : | # |
Autolanding: FAILED
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:f1e57f03529
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:56df4215c9d
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Preview Diff
1 | diff --git a/tests/unittests/helpers.py b/tests/unittests/helpers.py | |||
2 | index 9514745..2f5e51a 100644 | |||
3 | --- a/tests/unittests/helpers.py | |||
4 | +++ b/tests/unittests/helpers.py | |||
5 | @@ -1,6 +1,5 @@ | |||
6 | 1 | # This file is part of curtin. See LICENSE file for copyright and license info. | 1 | # This file is part of curtin. See LICENSE file for copyright and license info. |
7 | 2 | 2 | ||
8 | 3 | import contextlib | ||
9 | 4 | import imp | 3 | import imp |
10 | 5 | import importlib | 4 | import importlib |
11 | 6 | import mock | 5 | import mock |
12 | @@ -10,9 +9,11 @@ import shutil | |||
13 | 10 | import string | 9 | import string |
14 | 11 | import tempfile | 10 | import tempfile |
15 | 12 | from unittest import TestCase, skipIf | 11 | from unittest import TestCase, skipIf |
17 | 13 | 12 | from contextlib import contextmanager | |
18 | 14 | from curtin import util | 13 | from curtin import util |
19 | 15 | 14 | ||
20 | 15 | _real_subp = util.subp | ||
21 | 16 | |||
22 | 16 | 17 | ||
23 | 17 | def builtin_module_name(): | 18 | def builtin_module_name(): |
24 | 18 | options = ('builtins', '__builtin__') | 19 | options = ('builtins', '__builtin__') |
25 | @@ -27,7 +28,7 @@ def builtin_module_name(): | |||
26 | 27 | return name | 28 | return name |
27 | 28 | 29 | ||
28 | 29 | 30 | ||
30 | 30 | @contextlib.contextmanager | 31 | @contextmanager |
31 | 31 | def simple_mocked_open(content=None): | 32 | def simple_mocked_open(content=None): |
32 | 32 | if not content: | 33 | if not content: |
33 | 33 | content = '' | 34 | content = '' |
34 | @@ -54,6 +55,54 @@ def skipUnlessJsonSchema(): | |||
35 | 54 | class CiTestCase(TestCase): | 55 | class CiTestCase(TestCase): |
36 | 55 | """Common testing class which all curtin unit tests subclass.""" | 56 | """Common testing class which all curtin unit tests subclass.""" |
37 | 56 | 57 | ||
38 | 58 | allowed_subp = False | ||
39 | 59 | SUBP_SHELL_TRUE = "shell=True" | ||
40 | 60 | |||
41 | 61 | @contextmanager | ||
42 | 62 | def allow_subp(self, allowed_subp): | ||
43 | 63 | orig = self.allowed_subp | ||
44 | 64 | try: | ||
45 | 65 | self.allowed_subp = allowed_subp | ||
46 | 66 | yield | ||
47 | 67 | finally: | ||
48 | 68 | self.allowed_subp = orig | ||
49 | 69 | |||
50 | 70 | def setUp(self): | ||
51 | 71 | super(CiTestCase, self).setUp() | ||
52 | 72 | if self.allowed_subp is True: | ||
53 | 73 | util.subp = _real_subp | ||
54 | 74 | else: | ||
55 | 75 | util.subp = self._fake_subp | ||
56 | 76 | |||
57 | 77 | def _fake_subp(self, *args, **kwargs): | ||
58 | 78 | if 'args' in kwargs: | ||
59 | 79 | cmd = kwargs['args'] | ||
60 | 80 | else: | ||
61 | 81 | cmd = args[0] | ||
62 | 82 | |||
63 | 83 | if not isinstance(cmd, str): | ||
64 | 84 | cmd = cmd[0] | ||
65 | 85 | pass_through = False | ||
66 | 86 | if not isinstance(self.allowed_subp, (list, bool)): | ||
67 | 87 | raise TypeError("self.allowed_subp supports list or bool.") | ||
68 | 88 | if isinstance(self.allowed_subp, bool): | ||
69 | 89 | pass_through = self.allowed_subp | ||
70 | 90 | else: | ||
71 | 91 | pass_through = ( | ||
72 | 92 | (cmd in self.allowed_subp) or | ||
73 | 93 | (self.SUBP_SHELL_TRUE in self.allowed_subp and | ||
74 | 94 | kwargs.get('shell'))) | ||
75 | 95 | if pass_through: | ||
76 | 96 | return _real_subp(*args, **kwargs) | ||
77 | 97 | raise Exception( | ||
78 | 98 | "called subp. set self.allowed_subp=True to allow\n subp(%s)" % | ||
79 | 99 | ', '.join([str(repr(a)) for a in args] + | ||
80 | 100 | ["%s=%s" % (k, repr(v)) for k, v in kwargs.items()])) | ||
81 | 101 | |||
82 | 102 | def tearDown(self): | ||
83 | 103 | util.subp = _real_subp | ||
84 | 104 | super(CiTestCase, self).tearDown() | ||
85 | 105 | |||
86 | 57 | def add_patch(self, target, attr, **kwargs): | 106 | def add_patch(self, target, attr, **kwargs): |
87 | 58 | """Patches specified target object and sets it as attr on test | 107 | """Patches specified target object and sets it as attr on test |
88 | 59 | instance also schedules cleanup""" | 108 | instance also schedules cleanup""" |
89 | diff --git a/tests/unittests/test_apt_custom_sources_list.py b/tests/unittests/test_apt_custom_sources_list.py | |||
90 | index fb6eb0c..bf004b1 100644 | |||
91 | --- a/tests/unittests/test_apt_custom_sources_list.py | |||
92 | +++ b/tests/unittests/test_apt_custom_sources_list.py | |||
93 | @@ -93,7 +93,9 @@ class TestAptSourceConfigSourceList(CiTestCase): | |||
94 | 93 | def setUp(self): | 93 | def setUp(self): |
95 | 94 | super(TestAptSourceConfigSourceList, self).setUp() | 94 | super(TestAptSourceConfigSourceList, self).setUp() |
96 | 95 | self.new_root = self.tmp_dir() | 95 | self.new_root = self.tmp_dir() |
97 | 96 | self.add_patch('curtin.util.subp', 'm_subp') | ||
98 | 96 | # self.patchUtils(self.new_root) | 97 | # self.patchUtils(self.new_root) |
99 | 98 | self.m_subp.return_value = ("amd64", "") | ||
100 | 97 | 99 | ||
101 | 98 | def _apt_source_list(self, cfg, expected): | 100 | def _apt_source_list(self, cfg, expected): |
102 | 99 | "_apt_source_list - Test rendering from template (generic)" | 101 | "_apt_source_list - Test rendering from template (generic)" |
103 | diff --git a/tests/unittests/test_apt_source.py b/tests/unittests/test_apt_source.py | |||
104 | index 353cdf8..6ae5579 100644 | |||
105 | --- a/tests/unittests/test_apt_source.py | |||
106 | +++ b/tests/unittests/test_apt_source.py | |||
107 | @@ -75,6 +75,8 @@ class TestAptSourceConfig(CiTestCase): | |||
108 | 75 | self.aptlistfile3 = os.path.join(self.tmp, "single-deb3.list") | 75 | self.aptlistfile3 = os.path.join(self.tmp, "single-deb3.list") |
109 | 76 | self.join = os.path.join | 76 | self.join = os.path.join |
110 | 77 | self.matcher = re.compile(ADD_APT_REPO_MATCH).search | 77 | self.matcher = re.compile(ADD_APT_REPO_MATCH).search |
111 | 78 | self.add_patch('curtin.util.subp', 'm_subp') | ||
112 | 79 | self.m_subp.return_value = ('s390x', '') | ||
113 | 78 | 80 | ||
114 | 79 | @staticmethod | 81 | @staticmethod |
115 | 80 | def _add_apt_sources(*args, **kwargs): | 82 | def _add_apt_sources(*args, **kwargs): |
116 | diff --git a/tests/unittests/test_block.py b/tests/unittests/test_block.py | |||
117 | index 707386f..dcdcf51 100644 | |||
118 | --- a/tests/unittests/test_block.py | |||
119 | +++ b/tests/unittests/test_block.py | |||
120 | @@ -522,6 +522,12 @@ class TestPartTableSignature(CiTestCase): | |||
121 | 522 | gpt_content_4k = b'\x00' * 0x800 + b'EFI PART' + b'\x00' * (0x800 - 8) | 522 | gpt_content_4k = b'\x00' * 0x800 + b'EFI PART' + b'\x00' * (0x800 - 8) |
122 | 523 | null_content = b'\x00' * 0xf00 | 523 | null_content = b'\x00' * 0xf00 |
123 | 524 | 524 | ||
124 | 525 | def setUp(self): | ||
125 | 526 | super(TestPartTableSignature, self).setUp() | ||
126 | 527 | self.add_patch('curtin.util.subp', 'm_subp') | ||
127 | 528 | self.m_subp.side_effect = iter([ | ||
128 | 529 | util.ProcessExecutionError(stdout="", stderr="", exit_code=1)]) | ||
129 | 530 | |||
130 | 525 | def _test_util_load_file(self, content, device, read_len, offset, decode): | 531 | def _test_util_load_file(self, content, device, read_len, offset, decode): |
131 | 526 | return (bytes if not decode else str)(content[offset:offset+read_len]) | 532 | return (bytes if not decode else str)(content[offset:offset+read_len]) |
132 | 527 | 533 | ||
133 | @@ -577,15 +583,13 @@ class TestPartTableSignature(CiTestCase): | |||
134 | 577 | (self.assertTrue if expected else self.assertFalse)( | 583 | (self.assertTrue if expected else self.assertFalse)( |
135 | 578 | block.check_efi_signature(self.blockdev)) | 584 | block.check_efi_signature(self.blockdev)) |
136 | 579 | 585 | ||
140 | 580 | @mock.patch('curtin.block.util.subp') | 586 | def test_check_vtoc_signature_finds_vtoc_returns_true(self): |
141 | 581 | def test_check_vtoc_signature_finds_vtoc_returns_true(self, mock_subp): | 587 | self.m_subp.side_effect = iter([("vtoc.....ok", "")]) |
139 | 582 | mock_subp.return_value = ("vtoc.....ok", "") | ||
142 | 583 | self.assertTrue(block.check_vtoc_signature(self.blockdev)) | 588 | self.assertTrue(block.check_vtoc_signature(self.blockdev)) |
143 | 584 | 589 | ||
148 | 585 | @mock.patch('curtin.block.util.subp') | 590 | def test_check_vtoc_signature_returns_false_with_no_sig(self): |
149 | 586 | def test_check_vtoc_signature_returns_false_with_no_sig(self, mock_subp): | 591 | self.m_subp.side_effect = iter([ |
150 | 587 | mock_subp.side_effect = [ | 592 | util.ProcessExecutionError(stdout="", stderr="", exit_code=1)]) |
147 | 588 | util.ProcessExecutionError(stdout="", stderr="", exit_code=1)] | ||
151 | 589 | self.assertFalse(block.check_vtoc_signature(self.blockdev)) | 593 | self.assertFalse(block.check_vtoc_signature(self.blockdev)) |
152 | 590 | 594 | ||
153 | 591 | 595 | ||
154 | diff --git a/tests/unittests/test_block_zfs.py b/tests/unittests/test_block_zfs.py | |||
155 | index 3508d4b..e392000 100644 | |||
156 | --- a/tests/unittests/test_block_zfs.py | |||
157 | +++ b/tests/unittests/test_block_zfs.py | |||
158 | @@ -468,6 +468,30 @@ class TestAssertZfsSupported(CiTestCase): | |||
159 | 468 | with self.assertRaises(RuntimeError): | 468 | with self.assertRaises(RuntimeError): |
160 | 469 | zfs.zfs_assert_supported() | 469 | zfs.zfs_assert_supported() |
161 | 470 | 470 | ||
162 | 471 | @mock.patch('curtin.block.zfs.get_supported_filesystems') | ||
163 | 472 | @mock.patch('curtin.block.zfs.util.lsb_release') | ||
164 | 473 | @mock.patch('curtin.block.zfs.util.get_platform_arch') | ||
165 | 474 | @mock.patch('curtin.block.zfs.util') | ||
166 | 475 | def test_zfs_assert_supported_raises_exc_on_missing_binaries(self, | ||
167 | 476 | mock_util, | ||
168 | 477 | m_arch, | ||
169 | 478 | m_release, | ||
170 | 479 | m_supfs): | ||
171 | 480 | """zfs_assert_supported raises RuntimeError if no zpool or zfs tools""" | ||
172 | 481 | mock_util.get_platform_arch.return_value = 'amd64' | ||
173 | 482 | mock_util.lsb_release.return_value = {'codename': 'bionic'} | ||
174 | 483 | mock_util.subp.return_value = ("", "") | ||
175 | 484 | m_supfs.return_value = ['zfs'] | ||
176 | 485 | mock_util.which.return_value = None | ||
177 | 486 | |||
178 | 487 | with self.assertRaises(RuntimeError): | ||
179 | 488 | zfs.zfs_assert_supported() | ||
180 | 489 | |||
181 | 490 | |||
182 | 491 | class TestAssertZfsSupportedSubp(TestAssertZfsSupported): | ||
183 | 492 | |||
184 | 493 | allowed_subp = True | ||
185 | 494 | |||
186 | 471 | @mock.patch('curtin.block.zfs.util.subprocess.Popen') | 495 | @mock.patch('curtin.block.zfs.util.subprocess.Popen') |
187 | 472 | @mock.patch('curtin.block.zfs.util.is_kmod_loaded') | 496 | @mock.patch('curtin.block.zfs.util.is_kmod_loaded') |
188 | 473 | @mock.patch('curtin.block.zfs.get_supported_filesystems') | 497 | @mock.patch('curtin.block.zfs.get_supported_filesystems') |
189 | @@ -481,7 +505,6 @@ class TestAssertZfsSupported(CiTestCase): | |||
190 | 481 | m_popen, | 505 | m_popen, |
191 | 482 | ): | 506 | ): |
192 | 483 | """zfs_assert_supported raises RuntimeError modprobe zfs error""" | 507 | """zfs_assert_supported raises RuntimeError modprobe zfs error""" |
193 | 484 | |||
194 | 485 | m_arch.return_value = 'amd64' | 508 | m_arch.return_value = 'amd64' |
195 | 486 | m_release.return_value = {'codename': 'bionic'} | 509 | m_release.return_value = {'codename': 'bionic'} |
196 | 487 | m_supfs.return_value = ['ext4'] | 510 | m_supfs.return_value = ['ext4'] |
197 | @@ -497,25 +520,6 @@ class TestAssertZfsSupported(CiTestCase): | |||
198 | 497 | with self.assertRaises(RuntimeError): | 520 | with self.assertRaises(RuntimeError): |
199 | 498 | zfs.zfs_assert_supported() | 521 | zfs.zfs_assert_supported() |
200 | 499 | 522 | ||
201 | 500 | @mock.patch('curtin.block.zfs.get_supported_filesystems') | ||
202 | 501 | @mock.patch('curtin.block.zfs.util.lsb_release') | ||
203 | 502 | @mock.patch('curtin.block.zfs.util.get_platform_arch') | ||
204 | 503 | @mock.patch('curtin.block.zfs.util') | ||
205 | 504 | def test_zfs_assert_supported_raises_exc_on_missing_binaries(self, | ||
206 | 505 | mock_util, | ||
207 | 506 | m_arch, | ||
208 | 507 | m_release, | ||
209 | 508 | m_supfs): | ||
210 | 509 | """zfs_assert_supported raises RuntimeError if no zpool or zfs tools""" | ||
211 | 510 | mock_util.get_platform_arch.return_value = 'amd64' | ||
212 | 511 | mock_util.lsb_release.return_value = {'codename': 'bionic'} | ||
213 | 512 | mock_util.subp.return_value = ("", "") | ||
214 | 513 | m_supfs.return_value = ['zfs'] | ||
215 | 514 | mock_util.which.return_value = None | ||
216 | 515 | |||
217 | 516 | with self.assertRaises(RuntimeError): | ||
218 | 517 | zfs.zfs_assert_supported() | ||
219 | 518 | |||
220 | 519 | 523 | ||
221 | 520 | class TestZfsSupported(CiTestCase): | 524 | class TestZfsSupported(CiTestCase): |
222 | 521 | 525 | ||
223 | diff --git a/tests/unittests/test_clear_holders.py b/tests/unittests/test_clear_holders.py | |||
224 | index f73b49d..82b481a 100644 | |||
225 | --- a/tests/unittests/test_clear_holders.py | |||
226 | +++ b/tests/unittests/test_clear_holders.py | |||
227 | @@ -701,13 +701,14 @@ class TestClearHolders(CiTestCase): | |||
228 | 701 | mock_gen_holders_tree.return_value = self.example_holders_trees[1][1] | 701 | mock_gen_holders_tree.return_value = self.example_holders_trees[1][1] |
229 | 702 | clear_holders.assert_clear(device) | 702 | clear_holders.assert_clear(device) |
230 | 703 | 703 | ||
231 | 704 | @mock.patch('curtin.block.clear_holders.udev') | ||
232 | 704 | @mock.patch('curtin.block.clear_holders.multipath') | 705 | @mock.patch('curtin.block.clear_holders.multipath') |
233 | 705 | @mock.patch('curtin.block.clear_holders.lvm') | 706 | @mock.patch('curtin.block.clear_holders.lvm') |
234 | 706 | @mock.patch('curtin.block.clear_holders.zfs') | 707 | @mock.patch('curtin.block.clear_holders.zfs') |
235 | 707 | @mock.patch('curtin.block.clear_holders.mdadm') | 708 | @mock.patch('curtin.block.clear_holders.mdadm') |
236 | 708 | @mock.patch('curtin.block.clear_holders.util') | 709 | @mock.patch('curtin.block.clear_holders.util') |
237 | 709 | def test_start_clear_holders_deps(self, mock_util, mock_mdadm, mock_zfs, | 710 | def test_start_clear_holders_deps(self, mock_util, mock_mdadm, mock_zfs, |
239 | 710 | mock_lvm, mock_mp): | 711 | mock_lvm, mock_mp, mock_udev): |
240 | 711 | mock_zfs.zfs_supported.return_value = True | 712 | mock_zfs.zfs_supported.return_value = True |
241 | 712 | clear_holders.start_clear_holders_deps() | 713 | clear_holders.start_clear_holders_deps() |
242 | 713 | mock_mdadm.mdadm_assemble.assert_called_with( | 714 | mock_mdadm.mdadm_assemble.assert_called_with( |
243 | @@ -715,13 +716,15 @@ class TestClearHolders(CiTestCase): | |||
244 | 715 | mock_util.load_kernel_module.assert_has_calls([ | 716 | mock_util.load_kernel_module.assert_has_calls([ |
245 | 716 | mock.call('bcache')]) | 717 | mock.call('bcache')]) |
246 | 717 | 718 | ||
247 | 719 | @mock.patch('curtin.block.clear_holders.udev') | ||
248 | 718 | @mock.patch('curtin.block.clear_holders.multipath') | 720 | @mock.patch('curtin.block.clear_holders.multipath') |
249 | 719 | @mock.patch('curtin.block.clear_holders.lvm') | 721 | @mock.patch('curtin.block.clear_holders.lvm') |
250 | 720 | @mock.patch('curtin.block.clear_holders.zfs') | 722 | @mock.patch('curtin.block.clear_holders.zfs') |
251 | 721 | @mock.patch('curtin.block.clear_holders.mdadm') | 723 | @mock.patch('curtin.block.clear_holders.mdadm') |
252 | 722 | @mock.patch('curtin.block.clear_holders.util') | 724 | @mock.patch('curtin.block.clear_holders.util') |
253 | 723 | def test_start_clear_holders_deps_nozfs(self, mock_util, mock_mdadm, | 725 | def test_start_clear_holders_deps_nozfs(self, mock_util, mock_mdadm, |
255 | 724 | mock_zfs, mock_lvm, mock_mp): | 726 | mock_zfs, mock_lvm, mock_mp, |
256 | 727 | mock_udev): | ||
257 | 725 | """test that we skip zfs modprobe on unsupported platforms""" | 728 | """test that we skip zfs modprobe on unsupported platforms""" |
258 | 726 | mock_zfs.zfs_supported.return_value = False | 729 | mock_zfs.zfs_supported.return_value = False |
259 | 727 | clear_holders.start_clear_holders_deps() | 730 | clear_holders.start_clear_holders_deps() |
260 | diff --git a/tests/unittests/test_commands_block_meta.py b/tests/unittests/test_commands_block_meta.py | |||
261 | index aac1a9e..6da6412 100644 | |||
262 | --- a/tests/unittests/test_commands_block_meta.py | |||
263 | +++ b/tests/unittests/test_commands_block_meta.py | |||
264 | @@ -19,6 +19,8 @@ class TestGetPathToStorageVolume(CiTestCase): | |||
265 | 19 | self.add_patch(basepath + 'os.path.exists', 'm_exists') | 19 | self.add_patch(basepath + 'os.path.exists', 'm_exists') |
266 | 20 | self.add_patch(basepath + 'block.lookup_disk', 'm_lookup') | 20 | self.add_patch(basepath + 'block.lookup_disk', 'm_lookup') |
267 | 21 | self.add_patch(basepath + 'devsync', 'm_devsync') | 21 | self.add_patch(basepath + 'devsync', 'm_devsync') |
268 | 22 | self.add_patch(basepath + 'util.subp', 'm_subp') | ||
269 | 23 | self.add_patch(basepath + 'multipath.is_mpath_member', 'm_mp') | ||
270 | 22 | 24 | ||
271 | 23 | def test_block_lookup_called_with_disk_wwn(self): | 25 | def test_block_lookup_called_with_disk_wwn(self): |
272 | 24 | volume = 'mydisk' | 26 | volume = 'mydisk' |
273 | @@ -93,6 +95,8 @@ class TestGetPathToStorageVolume(CiTestCase): | |||
274 | 93 | ValueError('Error'), ValueError('Error')]) | 95 | ValueError('Error'), ValueError('Error')]) |
275 | 94 | # no path | 96 | # no path |
276 | 95 | self.m_exists.return_value = False | 97 | self.m_exists.return_value = False |
277 | 98 | # not multipath | ||
278 | 99 | self.m_mp.return_value = False | ||
279 | 96 | 100 | ||
280 | 97 | with self.assertRaises(ValueError): | 101 | with self.assertRaises(ValueError): |
281 | 98 | block_meta.get_path_to_storage_volume(volume, s_cfg) | 102 | block_meta.get_path_to_storage_volume(volume, s_cfg) |
282 | diff --git a/tests/unittests/test_commands_collect_logs.py b/tests/unittests/test_commands_collect_logs.py | |||
283 | index 1feba18..b5df902 100644 | |||
284 | --- a/tests/unittests/test_commands_collect_logs.py | |||
285 | +++ b/tests/unittests/test_commands_collect_logs.py | |||
286 | @@ -288,6 +288,7 @@ class TestCreateTar(CiTestCase): | |||
287 | 288 | 288 | ||
288 | 289 | Configured log_file or post_files which don't exist are ignored. | 289 | Configured log_file or post_files which don't exist are ignored. |
289 | 290 | """ | 290 | """ |
290 | 291 | self.add_patch('curtin.util.subp', 'mock_subp') | ||
291 | 291 | tarfile = self.tmp_path('my.tar', _dir=self.new_root) | 292 | tarfile = self.tmp_path('my.tar', _dir=self.new_root) |
292 | 292 | log1 = self.tmp_path('some.log', _dir=self.new_root) | 293 | log1 = self.tmp_path('some.log', _dir=self.new_root) |
293 | 293 | write_file(log1, 'log content') | 294 | write_file(log1, 'log content') |
294 | @@ -314,6 +315,7 @@ class TestCreateTar(CiTestCase): | |||
295 | 314 | 315 | ||
296 | 315 | def test_create_log_tarfile_redacts_maas_credentials(self): | 316 | def test_create_log_tarfile_redacts_maas_credentials(self): |
297 | 316 | """create_log_tarfile redacts sensitive maas credentials configured.""" | 317 | """create_log_tarfile redacts sensitive maas credentials configured.""" |
298 | 318 | self.add_patch('curtin.util.subp', 'mock_subp') | ||
299 | 317 | tarfile = self.tmp_path('my.tar', _dir=self.new_root) | 319 | tarfile = self.tmp_path('my.tar', _dir=self.new_root) |
300 | 318 | self.add_patch( | 320 | self.add_patch( |
301 | 319 | 'curtin.commands.collect_logs._redact_sensitive_information', | 321 | 'curtin.commands.collect_logs._redact_sensitive_information', |
302 | diff --git a/tests/unittests/test_gpg.py b/tests/unittests/test_gpg.py | |||
303 | index 2c83ae3..42ff8c4 100644 | |||
304 | --- a/tests/unittests/test_gpg.py | |||
305 | +++ b/tests/unittests/test_gpg.py | |||
306 | @@ -48,50 +48,6 @@ class TestCurtinGpg(CiTestCase): | |||
307 | 48 | "--recv", key], capture=True, | 48 | "--recv", key], capture=True, |
308 | 49 | retries=None) | 49 | retries=None) |
309 | 50 | 50 | ||
310 | 51 | @patch('time.sleep') | ||
311 | 52 | @patch('curtin.util._subp') | ||
312 | 53 | def test_recv_key_retry_raises(self, mock_under_subp, mock_sleep): | ||
313 | 54 | key = 'DEADBEEF' | ||
314 | 55 | keyserver = 'keyserver.ubuntu.com' | ||
315 | 56 | retries = (1, 2, 5, 10) | ||
316 | 57 | nr_calls = 5 | ||
317 | 58 | mock_under_subp.side_effect = iter([ | ||
318 | 59 | util.ProcessExecutionError()] * nr_calls) | ||
319 | 60 | |||
320 | 61 | with self.assertRaises(ValueError): | ||
321 | 62 | gpg.recv_key(key, keyserver, retries=retries) | ||
322 | 63 | |||
323 | 64 | print("_subp calls: %s" % mock_under_subp.call_args_list) | ||
324 | 65 | print("sleep calls: %s" % mock_sleep.call_args_list) | ||
325 | 66 | expected_calls = nr_calls * [ | ||
326 | 67 | call(["gpg", "--keyserver", keyserver, "--recv", key], | ||
327 | 68 | capture=True)] | ||
328 | 69 | mock_under_subp.assert_has_calls(expected_calls) | ||
329 | 70 | |||
330 | 71 | expected_calls = [call(1), call(2), call(5), call(10)] | ||
331 | 72 | mock_sleep.assert_has_calls(expected_calls) | ||
332 | 73 | |||
333 | 74 | @patch('time.sleep') | ||
334 | 75 | @patch('curtin.util._subp') | ||
335 | 76 | def test_recv_key_retry_works(self, mock_under_subp, mock_sleep): | ||
336 | 77 | key = 'DEADBEEF' | ||
337 | 78 | keyserver = 'keyserver.ubuntu.com' | ||
338 | 79 | nr_calls = 2 | ||
339 | 80 | mock_under_subp.side_effect = iter([ | ||
340 | 81 | util.ProcessExecutionError(), # 1 | ||
341 | 82 | ("", ""), | ||
342 | 83 | ]) | ||
343 | 84 | |||
344 | 85 | gpg.recv_key(key, keyserver, retries=[1]) | ||
345 | 86 | |||
346 | 87 | print("_subp calls: %s" % mock_under_subp.call_args_list) | ||
347 | 88 | print("sleep calls: %s" % mock_sleep.call_args_list) | ||
348 | 89 | expected_calls = nr_calls * [ | ||
349 | 90 | call(["gpg", "--keyserver", keyserver, "--recv", key], | ||
350 | 91 | capture=True)] | ||
351 | 92 | mock_under_subp.assert_has_calls(expected_calls) | ||
352 | 93 | mock_sleep.assert_has_calls([call(1)]) | ||
353 | 94 | |||
354 | 95 | @patch('curtin.util.subp') | 51 | @patch('curtin.util.subp') |
355 | 96 | def test_delete_key(self, mock_subp): | 52 | def test_delete_key(self, mock_subp): |
356 | 97 | key = 'DEADBEEF' | 53 | key = 'DEADBEEF' |
357 | @@ -160,4 +116,54 @@ class TestCurtinGpg(CiTestCase): | |||
358 | 160 | call(key, keyserver=keyserver, retries=None)]) | 116 | call(key, keyserver=keyserver, retries=None)]) |
359 | 161 | mock_del.assert_has_calls([call(key)]) | 117 | mock_del.assert_has_calls([call(key)]) |
360 | 162 | 118 | ||
361 | 119 | |||
362 | 120 | class TestCurtinGpgSubp(TestCurtinGpg): | ||
363 | 121 | |||
364 | 122 | allowed_subp = True | ||
365 | 123 | |||
366 | 124 | @patch('time.sleep') | ||
367 | 125 | @patch('curtin.util._subp') | ||
368 | 126 | def test_recv_key_retry_raises(self, mock_under_subp, mock_sleep): | ||
369 | 127 | key = 'DEADBEEF' | ||
370 | 128 | keyserver = 'keyserver.ubuntu.com' | ||
371 | 129 | retries = (1, 2, 5, 10) | ||
372 | 130 | nr_calls = 5 | ||
373 | 131 | mock_under_subp.side_effect = iter([ | ||
374 | 132 | util.ProcessExecutionError()] * nr_calls) | ||
375 | 133 | |||
376 | 134 | with self.assertRaises(ValueError): | ||
377 | 135 | gpg.recv_key(key, keyserver, retries=retries) | ||
378 | 136 | |||
379 | 137 | print("_subp calls: %s" % mock_under_subp.call_args_list) | ||
380 | 138 | print("sleep calls: %s" % mock_sleep.call_args_list) | ||
381 | 139 | expected_calls = nr_calls * [ | ||
382 | 140 | call(["gpg", "--keyserver", keyserver, "--recv", key], | ||
383 | 141 | capture=True)] | ||
384 | 142 | mock_under_subp.assert_has_calls(expected_calls) | ||
385 | 143 | |||
386 | 144 | expected_calls = [call(1), call(2), call(5), call(10)] | ||
387 | 145 | mock_sleep.assert_has_calls(expected_calls) | ||
388 | 146 | |||
389 | 147 | @patch('time.sleep') | ||
390 | 148 | @patch('curtin.util._subp') | ||
391 | 149 | def test_recv_key_retry_works(self, mock_under_subp, mock_sleep): | ||
392 | 150 | key = 'DEADBEEF' | ||
393 | 151 | keyserver = 'keyserver.ubuntu.com' | ||
394 | 152 | nr_calls = 2 | ||
395 | 153 | mock_under_subp.side_effect = iter([ | ||
396 | 154 | util.ProcessExecutionError(), # 1 | ||
397 | 155 | ("", ""), | ||
398 | 156 | ]) | ||
399 | 157 | |||
400 | 158 | gpg.recv_key(key, keyserver, retries=[1]) | ||
401 | 159 | |||
402 | 160 | print("_subp calls: %s" % mock_under_subp.call_args_list) | ||
403 | 161 | print("sleep calls: %s" % mock_sleep.call_args_list) | ||
404 | 162 | expected_calls = nr_calls * [ | ||
405 | 163 | call(["gpg", "--keyserver", keyserver, "--recv", key], | ||
406 | 164 | capture=True)] | ||
407 | 165 | mock_under_subp.assert_has_calls(expected_calls) | ||
408 | 166 | mock_sleep.assert_has_calls([call(1)]) | ||
409 | 167 | |||
410 | 168 | |||
411 | 163 | # vi: ts=4 expandtab syntax=python | 169 | # vi: ts=4 expandtab syntax=python |
412 | diff --git a/tests/unittests/test_util.py b/tests/unittests/test_util.py | |||
413 | index 438f7ee..0377357 100644 | |||
414 | --- a/tests/unittests/test_util.py | |||
415 | +++ b/tests/unittests/test_util.py | |||
416 | @@ -113,6 +113,7 @@ class TestSubp(CiTestCase): | |||
417 | 113 | utf8_invalid = b'ab\xaadef' | 113 | utf8_invalid = b'ab\xaadef' |
418 | 114 | utf8_valid = b'start \xc3\xa9 end' | 114 | utf8_valid = b'start \xc3\xa9 end' |
419 | 115 | utf8_valid_2 = b'd\xc3\xa9j\xc8\xa7' | 115 | utf8_valid_2 = b'd\xc3\xa9j\xc8\xa7' |
420 | 116 | allowed_subp = True | ||
421 | 116 | 117 | ||
422 | 117 | try: | 118 | try: |
423 | 118 | decode_type = unicode | 119 | decode_type = unicode |
424 | @@ -552,7 +553,7 @@ class TestTargetPath(CiTestCase): | |||
425 | 552 | paths.target_path("/target/", "///my/path/")) | 553 | paths.target_path("/target/", "///my/path/")) |
426 | 553 | 554 | ||
427 | 554 | 555 | ||
429 | 555 | class TestRunInChroot(CiTestCase): | 556 | class TestRunInChrootTestSubp(CiTestCase): |
430 | 556 | """Test the legacy 'RunInChroot'. | 557 | """Test the legacy 'RunInChroot'. |
431 | 557 | 558 | ||
432 | 558 | The test works by mocking ChrootableTarget's __enter__ to do nothing. | 559 | The test works by mocking ChrootableTarget's __enter__ to do nothing. |
433 | @@ -560,6 +561,7 @@ class TestRunInChroot(CiTestCase): | |||
434 | 560 | a.) RunInChroot is a subclass of ChrootableTarget | 561 | a.) RunInChroot is a subclass of ChrootableTarget |
435 | 561 | b.) ChrootableTarget's __exit__ only un-does work that its __enter__ | 562 | b.) ChrootableTarget's __exit__ only un-does work that its __enter__ |
436 | 562 | did. Meaning for our mocked case, it does nothing.""" | 563 | did. Meaning for our mocked case, it does nothing.""" |
437 | 564 | allowed_subp = True | ||
438 | 563 | 565 | ||
439 | 564 | @mock.patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a) | 566 | @mock.patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a) |
440 | 565 | def test_run_in_chroot_with_target_slash(self): | 567 | def test_run_in_chroot_with_target_slash(self): |
441 | @@ -567,6 +569,16 @@ class TestRunInChroot(CiTestCase): | |||
442 | 567 | out, err = i(['echo', 'HI MOM'], capture=True) | 569 | out, err = i(['echo', 'HI MOM'], capture=True) |
443 | 568 | self.assertEqual('HI MOM\n', out) | 570 | self.assertEqual('HI MOM\n', out) |
444 | 569 | 571 | ||
445 | 572 | |||
446 | 573 | class TestRunInChrootTest(CiTestCase): | ||
447 | 574 | """Test the legacy 'RunInChroot'. | ||
448 | 575 | |||
449 | 576 | The test works by mocking ChrootableTarget's __enter__ to do nothing. | ||
450 | 577 | The assumptions made are: | ||
451 | 578 | a.) RunInChroot is a subclass of ChrootableTarget | ||
452 | 579 | b.) ChrootableTarget's __exit__ only un-does work that its __enter__ | ||
453 | 580 | did. Meaning for our mocked case, it does nothing.""" | ||
454 | 581 | |||
455 | 570 | @mock.patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a) | 582 | @mock.patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a) |
456 | 571 | @mock.patch("curtin.util.subp") | 583 | @mock.patch("curtin.util.subp") |
457 | 572 | def test_run_in_chroot_with_target(self, m_subp): | 584 | def test_run_in_chroot_with_target(self, m_subp): |
PASSED: Continuous integration, rev:d68faec0fd4 4fd0d7f2b2d5ef3 2cb95d0056e188 /jenkins. ubuntu. com/server/ job/curtin- ci/44/ /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= metal-amd64/ 44/ /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= metal-arm64/ 44/ /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= metal-ppc64el/ 44/ /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= metal-s390x/ 44/
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/44// rebuild
https:/