forked from neil/lxc-templates
92fd4dd9ad
Signed-off-by: Andreas Freudenberg <andreas.freudenberg@licomonch.net>
1330 lines
41 KiB
Bash
1330 lines
41 KiB
Bash
#!/bin/bash
|
|
|
|
#
|
|
# template script for generating Fedora container for LXC
|
|
#
|
|
|
|
#
|
|
# lxc: linux Container library
|
|
|
|
# Authors:
|
|
# Daniel Lezcano <daniel.lezcano@free.fr>
|
|
# Ramez Hanna <rhanna@informatiq.org>
|
|
# Michael H. Warfield <mhw@WittsEnd.com>
|
|
# Reto Gantenbein <reto.gantenbein@linuxmonk.ch>
|
|
|
|
# This library is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU Lesser General Public
|
|
# License as published by the Free Software Foundation; either
|
|
# version 2.1 of the License, or (at your option) any later version.
|
|
|
|
# This library is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
# Lesser General Public License for more details.
|
|
|
|
# You should have received a copy of the GNU Lesser General Public
|
|
# License along with this library; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
# configurations
|
|
FEDORA_RELEASE_MIN=24
|
|
FEDORA_RELEASE_DEFAULT=${FEDORA_RELEASE_DEFAULT:-25}
|
|
FEDORA_RSYNC_URL="${FEDORA_RSYNC_URL:-archives.fedoraproject.org::fedora-enchilada}"
|
|
MIRRORLIST_URL=${MIRRORLIST_URL:-https://mirrors.fedoraproject.org/mirrorlist}
|
|
|
|
local_state_dir="@LOCALSTATEDIR@"
|
|
lxc_path="@LXCPATH@"
|
|
lxc_template_config="@LXCTEMPLATECONFIG@"
|
|
lxc_default_conf="@LXC_DEFAULT_CONFIG@"
|
|
|
|
# allows the cache directory to be set by environment variable
|
|
LXC_CACHE_PATH="${LXC_CACHE_PATH:-"${local_state_dir}/cache/lxc"}"
|
|
|
|
# these are only going into comments in the resulting config...
|
|
lxc_network_type=veth
|
|
lxc_network_link=lxcbr0
|
|
|
|
# Some combinations of the tuning knobs below do not exactly make sense.
|
|
# but that's ok.
|
|
#
|
|
# If the "root_password" is non-blank, use it, else set a default.
|
|
# This can be passed to the script as an environment variable and is
|
|
# set by a shell conditional assignment. Looks weird but it is what it is.
|
|
#
|
|
# If the root password contains a ding ($) then try to expand it.
|
|
# That will pick up things like ${name} and ${RANDOM}.
|
|
# If the root password contains more than 3 consecutive X's, pass it as
|
|
# a template to mktemp and take the result.
|
|
#
|
|
# If root_display_password = yes, display the temporary root password at exit.
|
|
# If root_store_password = yes, store it in the configuration directory
|
|
# If root_prompt_password = yes, invoke "passwd" to force the user to change
|
|
# the root password after the container is created.
|
|
# If root_expire_password = yes, you will be prompted to change the root
|
|
# password at the first login.
|
|
#
|
|
# These are conditional assignments... The can be overridden from the
|
|
# preexisting environment variables...
|
|
#
|
|
# Make sure this is in single quotes to defer expansion to later!
|
|
# :{root_password='Root-${name}-${RANDOM}'}
|
|
: "${root_password='Root-${name}-XXXXXX'}"
|
|
|
|
# Now, it doesn't make much sense to display, store, and force change
|
|
# together. But, we gotta test, right???
|
|
: "${root_display_password='no'}"
|
|
: "${root_store_password='yes'}"
|
|
# Prompting for something interactive has potential for mayhem
|
|
# with users running under the API... Don't default to "yes"
|
|
: "${root_prompt_password='no'}"
|
|
|
|
# Expire root password? Default to yes, but can be overridden from
|
|
# the environment variable
|
|
: "${root_expire_password='yes'}"
|
|
|
|
# detect use under userns (unsupported)
|
|
for arg in "$@"; do
|
|
[ "${arg}" = "--" ] && break
|
|
if [ "${arg}" = "--mapped-uid" ] || [ "${arg}" = "--mapped-gid" ]
|
|
then
|
|
echo "This template can't be used for unprivileged containers." 1>&2
|
|
echo "You may want to try the \"download\" template instead." 1>&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
# make sure the usual locations are in PATH
|
|
export PATH=${PATH}:/usr/sbin:/usr/bin:/sbin:/bin
|
|
|
|
# dnf package manager arguments
|
|
dnf_args=( --assumeyes --best --allowerasing --disablerepo=* --enablerepo=fedora --enablerepo=updates )
|
|
|
|
# This function is going to setup a minimal host-arch native Fedora bootstrap
|
|
# environment which will be used to create new containers on non-Fedora hosts.
|
|
#
|
|
bootstrap_fedora()
|
|
{
|
|
local cache="${1}"
|
|
|
|
local bootstrap="${cache}/bootstrap"
|
|
if [ -d "${bootstrap}" ]
|
|
then
|
|
echo "Existing Fedora bootstrap environment found. Testing ..."
|
|
if chroot_update_fedora "${bootstrap}"
|
|
then
|
|
CHROOT_DIR="${bootstrap}"
|
|
CHROOT_CMD="chroot ${CHROOT_DIR} "
|
|
|
|
echo "Bootstrap environment appears to be functional."
|
|
return 0
|
|
else
|
|
echo "Error: Bootstrap environment detected in ${bootstrap}"
|
|
echo "but appears to be non-functional. Please remove and restart."
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
echo "Setting up new Fedora ${FEDORA_RELEASE_DEFAULT} (${basearch}) bootstrap environment."
|
|
|
|
[[ -d "${cache}" ]] || mkdir -p "${cache}"
|
|
|
|
tmp_bootstrap_dir=$( mktemp -d --tmpdir="${cache}" bootstrap_XXXXXX )
|
|
pushd "${tmp_bootstrap_dir}" >/dev/null
|
|
|
|
mkdir squashfs liveos bootstrap
|
|
|
|
# download the LiveOS squashfs image
|
|
if [ ! -f "${cache}/install.img" ]
|
|
then
|
|
local os_path="linux/releases/${FEDORA_RELEASE_DEFAULT}/Everything/${basearch}/os"
|
|
local image_path="images/install.img"
|
|
local ret=1
|
|
|
|
if [ -n "${rsync}" ]
|
|
then
|
|
echo "Syncing LiveOS squashfs image from ${FEDORA_RSYNC_URL} ... "
|
|
rsync --archive --info=progress "${FEDORA_RSYNC_URL}/${os_path}/${image_path}" .
|
|
ret=$?
|
|
else
|
|
if [ -z "${mirror}" ]
|
|
then
|
|
get_mirrors "${FEDORA_RELEASE_DEFAULT}" "${basearch}" || return $?
|
|
else
|
|
local mirror_url="${mirror}/${os_path}"
|
|
fi
|
|
for url in ${mirror:-${mirror_urls}}
|
|
do
|
|
echo "Downloading LiveOS squashfs image from ${url} ... "
|
|
curl --silent --show-error --fail --remote-name "${url}/${image_path}"
|
|
ret=$?
|
|
if [ ${ret} -ne 0 ]
|
|
then
|
|
continue
|
|
else
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [ "${ret}" != 0 ] || [ ! -s install.img ]
|
|
then
|
|
echo "Error: Download of squashfs image failed."
|
|
return 1
|
|
fi
|
|
mv install.img "${cache}/"
|
|
else
|
|
echo "Using cached LiveOS squashfs image."
|
|
fi
|
|
|
|
echo "Mounting LiveOS squashfs file system."
|
|
if ! mount -o loop -t squashfs "${cache}/install.img" squashfs/
|
|
then
|
|
echo "
|
|
Error: Mount of LiveOS squashfs image failed
|
|
--------------------------------------------
|
|
You must have squashfs support available to mount image. LiveOS image is now
|
|
cached. Process may be rerun without penalty of downloading LiveOS again. If
|
|
LiveOS is corrupt, remove ${cache}/install.img
|
|
"
|
|
return 1
|
|
fi
|
|
|
|
# mount contained LiveOS
|
|
if ! mount -o loop squashfs/LiveOS/rootfs.img liveos
|
|
then
|
|
echo "
|
|
Error: Mount of LiveOS stage0 rootfs image failed
|
|
-------------------------------------------------
|
|
LiveOS download may be corrupt. Remove ${cache}/LiveOS
|
|
to force a new download.
|
|
"
|
|
return 1
|
|
fi
|
|
|
|
echo "Copying LiveOS content to bootstrap environment ... "
|
|
if ! rsync --archive --acls --hard-links --sparse liveos/. bootstrap/
|
|
then
|
|
echo "Error: Build of bootstrap environment failed."
|
|
echo "Keeping directory ${tmp_bootstrap_dir} for your investigation."
|
|
exit 1
|
|
fi
|
|
|
|
# unmount liveos mounts - we're done with liveos at this point
|
|
umount liveos
|
|
umount squashfs
|
|
|
|
# customize bootstrap rootfs
|
|
pushd bootstrap >/dev/null
|
|
|
|
# setup repositories in bootstrap chroot
|
|
CHROOT_DIR="$(pwd)"
|
|
CHROOT_CMD="chroot ${CHROOT_DIR} "
|
|
INSTALL_ROOT="/"
|
|
if ! setup_repositories "${cache}" "${basearch}" "${FEDORA_RELEASE_DEFAULT}" "${mirror}"
|
|
then
|
|
echo "Error: Failed to configure repositories in ${CHROOT_DIR}${INSTALL_ROOT}"
|
|
exit 1
|
|
fi
|
|
if [ -n "${mirror}" ]
|
|
then
|
|
set_dnf_mirror_url ./etc/yum.repos.d/fedora*.repo
|
|
fi
|
|
|
|
# make sure /etc/resolv.conf is up to date in the chroot
|
|
cp /etc/resolv.conf ./etc/
|
|
|
|
# verify bootstrap chroot by running a dnf update
|
|
chroot_update_fedora "$(pwd)"
|
|
ret=$?
|
|
|
|
popd >/dev/null
|
|
|
|
if [ "${ret}" != 0 ]
|
|
then
|
|
echo "Error: Build of bootstrap environment failed."
|
|
echo "Keeping directory ${tmp_bootstrap_dir} for your investigation."
|
|
return 1
|
|
fi
|
|
|
|
mv bootstrap "${cache}"
|
|
|
|
popd >/dev/null
|
|
rm -rf "${tmp_bootstrap_dir}"
|
|
|
|
CHROOT_DIR="${bootstrap}"
|
|
CHROOT_CMD="chroot ${CHROOT_DIR} "
|
|
|
|
echo "Fedora bootstrap environment successfully created."
|
|
return 0
|
|
}
|
|
|
|
chroot_mounts()
|
|
{
|
|
test -n "${1}" && local chroot="${1}" || return 1
|
|
|
|
mount -t proc proc "${chroot}/proc" &&
|
|
mount -o bind /dev "${chroot}/dev"
|
|
}
|
|
|
|
chroot_umounts()
|
|
{
|
|
test -n "${1}" && local chroot="${1}" || return 1
|
|
|
|
umount "${chroot}/proc" &&
|
|
umount "${chroot}/dev"
|
|
}
|
|
|
|
clean_cache()
|
|
{
|
|
local cache="${1}"
|
|
|
|
test ! -e "${cache}" && return 0
|
|
|
|
# lock, so we won't purge while someone is creating a repository
|
|
(
|
|
if ! flock -x 9
|
|
then
|
|
echo "Error: Cache repository is busy."
|
|
exit 1
|
|
fi
|
|
|
|
echo -n "Purging the Fedora bootstrap and download cache ... "
|
|
rm --preserve-root --one-file-system -rf "${cache}" && echo "Done." || exit 1
|
|
|
|
exit 0
|
|
|
|
) 9>"${local_state_dir}/lock/subsys/lxc-fedora"
|
|
|
|
return $?
|
|
}
|
|
|
|
# Customize container rootfs
|
|
#
|
|
configure_fedora()
|
|
{
|
|
local rootfs="${1}"
|
|
local release="${2}"
|
|
local utsname="${3}"
|
|
|
|
# disable selinux
|
|
mkdir -p "${rootfs}/selinux"
|
|
echo 0 > "${rootfs}/selinux/enforce"
|
|
|
|
# also kill it in the /etc/selinux/config file if it's there...
|
|
if [ -f "${rootfs}/etc/selinux/config" ]
|
|
then
|
|
sed -i '/^SELINUX=/s/.*/SELINUX=disabled/' "${rootfs}/etc/selinux/config"
|
|
fi
|
|
|
|
# nice catch from Dwight Engen in the Oracle template.
|
|
# wantonly plagerized here with much appreciation.
|
|
if [ -f "${rootfs}/usr/sbin/selinuxenabled" ]
|
|
then
|
|
rm -f "${rootfs}/usr/sbin/selinuxenabled"
|
|
ln -s /bin/false "${rootfs}/usr/sbin/selinuxenabled"
|
|
fi
|
|
|
|
# set hostname
|
|
echo "${utsname}" > "${rootfs}/etc/hostname"
|
|
|
|
# set default localtime to the host localtime if not set...
|
|
if [ -e /etc/localtime ] && [ ! -e "${rootfs}/etc/localtime" ]
|
|
then
|
|
# if /etc/localtime is a symlink, this should preserve it.
|
|
cp -a /etc/localtime "${rootfs}/etc/localtime"
|
|
fi
|
|
|
|
# set minimal hosts
|
|
cat <<EOF > "${rootfs}/etc/hosts"
|
|
127.0.0.1 localhost.localdomain localhost ${utsname}
|
|
::1 localhost6.localdomain6 localhost6
|
|
EOF
|
|
|
|
# setup console and tty[1-4] for login. note that /dev/console and
|
|
# /dev/tty[1-4] will be symlinks to the ptys /dev/lxc/console and
|
|
# /dev/lxc/tty[1-4] so that package updates can overwrite the symlinks.
|
|
# lxc will maintain these links and bind mount ptys over /dev/lxc/*
|
|
# since lxc.tty.dir is specified in the config.
|
|
|
|
# allow root login on console, tty[1-4], and pts/0 for libvirt
|
|
cat <<EOF >> "${rootfs}/etc/securetty"
|
|
# LXC (Linux Containers)
|
|
lxc/console
|
|
lxc/tty1
|
|
lxc/tty2
|
|
lxc/tty3
|
|
lxc/tty4
|
|
# For libvirt/Virtual Machine Monitor
|
|
pts/0
|
|
EOF
|
|
|
|
if [ "${root_display_password}" = yes ]
|
|
then
|
|
echo "Setting root password to '$root_password'"
|
|
fi
|
|
if [ "${root_store_password}" = yes ]
|
|
then
|
|
touch "${path}/tmp_root_pass"
|
|
chmod 600 "${path}/tmp_root_pass"
|
|
echo "${root_password}" > "${path}/tmp_root_pass"
|
|
echo "Storing root password in '${path}/tmp_root_pass'"
|
|
fi
|
|
|
|
echo "root:$root_password" | chroot "${rootfs}" chpasswd
|
|
|
|
if [ "${root_expire_password}" = yes ]
|
|
then
|
|
# also set this password as expired to force the user to change it!
|
|
chroot "${rootfs}" passwd -e root
|
|
fi
|
|
|
|
chroot_mounts "${rootfs}"
|
|
|
|
# always make sure /etc/resolv.conf is up to date in the target!
|
|
cp /etc/resolv.conf "${rootfs}/etc/"
|
|
|
|
# rebuild the rpm database based on the target rpm version...
|
|
rm -f "${rootfs}"/var/lib/rpm/__db*
|
|
chroot "${rootfs}" rpm --rebuilddb
|
|
|
|
chroot_umounts "${rootfs}"
|
|
|
|
# default systemd target
|
|
chroot "${rootfs}" ln -s /lib/systemd/system/multi-user.target \
|
|
/etc/systemd/system/default.target
|
|
|
|
# enable networking via systemd-networkd
|
|
test -d "${rootfs}/etc/systemd/network" || mkdir "${rootfs}/etc/systemd/network"
|
|
cat <<EOF > "${rootfs}/etc/systemd/network/eth0.network"
|
|
[Match]
|
|
Name=eth0
|
|
|
|
[Network]
|
|
DHCP=both
|
|
EOF
|
|
mkdir -p "${rootfs}/etc/systemd/system/socket.target.wants"
|
|
chroot "${rootfs}" ln -s /usr/lib/systemd/system/systemd-networkd.socket \
|
|
/etc/systemd/system/socket.target.wants/systemd-networkd.socket
|
|
chroot "${rootfs}" ln -s /usr/lib/systemd/system/systemd-networkd.service \
|
|
/etc/systemd/system/multi-user.target.wants/systemd-networkd.service
|
|
mkdir -p "${rootfs}/etc/systemd/system/network-online.target.wants"
|
|
chroot "${rootfs}" ln -s /usr/lib/systemd/system/systemd-networkd-wait-online.service \
|
|
/etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service
|
|
|
|
# disable traditional network init
|
|
chroot "${rootfs}" chkconfig network off
|
|
|
|
# enable systemd-resolved
|
|
rm -f "${rootfs}/etc/resolv.conf"
|
|
chroot "${rootfs}" ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf
|
|
chroot "${rootfs}" ln -s /usr/lib/systemd/system/systemd-resolved.service \
|
|
/etc/systemd/system/multi-user.target.wants/systemd-resolved.service
|
|
|
|
# if desired, prevent systemd from over-mounting /tmp with tmpfs
|
|
if [ "${masktmp}" -eq 1 ]
|
|
then
|
|
chroot "${rootfs}" ln -s /dev/null /etc/systemd/system/tmp.mount
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
copy_configuration()
|
|
{
|
|
local rootfs="${1}"
|
|
local config="${2}"
|
|
local utsname="${3}"
|
|
|
|
# include configuration from default.conf if available
|
|
grep -q "^lxc." "${lxc_default_conf}" > "${config}" 2>/dev/null
|
|
|
|
grep -q "^lxc.rootfs.path" "${config}" 2>/dev/null || echo "
|
|
lxc.rootfs.path = ${rootfs}
|
|
" >> "${config}"
|
|
|
|
# The following code is to create static MAC addresses for each
|
|
# interface in the container. This code will work for multiple
|
|
# interfaces in the default config. It will also strip any
|
|
# hwaddr stanzas out of the default config since we can not share
|
|
# MAC addresses between containers.
|
|
mv "${config}" "${config}.orig"
|
|
|
|
local line key
|
|
while read -r line
|
|
do
|
|
# This should catch variable expansions from the default config...
|
|
if expr "${line}" : '.*\$' > /dev/null 2>&1
|
|
then
|
|
line=$(eval "echo \"${line}\"")
|
|
fi
|
|
|
|
# There is a tab and a space in the regex bracket below!
|
|
# Seems that \s doesn't work in brackets.
|
|
key=$(expr "${line}" : '\s*\([^ ]*\)\s*=')
|
|
|
|
if [ "${key}" != "lxc.net.0.hwaddr" ]
|
|
then
|
|
echo "${line}" >> "${config}"
|
|
|
|
if [ "${key}" == "lxc.net.0.link" ]
|
|
then
|
|
echo "lxc.net.0.hwaddr = $(create_hwaddr)" >> "${config}"
|
|
fi
|
|
fi
|
|
done < "${config}.orig"
|
|
rm -f "${config}.orig"
|
|
|
|
if [ -e "${lxc_template_config}/fedora.common.conf" ]
|
|
then
|
|
echo "
|
|
# Include common configuration
|
|
lxc.include = ${lxc_template_config}/fedora.common.conf
|
|
" >> "${config}"
|
|
fi
|
|
|
|
cat <<EOF >> "${path}/config"
|
|
# Container specific configuration
|
|
lxc.arch = ${basearch}
|
|
lxc.uts.name = ${utsname}
|
|
|
|
# When using LXC with apparmor, uncomment the next line to run unconfined:
|
|
#lxc.apparmor.profile = unconfined
|
|
|
|
# example simple networking setup, uncomment to enable
|
|
#lxc.net.0.type = ${lxc_network_type}
|
|
#lxc.net.0.flags = up
|
|
#lxc.net.0.link = ${lxc_network_link}
|
|
#lxc.net.0.name = eth0
|
|
# Additional example for veth network type
|
|
# static MAC address,
|
|
#lxc.net.0.hwaddr = $(create_hwaddr)
|
|
# persistent veth device name on host side
|
|
# Note: This may potentially collide with other containers of same name!
|
|
#lxc.net.0.veth.pair = v-${name}-e0
|
|
EOF
|
|
|
|
if [ $? -ne 0 ]
|
|
then
|
|
echo "Failed to add configuration"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
copy_fedora()
|
|
{
|
|
local cache="${1}"
|
|
local rootfs="${2}"
|
|
echo -n "Copying ${cache} to ${rootfs} ... "
|
|
|
|
mkdir -p "${rootfs}" &&
|
|
rsync --archive --hard-links --sparse --acls --xattrs "${cache}/" "${rootfs}/" &&
|
|
echo || return 1
|
|
|
|
return 0
|
|
}
|
|
|
|
# Generate a random hardware (MAC) address composed of FE followed by
|
|
# 5 random bytes...
|
|
#
|
|
create_hwaddr()
|
|
{
|
|
openssl rand -hex 5 | sed -e 's/\(..\)/:\1/g; s/^/fe/'
|
|
}
|
|
|
|
# Make sure a fully functional rootfs of the requested release and architecture
|
|
# will be setup in the given cache directory. If this is a Fedora host the
|
|
# commands will run natively otherwise in a minimal Fedora bootstrap chroot.
|
|
#
|
|
download_fedora()
|
|
{
|
|
local cache_rootfs="${1}"
|
|
local setup_rootfs="${cache_rootfs%%/rootfs}/partial"
|
|
|
|
# suppress errors due to unknown locales
|
|
LC_ALL=C
|
|
LANG=en_US
|
|
|
|
echo "Downloading ${basearch} rootfs for Fedora ${release} ..."
|
|
|
|
# The following variables are going to be overwritten if the rootfs setup
|
|
# is run in a separate boostrap environment (can not build natively).
|
|
# These are the defaults for the non-boostrap (native) mode.
|
|
CHROOT_DIR=
|
|
CHROOT_CMD=
|
|
INSTALL_ROOT=${setup_rootfs}
|
|
|
|
if [ ! "${is_fedora}" ] || [ "${fedora_host_ver}" -lt "${FEDORA_VERSION_MINIMAL}" ]
|
|
then
|
|
# if this is not a supported Fedora host, use minimal bootstrap chroot
|
|
echo "Non-Fedora host detected. Checking for bootstrap environment ... "
|
|
if ! bootstrap_fedora "${cache}"
|
|
then
|
|
echo "Error: Fedora Bootstrap setup failed"
|
|
return 1
|
|
fi
|
|
echo "Using bootstrap environment at ${CHROOT_DIR}"
|
|
echo "Ensure bootstrap repository is fully setup for Fedora ${release}"
|
|
# check if the gpg-keys of the demanded release are installed in bootstrap
|
|
# this makes sure, that the bootstrap repo was already prepared for $release
|
|
# if we don't do this check, "setup_repositories" could try to install older
|
|
# versions of the release-rpms which would fail as rpm would fail
|
|
if [ -f ${CHROOT_DIR}/etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-${release}-primary ]
|
|
then
|
|
echo "The bootstrap repository seems to be prepared"
|
|
else
|
|
INSTALL_ROOT="/"
|
|
if ! setup_repositories "${cache}" "${basearch}" "${release}" "${mirror}"
|
|
then
|
|
echo "Error: Failed to configure repositories in ${CHROOT_DIR}${INSTALL_ROOT}"
|
|
return 1
|
|
fi
|
|
INSTALL_ROOT=${setup_rootfs}
|
|
fi
|
|
fi
|
|
|
|
if ! mkdir -p "${setup_rootfs}"
|
|
then
|
|
echo "Error: Failed to create '${setup_rootfs}' directory."
|
|
return 1
|
|
fi
|
|
|
|
trap revert_rootfs SIGHUP SIGINT SIGTERM
|
|
|
|
mkdir -p "${setup_rootfs}/var/lib/rpm"
|
|
|
|
# if the setup is going to be run in a chroot, mount the related file systems
|
|
if [ -n "${CHROOT_DIR}" ]
|
|
then
|
|
chroot_mounts "${CHROOT_DIR}"
|
|
|
|
# make sure rootfs is available in bootstrap chroot
|
|
INSTALL_ROOT="/run/install"
|
|
test -d "${CHROOT_DIR}${INSTALL_ROOT}" || mkdir -p "${CHROOT_DIR}${INSTALL_ROOT}"
|
|
mount -o bind "${setup_rootfs}" "${CHROOT_DIR}${INSTALL_ROOT}"
|
|
fi
|
|
|
|
if ! setup_repositories "${cache}" "${basearch}" "${release}" "${mirror}"
|
|
then
|
|
echo "Error: Failed to configure repositories in ${CHROOT_DIR}${INSTALL_ROOT}"
|
|
revert_rootfs >/dev/null
|
|
return 1
|
|
fi
|
|
|
|
# Unforunately <dnf-2.0 doesn't respect the repository configuration of the
|
|
# installroot but use the one from the host. This obviously doesn't work
|
|
# with a custom mirror or target architecture. Therefore a temporary dnf.conf
|
|
# is created and passed to the chroot command.
|
|
cat "${CHROOT_DIR}${INSTALL_ROOT}"/etc/yum.repos.d/{fedora,fedora-updates}.repo > "${CHROOT_DIR}${INSTALL_ROOT}/dnf.conf"
|
|
if [ -n "${mirror}" ]
|
|
then
|
|
set_dnf_mirror_url "${CHROOT_DIR}${INSTALL_ROOT}/dnf.conf"
|
|
fi
|
|
|
|
# install minimal container file system
|
|
local pkg_list="dnf initscripts passwd vim-minimal openssh-server openssh-clients dhclient rootfiles policycoreutils fedora-release fedora-repos"
|
|
if ! ${CHROOT_CMD}dnf --installroot "${INSTALL_ROOT}" \
|
|
--config="${INSTALL_ROOT}/dnf.conf" \
|
|
--releasever "${release}" \
|
|
${dnf_args[@]} \
|
|
install ${pkg_list}
|
|
then
|
|
echo "Error: Failed to setup the rootfs in ${CHROOT_DIR}${INSTALL_ROOT}."
|
|
revert_rootfs >/dev/null
|
|
return 1
|
|
fi
|
|
|
|
unmount_installroot
|
|
|
|
# from now on we'll work in the new rootfs
|
|
chroot_mounts "${setup_rootfs}"
|
|
|
|
# It might happen, that the dnf used above will write an incompatible
|
|
# RPM database for the version running in the rootfs. Rebuild it.
|
|
echo "Fixing up RPM databases"
|
|
rm -f "${setup_rootfs}"/var/lib/rpm/__db*
|
|
chroot "${setup_rootfs}" rpm --rebuilddb
|
|
|
|
# Restrict locale for installed packages to en_US to shrink image size
|
|
# following: https://pagure.io/fedora-kickstarts/blob/master/f/fedora-cloud-base.ks
|
|
echo "Cleanup locales and language files ..."
|
|
find "${setup_rootfs}/usr/share/locale" -mindepth 1 -maxdepth 1 -type d \
|
|
-not -name "${LANG}" -exec rm -rf {} +
|
|
|
|
chroot "${setup_rootfs}" localedef --list-archive | grep -v ^"${LANG}" | xargs \
|
|
chroot "${setup_rootfs}" localedef --delete-from-archive
|
|
|
|
mv -f "${setup_rootfs}/usr/lib/locale/locale-archive" \
|
|
"${setup_rootfs}/usr/lib/locale/locale-archive.tmpl"
|
|
chroot "${setup_rootfs}" build-locale-archive
|
|
|
|
echo "%_install_langs C:en:${LANG}:${LANG}.UTF-8" > "${setup_rootfs}/etc/rpm/macros.image-language-conf"
|
|
|
|
chroot_umounts "${setup_rootfs}"
|
|
|
|
# reset traps
|
|
trap SIGHUP
|
|
trap SIGINT
|
|
trap SIGTERM
|
|
|
|
# use generated rootfs as future cache
|
|
mv "${setup_rootfs}" "${cache_rootfs}"
|
|
|
|
echo "Download of Fedora rootfs complete."
|
|
return 0
|
|
}
|
|
|
|
# Query the Fedora mirrorlist for several HTTPS mirrors
|
|
#
|
|
get_mirrors()
|
|
{
|
|
local release="${1}"
|
|
local mirror_arch="${2}"
|
|
|
|
for trynumber in 1 2 3 4
|
|
do
|
|
[ "${trynumber}" != 1 ] && echo -n "Trying again ... "
|
|
|
|
# choose some mirrors by parsing directory index
|
|
mirror_urls=$(curl --silent --show-error --fail "${MIRRORLIST_URL}?repo=fedora-${release}&arch=${mirror_arch}" | sed '/^https:/!d' | sed '2,6!d')
|
|
|
|
# shellcheck disable=SC2181
|
|
if [ $? -eq 0 ] && [ -n "${mirror_urls}" ]
|
|
then
|
|
break
|
|
fi
|
|
|
|
echo "Warning: Failed to get a mirror on try ${trynumber}."
|
|
sleep 3
|
|
done
|
|
|
|
if [ -z "${mirror_urls}" ]
|
|
then
|
|
echo "Error: Failed to retrieve Fedora mirror URL. Please use '-m MIRROR' option."
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Install a functional Fedora rootfs into the container root
|
|
#
|
|
install_fedora()
|
|
{
|
|
local rootfs="${1}"
|
|
local cache="${2}"
|
|
local cache_rootfs="${cache}/${release}-${basearch}/rootfs"
|
|
|
|
mkdir -p "${local_state_dir}/lock/subsys/"
|
|
(
|
|
if ! flock -x 9
|
|
then
|
|
echo "Error: Cache repository is busy."
|
|
return 1
|
|
fi
|
|
|
|
echo "Checking cache download in ${cache_rootfs} ... "
|
|
if [ ! -e "${cache_rootfs}" ]
|
|
then
|
|
if ! download_fedora "${cache_rootfs}"
|
|
then
|
|
echo "Error: Failed to download Fedora ${release} (${basearch})"
|
|
return 1
|
|
fi
|
|
else
|
|
echo "Cache found at ${cache_rootfs}. Updating ..."
|
|
if ! chroot_update_fedora "${cache_rootfs}"
|
|
then
|
|
echo "Failed to update cached rootfs, continuing with previously cached version."
|
|
else
|
|
echo "Fedora update finished."
|
|
fi
|
|
fi
|
|
|
|
trap revert_container SIGHUP SIGINT SIGTERM
|
|
|
|
if ! copy_fedora "${cache_rootfs}" "${rootfs}"
|
|
then
|
|
echo "Error: Failed to copy rootfs"
|
|
return 1
|
|
fi
|
|
|
|
chroot_mounts "${rootfs}"
|
|
|
|
# install additional user provided packages
|
|
if [ -n "${packages}" ]
|
|
then
|
|
# always make sure /etc/resolv.conf is up to date in the target!
|
|
cp /etc/resolv.conf "${rootfs}/etc/"
|
|
|
|
echo "Installing user requested RPMs: ${packages}"
|
|
if ! chroot "${rootfs}" dnf install ${dnf_args[@]} $(tr ',' ' ' <<< "${packages}")
|
|
then
|
|
echo "Error: Installation of user provided packages failed."
|
|
echo "Cleaning up ... "
|
|
sleep 3 # wait for all file handles to properly close
|
|
chroot_umounts "${setup_rootfs}"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# cleanup dnf cache in new container
|
|
chroot "${rootfs}" dnf clean all
|
|
|
|
sleep 3 # wait for all file handles to properly close
|
|
chroot_umounts "${rootfs}"
|
|
|
|
return 0
|
|
) 9>"${local_state_dir}/lock/subsys/lxc-fedora"
|
|
|
|
return $?
|
|
}
|
|
|
|
# Cleanup partial container
|
|
#
|
|
revert_container()
|
|
{
|
|
echo "Interrupted, so cleaning up ..."
|
|
lxc-destroy -n "${name}" 2>/dev/null
|
|
# maybe was interrupted before copy config, try to prevent some mistakes
|
|
if [ -d "${path}" ] &&
|
|
[ "${path}" != "/" ] && [ "${path}" != "/tmp" ] && [ "${path}" != "/bin" ]
|
|
then
|
|
rm -rf "${path}"
|
|
fi
|
|
echo "Exiting ..."
|
|
exit 1
|
|
}
|
|
|
|
# Cleanup partial rootfs cache
|
|
#
|
|
revert_rootfs()
|
|
{
|
|
echo "Interrupted, so cleaning up ..."
|
|
unmount_installroot
|
|
rm -rf "${setup_rootfs}"
|
|
echo "Exiting ..."
|
|
exit 1
|
|
}
|
|
|
|
# Set dnf repository mirror in given repo files
|
|
#
|
|
set_dnf_mirror_url()
|
|
{
|
|
sed -i -e 's/^\(metalink=.*\)$/#\1/g' "${@}"
|
|
sed -i -e '/baseurl/ s|^#||g' "${@}"
|
|
sed -i -e "/baseurl/ s|http://download.fedoraproject.org/pub/fedora|${mirror}|g" "${@}"
|
|
}
|
|
|
|
# Setup dnf repository configuration. It can be run in a chroot by specifying
|
|
# $CHROOT_DIR (chroot directory) and $CHROOT_CMD (chroot command) and/or
|
|
# with an alternative RPM install root defined in $INSTALL_ROOT.
|
|
#
|
|
setup_repositories()
|
|
{
|
|
local cache="${1}"
|
|
local target_arch="${2}"
|
|
local release="${3}"
|
|
local mirror="${4}"
|
|
|
|
# select rpm packages to install depending on the version to install
|
|
rpmpkgs="fedora-release-${release} fedora-repos-${release}"
|
|
if [ "${release}" -gt 26 ]
|
|
then
|
|
rpmpkgs="${rpmpkgs} fedora-gpg-keys-${release}"
|
|
fi
|
|
|
|
# download repository packages if not found in cache
|
|
pushd "${cache}" >/dev/null
|
|
if [ -z "$(ls -1 ./fedora-release-${release}*.noarch.rpm 2>/dev/null)" ] ||
|
|
[ -z "$(ls -1 ./fedora-repos-${release}*.noarch.rpm 2>/dev/null)" ] ||
|
|
( [ "${release}" -gt 26 ] && [ -z "$(ls -1 ./fedora-gpg-keys-${release}*.noarch.rpm 2>/dev/null)" ] )
|
|
then
|
|
# if no mirror given, get an appropriate mirror from the mirror list
|
|
if [ -z "${mirror}" ]
|
|
then
|
|
get_mirrors "${release}" "${target_arch}" || return $?
|
|
else
|
|
# construct release-specific mirror url
|
|
mirror="${mirror}/linux/releases/${release}/Everything/${target_arch}/os"
|
|
fi
|
|
|
|
for mirror_url in ${mirror:-${mirror_urls}}
|
|
do
|
|
local release_url="${mirror_url}/Packages/f"
|
|
|
|
for pkg in ${rpmpkgs}
|
|
do
|
|
test -n "$(ls -1 ./${pkg}*.noarch.rpm 2>/dev/null)" && continue
|
|
|
|
# query package name by parsing directory index
|
|
echo "Requesting '${pkg}' package version from ${release_url}."
|
|
pkg_name=$(curl --silent --show-error --fail "${release_url}/" | sed -n -e "/${pkg}/ s/.*href=\"\(${pkg}-.*\.noarch\.rpm\)\">.*/\1/p" | tail -1)
|
|
|
|
# shellcheck disable=SC2181
|
|
if [ $? -ne 0 ] || [ -z "${pkg_name}" ]
|
|
then
|
|
echo "Error: Failed to get '${pkg}' version from ${release_url}/."
|
|
continue
|
|
fi
|
|
|
|
echo "Downloading '${release_url}/${pkg_name} ... "
|
|
if ! curl --silent --show-error --fail --remote-name "${release_url}/${pkg_name}"
|
|
then
|
|
echo "Error: Package download failed."
|
|
continue
|
|
fi
|
|
done
|
|
|
|
# if we have all packages continue
|
|
if [ -z "$(ls -1 ./fedora-release-${release}*.noarch.rpm 2>/dev/null)" ] ||
|
|
[ -z "$(ls -1 ./fedora-repos-${release}*.noarch.rpm 2>/dev/null)" ]
|
|
( [ "${release}" -gt 26 ] && [ -z "$(ls -1 ./fedora-gpg-keys-${release}*.noarch.rpm 2>/dev/null)" ] )
|
|
then
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# copy packages to chroot file system
|
|
if [ -n "${CHROOT_DIR}" ]
|
|
then
|
|
cp ./fedora-release-${release}*.noarch.rpm "${CHROOT_DIR}" &&
|
|
cp ./fedora-repos-${release}*.noarch.rpm "${CHROOT_DIR}"
|
|
if [ "${release}" -gt 26 ]
|
|
then
|
|
cp ./fedora-gpg-keys-${release}*.noarch.rpm "${CHROOT_DIR}"
|
|
fi
|
|
else
|
|
local pkgdir="${cache}"
|
|
fi
|
|
|
|
# use '--nodeps' to work around 'fedora-release-24-*' bash dependency
|
|
${CHROOT_CMD}rpm --root "${INSTALL_ROOT}" -ivh --nodeps "${pkgdir}"/{fedora-release-${release}*.noarch.rpm,fedora-repos-${release}*.noarch.rpm}
|
|
if [ "${release}" -gt 26 ];
|
|
then
|
|
${CHROOT_CMD}rpm --root "${INSTALL_ROOT}" -ivh --nodeps "${pkgdir}"/fedora-gpg-keys-${release}*.noarch.rpm
|
|
fi
|
|
local ret=$?
|
|
|
|
# dnf will take $basearch from host, so force the arch we want
|
|
sed -i "s|\$basearch|${target_arch}|" ${CHROOT_DIR}${INSTALL_ROOT}/etc/yum.repos.d/*
|
|
|
|
popd >/dev/null
|
|
|
|
if [ "${ret}" -ne 0 ]
|
|
then
|
|
echo "Failed to setup repositories in ${CHROOT_DIR}${INSTALL_ROOT}"
|
|
exit 1
|
|
fi
|
|
|
|
# cleanup installed packages
|
|
if [ -n "${CHROOT_DIR}" ]
|
|
then
|
|
# shellcheck disable=SC2086
|
|
rm -f "${CHROOT_DIR}"/{fedora-release-${release}*.noarch.rpm,fedora-repos-${release}*.noarch.rpm}
|
|
if [ "${release}" -gt 26 ]
|
|
then
|
|
rm -f "${CHROOT_DIR}"/fedora-gpg-keys-${release}*.noarch.rpm
|
|
fi
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Run dnf update in the given chroot directory.
|
|
#
|
|
chroot_update_fedora()
|
|
{
|
|
local chroot="${1}"
|
|
chroot_mounts "${chroot}"
|
|
|
|
# always make sure /etc/resolv.conf is up to date in the target!
|
|
cp /etc/resolv.conf "${chroot}/etc/"
|
|
|
|
chroot "${chroot}" dnf -y update
|
|
local ret=$?
|
|
|
|
sleep 3 # wait for all file handles to properly close
|
|
|
|
chroot_umounts "${chroot}"
|
|
|
|
return ${ret}
|
|
}
|
|
|
|
# Unmount installroot after bootstrapping or on error.
|
|
#
|
|
unmount_installroot() {
|
|
if [ -n "${CHROOT_DIR}" ]
|
|
then
|
|
sleep 3 # wait for all file handles to properly close
|
|
chroot_umounts "${CHROOT_DIR}" 2>/dev/null
|
|
umount "${CHROOT_DIR}${INSTALL_ROOT}" 2>/dev/null
|
|
fi
|
|
}
|
|
|
|
usage()
|
|
{
|
|
cat <<EOF
|
|
LXC Container configuration for Fedora images.
|
|
|
|
Template specific options can be passed to lxc-create after a '--' like this:
|
|
|
|
lxc-create --name=NAME -t fedora [OPTION..] -- [TEMPLATE_OPTION..]
|
|
|
|
Template options:
|
|
|
|
-a, --arch Define what arch the container will be [i386,x86_64]
|
|
-c, --clean Clean bootstrap and download cache
|
|
-d, --debug Run with 'set -x' to debug errors
|
|
--fqdn Fully qualified domain name (FQDN)
|
|
-h, --help Print this help text
|
|
--mask-tmp Prevent systemd from over-mounting /tmp with tmpfs.
|
|
-M, --mirror=MIRROR Fedora mirror to use during installation.
|
|
-p, --path=PATH Path to where the container will be created,
|
|
defaults to ${lxc_path}.
|
|
-P, --packages=PKGS Comma-separated list of additional RPM packages to
|
|
install into the container.
|
|
-R, --release=RELEASE Fedora release number of the container, defaults
|
|
to host's release if the host is Fedora.
|
|
--rootfs=ROOTFS Path for the actual container root file system
|
|
--rsync Use rsync instead of HTTPS to download bootstrap
|
|
image (insecure).
|
|
|
|
Environment variables:
|
|
|
|
LXC_CACHE_PATH Cache directory for image bootstrap. Defaults to
|
|
'${LXC_CACHE_PATH}'
|
|
|
|
MIRRORLIST_URL List of Fedora mirrors queried if no custom mirror is
|
|
given. Defaults to '${MIRRORLIST_URL}'
|
|
|
|
FEDORA_RSYNC_URL Fedora rsync URL to use for bootstrap with '--rsync'.
|
|
Defaults to '${FEDORA_RSYNC_URL}'
|
|
|
|
FEDORA_RELEASE_DEFAULT Set default Fedora release if not detected from the
|
|
host. Is set to '${FEDORA_RELEASE_DEFAULT}'
|
|
|
|
EOF
|
|
return 0
|
|
}
|
|
|
|
options=$(getopt -o a:hp:n:cR:dP:M: -l help,path:,rootfs:,name:,clean,release:,arch:,debug,fqdn:,mask-tmp,mirror:,packages:,rsync -- "$@")
|
|
# shellcheck disable=SC2181
|
|
if [ $? -ne 0 ]; then
|
|
usage
|
|
exit 1
|
|
fi
|
|
|
|
arch=$(uname -m)
|
|
debug=0
|
|
masktmp=0
|
|
|
|
eval set -- "$options"
|
|
while true
|
|
do
|
|
case "${1}" in
|
|
-h|--help) usage; exit 0 ;;
|
|
-n|--name) name="${2}"; shift 2 ;;
|
|
-p|--path) path="${2}"; shift 2 ;;
|
|
--rootfs) rootfs="${2}"; shift 2 ;;
|
|
-a|--arch) newarch="${2}"; shift 2 ;;
|
|
-c|--clean) clean=1; shift 1 ;;
|
|
-d|--debug) debug=1; shift 1 ;;
|
|
--fqdn) utsname="${2}"; shift 2 ;;
|
|
--mask-tmp) masktmp=1; shift 1 ;;
|
|
-M|--mirror) mirror="${2}"; shift 2 ;;
|
|
-P|--packages) packages="${2}"; shift 2 ;;
|
|
-R|--release) release="${2}"; shift 2 ;;
|
|
--rsync) rsync=1; shift 1 ;;
|
|
--) shift 1; break ;;
|
|
*) break ;;
|
|
esac
|
|
done
|
|
|
|
if [ "${debug}" -eq 1 ]
|
|
then
|
|
set -x
|
|
fi
|
|
|
|
# change to a safe directory
|
|
cd || exit $?
|
|
|
|
# Is this Fedora?
|
|
# Allow for weird remixes like the Raspberry Pi
|
|
#
|
|
# Use the Mitre standard CPE identifier for the release ID if possible...
|
|
# This may be in /etc/os-release or /etc/system-release-cpe. We
|
|
# should be able to use EITHER. Give preference to /etc/os-release for now.
|
|
|
|
if [ -e /etc/os-release ]
|
|
then
|
|
# This is a shell friendly configuration file. We can just source it.
|
|
# What we're looking for in here is the ID, VERSION_ID and the CPE_NAME
|
|
. /etc/os-release
|
|
fi
|
|
|
|
if [ "${CPE_NAME}" = "" ] && [ -e /etc/system-release-cpe ]
|
|
then
|
|
CPE_NAME=$(head -n1 /etc/system-release-cpe)
|
|
CPE_URI=$(expr "${CPE_NAME}" : '\([^:]*:[^:]*\)')
|
|
if [ "${CPE_URI}" != "cpe:/o" ]
|
|
then
|
|
CPE_NAME=
|
|
else
|
|
echo "Host CPE ID from /etc/system-release-cpe: ${CPE_NAME}"
|
|
# Probably a better way to do this but sill remain posix
|
|
# compatible but this works, shrug...
|
|
# Must be nice and not introduce convenient bashisms here.
|
|
ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:\([^:]*\)')
|
|
VERSION_ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:[^:]*:\([^:]*\)')
|
|
fi
|
|
fi
|
|
|
|
if [ "${ID}" = "fedora" ] && [ -n "${CPE_NAME}" ] && [ -n "${VERSION_ID}" ]
|
|
then
|
|
fedora_host_ver=${VERSION_ID}
|
|
is_fedora=true
|
|
fi
|
|
|
|
# Map a few architectures to their generic Fedora repository archs.
|
|
# The two ARM archs are a bit of a guesstimate for the v5 and v6
|
|
# archs. V6 should have hardware floating point (Rasberry Pi).
|
|
# The "arm" arch is safer (no hardware floating point). So
|
|
# there may be cases where we "get it wrong" for some v6 other
|
|
# than RPi.
|
|
basearch=${arch}
|
|
case "${arch}" in
|
|
i686) basearch=i386 ;;
|
|
armv3l|armv4l|armv5l) basearch=arm ;;
|
|
armv6l|armv7l|armv8l) basearch=armhfp ;;
|
|
*) ;;
|
|
esac
|
|
|
|
case "${basearch}" in
|
|
ppc64|s390x) FEDORA_RSYNC_URL="archives.fedoraproject.org::fedora-secondary" ;;
|
|
*) ;;
|
|
esac
|
|
|
|
# Somebody wants to specify an arch. This is very limited case.
|
|
# i386/i586/i686 on i386/x86_64
|
|
# - or -
|
|
# x86_64 on x86_64
|
|
if [ "${newarch}" != "" ] && [ "${newarch}" != "${arch}" ]
|
|
then
|
|
case "${newarch}" in
|
|
i386|i586|i686)
|
|
if [ "${basearch}" = "i386" ] || [ "${basearch}" = "x86_64" ]
|
|
then
|
|
# Make the arch a generic x86 32 bit...
|
|
basearch=i386
|
|
else
|
|
basearch=bad
|
|
fi
|
|
;;
|
|
*)
|
|
basearch=bad
|
|
;;
|
|
esac
|
|
|
|
if [ "${basearch}" = "bad" ]
|
|
then
|
|
echo "Error: You cannot build a ${newarch} Fedora container on a ${arch} host. Sorry!"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Let's do something better for the initial root password.
|
|
# It's not perfect but it will defeat common scanning brute force
|
|
# attacks in the case where ssh is exposed. It will also be set to
|
|
# expired, forcing the user to change it at first login.
|
|
if [ "${root_password}" = "" ]
|
|
then
|
|
root_password=Root-${name}-${RANDOM}
|
|
else
|
|
# If it's got a ding in it, try and expand it!
|
|
if [ "$(expr "${root_password}" : '.*$.')" != 0 ]
|
|
then
|
|
root_password=$(eval echo "${root_password}")
|
|
fi
|
|
|
|
# If it has more than 3 consecutive X's in it, feed it
|
|
# through mktemp as a template.
|
|
if [ "$(expr "${root_password}" : '.*XXXX')" != 0 ]
|
|
then
|
|
root_password=$(mktemp -u "${root_password}")
|
|
fi
|
|
fi
|
|
|
|
if [ -z "${utsname}" ]; then
|
|
utsname=${name}
|
|
fi
|
|
|
|
# This follows a standard "resolver" convention that an FQDN must have
|
|
# at least two dots or it is considered a local relative host name.
|
|
# If it doesn't, append the dns domain name of the host system.
|
|
#
|
|
# This changes one significant behavior when running
|
|
# "lxc_create -n Container_Name" without using the
|
|
# --fqdn option.
|
|
#
|
|
# Old behavior:
|
|
# utsname and hostname = Container_Name
|
|
# New behavior:
|
|
# utsname and hostname = Container_Name.Domain_Name
|
|
|
|
if [ "$(expr "${utsname}" : '.*\..*\.')" = 0 ]
|
|
then
|
|
if [ -n "$(dnsdomainname)" ] && [ "$(dnsdomainname)" != "localdomain" ]
|
|
then
|
|
utsname="${utsname}.$(dnsdomainname)"
|
|
fi
|
|
fi
|
|
|
|
# check if the pre-requisite binaries are available
|
|
prerequisite_pkgs=( curl openssl rsync )
|
|
needed_pkgs=""
|
|
for pkg in "${prerequisite_pkgs[@]}"
|
|
do
|
|
if ! type "${pkg}" >/dev/null 2>&1
|
|
then
|
|
needed_pkgs="${pkg} ${needed_pkgs}"
|
|
fi
|
|
done
|
|
if [ -n "${needed_pkgs}" ]
|
|
then
|
|
echo "Error: Missing command(s): ${needed_pkgs}"
|
|
exit 1
|
|
fi
|
|
|
|
if [ "$(id -u)" != "0" ]
|
|
then
|
|
echo "This script should be run as 'root'"
|
|
exit 1
|
|
fi
|
|
|
|
# cleanup cache if requested
|
|
cache="${LXC_CACHE_PATH}/fedora"
|
|
if [ -n "${clean}" ]
|
|
then
|
|
clean_cache "${cache}" || exit 1
|
|
exit 0
|
|
fi
|
|
|
|
# set container directory
|
|
if [ -z "${path}" ]
|
|
then
|
|
path="${lxc_path}/${name}"
|
|
fi
|
|
|
|
# set container rootfs and configuration path
|
|
config="${path}/config"
|
|
if [ -z "${rootfs}" ]
|
|
then
|
|
# check for 'lxc.rootfs.path' passed in through default config by lxc-create
|
|
if grep -q '^lxc.rootfs.path' "${config}" 2>/dev/null
|
|
then
|
|
rootfs=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' "${config}")
|
|
else
|
|
rootfs="${path}/rootfs"
|
|
fi
|
|
fi
|
|
|
|
# set release if not given
|
|
if [ -z "${release}" ]
|
|
then
|
|
if [ "${is_fedora}" ] && [ -n "${fedora_host_ver}" ]
|
|
then
|
|
echo "Detected Fedora ${fedora_host_ver} host. Set release to ${fedora_host_ver}."
|
|
release="${fedora_host_ver}"
|
|
else
|
|
echo "This is not a Fedora host or release is missing, defaulting release to ${FEDORA_RELEASE_DEFAULT}."
|
|
release="${FEDORA_RELEASE_DEFAULT}"
|
|
fi
|
|
fi
|
|
if [ "${release}" -lt "${FEDORA_RELEASE_MIN}" ]
|
|
then
|
|
echo "Error: Fedora release ${release} is not supported. Set -R at least to ${FEDORA_RELEASE_MIN}."
|
|
exit 1
|
|
fi
|
|
|
|
# bootstrap rootfs and copy to container file system
|
|
if ! install_fedora "${rootfs}" "${cache}"
|
|
then
|
|
echo "Error: Failed to create Fedora container"
|
|
exit 1
|
|
fi
|
|
|
|
# customize container file system
|
|
if ! configure_fedora "${rootfs}" "${release}" "${utsname}"
|
|
then
|
|
echo "Error: Failed to configure Fedora container"
|
|
exit 1
|
|
fi
|
|
|
|
# create container configuration (will be overwritten by newer lxc-create)
|
|
if ! copy_configuration "${rootfs}" "${config}" "${utsname}"
|
|
then
|
|
echo "Error: Failed write container configuration file"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -n "${clean}" ]; then
|
|
clean || exit 1
|
|
exit 0
|
|
fi
|
|
|
|
echo "Successfully created container '${name}'"
|
|
|
|
if [ "${root_display_password}" = "yes" ]
|
|
then
|
|
echo "The temporary password for root is: '$root_password'
|
|
|
|
You may want to note that password down before starting the container.
|
|
"
|
|
fi
|
|
|
|
if [ "${root_store_password}" = "yes" ]
|
|
then
|
|
echo "The temporary root password is stored in:
|
|
|
|
'${config}/tmp_root_pass'
|
|
"
|
|
fi
|
|
|
|
if [ "${root_prompt_password}" = "yes" ]
|
|
then
|
|
echo "Invoking the passwd command in the container to set the root password.
|
|
|
|
chroot ${rootfs} passwd
|
|
"
|
|
chroot "${rootfs}" passwd
|
|
else
|
|
if [ "${root_expire_password}" = "yes" ]
|
|
then
|
|
if ( mountpoint -q -- "${rootfs}" )
|
|
then
|
|
echo "To reset the root password, you can do:
|
|
|
|
lxc-start -n ${name}
|
|
lxc-attach -n ${name} -- passwd
|
|
lxc-stop -n ${name}
|
|
"
|
|
else
|
|
echo "
|
|
The root password is set up as expired and will require it to be changed
|
|
at first login, which you should do as soon as possible. If you lose the
|
|
root password or wish to change it without starting the container, you
|
|
can change it from the host by running the following command (which will
|
|
also reset the expired flag):
|
|
|
|
chroot ${rootfs} passwd
|
|
"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# vim: set ts=4 sw=4 expandtab:
|