Files
installerscript-sh/install.sh
2025-11-11 12:13:45 +01:00

299 lines
8.6 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
# --- Root / Sudo Logik ---
if [[ $EUID -ne 0 ]]; then
if ! command -v sudo >/dev/null 2>&1; then
echo -e "\033[0;31mFehler: Dieses Script benötigt sudo, ist aber nicht installiert.\033[0m"
echo "Bitte installiere sudo zuerst oder führe das Script als root aus."
exit 1
fi
SUDO="sudo"
else
SUDO=""
fi
BASE_URL="http://192.168.19.10:3020/madgerm/installerscript-sh.git"
API_URL="$BASE_URL/info.php" # Annahme: info.php ist weiterhin unter BASE_URL/info.php erreichbar
LOG_FILE="/tmp/homelab-installer.log"
TMP_DIR="/tmp/homelab-installer"
mkdir -p "$TMP_DIR"
# Farben
GREEN="\033[0;32m"
YELLOW="\033[1;33m"
RED="\033[0;31m"
NC="\033[0m"
log() {
echo -e "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}
cleanup() {
echo ""
log "${RED}⛔ Installation abgebrochen durch Benutzer.${NC}"
rm -rf "$TMP_DIR" 2>/dev/null || true
log "🛑 Abbruch abgeschlossen."
exit 130
}
trap cleanup INT
# --- Globale Pfadbasis für Passwort-Speicherung ---
INSTALLER_BASE_DIR="$(pwd)"
PASSWORD_STORE_FILE="$INSTALLER_BASE_DIR/keys.txt"
# --- Root / Sudo Helper ---
ensure_root() {
if [[ $EUID -ne 0 ]]; then
if ! command -v sudo >/dev/null 2>&1; then
log "${RED}Fehler: Dieses Script benötigt Root oder sudo.${NC}"
exit 1
fi
SUDO="sudo"
else
SUDO=""
fi
}
# --- Package Manager Erkennung ---
detect_pkg_manager() {
if command -v apt >/dev/null 2>&1; then PKG="apt"
elif command -v dnf >/dev/null 2>&1; then PKG="dnf"
elif command -v pacman >/dev/null 2>&1; then PKG="pacman"
elif command -v apk >/dev/null 2>&1; then PKG="apk"
else
log "${RED}Kein unterstützter Paketmanager gefunden.${NC}"
exit 1
fi
}
pkg_install() {
case "$PKG" in
apt) $SUDO apt update && $SUDO apt install -y "$@" ;;
dnf) $SUDO dnf install -y "$@" ;;
pacman) $SUDO pacman --noconfirm -Sy "$@" ;;
apk) $SUDO apk add "$@" ;;
esac
}
# --- Passwort Generator ---
generate_password() {
local name="$1"
local pass
pass="$(openssl rand -base64 24)"
echo "$name = $pass" >> "$PASSWORD_STORE_FILE"
log "${GREEN}🔐 Passwort erzeugt und gespeichert unter:${NC} $PASSWORD_STORE_FILE"
echo "$pass"
}
check_internet() {
ping -c 1 1.1.1.1 &>/dev/null || {
log "${RED}❗ Kein Internet erkannt.${NC}"
exit 1
}
}
log "🔍 Prüfe benötigte Programme..."
MISSING_PKGS=()
# --- Passwort Bereich schreiben ---
begin_password_section() {
local section="$1"
echo "" >> "$PASSWORD_STORE_FILE"
echo "===== $section =====" >> "$PASSWORD_STORE_FILE"
}
end_password_section() {
echo "===== ENDE $1 =====" >> "$PASSWORD_STORE_FILE"
echo "" >> "$PASSWORD_STORE_FILE"
}
# Passwort Generator (nutzt nun Section-Kontext)
generate_password() {
local key_name="$1"
local password
password="$(openssl rand -base64 24)"
echo "$key_name = $password" >> "$PASSWORD_STORE_FILE"
log "${GREEN}🔐 Passwort erzeugt:${NC} $key_name"
echo "$password"
}
need_cmd() {
local c="$1"
if ! command -v "$c" &>/dev/null; then
MISSING_PKGS+=("$c")
else
log "${GREEN}OK:${NC} $c vorhanden."
fi
}
need_cmd curl
need_cmd wget
need_cmd jq
need_cmd whiptail
if (( ${#MISSING_PKGS[@]} > 0 )); then
log "${YELLOW}Fehlende Pakete:${NC} ${MISSING_PKGS[*]}"
read -rp "Soll ich diese installieren? [n/Y]: " ans
[[ "$ans" =~ ^[YyJj]$ ]] || { log "Abbruch."; exit 1; }
$SUDO apt update
$SUDO apt install -y "${MISSING_PKGS[@]}"
fi
log "${GREEN}✅ Grundpakete vollständig.${NC}"
# --- Optional Ansible ---
if ! command -v ansible-playbook &>/dev/null; then
echo ""
echo "Ansible wird nur benötigt, wenn du Playbook-basierte Rezepte nutzen möchtest."
read -rp "Möchtest du Ansible installieren? [n/Y]: " install_ansible
if [[ "$install_ansible" =~ ^[YyJj]$ ]]; then
echo ""
echo "Installationsart:"
echo " 1) apt (einfach, aber ältere Version möglich)"
echo " 2) pip (empfohlen; ARM & x86 kompatibel; immer aktuell)"
read -rp "Auswahl [1/2, default 2]: " mode
mode="${mode:-2}"
if [[ "$mode" == "1" ]]; then
$SUDO apt update
$SUDO apt install -y ansible
else
if ! command -v pip3 &>/dev/null; then
$SUDO apt update
$SUDO apt install -y python3-pip
fi
pip3 install --break-system-packages ansible
fi
log "${GREEN}✅ Ansible installiert.${NC}"
else
log "${YELLOW}⏭ Ansible wird übersprungen.${NC}"
fi
else
log "${GREEN}OK:${NC} ansible-playbook vorhanden."
fi
install_docker() {
if ! command -v docker &> /dev/null; then
log "📦 Installiere Docker..."
pkg_install ca-certificates curl gnupg lsb-release
$SUDO install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | $SUDO gpg --dearmor -o /etc/apt/keyrings/docker.gpg
$SUDO chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | $SUDO tee /etc/apt/sources.list.d/docker.list > /dev/null
pkg_install docker-ce docker-ce-cli containerd.io docker-compose-plugin
log "${GREEN}✅ Docker installiert.${NC}"
else
log "${GREEN}OK:${NC} Docker ist bereits installiert."
fi
}
ask_to_install() {
local name="$1"
read -rp "Möchtest du '$name' installieren? [J/n]: " ans
[[ "$ans" =~ ^[JjYy]$ || -z "$ans" ]]
}
choose_from_list() {
local title="$1"
shift
local items=("$@")
local ROWS COLS H W
ROWS=$(tput lines)
COLS=$(tput cols)
H=$((ROWS * 80 / 100))
W=$((COLS * 80 / 100))
(( H < 15 )) && H=15
(( W < 40 )) && W=40
local menu_items=()
for item in "${items[@]}"; do
menu_items+=("$item" "")
done
local choice
choice=$(whiptail --title "$title" --menu "Mit ↑ ↓ und ENTER auswählen:" \
"$H" "$W" 15 \
"${menu_items[@]}" \
3>&1 1>&2 2>&3) || echo "back"
echo "$choice"
}
run_shell_recipe() {
local category="$1"
local recipe="$2"
local url="$BASE_URL/recipes/$category/$recipe/install.sh"
local script="$TMP_DIR/${category}_${recipe}_$(date +%s).sh"
log "📥 Lade Shell Installer..."
curl -fsSL "$url" -o "$script"
chmod +x "$script"
log "🚀 Starte Shell Installer..."
bash "$script"
}
run_ansible_recipe() {
local category="$1"
local recipe="$2"
local url="$BASE_URL/recipes/$category/$recipe/playbook.yml"
local file="/opt/homelab/playbooks/${category}_${recipe}.yml"
$SUDO mkdir -p /opt/homelab/playbooks
log "📥 Lade Ansible Playbook..."
curl -fsSL "$url" -o "$file"
log "🔧 Führe Ansible Playbook aus..."
ansible-playbook -i localhost, "$file"
}
run_recipe() {
local category="$1"
local recipe="$2"
local base="$BASE_URL/recipes/$category/$recipe"
local has_shell=$(curl -s --head "$base/install.sh" | grep -q "200" && echo yes || echo no)
local has_playbook=$(curl -s --head "$base/playbook.yml" | grep -q "200" && echo yes || echo no)
if [[ "$has_shell" == "yes" && "$has_playbook" == "no" ]]; then run_shell_recipe "$category" "$recipe"; return; fi
if [[ "$has_playbook" == "yes" && "$has_shell" == "no" ]]; then run_ansible_recipe "$category" "$recipe"; return; fi
if [[ "$has_shell" == "yes" && "$has_playbook" == "yes" ]]; then
mode=$(choose_from_list "Installationsmodus wählen" "Shell" "Ansible")
[[ "$mode" == "Shell" ]] && run_shell_recipe "$category" "$recipe"
[[ "$mode" == "Ansible" ]] && run_ansible_recipe "$category" "$recipe"
return
fi
whiptail --title "Fehler" --msgbox "Kein Installer gefunden." 10 50
}
open_category() {
local category="$1"
mapfile -t recipes < <(jq -r ".recipes.\"$category\"[]" "$TMP_DIR/info.json")
while true; do
choice=$(choose_from_list "Rezept wählen ($category)" "${recipes[@]}")
[[ "$choice" == "back" || -z "$choice" ]] && return
run_recipe "$category" "$choice"
done
}
main_menu() {
mapfile -t categories < <(jq -r '.recipes | keys[]' "$TMP_DIR/info.json")
while true; do
choice=$(choose_from_list "Kategorie wählen" "${categories[@]}")
[[ "$choice" == "back" || -z "$choice" ]] && continue
open_category "$choice"
done
}
check_internet
log "📥 Lade Menüstruktur..."
curl -fsSL "$API_URL" -o "$TMP_DIR/info.json"
log "🚀 Starte Homelab Installer"
main_menu