diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e8701e3e7..6b7d7c660 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,7 +56,7 @@ jobs: run: sudo apt update && sudo apt install just - uses: actions/checkout@v4 - name: Build and run container integration tests - run: sudo just run-container-integration + run: sudo just run-container-integration run-container-external-tests cargo-deny: runs-on: ubuntu-latest steps: diff --git a/Justfile b/Justfile index d9952fe4a..756866232 100644 --- a/Justfile +++ b/Justfile @@ -10,5 +10,9 @@ build-integration-test-image *ARGS: build run-container-integration: build-integration-test-image podman run --rm localhost/bootc-integration bootc-integration-tests container +# These tests may spawn their own container images. +run-container-external-tests: + ./tests/container/run localhost/bootc + unittest *ARGS: podman build --jobs=4 --target units -t localhost/bootc-units --build-arg=unitargs={{ARGS}} . diff --git a/crates/lib/src/cli.rs b/crates/lib/src/cli.rs index d815c537b..9a04257f5 100644 --- a/crates/lib/src/cli.rs +++ b/crates/lib/src/cli.rs @@ -472,6 +472,9 @@ pub(crate) enum InternalsOpts { // The stateroot stateroot: String, }, + /// Initiate a reboot the same way we would after --apply; intended + /// primarily for testing. + Reboot, #[cfg(feature = "rhsm")] /// Publish subscription-manager facts to /etc/rhsm/facts/bootc.facts PublishRhsmFacts, @@ -1230,6 +1233,7 @@ async fn run_from_opt(opt: Opt) -> Result<()> { Ok(()) } }, + InternalsOpts::Reboot => crate::reboot::reboot(), InternalsOpts::Fsck => { let sysroot = &get_storage().await?; crate::fsck::fsck(&sysroot, std::io::stdout().lock()).await?; diff --git a/tests/container/reboot/bootc-finish-test-reboot.service b/tests/container/reboot/bootc-finish-test-reboot.service new file mode 100644 index 000000000..392a1f8da --- /dev/null +++ b/tests/container/reboot/bootc-finish-test-reboot.service @@ -0,0 +1,11 @@ +[Unit] +ConditionPathExists=!/etc/initrd-release +After=local-fs.target +RequiresMountsFor=/run/bootc-test-reboot +Before=bootc-test-reboot.service +PartOf=bootc-test-reboot.service + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStop=touch /run/bootc-test-reboot/success diff --git a/tests/container/reboot/bootc-test-reboot.service b/tests/container/reboot/bootc-test-reboot.service new file mode 100644 index 000000000..53c887d4a --- /dev/null +++ b/tests/container/reboot/bootc-test-reboot.service @@ -0,0 +1,12 @@ +[Unit] +ConditionPathExists=!/etc/initrd-release +Requires=bootc-finish-test-reboot.service +After=bootc-finish-test-reboot.service + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=bootc internals reboot + +[Install] +WantedBy=multi-user.target diff --git a/tests/container/reboot/run b/tests/container/reboot/run new file mode 100755 index 000000000..41c524601 --- /dev/null +++ b/tests/container/reboot/run @@ -0,0 +1,22 @@ +#!/bin/bash +# Verify that invoking `bootc internals reboot` actually invokes a reboot, when +# running inside systemd. +# xref: +# - https://github.com/bootc-dev/bootc/issues/1416 +# - https://github.com/bootc-dev/bootc/issues/1419 +set -euo pipefail +image=$1 +tmpd=$(mktemp -d) +log() { + echo "$@" + "$@" +} +log timeout 120 podman run --rm --systemd=always --privileged -v /sys:/sys:ro --label bootc.test=reboot --net=none -v $(pwd):/src:ro -v $tmpd:/run/bootc-test-reboot $image /bin/sh -c 'cp /src/*.service /etc/systemd/system && systemctl enable bootc-test-reboot && exec /sbin/init' || true +ls -al $tmpd +if test '!' -f $tmpd/success; then + echo "reboot failed" 1>&2 + rm -rf "$tmpd" + exit 1 +fi +rm -rf "$tmpd" +echo "ok reboot" diff --git a/tests/container/run b/tests/container/run new file mode 100755 index 000000000..3e5e9266a --- /dev/null +++ b/tests/container/run @@ -0,0 +1,16 @@ +#!/bin/bash +set -euo pipefail +image=$1 +shift + +cd $(dirname $0) + +tests=$(find . -maxdepth 1 -type d) +for case in $tests; do + if test $case = .; then continue; fi + echo "Running: $case" + cd $case + ./run $image + cd - + echo "ok $case" +done