468d4562c7
Toolkit per deployare/aggiornare un tenant LoginMaster su qualsiasi Kubernetes (EKS/AKS/DOKS/Scaleway/vSphere/...). Contiene: - deploy.sh: bootstrap di un nuovo tenant (idempotente, re-run protection, storage class auto-rilevata, prompt separati api/admin tag, generazione segreti crittografici via openssl rand). - update.sh: rolling update zero-downtime con tag api/admin separati, rollback hint via 'kubectl rollout undo', riapplicazione opzionale del ConfigMap. - templates/: 8 manifest parametrici (envsubst): namespace, cert-manager TLS Mongo, NetworkPolicy intra-namespace, ConfigMap, MongoDB StatefulSet 3 repliche con TLS interno + initContainer per keyfile/PEM, tenant-api Deployment 2 repliche con CA validation, tenant-admin, ingress nginx + Let's Encrypt. Sicurezza: TLS interno Mongo (cert-manager CA self-signed 10y), keyFile per auth replica set, password client mai in argv, NetworkPolicy che isola il tenant, pod Mongo non-root (uid 999) con initContainer come root per i file runtime in tmpfs.
209 lines
8.1 KiB
Bash
Executable File
209 lines
8.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# =============================================================================
|
|
# update.sh — Aggiorna un tenant LoginMaster esistente.
|
|
#
|
|
# 1) Rolling update zero-downtime di api-tenant e/o admin-tenant ad un nuovo tag.
|
|
# 2) Opzionalmente riapplica il ConfigMap dal template (necessario se l'API ha
|
|
# iniziato a leggere nuove env var) e fa rollout restart per propagarle.
|
|
# 3) Su errore stampa il comando di rollback (kubectl rollout undo).
|
|
#
|
|
# Uso:
|
|
# ./update.sh # interattivo (chiede tutto)
|
|
# ./update.sh prod-1.3.8 # tag uguale per api+admin
|
|
# ./update.sh prod-1.3.8 prod-1.3.7 # tag api + tag admin separati
|
|
# =============================================================================
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
TEMPLATES_DIR="$SCRIPT_DIR/templates"
|
|
|
|
REGISTRY="hub.codebaker.it/loginmaster-tenant"
|
|
DEF_NAMESPACE="loginmaster-tenant"
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Helper
|
|
# -----------------------------------------------------------------------------
|
|
BLUE='\033[0;34m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; CYAN='\033[0;36m'; NC='\033[0m'
|
|
|
|
log() { echo -e "\n${BLUE}==>${NC} $*"; }
|
|
ok() { echo -e "${GREEN}✓${NC} $*"; }
|
|
warn() { echo -e "${YELLOW}!${NC} $*"; }
|
|
err() { echo -e "${RED}ERRORE:${NC} $*" >&2; exit 1; }
|
|
|
|
ask() {
|
|
local prompt="$1" def="${2:-}" input
|
|
if [[ -n "$def" ]]; then
|
|
read -r -p "$(echo -e "${CYAN}?${NC} ${prompt} [${def}]: ")" input
|
|
REPLY="${input:-$def}"
|
|
else
|
|
read -r -p "$(echo -e "${CYAN}?${NC} ${prompt}: ")" input
|
|
REPLY="$input"
|
|
fi
|
|
}
|
|
|
|
confirm() {
|
|
local prompt="$1" def="${2:-N}" input suffix
|
|
[[ "$def" == "Y" ]] && suffix="[Y/n]" || suffix="[y/N]"
|
|
read -r -p "$(echo -e "${CYAN}?${NC} ${prompt} ${suffix}: ")" input
|
|
input="${input:-$def}"
|
|
[[ "$input" =~ ^[yY]([eE][sS])?$ ]]
|
|
}
|
|
|
|
kc() { kubectl --context "$CONTEXT" -n "$NAMESPACE" "$@"; }
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Parametri
|
|
# -----------------------------------------------------------------------------
|
|
log "Parametri aggiornamento"
|
|
|
|
CURRENT_CTX=$(kubectl config current-context 2>/dev/null || echo "")
|
|
echo "Context kubectl disponibili:"
|
|
kubectl config get-contexts -o name | sed 's/^/ - /'
|
|
echo
|
|
ask "Context da usare" "$CURRENT_CTX"
|
|
CONTEXT="$REPLY"
|
|
|
|
kubectl --context "$CONTEXT" cluster-info >/dev/null 2>&1 \
|
|
|| err "Impossibile contattare il cluster sul context '$CONTEXT'"
|
|
ok "Connesso a $CONTEXT"
|
|
|
|
ask "Namespace del tenant" "$DEF_NAMESPACE"
|
|
NAMESPACE="$REPLY"
|
|
|
|
# Verifica che il namespace esista e abbia i deployment
|
|
kc get deployment tenant-api >/dev/null 2>&1 \
|
|
|| err "Deployment tenant-api non trovato nel namespace '$NAMESPACE'"
|
|
|
|
# Mostra stato attuale
|
|
log "Stato attuale"
|
|
CURRENT_API=$(kc get deployment tenant-api -o jsonpath='{.spec.template.spec.containers[0].image}')
|
|
CURRENT_ADMIN=$(kc get deployment tenant-admin -o jsonpath='{.spec.template.spec.containers[0].image}')
|
|
echo " tenant-api: $CURRENT_API"
|
|
echo " tenant-admin: $CURRENT_ADMIN"
|
|
echo
|
|
|
|
# Tag da argomenti posizionali o prompt. Permettiamo tag separati perché
|
|
# api-tenant e admin-tenant possono essere a versioni diverse.
|
|
if [[ -n "${1:-}" && -n "${2:-}" ]]; then
|
|
NEW_TAG_API="$1"
|
|
NEW_TAG_ADMIN="$2"
|
|
elif [[ -n "${1:-}" ]]; then
|
|
NEW_TAG_API="$1"
|
|
NEW_TAG_ADMIN="$1"
|
|
else
|
|
# Default = tag attuali estratti dal cluster (rimangono se l'utente conferma)
|
|
CUR_API_TAG="${CURRENT_API##*:}"
|
|
CUR_ADMIN_TAG="${CURRENT_ADMIN##*:}"
|
|
ask "Nuovo tag api-tenant" "$CUR_API_TAG"
|
|
NEW_TAG_API="$REPLY"
|
|
ask "Nuovo tag admin-tenant" "$CUR_ADMIN_TAG"
|
|
NEW_TAG_ADMIN="$REPLY"
|
|
fi
|
|
[[ -z "$NEW_TAG_API" ]] && err "Tag api-tenant obbligatorio"
|
|
[[ -z "$NEW_TAG_ADMIN" ]] && err "Tag admin-tenant obbligatorio"
|
|
|
|
NEW_API="${REGISTRY}/api-tenant:${NEW_TAG_API}"
|
|
NEW_ADMIN="${REGISTRY}/admin-tenant:${NEW_TAG_ADMIN}"
|
|
|
|
# Scelta componenti
|
|
UPDATE_API=true
|
|
UPDATE_ADMIN=true
|
|
if ! confirm "Aggiornare tenant-api a ${NEW_API}?" "Y"; then
|
|
UPDATE_API=false
|
|
fi
|
|
if ! confirm "Aggiornare tenant-admin a ${NEW_ADMIN}?" "Y"; then
|
|
UPDATE_ADMIN=false
|
|
fi
|
|
|
|
$UPDATE_API || $UPDATE_ADMIN || { echo "Nessun aggiornamento selezionato."; exit 0; }
|
|
|
|
# Riepilogo
|
|
log "Riepilogo aggiornamento"
|
|
echo " Context: $CONTEXT"
|
|
echo " Namespace: $NAMESPACE"
|
|
$UPDATE_API && echo " tenant-api: $CURRENT_API --> $NEW_API"
|
|
$UPDATE_ADMIN && echo " tenant-admin: $CURRENT_ADMIN --> $NEW_ADMIN"
|
|
echo
|
|
confirm "Procedere?" "N" || { echo "Annullato."; exit 0; }
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Aggiornamento
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# Suggerisce il rollback se lo script fallisce a metà strada (rollout fail, kubectl
|
|
# error, etc.). Viene disarmato solo dopo il rollout completato con successo.
|
|
print_rollback_hint() {
|
|
echo
|
|
warn "Aggiornamento interrotto. Per riportare i Deployment alla revisione precedente:"
|
|
$UPDATE_API && echo " kubectl --context '$CONTEXT' -n '$NAMESPACE' rollout undo deployment/tenant-api"
|
|
$UPDATE_ADMIN && echo " kubectl --context '$CONTEXT' -n '$NAMESPACE' rollout undo deployment/tenant-admin"
|
|
echo " (kubectl rollout undo usa la revision history del Deployment, non richiede il tag precedente)"
|
|
}
|
|
trap print_rollback_hint ERR
|
|
|
|
if $UPDATE_API; then
|
|
log "Aggiorno tenant-api"
|
|
kc set image deployment/tenant-api tenant-api="$NEW_API"
|
|
kc rollout status deployment/tenant-api --timeout=300s
|
|
ok "tenant-api aggiornato a $NEW_TAG_API"
|
|
fi
|
|
|
|
if $UPDATE_ADMIN; then
|
|
log "Aggiorno tenant-admin"
|
|
kc set image deployment/tenant-admin tenant-admin="$NEW_ADMIN"
|
|
kc rollout status deployment/tenant-admin --timeout=300s
|
|
ok "tenant-admin aggiornato a $NEW_TAG_ADMIN"
|
|
fi
|
|
|
|
trap - ERR
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# ConfigMap (opzionale — per propagare nuove env var introdotte dal template)
|
|
# -----------------------------------------------------------------------------
|
|
# Le env var iniettate via envFrom configMapRef NON si aggiornano automaticamente
|
|
# quando il ConfigMap cambia: i pod vanno riavviati. Questo blocco riapplica il
|
|
# template usando i valori CORRENTI dal cluster (preserva le scelte fatte al
|
|
# deploy iniziale o tramite kubectl edit) e fa rollout restart.
|
|
if confirm "Riapplicare anche il ConfigMap dal template (necessario se l'API legge nuove env var)?" "N"; then
|
|
command -v envsubst >/dev/null || err "envsubst non trovato (brew install gettext && brew link gettext --force)"
|
|
|
|
log "Leggo i valori correnti dal ConfigMap tenant-api-config"
|
|
cm_get() { kc get configmap tenant-api-config -o jsonpath="{.data.$1}" 2>/dev/null || true; }
|
|
|
|
NAMESPACE="$NAMESPACE" \
|
|
LOGINMASTER_API_URL=$(cm_get LOGINMASTER_API_URL) \
|
|
ADMIN_ALLOWED_ORIGIN=$(cm_get ADMIN_ALLOWED_ORIGIN) \
|
|
TENANT_ADMIN_URL=$(cm_get TENANT_ADMIN_URL) \
|
|
SMTP_HOST=$(cm_get SMTP_HOST) \
|
|
SMTP_PORT=$(cm_get SMTP_PORT) \
|
|
SMTP_SECURE=$(cm_get SMTP_SECURE) \
|
|
SMTP_USER=$(cm_get SMTP_USER) \
|
|
EMAIL_FROM=$(cm_get EMAIL_FROM) \
|
|
envsubst < "$TEMPLATES_DIR/03-configmap.yaml" \
|
|
| kc apply -f -
|
|
ok "ConfigMap riapplicato dal template"
|
|
|
|
log "Restart deployment/tenant-api (necessario per propagare env var)"
|
|
kc rollout restart deployment/tenant-api
|
|
kc rollout status deployment/tenant-api --timeout=300s
|
|
ok "tenant-api riavviato con il nuovo ConfigMap"
|
|
fi
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Verifica finale
|
|
# -----------------------------------------------------------------------------
|
|
log "Verifica finale"
|
|
echo
|
|
kc get pods -l 'app in (tenant-api, tenant-admin)'
|
|
echo
|
|
echo "Immagini attuali:"
|
|
echo " tenant-api: $(kc get deployment tenant-api -o jsonpath='{.spec.template.spec.containers[0].image}')"
|
|
echo " tenant-admin: $(kc get deployment tenant-admin -o jsonpath='{.spec.template.spec.containers[0].image}')"
|
|
echo
|
|
echo "Per fare rollback alla revisione precedente:"
|
|
$UPDATE_API && echo " kubectl --context '$CONTEXT' -n '$NAMESPACE' rollout undo deployment/tenant-api"
|
|
$UPDATE_ADMIN && echo " kubectl --context '$CONTEXT' -n '$NAMESPACE' rollout undo deployment/tenant-admin"
|
|
echo
|
|
ok "Aggiornamento completato."
|