Compare commits
2 Commits
b508f5c58c
...
c41a4560c1
| Author | SHA1 | Date | |
|---|---|---|---|
| c41a4560c1 | |||
| 2d829b74dd |
@ -23,6 +23,9 @@ By: Codex (GPT-5)
|
|||||||
| 28.12.2025 | 🏗️ Planning | ID: EPIC_000009 Update Webservice (External Team) dokumentiert. By: Codex (GPT-5) |
|
| 28.12.2025 | 🏗️ Planning | ID: EPIC_000009 Update Webservice (External Team) dokumentiert. By: Codex (GPT-5) |
|
||||||
| 28.12.2025 | 🏗️ Planning | ID: TASK_000027-TASK_000029 fuer EPIC_000008 ausgearbeitet. By: Codex (GPT-5) |
|
| 28.12.2025 | 🏗️ Planning | ID: TASK_000027-TASK_000029 fuer EPIC_000008 ausgearbeitet. By: Codex (GPT-5) |
|
||||||
| 28.12.2025 | ⚙️ Code | ID: Update-Config Keys in Settings/ENV/README definiert. By: Codex (GPT-5) |
|
| 28.12.2025 | ⚙️ Code | ID: Update-Config Keys in Settings/ENV/README definiert. By: Codex (GPT-5) |
|
||||||
|
| 28.12.2025 | 📝 Req | ID: Update-Flow fuer Client dokumentiert. By: Codex (GPT-5) |
|
||||||
|
| 28.12.2025 | 📝 Req | ID: Update-Flow Prototyp dokumentiert. By: Codex (GPT-5) |
|
||||||
|
| 28.12.2025 | ⚙️ Code | ID: Update-Client Prototyp-Skript hinzugefuegt. By: Codex (GPT-5) |
|
||||||
|
|
||||||
---
|
---
|
||||||
## Legende
|
## Legende
|
||||||
|
|||||||
44
docs/update-client.md
Normal file
44
docs/update-client.md
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
ID: DOC_000004 | Version: 0.1.0 | Status: Draft
|
||||||
|
By: Codex (GPT-5)
|
||||||
|
|
||||||
|
# Client Update Flow (Kiddo)
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Definiert den Client-seitigen Ablauf fuer das Pull-Update vom Update-Service.
|
||||||
|
|
||||||
|
## Manifest Format (JSON)
|
||||||
|
Beispiel:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": "0.1.2",
|
||||||
|
"artifact_url": "https://update.wlkns.org/kiddo/kiddo-0.1.2.tar.gz",
|
||||||
|
"sha256": "<hex>",
|
||||||
|
"sig_url": "https://update.wlkns.org/kiddo/kiddo-0.1.2.sig"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Flow (High Level)
|
||||||
|
1. Manifest abrufen (auth optional via Bearer Token).
|
||||||
|
2. `artifact_url` herunterladen.
|
||||||
|
3. SHA256 pruefen (Signatur optional).
|
||||||
|
4. In Staging-Verzeichnis entpacken.
|
||||||
|
5. Service stoppen.
|
||||||
|
6. Atomic swap: aktuelles Verzeichnis sichern, Staging nach `/opt/sk` verschieben.
|
||||||
|
7. Service starten.
|
||||||
|
8. Bei Fehlern Rollback auf Backup.
|
||||||
|
|
||||||
|
## Prototype Script
|
||||||
|
- `scripts/update_client.sh` implementiert den Flow als CLI-Prototyp.
|
||||||
|
- Erfordert `curl`, `tar`, `sha256sum`, `python3` und `systemctl`.
|
||||||
|
|
||||||
|
## Rollback
|
||||||
|
- Wenn Start fehlschlaegt: Backup nach `/opt/sk` zurueck, Service neu starten.
|
||||||
|
- Backup-Verzeichnis benoetigt genuegend Speicher.
|
||||||
|
|
||||||
|
## Security Notes
|
||||||
|
- Artefakte muessen checksum-verifiziert sein.
|
||||||
|
- Token-Handling ueber `SKD_UPDATE_TOKEN`.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
- Update-Service ist extern (update.wlkns.org).
|
||||||
|
- Service muss als root stoppen/starten koennen.
|
||||||
@ -1,4 +1,4 @@
|
|||||||
ID: TASK_000028 | Version: 0.1.0 | Status: Draft
|
ID: TASK_000028 | Version: 0.1.0 | Status: In Progress
|
||||||
By: Codex (GPT-5)
|
By: Codex (GPT-5)
|
||||||
|
|
||||||
# TASK_000028: Verify and apply update
|
# TASK_000028: Verify and apply update
|
||||||
@ -14,8 +14,10 @@ US_000027
|
|||||||
- `tar.gz` herunterladen und SHA256 pruefen.
|
- `tar.gz` herunterladen und SHA256 pruefen.
|
||||||
- Update atomar anwenden (staging, swap, rollback).
|
- Update atomar anwenden (staging, swap, rollback).
|
||||||
- Fehlerfall dokumentieren (Rollback, Status).
|
- Fehlerfall dokumentieren (Rollback, Status).
|
||||||
|
- Ablauf dokumentiert in `docs/update-client.md`.
|
||||||
|
|
||||||
## Definition of Done (DoD)
|
## Definition of Done (DoD)
|
||||||
- Verifikation (Checksum/Signatur) ist beschrieben.
|
- Verifikation (Checksum/Signatur) ist beschrieben.
|
||||||
- Atomare Anwendung und Rollback-Strategie sind definiert.
|
- Atomare Anwendung und Rollback-Strategie sind definiert.
|
||||||
- Fehlerbilder sind erfasst.
|
- Fehlerbilder sind erfasst.
|
||||||
|
- Prototyp-Skript ist als Referenz vorhanden.
|
||||||
|
|||||||
112
scripts/update_client.sh
Executable file
112
scripts/update_client.sh
Executable file
@ -0,0 +1,112 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SERVICE_NAME="${SERVICE_NAME:-skd}"
|
||||||
|
UPDATE_URL="${SKD_UPDATE_URL:-https://update.wlkns.org}"
|
||||||
|
UPDATE_TOKEN="${SKD_UPDATE_TOKEN:-}"
|
||||||
|
INSTALL_DIR="${INSTALL_DIR:-/opt/sk}"
|
||||||
|
STAGING_DIR=""
|
||||||
|
MANIFEST_FILE=""
|
||||||
|
ARTIFACT_FILE=""
|
||||||
|
BACKUP_DIR=""
|
||||||
|
|
||||||
|
log() {
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
if [[ -n "${STAGING_DIR}" && -d "${STAGING_DIR}" ]]; then
|
||||||
|
rm -rf "${STAGING_DIR}"
|
||||||
|
fi
|
||||||
|
if [[ -n "${MANIFEST_FILE}" && -f "${MANIFEST_FILE}" ]]; then
|
||||||
|
rm -f "${MANIFEST_FILE}"
|
||||||
|
fi
|
||||||
|
if [[ -n "${ARTIFACT_FILE}" && -f "${ARTIFACT_FILE}" ]]; then
|
||||||
|
rm -f "${ARTIFACT_FILE}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
require_cmd() {
|
||||||
|
if ! command -v "$1" >/dev/null 2>&1; then
|
||||||
|
echo "Required command not found: $1" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
require_cmd curl
|
||||||
|
require_cmd tar
|
||||||
|
require_cmd sha256sum
|
||||||
|
require_cmd python3
|
||||||
|
|
||||||
|
log "Fetching update manifest from ${UPDATE_URL}..."
|
||||||
|
MANIFEST_FILE="$(mktemp)"
|
||||||
|
if [[ -n "${UPDATE_TOKEN}" ]]; then
|
||||||
|
curl -fsS -H "Authorization: Bearer ${UPDATE_TOKEN}" "${UPDATE_URL}" -o "${MANIFEST_FILE}"
|
||||||
|
else
|
||||||
|
curl -fsS "${UPDATE_URL}" -o "${MANIFEST_FILE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
read_manifest() {
|
||||||
|
python3 - <<'PY' "${MANIFEST_FILE}"
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
path = sys.argv[1]
|
||||||
|
with open(path, "r", encoding="utf-8") as fh:
|
||||||
|
data = json.load(fh)
|
||||||
|
for key in ("version", "artifact_url", "sha256"):
|
||||||
|
if key not in data:
|
||||||
|
sys.exit(f"Manifest missing '{key}'")
|
||||||
|
print(data["version"])
|
||||||
|
print(data["artifact_url"])
|
||||||
|
print(data["sha256"])
|
||||||
|
PY
|
||||||
|
}
|
||||||
|
|
||||||
|
readarray -t manifest_values < <(read_manifest)
|
||||||
|
VERSION="${manifest_values[0]}"
|
||||||
|
ARTIFACT_URL="${manifest_values[1]}"
|
||||||
|
ARTIFACT_SHA256="${manifest_values[2]}"
|
||||||
|
|
||||||
|
log "Downloading artifact ${ARTIFACT_URL} (version ${VERSION})..."
|
||||||
|
ARTIFACT_FILE="$(mktemp --suffix=.tar.gz)"
|
||||||
|
if [[ -n "${UPDATE_TOKEN}" ]]; then
|
||||||
|
curl -fsS -H "Authorization: Bearer ${UPDATE_TOKEN}" "${ARTIFACT_URL}" -o "${ARTIFACT_FILE}"
|
||||||
|
else
|
||||||
|
curl -fsS "${ARTIFACT_URL}" -o "${ARTIFACT_FILE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Verifying checksum..."
|
||||||
|
CALC_SHA="$(sha256sum "${ARTIFACT_FILE}" | awk '{print $1}')"
|
||||||
|
if [[ "${CALC_SHA}" != "${ARTIFACT_SHA256}" ]]; then
|
||||||
|
echo "Checksum mismatch: expected ${ARTIFACT_SHA256} got ${CALC_SHA}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
STAGING_DIR="$(mktemp -d /opt/sk_update.XXXXXX)"
|
||||||
|
log "Extracting to staging ${STAGING_DIR}..."
|
||||||
|
tar -xzf "${ARTIFACT_FILE}" -C "${STAGING_DIR}"
|
||||||
|
|
||||||
|
BACKUP_DIR="/opt/sk_backup_${VERSION}_$(date +%s)"
|
||||||
|
log "Stopping service ${SERVICE_NAME}..."
|
||||||
|
sudo systemctl stop "${SERVICE_NAME}.service"
|
||||||
|
|
||||||
|
log "Swapping ${INSTALL_DIR} -> ${BACKUP_DIR}..."
|
||||||
|
if [[ -d "${INSTALL_DIR}" ]]; then
|
||||||
|
sudo mv "${INSTALL_DIR}" "${BACKUP_DIR}"
|
||||||
|
fi
|
||||||
|
sudo mv "${STAGING_DIR}" "${INSTALL_DIR}"
|
||||||
|
STAGING_DIR=""
|
||||||
|
|
||||||
|
log "Starting service ${SERVICE_NAME}..."
|
||||||
|
if sudo systemctl start "${SERVICE_NAME}.service"; then
|
||||||
|
log "Update applied successfully."
|
||||||
|
else
|
||||||
|
log "Service failed to start, rolling back..."
|
||||||
|
sudo rm -rf "${INSTALL_DIR}"
|
||||||
|
if [[ -d "${BACKUP_DIR}" ]]; then
|
||||||
|
sudo mv "${BACKUP_DIR}" "${INSTALL_DIR}"
|
||||||
|
fi
|
||||||
|
sudo systemctl start "${SERVICE_NAME}.service" || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user