cluster-deployment/src/run.sh

316 lines
11 KiB
Bash
Executable File

#!/usr/bin/env bash
if [[ "${EUID}" -ne 0 ]]; then
echo "This script must be run as root." >&2;
exit 1;
fi
###############################################################################
step_current="0";
step_max="1";
###############################################################################
###
# Load the lantern build engine
###
# Make sure the current directory is the location of this script to simplify matters
cd "$(dirname "$(readlink -f "$0")")" || { echo "Error: Failed to cd to script directory" >&2; exit 1; };
lantern_path="lib/lantern-build-engine/";
# Check out the lantern git submodule if needed
if [ ! -f "${lantern_path}/lantern.sh" ]; then git submodule update --init "${lantern_path}"; fi
#shellcheck disable=SC1090
source "${lantern_path}/lantern.sh";
source "lib/normalise-arch.sh";
#shellcheck disable=SC1090
source "/etc/os-release";
###############################################################################
rainbow="cat";
if command_exists lolcat; then
rainbow="lolcat";
fi
version="$(git rev-parse HEAD)";
last_version="";
if [[ -r "/etc/sbrl-provisioning-commitid.txt" ]]; then
#shellcheck disable=SC2034
last_version="$(cat "/etc/sbrl-provisioning-commitid.txt")";
fi
###############################################################################
# Asks the user a yes/no question.
# $1 The question to ask the user.
# Returns 0 if the answer was yes, or 1 if the answer was no.
ask_yesno() {
local question="$1";
whiptail --title "Step ${step_current} / ${step_max}" --yesno "${question}" 8 40;
return "$?"; # Not actually needed, but best to be explicit
}
# Asks the user for a string of text.
# $1 The window title.
# $2 The question to ask.
# $3 The default text value.
# Returns the answer as a string on the standard output.
ask_text() {
local title="$1";
local question="$2";
local default_text="$3";
whiptail --title "${title}" --inputbox "${question}" 10 40 "${default_text}" 3>&1 1>&2 2>&3;
return "$?"; # Not actually needed, but best to be explicit
}
# Asks the user for a password.
# $1 The window title.
# $2 The question to ask.
# $3 The default text value.
# Returns the answer as a string on the standard output.
ask_password() {
local title="$1";
local question="$2";
local default_text="$3";
whiptail --title "${title}" --passwordbox "${question}" 10 40 "${default_text}" 3>&1 1>&2 2>&3;
return "$?"; # Not actually needed, but best to be explicit
}
# Asks the user to choose at most 1 item from a list of items.
# $1 The window title.
# $2..$n The items that the user must choose between.
# Returns the chosen item as a string on the standard output.
ask_multichoice() {
local title="$1"; shift;
local args=();
while [[ "$#" -gt 0 ]]; do
args+=("$1");
args+=("$1");
shift;
done
whiptail --nocancel --notags --menu "$title" 15 40 5 "${args[@]}" 3>&1 1>&2 2>&3;
return "$?"; # Not actually needed, but best to be explicit
}
queue_postinstall_step() {
local stepname="$1";
echo "${stepname}" >>"${temp_dir}/steps-postinstall.txt";
}
queue_preinstall_step() {
local stepname="$1";
echo "${stepname}" >>"${temp_dir}/steps-preinstall.txt";
}
queue_apt_install() {
for package_name in "$@"; do
subtask_begin "[apt] Queueing install of ${package_name}";
echo "${package_name}" >>"${temp_dir}/apt-packages.txt";
subtask_end "$?";
done
}
queue_firewall_rule() {
local rule="$*";
subtask_begin "[firewall] Queuing firewall rule ${rule}";
echo "${rule}" >>"${temp_dir}/ufw-rules.txt";
subtask_end "$?";
}
###############################################################################
# ███████ ████████ ███████ ██████ ██████
# ██ ██ ██ ██ ██ ██ ████
# ███████ ██ █████ ██████ ██ ██ ██
# ██ ██ ██ ██ ████ ██
# ███████ ██ ███████ ██ ██████
stage_begin "Preparing to provision host";
task_begin "Creating temporary directory";
temp_dir="$(mktemp --tmpdir -d "sbrl-provisioning-XXXXXXX")";
on_exit() {
task_begin "Cleaning up";
rm -rf "${temp_dir}";
}
trap on_exit EXIT;
task_end "$?";
task_begin "Setting initial state";
cat apt-packages.txt >"${temp_dir}/apt-packages.txt";
queue_preinstall_step "5-apt-sbrl.sh";
queue_preinstall_step "10-apt.sh";
queue_preinstall_step "15-ufw.sh";
queue_preinstall_step "20-sshguard.sh";
# Install docker by default
if [[ "${CLUSTER_EXCLUDE}" =~ docker ]]; then queue_preinstall_step "20-docker.sh"; fi
queue_preinstall_step "50-avahi-daemon.sh";
queue_preinstall_step "50-cgroups-memory.sh";
queue_preinstall_step "100-chrony.sh";
queue_postinstall_step "20-collectd.sh";
task_end "$?";
stage_end "$?";
###############################################################################
# ███████ ████████ ███████ ██████ ██
# ██ ██ ██ ██ ██ ███
# ███████ ██ █████ ██████ ██
# ██ ██ ██ ██ ██
# ███████ ██ ███████ ██ ██
step_current="1";
stage_begin "Configuring software choices";
source ./steps-config/5-locale.sh;
source ./steps-config/10-hostname.sh;
if ask_yesno "Use apt-cacher-ng server?"; then
source ./steps-config/10-apt-cache.sh;
fi
if ask_yesno "Install and configure wesher mesh wireguard VPN?"; then
source ./steps-config/50-wesher-wireguard.sh;
fi
source ./steps-config/75-consul.sh; # Has it's own whiptail logic
source ./steps-config/80-nomad.sh; # Has it's own whiptail logic
if ask_yesno "Add Laminar CI SSH public key to root authorized_keys?"; then
source ./steps-config/80-laminar-ssh.sh;
fi
source ./steps-config/100-logging.sh;
#shellcheck disable=2034
collectd_username="$(ask_text "collectd", "Enter collectd username")";
#shellcheck disable=2034
collectd_password="$(ask_password "collectd" "Enter collectd password")";
stage_end "$?";
###############################################################################
# ███████ ████████ ███████ ██████ ██████
# ██ ██ ██ ██ ██ ██
# ███████ ██ █████ ██████ █████
# ██ ██ ██ ██ ██
# ███████ ██ ███████ ██ ███████
# Pre-install tasks
#######################################
# From here on, *all* tasks must be COMPLETELY NONINTERACTIVE.
# If you have something you need to ask the user, it should have been asked
# above. The reason for this is that we do *not* want the user to be sitting
# around waiting for the next dialog box. They should have to wait only once,
# as this saves time.
step_current="2";
stage_begin "Executing pre-install tasks";
while read -r preinstall_step; do
#shellcheck disable=SC1090
source "steps-preinstall/${preinstall_step}";
done < <(cat "${temp_dir}/steps-preinstall.txt");
stage_end "$?" "1 or more pre-install tasks failed";
###############################################################################
# ███████ ████████ ███████ ██████ ██████
# ██ ██ ██ ██ ██ ██
# ███████ ██ █████ ██████ █████
# ██ ██ ██ ██ ██
# ███████ ██ ███████ ██ ██████
# Install packages
#######################################
step_current="3";
stage_begin "Installing apt packages";
# We *want* word splitting here
#shellcheck disable=SC2046
apt-get install --no-install-recommends --yes $(cat "${temp_dir}/apt-packages.txt");
stage_end "$?" "Failed to install apt packages";
###############################################################################
# ███████ ████████ ███████ ██████ ██ ██
# ██ ██ ██ ██ ██ ██ ██
# ███████ ██ █████ ██████ ███████
# ██ ██ ██ ██ ██
# ███████ ██ ███████ ██ ██
# Post-install tasks
#######################################
step_current="4";
stage_begin "Running post-install tasks";
while read -r postinstall_step; do
#shellcheck disable=SC1090
source "steps-postinstall/${postinstall_step}";
done < <(cat "${temp_dir}/steps-postinstall.txt");
stage_begin "$?" "Failed to run 1 or more post-install tasks";
###############################################################################
###
# Final steps
###
step_current="5";
# This is here so that we can be sure it runs last, as it may prompt the user about ssh keys! Then we don't have to sit around waiting.
# Commented out for now, as I'm unsure as to whether we actually need this atm or not
# source "steps-last/10-ssh-cluster-config.sh";
source "steps-last/15-ufw.sh";
###############################################################################
step_current="done";
echo "${version}" >>/etc/sbrl-provisioning-commitid.txt
echo "
██████ ██████ ██████ ██ ██ ██ ███████ ██ ██████ ███ ██ ██ ███ ██ ██████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ████ ██ ██
██████ ██████ ██ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ███
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ██████ ████ ██ ███████ ██ ██████ ██ ████ ██ ██ ████ ██████
██████ ██████ ███ ███ ██████ ██ ███████ ████████ ███████ ██
██ ██ ██ ████ ████ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ ████ ██ ██████ ██ █████ ██ █████ ██
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██████ ██████ ██ ██ ██ ███████ ███████ ██ ███████ ██
$(cat /etc/hostname) is now ready for use! Here's a quick checklist:
• Ensure a static IP is configured on the router if required
• Reboot me to ensure everything is working as intended
• If eldarion is to access this for CI builds autonomously, update known_hosts by executing ssh -T dietpi@${new_hostname} AFTER rebooting.
" | "${rainbow}";