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
1diff --git a/curtin/commands/curthooks.py b/curtin/commands/curthooks.py
2index 4b36da2..b5baa06 100644
3--- a/curtin/commands/curthooks.py
4+++ b/curtin/commands/curthooks.py
5@@ -104,48 +104,66 @@ def disable_overlayroot(cfg, target):
6 shutil.move(local_conf, local_conf + ".old")
7
8
9-def disable_update_initramfs(cfg, target):
10- """ Find 'update-initramfs' in target and if found, change the name. """
11- update_initramfs = util.which('update-initramfs', target=target)
12- if update_initramfs:
13- LOG.debug('Diverting original update-initramfs in target.')
14- rename = update_initramfs + '.curtin-disabled'
15- divert = ['dpkg-divert', '--add', '--rename', '--divert', rename,
16- update_initramfs]
17- with util.ChrootableTarget(target) as in_chroot:
18- in_chroot.subp(divert)
19+def _update_initramfs_tools(machine=None):
20+ """ Return a list of binary names used to update an initramfs.
21
22- # create a dummy update-initramfs which just returns true;
23- # this handles postinstall scripts which make invoke update-initramfs
24- # directly
25- util.write_file(target + update_initramfs,
26- content="#!/bin/true\n# diverted by curtin",
27- mode=0o755)
28+ On some architectures there are helper binaries that are also
29+ used and will be included in the list.
30+ """
31+ tools = ['update-initramfs']
32+ if not machine:
33+ machine = platform.machine()
34+ if machine == 's390x':
35+ tools.append('zipl')
36+ elif machine == 'aarch64':
37+ tools.append('flash-kernel')
38+ return tools
39+
40+
41+def disable_update_initramfs(cfg, target, machine=None):
42+ """ Find update-initramfs tools in target and change their name. """
43+ with util.ChrootableTarget(target) as in_chroot:
44+ for tool in _update_initramfs_tools(machine=machine):
45+ found = util.which(tool, target=target)
46+ if found:
47+ LOG.debug('Diverting original %s in target.', tool)
48+ rename = found + '.curtin-disabled'
49+ divert = ['dpkg-divert', '--add', '--rename',
50+ '--divert', rename, found]
51+ in_chroot.subp(divert)
52+
53+ # create a dummy update-initramfs which just returns true;
54+ # this handles postinstall scripts which make invoke $tool
55+ # directly
56+ util.write_file(target + found,
57+ content="#!/bin/true\n# diverted by curtin",
58+ mode=0o755)
59
60
61 def update_initramfs_is_disabled(target):
62- # check if update-initramfs has been diverted
63+ """ Return a bool indicating if initramfs tooling is disabled. """
64 disabled = []
65 with util.ChrootableTarget(target) as in_chroot:
66 out, _err = in_chroot.subp(['dpkg-divert', '--list'], capture=True)
67 disabled = [divert for divert in out.splitlines()
68- if divert.endswith('update-initramfs.curtin-disabled')]
69+ if divert.endswith('.curtin-disabled')]
70 return len(disabled) > 0
71
72
73-def enable_update_initramfs(cfg, target):
74- """ Find 'update-initramfs.curtin-disabled ' in target and if found
75- restore to original name. """
76+def enable_update_initramfs(cfg, target, machine=None):
77+ """ Enable initramfs update tools by restoring their original name. """
78 if update_initramfs_is_disabled(target):
79 with util.ChrootableTarget(target) as in_chroot:
80- LOG.info(
81- 'Restoring update-initramfs in target for initrd updates.')
82- update_initramfs = util.which('update-initramfs', target=target)
83- # remove the diverted
84- util.del_file(target + update_initramfs)
85- # un-divert and restore original file
86- in_chroot.subp(
87- ['dpkg-divert', '--rename', '--remove', update_initramfs])
88+ for tool in _update_initramfs_tools(machine=machine):
89+ LOG.info('Restoring %s in target for initrd updates.', tool)
90+ found = util.which(tool, target=target)
91+ if not found:
92+ continue
93+ # remove the diverted
94+ util.del_file(target + found)
95+ # un-divert and restore original file
96+ in_chroot.subp(
97+ ['dpkg-divert', '--rename', '--remove', found])
98
99
100 def setup_zipl(cfg, target):
101@@ -1373,6 +1391,7 @@ def builtin_curthooks(cfg, target, state):
102 LOG.info('Running curtin builtin curthooks')
103 stack_prefix = state.get('report_stack_prefix', '')
104 state_etcd = os.path.split(state['fstab'])[0]
105+ machine = platform.machine()
106
107 distro_info = distro.get_distroinfo(target=target)
108 if not distro_info:
109@@ -1387,7 +1406,7 @@ def builtin_curthooks(cfg, target, state):
110 description="configuring apt configuring apt"):
111 do_apt_config(cfg, target)
112 disable_overlayroot(cfg, target)
113- disable_update_initramfs(cfg, target)
114+ disable_update_initramfs(cfg, target, machine)
115
116 # LP: #1742560 prevent zfs-dkms from being installed (Xenial)
117 if distro.lsb_release(target=target)['codename'] == 'xenial':
118@@ -1521,7 +1540,7 @@ def builtin_curthooks(cfg, target, state):
119 description="updating initramfs configuration"):
120 if osfamily == DISTROS.debian:
121 # re-enable update_initramfs
122- enable_update_initramfs(cfg, target)
123+ enable_update_initramfs(cfg, target, machine)
124 update_initramfs(target, all_kernels=True)
125 elif osfamily == DISTROS.redhat:
126 redhat_update_initramfs(target, cfg)
127@@ -1530,7 +1549,6 @@ def builtin_curthooks(cfg, target, state):
128 # day, but for now, assume no. They do require the initramfs
129 # to be updated, and this also triggers boot loader setup via
130 # flash-kernel.
131- machine = platform.machine()
132 if (machine.startswith('armv7') or
133 machine.startswith('s390x') or
134 machine.startswith('aarch64') and not util.is_uefi_bootable()):
135diff --git a/tests/unittests/test_curthooks.py b/tests/unittests/test_curthooks.py
136index a1e13de..3dde2ee 100644
137--- a/tests/unittests/test_curthooks.py
138+++ b/tests/unittests/test_curthooks.py
139@@ -90,8 +90,12 @@ class TestEnableDisableUpdateInitramfs(CiTestCase):
140 ccc = 'curtin.commands.curthooks'
141 self.add_patch(ccc + '.util.subp', 'mock_subp')
142 self.add_patch(ccc + '.util.which', 'mock_which')
143+ self.add_patch(ccc + '.platform.machine', 'mock_machine')
144 self.target = self.tmp_dir()
145+ self.mock_machine.return_value = 'x86_64'
146 self.update_initramfs = '/usr/sbin/update-initramfs'
147+ self.flash_kernel = '/usr/sbin/flash-kernel'
148+ self.zipl = '/sbin/zipl'
149
150 def test_disable_does_nothing_if_no_binary(self):
151 self.mock_which.return_value = None
152@@ -154,6 +158,37 @@ class TestEnableDisableUpdateInitramfs(CiTestCase):
153 curthooks.enable_update_initramfs({}, self.target)
154 self.assertEqual(0, self.mock_which.call_count)
155
156+ def _test_disable_on_machine(self, machine, tools):
157+ self.mock_machine.return_value = machine
158+ self.mock_which.side_effect = iter(tools)
159+ self.mock_subp.side_effect = iter([('', '')] * 10 * len(tools))
160+ curthooks.disable_update_initramfs({}, self.target, machine=machine)
161+ for tool in tools:
162+ tname = os.path.basename(tool)
163+ self.assertIn(
164+ call(['dpkg-divert', '--add', '--rename', '--divert',
165+ tool + '.curtin-disabled', tool], target=self.target),
166+ self.mock_subp.call_args_list)
167+ lhs = [call(tname, target=self.target)]
168+ self.assertIn(lhs, self.mock_which.call_args_list)
169+
170+ # make sure we have a dummy binary
171+ target_tool = self.target + tool
172+ self.assertTrue(os.path.exists(target_tool))
173+ self.assertTrue(util.is_exe(target_tool))
174+ expected_content = "#!/bin/true\n# diverted by curtin"
175+ self.assertEqual(expected_content, util.load_file(target_tool))
176+
177+ def test_disable_on_s390x_masks_zipl(self):
178+ machine = 's390x'
179+ tools = [self.update_initramfs, self.zipl]
180+ self._test_disable_on_machine(machine, tools)
181+
182+ def test_disable_on_arm_masks_flash_kernel(self):
183+ machine = 'aarch64'
184+ tools = [self.update_initramfs, self.flash_kernel]
185+ self._test_disable_on_machine(machine, tools)
186+
187
188 class TestUpdateInitramfs(CiTestCase):
189 def setUp(self):

Subscribers

People subscribed via source and target branches