Auf einer 1.2.8 liegt nichts von dieser Fassung -- upgrade.sh verlangte aber
ein install.sh daneben und brach sonst ab. Das war die falsche Voraussetzung
fuer genau den Fall, fuer den das Skript gedacht ist. Beide Skripte holen sich
das Release jetzt selbst:
curl -fsSLO .../raw/branch/main/deploy/upgrade.sh
sudo bash upgrade.sh --app tesm
Liegt das Skript in einem entpackten Release, wird dessen install.sh benutzt
statt eines Downloads: wer ein Paket ausgepackt hat, will nicht, dass ungefragt
ein anderes geladen wird. Mit --tag laesst sich eine Version festlegen.
Der Bootstrap steht wortgleich in beiden Dateien -- zwei eigenstaendige
Skripte koennen sich keine Datei teilen, und beide sollen allein genuegen. Ein
Hygienetest vergleicht die Bloecke, ein zweiter prueft, dass upgrade.sh kein
entpacktes Release mehr verlangt, ein dritter fuehrt den Block mit leerem PATH
aus und erwartet, dass er das fehlende Werkzeug benennt.
update.sh ruft install.sh nicht mehr per exec auf: dadurch lief die
Aufraeumfalle nie, und jedes Update liess ein entpacktes Release unter /tmp
liegen.
Ausserdem "opus" aus allen Beispielen genommen. Das war der Name der
Testinstanz neben der Fassung 1.x; seit dem Umbenennen heissen die
Installationen wie ihre Anwendung, und in einer Hilfe, die jemand anders liest,
ist "opus" ein Raetsel. In BETRIEB.md stand dabei zweimal /opt statt /srv.
Geprueft am System, nicht behauptet: die einzelne upgrade.sh hat eine echte
1.2.8 (Kopie des Archivs) **in dasselbe Verzeichnis** auf 2.0.8 gebracht --
Release geladen, 4 Clients, 2 Zugangsdaten und die DHCP-Konfiguration
uebernommen, Dienst laeuft, /login 200. Die Sicherung enthaelt die 1.2.8 samt
sqlite.db und fernet.key (Verzeichnis 0700, Passphrase 0600), und /tmp bleibt
leer. Danach dieselbe Probe mit der einzelnen update.sh.
Version 2.0.9.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
431 lines
18 KiB
Bash
431 lines
18 KiB
Bash
#!/usr/bin/env bash
|
|
# =============================================================================
|
|
# Upgrade vom Stand SONNET5 (Version 1.x) auf diese Fassung -- mit Datenuebernahme.
|
|
#
|
|
# Eine einzelne Datei genuegt -- sie holt sich das Release selbst:
|
|
#
|
|
# curl -fsSLO https://gitea.int.eertmoed.net/alientim/tesm/raw/branch/main/deploy/upgrade.sh
|
|
# sudo bash upgrade.sh --app tesm
|
|
#
|
|
# Auf einer 1.2.8 liegt nichts von diesem Projekt; alles andere waere die
|
|
# falsche Voraussetzung. Liegt das Skript in einem entpackten Release, wird
|
|
# dessen install.sh benutzt statt eines Downloads:
|
|
#
|
|
# sudo bash deploy/upgrade.sh --app tesm
|
|
# sudo bash deploy/upgrade.sh --app tesm --instance test --source /srv/tesm
|
|
#
|
|
# Der Ablauf ist die Vorgabe und braucht keine Schalter:
|
|
#
|
|
# 1. **Sichern.** Vollstaendige Kopie der alten Installation nach
|
|
# /srv/<name>-upgrade-<zeitstempel>/alt. Erst danach wird irgendetwas
|
|
# angefasst -- und aus dieser Kopie stammen auch alle weiteren Schritte.
|
|
# 2. **Altes System abschalten.** Dienst und Timer werden gestoppt und
|
|
# deaktiviert, damit sie nach einem Neustart nicht zurueckkommen.
|
|
# 3. **Neu installieren.** install.sh dieses Verzeichnisses.
|
|
# 4. **Exportieren.** Aus der Kopie entsteht eine Exportdatei im **alten**
|
|
# Format -- genau die, die der Vorgaenger selbst geschrieben haette.
|
|
# 5. **Uebersetzen.** Daraus eine Datei im neuen Format.
|
|
# 6. **Einspielen.** Diese Datei geht durch denselben Import wie eine
|
|
# Sicherung.
|
|
#
|
|
# Warum die Sicherung vor dem Export steht und nicht danach: laeuft das
|
|
# Upgrade **in dasselbe Verzeichnis** (also ohne --instance), ueberschreibt
|
|
# install.sh die alte Installation. Wer erst exportiert und dann sichert,
|
|
# sichert im schlechtesten Fall nichts mehr. Gelesen wird ausschliesslich aus
|
|
# der Kopie -- das Ergebnis ist dasselbe, die Reihenfolge ist die sichere.
|
|
#
|
|
# Der Lizenzserver kennt Schritt 4 und 5 nicht: seine Uebernahme liest die alte
|
|
# Datenbank unmittelbar und **signiert jede Lizenz neu**. Dabei wird der alte
|
|
# Signaturschluessel uebernommen -- sonst muesste jeder Kunde einen
|
|
# Ausstellerwechsel bestaetigen. Wer das nicht will: --new-key. Dort ist die
|
|
# Kopie aus Schritt 1 der Export.
|
|
# =============================================================================
|
|
set -euo pipefail
|
|
|
|
APP=""
|
|
INSTANCE=""
|
|
SOURCE=""
|
|
PASSPHRASE=""
|
|
ASSUME_YES=0
|
|
DRY_RUN=0
|
|
WITH_DATA=1
|
|
STOP_OLD=1
|
|
ADOPT_KEY=1
|
|
TAG="latest"
|
|
#: Namenszusatz, unter dem ``rename.sh`` eine verdraengte Installation der
|
|
#: Fassung 1.x ablegt. Beide Skripte muessen denselben Wert benutzen; ein
|
|
#: Hygienetest vergleicht sie.
|
|
#:
|
|
#: Auf einer echten 1.2.8 gibt es das **nicht** -- dort liegt die Installation
|
|
#: unter ihrem Hauptnamen. Der Zusatz entsteht erst, wenn ``rename.sh`` den
|
|
#: Hauptnamen fuer die neue Fassung braucht. Deshalb ist er der zweite Ort, an
|
|
#: dem gesucht wird, nicht der erste.
|
|
ARCHIV_SUFFIX="sonnet5"
|
|
|
|
#: Nur fuer die Ausgabe: wurde die Quelle selbst gefunden oder angegeben?
|
|
info_quelle=""
|
|
EXTRA=()
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
|
COLOR_OK=$'\033[32m'; COLOR_WARN=$'\033[33m'; COLOR_ERR=$'\033[31m'; COLOR_OFF=$'\033[0m'
|
|
step() { printf '\n%s==>%s %s\n' "$COLOR_OK" "$COLOR_OFF" "$*"; }
|
|
warn() { printf '%s !!%s %s\n' "$COLOR_WARN" "$COLOR_OFF" "$*"; }
|
|
fail() { printf '%s !!%s %s\n' "$COLOR_ERR" "$COLOR_OFF" "$*" >&2; exit 1; }
|
|
info() { printf ' %s\n' "$*"; }
|
|
|
|
usage() {
|
|
cat <<'USAGE'
|
|
Verwendung: upgrade.sh --app <tesm|tesm-license> [Optionen]
|
|
|
|
--app <name> Welche Anwendung (Pflicht)
|
|
--source <pfad> Verzeichnis der alten Installation
|
|
(Vorgabe: /srv/tesm bzw. /srv/tesm-license)
|
|
--instance <name> Neue Installation als benannte Instanz anlegen, statt
|
|
das alte Verzeichnis zu ueberschreiben. Damit bleibt die
|
|
alte Installation liegen und ist zurueckholbar.
|
|
--passphrase <text> Passphrase der Zwischendateien. Ohne Angabe wird eine
|
|
erzeugt und in der Sicherung abgelegt.
|
|
--no-data Nur installieren, keine Datenuebernahme
|
|
--keep-old-running Alten Dienst nicht stoppen (nur mit --instance sinnvoll)
|
|
--new-key Nur Lizenzserver: einen neuen Signaturschluessel
|
|
erzeugen, statt den alten zu uebernehmen. Dann muessen
|
|
alle Kunden den Ausstellerwechsel bestaetigen -- die
|
|
Vorgabe ist deshalb die Uebernahme.
|
|
--tag <vX.Y.Z> Bestimmte Version installieren (Vorgabe: latest).
|
|
Wird nur beachtet, wenn das Release geladen wird.
|
|
--dry-run Nur zeigen, was geschehen wuerde
|
|
--yes Keine Rueckfrage
|
|
-h, --help Diese Hilfe
|
|
|
|
Alles weitere wird unveraendert an install.sh weitergegeben, etwa --https
|
|
oder --domain.
|
|
|
|
Beispiele:
|
|
# In dasselbe Verzeichnis, alte Installation wird ersetzt:
|
|
sudo bash deploy/upgrade.sh --app tesm
|
|
|
|
# Daneben, zum Vergleich, alte Installation bleibt unangetastet:
|
|
sudo bash deploy/upgrade.sh --app tesm --instance test --keep-old-running
|
|
USAGE
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--app) APP="${2:?}"; shift 2 ;;
|
|
--source) SOURCE="${2:?}"; shift 2 ;;
|
|
--instance) INSTANCE="${2:?}"; shift 2 ;;
|
|
--passphrase) PASSPHRASE="${2:?}"; shift 2 ;;
|
|
--no-data) WITH_DATA=0; shift ;;
|
|
--keep-old-running) STOP_OLD=0; shift ;;
|
|
--new-key) ADOPT_KEY=0; shift ;;
|
|
--adopt-key) ADOPT_KEY=1; shift ;; # Vorgabe, hier nur zur Deutlichkeit
|
|
--tag) TAG="${2:?}"; shift 2 ;;
|
|
--dry-run) DRY_RUN=1; shift ;;
|
|
--yes|-y) ASSUME_YES=1; shift ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*) EXTRA+=("$1"); shift ;;
|
|
esac
|
|
done
|
|
|
|
[[ "$APP" == "tesm" || "$APP" == "tesm-license" ]] || { usage; fail "--app fehlt oder ist unbekannt"; }
|
|
[[ "$(id -u)" -eq 0 ]] || fail "bitte mit sudo ausfuehren"
|
|
|
|
# --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. Setzt: $TAG (aufgeloest) und $SRC (entpacktes
|
|
# Release). Raeumt sein Arbeitsverzeichnis beim Beenden auf.
|
|
#
|
|
# 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 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() { [[ -n "$BOOTSTRAP_WORK" ]] && rm -rf "$BOOTSTRAP_WORK"; }
|
|
trap bootstrap_aufraeumen EXIT
|
|
|
|
release_holen() {
|
|
local repo="${TESM_RELEASE_REPO:-$APP}" asset
|
|
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 "$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
|
|
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 -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 ------------------------------------------------------
|
|
|
|
# Woher kommt install.sh? Liegt dieses Skript in einem entpackten Release,
|
|
# nimm dessen Verzeichnis -- wer ein Paket ausgepackt hat, will nicht, dass
|
|
# ungefragt ein anderes geladen wird. Sonst hol das Release.
|
|
if [[ -f "$SCRIPT_DIR/install.sh" ]]; then
|
|
SRC="$REPO_DIR"
|
|
RELEASE_HERKUNFT="$SRC (entpacktes Release neben diesem Skript)"
|
|
else
|
|
release_holen || fail "Das Release liess sich nicht holen."
|
|
RELEASE_HERKUNFT="$TAG von $BASE/$OWNER"
|
|
fi
|
|
|
|
# -- Wo liegt die alte Fassung? ---------------------------------------------- #
|
|
# Zwei Orte kommen in Frage, und der zweite ist der Grund fuer diesen Block:
|
|
# ``rename.sh`` schiebt eine Installation der Fassung 1.x nach
|
|
# ``/srv/<app>-sonnet5``, sobald der Hauptname fuer die neue gebraucht wird.
|
|
# Danach liegt unter ``/srv/<app>`` die **laufende** Installation, und die
|
|
# Vorgabe zeigte ins Leere. Erkannt wird an ``sqlite.db`` im Wurzelverzeichnis
|
|
# -- die neue Fassung legt ihre Datenbank unter ``data/app.db`` ab.
|
|
ist_alte_fassung() { [[ -f "$1/sqlite.db" ]]; }
|
|
|
|
if [[ -z "$SOURCE" ]]; then
|
|
for kandidat in "/srv/$APP" "/srv/$APP-$ARCHIV_SUFFIX"; do
|
|
if ist_alte_fassung "$kandidat"; then SOURCE="$kandidat"; break; fi
|
|
done
|
|
if [[ -z "$SOURCE" ]]; then
|
|
warn "Keine Installation der Fassung 1.x gefunden. Gesucht wurde in:"
|
|
warn " /srv/$APP (dort liegt sie normalerweise)"
|
|
warn " /srv/$APP-$ARCHIV_SUFFIX (dorthin verschiebt rename.sh sie, falls"
|
|
warn " der Hauptname schon vergeben war)"
|
|
[[ -f "/srv/$APP/data/app.db" ]] && warn "Unter /srv/$APP liegt die laufende Installation dieser Fassung."
|
|
fail "Bitte --source setzen."
|
|
fi
|
|
info_quelle="gefunden: $SOURCE"
|
|
fi
|
|
[[ -d "$SOURCE" ]] || fail "Die alte Installation $SOURCE gibt es nicht (--source setzen)"
|
|
ist_alte_fassung "$SOURCE" || fail "In $SOURCE liegt keine sqlite.db -- das ist keine Installation der Vorgaengerfassung"
|
|
|
|
NAME="$APP"
|
|
[[ -n "$INSTANCE" ]] && NAME="$APP-$INSTANCE"
|
|
ADMIN="$NAME-admin"
|
|
NEW_ROOT="/srv/$NAME"
|
|
IN_PLACE=0
|
|
[[ "$(cd "$SOURCE" && pwd)" == "$NEW_ROOT" ]] && IN_PLACE=1
|
|
|
|
STAMP="$(date +%Y%m%d-%H%M%S)"
|
|
BACKUP="/srv/$NAME-upgrade-$STAMP"
|
|
ALT_KOPIE="$BACKUP/alt"
|
|
EXPORT_ALT="$BACKUP/export-alt.json"
|
|
IMPORT_NEU="$BACKUP/import-neu.json"
|
|
|
|
# Alte Einheiten der Vorgaengerfassung. Der Timer gehoert dazu: nur den Dienst
|
|
# zu stoppen liesse ihn beim naechsten Ablauf wieder anlaufen.
|
|
if [[ "$APP" == "tesm" ]]; then
|
|
ALTE_UNITS=(tesm.service tesm-check.timer tesm-check.service
|
|
tesm-check-restart.timer tesm-check-restart.service)
|
|
else
|
|
ALTE_UNITS=(tesm-license.service)
|
|
fi
|
|
|
|
step "Vorhaben"
|
|
info "Anwendung : $APP${INSTANCE:+ (Instanz $INSTANCE)}"
|
|
info "Neue Fassung : $RELEASE_HERKUNFT"
|
|
info "Alt : $SOURCE${info_quelle:+ (automatisch gefunden)}"
|
|
info "Neu : $NEW_ROOT$([[ $IN_PLACE -eq 1 ]] && echo ' << dasselbe Verzeichnis')"
|
|
info "Sicherung : $ALT_KOPIE"
|
|
info "Datenuebernahme: $([[ $WITH_DATA -eq 1 ]] && echo ja || echo nein)"
|
|
info "Altes System : $([[ $STOP_OLD -eq 1 ]] && echo 'stoppen und deaktivieren' || echo 'laeuft weiter')"
|
|
if [[ $IN_PLACE -eq 1 && $STOP_OLD -eq 0 ]]; then
|
|
warn "--keep-old-running bei gleichem Verzeichnis: der alte Dienst laeuft auf Dateien,"
|
|
warn "die gleich ersetzt werden. Das geht schief. Bitte --instance verwenden."
|
|
fi
|
|
|
|
if [[ $DRY_RUN -eq 1 ]]; then
|
|
step "--dry-run: hier ist Schluss."
|
|
exit 0
|
|
fi
|
|
|
|
if [[ $ASSUME_YES -eq 0 ]]; then
|
|
read -r -p "Fortfahren? [j/N] " answer
|
|
[[ "$answer" =~ ^[jJyY]$ ]] || { echo "Abgebrochen."; exit 0; }
|
|
fi
|
|
|
|
# -- 1. Sichern -------------------------------------------------------------- #
|
|
step "Sichern"
|
|
mkdir -p "$ALT_KOPIE"
|
|
chmod 700 "$BACKUP"
|
|
# -a: Rechte und Zeiten bleiben, damit die Kopie auch als Rueckweg taugt.
|
|
# Das venv der alten Installation bleibt aussen vor -- es ist nachbaubar,
|
|
# gross, und seine Shebangs zeigen ohnehin auf den alten Pfad.
|
|
tar -C "$SOURCE" --exclude=venv --exclude=__pycache__ -cf - . | tar -C "$ALT_KOPIE" -xf -
|
|
info "$(du -sh "$ALT_KOPIE" | cut -f1) kopiert (ohne venv)"
|
|
[[ -f "$ALT_KOPIE/sqlite.db" ]] || fail "In der Kopie fehlt die Datenbank -- Abbruch"
|
|
|
|
# -- 2. Altes System abschalten ---------------------------------------------- #
|
|
if [[ $STOP_OLD -eq 1 ]]; then
|
|
step "Altes System abschalten"
|
|
for unit in "${ALTE_UNITS[@]}"; do
|
|
if systemctl list-unit-files "$unit" --no-legend 2>/dev/null | grep -q .; then
|
|
systemctl disable --now "$unit" >/dev/null 2>&1 || true
|
|
info "$unit gestoppt und deaktiviert"
|
|
fi
|
|
done
|
|
else
|
|
step "Altes System bleibt in Betrieb"
|
|
fi
|
|
|
|
# -- 3. Neu installieren ----------------------------------------------------- #
|
|
step "Neu installieren"
|
|
INSTALL_ARGS=(--app "$APP" --yes)
|
|
[[ -n "$INSTANCE" ]] && INSTALL_ARGS+=(--instance "$INSTANCE")
|
|
bash "$SRC/deploy/install.sh" "${INSTALL_ARGS[@]}" ${EXTRA[0]+"${EXTRA[@]}"}
|
|
|
|
if [[ $WITH_DATA -eq 0 ]]; then
|
|
step "Fertig -- ohne Datenuebernahme"
|
|
info "Sicherung der alten Installation: $ALT_KOPIE"
|
|
exit 0
|
|
fi
|
|
|
|
command -v "$ADMIN" >/dev/null 2>&1 || fail "$ADMIN nicht gefunden -- die Installation ist unvollstaendig"
|
|
|
|
if [[ -z "$PASSPHRASE" ]]; then
|
|
PASSPHRASE="$(head -c 24 /dev/urandom | base64 | tr -d '=+/' | cut -c1-24)"
|
|
# Die Passphrase liegt neben den Dateien, die sie schuetzt. Das ist hier kein
|
|
# Widerspruch: das alte Format verlangt eine, das Verzeichnis gehoert root
|
|
# allein (0700), und ohne sie waeren die beiden Dateien spaeter unbrauchbar --
|
|
# auch als Rueckweg.
|
|
printf '%s\n' "$PASSPHRASE" > "$BACKUP/passphrase.txt"
|
|
chmod 600 "$BACKUP/passphrase.txt"
|
|
fi
|
|
|
|
if [[ "$APP" == "tesm" ]]; then
|
|
# -- 4. Exportieren -------------------------------------------------------- #
|
|
step "Daten der alten Installation lesen"
|
|
"$ADMIN" legacy-export --source "$ALT_KOPIE" --out "$EXPORT_ALT" \
|
|
--passphrase "$PASSPHRASE" --force
|
|
chmod 600 "$EXPORT_ALT"
|
|
|
|
# -- 5. Uebersetzen -------------------------------------------------------- #
|
|
step "Uebersetzen"
|
|
"$ADMIN" convert-legacy "$EXPORT_ALT" --ausgabe "$IMPORT_NEU" \
|
|
--passphrase "$PASSPHRASE" --neue-passphrase "$PASSPHRASE" --force
|
|
chmod 600 "$IMPORT_NEU"
|
|
|
|
# -- 6. Einspielen --------------------------------------------------------- #
|
|
step "Einspielen"
|
|
"$ADMIN" legacy-import "$IMPORT_NEU" --passphrase "$PASSPHRASE" --actor upgrade
|
|
else
|
|
# -- 4.-6. in einem: die Uebernahme liest die alte Datenbank unmittelbar --- #
|
|
step "Lizenzbestand uebernehmen"
|
|
# Der alte Signaturschluessel wird uebernommen. Fuer ein Upgrade ist das
|
|
# die einzige sinnvolle Vorgabe: es ist derselbe Server mit neuerer Software,
|
|
# und ohne den alten Schluessel muesste jeder Kunde den Ausstellerwechsel
|
|
# bestaetigen. Ueberschrieben wird dabei nichts -- die Uebernahme lehnt ab,
|
|
# wenn die neue Installation schon einen eigenen Schluessel hat.
|
|
MIGRATE_ARGS=(migrate-legacy --source "$ALT_KOPIE")
|
|
[[ $ADOPT_KEY -eq 1 ]] && MIGRATE_ARGS+=(--adopt-key)
|
|
"$ADMIN" "${MIGRATE_ARGS[@]}"
|
|
if [[ $ADOPT_KEY -eq 0 ]]; then
|
|
warn "--new-key: es gilt ein neuer Signaturschluessel. Die Kunden muessen den"
|
|
warn "Ausstellerwechsel einmal bestaetigen."
|
|
fi
|
|
fi
|
|
|
|
# -- Abschluss --------------------------------------------------------------- #
|
|
step "Fertig"
|
|
info "Sicherung der alten Installation : $ALT_KOPIE"
|
|
if [[ "$APP" == "tesm" ]]; then
|
|
info "Export im alten Format : $EXPORT_ALT"
|
|
info "Uebersetzte Datei : $IMPORT_NEU"
|
|
[[ -f "$BACKUP/passphrase.txt" ]] && info "Passphrase beider Dateien : $BACKUP/passphrase.txt"
|
|
fi
|
|
cat <<TEXT
|
|
|
|
Von Hand nachzuziehen -- das laesst sich nicht uebersetzen:
|
|
* Lokale Konten: der alte Hash ist bcrypt, hier gilt Argon2id. Ein Hash
|
|
ist nicht umrechenbar; das ist sein Zweck. Erstes Konto anlegen mit
|
|
sudo -u $APP $ADMIN create-admin
|
|
* Gruppen und Rechte: die Rechteschluessel wurden neu geschnitten.
|
|
* Verzeichnisdienst (LDAP), Webserver und Zertifikat.
|
|
TEXT
|
|
|
|
# Der Rueckweg sieht in den beiden Faellen verschieden aus. Ein Hinweis, der
|
|
# das alte Verzeichnis wiederherstellen laesst, obwohl es nie angefasst wurde,
|
|
# waere schlimmer als keiner: er wuerde die Daten der neuen Installation mit
|
|
# einem alten Stand ueberschreiben.
|
|
if [[ $IN_PLACE -eq 1 ]]; then
|
|
cat <<TEXT
|
|
|
|
Zurueck auf den alten Stand -- das Verzeichnis wurde ersetzt, die Kopie
|
|
bringt es zurueck:
|
|
sudo systemctl disable --now $NAME
|
|
sudo rm -rf $SOURCE && sudo mkdir -p $SOURCE
|
|
sudo tar -C $ALT_KOPIE -cf - . | sudo tar -C $SOURCE -xf -
|
|
# venv der alten Installation neu bauen:
|
|
sudo python3 -m venv $SOURCE/venv
|
|
sudo $SOURCE/venv/bin/python -m pip install -r $SOURCE/requirements.txt
|
|
sudo systemctl enable --now ${ALTE_UNITS[0]}
|
|
|
|
TEXT
|
|
else
|
|
cat <<TEXT
|
|
|
|
Zurueck auf den alten Stand -- $SOURCE wurde nicht angefasst, es genuegt,
|
|
die neue Instanz abzuschalten:
|
|
sudo systemctl disable --now $NAME $NAME-monitor
|
|
sudo rm -f /etc/nginx/sites-enabled/$NAME && sudo systemctl reload nginx
|
|
sudo systemctl enable --now ${ALTE_UNITS[0]}
|
|
|
|
TEXT
|
|
fi
|