aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-04-21 20:10:01 -0500
committerCraig Jennings <c@cjennings.net>2026-04-21 20:10:01 -0500
commit11af802af31b69e8e478baae3ea6e5b5090bafaf (patch)
treebb300af54a0f062d70b6b6bf821ecd69169b9c3e /tests
parent88b677cbcbbe126d50d5b334206a55559e5a4d29 (diff)
downloadarchangel-11af802af31b69e8e478baae3ea6e5b5090bafaf.tar.gz
archangel-11af802af31b69e8e478baae3ea6e5b5090bafaf.zip
feat: PrivateTmp=yes drop-in for systemd-tmpfiles on ZFS-root
On ZFS-on-root, statx() across sibling services' /var/tmp/systemd-private-*/tmp mounts returns errno 132 (ENOTNAM). This produces 10-30 journal errors per boot and causes systemd-tmpfiles-clean.service to fail every periodic run (exit 73 / CANTCREAT). Running tmpfiles inside its own mount namespace avoids traversing sibling private-tmp paths. install_zfs() now calls configure_tmpfiles_private_tmp() between configure_zfs_tools and sync_efi_partitions, so the genesis snapshot captures the drop-ins. Btrfs path is untouched — errno 132 is ZFS-specific. The drop-in file-writing is factored into install_dropin() in lib/common.sh (service, name, root; body from stdin). Six bats tests exercise path, content, directory permissions, idempotent overwrite, empty content, and special-character preservation. Full root-cause write-up and verification steps in docs/zfs-tmpfiles-private-tmp-fix.md.
Diffstat (limited to 'tests')
-rw-r--r--tests/unit/test_common.bats68
1 files changed, 68 insertions, 0 deletions
diff --git a/tests/unit/test_common.bats b/tests/unit/test_common.bats
index 04f4e09..c81d2e3 100644
--- a/tests/unit/test_common.bats
+++ b/tests/unit/test_common.bats
@@ -178,3 +178,71 @@ setup() {
prompt_password PASS "label" 0 < <(printf '\n\n') >/dev/null
[ -z "$PASS" ]
}
+
+#############################
+# install_dropin
+#############################
+
+setup_dropin_tmp() {
+ DROPIN_ROOT=$(mktemp -d)
+}
+
+teardown_dropin_tmp() {
+ [ -n "${DROPIN_ROOT:-}" ] && rm -rf "$DROPIN_ROOT"
+}
+
+@test "install_dropin writes conf file at expected path" {
+ setup_dropin_tmp
+ install_dropin foo bar "$DROPIN_ROOT" <<< "[Service]"
+ [ -f "$DROPIN_ROOT/etc/systemd/system/foo.service.d/bar.conf" ]
+ teardown_dropin_tmp
+}
+
+@test "install_dropin writes stdin content verbatim" {
+ setup_dropin_tmp
+ install_dropin foo bar "$DROPIN_ROOT" <<< "[Service]
+PrivateTmp=yes"
+ run cat "$DROPIN_ROOT/etc/systemd/system/foo.service.d/bar.conf"
+ [ "$status" -eq 0 ]
+ [[ "$output" == *"[Service]"* ]]
+ [[ "$output" == *"PrivateTmp=yes"* ]]
+ teardown_dropin_tmp
+}
+
+@test "install_dropin creates dropin dir with 755 perms" {
+ setup_dropin_tmp
+ install_dropin foo bar "$DROPIN_ROOT" <<< "x"
+ local perms
+ perms=$(stat -c '%a' "$DROPIN_ROOT/etc/systemd/system/foo.service.d")
+ [ "$perms" = "755" ]
+ teardown_dropin_tmp
+}
+
+@test "install_dropin is idempotent — second call overwrites content" {
+ setup_dropin_tmp
+ install_dropin foo bar "$DROPIN_ROOT" <<< "first"
+ install_dropin foo bar "$DROPIN_ROOT" <<< "second"
+ run cat "$DROPIN_ROOT/etc/systemd/system/foo.service.d/bar.conf"
+ [ "$output" = "second" ]
+ teardown_dropin_tmp
+}
+
+@test "install_dropin accepts empty content" {
+ setup_dropin_tmp
+ install_dropin foo bar "$DROPIN_ROOT" < /dev/null
+ [ -f "$DROPIN_ROOT/etc/systemd/system/foo.service.d/bar.conf" ]
+ [ ! -s "$DROPIN_ROOT/etc/systemd/system/foo.service.d/bar.conf" ]
+ teardown_dropin_tmp
+}
+
+@test "install_dropin preserves special characters in content" {
+ setup_dropin_tmp
+ install_dropin foo bar "$DROPIN_ROOT" <<< '# comment with $var and `backtick`
+[Service]
+Environment="FOO=bar baz"'
+ run cat "$DROPIN_ROOT/etc/systemd/system/foo.service.d/bar.conf"
+ [[ "$output" == *'$var'* ]]
+ [[ "$output" == *'`backtick`'* ]]
+ [[ "$output" == *'"FOO=bar baz"'* ]]
+ teardown_dropin_tmp
+}