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