Merge ~dbungert/curtin:nonzero-fs_pass into curtin:master

Proposed by Dan Bungert
Status: Superseded
Proposed branch: ~dbungert/curtin:nonzero-fs_pass
Merge into: curtin:master
Diff against target: 323 lines (+90/-24)
6 files modified
curtin/block/schemas.py (+5/-0)
curtin/commands/block_meta.py (+13/-3)
doc/topics/storage.rst (+13/-3)
examples/tests/filesystem_battery.yaml (+3/-0)
tests/unittests/test_commands_block_meta.py (+53/-15)
tests/vmtests/test_fs_battery.py (+3/-3)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Needs Fixing
Ryan Harper (community) Needs Fixing
Michael Hudson-Doyle Pending
Review via email: mp+399817@code.launchpad.net

This proposal has been superseded by a proposal from 2021-03-22.

Commit message

Output a 1 or 2 passno for fstab lines if not overridden

When outputing the fstab line, examine the path and use it to determine to
output a passno of 1 or 2. 'none' paths get a 0.

Also document that passno/freq can be set in the yaml.

LP: #1717584, LP: #1785354

Description of the change

Assign a default fstab passno of -1, and if that has not been overridden with a
real value, output 0/1/2 depending on the target path.
The decision was made to not pay attention to bind mounts and fsck them anyhow,
with the belief of use cases where this would not be redundant.
tmpfs and similar end up with a passno of 2, but this seems harmless.

To post a comment you must log in.
Revision history for this message
Dan Bungert (dbungert) wrote :

One of the last changes broke unit tests, fixing.

~dbungert/curtin:nonzero-fs_pass updated
fa4b7bb... by Dan Bungert

Update schema for mount for passno/freq

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

Thanks for submitting the MR.

<rant>
Personally I don't think this is the right choice to make by default. I don't think we install anything but journaled filesystems in which fsck will merely replay the journal just like the kernel already does. Look at fsck.xfs(8) manpage for a laugh.
</rant>

We should at least:

1) Add a curtin feature flag indicating curtin will now write fstabs which will enable fsck by default (curtin/__init__.py)
2) Add a config option to control the default fstab fsck value (block-meta: fstab_passno_default)
3) sync with MAAS/subiquity so they opt-in to fsck by default feature.
4) Consider not enabling the feature by default such that all MAAS/Subiquity users which move up to newer curtin are generating different installs than they did before the update.

I'll note that both MAAS and subiquity *can* fsck by default today if they construct storage config mount entries with the 'spec' field filled out they way they want.

review: Needs Fixing
Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

In parallel with Ryan writing his comment we found that absolutely nothing in modern Ubuntu cares about passno==1 vs passno==2 so we should definitely drop that logic.

Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

We discussed this internally a bit and still think we should change the default. (I haven't heard back from the MAAS team yet). The kernel is not the only thing that reads filesystems (UEFI firmware and grub both do), and for the filesystems that do not implement any kind of non-trivial fsck, we're not at the boot optimization point where adding an invocation of essentially /bin/true will have a noticeable impact. So the (small) upsides seem to outweigh the (even smaller) downsides and I don't think this makes enough difference to anyone to warrant a feature flag.

Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

Also we have changed things so fsck is run in clouds recently after not doing so was causing problems (don't know the details) and we should be consistent about this.

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

> We discussed this internally a bit and still think we should change the

I do wish this sort of discussion would happen either on discourse or
ubuntu-devel/ubuntu-server.

> default. (I haven't heard back from the MAAS team yet). The kernel is not the
> only thing that reads filesystems (UEFI firmware and grub both do), and for

VFAT is not a journaled filesystem, I'm 100% fine with enabling fsck on this.

As for grub the design of journaled filesystems are such that it's always
readable even on crash. Worst case IIUC is a newer files could not show up
until the journal replays.

Additionally, this journal replay (and fsck) happens after grub has run; any
new writes to the filesystem won't have been checked with fsck unless fsck
happens on shutdown (I don't think this is enabled anywhere in Ubuntu) and
still won't help in the case of a crash.

> the filesystems that do not implement any kind of non-trivial fsck, we're not
> at the boot optimization point where adding an invocation of essentially

This isn't a boot optimization; rather it can only slow boot down and for zero
benefit for journaled filesystems.

> /bin/true will have a noticeable impact. So the (small) upsides seem to
> outweigh the (even smaller) downsides and I don't think this makes enough
> difference to anyone to warrant a feature flag.

Changing behavior always warrants feature flags; they're free just like
version bumps. Let's not hide this "feature".

>
> Also we have changed things so fsck is run in clouds recently after not doing
> so was causing problems (don't know the details) and we should be consistent
> about this.

I've been opposed to this as well. For all of the same reasons here and a few
more (cloud instances not being pets, not having interactive consoles to do
repairs, fsck having a non-zero effect on time it takes to mount the rootfs)

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)

Unmerged commits

fa4b7bb... by Dan Bungert

Update schema for mount for passno/freq

4430c43... by Dan Bungert

Update fs_battery test for passno/freq config overrides

Given that I've added docs for passno/freq mount config parameters,
let's also test that doing so actually behaves.

9304e20... by Dan Bungert

Doc update

d51b18a... by Dan Bungert

Update passno in vmtests

35f95b1... by Dan Bungert

Add and adjust tests for passno generation

7ed5782... by Dan Bungert

Allow fstab_line_for_data to choose passno

* fstab data structure FstabData is initialized with passno = "-1"
* other initializers are expected to default to that value
* fstab_line_for_data, when encountering a -1 passno, shall apply simple
  logic to decide on a passno value to generate
* mount to / or /boot things given passno 1
* other mounts that aren't none given passno 2
* 'none' mounts given passno 0
* callers can override passno - supply a passno value that isn't "-1"

Unit tests pass, further testing pending.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/curtin/block/schemas.py b/curtin/block/schemas.py
2index 4dc2f0a..5196012 100644
3--- a/curtin/block/schemas.py
4+++ b/curtin/block/schemas.py
5@@ -262,6 +262,11 @@ MOUNT = {
6 ],
7 },
8 'spec': {'type': 'string'}, # XXX: Tighten this to fstab fs_spec
9+ 'freq': {'type': 'integer',
10+ 'pattern': r'[0-9]'},
11+ 'passno': {'type': ['integer', 'string'],
12+ 'pattern': r'(0|[1-9][0-9]*)',
13+ 'minimum': 0},
14 },
15 }
16 PARTITION = {
17diff --git a/curtin/commands/block_meta.py b/curtin/commands/block_meta.py
18index cf6bc02..9199afb 100644
19--- a/curtin/commands/block_meta.py
20+++ b/curtin/commands/block_meta.py
21@@ -27,7 +27,7 @@ import time
22 FstabData = namedtuple(
23 "FstabData", ('spec', 'path', 'fstype', 'options', 'freq', 'passno',
24 'device'))
25-FstabData.__new__.__defaults__ = (None, None, None, "", "0", "0", None)
26+FstabData.__new__.__defaults__ = (None, None, None, "", "0", "-1", None)
27
28
29 SIMPLE = 'simple'
30@@ -1020,7 +1020,7 @@ def mount_data(info, storage_config):
31 fstype = info.get('fstype')
32 path = info.get('path')
33 freq = str(info.get('freq', 0))
34- passno = str(info.get('passno', 0))
35+ passno = str(info.get('passno', -1))
36
37 # turn empty options into "defaults", which works in fstab and mount -o.
38 if not info.get('options'):
39@@ -1151,8 +1151,18 @@ def fstab_line_for_data(fdata):
40 else:
41 comment = None
42
43+ passno = fdata.passno
44+ if passno == "-1":
45+ # / and /boot things get 1
46+ if path == "/" or path[1:].split('/')[0] in ('', 'boot'):
47+ passno = "1"
48+ elif path != "none":
49+ passno = "2"
50+ else:
51+ passno = "0"
52+
53 entry = ' '.join((spec, path, fdata.fstype, options,
54- fdata.freq, fdata.passno)) + "\n"
55+ fdata.freq, passno)) + "\n"
56 line = '\n'.join([comment, entry] if comment else [entry])
57 return line
58
59diff --git a/doc/topics/storage.rst b/doc/topics/storage.rst
60index 75c5537..58e93f7 100644
61--- a/doc/topics/storage.rst
62+++ b/doc/topics/storage.rst
63@@ -542,6 +542,11 @@ One of ``device`` or ``spec`` must be present.
64 fstab entry will contain ``_netdev`` to indicate networking is
65 required to mount this filesystem.
66
67+**freq**: *<dump(8) integer from 0-9 inclusive>*
68+
69+The ``freq`` key refers to the freq as defined in dump(8).
70+Defaults to ``0`` if unspecified.
71+
72 **fstype**: *<fileystem type>*
73
74 ``fstype`` is only required if ``device`` is not present. It indicates
75@@ -552,7 +557,7 @@ to ``/etc/fstab``
76
77 The ``options`` key will replace the default options value of ``defaults``.
78
79-.. warning::
80+.. warning::
81 The kernel and user-space utilities may differ between the install
82 environment and the runtime environment. Not all kernels and user-space
83 combinations will support all options. Providing options for a mount point
84@@ -565,6 +570,11 @@ The ``options`` key will replace the default options value of ``defaults``.
85 If either of the environments (install or target) do not have support for
86 the provided options, the behavior is undefined.
87
88+**passno**: *<fsck(8) non-negative integer, typically 0-2>*
89+
90+The ``passno`` key refers to the fs_passno as defined in fsck(8).
91+If unspecified, ``curtin`` will choose a value based on the other properties.
92+
93 **spec**: *<fs_spec>*
94
95 The ``spec`` attribute defines the fsspec as defined in fstab(5).
96@@ -596,7 +606,7 @@ Below is an example of configuring a bind mount.
97
98 That would result in a fstab entry like::
99
100- /my/bind-over-var-lib /var/lib none bind 0 0
101+ /my/bind-over-var-lib /var/lib none bind 0 2
102
103 **Tmpfs Mount**
104
105@@ -613,7 +623,7 @@ Below is an example of configuring a tmpfsbind mount.
106
107 That would result in a fstab entry like::
108
109- none /my/tmpfs tmpfs size=4194304 0 0
110+ none /my/tmpfs tmpfs size=4194304 0 2
111
112
113 Lvm Volgroup Command
114diff --git a/examples/tests/filesystem_battery.yaml b/examples/tests/filesystem_battery.yaml
115index 8166360..406a087 100644
116--- a/examples/tests/filesystem_battery.yaml
117+++ b/examples/tests/filesystem_battery.yaml
118@@ -111,15 +111,18 @@ storage:
119 spec: "none"
120 path: "/my/ramfs"
121 fstype: "ramfs"
122+ passno: 0
123 - id: bind1
124 fstype: "none"
125 options: "bind"
126 path: "/var/cache"
127 spec: "/my/bind-over-var-cache"
128 type: mount
129+ freq: 3
130 - id: bind2
131 fstype: "none"
132 options: "bind,ro"
133 path: "/my/bind-ro-etc"
134 spec: "/etc"
135 type: mount
136+ freq: 1
137diff --git a/tests/unittests/test_commands_block_meta.py b/tests/unittests/test_commands_block_meta.py
138index 8cfd6af..da8b2e2 100644
139--- a/tests/unittests/test_commands_block_meta.py
140+++ b/tests/unittests/test_commands_block_meta.py
141@@ -361,7 +361,7 @@ class TestBlockMeta(CiTestCase):
142 block_meta.mount_handler(mount_info, self.storage_config)
143 options = 'defaults'
144 comment = "# / was on /wark/xxx during curtin installation"
145- expected = "%s\n%s %s %s %s 0 0\n" % (comment,
146+ expected = "%s\n%s %s %s %s 0 1\n" % (comment,
147 disk_info['path'],
148 mount_info['path'],
149 fs_info['fstype'], options)
150@@ -389,7 +389,7 @@ class TestBlockMeta(CiTestCase):
151 block_meta.mount_handler(mount_info, self.storage_config)
152 options = 'ro'
153 comment = "# /readonly was on /wark/xxx during curtin installation"
154- expected = "%s\n%s %s %s %s 0 0\n" % (comment,
155+ expected = "%s\n%s %s %s %s 0 2\n" % (comment,
156 disk_info['path'],
157 mount_info['path'],
158 fs_info['fstype'], options)
159@@ -418,7 +418,7 @@ class TestBlockMeta(CiTestCase):
160 block_meta.mount_handler(mount_info, self.storage_config)
161 options = 'defaults'
162 comment = "# /readonly was on /wark/xxx during curtin installation"
163- expected = "%s\n%s %s %s %s 0 0\n" % (comment,
164+ expected = "%s\n%s %s %s %s 0 2\n" % (comment,
165 disk_info['path'],
166 mount_info['path'],
167 fs_info['fstype'], options)
168@@ -449,7 +449,7 @@ class TestBlockMeta(CiTestCase):
169 block_meta.mount_handler(mount_info, self.storage_config)
170 options = 'defaults'
171 comment = "# /readonly was on /wark/xxx during curtin installation"
172- expected = "#curtin-test\n%s\n%s %s %s %s 0 0\n" % (comment,
173+ expected = "#curtin-test\n%s\n%s %s %s %s 0 2\n" % (comment,
174 disk_info['path'],
175 mount_info['path'],
176 fs_info['fstype'],
177@@ -610,7 +610,7 @@ class TestFstabData(CiTestCase):
178 self.assertEqual(
179 block_meta.FstabData(
180 spec="none", fstype="tmpfs", path="/tmpfs",
181- options="defaults", freq="0", passno="0", device=None),
182+ options="defaults", freq="0", device=None),
183 block_meta.mount_data(info, {'xm1': info}))
184
185 @patch('curtin.block.iscsi.volpath_is_iscsi')
186@@ -625,7 +625,7 @@ class TestFstabData(CiTestCase):
187 self.assertEqual(
188 block_meta.FstabData(
189 spec=None, fstype="ext4", path="/",
190- options="noatime", freq="0", passno="0", device="/dev/xda1"),
191+ options="noatime", freq="0", device="/dev/xda1"),
192 block_meta.mount_data(scfg['m1'], scfg))
193
194 @patch('curtin.block.iscsi.volpath_is_iscsi', return_value=False)
195@@ -643,7 +643,7 @@ class TestFstabData(CiTestCase):
196 self.assertEqual(
197 block_meta.FstabData(
198 spec=None, fstype="vfat", path="/boot/efi",
199- options="defaults", freq="0", passno="0", device="/dev/xda1"),
200+ options="defaults", freq="0", device="/dev/xda1"),
201 block_meta.mount_data(scfg['m1'], scfg))
202
203 @patch('curtin.block.iscsi.volpath_is_iscsi')
204@@ -657,7 +657,7 @@ class TestFstabData(CiTestCase):
205 self.assertEqual(
206 block_meta.FstabData(
207 spec=None, fstype="ext4", path="/",
208- options="noatime,_netdev", freq="0", passno="0",
209+ options="noatime,_netdev", freq="0",
210 device="/dev/xda1"),
211 block_meta.mount_data(scfg['m1'], scfg))
212
213@@ -683,7 +683,7 @@ class TestFstabData(CiTestCase):
214 self.assertEqual(
215 block_meta.FstabData(
216 spec=myspec, fstype="ext3", path="/",
217- options="noatime", freq="0", passno="0",
218+ options="noatime", freq="0",
219 device=None),
220 block_meta.mount_data(mnt, scfg))
221
222@@ -761,15 +761,53 @@ class TestFstabData(CiTestCase):
223 spec="/dev/disk2", path="/mnt", fstype='btrfs', options='noatime')
224 lines = block_meta.fstab_line_for_data(fdata).splitlines()
225 self.assertEqual(
226- ["/dev/disk2", "/mnt", "btrfs", "noatime", "0", "0"],
227+ ["/dev/disk2", "/mnt", "btrfs", "noatime", "0", "2"],
228+ lines[1].split())
229+
230+ def test_fstab_line_root_and_no_passno(self):
231+ """fstab_line_for_data passno autoselect for /."""
232+ fdata = block_meta.FstabData(
233+ spec="/dev/disk2", path="/", fstype='btrfs', options='noatime')
234+ lines = block_meta.fstab_line_for_data(fdata).splitlines()
235+ self.assertEqual(
236+ ["/dev/disk2", "/", "btrfs", "noatime", "0", "1"],
237+ lines[1].split())
238+
239+ def test_fstab_line_boot_and_no_passno(self):
240+ """fstab_line_for_data passno autoselect for /boot."""
241+ fdata = block_meta.FstabData(
242+ spec="/dev/disk2", path="/boot", fstype='btrfs', options='noatime')
243+ lines = block_meta.fstab_line_for_data(fdata).splitlines()
244+ self.assertEqual(
245+ ["/dev/disk2", "/boot", "btrfs", "noatime", "0", "1"],
246+ lines[1].split())
247+
248+ def test_fstab_line_boot_efi_and_no_passno(self):
249+ """fstab_line_for_data passno autoselect for /boot/efi."""
250+ fdata = block_meta.FstabData(
251+ spec="/dev/disk2", path="/boot/efi", fstype='btrfs',
252+ options='noatime')
253+ lines = block_meta.fstab_line_for_data(fdata).splitlines()
254+ self.assertEqual(
255+ ["/dev/disk2", "/boot/efi", "btrfs", "noatime", "0", "1"],
256+ lines[1].split())
257+
258+ def test_fstab_line_almost_boot(self):
259+ """fstab_line_for_data passno that pretends to be /boot."""
260+ fdata = block_meta.FstabData(
261+ spec="/dev/disk2", path="/boots", fstype='btrfs',
262+ options='noatime')
263+ lines = block_meta.fstab_line_for_data(fdata).splitlines()
264+ self.assertEqual(
265+ ["/dev/disk2", "/boots", "btrfs", "noatime", "0", "2"],
266 lines[1].split())
267
268 def test_fstab_line_for_data_with_passno_and_freq(self):
269 """fstab_line_for_data should respect passno and freq."""
270 fdata = block_meta.FstabData(
271- spec="/dev/d1", path="/mnt", fstype='ext4', freq="1", passno="2")
272+ spec="/dev/d1", path="/mnt", fstype='ext4', freq="1", passno="1")
273 lines = block_meta.fstab_line_for_data(fdata).splitlines()
274- self.assertEqual(["1", "2"], lines[1].split()[4:6])
275+ self.assertEqual(["1", "1"], lines[1].split()[4:6])
276
277 def test_fstab_line_for_data_raises_error_without_spec_or_device(self):
278 """fstab_line_for_data should raise ValueError if no spec or device."""
279@@ -797,7 +835,7 @@ class TestFstabData(CiTestCase):
280 "# /mnt was on /dev/disk2 during curtin installation",
281 lines[0])
282 self.assertEqual(
283- [by_uuid, "/mnt", "ext4", "defaults", "0", "0"],
284+ [by_uuid, "/mnt", "ext4", "defaults", "0", "2"],
285 lines[1].split())
286 self.assertEqual(1, m_uinfo.call_count)
287 self.assertEqual(1, m_vol_type.call_count)
288@@ -819,7 +857,7 @@ class TestFstabData(CiTestCase):
289 "# /mnt was on /dev/disk2 during curtin installation",
290 lines[0])
291 self.assertEqual(
292- ["/dev/disk2", "/mnt", "ext4", "defaults", "0", "0"],
293+ ["/dev/disk2", "/mnt", "ext4", "defaults", "0", "2"],
294 lines[1].split())
295 self.assertEqual(1, m_uinfo.call_count)
296 self.assertEqual(1, m_vol_type.call_count)
297@@ -837,7 +875,7 @@ class TestFstabData(CiTestCase):
298 '# /mnt was on /dev/xvda1 during curtin installation',
299 lines[0])
300 self.assertEqual(
301- ["/dev/xvda1", "/mnt", "ext4", "defaults", "0", "0"],
302+ ["/dev/xvda1", "/mnt", "ext4", "defaults", "0", "2"],
303 lines[1].split())
304 self.assertEqual(0, m_get_uuid.call_count)
305
306diff --git a/tests/vmtests/test_fs_battery.py b/tests/vmtests/test_fs_battery.py
307index 7d7b494..e5903f1 100644
308--- a/tests/vmtests/test_fs_battery.py
309+++ b/tests/vmtests/test_fs_battery.py
310@@ -159,10 +159,10 @@ class TestFsBattery(VMBaseClass):
311 def test_fstab_has_mounts(self):
312 """Verify each of the expected "my" mounts got into fstab."""
313 expected = [
314- "none /my/tmpfs tmpfs size=4194304 0 0".split(),
315+ "none /my/tmpfs tmpfs size=4194304 0 2".split(),
316 "none /my/ramfs ramfs defaults 0 0".split(),
317- "/my/bind-over-var-cache /var/cache none bind 0 0".split(),
318- "/etc /my/bind-ro-etc none bind,ro 0 0".split(),
319+ "/my/bind-over-var-cache /var/cache none bind 3 2".split(),
320+ "/etc /my/bind-ro-etc none bind,ro 1 2".split(),
321 ]
322 fstab_found = [
323 line.split() for line in self.load_collect_file(

Subscribers

People subscribed via source and target branches