Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 427 lines (368 sloc) 15.5 KB
#!/bin/bash
set -eux
# shellcheck source=tests/lib/dirs.sh
. "$TESTSLIB/dirs.sh"
# shellcheck source=tests/lib/apt.sh
. "$TESTSLIB/apt.sh"
# shellcheck source=tests/lib/snaps.sh
. "$TESTSLIB/snaps.sh"
disable_kernel_rate_limiting() {
# kernel rate limiting hinders debugging security policy so turn it off
echo "Turning off kernel rate-limiting"
# TODO: we should be able to run the tests with rate limiting disabled so
# debug output is robust, but we currently can't :(
echo "SKIPPED: see https://forum.snapcraft.io/t/snapd-spread-tests-should-be-able-to-run-with-kernel-rate-limiting-disabled/424"
#sysctl -w kernel.printk_ratelimit=0
}
update_core_snap_for_classic_reexec() {
# it is possible to disable this to test that snapd (the deb) works
# fine with whatever is in the core snap
if [ "$MODIFY_CORE_SNAP_FOR_REEXEC" != "1" ]; then
echo "Not modifying the core snap as requested via MODIFY_CORE_SNAP_FOR_REEXEC"
return
fi
# We want to use the in-tree snap/snapd/snap-exec/snapctl, because
# we re-exec by default.
# To accomplish that, we'll just unpack the core we just grabbed,
# shove the new snap-exec and snapctl in there, and repack it.
# First of all, unmount the core
core="$(readlink -f "$SNAPMOUNTDIR/core/current" || readlink -f "$SNAPMOUNTDIR/ubuntu-core/current")"
snap="$(mount | grep " $core" | awk '{print $1}')"
umount --verbose "$core"
# Now unpack the core, inject the new snap-exec/snapctl into it
unsquashfs "$snap"
# clean the old snapd libexec binaries, just in case
rm squashfs-root/usr/lib/snapd/*
# and copy in the current ones
cp -a "$LIBEXECDIR"/snapd/* squashfs-root/usr/lib/snapd/
# also the binaries themselves
cp -a /usr/bin/{snap,snapctl} squashfs-root/usr/bin/
# and snap-confine's apparmor
if [ -e /etc/apparmor.d/usr.lib.snapd.snap-confine.real ]; then
cp -a /etc/apparmor.d/usr.lib.snapd.snap-confine.real squashfs-root/etc/apparmor.d/usr.lib.snapd.snap-confine.real
else
cp -a /etc/apparmor.d/usr.lib.snapd.snap-confine squashfs-root/etc/apparmor.d/usr.lib.snapd.snap-confine.real
fi
# repack, cheating to speed things up (4sec vs 1.5min)
mv "$snap" "${snap}.orig"
mksnap_fast "squashfs-root" "$snap"
rm -rf squashfs-root
# Now mount the new core snap, first discarding the old mount namespace
/usr/lib/snapd/snap-discard-ns core
mount "$snap" "$core"
check_file() {
if ! cmp "$1" "$2" ; then
echo "$1 in tree and $2 in core snap are unexpectedly not the same"
exit 1
fi
}
# Make sure we're running with the correct copied bits
for p in "$LIBEXECDIR/snapd/snap-exec" "$LIBEXECDIR/snapd/snap-confine" "$LIBEXECDIR/snapd/snap-discard-ns" "$LIBEXECDIR/snapd/snapd"; do
check_file "$p" "$core/usr/lib/snapd/$(basename "$p")"
done
for p in /usr/bin/snapctl /usr/bin/snap; do
check_file "$p" "$core$p"
done
}
prepare_each_classic() {
mkdir -p /etc/systemd/system/snapd.service.d
if [ -z "${SNAP_REEXEC:-}" ]; then
rm -f /etc/systemd/system/snapd.service.d/reexec.conf
else
cat <<EOF > /etc/systemd/system/snapd.service.d/reexec.conf
[Service]
Environment=SNAP_REEXEC=$SNAP_REEXEC
EOF
fi
if [ ! -f /etc/systemd/system/snapd.service.d/local.conf ]; then
echo "/etc/systemd/system/snapd.service.d/local.conf vanished!"
exit 1
fi
}
prepare_classic() {
install_build_snapd
if snap --version |MATCH unknown; then
echo "Package build incorrect, 'snap --version' mentions 'unknown'"
snap --version
apt-cache policy snapd
exit 1
fi
if "$LIBEXECDIR/snapd/snap-confine" --version | MATCH unknown; then
echo "Package build incorrect, 'snap-confine --version' mentions 'unknown'"
apt-cache policy snap-confine
"$LIBEXECDIR/snapd/snap-confine" --version
exit 1
fi
mkdir -p /etc/systemd/system/snapd.service.d
cat <<EOF > /etc/systemd/system/snapd.service.d/local.conf
[Unit]
StartLimitInterval=0
[Service]
Environment=SNAPD_DEBUG_HTTP=7 SNAPD_DEBUG=1 SNAPPY_TESTING=1 SNAPD_CONFIGURE_HOOK_TIMEOUT=30s
EOF
mkdir -p /etc/systemd/system/snapd.socket.d
cat <<EOF > /etc/systemd/system/snapd.socket.d/local.conf
[Unit]
StartLimitInterval=0
EOF
if [ "$REMOTE_STORE" = staging ]; then
# shellcheck source=tests/lib/store.sh
. "$TESTSLIB/store.sh"
setup_staging_store
fi
# Snapshot the state including core.
if [ ! -f "$SPREAD_PATH/snapd-state.tar.gz" ]; then
! snap list | grep core || exit 1
# use parameterized core channel (defaults to edge) instead
# of a fixed one and close to stable in order to detect defects
# earlier
snap install --"$CORE_CHANNEL" core
snap list | grep core
systemctl stop snapd.{service,socket}
update_core_snap_for_classic_reexec
systemctl start snapd.{service,socket}
# ensure no auto-refresh happens during the tests
if [ -e /snap/core/current/meta/hooks/configure ]; then
snap set core refresh.schedule="$(date +%a --date=2days)@12:00-14:00"
snap set core refresh.disabled=true
fi
echo "Ensure that the grub-editenv list output is empty on classic"
output=$(grub-editenv list)
if [ -n "$output" ]; then
echo "Expected empty grub environment, got:"
echo "$output"
exit 1
fi
systemctl stop snapd.{service,socket}
systemctl daemon-reload
escaped_snap_mount_dir="$(systemd-escape --path "$SNAPMOUNTDIR")"
mounts="$(systemctl list-unit-files --full | grep "^$escaped_snap_mount_dir[-.].*\.mount" | cut -f1 -d ' ')"
services="$(systemctl list-unit-files --full | grep "^$escaped_snap_mount_dir[-.].*\.service" | cut -f1 -d ' ')"
for unit in $services $mounts; do
systemctl stop "$unit"
done
tar czf "$SPREAD_PATH"/snapd-state.tar.gz /var/lib/snapd "$SNAPMOUNTDIR" /etc/systemd/system/"$escaped_snap_mount_dir"-*core*.mount
systemctl daemon-reload # Workaround for http://paste.ubuntu.com/17735820/
core=$(ls /var/lib/snapd/snaps/core_*.snap)
# on 14.04 it is possible that the core snap is still mounted at this point, unmount
# to prevent errors starting the mount unit
if [[ "$SPREAD_SYSTEM" = ubuntu-14.04-* ]] && mount | grep -q "$core"; then
umount "$core" || true
fi
for unit in $mounts $services; do
systemctl start "$unit"
done
systemctl start snapd.socket
fi
if [[ "$SPREAD_SYSTEM" == debian-* || "$SPREAD_SYSTEM" == ubuntu-* ]]; then
if [[ "$SPREAD_SYSTEM" == ubuntu-* ]]; then
quiet apt install -y -q pollinate
pollinate
fi
# Improve entropy for the whole system quite a lot to get fast
# key generation during our test cycles
apt-get install -y -q rng-tools
echo "HRNGDEVICE=/dev/urandom" > /etc/default/rng-tools
/etc/init.d/rng-tools restart
fi
disable_kernel_rate_limiting
}
setup_reflash_magic() {
# install the stuff we need
distro_install_package kpartx busybox-static
distro_install_local_package "$GOHOME"/snapd_*.deb
distro_clean_package_cache
snap install "--${CORE_CHANNEL}" core
# install ubuntu-image
snap install --classic --edge ubuntu-image
# needs to be under /home because ubuntu-device-flash
# uses snap-confine and that will hide parts of the hostfs
IMAGE_HOME=/home/image
mkdir -p "$IMAGE_HOME"
# modify the core snap so that the current root-pw works there
# for spread to do the first login
UNPACKD="/tmp/core-snap"
unsquashfs -d "$UNPACKD" /var/lib/snapd/snaps/core_*.snap
# FIXME: netplan workaround
mkdir -p "$UNPACKD/etc/netplan"
# set root pw by concating root line from host and rest from core
want_pw="$(grep ^root /etc/shadow)"
echo "$want_pw" > /tmp/new-shadow
tail -n +2 /etc/shadow >> /tmp/new-shadow
cp -v /tmp/new-shadow "$UNPACKD/etc/shadow"
cp -v /etc/passwd "$UNPACKD/etc/passwd"
# ensure spread -reuse works in the core image as well
if [ -e /.spread.yaml ]; then
cp -av /.spread.yaml "$UNPACKD"
fi
# we need the test user in the image
# see the comment in spread.yaml about 12345
sed -i 's/^test.*$//' "$UNPACKD"/etc/{shadow,passwd}
chroot "$UNPACKD" addgroup --quiet --gid 12345 test
chroot "$UNPACKD" adduser --quiet --no-create-home --uid 12345 --gid 12345 --disabled-password --gecos '' test
echo 'test ALL=(ALL) NOPASSWD:ALL' >> "$UNPACKD/etc/sudoers.d/99-test-user"
echo 'ubuntu ALL=(ALL) NOPASSWD:ALL' >> "$UNPACKD/etc/sudoers.d/99-ubuntu-user"
# modify sshd so that we can connect as root
sed -i 's/\(PermitRootLogin\|PasswordAuthentication\)\>.*/\1 yes/' "$UNPACKD/etc/ssh/sshd_config"
# FIXME: install would be better but we don't have dpkg on
# the image
# unpack our freshly build snapd into the new core snap
dpkg-deb -x "$SPREAD_PATH"/../snapd_*.deb "$UNPACKD"
# ensure any new timer units are available
cp -a /etc/systemd/system/timers.target.wants/*.timer "$UNPACKD/etc/systemd/system/timers.target.wants"
# add gpio and iio slots
cat >> "$UNPACKD/meta/snap.yaml" <<-EOF
slots:
gpio-pin:
interface: gpio
number: 100
direction: out
iio0:
interface: iio
path: /dev/iio:device0
EOF
# build new core snap for the image
snapbuild "$UNPACKD" "$IMAGE_HOME"
# FIXME: fetch directly once its in the assertion service
cp "$TESTSLIB/assertions/pc-${REMOTE_STORE}.model" "$IMAGE_HOME/pc.model"
# FIXME: how to test store updated of ubuntu-core with sideloaded snap?
IMAGE=all-snap-amd64.img
# ensure that ubuntu-image is using our test-build of snapd with the
# test keys and not the bundled version of usr/bin/snap from the snap.
# Note that we can not put it into /usr/bin as '/usr' is different
# when the snap uses confinement.
cp /usr/bin/snap "$IMAGE_HOME"
export UBUNTU_IMAGE_SNAP_CMD="$IMAGE_HOME/snap"
EXTRA_FUNDAMENTAL=
IMAGE_CHANNEL=edge
if [ "$KERNEL_CHANNEL" = "$GADGET_CHANNEL" ]; then
IMAGE_CHANNEL="$KERNEL_CHANNEL"
else
# download pc-kernel snap for the specified channel and set ubuntu-image channel
# to gadget, so that we don't need to download it
snap download --channel="$KERNEL_CHANNEL" pc-kernel
EXTRA_FUNDAMENTAL="--extra-snaps $PWD/pc-kernel_*.snap"
IMAGE_CHANNEL="$GADGET_CHANNEL"
fi
/snap/bin/ubuntu-image -w "$IMAGE_HOME" "$IMAGE_HOME/pc.model" \
--channel "$IMAGE_CHANNEL" \
"$EXTRA_FUNDAMENTAL" \
--extra-snaps "$IMAGE_HOME"/core_*.snap \
--output "$IMAGE_HOME/$IMAGE"
rm -f ./pc-kernel_*.{snap,assert} ./pc_*.{snap,assert}
# mount fresh image and add all our SPREAD_PROJECT data
kpartx -avs "$IMAGE_HOME/$IMAGE"
# FIXME: hardcoded mapper location, parse from kpartx
mount /dev/mapper/loop2p3 /mnt
mkdir -p /mnt/user-data/
cp -ar /home/gopath /mnt/user-data/
# create test user home dir
mkdir -p /mnt/user-data/test
# using symbolic names requires test:test have the same ids
# inside and outside which is a pain (see 12345 above), but
# using the ids directly is the wrong kind of fragile
chown --verbose test:test /mnt/user-data/test
# we do what sync-dirs is normally doing on boot, but because
# we have subdirs/files in /etc/systemd/system (created below)
# the writeable-path sync-boot won't work
mkdir -p /mnt/system-data/etc/systemd
(cd /tmp ; unsquashfs -v "$IMAGE_HOME"/core_*.snap etc/systemd/system)
cp -avr /tmp/squashfs-root/etc/systemd/system /mnt/system-data/etc/systemd/
# FIXUP silly systemd
mkdir -p /mnt/system-data/etc/systemd/system/snapd.service.d
cat <<EOF > /mnt/system-data/etc/systemd/system/snapd.service.d/local.conf
[Unit]
StartLimitInterval=0
[Service]
Environment=SNAPD_DEBUG_HTTP=7 SNAPD_DEBUG=1 SNAPPY_TESTING=1 SNAPPY_USE_STAGING_STORE=$SNAPPY_USE_STAGING_STORE
ExecStartPre=/bin/touch /dev/iio:device0
EOF
mkdir -p /mnt/system-data/etc/systemd/system/snapd.socket.d
cat <<EOF > /mnt/system-data/etc/systemd/system/snapd.socket.d/local.conf
[Unit]
StartLimitInterval=0
EOF
umount /mnt
kpartx -d "$IMAGE_HOME/$IMAGE"
# the reflash magic
# FIXME: ideally in initrd, but this is good enough for now
cat > "$IMAGE_HOME/reflash.sh" << EOF
#!/bin/sh -ex
mount -t tmpfs none /tmp
cp /bin/busybox /tmp
cp $IMAGE_HOME/$IMAGE /tmp
sync
# blow away everything
/tmp/busybox dd if=/tmp/$IMAGE of=/dev/sda bs=4M
# and reboot
/tmp/busybox sync
/tmp/busybox echo b > /proc/sysrq-trigger
EOF
chmod +x "$IMAGE_HOME/reflash.sh"
# extract ROOT from /proc/cmdline
ROOT=$(sed -e 's/^.*root=//' -e 's/ .*$//' /proc/cmdline)
cat >/boot/grub/grub.cfg <<EOF
set default=0
set timeout=2
menuentry 'flash-all-snaps' {
linux /vmlinuz root=$ROOT ro init=$IMAGE_HOME/reflash.sh console=ttyS0
initrd /initrd.img
}
EOF
}
prepare_all_snap() {
# we are still a "classic" image, prepare the surgery
if [ -e /var/lib/dpkg/status ]; then
setup_reflash_magic
REBOOT
fi
# verify after the first reboot that we are now in the all-snap world
if [ "$SPREAD_REBOOT" = 1 ]; then
echo "Ensure we are now in an all-snap world"
if [ -e /var/lib/dpkg/status ]; then
echo "Rebooting into all-snap system did not work"
exit 1
fi
fi
echo "Wait for firstboot change to be ready"
while ! snap changes | grep "Done"; do
snap changes || true
snap change 1 || true
sleep 1
done
echo "Ensure fundamental snaps are still present"
# shellcheck source=tests/lib/names.sh
. "$TESTSLIB/names.sh"
for name in "$gadget_name" "$kernel_name" core; do
if ! snap list "$name"; then
echo "Not all fundamental snaps are available, all-snap image not valid"
echo "Currently installed snaps"
snap list
exit 1
fi
done
# ensure no auto-refresh happens during the tests
if [ -e /snap/core/current/meta/hooks/configure ]; then
snap set core refresh.schedule="$(date +%a --date=2days)@12:00-14:00"
snap set core refresh.disabled=true
fi
# Snapshot the fresh state (including boot/bootenv)
if [ ! -f "$SPREAD_PATH/snapd-state.tar.gz" ]; then
# we need to ensure that we also restore the boot environment
# fully for tests that break it
BOOT=""
if ls /boot/uboot/*; then
BOOT=/boot/uboot/
elif ls /boot/grub/*; then
BOOT=/boot/grub/
else
echo "Cannot determine bootdir in /boot:"
ls /boot
exit 1
fi
systemctl stop snapd.service snapd.socket
tar czf "$SPREAD_PATH/snapd-state.tar.gz" /var/lib/snapd $BOOT
systemctl start snapd.socket
fi
disable_kernel_rate_limiting
}