provisioning tool for building opinionated architecture
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
|
|
#!/bin/bash
MIAOU_BASEDIR=$(readlink -f "$(dirname "$0")/..") readonly MIAOU_BASEDIR
# shellcheck source=/dev/null . "$MIAOU_BASEDIR/lib/functions.sh" miaou_init
EXPANDED_CONF="$MIAOU_CONFIGDIR/miaou.expanded.yaml" NEW_GROUP=lxd readonly NEW_GROUP EXPANDED_CONF
on_exit() { if [ -n "${1:-}" ]; then echowarn "Aborted by $1" elif [ "${status:-}" -ne 0 ]; then echowarn "Failure (status $status)" fi }
function prepare_lxd { local PREFIX="lxd:prepare"
# test group lxd assign to current user if ! groups | grep -q lxd; then echo "define lxd and assign to user <$USER>" sudo groupadd --force "$NEW_GROUP" sudo usermod --append --groups "$NEW_GROUP" "$(whoami)"
realcommand="$MIAOU_BASEDIR/lib/install.sh" sg "$NEW_GROUP" -c "EMAIL=$valid_email $realcommand SESSION_RELOAD_REQUIRED $TARGET"
set +e disable_all_signals sudo su - "$(whoami)" kill -9 "$PPID" # no further processing because exec has been called!
else echo "user <$USER> already belongs to group <lxd>!" fi
sudo /opt/miaou-bash/tools/idem_apt_install lxd btrfs-progs
# sudo tee /etc/systemd/system/lxd-containers-restart-on-failure.service &>/dev/null <<EOF # [Unit] # Description=restart lxd containers when no dhclient # After=lxd.service lxd-containers.service # Requires=lxd.socket # StartLimitInterval=60 # StartLimitBurst=5
# [Service] # Type=exec
# ExecCondition=sh -c '[ \$(lxc list status=running -c4 -fcsv | wc -l) -gt 0 ]] && lxc list status=running -c4 -fcsv | grep -vq eth0' # ExecStart=systemctl restart lxd-containers.service # ExecStartPost=sh -c 'sleep 2 ; [ \$(lxc list status=running -c4 -fcsv | wc -l) -eq 0 ]] || lxc list status=running -c4 -fcsv | grep -q eth0'
# Restart=on-failure # RestartSec=10
# [Install] # WantedBy=multi-user.target # EOF # sudo systemctl daemon-reload }
function configure_lxd { local PREFIX="lxd:configure"
# test lxdbr0 FIXME: preseed too much repetitions!!!! if ! lxc network info lxdbr0 &>/dev/null; then
echo "bridge <lxdbr0> down, so initialization will use default preseed..."
if [[ $(printenv container) == 'lxc' ]]; then echo "--------------------------------" echo "nested configuration applying..." echo "--------------------------------" cat <<EOF | sg $NEW_GROUP -c 'lxd init --preseed' config: {} networks: - name: lxdbr0 type: bridge config: ipv4.address: auto ipv6.address: none storage_pools: - config: {} description: "" name: default driver: dir profiles: - config: security.privileged: "true" description: "" devices: eth0: name: eth0 nictype: bridged parent: lxdbr0 type: nic root: path: / pool: default type: disk name: default projects: [] cluster: null EOF else empty_block_partition=$(lsblk -o NAME,FSTYPE,TYPE -ti -P | grep 'FSTYPE="" TYPE="part"' | head -n1 | cut -d'"' -f2) if [[ -n "$empty_block_partition" ]]; then echo "--------------------------------" echo "use empty block partition /dev/$empty_block_partition for speed and optimization" echo "--------------------------------" cat <<EOF | sudo lxd init --preseed config: {} networks: - config: ipv4.address: auto ipv6.address: none description: "" name: lxdbr0 type: "" project: default storage_pools: - config: source: /dev/$empty_block_partition description: "" name: default driver: btrfs profiles: - config: {} description: "" devices: eth0: name: eth0 network: lxdbr0 type: nic root: path: / pool: default type: disk name: default projects: [] cluster: null EOF else echo "--------------------------------" echo "use dir partition for development purpose" echo "--------------------------------" cat <<EOF | lxd init --preseed config: {} networks: - name: lxdbr0 type: bridge config: ipv4.address: auto ipv6.address: none storage_pools: - config: {} description: "" name: default driver: dir profiles: - config: {} description: "" devices: eth0: name: eth0 network: lxdbr0 type: nic root: path: / pool: default type: disk name: default projects: [] cluster: null EOF fi fi else echo "bridge <lxdbr0> found implies it has been already initialized!" fi
set_alias 'sameuser' "exec @ARG1@ -- su --whitelist-environment container,container_hostname - $(whoami)" set_alias 'login' 'exec @ARGS@ --mode interactive -- /bin/bash -c $@${user:-root} - exec su --whitelist-environment container,container_hostname - ' set_alias 'll' 'list -c ns4mDNc'
# test environment container hostname local env_container_hostname env_container_hostname=$(sg $NEW_GROUP -c 'lxc profile get default environment.container_hostname') if [[ -z "$env_container_hostname" ]]; then env_container_hostname=$(hostname -s) if env | grep -q container_hostname; then local previous_container_hostname previous_container_hostname=$(env | grep container_hostname | cut -d '=' -f2) env_container_hostname="$previous_container_hostname $env_container_hostname" fi
echo -n "set environment container_hostname to <$env_container_hostname> ... " sg $NEW_GROUP -c "lxc profile set default environment.container_hostname \"$env_container_hostname\"" PREFIX="" echoinfo DONE else echo "environment container_hostname <$env_container_hostname> already defined!" fi
if ! grep -q "root:$(id -u):1" /etc/subuid; then echo -n "saving subuid, subgid permissions for <$(whoami)> ..." printf "root:$(id -u):1\n" | sudo tee -a /etc/subuid /etc/subgid &>/dev/null PREFIX="" echoinfo DONE else echo "subuid, subgid allowing <$(whoami)> already done!" fi
if [[ ! -d "$HOME/LXD/SHARED" ]]; then echo -n "$HOME/LXD/SHARED creating ... " mkdir "$HOME/LXD/SHARED" -p PREFIX="" echoinfo DONE else echo "folder <$HOME/LXD/SHARED> already created!" fi
if [[ ! -d "$HOME/LXD/BACKUP" ]]; then echo -n "$HOME/LXD/BACKUP creating ... " mkdir "$HOME/LXD/BACKUP" -p PREFIX="" echoinfo DONE else echo "folder <$HOME/LXD/BACKUP> already created!" fi
}
function set_alias { local name="$1" local command="$2" if ! lxc alias list -f csv | grep -q "^$name,"; then echo -n "defining new lxc alias <$name> ..." lxc alias add "$name" "$command" PREFIX="" echoinfo DONE else echo "lxc alias <$name> already defined!" fi
}
function miaou_evalfrombashrc() { local PREFIX="miaou:bashrc" output=$( /opt/miaou-bash/tools/append_or_replace \
"^eval \"\\$\($MIAOU_BASEDIR/lib/install.sh shellenv\)\"$" \
"eval \"\$($MIAOU_BASEDIR/lib/install.sh shellenv)\"" \
"$HOME/.bashrc" )
if [[ "$output" == "appended" ]]; then echo "new path <$MIAOU_BASEDIR> created!" SESSION_RELOAD_REQUIRED=true else echo "path <$MIAOU_BASEDIR> already loaded!" fi }
function ask_target() { PS3='Choose miaou target purpose: ' foods=("Dev" "Beta" "Prod") select ans in "${foods[@]}"; do builtin echo "${ans^^}" break done }
function check_credential { local PREFIX="check:credential"
check_yaml_defined_value /etc/miaou/defaults.yaml 'credential.username' && check_yaml_defined_value /etc/miaou/defaults.yaml 'credential.shadow' && check_yaml_defined_value /etc/miaou/defaults.yaml 'credential.email' && check_yaml_defined_value /etc/miaou/defaults.yaml 'credential.redis' }
function check_target() { case "${TARGET^^}" in DEV) ;; BETA) ;; PROD) ;; *) if [[ -f /etc/miaou/defaults.yaml ]]; then # load already defined target in expanded conf TARGET=$(grep -Es "^target:" /etc/miaou/defaults.yaml | cut -d ' ' -f2) else TARGET=$(ask_target) fi ;; esac TARGET=${TARGET,,} # downcase return 0 }
function miaou_configfiles() { local PREFIX="miaou:config"
if [[ ! -d /etc/miaou ]]; then echo -n "configuration initializing ..." sudo mkdir -p /etc/miaou sudo chown "$USER" /etc/miaou PREFIX="" echoinfo DONE fi
if [[ ! -f /etc/miaou/defaults.yaml ]]; then echo -n "building /etc/miaou/defaults.yaml for the first time..." shadow_passwd=$(sudo grep "$CURRENT_USER" /etc/shadow | cut -d ':' -f2) redis=$( SIZE=12 tr -cd '[:alnum:]' </dev/urandom | fold -w $SIZE | head -n1 ) env current_user="$CURRENT_USER" shadow_passwd="$shadow_passwd" valid_email="$valid_email" redis="$redis" tera -e --env-key env --env-only -t "$MIAOU_BASEDIR/templates/etc/defaults.yaml.j2" -o /etc/miaou/defaults.yaml >/dev/null yq ".target=\"$TARGET\"" /etc/miaou/defaults.yaml -i PREFIX="" echoinfo DONE fi
if [[ ! -f /etc/miaou/miaou.yaml ]]; then echo -n "building /etc/miaou/miaou.yaml for the first time..." cp "$MIAOU_BASEDIR/templates/etc/miaou.yaml.j2" /etc/miaou/miaou.yaml PREFIX="" echoinfo DONE fi
PREVIOUS_TARGET="" echo "expanded configuration stored in <$MIAOU_CONFIGDIR>!" [[ -f "$EXPANDED_CONF" ]] && PREVIOUS_TARGET=$(grep -Es "^target:" "$EXPANDED_CONF" | cut -d ' ' -f2)
if [[ "$PREVIOUS_TARGET" != "$TARGET" ]]; then if [[ -z "$PREVIOUS_TARGET" ]]; then echo "new target defined <$TARGET>" else echowarnn "TARGET has changed from <$PREVIOUS_TARGET> to <$TARGET>, do you agree?" if askConfirmation N; then echowarn "removing previous settings, please restart <miaou> to apply changes" rm "$MIAOU_CONFIGDIR" -rf else echoerr "TARGET not accepted, exit" exit 102 fi fi yq ".target=\"$TARGET\"" /etc/miaou/defaults.yaml -i else echo "target <$TARGET> already defined!" fi }
function opt_link() { if [[ $MIAOU_BASEDIR != '/opt/miaou-server' ]]; then if [[ -L '/opt/miaou-server' && -d '/opt/miaou-server' && $(readlink /opt/miaou-server) == "$MIAOU_BASEDIR" ]]; then echo "symbolic link /opt/miaou-server already set up!" else sudo rm -f /opt/miaou-server sudo ln -s "$MIAOU_BASEDIR" /opt/miaou-server echo "symbolic link /opt/miaou-server successfully defined!" fi else echo "real path /opt/miaou-server already set up!" fi }
function miaou_resolver() { local PREFIX="miaou:resolver" bridge=$(ip addr show lxdbr0 | grep "inet\b" | awk '{print $2}' | cut -d/ -f1) gateway=$(ip route | grep default | cut -d' ' -f3)
echo -n "test gateway <$gateway> as DNS resolver..." if dig +short "@$gateway" "$FDN_DOMAINNAME" >/dev/null; then resolver="$gateway" PREFIX="" echoinfo DONE else PREFIX="" echoinfo "finally replaced by FDN_RESOLVER=<$FDN_RESOLVER>" resolver="$FDN_RESOLVER" fi
if command -v nmcli &>/dev/null; then if [[ ! -f /etc/NetworkManager/dispatcher.d/50-miaou-resolver ]]; then echo -n "use NetworkManager dispatcher to deal with LXD bridge automatically..." sudo cp "$MIAOU_BASEDIR/templates/network-manager/50-miaou-resolver" /etc/NetworkManager/dispatcher.d/ sudo chmod +x /etc/NetworkManager/dispatcher.d/50-miaou-resolver ACTIVE_CONNECTION=$(nmcli -g NAME connection show --active | head -n1) sudo nmcli connection up "$ACTIVE_CONNECTION" &>/dev/null PREFIX="" echoinfo DONE else echo "miaou-resolver in NetworkManager dispatcher already initialized!" fi fi
if sudo systemctl is-enabled systemd-resolved.service --quiet &>/dev/null; then echo -n "disabling systemd-resolved..." sudo systemctl stop systemd-resolved.service --quiet sudo systemctl disable systemd-resolved.service --quiet PREFIX="" echoinfo DONE fi
if ! grep -q "nameserver $bridge" /etc/resolv.conf; then echo -n "customize resolv.conf from scratch with bridge=<$bridge>, resolver=<$resolver> ..." sudo chattr -i /etc/resolv.conf sudo tee /etc/resolv.conf &>/dev/null <<EOF # generated by miaou nameserver $bridge # LXD bridge nameserver $resolver # resolver EOF PREFIX="" echoinfo DONE sudo chattr +i /etc/resolv.conf else echo "customize resolv.conf already defined!" fi }
function extra_dev_desktop { # FIXME: detect if DEV && DESKTOP_USAGE to install spicy-client and other desktop tools : }
function override_lxd_service { local PREFIX="lxd:override"
if [[ ! -d /etc/systemd/system/lxd.service.d ]]; then echo -n "override lxd service..." sudo mkdir -p /etc/systemd/system/lxd.service.d sudo tee /etc/systemd/system/lxd.service.d/override.conf &>/dev/null <<EOF [Service] ExecStartPost=systemctl reload nftables.service Environment=LANGUAGE=en:en_US EOF sudo systemctl daemon-reload sudo systemctl restart lxd.service PREFIX="" echoinfo "DONE" else echo "lxd service already overridden!" fi }
function ask_for_email { local PREFIX="install:ask_for_email" valid_email=$(auto_detect_email)
while ! is_email_valid "$valid_email"; do echo -n "mandatory email: " read -rei "$valid_email" valid_email done }
function preload_bookworm_image { local PREFIX="preload:bookworm"
if [[ $(lxc image list debian/12/cloud -f csv | wc -l) -lt 1 ]]; then echo -n "downloading images from public remote, please hold on..." sg $NEW_GROUP -c 'lxc image copy images:debian/12/cloud local: --copy-aliases --quiet' PREFIX="" echoinfo DONE else echo -n "refreshing images from public remote..." sg $NEW_GROUP -c 'lxc image refresh debian/12/cloud --quiet' PREFIX="" echoinfo DONE fi }
function is_email_valid { [[ "$1" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$ ]] }
function auto_detect_email { found_email="${EMAIL:-}" [[ -z "$found_email" ]] && [[ -f /etc/miaou/defaults.yaml ]] && found_email=$(yq '.credential.email' /etc/miaou/defaults.yaml) [[ "$found_email" == 'null' ]] && found_email='' if ! is_email_valid "$found_email"; then if [[ -f $HOME/.ssh/authorized_keys ]]; then while IFS= read -r line; do found_email="$line" is_email_valid "$found_email" && break done < <(cut -d ' ' -f3 <"$HOME/.ssh/authorized_keys") fi fi
builtin echo "$found_email" }
### MAIN
if [[ "${1:-}" == "SESSION_RELOAD_REQUIRED" ]]; then SESSION_RELOAD_REQUIRED=true shift else SESSION_RELOAD_REQUIRED=false fi
if [[ "${1:-}" == "shellenv" ]]; then unset PREFIX echo "export MIAOU_BASEDIR=$MIAOU_BASEDIR" echo "export PATH=\"\$MIAOU_BASEDIR/scripts\":\$PATH" else
. "$MIAOU_BASEDIR/lib/init.sh"
trap 'status=$?; on_exit; exit $status' EXIT trap 'trap - HUP; on_exit SIGHUP; kill -HUP $$' HUP trap 'trap - INT; on_exit SIGINT; kill -INT $$' INT trap 'trap - TERM; on_exit SIGTERM; kill -TERM $$' TERM
PREFIX="miaou" : $PREFIX TARGET=${1:-} CURRENT_USER=$(id -un)
check_normal_user check_target ask_for_email
sudo_required install_miaou_bash install_mandatory_commands prepare_toolbox add_toolbox_sudoers prepare_nftables prepare_lxd override_lxd_service configure_lxd preload_bookworm_image miaou_resolver miaou_evalfrombashrc miaou_configfiles opt_link extra_dev_desktop
if [[ "$SESSION_RELOAD_REQUIRED" == true ]]; then echoinfo "successful \`session\` installation" echowarn "current session has been reloaded, due to new group <$NEW_GROUP>! Next time you type \`exit\`, this session will emit a kill signal!" else echoinfo "successful installation" fi fi
|