Skip to content

Commit

Permalink
test: add bats tests for mount
Browse files Browse the repository at this point in the history
Add a bats tests suite for mounting and for failing to mount when the
images are bad.

Uses the ATOMFS_TEST_RUN_DIR env var to avoid polluting your host's
/run/atomfs/meta dir.

copies the guestmount test from the github yaml into bats and expands it
a bit

I apologize for the bash quoting situation, forgive me

Missing cases:
- 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 <[email protected]>
  • Loading branch information
mikemccracken committed Oct 19, 2024
1 parent 050bafb commit 8d6f748
Show file tree
Hide file tree
Showing 7 changed files with 335 additions and 7 deletions.
50 changes: 49 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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-*
23 changes: 17 additions & 6 deletions cmd/atomfs/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/urfave/cli"
"machinerun.io/atomfs"
"machinerun.io/atomfs/log"
"machinerun.io/atomfs/mount"
"machinerun.io/atomfs/squashfs"
)
Expand Down Expand Up @@ -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 {
Expand All @@ -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)
Expand All @@ -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
}
Expand Down
2 changes: 2 additions & 0 deletions test/1.README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Just a file to import into a scratch stacker image

14 changes: 14 additions & 0 deletions test/1.stacker.yaml
Original file line number Diff line number Diff line change
@@ -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: /
16 changes: 16 additions & 0 deletions test/helpers.bash
Original file line number Diff line number Diff line change
@@ -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
}
201 changes: 201 additions & 0 deletions test/mount.bats
Original file line number Diff line number Diff line change
@@ -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 <<EOF
set -x
export ATOMFS_TEST_RUN_DIR=$ATOMFS_TEST_RUN_DIR
export PERSIST_DIR=${BATS_TEST_TMPDIR}/upperdir
mkdir -p \$PERSIST_DIR
export INNER_MNTNSNAME=\$(readlink /proc/self/ns/mnt | cut -c 6-15)
set +e
atomfs --debug mount --persist=\$PERSIST_DIR ${BATS_SUITE_TMPDIR}/oci:test-squashfs $MP
[ \$? -eq 0 ] && {
echo guestmount without allow-missing should fail, because we do not have verity
exit 1
}
set -e
atomfs --debug mount --allow-missing-verity --persist=\$PERSIST_DIR ${BATS_SUITE_TMPDIR}/oci:test-squashfs $MP
[ -f $MP/1.README.md ]
[ -f $MP/random.txt ]
touch $MP/let-me-write
set +e
atomfs --debug verify $MP
[ \$? -eq 0 ] && {
echo mount with squashfuse ignores verity, so verify should have failed, output should include warning
exit 1
}
set -e
find $ATOMFS_TEST_RUN_DIR/meta/\$INNER_MNTNSNAME/ -name config.json|xargs cat
find $ATOMFS_TEST_RUN_DIR/meta/\$INNER_MNTNSNAME/
atomfs --debug umount $MP
[ -f \$PERSIST_DIR/let-me-write ]
# mount point and meta dir should be empty:
[ -d $MP ]
[ -z \$( ls -A $MP) ]
[ -d $ATOMFS_TEST_RUN_DIR/meta/\$INNER_MNTNSNAME/ ]
[ -z \$( ls -A $ATOMFS_TEST_RUN_DIR/meta/\$INNER_MNTNSNAME/) ]
EOF
}


@test "guestmount works on images without verity" {

lxc-usernsexec -s <<EOF
set -x
export ATOMFS_TEST_RUN_DIR=$ATOMFS_TEST_RUN_DIR
export PERSIST_DIR=${BATS_TEST_TMPDIR}/upperdir
mkdir -p \$PERSIST_DIR
export INNER_MNTNSNAME=\$(readlink /proc/self/ns/mnt | cut -c 6-15)
atomfs --debug mount --allow-missing-verity --persist=\$PERSIST_DIR ${BATS_SUITE_TMPDIR}/oci-no-verity:test-squashfs $MP
[ -f $MP/1.README.md ]
[ -f $MP/random.txt ]
touch $MP/let-me-write
set +e
atomfs --debug verify $MP
[ \$? -eq 0 ] && {
echo mount with squashfuse ignores verity, so verify should have failed, output should include warning
exit 1
}
set -e
atomfs --debug umount $MP
[ -f \$PERSIST_DIR/let-me-write ]
[ -d $MP ]
[ -z \$( ls -A $MP) ]
[ -d $ATOMFS_TEST_RUN_DIR/meta/\$INNER_MNTNSNAME/ ]
[ -z \$( ls -A $ATOMFS_TEST_RUN_DIR/meta/\$INNER_MNTNSNAME/) ]
EOF
}
36 changes: 36 additions & 0 deletions test/verify.bats
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 8d6f748

Please sign in to comment.