#!/bin/bash # Intended to be called from the root.d cloud-image script as follows: # $TMP_HOOKS_PATH/bin/extract-image $BASE_IMAGE_FILE $BASE_IMAGE_TAR $IMAGE_LOCATION $CACHED_IMAGE if [ ${DIB_DEBUG_TRACE:-0} -gt 0 ]; then set -x fi set -eu set -o pipefail BASE_IMAGE_FILE=$1 BASE_IMAGE_TAR=$2 IMAGE_LOCATION=$3 CACHED_IMAGE=$4 CACHED_TAR=$DIB_IMAGE_CACHE/$BASE_IMAGE_TAR DIB_LOCAL_IMAGE=${DIB_LOCAL_IMAGE:-""} TAR_LOCK=$CACHED_TAR.lock function extract_image() { if [ -n "$DIB_OFFLINE" -a -f "$CACHED_TAR" ] ; then echo "Not checking freshness of cached $CACHED_TAR." else if [ -z "$DIB_LOCAL_IMAGE" ]; then echo "Fetching Base Image" # There seems to be some bad Fedora mirrors returning http 404's for the cloud image. # If the image fails to download due to a 404 we retry once. set +e $TMP_HOOKS_PATH/bin/cache-url $IMAGE_LOCATION $CACHED_IMAGE RV=$? set -e if [ "$RV" == "44" ] ; then $TMP_HOOKS_PATH/bin/cache-url $IMAGE_LOCATION $CACHED_IMAGE elif [ "$RV" != "0" ] ; then exit 1 fi fi if [ ! -f $CACHED_TAR -o \ $CACHED_IMAGE -nt $CACHED_TAR ] ; then echo "Repacking base image as tarball." WORKING=$(mktemp --tmpdir=${TMP_DIR:-/tmp} -d) EACTION="rm -r $WORKING" trap "$EACTION" EXIT echo "Working in $WORKING" RAW_FILE=$(mktemp --tmpdir=$WORKING XXXXXX.raw) if [ "${CACHED_IMAGE: -3}" == ".xz" ] ; then QCOW2_FILE=$(mktemp --tmpdir=$WORKING XXXXXX.qcow2) # This leaves the old image in place so cache-url wont get it again unxz --stdout $CACHED_IMAGE > $QCOW2_FILE CACHED_IMAGE=$QCOW2_FILE fi qemu-img convert -f qcow2 -O raw $CACHED_IMAGE $RAW_FILE ROOT_PARTITON=p$(sudo kpartx -l $RAW_FILE | awk "/loop[0-9]+p/"|wc -l) sudo udevadm settle # kpartx fails if no /dev/loop* exists, "losetup -f" prints first unused # loop device and creates it if it doesn't exist sudo losetup -f # XXX: Parsing stdout is dangerous, would like a better way to discover # the device used for the image. ROOT_LOOPDEV=$(sudo kpartx -av $RAW_FILE | \ awk "/loop[0-9]+$ROOT_PARTITON/ {print \$3}") # If running inside Docker, make our nodes manually, because udev will not be working. if [ -f /.dockerenv ]; then sudo dmsetup --noudevsync mknodes fi if ! timeout 5 sh -c "while ! [ -e /dev/mapper/$ROOT_LOOPDEV ]; do sleep 1; done"; then echo "Error: Could not find /dev/mapper/$ROOT_LOOPDEV" exit 1 fi EACTION="sudo kpartx -d $RAW_FILE ; $EACTION" trap "$EACTION" EXIT mkdir $WORKING/mnt if [ "xfs" = "$(sudo blkid -o value -s TYPE /dev/mapper/$ROOT_LOOPDEV)" ]; then # mount xfs with nouuid, just in case that uuid is already mounted MOUNTOPTS="-o nouuid" else MOUNTOPTS="" fi sudo mount $MOUNTOPTS /dev/mapper/$ROOT_LOOPDEV $WORKING/mnt EACTION="sudo umount -f $WORKING/mnt ; $EACTION" trap "$EACTION" EXIT # find out if chroot tar has full xattr support if [ 0 == `sudo chroot $WORKING/mnt bin/tar --help | grep -c xattrs-exclude` ]; then TAROPTS="--no-xattrs" else TAROPTS="--xattrs --xattrs-include='*' --xattrs-exclude='security.selinux'" fi # Chroot in so that we get the correct uid/gid sudo chroot $WORKING/mnt bin/tar $TAROPTS -cz . > $WORKING/tmp.tar mv $WORKING/tmp.tar $CACHED_TAR else echo "Using cached tar from $CACHED_TAR" fi fi # Extract the base image (use --numeric-owner to avoid UID/GID mismatch between # image tarball and host OS e.g. when building Fedora image on an openSUSE host) # Include all xattrs except selinux because the selinux ones cause issues in our # chroot environment, and we restore all of those at the end of the build anyway. echo "Extracting base root image from $CACHED_TAR" sudo tar -C $TARGET_ROOT --numeric-owner --xattrs --xattrs-include='*' --xattrs-exclude='security.selinux' -xzf $CACHED_TAR } ( echo "Getting $TAR_LOCK: $(date)" # Wait up to 20 minutes for another process to download if ! flock -w 1200 9 ; then echo "Did not get $TAR_LOCK: $(date)" exit 1 fi extract_image ) 9> $TAR_LOCK