anvil/smithy

425 lines
11 KiB
Bash
Executable File

#!/bin/bash
locale -a | grep -q -e "^en_US$" && export LANG="en_US"
shopt -s nocasematch
SMITHY_NAME=$(readlink -f "$0")
cd "$(dirname "$0")"
VERBOSE="${VERBOSE:-0}"
YUM_OPTS="--assumeyes --nogpgcheck"
YUM_CONFIG_MANAGER_OPTS=""
CURL_OPTS=""
# Give access to the system packages so that when clients get installed
# after installation that the virtualenv can access them to do things like
# install images, setup keystone...
VENV_OPTS="--system-site-packages"
VENV_DIR="$PWD/.venv"
VENV_ACTIVATE="$VENV_DIR/bin/activate"
PIP="$VENV_DIR/bin/pip"
YYOOM_CMD="$PWD/tools/yyoom"
if [ "$VERBOSE" == "0" ]; then
YUM_OPTS="$YUM_OPTS -q"
CURL_OPTS="-s"
VENV_OPTS="$VENV_OPTS -q"
fi
# Source in our variables (or overrides)
source ".anvilrc"
if [ -n "$SUDO_USER" ]; then
USER_HOME=$(getent passwd "$SUDO_USER" | cut -d: -f6)
if [ -n "$USER_HOME" ]; then
HOME_RC="${USER_HOME}/.anvilrc"
if [ -f "$HOME_RC" ]; then
source "$HOME_RC"
fi
fi
fi
dump_list()
{
for var in "$@"; do
for name in $var; do
echo " - $name"
done
done
}
yum_install()
{
local requires=$@
if [ "$VERBOSE" == "0" ]; then
yum install $YUM_OPTS $requires > /dev/null 2>&1
else
yum install $YUM_OPTS $requires
fi
return $?
}
yum_remove()
{
local remove=$@
if [ "$VERBOSE" == "0" ]; then
yum remove $YUM_OPTS $remove > /dev/null 2>&1
else
yum remove $YUM_OPTS $remove
fi
return $?
}
bootstrap_apt_packages()
{
if [ -n "$REQUIRES" ]; then
echo -e "Installing system packages:"
dump_list $REQUIRES
echo "Please wait..."
if [ "$VERBOSE" == "0" ]; then
apt-get -y install $REQUIRES > /dev/null 2>&1
else
apt-get -y install $REQUIRES
fi
if [ "$?" != "0" ]; then
echo -e "Failed installing!"
return 1
fi
fi
}
bootstrap_rpm_packages()
{
if [ -n "$REQUIRES" ]; then
echo -e "Installing system packages:"
dump_list $REQUIRES
echo "Please wait..."
yum_install $REQUIRES
if [ "$?" != "0" ]; then
echo -e "Failed installing!"
return 1
fi
fi
if [ -n "$CONFLICTS" ]; then
echo -e "Removing conflicting system packages:"
dump_list $CONFLICTS
echo "Please wait..."
yum_remove $CONFLICTS
if [ "$?" != "0" ]; then
echo -e "Failed removing!"
return 1
fi
fi
return 0
}
clean_anvil_deps()
{
# A previously failed anvil build which is not fully cleaned
# up has the potential for leaving broken packages. We disable
# the anvil-deps repo on bootstrap to avoid subsequent yum failures
# when pulling packages from this repo during bootstrap.
yum_config_manager=`which yum-config-manager 2>/dev/null` || true
if [ -n "$yum_config_manager" ]; then
$yum_config_manager $YUM_CONFIG_MANAGER_OPTS --disable anvil-deps
fi
}
clean_pip()
{
# See: https://github.com/pypa/pip/issues/982
if [ -n "$SUDO_USER" ]; then
rm -rf "/tmp/pip-build-$SUDO_USER"
fi
}
bootstrap_epel()
{
# Installs the repository that will allow for installation of packages
# from epel, see https://fedoraproject.org/wiki/EPEL for information
# about what is epel.
[ -z "$EPEL_RPM_URL" ] && return 0
echo "Installing epel rpm from $EPEL_RPM_URL"
cache_and_install_rpm_url "$EPEL_RPM_URL"
return $?
}
bootstrap_repos()
{
# Installs other repositories that will allow for installation of packages
# from places other than epel. This allows for anvil to find dependencies
# from other places (so anvil can avoid building those dependencies in the
# first place).
[ -z "$REPO_URLS" ] && return 0
# If urls were provided but no directory, that's bad.
[ -z "$REPO_DIR" ] && return 1
for repo_url in $REPO_URLS; do
echo "Installing repo file from $repo_url -> $REPO_DIR"
local base_filename=$(basename $repo_url)
local output_filename="$REPO_DIR/$base_filename"
if [ "$VERBOSE" == "0" ]; then
wget -q "$repo_url" -O "$output_filename"
else
wget "$repo_url" -O "$output_filename"
fi
if [ "$?" != "0" ]; then
echo -e "Failed downloading $repo_url -> $output_filename"
return 1
fi
done
return 0
}
unsudo()
{
# If a sudo user is active the given files/directories will be changed to
# be owned by that user instead of the current root user, if no sudo user
# is active, then nothing changes.
if [ -n "$SUDO_UID" -a -n "$SUDO_GID" ]; then
if [ "$VERBOSE" == "0" ]; then
chown -R "$SUDO_UID:$SUDO_GID" $@
else
chown -R -c "$SUDO_UID:$SUDO_GID" $@
fi
fi
}
bootstrap_virtualenv()
{
# Creates a virtualenv and then installs anvils requirements in it.
echo "Setting up virtualenv in $VENV_DIR"
$VENV_CMD $VENV_OPTS "$VENV_DIR" || return 1
unsudo $VENV_DIR
local deps=$(cat requirements.txt | grep -v '^$\|^\s*\#' | sort)
if [ -n "$deps" ]; then
echo "Installing anvil dependencies in $VENV_DIR"
dump_list $deps
echo "Please wait..."
if [ "$VERBOSE" == "0" ]; then
$PIP install -r requirements.txt > /dev/null 2>&1
else
$PIP install -v -r requirements.txt
fi
if [ "$?" != "0" ]; then
return 1
fi
fi
unsudo $VENV_DIR
}
bootstrap_selinux()
{
# See if selinux is on.
echo "Enabling selinux for yum like binaries."
if [ "$(getenforce)" == "Enforcing" ]; then
# Ensure all yum api interacting binaries are ok to be used.
chcon -h "system_u:object_r:rpm_exec_t:s0" "$YYOOM_CMD"
fi
}
run_smithy()
{
source "$VENV_ACTIVATE"
local python=$(which python)
exec "$python" anvil $ARGS
}
puke()
{
local cleaned_force=$(echo "$FORCE" | sed -e 's/\([A-Z]\)/\L\1/g;s/\s//g')
if [ "$cleaned_force" != "yes" ]; then
echo -e "To run anyway set FORCE=yes and rerun." >&2
exit 1
fi
}
needs_bootstrap()
{
# Checks if we need to perform the bootstrap phase.
if [ "$SKIP_BOOTSTRAP" == "yes" ]; then
return 1
fi
if [ "$BOOTSTRAP" == "true" ]; then
return 0
fi
if [ ! -d "$VENV_DIR" -o ! -f "$VENV_ACTIVATE" -o ! -f "$PIP" ]; then
return 0
fi
return 1
}
rpm_is_installed()
{
# Checks if an rpm is already installed.
local name=$(basename "$1")
rpm -q "${name%.rpm}" &>/dev/null
return $?
}
cache_and_install_rpm_url()
{
# Downloads an rpm from a url and then installs it (if it's not already
# installed).
url=${1:?"Error: rpm url is undefined!"}
cachedir=${RPM_CACHEDIR:-'/tmp'}
rpm=$(basename "$url")
if rpm_is_installed "$rpm"; then
return 0
fi
if [ ! -f "$cachedir/$rpm" ]; then
echo -e "Downloading ${rpm} to ${cachedir}"
curl $CURL_OPTS "$url" -o "$cachedir/$rpm" || return 1
fi
echo -e "Installing $cachedir/$rpm"
yum_install "$cachedir/$rpm"
return $?
}
greatest_version()
{
for arg in "$@"; do
echo "$arg"
done | sort --version-sort --reverse | head -n1
}
smallest_version()
{
for arg in "$@"; do
echo "$arg"
done | sort --version-sort | head -n1
}
## Identify which bootstrap configuration file to use: either set
## explicitly (BSCONF_FILE) or determined based on the os distribution:
BSCONF_DIR="${BSCONF_DIR:-$(dirname $(readlink -f "$0"))/tools/bootstrap}"
get_os_info()
{
if [ "$(uname)" = "Linux" ] ; then
if [ -f /etc/redhat-release ] ; then
PKG="rpm"
OSNAME=`cat /etc/redhat-release`
OSDIST=`cat /etc/redhat-release | sed -e 's/release.*$//g;s/\s//g'`
if [ "$OSDIST" == "CentOSLinux" ] ; then
OSDIST="CentOS"
fi
PSUEDONAME=`cat /etc/redhat-release | sed s/.*\(// | sed s/\)//`
RELEASE=`cat /etc/redhat-release | sed s/.*release\ // | sed s/\ .*//`
MAJOR_RELEASE=`cat /etc/redhat-release | sed s/.*release\ // | sed s/\ .*// | cut -f1 -d"."`
elif [ -f /etc/debian_version ] ; then
PKG="deb"
OSDIST=`cat /etc/lsb-release | grep '^DISTRIB_ID' | awk -F= '{ print $2 }'`
PSUEDONAME=`cat /etc/lsb-release | grep '^DISTRIB_CODENAME' | awk -F= '{ print $2 }'`
RELEASE=`cat /etc/lsb-release | grep '^DISTRIB_RELEASE' | awk -F= '{ print $2 }'`
MAJOR_RELEASE=`cat /etc/lsb-release | grep '^DISTRIB_RELEASE' | awk -F= '{ print $2 }' | cut -f1 -d"."`
OSNAME="$OSDIST $RELEASE ($PSUEDONAME)"
fi
fi
}
get_os_info
if [ -z "$BSCONF_FILE" ]; then
BSCONF_FILE="$BSCONF_DIR/$OSDIST"
fi
ARGS=""
BOOTSTRAP=false
# Ad-hoc getopt to handle long opts.
#
# Smithy opts are consumed while those to anvil are copied through.
while [ "$#" != 0 ]; do
case "$1" in
'--bootstrap')
BOOTSTRAP=true
shift
;;
'--force')
FORCE=yes
shift
;;
*)
ARGS="$ARGS $1"
shift
;;
esac
done
# Source immediately so that we can export the needed variables.
if [ -f "$BSCONF_FILE" ]; then
source "$BSCONF_FILE"
fi
if ! needs_bootstrap; then
clean_pip
run_smithy
else
clean_anvil_deps
fi
if [ "$BOOTSTRAP" == "false" ]; then
echo "This system needs to be updated in order to run anvil!" >&2
echo "Running 'sudo $SMITHY_NAME --bootstrap' will attempt to do so." >&2
puke
fi
## Bootstrap smithy
if [ "$(id -u)" != "0" ]; then
echo "You must run '$SMITHY_NAME --bootstrap' with root privileges!" >&2
exit 1
fi
if [ ! -f "$BSCONF_FILE" ]; then
echo "Anvil has not been tested on distribution '$OSNAME'" >&2
puke
else
MIN_RELEASE=${MIN_RELEASE:?"Error: MIN_RELEASE is undefined!"}
SHORTNAME=${SHORTNAME:?"Error: SHORTNAME is undefined!"}
if [ "$MIN_RELEASE" != "$RELEASE" ]; then
if [ "$RELEASE" != "$(greatest_version "$RELEASE" "$MIN_RELEASE")" ]; then
echo "This script must be run on $SHORTNAME $MIN_RELEASE and newer and not $SHORTNAME $RELEASE." >&2
puke
fi
fi
if [ -n "$MAX_RELEASE" ]; then
if [ "$MAX_RELEASE" != "$RELEASE" ]; then
if [ "$MAX_RELEASE" == "$(smallest_version "$MAX_RELEASE" "$RELEASE")" ]; then
echo "This script must be run on $SHORTNAME $MAX_RELEASE or older and not $SHORTNAME $RELEASE." >&2
puke
fi
fi
fi
fi
# If we got here then we must be in force mode, setup with the default configuration
# file for unknown distros...
if [ ! -f "$BSCONF_FILE" ]; then
BSCONF_FILE="$BSCONF_DIR/Unknown"
SHORTNAME="$OSDIST"
source "$BSCONF_FILE"
fi
echo "Bootstrapping $SHORTNAME $RELEASE"
echo "Please wait..."
clean_pip
for step in ${STEPS:?"Error: STEPS is undefined!"}; do
echo "--- Running bootstrap step '$step' ---"
"bootstrap_${step}"
if [ $? != 0 ]; then
echo "Bootstrapping $SHORTNAME $RELEASE failed." >&2
exit 1
fi
done
clean_pip
# Anvil writes configurations in these locations, make sure they are created
# and that the user running this script can actually access those files (even
# later if they are not running with sudo).
mkdir -p -v /etc/anvil /usr/share/anvil
touch /var/log/anvil.log
unsudo /etc/anvil /usr/share/anvil /var/log/anvil.log
echo "Bootstrapped for $SHORTNAME $RELEASE"
ARGS="-a moo"
run_smithy