1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
#!/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.
#
# FIXME: Despite the guidance in the Build script API docs, this produces a
# tarball even though the process does not naturally produce one. This is
# because we are also creating some rather bizarre tar edge cases. These
# should be moved to a separate script.
#
# ch-test-scope: quick
set -ex
srcdir=$1
tarball_uncompressed=${2}.tar
tarball=${tarball_uncompressed}.gz
workdir=$3
arch=$(uname -m)
mirror=http://dl-cdn.alpinelinux.org/alpine/v3.17
# Dynamically select apk-tools-static version. We would prefer to hard-code a
# version (and upgrade on our schedule), but we can’t because Alpine does not
# keep old package versions. If we try, the build breaks every few months (for
# example, see issue #242).
apk_tools=$( wget -qO - "${mirror}/main/${arch}" \
| grep -F apk-tools-static \
| sed -E 's/^.*(apk-tools-static-[0-9.r-]+\.apk).*$/\1/')
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”.
#
# 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.
ch_run="ch-run -u0 -g0 -w ${img}"
## Bootstrap base Alpine Linux.
# Download statically linked apk.
wget "${mirror}/main/${arch}/${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
# 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.
# Fixtures for --bind tests
mkdir img/home/directory-in-home
touch img/home/file-in-home
# Test programs.
cp -r "$srcdir" img/test
$ch_run --cd /test -- sh -c 'make clean && make'
# Fixtures for /dev cleaning.
touch img/dev/deleteme
mkdir -p img/mnt/dev
touch img/mnt/dev/dontdeleteme
# Fixture to make sure we raise hidden files in non-tarbombs.
touch img/.hiddenfile1 img/..hiddenfile2 img/...hiddenfile3
# Fixtures for bind-mounting
ln -s ../bind4 img/mnt/bind4
ln -s ./doesnotexist img/mnt/link-b0rken-rel
ln -s /doesnotexist img/mnt/link-b0rken-abs
ln -s /tmp img/mnt/link-bad-abs
ln -s ../.. img/mnt/link-bad-rel
# Fixture to test resolv.conf as symlink (issue #1015).
mv img/etc/resolv.conf img/etc/resolv.conf.real
ln -s /etc/resolv.conf.real img/etc/resolv.conf
# Fixtures to validate permissions are retained on export (issue #1241). See
# FAQ for why this isn’t 7777.
touch img/maxperms_file
chmod 0777 img/maxperms_file
mkdir img/maxperms_dir
chmod 1777 img/maxperms_dir
# Get rid of “/root” directory, used for “HOME” test in “ch-run_misc.bats”.
rmdir "$img"/root
## Tar it up.
# Using pigz saves about 8 seconds. Normally we wouldn’t care about that, but
# this script is part of the quick scope, 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
# Charliecloud supports images both with a single top level directory and
# without (tarbomb). The Docker images in the test suite are all tarbombs
# (because that’s what “docker export” gives us), so use a containing
# directory for this one.
tar cf "$tarball_uncompressed" -- img
# Finalize the tarball.
$gzip_cmd -f "$tarball_uncompressed"
[[ -f $tarball ]]
|