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/cmd/atomfs/verify.go b/cmd/atomfs/verify.go index cb55593..957d2df 100644 --- a/cmd/atomfs/verify.go +++ b/cmd/atomfs/verify.go @@ -7,6 +7,7 @@ import ( "github.com/urfave/cli" "machinerun.io/atomfs" + "machinerun.io/atomfs/log" "machinerun.io/atomfs/mount" "machinerun.io/atomfs/squashfs" ) @@ -47,7 +48,8 @@ func doVerify(ctx *cli.Context) error { return err } - mountsdir := filepath.Join(atomfs.RuntimeDir(), "meta", mountNSName, atomfs.ReplacePathSeparators(mountpoint), "mounts") + metadir := filepath.Join(atomfs.RuntimeDir(), "meta", mountNSName, atomfs.ReplacePathSeparators(mountpoint)) + mountsdir := filepath.Join(metadir, "mounts") mounts, err := mount.ParseMounts("/proc/self/mountinfo") if err != nil { @@ -62,16 +64,19 @@ func doVerify(ctx *cli.Context) error { } allOK := true + checkedCount := 0 for _, m := range mounts { - - if m.FSType != "squashfs" { + if !strings.HasPrefix(m.Target, mountsdir) { continue } - - if !strings.HasPrefix(m.Target, mountsdir) { + if m.FSType == "fuse.squashfuse_ll" { + log.Warnf("found squashfuse mount not supported by verify at %q", m.Source) continue } - + if m.FSType != "squashfs" { + continue + } + checkedCount = checkedCount + 1 err = squashfs.ConfirmExistingVerityDeviceCurrentValidity(m.Source) if err != nil { fmt.Printf("%s: CORRUPTION FOUND\n", m.Source) @@ -81,6 +86,12 @@ func doVerify(ctx *cli.Context) error { } } + // TODO - want to also be able to compare to expected # of mounts from + // molecule, need to write more molecule info during mol.mount + if checkedCount == 0 { + return fmt.Errorf("no applicable mounts found in %q", mountsdir) + } + if allOK { return nil } 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..2a6886d --- /dev/null +++ b/test/helpers.bash @@ -0,0 +1,16 @@ + + +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 + stacker --oci-dir $1/oci-no-verity --debug build -f $(dirname $BATS_TEST_FILENAME)/1.stacker.yaml --layer-type squashfs --no-squashfs-verity +} diff --git a/test/mount.bats b/test/mount.bats new file mode 100644 index 0000000..b0c484e --- /dev/null +++ b/test/mount.bats @@ -0,0 +1,201 @@ +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 + assert_dir_exists $ATOMFS_TEST_RUN_DIR/meta/$MY_MNTNSNAME/ + + 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 exist but be empty: + assert_dir_exists $MP + assert [ -z $( ls -A $MP) ] + assert_dir_exists $ATOMFS_TEST_RUN_DIR/meta/$MY_MNTNSNAME/ + assert [ -z $( ls -A $ATOMFS_TEST_RUN_DIR/meta/$MY_MNTNSNAME/ ) ] + +} + +@test "mount with missing verity data fails" { + run atomfs --debug mount ${BATS_SUITE_TMPDIR}/oci-no-verity:test-squashfs $MP + assert_failure + assert_line --partial "is missing verity data" + + # mount point and meta dir should exist but be empty: + assert_dir_exists $MP + assert [ -z $( ls -A $MP) ] + assert_dir_exists $ATOMFS_TEST_RUN_DIR/meta/$MY_MNTNSNAME/ + assert [ -z $( ls -A $ATOMFS_TEST_RUN_DIR/meta/$MY_MNTNSNAME/ ) ] + +} + +@test "mount with missing verity data passes if you ignore it" { + run atomfs --debug mount --allow-missing-verity ${BATS_SUITE_TMPDIR}/oci-no-verity:test-squashfs $MP + assert_success + + run atomfs --debug umount $MP + assert_success + + # mount point and meta dir should exist but be empty: + assert_dir_exists $MP + assert [ -z $( ls -A $MP) ] + assert_dir_exists $ATOMFS_TEST_RUN_DIR/meta/$MY_MNTNSNAME/ + assert [ -z $( ls -A $ATOMFS_TEST_RUN_DIR/meta/$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 + assert_dir_exists $ATOMFS_TEST_RUN_DIR/meta/$MY_MNTNSNAME/ + + 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 exist but be empty: + assert_dir_exists $MP + assert [ -z $( ls -A $MP) ] + assert_dir_exists $ATOMFS_TEST_RUN_DIR/meta/$MY_MNTNSNAME/ + assert [ -z $( ls -A $ATOMFS_TEST_RUN_DIR/meta/$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 exist but be empty: + assert_dir_exists $MP + assert [ -z $( ls -A $MP) ] + assert_dir_exists $ATOMFS_TEST_RUN_DIR/meta/$MY_MNTNSNAME/ + assert [ -z $( ls -A $ATOMFS_TEST_RUN_DIR/meta/$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 +} + +@test "guestmount works ignoring verity" { + + lxc-usernsexec -s < 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 +}