Merge ~raharper/curtin:fix/initramfs-only-once-x86-only into curtin:master

Proposed by Ryan Harper
Status: Merged
Approved by: Chad Smith
Approved revision: 9f069ce64a602c40c97b15639e780d0efdcfd1b3
Merge reported by: Server Team CI bot
Merged at revision: not available
Proposed branch: ~raharper/curtin:fix/initramfs-only-once-x86-only
Merge into: curtin:master
Diff against target: 189 lines (+85/-32)
2 files modified
curtin/commands/curthooks.py (+50/-32)
tests/unittests/test_curthooks.py (+35/-0)
Reviewer Review Type Date Requested Status
Chad Smith Approve
Michael Hudson-Doyle Approve
Server Team CI bot continuous-integration Approve
Review via email: mp+376858@code.launchpad.net

Commit message

curthooks: handle s390x/aarch64 kernel install hooks

On s390x, and aarch64, the kernel package directly calls
arch specific commands (zipl, flash-kernel) which fail to run
correctly if an initramfs isn't already created in the image. This
broke installation when we attempt to only create the initramfs
once. This branch adds arch support into the code path to divert
not only 'update-initramfs' but the arch specific tools as well and
restores them prior to running the real 'update-initramfs' command.

LP: #1856038

To post a comment you must log in.
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Chad Smith (chad.smith) wrote :

Looks good. A few questions, nits and concerns.

Revision history for this message
Chad Smith (chad.smith) :
review: Needs Fixing
Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

I agree with Chad's comments I think. I wonder how come this doesn't fail for livefs builds?

Do vmtests not catch this because they are installing images that already have kernels in?

Revision history for this message
Ryan Harper (raharper) :
9f069ce... by Ryan Harper

Use common method for enumerating tools used, add docstrings.

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

> I agree with Chad's comments I think. I wonder how come this doesn't fail for
> livefs builds?

I don't know.

>
> Do vmtests not catch this because they are installing images that already have
> kernels in?

vmtests catch this; It was landed because the autolander which runs some vmtests before merging doesn't run on s390x/arm64.

We'll file an issue for that

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Chad Smith (chad.smith) :
Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

LGTM. Glad there are vmtests that catch this, we should make sure they are regularly run as the archive changes in case the set of used tools changes.

review: Approve
Revision history for this message
Chad Smith (chad.smith) wrote :

This works for me. Thanks for consolidating the common logic on this Ryan. Let's do the dpkg --print-architecture later so we can make sure we are only matching one set of 'platform' strings throughout and retain consistency throughout the code base.

review: Approve
Revision history for this message
Ryan Harper (raharper) :
Revision history for this message
Chad Smith (chad.smith) wrote :

Thanks for the back and forth here.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/curtin/commands/curthooks.py b/curtin/commands/curthooks.py
index 4b36da2..b5baa06 100644
--- a/curtin/commands/curthooks.py
+++ b/curtin/commands/curthooks.py
@@ -104,48 +104,66 @@ def disable_overlayroot(cfg, target):
104 shutil.move(local_conf, local_conf + ".old")104 shutil.move(local_conf, local_conf + ".old")
105105
106106
107def disable_update_initramfs(cfg, target):107def _update_initramfs_tools(machine=None):
108 """ Find 'update-initramfs' in target and if found, change the name. """108 """ Return a list of binary names used to update an initramfs.
109 update_initramfs = util.which('update-initramfs', target=target)
110 if update_initramfs:
111 LOG.debug('Diverting original update-initramfs in target.')
112 rename = update_initramfs + '.curtin-disabled'
113 divert = ['dpkg-divert', '--add', '--rename', '--divert', rename,
114 update_initramfs]
115 with util.ChrootableTarget(target) as in_chroot:
116 in_chroot.subp(divert)
117109
118 # create a dummy update-initramfs which just returns true;110 On some architectures there are helper binaries that are also
119 # this handles postinstall scripts which make invoke update-initramfs111 used and will be included in the list.
120 # directly112 """
121 util.write_file(target + update_initramfs,113 tools = ['update-initramfs']
122 content="#!/bin/true\n# diverted by curtin",114 if not machine:
123 mode=0o755)115 machine = platform.machine()
116 if machine == 's390x':
117 tools.append('zipl')
118 elif machine == 'aarch64':
119 tools.append('flash-kernel')
120 return tools
121
122
123def disable_update_initramfs(cfg, target, machine=None):
124 """ Find update-initramfs tools in target and change their name. """
125 with util.ChrootableTarget(target) as in_chroot:
126 for tool in _update_initramfs_tools(machine=machine):
127 found = util.which(tool, target=target)
128 if found:
129 LOG.debug('Diverting original %s in target.', tool)
130 rename = found + '.curtin-disabled'
131 divert = ['dpkg-divert', '--add', '--rename',
132 '--divert', rename, found]
133 in_chroot.subp(divert)
134
135 # create a dummy update-initramfs which just returns true;
136 # this handles postinstall scripts which make invoke $tool
137 # directly
138 util.write_file(target + found,
139 content="#!/bin/true\n# diverted by curtin",
140 mode=0o755)
124141
125142
126def update_initramfs_is_disabled(target):143def update_initramfs_is_disabled(target):
127 # check if update-initramfs has been diverted144 """ Return a bool indicating if initramfs tooling is disabled. """
128 disabled = []145 disabled = []
129 with util.ChrootableTarget(target) as in_chroot:146 with util.ChrootableTarget(target) as in_chroot:
130 out, _err = in_chroot.subp(['dpkg-divert', '--list'], capture=True)147 out, _err = in_chroot.subp(['dpkg-divert', '--list'], capture=True)
131 disabled = [divert for divert in out.splitlines()148 disabled = [divert for divert in out.splitlines()
132 if divert.endswith('update-initramfs.curtin-disabled')]149 if divert.endswith('.curtin-disabled')]
133 return len(disabled) > 0150 return len(disabled) > 0
134151
135152
136def enable_update_initramfs(cfg, target):153def enable_update_initramfs(cfg, target, machine=None):
137 """ Find 'update-initramfs.curtin-disabled ' in target and if found154 """ Enable initramfs update tools by restoring their original name. """
138 restore to original name. """
139 if update_initramfs_is_disabled(target):155 if update_initramfs_is_disabled(target):
140 with util.ChrootableTarget(target) as in_chroot:156 with util.ChrootableTarget(target) as in_chroot:
141 LOG.info(157 for tool in _update_initramfs_tools(machine=machine):
142 'Restoring update-initramfs in target for initrd updates.')158 LOG.info('Restoring %s in target for initrd updates.', tool)
143 update_initramfs = util.which('update-initramfs', target=target)159 found = util.which(tool, target=target)
144 # remove the diverted160 if not found:
145 util.del_file(target + update_initramfs)161 continue
146 # un-divert and restore original file162 # remove the diverted
147 in_chroot.subp(163 util.del_file(target + found)
148 ['dpkg-divert', '--rename', '--remove', update_initramfs])164 # un-divert and restore original file
165 in_chroot.subp(
166 ['dpkg-divert', '--rename', '--remove', found])
149167
150168
151def setup_zipl(cfg, target):169def setup_zipl(cfg, target):
@@ -1373,6 +1391,7 @@ def builtin_curthooks(cfg, target, state):
1373 LOG.info('Running curtin builtin curthooks')1391 LOG.info('Running curtin builtin curthooks')
1374 stack_prefix = state.get('report_stack_prefix', '')1392 stack_prefix = state.get('report_stack_prefix', '')
1375 state_etcd = os.path.split(state['fstab'])[0]1393 state_etcd = os.path.split(state['fstab'])[0]
1394 machine = platform.machine()
13761395
1377 distro_info = distro.get_distroinfo(target=target)1396 distro_info = distro.get_distroinfo(target=target)
1378 if not distro_info:1397 if not distro_info:
@@ -1387,7 +1406,7 @@ def builtin_curthooks(cfg, target, state):
1387 description="configuring apt configuring apt"):1406 description="configuring apt configuring apt"):
1388 do_apt_config(cfg, target)1407 do_apt_config(cfg, target)
1389 disable_overlayroot(cfg, target)1408 disable_overlayroot(cfg, target)
1390 disable_update_initramfs(cfg, target)1409 disable_update_initramfs(cfg, target, machine)
13911410
1392 # LP: #1742560 prevent zfs-dkms from being installed (Xenial)1411 # LP: #1742560 prevent zfs-dkms from being installed (Xenial)
1393 if distro.lsb_release(target=target)['codename'] == 'xenial':1412 if distro.lsb_release(target=target)['codename'] == 'xenial':
@@ -1521,7 +1540,7 @@ def builtin_curthooks(cfg, target, state):
1521 description="updating initramfs configuration"):1540 description="updating initramfs configuration"):
1522 if osfamily == DISTROS.debian:1541 if osfamily == DISTROS.debian:
1523 # re-enable update_initramfs1542 # re-enable update_initramfs
1524 enable_update_initramfs(cfg, target)1543 enable_update_initramfs(cfg, target, machine)
1525 update_initramfs(target, all_kernels=True)1544 update_initramfs(target, all_kernels=True)
1526 elif osfamily == DISTROS.redhat:1545 elif osfamily == DISTROS.redhat:
1527 redhat_update_initramfs(target, cfg)1546 redhat_update_initramfs(target, cfg)
@@ -1530,7 +1549,6 @@ def builtin_curthooks(cfg, target, state):
1530 # day, but for now, assume no. They do require the initramfs1549 # day, but for now, assume no. They do require the initramfs
1531 # to be updated, and this also triggers boot loader setup via1550 # to be updated, and this also triggers boot loader setup via
1532 # flash-kernel.1551 # flash-kernel.
1533 machine = platform.machine()
1534 if (machine.startswith('armv7') or1552 if (machine.startswith('armv7') or
1535 machine.startswith('s390x') or1553 machine.startswith('s390x') or
1536 machine.startswith('aarch64') and not util.is_uefi_bootable()):1554 machine.startswith('aarch64') and not util.is_uefi_bootable()):
diff --git a/tests/unittests/test_curthooks.py b/tests/unittests/test_curthooks.py
index a1e13de..3dde2ee 100644
--- a/tests/unittests/test_curthooks.py
+++ b/tests/unittests/test_curthooks.py
@@ -90,8 +90,12 @@ class TestEnableDisableUpdateInitramfs(CiTestCase):
90 ccc = 'curtin.commands.curthooks'90 ccc = 'curtin.commands.curthooks'
91 self.add_patch(ccc + '.util.subp', 'mock_subp')91 self.add_patch(ccc + '.util.subp', 'mock_subp')
92 self.add_patch(ccc + '.util.which', 'mock_which')92 self.add_patch(ccc + '.util.which', 'mock_which')
93 self.add_patch(ccc + '.platform.machine', 'mock_machine')
93 self.target = self.tmp_dir()94 self.target = self.tmp_dir()
95 self.mock_machine.return_value = 'x86_64'
94 self.update_initramfs = '/usr/sbin/update-initramfs'96 self.update_initramfs = '/usr/sbin/update-initramfs'
97 self.flash_kernel = '/usr/sbin/flash-kernel'
98 self.zipl = '/sbin/zipl'
9599
96 def test_disable_does_nothing_if_no_binary(self):100 def test_disable_does_nothing_if_no_binary(self):
97 self.mock_which.return_value = None101 self.mock_which.return_value = None
@@ -154,6 +158,37 @@ class TestEnableDisableUpdateInitramfs(CiTestCase):
154 curthooks.enable_update_initramfs({}, self.target)158 curthooks.enable_update_initramfs({}, self.target)
155 self.assertEqual(0, self.mock_which.call_count)159 self.assertEqual(0, self.mock_which.call_count)
156160
161 def _test_disable_on_machine(self, machine, tools):
162 self.mock_machine.return_value = machine
163 self.mock_which.side_effect = iter(tools)
164 self.mock_subp.side_effect = iter([('', '')] * 10 * len(tools))
165 curthooks.disable_update_initramfs({}, self.target, machine=machine)
166 for tool in tools:
167 tname = os.path.basename(tool)
168 self.assertIn(
169 call(['dpkg-divert', '--add', '--rename', '--divert',
170 tool + '.curtin-disabled', tool], target=self.target),
171 self.mock_subp.call_args_list)
172 lhs = [call(tname, target=self.target)]
173 self.assertIn(lhs, self.mock_which.call_args_list)
174
175 # make sure we have a dummy binary
176 target_tool = self.target + tool
177 self.assertTrue(os.path.exists(target_tool))
178 self.assertTrue(util.is_exe(target_tool))
179 expected_content = "#!/bin/true\n# diverted by curtin"
180 self.assertEqual(expected_content, util.load_file(target_tool))
181
182 def test_disable_on_s390x_masks_zipl(self):
183 machine = 's390x'
184 tools = [self.update_initramfs, self.zipl]
185 self._test_disable_on_machine(machine, tools)
186
187 def test_disable_on_arm_masks_flash_kernel(self):
188 machine = 'aarch64'
189 tools = [self.update_initramfs, self.flash_kernel]
190 self._test_disable_on_machine(machine, tools)
191
157192
158class TestUpdateInitramfs(CiTestCase):193class TestUpdateInitramfs(CiTestCase):
159 def setUp(self):194 def setUp(self):

Subscribers

People subscribed via source and target branches