#!/bin/bash

# Build an Alpine Linux image roughly following the chroot(2) instructions:
# https://wiki.alpinelinux.org/wiki/Installing_Alpine_Linux_in_a_chroot
#
# We deliberately do not sudo. It's a little rough around the edges, because
# apk expects root, but it better follows the principle of least privilege. We
# could tidy by using the fakeroot utility, but AFAICT that's not particularly
# common and we'd prefer not to introduce another dependency. For example,
# it's a standard tool on Debian but only in EPEL for CentOS.

set -ex

SRCDIR=$1
TARBALL=$2
WORKDIR=$3
MIRROR=http://dl-cdn.alpinelinux.org/alpine/v3.6
APK_TOOLS=apk-tools-static-2.7.4-r0.apk
IMG="$WORKDIR/img"

cd $WORKDIR

# "apk add" wants to install a bunch of files root:root. Thus, if we don't map
# ourselves to root:root, we get thousands of errors about "Failed to set
# ownership". Mapping is not available in setuid mode, so adjust the ch-run
# arguments accordingly, and make some manual corrections below.
#
# For most Build scripts, we'd simply error out with missing prerequisites,
# but this is a core image that much of the test suite depends on.
if ( ch-run --is-setuid ); then
    CH_RUN="ch-run -w --no-home $IMG"
else
    CH_RUN="ch-run -u0 -g0 -w --no-home $IMG"
fi

## Bootstrap base Alpine Linux.

# Download statically linked apk.
wget $MIRROR/main/x86_64/$APK_TOOLS

# Bootstrap directories.
mkdir img
mkdir img/{dev,etc,proc,sys,tmp}
touch img/etc/{group,hosts,passwd,resolv.conf}

# Bootstrap static apk.
(cd img && tar xf ../$APK_TOOLS)
mkdir img/etc/apk
echo $MIRROR/main > img/etc/apk/repositories

# Install the base system and a dynamically linked apk.
#
# This will give a few errors about chown failures. However, the install does
# seem to work, so we ignore the failed exit code.
$CH_RUN -- /sbin/apk.static \
           --allow-untrusted --initdb --update-cache \
           add alpine-base apk-tools \
  || true

# apk doesn't install the busybox symlinks in setuid mode.
if ( ch-run --is-setuid ); then
    rm img/bin/sh
    for i in $($CH_RUN -- /bin/busybox --list); do
        ln -s /bin/busybox img/bin/$i
    done
    for i in env; do
        ln -s /bin/busybox img/usr/bin/$i
    done
fi

# Now that we've bootstrapped, we don't need apk.static any more. It wasn't
# installed using apk, so it's not in the database and can just be rm'ed.
rm img/sbin/apk.static.*

# Install packages we need for our tests.
$CH_RUN -- /sbin/apk add gcc make musl-dev python3 || true

# Validate the install.
$CH_RUN -- /sbin/apk audit --system
$CH_RUN -- /sbin/apk stats

# Fix permissions.
#
# Note that this removes setuid/setgid bits from a few files (and
# directories). There is not a race condition, i.e., a window where setuid
# executables could become the invoking users, which would be a security hole,
# because the setuid/setgid binaries are not group- or world-readable until
# after this chmod.
chmod -R u+rw,ug-s img


## Install our test stuff.

# Sentinel file for --no-home --bind test
echo "tmpfs and host home are not overmounted" \
  > img/home/overmount-me

# We want ch-ssh
touch img/usr/bin/ch-ssh

# Install test programs.
cp -r $SRCDIR img/test
$CH_RUN -- sh -c 'cd /test && make'


## Tar it up.

# Using pigz saves about 8 seconds. Normally we wouldn't care about that, but
# this script is park of "make test-quick", which we'd like developers to use
# frequently, so every second matters.
if ( command -v pigz >/dev/null 2>&1 ); then
    GZIP_CMD=pigz
else
    GZIP_CMD=gzip
fi

cd img
tar c . | $GZIP_CMD > $TARBALL
