1# 2# SPDX-License-Identifier: MIT 3# 4 5from oeqa.selftest.case import OESelftestTestCase 6from oeqa.utils.commands import bitbake, runqemu 7from oeqa.core.decorator import OETestTag 8 9def getline_qemu(out, line): 10 for l in out.split('\n'): 11 if line in l: 12 return l 13 14def getline(res, line): 15 return getline_qemu(res.output, line) 16 17class OverlayFSTests(OESelftestTestCase): 18 """Overlayfs class usage tests""" 19 20 def add_overlay_conf_to_machine(self): 21 machine_inc = """ 22OVERLAYFS_MOUNT_POINT[mnt-overlay] = "/mnt/overlay" 23""" 24 self.set_machine_config(machine_inc) 25 26 def test_distro_features_missing(self): 27 """ 28 Summary: Check that required DISTRO_FEATURES are set 29 Expected: Fail when either systemd or overlayfs are not in DISTRO_FEATURES 30 Author: Vyacheslav Yurkov <uvv.mail@gmail.com> 31 """ 32 33 config = """ 34IMAGE_INSTALL:append = " overlayfs-user" 35""" 36 overlayfs_recipe_append = """ 37inherit overlayfs 38""" 39 self.write_config(config) 40 self.add_overlay_conf_to_machine() 41 self.write_recipeinc('overlayfs-user', overlayfs_recipe_append) 42 43 res = bitbake('core-image-minimal', ignore_status=True) 44 line = getline(res, "overlayfs-user was skipped: missing required distro features") 45 self.assertTrue("overlayfs" in res.output, msg=res.output) 46 self.assertTrue("systemd" in res.output, msg=res.output) 47 self.assertTrue("ERROR: Required build target 'core-image-minimal' has no buildable providers." in res.output, msg=res.output) 48 49 def test_not_all_units_installed(self): 50 """ 51 Summary: Test QA check that we have required mount units in the image 52 Expected: Fail because mount unit for overlay partition is not installed 53 Author: Vyacheslav Yurkov <uvv.mail@gmail.com> 54 """ 55 56 config = """ 57IMAGE_INSTALL:append = " overlayfs-user" 58DISTRO_FEATURES:append = " systemd overlayfs" 59""" 60 61 self.write_config(config) 62 self.add_overlay_conf_to_machine() 63 64 res = bitbake('core-image-minimal', ignore_status=True) 65 line = getline(res, " Mount path /mnt/overlay not found in fstab and unit mnt-overlay.mount not found in systemd unit directories") 66 self.assertTrue(line and line.startswith("WARNING:"), msg=res.output) 67 line = getline(res, "Not all mount paths and units are installed in the image") 68 self.assertTrue(line and line.startswith("ERROR:"), msg=res.output) 69 70 def test_not_all_units_installed_but_qa_skipped(self): 71 """ 72 Summary: Test skipping the QA check 73 Expected: Image is created successfully 74 Author: Claudius Heine <ch@denx.de> 75 """ 76 77 config = """ 78IMAGE_INSTALL:append = " overlayfs-user" 79DISTRO_FEATURES += "systemd overlayfs" 80OVERLAYFS_QA_SKIP[mnt-overlay] = "mount-configured" 81""" 82 83 self.write_config(config) 84 self.add_overlay_conf_to_machine() 85 86 bitbake('core-image-minimal') 87 88 def test_mount_unit_not_set(self): 89 """ 90 Summary: Test whether mount unit was set properly 91 Expected: Fail because mount unit was not set 92 Author: Vyacheslav Yurkov <uvv.mail@gmail.com> 93 """ 94 95 config = """ 96IMAGE_INSTALL:append = " overlayfs-user" 97DISTRO_FEATURES:append = " systemd overlayfs" 98""" 99 100 self.write_config(config) 101 102 res = bitbake('core-image-minimal', ignore_status=True) 103 line = getline(res, "A recipe uses overlayfs class but there is no OVERLAYFS_MOUNT_POINT set in your MACHINE configuration") 104 self.assertTrue(line and line.startswith("Parsing recipes...ERROR:"), msg=res.output) 105 106 def test_wrong_mount_unit_set(self): 107 """ 108 Summary: Test whether mount unit was set properly 109 Expected: Fail because not the correct flag used for mount unit 110 Author: Vyacheslav Yurkov <uvv.mail@gmail.com> 111 """ 112 113 config = """ 114IMAGE_INSTALL:append = " overlayfs-user" 115DISTRO_FEATURES:append = " systemd overlayfs" 116""" 117 118 wrong_machine_config = """ 119OVERLAYFS_MOUNT_POINT[usr-share-overlay] = "/usr/share/overlay" 120""" 121 122 self.write_config(config) 123 self.set_machine_config(wrong_machine_config) 124 125 res = bitbake('core-image-minimal', ignore_status=True) 126 line = getline(res, "Missing required mount point for OVERLAYFS_MOUNT_POINT[mnt-overlay] in your MACHINE configuration") 127 self.assertTrue(line and line.startswith("Parsing recipes...ERROR:"), msg=res.output) 128 129 def _test_correct_image(self, recipe, data): 130 """ 131 Summary: Check that we can create an image when all parameters are 132 set correctly 133 Expected: Image is created successfully 134 Author: Vyacheslav Yurkov <uvv.mail@gmail.com> 135 """ 136 137 config = """ 138IMAGE_INSTALL:append = " overlayfs-user systemd-machine-units" 139DISTRO_FEATURES:append = " systemd overlayfs" 140 141# Use systemd as init manager 142VIRTUAL-RUNTIME_init_manager = "systemd" 143 144# enable overlayfs in the kernel 145KERNEL_EXTRA_FEATURES:append = " features/overlayfs/overlayfs.scc" 146""" 147 148 overlayfs_recipe_append = """ 149OVERLAYFS_WRITABLE_PATHS[mnt-overlay] += "/usr/share/another-overlay-mount" 150 151SYSTEMD_SERVICE:${PN} += " \ 152 my-application.service \ 153" 154 155do_install:append() { 156 install -d ${D}${systemd_system_unitdir} 157 cat <<EOT > ${D}${systemd_system_unitdir}/my-application.service 158[Unit] 159Description=Sample application start-up unit 160After=overlayfs-user-overlays.service 161Requires=overlayfs-user-overlays.service 162 163[Service] 164Type=oneshot 165ExecStart=/bin/true 166RemainAfterExit=true 167 168[Install] 169WantedBy=multi-user.target 170EOT 171} 172""" 173 174 self.write_config(config) 175 self.add_overlay_conf_to_machine() 176 self.write_recipeinc(recipe, data) 177 self.write_recipeinc('overlayfs-user', overlayfs_recipe_append) 178 179 bitbake('core-image-minimal') 180 181 with runqemu('core-image-minimal') as qemu: 182 # Check that application service started 183 status, output = qemu.run_serial("systemctl status my-application") 184 self.assertTrue("active (exited)" in output, msg=output) 185 186 # Check that overlay mounts are dependencies of our application unit 187 status, output = qemu.run_serial("systemctl list-dependencies my-application") 188 self.assertTrue("overlayfs-user-overlays.service" in output, msg=output) 189 190 status, output = qemu.run_serial("systemctl list-dependencies overlayfs-user-overlays") 191 self.assertTrue("usr-share-another\\x2doverlay\\x2dmount.mount" in output, msg=output) 192 self.assertTrue("usr-share-my\\x2dapplication.mount" in output, msg=output) 193 194 # Check that we have /mnt/overlay fs mounted as tmpfs and 195 # /usr/share/my-application as an overlay (see overlayfs-user recipe) 196 status, output = qemu.run_serial("/bin/mount -t tmpfs,overlay") 197 198 line = getline_qemu(output, "on /mnt/overlay") 199 self.assertTrue(line and line.startswith("tmpfs"), msg=output) 200 201 line = getline_qemu(output, "upperdir=/mnt/overlay/upper/usr/share/my-application") 202 self.assertTrue(line and line.startswith("overlay"), msg=output) 203 204 line = getline_qemu(output, "upperdir=/mnt/overlay/upper/usr/share/another-overlay-mount") 205 self.assertTrue(line and line.startswith("overlay"), msg=output) 206 207 @OETestTag("runqemu") 208 def test_correct_image_fstab(self): 209 """ 210 Summary: Check that we can create an image when all parameters are 211 set correctly via fstab 212 Expected: Image is created successfully 213 Author: Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com> 214 """ 215 216 base_files_append = """ 217do_install:append() { 218 cat <<EOT >> ${D}${sysconfdir}/fstab 219tmpfs /mnt/overlay tmpfs mode=1777,strictatime,nosuid,nodev 0 0 220EOT 221} 222""" 223 224 self._test_correct_image('base-files', base_files_append) 225 226 @OETestTag("runqemu") 227 def test_correct_image_unit(self): 228 """ 229 Summary: Check that we can create an image when all parameters are 230 set correctly via mount unit 231 Expected: Image is created successfully 232 Author: Vyacheslav Yurkov <uvv.mail@gmail.com> 233 """ 234 235 systemd_machine_unit_append = """ 236SYSTEMD_SERVICE:${PN} += " \ 237 mnt-overlay.mount \ 238" 239 240do_install:append() { 241 install -d ${D}${systemd_system_unitdir} 242 cat <<EOT > ${D}${systemd_system_unitdir}/mnt-overlay.mount 243[Unit] 244Description=Tmpfs directory 245DefaultDependencies=no 246 247[Mount] 248What=tmpfs 249Where=/mnt/overlay 250Type=tmpfs 251Options=mode=1777,strictatime,nosuid,nodev 252 253[Install] 254WantedBy=multi-user.target 255EOT 256} 257 258""" 259 260 self._test_correct_image('systemd-machine-units', systemd_machine_unit_append) 261 262@OETestTag("runqemu") 263class OverlayFSEtcRunTimeTests(OESelftestTestCase): 264 """overlayfs-etc class tests""" 265 266 def test_all_required_variables_set(self): 267 """ 268 Summary: Check that required variables are set 269 Expected: Fail when any of required variables is missing 270 Author: Vyacheslav Yurkov <uvv.mail@gmail.com> 271 """ 272 273 configBase = """ 274DISTRO_FEATURES:append = " systemd" 275 276# Use systemd as init manager 277VIRTUAL-RUNTIME_init_manager = "systemd" 278 279# enable overlayfs in the kernel 280KERNEL_EXTRA_FEATURES:append = " features/overlayfs/overlayfs.scc" 281 282# Image configuration for overlayfs-etc 283EXTRA_IMAGE_FEATURES += "overlayfs-etc" 284IMAGE_FEATURES:remove = "package-management" 285""" 286 configMountPoint = """ 287OVERLAYFS_ETC_MOUNT_POINT = "/data" 288""" 289 configDevice = """ 290OVERLAYFS_ETC_DEVICE = "/dev/mmcblk0p1" 291""" 292 293 self.write_config(configBase) 294 res = bitbake('core-image-minimal', ignore_status=True) 295 line = getline(res, "OVERLAYFS_ETC_MOUNT_POINT must be set in your MACHINE configuration") 296 self.assertTrue(line, msg=res.output) 297 298 self.append_config(configMountPoint) 299 res = bitbake('core-image-minimal', ignore_status=True) 300 line = getline(res, "OVERLAYFS_ETC_DEVICE must be set in your MACHINE configuration") 301 self.assertTrue(line, msg=res.output) 302 303 self.append_config(configDevice) 304 res = bitbake('core-image-minimal', ignore_status=True) 305 line = getline(res, "OVERLAYFS_ETC_FSTYPE should contain a valid file system type on /dev/mmcblk0p1") 306 self.assertTrue(line, msg=res.output) 307 308 def test_image_feature_conflict(self): 309 """ 310 Summary: Overlayfs-etc is not allowed to be used with package-management 311 Expected: Feature conflict 312 Author: Vyacheslav Yurkov <uvv.mail@gmail.com> 313 """ 314 315 config = """ 316DISTRO_FEATURES:append = " systemd" 317 318# Use systemd as init manager 319VIRTUAL-RUNTIME_init_manager = "systemd" 320 321# enable overlayfs in the kernel 322KERNEL_EXTRA_FEATURES:append = " features/overlayfs/overlayfs.scc" 323EXTRA_IMAGE_FEATURES += "overlayfs-etc" 324EXTRA_IMAGE_FEATURES += "package-management" 325""" 326 327 self.write_config(config) 328 329 res = bitbake('core-image-minimal', ignore_status=True) 330 line = getline(res, "contains conflicting IMAGE_FEATURES") 331 self.assertTrue("overlayfs-etc" in res.output, msg=res.output) 332 self.assertTrue("package-management" in res.output, msg=res.output) 333 334 def test_image_feature_is_missing_class_included(self): 335 configAppend = """ 336INHERIT += "overlayfs-etc" 337""" 338 self.run_check_image_feature(configAppend) 339 340 def test_image_feature_is_missing(self): 341 self.run_check_image_feature() 342 343 def run_check_image_feature(self, appendToConfig=""): 344 """ 345 Summary: Overlayfs-etc class is not applied when image feature is not set 346 even if we inherit it directly, 347 Expected: Image is created successfully but /etc is not an overlay 348 Author: Vyacheslav Yurkov <uvv.mail@gmail.com> 349 """ 350 351 config = f""" 352DISTRO_FEATURES:append = " systemd" 353 354# Use systemd as init manager 355VIRTUAL-RUNTIME_init_manager = "systemd" 356 357# enable overlayfs in the kernel 358KERNEL_EXTRA_FEATURES:append = " features/overlayfs/overlayfs.scc" 359 360IMAGE_FSTYPES += "wic" 361WKS_FILE = "overlayfs_etc.wks.in" 362 363EXTRA_IMAGE_FEATURES += "read-only-rootfs" 364# Image configuration for overlayfs-etc 365OVERLAYFS_ETC_MOUNT_POINT = "/data" 366OVERLAYFS_ETC_DEVICE = "/dev/sda3" 367{appendToConfig} 368""" 369 370 self.write_config(config) 371 372 bitbake('core-image-minimal') 373 374 with runqemu('core-image-minimal', image_fstype='wic') as qemu: 375 status, output = qemu.run_serial("/bin/mount") 376 377 line = getline_qemu(output, "upperdir=/data/overlay-etc/upper") 378 self.assertFalse(line, msg=output) 379 380 def test_sbin_init_preinit(self): 381 self.run_sbin_init(False) 382 383 def test_sbin_init_original(self): 384 self.run_sbin_init(True) 385 386 def run_sbin_init(self, origInit): 387 """ 388 Summary: Confirm we can replace original init and mount overlay on top of /etc 389 Expected: Image is created successfully and /etc is mounted as an overlay 390 Author: Vyacheslav Yurkov <uvv.mail@gmail.com> 391 """ 392 393 config = """ 394DISTRO_FEATURES:append = " systemd" 395 396# Use systemd as init manager 397VIRTUAL-RUNTIME_init_manager = "systemd" 398 399# enable overlayfs in the kernel 400KERNEL_EXTRA_FEATURES:append = " features/overlayfs/overlayfs.scc" 401 402IMAGE_FSTYPES += "wic" 403OVERLAYFS_INIT_OPTION = "{OVERLAYFS_INIT_OPTION}" 404WKS_FILE = "overlayfs_etc.wks.in" 405 406EXTRA_IMAGE_FEATURES += "read-only-rootfs" 407# Image configuration for overlayfs-etc 408EXTRA_IMAGE_FEATURES += "overlayfs-etc" 409IMAGE_FEATURES:remove = "package-management" 410OVERLAYFS_ETC_MOUNT_POINT = "/data" 411OVERLAYFS_ETC_FSTYPE = "ext4" 412OVERLAYFS_ETC_DEVICE = "/dev/sda3" 413OVERLAYFS_ETC_USE_ORIG_INIT_NAME = "{OVERLAYFS_ETC_USE_ORIG_INIT_NAME}" 414""" 415 416 args = { 417 'OVERLAYFS_INIT_OPTION': "" if origInit else "init=/sbin/preinit", 418 'OVERLAYFS_ETC_USE_ORIG_INIT_NAME': int(origInit == True) 419 } 420 421 self.write_config(config.format(**args)) 422 423 bitbake('core-image-minimal') 424 testFile = "/etc/my-test-data" 425 426 with runqemu('core-image-minimal', image_fstype='wic', discard_writes=False) as qemu: 427 status, output = qemu.run_serial("/bin/mount") 428 429 line = getline_qemu(output, "/dev/sda3") 430 self.assertTrue("/data" in output, msg=output) 431 432 line = getline_qemu(output, "upperdir=/data/overlay-etc/upper") 433 self.assertTrue(line and line.startswith("/data/overlay-etc/upper on /etc type overlay"), msg=output) 434 435 status, output = qemu.run_serial("touch " + testFile) 436 status, output = qemu.run_serial("sync") 437 status, output = qemu.run_serial("ls -1 " + testFile) 438 line = getline_qemu(output, testFile) 439 self.assertTrue(line and line.startswith(testFile), msg=output) 440 441 # Check that file exists in /etc after reboot 442 with runqemu('core-image-minimal', image_fstype='wic') as qemu: 443 status, output = qemu.run_serial("ls -1 " + testFile) 444 line = getline_qemu(output, testFile) 445 self.assertTrue(line and line.startswith(testFile), msg=output) 446