#!/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 # 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 " if [[ ${DISTRO_NAME} = fedora && ${DIB_RELEASE} -gt 29 ]]; then packages+="fedora-release-cloud fedora-release-common " else packages+="${DISTRO_NAME}-release " fi # 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 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 if [[ -n ${DIB_YUM_MINIMAL_EXTRA_REPOS:-} ]]; then sudo cp ${DIB_YUM_MINIMAL_EXTRA_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 # 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 ( 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 " 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 " # 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