Rueckweg bei jedem Fehlschlag, install.sh oben im Paket, eigener Port je Instanz
This commit is contained in:
1 parent
337a52cc38
commit
258e90bf4f
4 files changed
+465
-39
No files matched your search
+165
-20
@@ -28,6 +28,9 @@ set -euo pipefail
|
|||||||
|
|
||||||
APP=""
|
APP=""
|
||||||
INSTANCE=""
|
INSTANCE=""
|
||||||
|
#: Verzeichnis der Umgebungsdateien. Ueberschreibbar, damit sich die
|
||||||
|
#: Portsuche in einem Test wirklich ausfuehren laesst.
|
||||||
|
ETC_TESM="${TESM_ETC_DIR:-/etc/tesm}"
|
||||||
PORT=""
|
PORT=""
|
||||||
HTTP_PORT=""
|
HTTP_PORT=""
|
||||||
SKIP_PACKAGES=0
|
SKIP_PACKAGES=0
|
||||||
@@ -46,6 +49,62 @@ warn() { printf '%s !!%s %s\n' "$COLOR_WARN" "$COLOR_OFF" "$*"; }
|
|||||||
fail() { printf '%s !!%s %s\n' "$COLOR_ERR" "$COLOR_OFF" "$*" >&2; exit 1; }
|
fail() { printf '%s !!%s %s\n' "$COLOR_ERR" "$COLOR_OFF" "$*" >&2; exit 1; }
|
||||||
info() { printf ' %s\n' "$*"; }
|
info() { printf ' %s\n' "$*"; }
|
||||||
|
|
||||||
|
#: Merker fuer die EXIT-Falle. Zurueckgerollt wird nur, was auch gesichert ist.
|
||||||
|
SICHERUNG_STEHT=0
|
||||||
|
INSTALL_FERTIG=0
|
||||||
|
ZURUECKGEROLLT=0
|
||||||
|
|
||||||
|
zurueckrollen_install() {
|
||||||
|
[[ $SICHERUNG_STEHT -eq 1 && $ZURUECKGEROLLT -eq 0 ]] || return 0
|
||||||
|
# Kommt nur zum Zug, wenn die Sicherung steht -- dann sind diese Variablen
|
||||||
|
# gesetzt. Der Vollstaendigkeit halber trotzdem gepruefte Namen.
|
||||||
|
[[ -n "${BACKUP_DIR:-}" && -d "$BACKUP_DIR" ]] || { warn "Keine Sicherung gefunden -- nichts wiederhergestellt."; return 0; }
|
||||||
|
ZURUECKGEROLLT=1
|
||||||
|
warn "Alten Stand wiederherstellen."
|
||||||
|
systemctl stop "$UNIT" 2>/dev/null || true
|
||||||
|
[[ "$APP" == "tesm" ]] && systemctl stop "$MONITOR_UNIT" 2>/dev/null || true
|
||||||
|
rm -rf "$FAILED_DIR"
|
||||||
|
mv "$ROOT" "$FAILED_DIR" 2>/dev/null || true
|
||||||
|
cp -a "$BACKUP_DIR" "$ROOT"
|
||||||
|
systemctl start "$UNIT" 2>/dev/null || true
|
||||||
|
[[ "$APP" == "tesm" ]] && systemctl start "$MONITOR_UNIT" 2>/dev/null || true
|
||||||
|
warn "Wiederhergestellt aus $BACKUP_DIR"
|
||||||
|
warn "Fehlgeschlagener Stand liegt unter $FAILED_DIR"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Bei **jedem** Fehlschlag, nicht nur bei einem, an den ich gedacht habe:
|
||||||
|
# Systempakete, venv, sudoers, nginx, ein Ctrl-C. Vorher blieb der Dienst
|
||||||
|
# gestoppt und das Verzeichnis teils ersetzt zurueck.
|
||||||
|
bei_fehler() {
|
||||||
|
local code=$?
|
||||||
|
[[ $INSTALL_FERTIG -eq 1 || "$code" == "0" ]] && return "$code"
|
||||||
|
if [[ $SICHERUNG_STEHT -eq 1 ]]; then
|
||||||
|
zurueckrollen_install
|
||||||
|
else
|
||||||
|
# Neuinstallation: es gibt keinen alten Stand. Nichts loeschen -- im
|
||||||
|
# Zielverzeichnis kann eine Fassung 1.x liegen, deren Sicherung ein
|
||||||
|
# anderes Skript haelt (upgrade.sh).
|
||||||
|
# Vorsichtige Ersetzung: bricht es schon beim Auswerten der Argumente ab,
|
||||||
|
# gibt es $ROOT noch nicht -- und "set -u" wuerde die Falle selbst zum
|
||||||
|
# Fehler machen.
|
||||||
|
if [[ ${IS_UPDATE:-0} -eq 1 ]]; then
|
||||||
|
# Abbruch vor dem Sicherungsschritt: die Installation ist unangetastet.
|
||||||
|
warn "Abgebrochen (Code $code), bevor etwas geaendert wurde."
|
||||||
|
warn "Die bestehende Installation ist unberuehrt -- nichts zurueckzurollen."
|
||||||
|
else
|
||||||
|
warn "Abgebrochen (Code $code). Es gab keinen vorherigen Stand dieser Fassung,"
|
||||||
|
warn "also wurde nichts wiederhergestellt und nichts geloescht."
|
||||||
|
fi
|
||||||
|
# Nur sagen, was es wirklich gibt: bei einem Abbruch an der Rueckfrage
|
||||||
|
# existiert das Verzeichnis noch nicht, und die Zeile log dann.
|
||||||
|
if [[ -n "${ROOT:-}" && -d "${ROOT:-}" ]]; then
|
||||||
|
warn "Angelegt wurde bis hierhin: $ROOT (Daten unter data/ unberuehrt)."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return "$code"
|
||||||
|
}
|
||||||
|
trap bei_fehler EXIT
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<'USAGE'
|
cat <<'USAGE'
|
||||||
Verwendung: install.sh --app <tesm|tesm-license> [Optionen]
|
Verwendung: install.sh --app <tesm|tesm-license> [Optionen]
|
||||||
@@ -112,13 +171,24 @@ MONITOR_UNIT="$NAME-monitor.service"
|
|||||||
NGINX_SITE="/etc/nginx/sites-available/$NAME"
|
NGINX_SITE="/etc/nginx/sites-available/$NAME"
|
||||||
ENV_PREFIX="TESM_"
|
ENV_PREFIX="TESM_"
|
||||||
[[ "$APP" == "tesm-license" ]] && ENV_PREFIX="TESM_LICENSE_"
|
[[ "$APP" == "tesm-license" ]] && ENV_PREFIX="TESM_LICENSE_"
|
||||||
|
|
||||||
|
# Ausgabe des Lizenzservers: die Master-Ausgabe bringt das Teilpaket
|
||||||
|
# tesm_license/master mit -- den einzigen Weg, auf dem eine Lizenzdatei die
|
||||||
|
# Rolle "master" bekommt. Das Release fuer Sub-Lizenzserver hat es nicht.
|
||||||
|
# Die Ausgabe haengt an der Anwesenheit des Verzeichnisses, nicht an einer
|
||||||
|
# Einstellung: eine Einstellung liesse sich vor Ort umstellen.
|
||||||
|
MASTER_TEILPAKET="apps/tesm-license/src/tesm_license/master"
|
||||||
|
EDITION="standard"
|
||||||
|
if [[ "$APP" == "tesm-license" ]]; then
|
||||||
|
if [[ -d "$REPO_DIR/$MASTER_TEILPAKET" ]]; then EDITION="master"; else EDITION="sub"; fi
|
||||||
|
fi
|
||||||
# Bei einer Aktualisierung gilt zuerst, was schon eingerichtet ist. Sonst
|
# Bei einer Aktualisierung gilt zuerst, was schon eingerichtet ist. Sonst
|
||||||
# verschiebt ein Update ohne Portangaben die Anwendung: eine benannte Instanz
|
# verschiebt ein Update ohne Portangaben die Anwendung: eine benannte Instanz
|
||||||
# faellt auf 8080/8443 zurueck, auch wenn sie seit der Installation auf Port 80
|
# faellt auf 8080/8443 zurueck, auch wenn sie seit der Installation auf Port 80
|
||||||
# lief -- und ein Reverse Proxy davor zeigt dann ins Leere. Dieselbe Regel wie
|
# lief -- und ein Reverse Proxy davor zeigt dann ins Leere. Dieselbe Regel wie
|
||||||
# beim Secure-Flag weiter unten: eine Aktualisierung nimmt nichts weg, was
|
# beim Secure-Flag weiter unten: eine Aktualisierung nimmt nichts weg, was
|
||||||
# nicht ausdruecklich neu gesetzt wird.
|
# nicht ausdruecklich neu gesetzt wird.
|
||||||
ENV_FILE="/etc/tesm/$NAME.env"
|
ENV_FILE="$ETC_TESM/$NAME.env"
|
||||||
if [[ -z "$PORT" && -f "$ENV_FILE" ]]; then
|
if [[ -z "$PORT" && -f "$ENV_FILE" ]]; then
|
||||||
PORT="$(sed -n "s/^${ENV_PREFIX}BIND=127\.0\.0\.1:\([0-9]\{1,5\}\)$/\1/p" "$ENV_FILE" | head -1)"
|
PORT="$(sed -n "s/^${ENV_PREFIX}BIND=127\.0\.0\.1:\([0-9]\{1,5\}\)$/\1/p" "$ENV_FILE" | head -1)"
|
||||||
fi
|
fi
|
||||||
@@ -127,10 +197,30 @@ if [[ -f "$NGINX_SITE" ]]; then
|
|||||||
[[ -z "$HTTPS_PORT" ]] && HTTPS_PORT="$(sed -n 's/^[[:space:]]*listen[[:space:]]\([0-9]\{1,5\}\)[[:space:]]\+ssl.*/\1/p' "$NGINX_SITE" | head -1)"
|
[[ -z "$HTTPS_PORT" ]] && HTTPS_PORT="$(sed -n 's/^[[:space:]]*listen[[:space:]]\([0-9]\{1,5\}\)[[:space:]]\+ssl.*/\1/p' "$NGINX_SITE" | head -1)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
port_belegt() {
|
||||||
|
local p="$1"
|
||||||
|
# Schon von einer anderen Installation vergeben? Die Umgebungsdateien sind
|
||||||
|
# die Wahrheit -- ein Dienst kann gerade gestoppt sein und den Port trotzdem
|
||||||
|
# beanspruchen.
|
||||||
|
grep -qs "BIND=127\.0\.0\.1:$p\$" "$ETC_TESM"/*.env 2>/dev/null &&
|
||||||
|
return 0
|
||||||
|
# Oder gerade in Benutzung, von was auch immer.
|
||||||
|
command -v ss >/dev/null 2>&1 && [[ -n "$(ss -Hltn "sport = :$p" 2>/dev/null)" ]] && return 0
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
if [[ -z "$PORT" ]]; then
|
if [[ -z "$PORT" ]]; then
|
||||||
PORT=5000
|
BASISPORT=5000
|
||||||
[[ "$APP" == "tesm-license" ]] && PORT=5001
|
[[ "$APP" == "tesm-license" ]] && BASISPORT=5001
|
||||||
[[ -n "$INSTANCE" ]] && PORT=$((PORT + 100))
|
if [[ -n "$INSTANCE" ]]; then
|
||||||
|
# Frueher bekam **jede** Instanz Basis+100. Die zweite startete nie
|
||||||
|
# ("Address already in use") -- und die Gesundheitspruefung merkte es
|
||||||
|
# nicht, weil die erste auf demselben Port antwortete.
|
||||||
|
PORT=$((BASISPORT + 100))
|
||||||
|
while port_belegt "$PORT"; do PORT=$((PORT + 1)); done
|
||||||
|
else
|
||||||
|
PORT=$BASISPORT
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
if [[ -z "$HTTP_PORT" ]]; then
|
if [[ -z "$HTTP_PORT" ]]; then
|
||||||
HTTP_PORT=80
|
HTTP_PORT=80
|
||||||
@@ -212,12 +302,24 @@ fi
|
|||||||
# Der Vorgaenger lief als root. Hier laeuft der Dienst unprivilegiert; Rootrechte
|
# Der Vorgaenger lief als root. Hier laeuft der Dienst unprivilegiert; Rootrechte
|
||||||
# gibt es ausschliesslich ueber den Helfer.
|
# gibt es ausschliesslich ueber den Helfer.
|
||||||
|
|
||||||
|
# Ein Sub-Paket auf eine Master-Installation waere ein stiller Rueckbau: der
|
||||||
|
# Abgleich unten laeuft mit --delete und wuerde das Teilpaket entfernen. Danach
|
||||||
|
# gaebe es auf dem Master keinen Weg mehr, eine Master-Lizenz auszustellen --
|
||||||
|
# und niemand haette es gesagt.
|
||||||
|
if [[ "$EDITION" == "sub" && -d "$ROOT/$MASTER_TEILPAKET" ]]; then
|
||||||
|
fail "Hier laeuft die Master-Ausgabe, dieses Paket ist die Ausgabe fuer Sub-Lizenzserver.
|
||||||
|
Eine Aktualisierung damit wuerde die Master-Ausgabe entfernen.
|
||||||
|
Passendes Paket: Repository tesm-license-master (privat).
|
||||||
|
Bewusster Rueckbau: $ROOT/$MASTER_TEILPAKET von Hand entfernen, dann erneut."
|
||||||
|
fi
|
||||||
|
|
||||||
# -- Backup ------------------------------------------------------------------
|
# -- Backup ------------------------------------------------------------------
|
||||||
if [[ $IS_UPDATE -eq 1 ]]; then
|
if [[ $IS_UPDATE -eq 1 ]]; then
|
||||||
step "Sicherung vor der Aktualisierung"
|
step "Sicherung vor der Aktualisierung"
|
||||||
systemctl stop "$UNIT" 2>/dev/null || true
|
systemctl stop "$UNIT" 2>/dev/null || true
|
||||||
[[ "$APP" == "tesm" ]] && systemctl stop "$MONITOR_UNIT" 2>/dev/null || true
|
[[ "$APP" == "tesm" ]] && systemctl stop "$MONITOR_UNIT" 2>/dev/null || true
|
||||||
cp -a "$ROOT" "$BACKUP_DIR"
|
cp -a "$ROOT" "$BACKUP_DIR"
|
||||||
|
SICHERUNG_STEHT=1
|
||||||
info "Sicherung: $BACKUP_DIR"
|
info "Sicherung: $BACKUP_DIR"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -347,8 +449,8 @@ step "Umgebung"
|
|||||||
# EnvironmentFile, die Kommandozeilenwerkzeuge ueber ihren Wrapper. Ohne das
|
# EnvironmentFile, die Kommandozeilenwerkzeuge ueber ihren Wrapper. Ohne das
|
||||||
# wuesste "tesm-admin" nicht, wo die Instanz liegt, und wuerde sich eine leere
|
# wuesste "tesm-admin" nicht, wo die Instanz liegt, und wuerde sich eine leere
|
||||||
# Datenbank im Arbeitsverzeichnis anlegen.
|
# Datenbank im Arbeitsverzeichnis anlegen.
|
||||||
install -d -o root -g root -m 0755 /etc/tesm
|
install -d -o root -g root -m 0755 "$ETC_TESM"
|
||||||
ENV_FILE="/etc/tesm/$NAME.env"
|
ENV_FILE="$ETC_TESM/$NAME.env"
|
||||||
TMP_ENV="$(mktemp)"
|
TMP_ENV="$(mktemp)"
|
||||||
{
|
{
|
||||||
echo "# Von install.sh erzeugt fuer $NAME -- nicht von Hand bearbeiten."
|
echo "# Von install.sh erzeugt fuer $NAME -- nicht von Hand bearbeiten."
|
||||||
@@ -491,7 +593,11 @@ fi
|
|||||||
step "Gesundheitspruefung"
|
step "Gesundheitspruefung"
|
||||||
HEALTHY=0
|
HEALTHY=0
|
||||||
for attempt in $(seq 1 20); do
|
for attempt in $(seq 1 20); do
|
||||||
if curl -fsS --max-time 3 "http://127.0.0.1:$PORT/gesundheit" >/dev/null 2>&1; then
|
# Beides: der Dienst muss laufen **und** antworten. Nur die Antwort zu
|
||||||
|
# pruefen ging schon einmal schief -- auf einem gemeinsamen Port antwortete
|
||||||
|
# eine andere Instanz, und ein toter Dienst galt als gesund.
|
||||||
|
if systemctl is-active --quiet "$UNIT" &&
|
||||||
|
curl -fsS --max-time 3 "http://127.0.0.1:$PORT/gesundheit" >/dev/null 2>&1; then
|
||||||
HEALTHY=1
|
HEALTHY=1
|
||||||
info "Antwort nach ${attempt}x"
|
info "Antwort nach ${attempt}x"
|
||||||
break
|
break
|
||||||
@@ -502,13 +608,9 @@ done
|
|||||||
if [[ $HEALTHY -eq 0 ]]; then
|
if [[ $HEALTHY -eq 0 ]]; then
|
||||||
warn "Die Anwendung antwortet nicht."
|
warn "Die Anwendung antwortet nicht."
|
||||||
if [[ $IS_UPDATE -eq 1 ]]; then
|
if [[ $IS_UPDATE -eq 1 ]]; then
|
||||||
warn "Rollback wird ausgefuehrt."
|
# Dieselbe Funktion wie die EXIT-Falle -- zwei Wege, die dasselbe tun
|
||||||
systemctl stop "$UNIT" 2>/dev/null || true
|
# sollen, laufen sonst irgendwann auseinander.
|
||||||
[[ "$APP" == "tesm" ]] && systemctl stop "$MONITOR_UNIT" 2>/dev/null || true
|
zurueckrollen_install
|
||||||
mv "$ROOT" "$FAILED_DIR"
|
|
||||||
cp -a "$BACKUP_DIR" "$ROOT"
|
|
||||||
systemctl start "$UNIT" || true
|
|
||||||
[[ "$APP" == "tesm" ]] && systemctl start "$MONITOR_UNIT" || true
|
|
||||||
fail "Aktualisierung fehlgeschlagen. Alter Stand wiederhergestellt.
|
fail "Aktualisierung fehlgeschlagen. Alter Stand wiederhergestellt.
|
||||||
Fehlgeschlagene Version : $FAILED_DIR
|
Fehlgeschlagene Version : $FAILED_DIR
|
||||||
Sicherung : $BACKUP_DIR
|
Sicherung : $BACKUP_DIR
|
||||||
@@ -568,12 +670,45 @@ if [[ $HTTPS -eq 1 ]]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# -- Lizenzserver einrichten -------------------------------------------------
|
||||||
|
# Bei einer Neuinstallation: Signaturschluessel anlegen (braucht jeder
|
||||||
|
# Lizenzserver, um Kundenlizenzen zu signieren) und in der Master-Ausgabe
|
||||||
|
# gleich die eigene Master-Lizenz. Bei einer Aktualisierung nichts davon --
|
||||||
|
# ein bestehender Schluessel darf nie angetastet werden.
|
||||||
|
if [[ "$APP" == "tesm-license" && $IS_UPDATE -eq 0 ]]; then
|
||||||
|
# Das Datenverzeichnis steht in der Umgebungsdatei -- es muss nicht
|
||||||
|
# $ROOT/data sein, wenn jemand es dort umgestellt hat.
|
||||||
|
DATEN_DIR="$(sed -n "s/^${ENV_PREFIX}DATA_DIR=//p" "$ENV_FILE" | tail -1)"
|
||||||
|
DATEN_DIR="${DATEN_DIR:-$ROOT/data}"
|
||||||
|
step "Signaturschluessel"
|
||||||
|
if sudo -u "$SERVICE_USER" "/usr/local/bin/$NAME-admin" init-key; then
|
||||||
|
info "Bitte die Datei $DATEN_DIR/master_signing_key.json SOFORT sichern."
|
||||||
|
else
|
||||||
|
fail "Der Signaturschluessel liess sich nicht erzeugen. Ohne ihn kann dieser Server
|
||||||
|
keine Lizenz ausstellen. Protokoll: journalctl -u $UNIT -n 50"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$EDITION" == "master" ]]; then
|
||||||
|
step "Eigene Master-Lizenz"
|
||||||
|
if [[ -s "$DATEN_DIR/license.json" ]]; then
|
||||||
|
info "Es liegt schon eine Lizenzdatei -- unangetastet."
|
||||||
|
elif sudo -u "$SERVICE_USER" "/usr/local/bin/$NAME-admin" bootstrap; then
|
||||||
|
systemctl restart "$UNIT"
|
||||||
|
info "Dienst neu gestartet, damit die Lizenz gelesen wird."
|
||||||
|
else
|
||||||
|
fail "Die Master-Lizenz liess sich nicht ausstellen.
|
||||||
|
Nachtraeglich: sudo -u $SERVICE_USER $NAME-admin bootstrap"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ $IS_UPDATE -eq 1 ]]; then
|
if [[ $IS_UPDATE -eq 1 ]]; then
|
||||||
rm -rf "$BACKUP_DIR"
|
rm -rf "$BACKUP_DIR"
|
||||||
info "Sicherung entfernt (Aktualisierung erfolgreich)"
|
info "Sicherung entfernt (Aktualisierung erfolgreich)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# -- Abschluss ---------------------------------------------------------------
|
# -- Abschluss ---------------------------------------------------------------
|
||||||
|
INSTALL_FERTIG=1
|
||||||
step "Fertig"
|
step "Fertig"
|
||||||
ADMIN_CMD="tesm-admin"
|
ADMIN_CMD="tesm-admin"
|
||||||
[[ "$APP" == "tesm-license" ]] && ADMIN_CMD="tesm-license-admin"
|
[[ "$APP" == "tesm-license" ]] && ADMIN_CMD="tesm-license-admin"
|
||||||
@@ -589,14 +724,24 @@ cat <<SUMMARY
|
|||||||
Naechste Schritte:
|
Naechste Schritte:
|
||||||
SUMMARY
|
SUMMARY
|
||||||
|
|
||||||
if [[ "$APP" == "tesm-license" ]]; then
|
if [[ "$APP" == "tesm-license" && "$EDITION" == "master" ]]; then
|
||||||
cat <<SUMMARY
|
cat <<SUMMARY
|
||||||
1. Signaturschluessel erzeugen und sofort sichern:
|
1. Administrator anlegen:
|
||||||
sudo -u $SERVICE_USER $NAME-admin init-key
|
|
||||||
2. Eigene Master-Lizenz ausstellen:
|
|
||||||
sudo -u $SERVICE_USER $NAME-admin bootstrap
|
|
||||||
3. Administrator anlegen:
|
|
||||||
sudo -u $SERVICE_USER $NAME-admin create-admin
|
sudo -u $SERVICE_USER $NAME-admin create-admin
|
||||||
|
2. Signaturschluessel sichern -- ohne ihn ist keine ausgestellte Lizenz
|
||||||
|
mehr pruefbar:
|
||||||
|
$ROOT/data/master_signing_key.json
|
||||||
|
SUMMARY
|
||||||
|
elif [[ "$APP" == "tesm-license" ]]; then
|
||||||
|
cat <<SUMMARY
|
||||||
|
1. Administrator anlegen:
|
||||||
|
sudo -u $SERVICE_USER $NAME-admin create-admin
|
||||||
|
2. Beim Master-Lizenzserver eine Lizenz fuer diesen Server anfordern und
|
||||||
|
unter "Eigene Lizenz" einspielen. Diese Ausgabe ist ein Sub-Lizenzserver
|
||||||
|
und stellt keine eigene Master-Lizenz aus.
|
||||||
|
3. Signaturschluessel sichern -- mit ihm signiert dieser Server seine
|
||||||
|
Kundenlizenzen:
|
||||||
|
$ROOT/data/master_signing_key.json
|
||||||
SUMMARY
|
SUMMARY
|
||||||
else
|
else
|
||||||
cat <<SUMMARY
|
cat <<SUMMARY
|
||||||
|
|||||||
+59
-6
@@ -11,6 +11,10 @@
|
|||||||
# Auf einer bestehenden Installation liegt sie schon bereit:
|
# Auf einer bestehenden Installation liegt sie schon bereit:
|
||||||
#
|
#
|
||||||
# sudo bash /srv/tesm/deploy/update.sh tesm
|
# sudo bash /srv/tesm/deploy/update.sh tesm
|
||||||
|
#
|
||||||
|
# Laeuft hier die Master-Ausgabe des Lizenzservers, kommt das Release aus dem
|
||||||
|
# privaten Repositorium tesm-license-master -- dafuer braucht es einen
|
||||||
|
# Zugriffstoken in TESM_RELEASE_TOKEN oder in /etc/tesm-release.token.
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
@@ -31,14 +35,39 @@ EXTRA=("$@")
|
|||||||
exit 2
|
exit 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Repositorium: die Master-Ausgabe des Lizenzservers liegt in einem eigenen,
|
||||||
|
# privaten Repositorium, heisst als Installation aber weiter "tesm-license".
|
||||||
|
# Erkannt wird sie an der Installation und nicht an einem Schalter -- wer
|
||||||
|
# aktualisiert, soll nicht erst wissen muessen, welche Ausgabe hier laeuft.
|
||||||
|
# Sonst holte der Aufruf die Ausgabe fuer Sub-Lizenzserver, und der Installer
|
||||||
|
# muesste sie abweisen.
|
||||||
|
REPO="$APP"
|
||||||
|
if [[ "$APP" == "tesm-license" ]]; then
|
||||||
|
NAME="$APP"
|
||||||
|
for ((i = 0; i < ${#EXTRA[@]}; i++)); do
|
||||||
|
[[ "${EXTRA[i]}" == "--instance" ]] && NAME="$APP-${EXTRA[i + 1]:-}"
|
||||||
|
done
|
||||||
|
# Die Wurzel ist ueberschreibbar, damit ein Test die Erkennung wirklich
|
||||||
|
# ausfuehren kann und nicht nur den Quelltext liest.
|
||||||
|
if [[ -d "${TESM_SRV_ROOT:-/srv}/$NAME/apps/tesm-license/src/tesm_license/master" ]]; then
|
||||||
|
REPO="tesm-license-master"
|
||||||
|
echo "==> Master-Ausgabe erkannt -- Release aus $REPO."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# --8<-- Bootstrap: Release holen --------------------------------------------
|
# --8<-- Bootstrap: Release holen --------------------------------------------
|
||||||
# Wortgleich in update.sh und upgrade.sh; ein Hygienetest vergleicht die beiden
|
# Wortgleich in update.sh und upgrade.sh; ein Hygienetest vergleicht die beiden
|
||||||
# Bloecke. Zwei eigenstaendige Skripte koennen sich keine Datei teilen, und
|
# Bloecke. Zwei eigenstaendige Skripte koennen sich keine Datei teilen, und
|
||||||
# beide sollen als **einzelne Datei** genuegen: auf das Zielsystem legen,
|
# beide sollen als **einzelne Datei** genuegen: auf das Zielsystem legen,
|
||||||
# ausfuehren, fertig -- auf einer 1.2.8 liegt nichts von diesem Projekt.
|
# ausfuehren, fertig -- auf einer 1.2.8 liegt nichts von diesem Projekt.
|
||||||
#
|
#
|
||||||
# Erwartet: $APP und $TAG. Setzt: $TAG (aufgeloest) und $SRC (entpacktes
|
# Erwartet: $APP und $TAG, wahlweise $REPO (Vorgabe: $APP). Setzt: $TAG
|
||||||
# Release). Raeumt sein Arbeitsverzeichnis beim Beenden auf.
|
# (aufgeloest) und $SRC (entpacktes Release). Raeumt sein Arbeitsverzeichnis
|
||||||
|
# beim Beenden auf.
|
||||||
|
#
|
||||||
|
# $REPO gibt es, weil Installationsname und Repositorium auseinanderfallen
|
||||||
|
# koennen: die Master-Ausgabe des Lizenzservers liegt in
|
||||||
|
# "tesm-license-master", die Installation heisst weiter "tesm-license".
|
||||||
#
|
#
|
||||||
# Zwei Fallen des Vorgaengers sind hier vermieden:
|
# Zwei Fallen des Vorgaengers sind hier vermieden:
|
||||||
# * Der Tag "latest" wird ueber die API aufgeloest, nie direkt in die
|
# * Der Tag "latest" wird ueber die API aufgeloest, nie direkt in die
|
||||||
@@ -48,6 +77,15 @@ EXTRA=("$@")
|
|||||||
BASE="${TESM_RELEASE_BASE:-https://gitea.int.eertmoed.net}"
|
BASE="${TESM_RELEASE_BASE:-https://gitea.int.eertmoed.net}"
|
||||||
OWNER="${TESM_RELEASE_OWNER:-alientim}"
|
OWNER="${TESM_RELEASE_OWNER:-alientim}"
|
||||||
|
|
||||||
|
# Ein privates Repositorium -- tesm-license-master -- gibt sein Release nur mit
|
||||||
|
# Zugriffstoken heraus. Reihenfolge: Umgebungsvariable, dann eine Datei, die
|
||||||
|
# der Betreiber selbst anlegt. Der Installer legt sie nie an: ob ein Token auf
|
||||||
|
# dem Server liegt, ist keine Entscheidung eines Skripts.
|
||||||
|
TOKEN="${TESM_RELEASE_TOKEN:-}"
|
||||||
|
if [[ -z "$TOKEN" && -r /etc/tesm-release.token ]]; then
|
||||||
|
TOKEN="$(tr -d '[:space:]' < /etc/tesm-release.token)"
|
||||||
|
fi
|
||||||
|
|
||||||
# Ein Wagenruecklauf im Paket macht jedes Shell-Skript auf dem Ziel unbrauchbar
|
# Ein Wagenruecklauf im Paket macht jedes Shell-Skript auf dem Ziel unbrauchbar
|
||||||
# ("bad interpreter"). Die Pruefung darf dabei nicht selbst von einem
|
# ("bad interpreter"). Die Pruefung darf dabei nicht selbst von einem
|
||||||
# Zusatzpaket abhaengen: mit "file" -- das auf einem schlanken Server fehlt --
|
# Zusatzpaket abhaengen: mit "file" -- das auf einem schlanken Server fehlt --
|
||||||
@@ -63,11 +101,24 @@ hat_crlf() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
BOOTSTRAP_WORK=""
|
BOOTSTRAP_WORK=""
|
||||||
bootstrap_aufraeumen() { [[ -n "$BOOTSTRAP_WORK" ]] && rm -rf "$BOOTSTRAP_WORK"; }
|
bootstrap_aufraeumen() {
|
||||||
|
# Den Ausstiegscode zuerst festhalten: alles danach ueberschreibt ihn.
|
||||||
|
local code=$?
|
||||||
|
[[ -n "$BOOTSTRAP_WORK" ]] && rm -rf "$BOOTSTRAP_WORK"
|
||||||
|
# Wer einen eigenen Abschluss braucht -- etwa einen Rueckweg nach einem
|
||||||
|
# Fehlschlag --, definiert "beim_beenden". Ein zweiter "trap ... EXIT" waere
|
||||||
|
# der falsche Weg: er wuerde diesen hier ersetzen, und das entpackte Release
|
||||||
|
# bliebe unter /tmp liegen.
|
||||||
|
declare -F beim_beenden >/dev/null 2>&1 && beim_beenden "$code"
|
||||||
|
return "$code"
|
||||||
|
}
|
||||||
trap bootstrap_aufraeumen EXIT
|
trap bootstrap_aufraeumen EXIT
|
||||||
|
|
||||||
release_holen() {
|
release_holen() {
|
||||||
local repo="${TESM_RELEASE_REPO:-$APP}" asset
|
local repo="${TESM_RELEASE_REPO:-${REPO:-$APP}}" asset
|
||||||
|
# Leeres Array und "set -u" vertragen sich nur mit dieser Schreibweise.
|
||||||
|
local -a kopf=()
|
||||||
|
[[ -n "$TOKEN" ]] && kopf=(-H "Authorization: token $TOKEN")
|
||||||
for werkzeug in curl tar python3 find; do
|
for werkzeug in curl tar python3 find; do
|
||||||
command -v "$werkzeug" >/dev/null 2>&1 || {
|
command -v "$werkzeug" >/dev/null 2>&1 || {
|
||||||
echo "Auf diesem System fehlt $werkzeug -- ohne das geht es nicht." >&2
|
echo "Auf diesem System fehlt $werkzeug -- ohne das geht es nicht." >&2
|
||||||
@@ -78,9 +129,11 @@ release_holen() {
|
|||||||
|
|
||||||
if [[ "$TAG" == "latest" ]]; then
|
if [[ "$TAG" == "latest" ]]; then
|
||||||
echo "==> Ermittle aktuellen Release-Tag ..."
|
echo "==> Ermittle aktuellen Release-Tag ..."
|
||||||
TAG="$(curl -fsS "$BASE/api/v1/repos/$OWNER/$repo/releases/latest" \
|
TAG="$(curl -fsS ${kopf[0]+"${kopf[@]}"} "$BASE/api/v1/repos/$OWNER/$repo/releases/latest" \
|
||||||
| python3 -c 'import json,sys; print(json.load(sys.stdin)["tag_name"])')" || {
|
| python3 -c 'import json,sys; print(json.load(sys.stdin)["tag_name"])')" || {
|
||||||
echo "Kein Release gefunden unter $BASE/$OWNER/$repo." >&2
|
echo "Kein Release gefunden unter $BASE/$OWNER/$repo." >&2
|
||||||
|
[[ -z "$TOKEN" ]] &&
|
||||||
|
echo "Ist das Repositorium privat? Dann TESM_RELEASE_TOKEN setzen." >&2
|
||||||
echo "Andere Quelle: TESM_RELEASE_BASE, TESM_RELEASE_OWNER, TESM_RELEASE_REPO." >&2
|
echo "Andere Quelle: TESM_RELEASE_BASE, TESM_RELEASE_OWNER, TESM_RELEASE_REPO." >&2
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@@ -89,7 +142,7 @@ release_holen() {
|
|||||||
|
|
||||||
asset="$repo-$TAG.tar.gz"
|
asset="$repo-$TAG.tar.gz"
|
||||||
echo "==> Lade $asset ..."
|
echo "==> Lade $asset ..."
|
||||||
curl -fsSL -o "$BOOTSTRAP_WORK/$asset" \
|
curl -fsSL ${kopf[0]+"${kopf[@]}"} -o "$BOOTSTRAP_WORK/$asset" \
|
||||||
"$BASE/$OWNER/$repo/releases/download/$TAG/$asset" || {
|
"$BASE/$OWNER/$repo/releases/download/$TAG/$asset" || {
|
||||||
echo "Download fehlgeschlagen: $BASE/$OWNER/$repo/releases/download/$TAG/$asset" >&2
|
echo "Download fehlgeschlagen: $BASE/$OWNER/$repo/releases/download/$TAG/$asset" >&2
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
+60
-13
@@ -53,6 +53,11 @@ STOP_OLD=1
|
|||||||
ADOPT_KEY=1
|
ADOPT_KEY=1
|
||||||
TAG="latest"
|
TAG="latest"
|
||||||
ROLLBACK=""
|
ROLLBACK=""
|
||||||
|
|
||||||
|
#: Steht die Sicherung schon, und ist der Lauf durch? Daran entscheidet
|
||||||
|
#: die EXIT-Falle, ob sie zurueckrollt.
|
||||||
|
SICHERUNG_STEHT=0
|
||||||
|
UPGRADE_FERTIG=0
|
||||||
#: Namenszusatz, unter dem ``rename.sh`` eine verdraengte Installation der
|
#: Namenszusatz, unter dem ``rename.sh`` eine verdraengte Installation der
|
||||||
#: Fassung 1.x ablegt. Beide Skripte muessen denselben Wert benutzen; ein
|
#: Fassung 1.x ablegt. Beide Skripte muessen denselben Wert benutzen; ein
|
||||||
#: Hygienetest vergleicht sie.
|
#: Hygienetest vergleicht sie.
|
||||||
@@ -252,6 +257,20 @@ zurueckrollen() {
|
|||||||
info "Zustand pruefen: systemctl status ${hinweis:-$APP.service}"
|
info "Zustand pruefen: systemctl status ${hinweis:-$APP.service}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Wird von der EXIT-Falle des Bootstraps aufgerufen -- mit dem Ausstiegscode.
|
||||||
|
# Damit gilt der Rueckweg fuer **jeden** Fehlschlag und nicht nur fuer den, an
|
||||||
|
# den ich gedacht habe: Export, Uebersetzen, Einspielen, nginx, ein Ctrl-C.
|
||||||
|
beim_beenden() {
|
||||||
|
local code="${1:-0}"
|
||||||
|
[[ "$code" == "0" ]] && return 0
|
||||||
|
[[ $UPGRADE_FERTIG -eq 1 ]] && return 0
|
||||||
|
if [[ $SICHERUNG_STEHT -eq 0 ]]; then
|
||||||
|
warn "Abgebrochen (Code $code), bevor eine Sicherung stand -- nichts zurueckzurollen."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
zurueckrollen "$BACKUP" "Abgebrochen (Code $code)."
|
||||||
|
}
|
||||||
|
|
||||||
neueste_sicherung() {
|
neueste_sicherung() {
|
||||||
local kandidat
|
local kandidat
|
||||||
kandidat="$(ls -1dt /srv/"$NAME"-upgrade-*/ 2>/dev/null | head -1)"
|
kandidat="$(ls -1dt /srv/"$NAME"-upgrade-*/ 2>/dev/null | head -1)"
|
||||||
@@ -286,8 +305,13 @@ fi
|
|||||||
# beide sollen als **einzelne Datei** genuegen: auf das Zielsystem legen,
|
# beide sollen als **einzelne Datei** genuegen: auf das Zielsystem legen,
|
||||||
# ausfuehren, fertig -- auf einer 1.2.8 liegt nichts von diesem Projekt.
|
# ausfuehren, fertig -- auf einer 1.2.8 liegt nichts von diesem Projekt.
|
||||||
#
|
#
|
||||||
# Erwartet: $APP und $TAG. Setzt: $TAG (aufgeloest) und $SRC (entpacktes
|
# Erwartet: $APP und $TAG, wahlweise $REPO (Vorgabe: $APP). Setzt: $TAG
|
||||||
# Release). Raeumt sein Arbeitsverzeichnis beim Beenden auf.
|
# (aufgeloest) und $SRC (entpacktes Release). Raeumt sein Arbeitsverzeichnis
|
||||||
|
# beim Beenden auf.
|
||||||
|
#
|
||||||
|
# $REPO gibt es, weil Installationsname und Repositorium auseinanderfallen
|
||||||
|
# koennen: die Master-Ausgabe des Lizenzservers liegt in
|
||||||
|
# "tesm-license-master", die Installation heisst weiter "tesm-license".
|
||||||
#
|
#
|
||||||
# Zwei Fallen des Vorgaengers sind hier vermieden:
|
# Zwei Fallen des Vorgaengers sind hier vermieden:
|
||||||
# * Der Tag "latest" wird ueber die API aufgeloest, nie direkt in die
|
# * Der Tag "latest" wird ueber die API aufgeloest, nie direkt in die
|
||||||
@@ -297,6 +321,15 @@ fi
|
|||||||
BASE="${TESM_RELEASE_BASE:-https://gitea.int.eertmoed.net}"
|
BASE="${TESM_RELEASE_BASE:-https://gitea.int.eertmoed.net}"
|
||||||
OWNER="${TESM_RELEASE_OWNER:-alientim}"
|
OWNER="${TESM_RELEASE_OWNER:-alientim}"
|
||||||
|
|
||||||
|
# Ein privates Repositorium -- tesm-license-master -- gibt sein Release nur mit
|
||||||
|
# Zugriffstoken heraus. Reihenfolge: Umgebungsvariable, dann eine Datei, die
|
||||||
|
# der Betreiber selbst anlegt. Der Installer legt sie nie an: ob ein Token auf
|
||||||
|
# dem Server liegt, ist keine Entscheidung eines Skripts.
|
||||||
|
TOKEN="${TESM_RELEASE_TOKEN:-}"
|
||||||
|
if [[ -z "$TOKEN" && -r /etc/tesm-release.token ]]; then
|
||||||
|
TOKEN="$(tr -d '[:space:]' < /etc/tesm-release.token)"
|
||||||
|
fi
|
||||||
|
|
||||||
# Ein Wagenruecklauf im Paket macht jedes Shell-Skript auf dem Ziel unbrauchbar
|
# Ein Wagenruecklauf im Paket macht jedes Shell-Skript auf dem Ziel unbrauchbar
|
||||||
# ("bad interpreter"). Die Pruefung darf dabei nicht selbst von einem
|
# ("bad interpreter"). Die Pruefung darf dabei nicht selbst von einem
|
||||||
# Zusatzpaket abhaengen: mit "file" -- das auf einem schlanken Server fehlt --
|
# Zusatzpaket abhaengen: mit "file" -- das auf einem schlanken Server fehlt --
|
||||||
@@ -312,11 +345,24 @@ hat_crlf() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
BOOTSTRAP_WORK=""
|
BOOTSTRAP_WORK=""
|
||||||
bootstrap_aufraeumen() { [[ -n "$BOOTSTRAP_WORK" ]] && rm -rf "$BOOTSTRAP_WORK"; }
|
bootstrap_aufraeumen() {
|
||||||
|
# Den Ausstiegscode zuerst festhalten: alles danach ueberschreibt ihn.
|
||||||
|
local code=$?
|
||||||
|
[[ -n "$BOOTSTRAP_WORK" ]] && rm -rf "$BOOTSTRAP_WORK"
|
||||||
|
# Wer einen eigenen Abschluss braucht -- etwa einen Rueckweg nach einem
|
||||||
|
# Fehlschlag --, definiert "beim_beenden". Ein zweiter "trap ... EXIT" waere
|
||||||
|
# der falsche Weg: er wuerde diesen hier ersetzen, und das entpackte Release
|
||||||
|
# bliebe unter /tmp liegen.
|
||||||
|
declare -F beim_beenden >/dev/null 2>&1 && beim_beenden "$code"
|
||||||
|
return "$code"
|
||||||
|
}
|
||||||
trap bootstrap_aufraeumen EXIT
|
trap bootstrap_aufraeumen EXIT
|
||||||
|
|
||||||
release_holen() {
|
release_holen() {
|
||||||
local repo="${TESM_RELEASE_REPO:-$APP}" asset
|
local repo="${TESM_RELEASE_REPO:-${REPO:-$APP}}" asset
|
||||||
|
# Leeres Array und "set -u" vertragen sich nur mit dieser Schreibweise.
|
||||||
|
local -a kopf=()
|
||||||
|
[[ -n "$TOKEN" ]] && kopf=(-H "Authorization: token $TOKEN")
|
||||||
for werkzeug in curl tar python3 find; do
|
for werkzeug in curl tar python3 find; do
|
||||||
command -v "$werkzeug" >/dev/null 2>&1 || {
|
command -v "$werkzeug" >/dev/null 2>&1 || {
|
||||||
echo "Auf diesem System fehlt $werkzeug -- ohne das geht es nicht." >&2
|
echo "Auf diesem System fehlt $werkzeug -- ohne das geht es nicht." >&2
|
||||||
@@ -327,9 +373,11 @@ release_holen() {
|
|||||||
|
|
||||||
if [[ "$TAG" == "latest" ]]; then
|
if [[ "$TAG" == "latest" ]]; then
|
||||||
echo "==> Ermittle aktuellen Release-Tag ..."
|
echo "==> Ermittle aktuellen Release-Tag ..."
|
||||||
TAG="$(curl -fsS "$BASE/api/v1/repos/$OWNER/$repo/releases/latest" \
|
TAG="$(curl -fsS ${kopf[0]+"${kopf[@]}"} "$BASE/api/v1/repos/$OWNER/$repo/releases/latest" \
|
||||||
| python3 -c 'import json,sys; print(json.load(sys.stdin)["tag_name"])')" || {
|
| python3 -c 'import json,sys; print(json.load(sys.stdin)["tag_name"])')" || {
|
||||||
echo "Kein Release gefunden unter $BASE/$OWNER/$repo." >&2
|
echo "Kein Release gefunden unter $BASE/$OWNER/$repo." >&2
|
||||||
|
[[ -z "$TOKEN" ]] &&
|
||||||
|
echo "Ist das Repositorium privat? Dann TESM_RELEASE_TOKEN setzen." >&2
|
||||||
echo "Andere Quelle: TESM_RELEASE_BASE, TESM_RELEASE_OWNER, TESM_RELEASE_REPO." >&2
|
echo "Andere Quelle: TESM_RELEASE_BASE, TESM_RELEASE_OWNER, TESM_RELEASE_REPO." >&2
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@@ -338,7 +386,7 @@ release_holen() {
|
|||||||
|
|
||||||
asset="$repo-$TAG.tar.gz"
|
asset="$repo-$TAG.tar.gz"
|
||||||
echo "==> Lade $asset ..."
|
echo "==> Lade $asset ..."
|
||||||
curl -fsSL -o "$BOOTSTRAP_WORK/$asset" \
|
curl -fsSL ${kopf[0]+"${kopf[@]}"} -o "$BOOTSTRAP_WORK/$asset" \
|
||||||
"$BASE/$OWNER/$repo/releases/download/$TAG/$asset" || {
|
"$BASE/$OWNER/$repo/releases/download/$TAG/$asset" || {
|
||||||
echo "Download fehlgeschlagen: $BASE/$OWNER/$repo/releases/download/$TAG/$asset" >&2
|
echo "Download fehlgeschlagen: $BASE/$OWNER/$repo/releases/download/$TAG/$asset" >&2
|
||||||
return 1
|
return 1
|
||||||
@@ -514,6 +562,7 @@ for unit in "${ALTE_UNITS[@]}"; do
|
|||||||
done
|
done
|
||||||
[[ -L "/etc/nginx/sites-enabled/$NAME" ]] && printf 'site-enabled %s\n' "$NAME" >> "$SYSTEM_KOPIE/manifest"
|
[[ -L "/etc/nginx/sites-enabled/$NAME" ]] && printf 'site-enabled %s\n' "$NAME" >> "$SYSTEM_KOPIE/manifest"
|
||||||
info "$(grep -c . "$SYSTEM_KOPIE/manifest") Systemdatei(en) und Zustaende vermerkt"
|
info "$(grep -c . "$SYSTEM_KOPIE/manifest") Systemdatei(en) und Zustaende vermerkt"
|
||||||
|
SICHERUNG_STEHT=1
|
||||||
|
|
||||||
# -- 2. Altes System abschalten ---------------------------------------------- #
|
# -- 2. Altes System abschalten ---------------------------------------------- #
|
||||||
if [[ $STOP_OLD -eq 1 ]]; then
|
if [[ $STOP_OLD -eq 1 ]]; then
|
||||||
@@ -532,15 +581,12 @@ fi
|
|||||||
step "Neu installieren"
|
step "Neu installieren"
|
||||||
INSTALL_ARGS=(--app "$APP" --yes)
|
INSTALL_ARGS=(--app "$APP" --yes)
|
||||||
[[ -n "$INSTANCE" ]] && INSTALL_ARGS+=(--instance "$INSTANCE")
|
[[ -n "$INSTANCE" ]] && INSTALL_ARGS+=(--instance "$INSTANCE")
|
||||||
if ! bash "$SRC/deploy/install.sh" "${INSTALL_ARGS[@]}" ${EXTRA[0]+"${EXTRA[@]}"}; then
|
# Kein eigener Fehlerzweig mehr: die EXIT-Falle rollt bei jedem Fehlschlag
|
||||||
# Hier stand das System bisher halb umgebaut da: alter Dienst aus, neuer
|
# zurueck -- hier, beim Export, beim Einspielen oder bei einem Ctrl-C.
|
||||||
# nicht vorhanden. Der Rueckweg bringt es in den Zustand von vorher, damit
|
bash "$SRC/deploy/install.sh" "${INSTALL_ARGS[@]}" ${EXTRA[0]+"${EXTRA[@]}"}
|
||||||
# ein zweiter Versuch nach der Ursache moeglich ist.
|
|
||||||
zurueckrollen "$BACKUP" "Die Installation ist fehlgeschlagen."
|
|
||||||
fail "Upgrade abgebrochen und zurueckgerollt. Ursache oben nachsehen, dann erneut versuchen."
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ $WITH_DATA -eq 0 ]]; then
|
if [[ $WITH_DATA -eq 0 ]]; then
|
||||||
|
UPGRADE_FERTIG=1
|
||||||
step "Fertig -- ohne Datenuebernahme"
|
step "Fertig -- ohne Datenuebernahme"
|
||||||
info "Sicherung der alten Installation: $ALT_KOPIE"
|
info "Sicherung der alten Installation: $ALT_KOPIE"
|
||||||
exit 0
|
exit 0
|
||||||
@@ -592,6 +638,7 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# -- Abschluss --------------------------------------------------------------- #
|
# -- Abschluss --------------------------------------------------------------- #
|
||||||
|
UPGRADE_FERTIG=1
|
||||||
step "Fertig"
|
step "Fertig"
|
||||||
info "Sicherung der alten Installation : $ALT_KOPIE"
|
info "Sicherung der alten Installation : $ALT_KOPIE"
|
||||||
if [[ "$APP" == "tesm" ]]; then
|
if [[ "$APP" == "tesm" ]]; then
|
||||||
|
|||||||
+181
@@ -0,0 +1,181 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================================
|
||||||
|
# Installation -- der kurze Weg.
|
||||||
|
#
|
||||||
|
# Diese Datei liegt oben im Paket und ruft den Installer unter deploy/ auf:
|
||||||
|
#
|
||||||
|
# tar xzf tesm-vX.Y.Z.tar.gz && cd tesm
|
||||||
|
# sudo bash install.sh --app tesm --https
|
||||||
|
#
|
||||||
|
# Sie genuegt aber auch **allein**: liegt kein Paket um sie herum, holt sie
|
||||||
|
# sich das aktuelle Release selbst.
|
||||||
|
#
|
||||||
|
# curl -fsSLO https://gitea.int.eertmoed.net/alientim/tesm/raw/branch/main/install.sh
|
||||||
|
# sudo bash install.sh --app tesm --https
|
||||||
|
#
|
||||||
|
# Die Master-Ausgabe des Lizenzservers liegt im privaten Repositorium
|
||||||
|
# tesm-license-master. Allein-Betrieb dafuer:
|
||||||
|
#
|
||||||
|
# export TESM_RELEASE_REPO=tesm-license-master
|
||||||
|
# export TESM_RELEASE_TOKEN=<Gitea-Token>
|
||||||
|
# sudo -E bash install.sh --app tesm-license --https
|
||||||
|
#
|
||||||
|
# Aus einem entpackten Paket braucht es das nicht: dort entscheidet, welches
|
||||||
|
# Paket es ist. Die Master-Ausgabe legt beim Installieren Signaturschluessel
|
||||||
|
# und eigene Master-Lizenz selbst an.
|
||||||
|
#
|
||||||
|
# Alle Argumente gehen unveraendert an deploy/install.sh weiter; dessen Hilfe:
|
||||||
|
#
|
||||||
|
# bash install.sh --help
|
||||||
|
# =============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
# --app wird nur gelesen, nicht verbraucht: der Bootstrap braucht es fuer den
|
||||||
|
# Namen des Repositoriums, der Installer bekommt die Argumente unveraendert.
|
||||||
|
APP=""
|
||||||
|
TAG="latest"
|
||||||
|
ARGUMENTE=("$@")
|
||||||
|
for ((i = 0; i < $#; i++)); do
|
||||||
|
case "${ARGUMENTE[i]}" in
|
||||||
|
--app) APP="${ARGUMENTE[i + 1]:-}" ;;
|
||||||
|
--tag) TAG="${ARGUMENTE[i + 1]:-latest}" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Ohne Paket drumherum brauchen wir den Namen, um das richtige Release zu
|
||||||
|
# finden -- die beiden Anwendungen liegen in getrennten Repositorien.
|
||||||
|
if [[ ! -f "$SCRIPT_DIR/deploy/install.sh" ]]; then
|
||||||
|
[[ "$APP" == "tesm" || "$APP" == "tesm-license" ]] || {
|
||||||
|
echo "Diese Datei liegt allein hier und muss sich das Release holen --" >&2
|
||||||
|
echo "dafuer braucht sie --app <tesm|tesm-license>." >&2
|
||||||
|
echo "Verwendung: install.sh --app <tesm|tesm-license> [weitere Optionen]" >&2
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --8<-- Bootstrap: Release holen --------------------------------------------
|
||||||
|
# Wortgleich in update.sh und upgrade.sh; ein Hygienetest vergleicht die beiden
|
||||||
|
# Bloecke. Zwei eigenstaendige Skripte koennen sich keine Datei teilen, und
|
||||||
|
# beide sollen als **einzelne Datei** genuegen: auf das Zielsystem legen,
|
||||||
|
# ausfuehren, fertig -- auf einer 1.2.8 liegt nichts von diesem Projekt.
|
||||||
|
#
|
||||||
|
# Erwartet: $APP und $TAG, wahlweise $REPO (Vorgabe: $APP). Setzt: $TAG
|
||||||
|
# (aufgeloest) und $SRC (entpacktes Release). Raeumt sein Arbeitsverzeichnis
|
||||||
|
# beim Beenden auf.
|
||||||
|
#
|
||||||
|
# $REPO gibt es, weil Installationsname und Repositorium auseinanderfallen
|
||||||
|
# koennen: die Master-Ausgabe des Lizenzservers liegt in
|
||||||
|
# "tesm-license-master", die Installation heisst weiter "tesm-license".
|
||||||
|
#
|
||||||
|
# Zwei Fallen des Vorgaengers sind hier vermieden:
|
||||||
|
# * Der Tag "latest" wird ueber die API aufgeloest, nie direkt in die
|
||||||
|
# Download-URL geschrieben. Gitea liefert sonst mit HTTP 200 ein
|
||||||
|
# automatisch erzeugtes Quell-Snapshot statt des echten Release-Anhangs.
|
||||||
|
# * Nach dem Entpacken wird geprueft, dass ein install.sh existiert.
|
||||||
|
BASE="${TESM_RELEASE_BASE:-https://gitea.int.eertmoed.net}"
|
||||||
|
OWNER="${TESM_RELEASE_OWNER:-alientim}"
|
||||||
|
|
||||||
|
# Ein privates Repositorium -- tesm-license-master -- gibt sein Release nur mit
|
||||||
|
# Zugriffstoken heraus. Reihenfolge: Umgebungsvariable, dann eine Datei, die
|
||||||
|
# der Betreiber selbst anlegt. Der Installer legt sie nie an: ob ein Token auf
|
||||||
|
# dem Server liegt, ist keine Entscheidung eines Skripts.
|
||||||
|
TOKEN="${TESM_RELEASE_TOKEN:-}"
|
||||||
|
if [[ -z "$TOKEN" && -r /etc/tesm-release.token ]]; then
|
||||||
|
TOKEN="$(tr -d '[:space:]' < /etc/tesm-release.token)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ein Wagenruecklauf im Paket macht jedes Shell-Skript auf dem Ziel unbrauchbar
|
||||||
|
# ("bad interpreter"). Die Pruefung darf dabei nicht selbst von einem
|
||||||
|
# Zusatzpaket abhaengen: mit "file" -- das auf einem schlanken Server fehlt --
|
||||||
|
# endete sie in "command not found", grep bekam nichts zu sehen, und das Skript
|
||||||
|
# machte weiter, als waere alles in Ordnung.
|
||||||
|
hat_crlf() {
|
||||||
|
local cr
|
||||||
|
cr="$(printf '\r')"
|
||||||
|
# -U (binaer lesen) ist unter Linux ohne Wirkung, auf einer
|
||||||
|
# Windows-Buildmaschine aber entscheidend: dort entfernt grep den
|
||||||
|
# Wagenruecklauf beim Lesen und findet deshalb nie einen.
|
||||||
|
LC_ALL=C grep -qU "$cr" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOTSTRAP_WORK=""
|
||||||
|
bootstrap_aufraeumen() {
|
||||||
|
# Den Ausstiegscode zuerst festhalten: alles danach ueberschreibt ihn.
|
||||||
|
local code=$?
|
||||||
|
[[ -n "$BOOTSTRAP_WORK" ]] && rm -rf "$BOOTSTRAP_WORK"
|
||||||
|
# Wer einen eigenen Abschluss braucht -- etwa einen Rueckweg nach einem
|
||||||
|
# Fehlschlag --, definiert "beim_beenden". Ein zweiter "trap ... EXIT" waere
|
||||||
|
# der falsche Weg: er wuerde diesen hier ersetzen, und das entpackte Release
|
||||||
|
# bliebe unter /tmp liegen.
|
||||||
|
declare -F beim_beenden >/dev/null 2>&1 && beim_beenden "$code"
|
||||||
|
return "$code"
|
||||||
|
}
|
||||||
|
trap bootstrap_aufraeumen EXIT
|
||||||
|
|
||||||
|
release_holen() {
|
||||||
|
local repo="${TESM_RELEASE_REPO:-${REPO:-$APP}}" asset
|
||||||
|
# Leeres Array und "set -u" vertragen sich nur mit dieser Schreibweise.
|
||||||
|
local -a kopf=()
|
||||||
|
[[ -n "$TOKEN" ]] && kopf=(-H "Authorization: token $TOKEN")
|
||||||
|
for werkzeug in curl tar python3 find; do
|
||||||
|
command -v "$werkzeug" >/dev/null 2>&1 || {
|
||||||
|
echo "Auf diesem System fehlt $werkzeug -- ohne das geht es nicht." >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
done
|
||||||
|
BOOTSTRAP_WORK="$(mktemp -d /tmp/tesm-release-XXXXXX)"
|
||||||
|
|
||||||
|
if [[ "$TAG" == "latest" ]]; then
|
||||||
|
echo "==> Ermittle aktuellen Release-Tag ..."
|
||||||
|
TAG="$(curl -fsS ${kopf[0]+"${kopf[@]}"} "$BASE/api/v1/repos/$OWNER/$repo/releases/latest" \
|
||||||
|
| python3 -c 'import json,sys; print(json.load(sys.stdin)["tag_name"])')" || {
|
||||||
|
echo "Kein Release gefunden unter $BASE/$OWNER/$repo." >&2
|
||||||
|
[[ -z "$TOKEN" ]] &&
|
||||||
|
echo "Ist das Repositorium privat? Dann TESM_RELEASE_TOKEN setzen." >&2
|
||||||
|
echo "Andere Quelle: TESM_RELEASE_BASE, TESM_RELEASE_OWNER, TESM_RELEASE_REPO." >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
echo " $TAG"
|
||||||
|
fi
|
||||||
|
|
||||||
|
asset="$repo-$TAG.tar.gz"
|
||||||
|
echo "==> Lade $asset ..."
|
||||||
|
curl -fsSL ${kopf[0]+"${kopf[@]}"} -o "$BOOTSTRAP_WORK/$asset" \
|
||||||
|
"$BASE/$OWNER/$repo/releases/download/$TAG/$asset" || {
|
||||||
|
echo "Download fehlgeschlagen: $BASE/$OWNER/$repo/releases/download/$TAG/$asset" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "==> Entpacke ..."
|
||||||
|
tar -xzf "$BOOTSTRAP_WORK/$asset" -C "$BOOTSTRAP_WORK"
|
||||||
|
# Drei Ebenen, nicht zwei: release.sh packt mit dem Praefix "<name>/", also
|
||||||
|
# liegt das Skript unter <name>/deploy/install.sh. Mit "-maxdepth 2" fand die
|
||||||
|
# Suche nie etwas -- und die Fehlermeldung schob es auf den Download.
|
||||||
|
SRC="$(find "$BOOTSTRAP_WORK" -maxdepth 3 -name install.sh -path '*/deploy/*' \
|
||||||
|
-printf '%h/..\n' | head -1)"
|
||||||
|
[[ -n "$SRC" ]] || {
|
||||||
|
echo "Im Paket fehlt deploy/install.sh -- Download unbrauchbar." >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
SRC="$(cd "$SRC" && pwd)"
|
||||||
|
|
||||||
|
echo "==> Pruefe auf CRLF (Windows-Buildmaschinen) ..."
|
||||||
|
if hat_crlf "$SRC/deploy/install.sh"; then
|
||||||
|
echo "Das Paket enthaelt CRLF-Zeilenenden. Beim Paketieren fehlte" >&2
|
||||||
|
echo "'git -c core.autocrlf=false archive'." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
# --8<-- Ende Bootstrap ------------------------------------------------------
|
||||||
|
|
||||||
|
if [[ -f "$SCRIPT_DIR/deploy/install.sh" ]]; then
|
||||||
|
# Im Paket: der Installer liegt daneben.
|
||||||
|
exec bash "$SCRIPT_DIR/deploy/install.sh" ${ARGUMENTE[0]+"${ARGUMENTE[@]}"}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Allein: Release holen und dessen Installer benutzen. Kein exec -- sonst
|
||||||
|
# laeuft die Aufraeumfalle nie und das entpackte Release bliebe unter /tmp.
|
||||||
|
release_holen || { echo "Das Release liess sich nicht holen." >&2; exit 1; }
|
||||||
|
bash "$SRC/deploy/install.sh" ${ARGUMENTE[0]+"${ARGUMENTE[@]}"}
|
||||||
Reference in new issue
Block a user