fuel-plugin-ironic/deployment_scripts/fuel-bootstrap-image-builder/bin/fuel-bootstrap-image

449 lines
12 KiB
Bash
Executable File

#!/bin/sh
set -ex
MYSELF="${0##*/}"
bindir="${0%/*}"
datadir="${bindir%/*}/share/fuel-bootstrap-image"
global_conf="/etc/fuel-bootstrap-image.conf"
[ -r "$global_conf" ] && . "$global_conf"
[ -z "$MOS_VERSION" ] && MOS_VERSION="7.0"
[ -z "$DISTRO_RELEASE" ] && DISTRO_RELEASE="trusty"
[ -z "$MIRROR_DISTRO" ] && MIRROR_DISTRO="http://archive.ubuntu.com/ubuntu"
[ -z "$MIRROR_MOS" ] && MIRROR_MOS="http://mirror.fuel-infra.org/mos-repos/$MOS_VERSION/cluster/base/$DISTRO_RELEASE"
[ -z "$KERNEL_FLAVOR" ] && KERNEL_FLAVOR="-generic-lts-trusty"
[ -z "$ARCH" ] && ARCH="amd64"
[ -z "$DESTDIR" ] && DESTDIR="/var/www/nailgun/bootstrap/ubuntu"
[ -z "$BOOTSTRAP_SSH_KEYS" ] && BOOTSTRAP_SSH_KEYS="$datadir/ubuntu/files/root/.ssh/authorized_keys"
BOOTSTRAP_FUEL_PKGS_DFLT="openssh-server ntp"
# Packages required for the master node to discover a bootstrap node
if [ -z "$BOOTSTRAP_IRONIC" ]; then
BOOTSTRAP_FUEL_PKGS_DFLT="$BOOTSTRAP_FUEL_PKGS_DFLT openssh-client mcollective nailgun-agent nailgun-mcagents nailgun-net-check"
GONFIG_SOURCE="$datadir/ubuntu/files/"
else
GONFIG_SOURCE="$datadir/ubuntu/files.ironic/"
fi
[ -z "$BOOTSTRAP_FUEL_PKGS" ] && BOOTSTRAP_FUEL_PKGS="$BOOTSTRAP_FUEL_PKGS_DFLT"
if [ -n "$http_proxy" ]; then
export HTTP_PROXY="$http_proxy"
elif [ -n "$HTTP_PROXY" ]; then
export http_proxy="$HTTP_PROXY"
fi
# Kernel, firmware, live boot
BOOTSTRAP_PKGS="ubuntu-minimal live-boot live-boot-initramfs-tools linux-image${KERNEL_FLAVOR} linux-firmware linux-firmware-nonfree"
# compress initramfs with xz, make squashfs root filesystem image
BOOTSTRAP_PKGS="$BOOTSTRAP_PKGS xz-utils squashfs-tools"
# Smaller tools providing the standard ones.
# - mdadm depends on mail-transport-agent, default one is postfix => use msmtp instead
BOOTSTRAP_PKGS="$BOOTSTRAP_PKGS msmtp-mta gdebi-core"
apt_setup ()
{
local root="$1"
local sources_list="${root}/etc/apt/sources.list"
local apt_prefs="${root}/etc/apt/preferences"
local mos_codename="mos${MOS_VERSION}-${DISTRO_RELEASE}"
local broken_repo=''
local release_file="$MIRROR_MOS/dists/$mos_codename/Release"
if ! wget -q -O /dev/null "$release_file" 2>/dev/null; then
broken_repo='yes'
fi
mkdir -p "${sources_list%/*}"
cat > "$sources_list" <<-EOF
deb $MIRROR_DISTRO ${DISTRO_RELEASE} main universe multiverse restricted
deb $MIRROR_DISTRO ${DISTRO_RELEASE}-security main universe multiverse restricted
deb $MIRROR_DISTRO ${DISTRO_RELEASE}-updates main universe multiverse restricted
EOF
if [ -z "$broken_repo" ]; then
cat >> "$sources_list" <<-EOF
deb $MIRROR_MOS ${mos_codename} main
deb $MIRROR_MOS ${mos_codename}-security main
deb $MIRROR_MOS ${mos_codename}-updates main
deb $MIRROR_MOS ${mos_codename}-holdback main
EOF
else
# TODO(asheplyakov): remove this after perestroika repo gets fixed
cat >> "$sources_list" <<-EOF
deb $MIRROR_MOS ${DISTRO_RELEASE} main
EOF
fi
if [ -n "$EXTRA_DEB_REPOS" ]; then
l="$EXTRA_DEB_REPOS"
IFS='|'
set -- $l
unset IFS
for repo; do
echo "$repo"
done >> "$sources_list"
fi
cat > "$apt_prefs" <<-EOF
Package: *
Pin: release o=Mirantis, n=mos${MOS_VERSION}
Pin-Priority: 1101
Package: *
Pin: release o=Mirantis, n=${DISTRO_RELEASE}
Pin-Priority: 1101
EOF
if [ -n "$HTTP_PROXY" ]; then
cat > "$root/etc/apt/apt.conf.d/01mirantis-use-proxy" <<-EOF
Acquire::http::Proxy "$HTTP_PROXY";
EOF
fi
}
run_apt_get ()
{
local root="$1"
shift
chroot "$root" env \
LC_ALL=C \
DEBIAN_FRONTEND=noninteractive \
DEBCONF_NONINTERACTIVE_SEEN=true \
TMPDIR=/tmp \
TMP=/tmp \
apt-get $@
}
run_apt_key ()
{
local root="$1"
shift
chroot "$root" env \
LC_ALL=C \
DEBIAN_FRONTEND=noninteractive \
DEBCONF_NONINTERACTIVE_SEEN=true \
TMPDIR=/tmp \
TMP=/tmp \
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys $@
}
dpkg_is_too_old ()
{
# XXX: dpkg-deb versions older than 1.15.6 can't handle data.tar.xz
# (which is the default payload of Ubuntu packages)
# Such an ancient version of dpkg is shipped with CentOS 6.[56]
local dpkg_version
local dpkg_major_version
local dpkg_minor_version
local dpkg_patch_version
if ! dpkg-deb --help >/dev/null 2>&1; then
return 0
fi
dpkg_version=`dpkg-deb --version | sed -rne '1 s/^.*\s+version\s+([0-9]+)\.([0-9]+)\.([0-9]+).*/\1.\2.\3/p'`
[ -z "$dpkg_version" ] && return 0
IFS='.'
set -- $dpkg_version
unset IFS
dpkg_major_version="$1"
dpkg_minor_version="$2"
dpkg_patch_version="$3"
if [ $dpkg_major_version -le 1 ] && [ $dpkg_minor_version -le 15 ] && [ $dpkg_patch_version -lt 6 ]; then
echo "DEBUG: $MYSELF: dpkg is too old, using ar to unpack debian packages" >&2
return 0
fi
return 1
}
run_debootstrap ()
{
local root="$1"
[ -z "$root" ] && exit 1
local insecure="--no-check-gpg"
local extractor=''
if dpkg_is_too_old; then
# Ubuntu packages use data.tar.xz payload. Ancient versions of
# dpkg (in particular the ones shipped with CentOS 6.x) can't
# handle such packages. Tell debootstrap to use ar instead to
# avoid the failure.
extractor='--extractor=ar'
fi
env \
LC_ALL=C \
DEBIAN_FRONTEND=noninteractive \
DEBCONF_NONINTERACTIVE_SEEN=true \
debootstrap $insecure $extractor --arch=${ARCH} ${DISTRO_RELEASE} "$root" $MIRROR_DISTRO
}
install_packages ()
{
local root="$1"
shift
echo "INFO: $MYSELF: installing pkgs: $*" >&2
run_apt_get "$root" install --yes $@
}
upgrade_chroot ()
{
local root="$1"
run_apt_key "$root" CA2B20483E301371
run_apt_get "$root" update
if ! mountpoint -q "$root/proc"; then
mount -t proc bootstrapproc "$root/proc"
fi
run_apt_get "$root" dist-upgrade --yes
}
add_local_mos_repo ()
{
# we need the local APT repo (/var/www/nailgun/ubuntu/x86_64)
# before web server is up and running => use bind mount
local root="$1"
# TODO(asheplyakov): use proper arch name (amd64)
local local_repo="/var/www/nailgun/ubuntu/x86_64"
local path_in_chroot="/tmp/local-apt"
local source_parts_d="${root}/etc/apt/sources.list.d"
# TODO(asheplyakov): update the codename after repo get fixed
local mos_codename="mos${MOS_VERSION}"
mkdir -p "${root}${path_in_chroot}" "${source_parts_d}"
mount -o bind "$local_repo" "${root}${path_in_chroot}"
mount -o remount,ro,bind "${root}${path_in_chroot}"
cat > "${source_parts_d}/nailgun-local.list" <<-EOF
deb file://${path_in_chroot} ${mos_codename} main
EOF
}
allow_insecure_apt ()
{
local root="$1"
local conflet="${root}/etc/apt/apt.conf.d/02mirantis-insecure-apt"
mkdir -p "${conflet%/*}"
echo 'APT::Get::AllowUnauthenticated 1;' > "$conflet"
}
suppress_services_start ()
{
local root="$1"
local policy_rc="$root/usr/sbin/policy-rc.d"
mkdir -p "${policy_rc%/*}"
cat > "$policy_rc" <<-EOF
#!/bin/sh
# suppress services start in the staging chroot
exit 101
EOF
chmod 755 "$policy_rc"
}
propagate_host_resolv_conf ()
{
local root="$1"
mkdir -p "$root/etc"
for conf in "/etc/resolv.conf" "/etc/hosts"; do
if [ -e "${root}${conf}" ]; then
cp -a "${root}${conf}" "${root}${conf}.bak"
fi
done
}
restore_resolv_conf ()
{
local root="$1"
for conf in "/etc/resolv.conf" "/etc/hosts"; do
if [ -e "${root}${conf}.bak" ]; then
rm -f "${root}${conf}"
cp -a "${root}${conf}.bak" "${root}${conf}"
fi
done
}
make_utf8_locale ()
{
local root="$1"
chroot "$root" /bin/sh -c "locale-gen en_US.UTF-8 && dpkg-reconfigure locales"
}
copy_conf_files ()
{
local root="$1"
local sdir="$2"
rsync -rlptDK "${sdir}" "${root%/}"
sed -i $root/etc/shadow -e '/^root/c\root:$$6$$oC7haQNQ$$LtVf6AI.QKn9Jb89r83PtQN9fBqpHT9bAFLzy.YVxTLiFgsoqlPY3awKvbuSgtxYHx4RUcpUqMotp.WZ0Hwoj.:15441:0:99999:7:::'
}
install_ssh_keys ()
{
local root="$1"
shift
if [ -z "$*" ]; then
echo "*** Error: $MYSELF: no ssh keys specified" >&2
exit 1
fi
local authorized_keys="$root/root/.ssh/authorized_keys"
local dot_ssh_dir="${authorized_keys%/*}"
if [ ! -d "${dot_ssh_dir}" ]; then
mkdir -p -m700 "${dot_ssh_dir}"
fi
for key; do
if [ ! -r "$key" ]; then
echo "*** Error: $MYSELF: no such file: $key" >&2
exit 1
fi
done
cat $@ > "$authorized_keys"
chmod 640 "$authorized_keys"
}
cleanup_chroot ()
{
local root="$1"
[ -z "$root" ] && exit 1
signal_chrooted_processes "$root" SIGTERM
signal_chrooted_processes "$root" SIGKILL
# umount "${root}/tmp/local-apt" 2>/dev/null || umount -l "${root}/tmp/local-apt"
# rm -f "${root}/etc/apt/sources.list.d/nailgun-local.list"
rm -rf $root/var/cache/apt/archives/*.deb
rm -f $root/etc/apt/apt.conf.d/01mirantis-use-proxy.conf
rm -f $root/var/log/bootstrap.log
rm -rf $root/tmp/*
rm -rf $root/run/*
}
install_agent ()
{
local root="$1"
local package_path="$2"
local full_path=`ls $package_path/fuel-agent*.deb`
local package=`basename $full_path`
cp $full_path $root/tmp
chroot "$root" env \
LC_ALL=C \
DEBIAN_FRONTEND=noninteractive \
DEBCONF_NONINTERACTIVE_SEEN=true \
TMPDIR=/tmp \
TMP=/tmp \
gdebi -n /tmp/$package
rm -f $root/tmp/$package
}
recompress_initramfs ()
{
local root="$1"
local initramfs_conf="$root/etc/initramfs-tools/initramfs.conf"
sed -i $initramfs_conf -re 's/COMPRESS\s*=\s*gzip/COMPRESS=xz/'
rm -f $root/boot/initrd*
chroot "$root" \
env \
LC_ALL=C \
DEBIAN_FRONTEND=noninteractive \
DEBCONF_NONINTERACTIVE_SEEN=true \
TMPDIR=/tmp \
TMP=/tmp \
update-initramfs -c -k all
}
mk_squashfs_image ()
{
local root="$1"
local tmp="$$"
[ -d "$DESTDIR" ] && mkdir -p "$DESTDIR"
cp -a $root/boot/initrd* $DESTDIR/initramfs.img.${tmp}
cp -a $root/boot/vmlinuz* $DESTDIR/linux.${tmp}
rm -f $root/boot/initrd*
rm -f $root/boot/vmlinuz*
# run mksquashfs inside a chroot (Ubuntu kernel will be able to
# mount an image produced by Ubuntu squashfs-tools)
mount -t tmpfs -o rw,nodev,nosuid,noatime,mode=0755,size=4M mnt${tmp} "$root/mnt"
mkdir -p "$root/mnt/src" "$root/mnt/dst"
mount -o bind "$root" "$root/mnt/src"
mount -o remount,bind,ro "$root/mnt/src"
mount -o bind "$DESTDIR" "$root/mnt/dst"
if ! mountpoint -q "$root/proc"; then
mount -t proc sandboxproc "$root/proc"
fi
chroot "$root" mksquashfs /mnt/src /mnt/dst/root.squashfs.${tmp} -comp xz -no-progress -noappend
mv $DESTDIR/initramfs.img.${tmp} $DESTDIR/initramfs.img
mv $DESTDIR/linux.${tmp} $DESTDIR/linux
mv $DESTDIR/root.squashfs.${tmp} $DESTDIR/root.squashfs
umount "$root/mnt/dst"
umount "$root/mnt/src"
umount "$root/mnt"
}
build_image ()
{
local root="$1"
chmod 755 "$root"
suppress_services_start "$root"
run_debootstrap "$root"
suppress_services_start "$root"
propagate_host_resolv_conf "$root"
make_utf8_locale "$root"
apt_setup "$root"
# add_local_mos_repo "$root"
allow_insecure_apt "$root"
upgrade_chroot "$root"
install_packages "$root" $BOOTSTRAP_PKGS $BOOTSTRAP_FUEL_PKGS
install_agent "$root" $AGENT_PACKAGE_PATH
recompress_initramfs "$root"
copy_conf_files "$root" $GONFIG_SOURCE
install_ssh_keys "$root" $BOOTSTRAP_SSH_KEYS
restore_resolv_conf "$root"
cleanup_chroot "$root"
mk_squashfs_image "$root"
}
root=`mktemp -d --tmpdir fuel-bootstrap-image.XXXXXXXXX`
main ()
{
build_image "$root"
}
signal_chrooted_processes ()
{
local root="$1"
local signal="${2:-SIGTERM}"
local max_attempts=10
local timeout=2
local count=0
local found_processes
[ ! -d "$root" ] && return 0
while [ $count -lt $max_attempts ]; do
found_processes=''
for pid in `fuser $root 2>/dev/null`; do
[ "$pid" = "kernel" ] && continue
if [ "`readlink /proc/$pid/root`" = "$root" ]; then
found_processes='yes'
kill "-${signal}" $pid
fi
done
[ -z "$found_processes" ] && break
count=$((count+1))
sleep $timeout
done
}
final_cleanup ()
{
signal_chrooted_processes "$root" SIGTERM
signal_chrooted_processes "$root" SIGKILL
for mnt in /tmp/local-apt /mnt/dst /mnt/src /mnt /proc; do
if mountpoint -q "${root}${mnt}"; then
umount "${root}${mnt}" || umount -l "${root}${mnt}" || true
fi
done
if [ -z "$SAVE_TEMPS" ]; then
rm -rf "$root"
fi
}
trap final_cleanup 0
trap final_cleanup HUP TERM INT QUIT
main