You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ansible/test/lib/ansible_test/_util/target/setup/bootstrap.sh

427 lines
12 KiB
Bash

# shellcheck shell=sh
set -eu
remove_externally_managed_marker()
{
"${python_interpreter}" -c '
import pathlib
import sysconfig
path = pathlib.Path(sysconfig.get_path("stdlib")) / "EXTERNALLY-MANAGED"
path.unlink(missing_ok=True)
'
}
install_ssh_keys()
{
if [ ! -f "${ssh_private_key_path}" ]; then
# write public/private ssh key pair
public_key_path="${ssh_private_key_path}.pub"
# shellcheck disable=SC2174
mkdir -m 0700 -p "${ssh_path}"
touch "${public_key_path}" "${ssh_private_key_path}"
chmod 0600 "${public_key_path}" "${ssh_private_key_path}"
echo "${ssh_public_key}" > "${public_key_path}"
echo "${ssh_private_key}" > "${ssh_private_key_path}"
# add public key to authorized_keys
authoried_keys_path="${HOME}/.ssh/authorized_keys"
# the existing file is overwritten to avoid conflicts (ex: RHEL on EC2 blocks root login)
cat "${public_key_path}" > "${authoried_keys_path}"
chmod 0600 "${authoried_keys_path}"
# add localhost's server keys to known_hosts
known_hosts_path="${HOME}/.ssh/known_hosts"
for key in /etc/ssh/ssh_host_*_key.pub; do
echo "localhost $(cat "${key}")" >> "${known_hosts_path}"
done
fi
}
customize_bashrc()
{
true > ~/.bashrc
# Show color `ls` results when available.
if ls --color > /dev/null 2>&1; then
echo "alias ls='ls --color'" >> ~/.bashrc
elif ls -G > /dev/null 2>&1; then
echo "alias ls='ls -G'" >> ~/.bashrc
fi
# Improve shell prompts for interactive use.
echo "export PS1='\[\e]0;\u@\h: \w\a\]\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '" >> ~/.bashrc
}
install_pip() {
if ! "${python_interpreter}" -m pip.__main__ --version --disable-pip-version-check 2>/dev/null; then
case "${python_version}" in
*)
pip_bootstrap_url="https://ci-files.testing.ansible.com/ansible-test/get-pip-23.1.2.py"
;;
esac
while true; do
curl --silent --show-error "${pip_bootstrap_url}" -o /tmp/get-pip.py && \
"${python_interpreter}" /tmp/get-pip.py --disable-pip-version-check --quiet && \
rm /tmp/get-pip.py \
&& break
echo "Failed to install packages. Sleeping before trying again..."
sleep 10
done
fi
}
bootstrap_remote_alpine()
{
py_pkg_prefix="py3"
packages="
acl
bash
gcc
python3-dev
${py_pkg_prefix}-pip
sudo
"
if [ "${controller}" ]; then
packages="
${packages}
${py_pkg_prefix}-cryptography
${py_pkg_prefix}-packaging
${py_pkg_prefix}-yaml
${py_pkg_prefix}-jinja2
${py_pkg_prefix}-resolvelib
"
fi
while true; do
# shellcheck disable=SC2086
apk add -q ${packages} \
&& break
echo "Failed to install packages. Sleeping before trying again..."
sleep 10
done
# Upgrade the `libexpat` package to ensure that an upgraded Python (`pyexpat`) continues to work.
while true; do
# shellcheck disable=SC2086
apk upgrade -q libexpat \
&& break
echo "Failed to upgrade libexpat. Sleeping before trying again..."
sleep 10
done
}
bootstrap_remote_fedora()
{
py_pkg_prefix="python3"
packages="
acl
gcc
${py_pkg_prefix}-devel
"
if [ "${controller}" ]; then
packages="
${packages}
${py_pkg_prefix}-cryptography
${py_pkg_prefix}-jinja2
${py_pkg_prefix}-packaging
${py_pkg_prefix}-pyyaml
${py_pkg_prefix}-resolvelib
"
fi
while true; do
# shellcheck disable=SC2086
dnf install -q -y ${packages} \
&& break
echo "Failed to install packages. Sleeping before trying again..."
sleep 10
done
}
bootstrap_remote_freebsd()
{
packages="
python${python_package_version}
py${python_package_version}-sqlite3
py${python_package_version}-setuptools
bash
curl
gtar
sudo
"
if [ "${controller}" ]; then
jinja2_pkg="py${python_package_version}-jinja2"
cryptography_pkg="py${python_package_version}-cryptography"
pyyaml_pkg="py${python_package_version}-yaml"
packaging_pkg="py${python_package_version}-packaging"
# Declare platform/python version combinations which do not have supporting OS packages available.
# For these combinations ansible-test will use pip to install the requirements instead.
case "${platform_version}/${python_version}" in
13.3/3.9)
# defaults above 'just work'TM
;;
13.3/3.11)
jinja2_pkg="" # not available
cryptography_pkg="" # not available
pyyaml_pkg="" # not available
;;
14.0/3.9)
# defaults above 'just work'TM
;;
14.0/3.11)
cryptography_pkg="" # not available
jinja2_pkg="" # not available
pyyaml_pkg="" # not available
;;
*)
# just assume nothing is available
jinja2_pkg="" # not available
cryptography_pkg="" # not available
pyyaml_pkg="" # not available
packaging_pkg="" # not available
;;
esac
packages="
${packages}
${packaging_pkg}
libyaml
${pyyaml_pkg}
${jinja2_pkg}
${cryptography_pkg}
"
fi
while true; do
# shellcheck disable=SC2086
env ASSUME_ALWAYS_YES=YES pkg bootstrap && \
pkg install -q -y ${packages} \
&& break
echo "Failed to install packages. Sleeping before trying again..."
sleep 10
done
install_pip
if ! grep '^PermitRootLogin yes$' /etc/ssh/sshd_config > /dev/null; then
sed -i '' 's/^# *PermitRootLogin.*$/PermitRootLogin yes/;' /etc/ssh/sshd_config
service sshd restart
fi
# make additional wheels available for packages which lack them for this platform
echo "# generated by ansible-test
[global]
extra-index-url = https://spare-tire.testing.ansible.com/simple/
prefer-binary = yes
" > /etc/pip.conf
# enable ACL support on the root filesystem (required for become between unprivileged users)
fs_path="/"
fs_device="$(mount -v "${fs_path}" | cut -w -f 1)"
# shellcheck disable=SC2001
fs_device_escaped=$(echo "${fs_device}" | sed 's|/|\\/|g')
mount -o acls "${fs_device}" "${fs_path}"
awk 'BEGIN{FS=" "}; /'"${fs_device_escaped}"'/ {gsub(/^rw$/,"rw,acls", $4); print; next} // {print}' /etc/fstab > /etc/fstab.new
mv /etc/fstab.new /etc/fstab
# enable sudo without a password for the wheel group, allowing ansible to use the sudo become plugin
echo '%wheel ALL=(ALL:ALL) NOPASSWD: ALL' > /usr/local/etc/sudoers.d/ansible-test
}
bootstrap_remote_macos()
{
# Silence macOS deprecation warning for bash.
echo "export BASH_SILENCE_DEPRECATION_WARNING=1" >> ~/.bashrc
# Make sure ~/ansible/ is the starting directory for interactive shells on the control node.
# The root home directory is under a symlink. Without this the real path will be displayed instead.
if [ "${controller}" ]; then
echo "cd ~/ansible/" >> ~/.bashrc
fi
# Make sure commands like 'brew' can be found.
# This affects users with the 'zsh' shell, as well as 'root' accessed using 'sudo' from a user with 'zsh' for a shell.
# shellcheck disable=SC2016
echo 'PATH="/usr/local/bin:$PATH"' > /etc/zshenv
}
bootstrap_remote_rhel_9()
{
if [ "${python_version}" = "3.9" ]; then
py_pkg_prefix="python3"
else
py_pkg_prefix="python${python_version}"
fi
packages="
gcc
${py_pkg_prefix}-devel
"
# pip is not included in the Python devel package under Python 3.11
if [ "${python_version}" != "3.9" ]; then
packages="
${packages}
${py_pkg_prefix}-pip
"
fi
# Jinja2 is not installed with an OS package since the provided version is too old.
# Instead, ansible-test will install it using pip.
# packaging and resolvelib are missing for Python 3.11 (and possible later) so we just
# skip them and let ansible-test install them from PyPI.
if [ "${controller}" ]; then
packages="
${packages}
${py_pkg_prefix}-cryptography
${py_pkg_prefix}-pyyaml
"
fi
while true; do
# shellcheck disable=SC2086
dnf install -q -y ${packages} \
&& break
echo "Failed to install packages. Sleeping before trying again..."
sleep 10
done
}
bootstrap_remote_rhel()
{
case "${platform_version}" in
9.*) bootstrap_remote_rhel_9 ;;
esac
}
bootstrap_remote_ubuntu()
{
py_pkg_prefix="python3"
packages="
acl
gcc
python${python_version}-dev
python3-pip
python${python_version}-venv
"
if [ "${controller}" ]; then
cryptography_pkg="${py_pkg_prefix}-cryptography"
jinja2_pkg="${py_pkg_prefix}-jinja2"
packaging_pkg="${py_pkg_prefix}-packaging"
pyyaml_pkg="${py_pkg_prefix}-yaml"
resolvelib_pkg="${py_pkg_prefix}-resolvelib"
# Declare platforms which do not have supporting OS packages available.
# For these ansible-test will use pip to install the requirements instead.
# Only the platform is checked since Ubuntu shares Python packages across Python versions.
case "${platform_version}" in
"20.04")
jinja2_pkg="" # too old
resolvelib_pkg="" # not available
;;
esac
packages="
${packages}
${cryptography_pkg}
${jinja2_pkg}
${packaging_pkg}
${pyyaml_pkg}
${resolvelib_pkg}
"
fi
while true; do
# shellcheck disable=SC2086
apt-get update -qq -y && \
DEBIAN_FRONTEND=noninteractive apt-get install -qq -y --no-install-recommends ${packages} \
&& break
echo "Failed to install packages. Sleeping before trying again..."
sleep 10
done
}
bootstrap_docker()
{
# Required for newer mysql-server packages to install/upgrade on Ubuntu 16.04.
rm -f /usr/sbin/policy-rc.d
for key_value in ${python_interpreters}; do
IFS=':' read -r python_version python_interpreter << EOF
${key_value}
EOF
echo "Bootstrapping Python ${python_version} at: ${python_interpreter}"
remove_externally_managed_marker
done
}
bootstrap_remote()
{
for key_value in ${python_interpreters}; do
IFS=':' read -r python_version python_interpreter << EOF
${key_value}
EOF
echo "Bootstrapping Python ${python_version} at: ${python_interpreter}"
python_package_version="$(echo "${python_version}" | tr -d '.')"
case "${platform}" in
"alpine") bootstrap_remote_alpine ;;
"fedora") bootstrap_remote_fedora ;;
"freebsd") bootstrap_remote_freebsd ;;
"macos") bootstrap_remote_macos ;;
"rhel") bootstrap_remote_rhel ;;
"ubuntu") bootstrap_remote_ubuntu ;;
esac
remove_externally_managed_marker
done
}
bootstrap()
{
ssh_path="${HOME}/.ssh"
ssh_private_key_path="${ssh_path}/id_${ssh_key_type}"
install_ssh_keys
customize_bashrc
# allow tests to detect ansible-test bootstrapped instances, as well as the bootstrap type
echo "${bootstrap_type}" > /etc/ansible-test.bootstrap
case "${bootstrap_type}" in
"docker") bootstrap_docker ;;
"remote") bootstrap_remote ;;
esac
}
# These variables will be templated before sending the script to the host.
# They are at the end of the script to maintain line numbers for debugging purposes.
bootstrap_type=#{bootstrap_type}
controller=#{controller}
platform=#{platform}
platform_version=#{platform_version}
python_interpreters=#{python_interpreters}
ssh_key_type=#{ssh_key_type}
ssh_private_key=#{ssh_private_key}
ssh_public_key=#{ssh_public_key}
bootstrap