From 14bf28eefff77e5f5e121923ed7664b13e8fe79d Mon Sep 17 00:00:00 2001 From: Michael McCracken Date: Mon, 14 Oct 2024 17:43:22 -0700 Subject: [PATCH] test: add bats tests for mount Add a bats tests suite for mounting and for failing to mount when the images are bad. Currently assumes you can run things with sudo. Uses the ATOMFS_TEST_RUN_DIR env var to avoid polluting your host's /run/atomfs/meta dir. Missing cases: - running as unpriv - testing `atomfs verify` on bad images: requires manufacturing a verity image that will mount OK but has a bad block that won't get read until later. I have tested verify with mounted bad images that I mounted with a purposely broken atomfs, but there should be a better way for CI. Signed-off-by: Michael McCracken tests Signed-off-by: Michael McCracken --- Makefile | 50 ++++++++++++++++++++++++++- test/1.README.md | 2 ++ test/1.stacker.yaml | 14 ++++++++ test/helpers.bash | 15 ++++++++ test/mount.bats | 84 +++++++++++++++++++++++++++++++++++++++++++++ test/verify.bats | 36 +++++++++++++++++++ 6 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 test/1.README.md create mode 100644 test/1.stacker.yaml create mode 100644 test/helpers.bash create mode 100644 test/mount.bats create mode 100644 test/verify.bats diff --git a/Makefile b/Makefile index d896678..32da70c 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,14 @@ ROOT := $(shell git rev-parse --show-toplevel) GO_SRC_DIRS := $(shell find . -name "*.go" | xargs -n1 dirname | sort -u) GO_SRC := $(shell find . -name "*.go") VERSION_LDFLAGS=-X main.Version=$(MAIN_VERSION) +BATS = $(TOOLS_D)/bin/bats +BATS_VERSION := v1.10.0 +STACKER = $(TOOLS_D)/bin/stacker +STACKER_VERSION := v1.0.0 +TOOLS_D := $(ROOT)/tools + +export PATH := $(TOOLS_D)/bin:$(PATH) + .PHONY: gofmt gofmt: .made-gofmt @@ -23,6 +31,46 @@ atomfs: .made-gofmt $(GO_SRC) gotest: $(GO_SRC) go test -coverprofile=coverage.txt -ldflags "$(VERSION_LDFLAGS)" ./... -clean: +.ONESHELL: +$(STACKER): + set -e; rm -rf stacker-git; + mkdir -p $(TOOLS_D)/bin; + cd $(TOOLS_D)/bin + wget https://github.com/project-stacker/stacker/releases/download/$(STACKER_VERSION)/stacker + chmod +x stacker + +.ONESHELL: +$(BATS): + set -ex + rm -rf bats-core + mkdir -p $(TOOLS_D)/bin + git clone -b $(BATS_VERSION) https://github.com/bats-core/bats-core.git + cd bats-core + ./install.sh $(TOOLS_D) + cd .. + rm -rf bats-core + mkdir -p test/test_helper + git clone --depth 1 https://github.com/bats-core/bats-support test/test_helper/bats-support + git clone --depth 1 https://github.com/bats-core/bats-assert test/test_helper/bats-assert + git clone --depth 1 https://github.com/bats-core/bats-file test/test_helper/bats-file + +.ONESHELL: +batstest: $(BATS) $(STACKER) atomfs test/random.txt + set -ex + cd $(ROOT)/test + sudo $(BATS) --tap --timing *.bats + +.ONESHELL: +test/random.txt: + cd test + dd if=/dev/random of=/dev/stdout count=2048 | base64 > random.txt + +.PHONY: test toolsclean +test: gotest batstest + +toolsclean: + rm -f $(TOOLS_D) + +clean: toolsclean rm -f $(ROOT)/bin rm .made-* diff --git a/test/1.README.md b/test/1.README.md new file mode 100644 index 0000000..32fc2ca --- /dev/null +++ b/test/1.README.md @@ -0,0 +1,2 @@ +# Just a file to import into a scratch stacker image + diff --git a/test/1.stacker.yaml b/test/1.stacker.yaml new file mode 100644 index 0000000..5f03389 --- /dev/null +++ b/test/1.stacker.yaml @@ -0,0 +1,14 @@ +test_base: + from: + type: scratch + imports: + - path: 1.README.md + dest: / + +test: + from: + type: built + tag: test_base + imports: + - path: random.txt + dest: / diff --git a/test/helpers.bash b/test/helpers.bash new file mode 100644 index 0000000..3213a3b --- /dev/null +++ b/test/helpers.bash @@ -0,0 +1,15 @@ + + +if [ "$(id -u)" != "0" ]; then + echo "you should be root to run this suite" + exit 1 +fi + +ROOT_D=$(dirname $BATS_TEST_FILENAME)/.. +TOOLS_D=$ROOT_D/tools +export PATH="$TOOLS_D/bin:$ROOT_D/bin:$PATH" + +build_image_at() { + cd $1 + stacker --oci-dir $1/oci --debug build -f $(dirname $BATS_TEST_FILENAME)/1.stacker.yaml --layer-type squashfs +} diff --git a/test/mount.bats b/test/mount.bats new file mode 100644 index 0000000..85a9095 --- /dev/null +++ b/test/mount.bats @@ -0,0 +1,84 @@ +load helpers +load 'test_helper/bats-support/load' +load 'test_helper/bats-assert/load' +load 'test_helper/bats-file/load' + +function setup_file() { + build_image_at $BATS_SUITE_TMPDIR + export ATOMFS_TEST_RUN_DIR=${BATS_SUITE_TMPDIR}/run/atomfs + mkdir -p $ATOMFS_TEST_RUN_DIR + export MY_MNTNSNAME=$(readlink /proc/self/ns/mnt | cut -c 6-15) +} + +function setup() { + export MP=${BATS_TEST_TMPDIR}/testmountpoint + mkdir -p $MP +} + +@test "RO mount/umount and verify of good image works" { + run atomfs --debug mount ${BATS_SUITE_TMPDIR}/oci:test-squashfs $MP + assert_success + assert_file_exists $MP/1.README.md + assert_file_exists $MP/random.txt + + run touch $MP/do-not-let-me + assert_failure + + run atomfs verify $MP + assert_success + + run atomfs --debug umount $MP + assert_success + + # mount point and meta dir should be empty: + assert [ -z "$( ls -A '$MP')" ] + assert [ -z "$( ls -A '$ATOMFS_TEST_RUN_DIR/$MY_MNTNSNAME/')" ] +} + +@test "mount/umount with writeable overlay" { + run atomfs --debug mount --writeable ${BATS_SUITE_TMPDIR}/oci:test-squashfs $MP + assert_success + assert_file_exists $MP/1.README.md + assert_file_exists $MP/random.txt + + run touch $MP/this-time-let-me + assert_success + + run cp $MP/1.README.md $MP/3.README.md + assert_success + + run atomfs --debug umount $MP + assert_success + + # mount point and meta dir should be empty: + assert [ -z "$( ls -A '$MP')" ] + assert [ -z "$( ls -A '$ATOMFS_TEST_RUN_DIR/$MY_MNTNSNAME/')" ] +} + +@test "mount with writeable overlay in separate dir" { + export PERSIST_DIR=${BATS_TEST_TMPDIR}/upperdir + mkdir -p $PERSIST_DIR + run atomfs --debug mount --persist=${PERSIST_DIR} ${BATS_SUITE_TMPDIR}/oci:test-squashfs $MP + assert_success + assert_file_exists $MP/1.README.md + assert_file_exists $MP/random.txt + + run touch $MP/this-time-let-me + assert_success + run cp $MP/1.README.md $MP/3.README.md + assert_success + + assert_file_exists $PERSIST_DIR/this-time-let-me + assert_file_exists $PERSIST_DIR/3.README.md + assert_file_not_exists $PERSIST_DIR/1.README.md + + run atomfs --debug umount $MP + assert_success + # mount point and meta dir should be empty: + assert [ -z "$( ls -A '$MP')" ] + assert [ -z "$( ls -A '$ATOMFS_TEST_RUN_DIR/$MY_MNTNSNAME/')" ] + + # but persist dir should still be there: + assert_file_exists $PERSIST_DIR/this-time-let-me + assert_file_exists $PERSIST_DIR/3.README.md +} diff --git a/test/verify.bats b/test/verify.bats new file mode 100644 index 0000000..5ab78e5 --- /dev/null +++ b/test/verify.bats @@ -0,0 +1,36 @@ +load helpers +load 'test_helper/bats-support/load' +load 'test_helper/bats-assert/load' +load 'test_helper/bats-file/load' + +function setup_file() { + export ATOMFS_TEST_RUN_DIR=${BATS_SUITE_TMPDIR}/run/atomfs + mkdir -p $ATOMFS_TEST_RUN_DIR +} + +@test "mounting tampered small images fails immediately" { + build_image_at $BATS_TEST_TMPDIR + + sha256sum $BATS_TEST_TMPDIR/oci/blobs/sha256/* > initialsums + + # write some bad data onto the squash blobs to make them invalid + for blob in $BATS_TEST_TMPDIR/oci/blobs/sha256/* ; do + file $blob | grep "Squashfs filesystem" || continue + dd if=/dev/random of=$blob conv=notrunc seek=100 count=100 + done + + sha256sum $BATS_TEST_TMPDIR/oci/blobs/sha256/* > finalsums + + # the sums should be different, so assert that diff finds diffs: + run diff initialsums finalsums + assert_failure + + mkdir -p mountpoint + run atomfs --debug mount ${BATS_TEST_TMPDIR}/oci:test-squashfs mountpoint + assert_failure + +} + +@test "TODO: check atomfs verify on a mounted image that isn't detected immediately" { + echo TODO +}