feat : Utilisation web disponible et simplification du deployement des scripts (WIP)
This commit is contained in:
@@ -2,9 +2,12 @@
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
DEFAULT_ENV_FILE="${SCRIPT_DIR}/.env"
|
||||
CONFIG_DIR="${SCRIPT_DIR}/config"
|
||||
GLOBAL_ENV_FILE_DEFAULT="${CONFIG_DIR}/global.env"
|
||||
TARGETS_DIR_DEFAULT="${CONFIG_DIR}/targets"
|
||||
|
||||
ENV_FILE="${ENV_FILE:-$DEFAULT_ENV_FILE}"
|
||||
GLOBAL_ENV_FILE="${GLOBAL_ENV_FILE:-$GLOBAL_ENV_FILE_DEFAULT}"
|
||||
TARGETS_DIR="${TARGETS_DIR:-$TARGETS_DIR_DEFAULT}"
|
||||
|
||||
CLI_TARGET=""
|
||||
CLI_DB=""
|
||||
@@ -12,13 +15,17 @@ CLI_OVERWRITE=""
|
||||
CLI_RESTORE_ROLES=""
|
||||
CLI_REQUEST_ID=""
|
||||
NON_INTERACTIVE="${NON_INTERACTIVE:-no}"
|
||||
JSON_ONLY="${JSON_ONLY:-no}"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--env-file)
|
||||
[[ $# -ge 2 ]] || { echo "Argument manquant pour --env-file" >&2; exit 1; }
|
||||
ENV_FILE="$2"
|
||||
--global-env-file)
|
||||
[[ $# -ge 2 ]] || { echo "Argument manquant pour --global-env-file" >&2; exit 1; }
|
||||
GLOBAL_ENV_FILE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--targets-dir)
|
||||
[[ $# -ge 2 ]] || { echo "Argument manquant pour --targets-dir" >&2; exit 1; }
|
||||
TARGETS_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--target)
|
||||
@@ -50,10 +57,6 @@ while [[ $# -gt 0 ]]; do
|
||||
NON_INTERACTIVE="yes"
|
||||
shift
|
||||
;;
|
||||
--json-only)
|
||||
JSON_ONLY="yes"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Argument inconnu : $1" >&2
|
||||
exit 1
|
||||
@@ -61,33 +64,17 @@ while [[ $# -gt 0 ]]; do
|
||||
esac
|
||||
done
|
||||
|
||||
json_escape() {
|
||||
python3 - <<'PY' "$1"
|
||||
import json, sys
|
||||
print(json.dumps(sys.argv[1]))
|
||||
PY
|
||||
}
|
||||
|
||||
print_json_and_exit() {
|
||||
local status="$1"
|
||||
local message="$2"
|
||||
local exit_code="$3"
|
||||
|
||||
printf '{'
|
||||
printf '"status":%s,' "$(json_escape "$status")"
|
||||
printf '"message":%s,' "$(json_escape "$message")"
|
||||
printf '"target":%s,' "$(json_escape "${TARGET:-}")"
|
||||
printf '"request_id":%s' "$(json_escape "${REQUEST_ID:-}")"
|
||||
printf '}\n'
|
||||
exit "$exit_code"
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
|
||||
}
|
||||
|
||||
fail() {
|
||||
print_json_and_exit "error" "$*" 1
|
||||
log "ERROR: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
command -v "$1" >/dev/null 2>&1 || fail "commande requise absente : $1"
|
||||
}
|
||||
|
||||
to_bool_yes_no() {
|
||||
@@ -104,124 +91,112 @@ is_tty() {
|
||||
[[ -t 0 && -t 1 ]]
|
||||
}
|
||||
|
||||
print_stdout() {
|
||||
[[ "$JSON_ONLY" == "yes" ]] || echo "$*"
|
||||
}
|
||||
|
||||
sanitize_key() {
|
||||
local s="${1:-}"
|
||||
s="${s//[^a-zA-Z0-9_]/_}"
|
||||
printf "%s" "$s"
|
||||
}
|
||||
|
||||
get_target_var() {
|
||||
local target="$1"
|
||||
local key="$2"
|
||||
local safe_target
|
||||
safe_target="$(sanitize_key "$target")"
|
||||
local var_name="TARGET_${key}_${safe_target}"
|
||||
printf "%s" "${!var_name:-}"
|
||||
}
|
||||
|
||||
shell_quote() {
|
||||
printf "%q" "$1"
|
||||
}
|
||||
|
||||
[[ -f "$ENV_FILE" ]] || {
|
||||
echo '{"status":"error","message":"fichier .env IA introuvable"}'
|
||||
exit 1
|
||||
cleanup() {
|
||||
rm -f "${BOOTSTRAP_JSON:-}"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
[[ -f "$GLOBAL_ENV_FILE" ]] || fail "fichier global introuvable : $GLOBAL_ENV_FILE"
|
||||
[[ -d "$TARGETS_DIR" ]] || fail "dossier targets introuvable : $TARGETS_DIR"
|
||||
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
source "$ENV_FILE"
|
||||
source "$GLOBAL_ENV_FILE"
|
||||
set +a
|
||||
|
||||
for cmd in bash ssh python3; do
|
||||
require_cmd "$cmd" || fail "commande requise absente sur IA : $cmd"
|
||||
done
|
||||
require_cmd ssh
|
||||
require_cmd git
|
||||
require_cmd python3
|
||||
|
||||
TARGET="${CLI_TARGET:-${TARGET:-}}"
|
||||
REQUESTED_DB="${CLI_DB:-${REQUESTED_DB:-}}"
|
||||
REQUEST_ID="${CLI_REQUEST_ID:-${REQUEST_ID:-}}"
|
||||
ALLOW_OVERWRITE_RAW="${CLI_OVERWRITE:-${ALLOW_OVERWRITE:-no}}"
|
||||
RESTORE_ROLES_RAW="${CLI_RESTORE_ROLES:-${RESTORE_ROLES:-yes}}"
|
||||
REQUEST_ID="${CLI_REQUEST_ID:-${REQUEST_ID:-$(date '+%Y%m%d%H%M%S')_$RANDOM}}"
|
||||
|
||||
ALLOW_OVERWRITE="$(to_bool_yes_no "$ALLOW_OVERWRITE_RAW")" || fail "ALLOW_OVERWRITE invalide"
|
||||
RESTORE_ROLES="$(to_bool_yes_no "$RESTORE_ROLES_RAW")" || fail "RESTORE_ROLES invalide"
|
||||
|
||||
: "${TARGETS:?Variable TARGETS manquante dans le .env IA}"
|
||||
|
||||
read -r -a TARGETS_ARRAY <<< "$TARGETS"
|
||||
[[ "${#TARGETS_ARRAY[@]}" -gt 0 ]] || fail "aucune cible définie dans TARGETS"
|
||||
|
||||
if [[ -z "$TARGET" ]]; then
|
||||
if [[ "$NON_INTERACTIVE" == "yes" ]]; then
|
||||
fail "TARGET manquante en mode non interactif"
|
||||
fi
|
||||
|
||||
mapfile -t TARGET_LIST < <(find "$TARGETS_DIR" -maxdepth 1 -type f -name '*.env' -printf '%f\n' | sed 's/\.env$//' | LC_ALL=C sort)
|
||||
|
||||
[[ "${#TARGET_LIST[@]}" -gt 0 ]] || fail "aucune cible définie dans ${TARGETS_DIR}"
|
||||
|
||||
if is_tty; then
|
||||
print_stdout "Cibles disponibles :"
|
||||
for i in "${!TARGETS_ARRAY[@]}"; do
|
||||
print_stdout " $((i + 1))) ${TARGETS_ARRAY[$i]}"
|
||||
echo "Cibles disponibles :"
|
||||
for i in "${!TARGET_LIST[@]}"; do
|
||||
echo " $((i + 1))) ${TARGET_LIST[$i]}"
|
||||
done
|
||||
echo
|
||||
read -r -p "Sélectionnez le numéro de la cible : " TARGET_INDEX
|
||||
[[ "$TARGET_INDEX" =~ ^[0-9]+$ ]] || fail "numéro de cible invalide"
|
||||
(( TARGET_INDEX >= 1 && TARGET_INDEX <= ${#TARGETS_ARRAY[@]} )) || fail "numéro hors plage"
|
||||
TARGET="${TARGETS_ARRAY[$((TARGET_INDEX - 1))]}"
|
||||
(( TARGET_INDEX >= 1 && TARGET_INDEX <= ${#TARGET_LIST[@]} )) || fail "numéro hors plage"
|
||||
TARGET="${TARGET_LIST[$((TARGET_INDEX - 1))]}"
|
||||
else
|
||||
fail "TARGET manquante et aucune interaction terminal disponible"
|
||||
fi
|
||||
fi
|
||||
|
||||
TARGET_ALLOWED="no"
|
||||
for candidate in "${TARGETS_ARRAY[@]}"; do
|
||||
if [[ "$candidate" == "$TARGET" ]]; then
|
||||
TARGET_ALLOWED="yes"
|
||||
break
|
||||
fi
|
||||
done
|
||||
[[ "$TARGET_ALLOWED" == "yes" ]] || fail "cible refusée : non présente dans TARGETS"
|
||||
TARGET_ENV_SOURCE="${TARGETS_DIR}/${TARGET}.env"
|
||||
[[ -f "$TARGET_ENV_SOURCE" ]] || fail "fichier cible introuvable : $TARGET_ENV_SOURCE"
|
||||
|
||||
TARGET_HOST="$(get_target_var "$TARGET" "HOST")"
|
||||
TARGET_USER="$(get_target_var "$TARGET" "USER")"
|
||||
TARGET_SSH_KEY="$(get_target_var "$TARGET" "SSH_KEY")"
|
||||
TARGET_SSH_PORT="$(get_target_var "$TARGET" "SSH_PORT")"
|
||||
TARGET_SSH_CONNECT_TIMEOUT="$(get_target_var "$TARGET" "SSH_CONNECT_TIMEOUT")"
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
source "$TARGET_ENV_SOURCE"
|
||||
set +a
|
||||
|
||||
TARGET_REPO_URL="$(get_target_var "$TARGET" "REPO_URL")"
|
||||
TARGET_REPO_BRANCH="$(get_target_var "$TARGET" "REPO_BRANCH")"
|
||||
TARGET_REPO_DIR="$(get_target_var "$TARGET" "REPO_DIR")"
|
||||
TARGET_CORE_SCRIPT="$(get_target_var "$TARGET" "CORE_SCRIPT")"
|
||||
TARGET_ENV_FILE="$(get_target_var "$TARGET" "ENV_FILE")"
|
||||
|
||||
TARGET_SSH_PORT="${TARGET_SSH_PORT:-22}"
|
||||
TARGET_SSH_CONNECT_TIMEOUT="${TARGET_SSH_CONNECT_TIMEOUT:-8}"
|
||||
TARGET_REPO_BRANCH="${TARGET_REPO_BRANCH:-main}"
|
||||
|
||||
[[ -n "$TARGET_HOST" ]] || fail "TARGET_HOST_${TARGET} manquante"
|
||||
[[ -n "$TARGET_USER" ]] || fail "TARGET_USER_${TARGET} manquante"
|
||||
[[ -n "$TARGET_SSH_KEY" ]] || fail "TARGET_SSH_KEY_${TARGET} manquante"
|
||||
[[ -n "$TARGET_REPO_URL" ]] || fail "TARGET_REPO_URL_${TARGET} manquante"
|
||||
[[ -n "$TARGET_REPO_DIR" ]] || fail "TARGET_REPO_DIR_${TARGET} manquante"
|
||||
[[ -n "$TARGET_CORE_SCRIPT" ]] || fail "TARGET_CORE_SCRIPT_${TARGET} manquante"
|
||||
[[ -n "$TARGET_ENV_FILE" ]] || fail "TARGET_ENV_FILE_${TARGET} manquante"
|
||||
TARGET_HOST="${TARGET_HOST:-}"
|
||||
TARGET_PORT="${TARGET_PORT:-22}"
|
||||
TARGET_USER="${TARGET_BOOTSTRAP_USER:-}"
|
||||
TARGET_SSH_KEY="${TARGET_BOOTSTRAP_SSH_KEY:-}"
|
||||
TARGET_REPO_URL="${TARGET_REPO_URL:-${GLOBAL_REPO_URL:-}}"
|
||||
TARGET_REPO_BRANCH="${TARGET_REPO_BRANCH:-${GLOBAL_REPO_BRANCH:-main}}"
|
||||
TARGET_REPO_DIR="${TARGET_REPO_DIR:-}"
|
||||
TARGET_ENV_FILE="${TARGET_ENV_FILE:-}"
|
||||
TARGET_ENABLE_BOOTSTRAP="${TARGET_ENABLE_BOOTSTRAP:-${GLOBAL_ENABLE_BOOTSTRAP:-yes}}"
|
||||
|
||||
[[ -n "$TARGET_HOST" ]] || fail "TARGET_HOST manquante"
|
||||
[[ "$TARGET_PORT" =~ ^[0-9]+$ ]] || fail "TARGET_PORT invalide"
|
||||
[[ -n "$TARGET_USER" ]] || fail "TARGET_BOOTSTRAP_USER manquante"
|
||||
[[ -n "$TARGET_SSH_KEY" ]] || fail "TARGET_BOOTSTRAP_SSH_KEY manquante"
|
||||
[[ -f "$TARGET_SSH_KEY" ]] || fail "clé SSH cible introuvable : $TARGET_SSH_KEY"
|
||||
[[ -r "$TARGET_SSH_KEY" ]] || fail "clé SSH cible non lisible : $TARGET_SSH_KEY"
|
||||
|
||||
if [[ -z "$REQUEST_ID" ]]; then
|
||||
REQUEST_ID="$(date '+%Y%m%d%H%M%S')_$$"
|
||||
fi
|
||||
[[ -n "$TARGET_REPO_URL" ]] || fail "GLOBAL_REPO_URL/TARGET_REPO_URL manquant"
|
||||
[[ -n "$TARGET_REPO_BRANCH" ]] || fail "GLOBAL_REPO_BRANCH/TARGET_REPO_BRANCH manquant"
|
||||
[[ -n "$TARGET_REPO_DIR" ]] || fail "TARGET_REPO_DIR manquante"
|
||||
[[ -n "$TARGET_ENV_FILE" ]] || fail "TARGET_ENV_FILE manquante"
|
||||
|
||||
TARGET_ENABLE_BOOTSTRAP="$(to_bool_yes_no "$TARGET_ENABLE_BOOTSTRAP")" || fail "TARGET_ENABLE_BOOTSTRAP invalide"
|
||||
|
||||
BOOTSTRAP_SCRIPT_LOCAL="${SCRIPT_DIR}/bootstrap-target-host.sh"
|
||||
[[ -f "$BOOTSTRAP_SCRIPT_LOCAL" ]] || fail "script bootstrap introuvable : $BOOTSTRAP_SCRIPT_LOCAL"
|
||||
[[ -x "$BOOTSTRAP_SCRIPT_LOCAL" ]] || chmod 700 "$BOOTSTRAP_SCRIPT_LOCAL" || fail "chmod impossible sur $BOOTSTRAP_SCRIPT_LOCAL"
|
||||
|
||||
if [[ -z "$REQUESTED_DB" ]]; then
|
||||
DBS_FOR_TARGET="${TARGET_DBS:-}"
|
||||
if [[ "$NON_INTERACTIVE" == "yes" ]]; then
|
||||
fail "REQUESTED_DB manquante en mode non interactif"
|
||||
fi
|
||||
|
||||
read -r -a DBS_ARRAY <<< "$DBS_FOR_TARGET"
|
||||
[[ "${#DBS_ARRAY[@]}" -gt 0 ]] || fail "TARGET_DBS vide"
|
||||
|
||||
if is_tty; then
|
||||
echo "Bases disponibles :"
|
||||
for i in "${!DBS_ARRAY[@]}"; do
|
||||
echo " $((i + 1))) ${DBS_ARRAY[$i]}"
|
||||
done
|
||||
echo
|
||||
read -r -p "Nom exact de la base à restaurer : " REQUESTED_DB
|
||||
[[ -n "$REQUESTED_DB" ]] || fail "nom de base vide"
|
||||
else
|
||||
fail "REQUESTED_DB manquante et aucune interaction terminal disponible"
|
||||
fi
|
||||
@@ -229,15 +204,52 @@ fi
|
||||
|
||||
[[ "$REQUESTED_DB" =~ ^[a-zA-Z0-9_]+$ ]] || fail "nom de base invalide"
|
||||
|
||||
if [[ "$TARGET_ENABLE_BOOTSTRAP" == "yes" ]]; then
|
||||
log "Bootstrap initial activé pour la cible ${TARGET}"
|
||||
BOOTSTRAP_JSON="/tmp/bootstrap_target_${REQUEST_ID}.json"
|
||||
|
||||
"$BOOTSTRAP_SCRIPT_LOCAL" \
|
||||
--global-env-file "$GLOBAL_ENV_FILE" \
|
||||
--targets-dir "$TARGETS_DIR" \
|
||||
--target "$TARGET" \
|
||||
--json-only >"$BOOTSTRAP_JSON" || {
|
||||
cat "$BOOTSTRAP_JSON" 2>/dev/null || true
|
||||
fail "échec du bootstrap initial de la cible ${TARGET}"
|
||||
}
|
||||
|
||||
BOOTSTRAP_STATUS="$(
|
||||
python3 - <<'PY' "$BOOTSTRAP_JSON"
|
||||
import json, sys
|
||||
with open(sys.argv[1], 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
print(data.get("status", "error"))
|
||||
PY
|
||||
)"
|
||||
|
||||
if [[ "$BOOTSTRAP_STATUS" != "success" ]]; then
|
||||
cat "$BOOTSTRAP_JSON"
|
||||
fail "bootstrap initial échoué pour la cible ${TARGET}"
|
||||
fi
|
||||
|
||||
log "Bootstrap initial terminé pour ${TARGET}"
|
||||
else
|
||||
log "Bootstrap initial désactivé pour ${TARGET}"
|
||||
fi
|
||||
|
||||
SSH_OPTS=(
|
||||
-i "$TARGET_SSH_KEY"
|
||||
-p "$TARGET_SSH_PORT"
|
||||
-p "$TARGET_PORT"
|
||||
-o IdentitiesOnly=yes
|
||||
-o BatchMode=yes
|
||||
-o ConnectTimeout="$TARGET_SSH_CONNECT_TIMEOUT"
|
||||
-o StrictHostKeyChecking=yes
|
||||
-o StrictHostKeyChecking=accept-new
|
||||
-o ConnectTimeout=8
|
||||
)
|
||||
|
||||
ssh "${SSH_OPTS[@]}" "${TARGET_USER}@${TARGET_HOST}" "exit 0" >/dev/null 2>&1 \
|
||||
|| fail "connexion SSH impossible vers la cible ${TARGET_USER}@${TARGET_HOST}"
|
||||
|
||||
TARGET_CORE_SCRIPT="${TARGET_REPO_DIR}/rebuild-bdd-core.sh"
|
||||
|
||||
REMOTE_BOOTSTRAP_CMD="
|
||||
set -euo pipefail
|
||||
|
||||
@@ -291,9 +303,12 @@ PY
|
||||
|
||||
if [[ \"\$PRECHECK_STATUS\" != \"success\" ]]; then
|
||||
cat \"\$PRECHECK_JSON\"
|
||||
rm -f \"\$PRECHECK_JSON\"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -f \"\$PRECHECK_JSON\"
|
||||
|
||||
exec \"\$CORE_SCRIPT\" \
|
||||
--env-file \"\$TARGET_ENV_FILE\" \
|
||||
--db \"\$REQUESTED_DB\" \
|
||||
@@ -304,4 +319,4 @@ exec \"\$CORE_SCRIPT\" \
|
||||
--json-only
|
||||
"
|
||||
|
||||
exec ssh "${SSH_OPTS[@]}" "${TARGET_USER}@${TARGET_HOST}" "$REMOTE_BOOTSTRAP_CMD"
|
||||
ssh "${SSH_OPTS[@]}" "${TARGET_USER}@${TARGET_HOST}" "$REMOTE_BOOTSTRAP_CMD"
|
||||
Reference in New Issue
Block a user