diskimage-builder/diskimage_builder/elements/yum-minimal/root.d/08-yum-chroot

329 lines
13 KiB
Text
Raw Normal View History

#!/bin/bash
#
# Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# dib-lint: disable=safe_sudo
if [ "${DIB_DEBUG_TRACE:-0}" -gt 0 ]; then
set -x
fi
set -eu
set -o pipefail
source $_LIB/common-functions
if [ -f ${TARGET_ROOT}/.extra_settings ] ; then
. ${TARGET_ROOT}/.extra_settings
fi
ARCH=${ARCH:-x86_64}
if [ $ARCH = amd64 ]; then
ARCH=x86_64
fi
# Calling elements will need to set DISTRO_NAME and DIB_RELEASE
# TODO Maybe deal with DIB_DISTRIBUTION_MIRROR
http_proxy=${http_proxy:-}
YUM=${YUM:-yum}
WORKING=$(mktemp --tmpdir=${TMP_DIR:-/tmp} -d)
EACTION="rm -r $WORKING"
trap "$EACTION" EXIT
YUM_CACHE=$DIB_IMAGE_CACHE/yum
mkdir -p $YUM_CACHE
Fixup RPM db path when building Fedora on Ubuntu On Debian/Ubuntu installs of RPM, /usr/lib/rpm/macros sets %_dbpath %(echo $HOME/.rpmdb) which makes quite a bit of sense, because RPM is not the system packager and thus RPM is setup to install things into a hierarchy in the users homedir. However, this messes things up when building a Fedora chroot on an Ubuntu platform. We use RPM & yum from the base-system to bootstrap the Fedora chroot. While both obey --root flags, they still pick up the %_dbpath macro and so end up creating the RPM database in <chroot>/home/user/.rpmdb After we have bootstrapped yum/dnf, we execute further installation commands from inside the chroot -- where we now have the Fedora version of /usr/lib/rpm/macros and hence have _dbpath set to /var/lib/rpm -- except there is no rpm database there. Should anyone be finding this in the future, the actual issue that appears is $ sudo chroot /opt/dib_tmp/image.b6B5S3f6/mnt dnf makecache Error: Failed to synchronize cache for repo 'fedora' from \ 'https://mirrors.fedoraproject.org/metalink?repo=fedora-$releasever&arch=x86_64': \ Cannot prepare internal mirrorlist: file "repomd.xml" was not found in metalink Note the issue there is that $releasever is not expanded, because the rpmdb where this info is kept is not populated. The trick is to make sure we override this value when using the host rpm/yum to setup the chroot. The bare rpm calls, which we use to install the repos, have a --dbpath argument where we can override this. yum does not however, so we override this in the global ~/.rpmmacros while we are installing the packaging tools and dependencies into the chroot. Copious comments are included, because this is super-confusing. Change-Id: I20801150ea02d1c64f118eb969fb2aec473476f7
2015-10-23 04:42:00 +00:00
# Note, on Debian/Ubuntu, %_dbpath is set in the RPM macros as
# ${HOME}/.rpmdb/ -- this makes sense as RPM isn't the system
# packager. This path is relative to the "--root" argument
_RPM="rpm --dbpath=/var/lib/rpm"
# install the [fedora|centos]-[release|repo] packages inside the
# chroot, which are needed to bootstrap yum/dnf
#
# note this runs outside the chroot, where we're assuming the platform
# has yum/yumdownloader
function _install_repos {
local packages
local rc
# pre-install the base system packages via rpm. We previously
# just left it up to yum to drag these in when we "yum install
# yum" in the chroot in _install_pkg_manager. This raised a small
# problem that inside the empty chroot yum went ahead and did a
# mkdir for /var/run to put some pid file in, which then messed up
# the "filesystem" package making /var/run a symlink to /run
# ... which leads to odd issues with a running system.
#
# TODO: these packages still have some small %posttrans stuff that
# depends on other packages (see rhbz#1306489) ... maybe the idea
# is that they are only installed in one big transaction with the
# rest of the system? but we don't want to use yum to do this
# (see above) so ...
packages="basesystem filesystem setup "
packages+="${DISTRO_NAME}-release "
# Starting in F21 this was split into a separate package
if [[ ${DISTRO_NAME} == 'fedora' ]]; then
packages+="fedora-repos "
fi
# F27 started putting gpg keys into this separate package
if [[ ${DISTRO_NAME} = fedora && ${DIB_RELEASE} -gt 26 ]]; then
packages+="fedora-gpg-keys "
fi
# By default, parent elements (fedora-minimal, centos-minimal)
# have a yum.repos.d directory in the element with a default repo;
# this is copied to TMP_HOOK_PATH by the usual hook-copying
# routines. In the gate, environment.d files for the funtional
# tests will set DIB_YUM_MINIMAL_BOOTSTRAP_REPOS -- this contains
# mirrors correct for the region setup by contrib/setup-gate-mirrors.sh
local repo=${DIB_YUM_MINIMAL_BOOTSTRAP_REPOS:-${TMP_HOOKS_PATH}/yum.repos.d}
# yumdownloader puts repo xml files and such into a directory
# ${TMPDIR}/yum-$USER-random. Since we don't need this once the
# initial download happens, redirect TMPDIR for this call so we
# can clean it up nicely
local temp_tmp
temp_tmp=$(mktemp -d)
TMPDIR=${temp_tmp} yumdownloader --verbose \
--releasever=$DIB_RELEASE \
--setopt=reposdir=$repo \
--destdir=$WORKING \
${packages} && rc=$? || rc=$?
rm -rf ${temp_tmp}
if [[ ${rc} != 0 ]]; then
die "Failed to download initial packages: ${packages}"
fi
# --nodeps works around these wanting /bin/sh in some fedora
# releases, see rhbz#1265873
Fixup RPM db path when building Fedora on Ubuntu On Debian/Ubuntu installs of RPM, /usr/lib/rpm/macros sets %_dbpath %(echo $HOME/.rpmdb) which makes quite a bit of sense, because RPM is not the system packager and thus RPM is setup to install things into a hierarchy in the users homedir. However, this messes things up when building a Fedora chroot on an Ubuntu platform. We use RPM & yum from the base-system to bootstrap the Fedora chroot. While both obey --root flags, they still pick up the %_dbpath macro and so end up creating the RPM database in <chroot>/home/user/.rpmdb After we have bootstrapped yum/dnf, we execute further installation commands from inside the chroot -- where we now have the Fedora version of /usr/lib/rpm/macros and hence have _dbpath set to /var/lib/rpm -- except there is no rpm database there. Should anyone be finding this in the future, the actual issue that appears is $ sudo chroot /opt/dib_tmp/image.b6B5S3f6/mnt dnf makecache Error: Failed to synchronize cache for repo 'fedora' from \ 'https://mirrors.fedoraproject.org/metalink?repo=fedora-$releasever&arch=x86_64': \ Cannot prepare internal mirrorlist: file "repomd.xml" was not found in metalink Note the issue there is that $releasever is not expanded, because the rpmdb where this info is kept is not populated. The trick is to make sure we override this value when using the host rpm/yum to setup the chroot. The bare rpm calls, which we use to install the repos, have a --dbpath argument where we can override this. yum does not however, so we override this in the global ~/.rpmmacros while we are installing the packaging tools and dependencies into the chroot. Copious comments are included, because this is super-confusing. Change-Id: I20801150ea02d1c64f118eb969fb2aec473476f7
2015-10-23 04:42:00 +00:00
sudo $_RPM --root $TARGET_ROOT --nodeps -ivh $WORKING/*rpm
# install the bootstrap mirror repos over the default ones, if
# set. we will remove this at the end so the final image has
# regular mirrors
if [[ -n ${DIB_YUM_MINIMAL_BOOTSTRAP_REPOS:-} ]]; then
for repo in $TARGET_ROOT/etc/yum.repos.d/*.repo; do
sudo mv $repo $repo.USING_MIRROR
done
sudo cp ${DIB_YUM_MINIMAL_BOOTSTRAP_REPOS}/* \
$TARGET_ROOT/etc/yum.repos.d/
fi
}
# _install_pkg_manager packages...
#
# install the package manager packages. This is done outside the chroot
# and with yum from the build system.
# TODO: one day build systems will be dnf only, but we don't handle
# that right now
function _install_pkg_manager {
# Install into the chroot, using the gpg keys from the release
# rpm's installed in the chroot
sudo sed -i "s,/etc/pki/rpm-gpg,$TARGET_ROOT/etc/pki/rpm-gpg,g" \
$TARGET_ROOT/etc/yum.repos.d/*repo
Fixup RPM db path when building Fedora on Ubuntu On Debian/Ubuntu installs of RPM, /usr/lib/rpm/macros sets %_dbpath %(echo $HOME/.rpmdb) which makes quite a bit of sense, because RPM is not the system packager and thus RPM is setup to install things into a hierarchy in the users homedir. However, this messes things up when building a Fedora chroot on an Ubuntu platform. We use RPM & yum from the base-system to bootstrap the Fedora chroot. While both obey --root flags, they still pick up the %_dbpath macro and so end up creating the RPM database in <chroot>/home/user/.rpmdb After we have bootstrapped yum/dnf, we execute further installation commands from inside the chroot -- where we now have the Fedora version of /usr/lib/rpm/macros and hence have _dbpath set to /var/lib/rpm -- except there is no rpm database there. Should anyone be finding this in the future, the actual issue that appears is $ sudo chroot /opt/dib_tmp/image.b6B5S3f6/mnt dnf makecache Error: Failed to synchronize cache for repo 'fedora' from \ 'https://mirrors.fedoraproject.org/metalink?repo=fedora-$releasever&arch=x86_64': \ Cannot prepare internal mirrorlist: file "repomd.xml" was not found in metalink Note the issue there is that $releasever is not expanded, because the rpmdb where this info is kept is not populated. The trick is to make sure we override this value when using the host rpm/yum to setup the chroot. The bare rpm calls, which we use to install the repos, have a --dbpath argument where we can override this. yum does not however, so we override this in the global ~/.rpmmacros while we are installing the packaging tools and dependencies into the chroot. Copious comments are included, because this is super-confusing. Change-Id: I20801150ea02d1c64f118eb969fb2aec473476f7
2015-10-23 04:42:00 +00:00
# See notes on $_RPM variable -- we need to override the
# $HOME-based dbpath set on debian/ubuntu here. Unfortunately,
# yum does not have a way to override rpm macros from the command
# line. So we modify the user's ~/.rpmmacros to set %_dbpath back
# to "/var/lib/rpm" (note, this is taken relative to the
# --installroot).
#
# Also note, we only want this done around this call -- this is
# the only place we are using yum outside the chroot, and hence
# picking up the base-system's default rpm macros. For example,
# the yumdownloader calls above in _install_repos want to use
# ~/.rpmdb/ ... there is nothing in the build-system /var/lib/rpm!
#
# Another issue we hit is having to set --releasedir here. yum
# determines $releasevar based on (more or less) "rpm -q
# --whatprovides $distroverpkg". By default, this is
# "redhat-release" (fedora-release provides redhat-release) but
# some platforms like CentOS override it in /etc/yum.conf (to
# centos-release in their case). You can't override this (see
# [1]), but setting --releasever works around this.
#
# [1] https://bugzilla.redhat.com/show_bug.cgi?id=1287333
Fixup RPM db path when building Fedora on Ubuntu On Debian/Ubuntu installs of RPM, /usr/lib/rpm/macros sets %_dbpath %(echo $HOME/.rpmdb) which makes quite a bit of sense, because RPM is not the system packager and thus RPM is setup to install things into a hierarchy in the users homedir. However, this messes things up when building a Fedora chroot on an Ubuntu platform. We use RPM & yum from the base-system to bootstrap the Fedora chroot. While both obey --root flags, they still pick up the %_dbpath macro and so end up creating the RPM database in <chroot>/home/user/.rpmdb After we have bootstrapped yum/dnf, we execute further installation commands from inside the chroot -- where we now have the Fedora version of /usr/lib/rpm/macros and hence have _dbpath set to /var/lib/rpm -- except there is no rpm database there. Should anyone be finding this in the future, the actual issue that appears is $ sudo chroot /opt/dib_tmp/image.b6B5S3f6/mnt dnf makecache Error: Failed to synchronize cache for repo 'fedora' from \ 'https://mirrors.fedoraproject.org/metalink?repo=fedora-$releasever&arch=x86_64': \ Cannot prepare internal mirrorlist: file "repomd.xml" was not found in metalink Note the issue there is that $releasever is not expanded, because the rpmdb where this info is kept is not populated. The trick is to make sure we override this value when using the host rpm/yum to setup the chroot. The bare rpm calls, which we use to install the repos, have a --dbpath argument where we can override this. yum does not however, so we override this in the global ~/.rpmmacros while we are installing the packaging tools and dependencies into the chroot. Copious comments are included, because this is super-confusing. Change-Id: I20801150ea02d1c64f118eb969fb2aec473476f7
2015-10-23 04:42:00 +00:00
(
flock -w 1200 9 || die "Can not lock .rpmmacros"
echo "%_dbpath /var/lib/rpm" >> $HOME/.rpmmacros
local _lang_pack=""
local _extra_pkgs=""
if [ $DISTRO_NAME = "fedora" -a $DIB_RELEASE -le 23 ]; then
# _install_langs is a rpm macro that limits the translation
# files, etc installed by packages. For Fedora 23 [1], the
# glibc-common package will obey this to only install the
# listed locales, keeping things much smaller (we still have
# to clean up locales manually on centos7). We install just
# en_US because people often ssh in with that locale, but
# leave out everything else. Note that yum has an option to
# set this from the command-line [2], but the yum in trusty we
# are using is too old to have it. So we set it directly in
# the macros file
#
# [1] http://pkgs.fedoraproject.org/cgit/rpms/glibc.git/commit/glibc.spec?h=f23&id=91764bd9ec690d4b8a886c0a3a104aac12d340d2
# [2] http://yum.baseurl.org/gitweb?p=yum.git;a=commit;h=26128173b362474456e8f0642073ecb0322ed031
echo "%_install_langs C:en_US:en_US.UTF-8" >> $HOME/.rpmmacros
elif [ $DISTRO_NAME = "fedora" -a $DIB_RELEASE -ge 24 ]; then
# glibc on F24 has split locales into "langpack" packages.
# Yum doesn't understand the weak-dependencies glibc now
# uses to get the minimal-langpack and chooses a
# random(ish) one that satisfies the locale dependency
# (rhbz#1349258). Work-around this by explicitly requring
# the minimal and english (for en_US.UTF-8) pack.
_lang_pack="glibc-minimal-langpack glibc-langpack-en"
fi
# Yum has some issues choosing weak dependencies. It can end
# up choosing "coreutils-single" instead of "coreutils" which
# causes problems later when a package actually requires
# coreutils. For more info see
# https://bugzilla.redhat.com/show_bug.cgi?id=1286445
# Really all we can do is pre-install the right thing
_extra_pkgs+="coreutils "
Fixup RPM db path when building Fedora on Ubuntu On Debian/Ubuntu installs of RPM, /usr/lib/rpm/macros sets %_dbpath %(echo $HOME/.rpmdb) which makes quite a bit of sense, because RPM is not the system packager and thus RPM is setup to install things into a hierarchy in the users homedir. However, this messes things up when building a Fedora chroot on an Ubuntu platform. We use RPM & yum from the base-system to bootstrap the Fedora chroot. While both obey --root flags, they still pick up the %_dbpath macro and so end up creating the RPM database in <chroot>/home/user/.rpmdb After we have bootstrapped yum/dnf, we execute further installation commands from inside the chroot -- where we now have the Fedora version of /usr/lib/rpm/macros and hence have _dbpath set to /var/lib/rpm -- except there is no rpm database there. Should anyone be finding this in the future, the actual issue that appears is $ sudo chroot /opt/dib_tmp/image.b6B5S3f6/mnt dnf makecache Error: Failed to synchronize cache for repo 'fedora' from \ 'https://mirrors.fedoraproject.org/metalink?repo=fedora-$releasever&arch=x86_64': \ Cannot prepare internal mirrorlist: file "repomd.xml" was not found in metalink Note the issue there is that $releasever is not expanded, because the rpmdb where this info is kept is not populated. The trick is to make sure we override this value when using the host rpm/yum to setup the chroot. The bare rpm calls, which we use to install the repos, have a --dbpath argument where we can override this. yum does not however, so we override this in the global ~/.rpmmacros while we are installing the packaging tools and dependencies into the chroot. Copious comments are included, because this is super-confusing. Change-Id: I20801150ea02d1c64f118eb969fb2aec473476f7
2015-10-23 04:42:00 +00:00
sudo -E yum -y \
--disableexcludes=all \
--setopt=cachedir=$YUM_CACHE/$ARCH/$DIB_RELEASE \
--setopt=reposdir=$TARGET_ROOT/etc/yum.repos.d \
--releasever=$DIB_RELEASE \
--installroot $TARGET_ROOT \
install $@ ${_lang_pack} ${_extra_pkgs} && rc=$? || rc=$?
# Note we've modified the base system's .rpmmacros. Ensure we
# clean it up *always*
# sed makes it easy to remove last line, but not last n lines...
sed -i '$ d' $HOME/.rpmmacros; sed -i '$ d' $HOME/.rpmmacros;
if [ $rc != 0 ]; then
die "Initial yum install to chroot failed! Can not continue."
fi
) 9>$DIB_LOCKFILES/.rpmmacros.dib.lock
# Set gpg path back because subsequent actions will take place in
# the chroot
sudo sed -i "s,$TARGET_ROOT/etc/pki/rpm-gpg,/etc/pki/rpm-gpg,g" \
$TARGET_ROOT/etc/yum.repos.d/*repo
}
# Note this is not usually done for root.d elements (see
# lib/common-functions:mount_proc_dev_sys) but it's important that
# we have things like /dev/urandom around inside the chroot for
# the rpm [pre|post]inst scripts within the packages.
sudo mkdir -p $TARGET_ROOT/proc $TARGET_ROOT/dev $TARGET_ROOT/sys
sudo mount -t proc none $TARGET_ROOT/proc
sudo mount --bind /dev $TARGET_ROOT/dev
sudo mount -t devpts $(mount_dev_pts_options) devpts $TARGET_ROOT/dev/pts
sudo mount -t sysfs none $TARGET_ROOT/sys
# initalize rpmdb
sudo mkdir -p $TARGET_ROOT/var/lib/rpm
sudo $_RPM --root $TARGET_ROOT --initdb
# this makes sure that running yum/dnf in the chroot it can get
# out to download stuff
sudo mkdir $TARGET_ROOT/etc
sudo cp /etc/resolv.conf $TARGET_ROOT/etc/resolv.conf
# Bind mount the external yum cache inside the chroot. Same logic
# as in the yum element to provide for yum caching copied here
# because the sequencing is wrong otherwise
sudo mkdir -p $TMP_MOUNT_PATH/tmp/yum
sudo mount --bind $YUM_CACHE $TMP_MOUNT_PATH/tmp/yum
_install_repos
# install dnf for >= f22
if [ $DIB_RELEASE -ge 22 ]; then
# Fedora 27 has a "curl-minimal" package that will get pulled in as a
# rpm dependency. This causes problems later if/when "curl" gets
# installed. To avoid this, just install the full curl first up.
_install_pkg_manager dnf dnf-plugins-core yum curl
else
_install_pkg_manager yum
fi
# we just installed yum/dnf with "outside" tools (yum/rpm) which
# might have created /var/lib/[yum|rpm] (etc) that are slighlty
# incompatible. Refresh everything with the in-chroot tools
sudo -E chroot $TARGET_ROOT rpm --rebuilddb
sudo -E chroot $TARGET_ROOT ${YUM} clean all
# populate the lang reduction macro in the chroot
echo "%_install_langs C:en_US:en_US.UTF-8" | \
sudo tee -a $TARGET_ROOT/etc/rpm/macros.langs > /dev/null
_base_packages="systemd passwd findutils sudo util-linux-ng "
# This package is split out from systemd on >F24, dracut is
# missing the dependency and will fail to make an initrd without
# it; see
# https://bugzilla.redhat.com/show_bug.cgi?id=1398505
_base_packages+="systemd-udev "
# F29 moved to dbus-broker as the default, but dbus-daemon is
# still available. Explicitly pull it in; note we have an
# install.d script to enable the service explicitly it as this
# seems required.
if [[ ${DISTRO_NAME} = fedora && ${DIB_RELEASE} -ge 29 ]]; then
_base_packages+="dbus-broker"
fi
# bootstrap the environment within the chroot; bring in new
# metadata with an update and install some base packages we need.
sudo -E chroot $TARGET_ROOT ${YUM} -y update
sudo -E chroot $TARGET_ROOT ${YUM} -y \
--setopt=cachedir=/tmp/yum/$ARCH/$DIB_RELEASE \
install ${_base_packages}
# Put in a dummy /etc/resolv.conf over the temporary one we used
# to bootstrap. systemd has a bug/feature [1] that it will assume
# you want systemd-networkd as the network manager and create a
# broken symlink to /run/... if the base image doesn't have one.
# This broken link confuses things like dhclient.
# [1] https://bugzilla.redhat.com/show_bug.cgi?id=1197204
echo -e "# This file intentionally left blank\n" | \
sudo tee $TARGET_ROOT/etc/resolv.conf
# set the most reliable UTF-8 locale
echo -e 'LANG="en_US.UTF-8"' | \
sudo tee $TARGET_ROOT/etc/locale.conf
# default to UTC
sudo -E chroot $TARGET_ROOT ln -sf /usr/share/zoneinfo/UTC \
/etc/localtime
# cleanup
# TODO : move this into a exit trap; and reconsider how
# this integrates with the global exit cleanup path.
sudo umount $TMP_MOUNT_PATH/tmp/yum
sudo umount $TARGET_ROOT/proc
sudo umount $TARGET_ROOT/dev/pts
sudo umount $TARGET_ROOT/dev
sudo umount $TARGET_ROOT/sys
# RPM doesn't know whether files have been changed since install
# At this point though, we know for certain that we have changed no
# config files, so anything marked .rpmnew is just a bug.
for newfile in $(sudo find $TARGET_ROOT -type f -name '*rpmnew') ; do
sudo mv $newfile $(echo $newfile | sed 's/.rpmnew$//')
done
sudo rm -f ${TARGET_ROOT}/.extra_settings