417 lines
14 KiB
Bash
Executable File
417 lines
14 KiB
Bash
Executable File
#!/bin/bash
|
|
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
|
|
|
|
function display_help() {
|
|
uecho "$scriptname: Déploiement distant avec runs
|
|
|
|
USAGE
|
|
$scriptname [-h host] [-T tmproot] rscriptname name=value...
|
|
$scriptname [-h host] [-T tmproot] @recipe name=value...
|
|
$scriptname [-h host] [-T tmproot] -f rscript name=value...
|
|
$scriptname [-h host] [-T tmproot] -r recipe name=value...
|
|
|
|
Lancer ce script sans argument (hors options) est équivalent à le lancer avec
|
|
l'argument @default
|
|
|
|
OPTIONS
|
|
-C Ne pas faire le déploiement. Configurer uniquement la connexion par clé
|
|
sur les hôtes distants spécifiés pour le user spécifié. Il faut pouvoir
|
|
se connecter par mot de passe pour configurer la connexion par clé.
|
|
Si l'on veut configurer la connexion par clé pour le user root, mais que
|
|
ce n'est pas possible de se connecter par mot de passe avec le user root
|
|
sur l'hôte distant, et qu'il existe un user sudoer sur l'hôte distant,
|
|
il est possible de faire la configuration avec '--configure root'. La
|
|
commande serait alors
|
|
$scriptname -h user@host --configure root
|
|
-T tmproot
|
|
Spécifier le répertoire temporaire sur l'hôte distant, comme par exemple
|
|
/var/tmp. Cette option est utile pour les vservers, qui ont par défaut
|
|
un /tmp minuscule de 16 Mo.
|
|
-S ssh
|
|
Spécifier le programme à utiliser pour la connection par ssh.
|
|
-h host
|
|
-h @hostsfile
|
|
Spécifier un hôte distant sur lequel faire le déploiement. Plusieurs
|
|
options -h peuvent être spécifiées, ou alors on peut séparer plusieurs
|
|
hôtes par ':', e.g. -h host1:host2
|
|
Par défaut, la connexion sur l'hôte distant se fait avec l'utilisateur
|
|
root. Il est possible de spécifier un autre utilisateur avec la syntaxe
|
|
user@host, e.g -h $USER@host
|
|
La forme @hostsfile permet de lire la liste des hôtes depuis le fichier
|
|
hostsfile, à raison d'un hôte par ligne.
|
|
Si cette option n'est pas spécifiée, et que le répertoire courant est
|
|
dans un des répertoires de \$RUNSHOSTSPATH, sélectionner l'hôte
|
|
correspondant. Sinon, l'utilisateur doit saisir l'hôte distant de façon
|
|
interactive.
|
|
-f RSCRIPT
|
|
Lancer le script individuel spécifié au lieu de chercher dans les
|
|
répertoires \$RUNS{SCRIPTS,HOSTS}PATH
|
|
-r RECIPE
|
|
Lancer les scripts spécifiés dans le fichier de recettes individuel
|
|
spécifié.
|
|
-z Forcer la réinstallation des scripts qui se basent sur shouldrun/setdone
|
|
-o OUTPUT
|
|
Générer l'archive à lancer sur l'hôte distant au lieu de faire le
|
|
déploiement. Si plusieurs hôtes sont spécifiés, OUTPUT est considéré
|
|
comme un nom de base auquel est ajouté le nom de l'hôte sur lequel
|
|
l'archive doit être déployée.
|
|
--init
|
|
--no-init
|
|
Forcer (resp. empêcher) la création des répertoires d'hôte correspondant
|
|
aux hôtes spécifiés. Par défaut, la création des répertoires d'hôte est
|
|
effectuée uniquement si ce script est lancé sans argument.
|
|
--sysinfos
|
|
Après un déploiement réussi sur l'hôte distant, inscrire si ce n'est
|
|
déjà fait le résultat de la commande usysinfos dans le fichier
|
|
sysinfos.conf du répertoire d'hôte.
|
|
Cette option est automatiquement activée si ce script est lancé sans
|
|
argument (hors options)."
|
|
}
|
|
|
|
source "$(dirname "$0")/ulib/ulib" &&
|
|
urequire DEFAULTS runs ||
|
|
exit 1
|
|
|
|
set_defaults runs
|
|
|
|
action=deploy
|
|
confuser=
|
|
tmproot=
|
|
hosts=()
|
|
force_make_archive=
|
|
rscripts=()
|
|
recipes=()
|
|
runsreset=
|
|
output=
|
|
init=auto
|
|
init_sysinfos=
|
|
sysinfos_data=
|
|
runsscriptspath=
|
|
runsmodulespath=
|
|
runshostspath=
|
|
parse_opts "${PRETTYOPTS[@]}" \
|
|
--help '$exit_with display_help' \
|
|
-C action=configure \
|
|
--configure: '$set@ confuser;action=configure' \
|
|
-T:,--tmproot: tmproot= \
|
|
-S: SSH= \
|
|
--force-make-archive force_make_archive=1 \
|
|
-h:,-H:,--host: hosts \
|
|
-f: rscripts \
|
|
-r: recipes \
|
|
-z runsreset=1 \
|
|
-o: output= \
|
|
--init init=1 \
|
|
--no-init init= \
|
|
--sysinfos init_sysinfos=1 \
|
|
--runsscriptspath: runsscriptspath= \
|
|
--runsmodulespath: runsmodulespath= \
|
|
--runshostspath: runshostspath= \
|
|
@ args -- "$@" && set -- "${args[@]}" || die "$args"
|
|
|
|
[ -n "$runsscriptspath" ] && RUNSSCRIPTSPATH="$runsscriptspath"
|
|
[ -n "$runsmodulespath" ] && RUNSMODULESPATH="$runsmodulespath"
|
|
[ -n "$runshostspath" ] && RUNSHOSTSPATH="$runshostspath"
|
|
|
|
__PARSED_HOSTS=()
|
|
__PARSED_FILES=()
|
|
function parse_hostsfile() {
|
|
# Lire chacun des fichiers $* et initialiser __PARSED_HOSTS avec la liste
|
|
# des hôtes mentionnés dans les fichiers.
|
|
local inputfile basedir inputs input
|
|
for inputfile in "$@"; do
|
|
inputfile="$(abspath "$inputfile")"
|
|
array_contains __PARSED_FILES "$inputfile" && {
|
|
ewarn "$(ppath "$inputfile"): inclusion récursive"
|
|
continue
|
|
}
|
|
array_add __PARSED_FILES "$inputfile"
|
|
basedir="$(dirname "$inputfile")"
|
|
|
|
array_from_lines inputs "$(<"$inputfile" filter_conf)" || {
|
|
ewarn "$inputfile: fichier ingnoré"
|
|
continue
|
|
}
|
|
for input in "${inputs[@]}"; do
|
|
if [ "${input#@}" != "$input" ]; then
|
|
# fichier inclus
|
|
parse_hostsfile "$(abspath "${input#@}" "$basedir")"
|
|
else
|
|
array_addu __PARSED_HOSTS "$input"
|
|
fi
|
|
done
|
|
done
|
|
}
|
|
function __dot_is_localhost() { [ "$1" == "." ] && echo "localhost" || echo "$1"; }
|
|
function fix_hosts() {
|
|
# Si hosts contient des éléments multiple, comme a:b, séparer ces
|
|
# éléments. i.e (a b:c) --> (a b c)
|
|
# Supporter la syntaxe @hostsfile qui permet de charger la liste des hôtes
|
|
# depuis un fichier.
|
|
# Remplacer aussi les '.' par 'localhost'
|
|
array_fix_paths hosts
|
|
local -a _hosts _tmphosts host
|
|
for host in "${hosts[@]}"; do
|
|
host="${host%/}"
|
|
if [ "${host#@}" != "$host" ]; then
|
|
__PARSED_HOSTS=()
|
|
parse_hostsfile "${host#@}"
|
|
array_fix_paths __PARSED_HOSTS
|
|
array_extendu _hosts __PARSED_HOSTS
|
|
else
|
|
array_addu _hosts "$host"
|
|
fi
|
|
done
|
|
array_copy hosts _hosts
|
|
array_map hosts __dot_is_localhost
|
|
}
|
|
|
|
################################################################################
|
|
# Configuration de l'accès par clé aux hôtes
|
|
|
|
if [ "$action" == "configure" ]; then
|
|
[ -n "${hosts[*]}" ] || hosts=("$@")
|
|
[ -n "${hosts[*]}" ] || die "Vous devez spécifier la liste des hôtes à configurer"
|
|
fix_hosts
|
|
|
|
runs_initdomains
|
|
for userhost in "${hosts[@]}"; do
|
|
splituserhost "$userhost" user host
|
|
[ -n "$user" ] || user=root
|
|
|
|
if [ "$host" != "localhost" ]; then
|
|
splithost "$host" hostname domain
|
|
if [ -z "$domain" ]; then
|
|
host="$(runs_find_host "$host")"
|
|
splithost "$host" hostname domain
|
|
fi
|
|
[ -n "$domain" ] || host="$(runs_add_domain "$host")"
|
|
fi
|
|
|
|
"$scriptdir/ruinst" -C ${confuser:+--configure "$confuser"} \
|
|
${tmproot:+--tmproot "$tmproot"} ${SSH:+-S "$SSH"} \
|
|
${force_make_archive:+--force-make-archive} \
|
|
-h "$user@$host" || continue
|
|
etitle "Configuration initiale pour runs" \
|
|
"$scriptdir/runs" \
|
|
${runsscriptspath:+--runsscriptspath "$runsscriptspath"} \
|
|
${runsmodulespath:+--runsmodulespath "$runsmodulespath"} \
|
|
${runshostspath:+--runshostspath "$runshostspath"} \
|
|
--init -h "$host"
|
|
done
|
|
exit 0
|
|
fi
|
|
|
|
################################################################################
|
|
# Déploiement
|
|
|
|
SSH="${SSH:-ssh}"
|
|
make_archive=
|
|
|
|
## Hôtes sur lesquels faire le déploiement
|
|
if array_isempty hosts; then
|
|
array_split hostsdirs "$RUNSHOSTSPATH" :
|
|
cwd="$(pwd)"
|
|
for hostsdir in "${hostsdirs[@]}"; do
|
|
hostsdir="$(abspath "$hostsdir")"
|
|
if [ "${cwd#$hostsdir/}" != "$cwd" ]; then
|
|
host="${cwd#$hostsdir/}"
|
|
host="${host%%/*}"
|
|
enote "Sélection automatique de l'hôte $host"
|
|
hosts=("$host")
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
if array_isempty hosts; then
|
|
read_value "Entrez une liste d'hôtes séparés par ':'" hosts "localhost"
|
|
fi
|
|
fix_hosts
|
|
array_isempty hosts && die "Vous devez spécifier l'hôte distant avec l'option -h"
|
|
|
|
## Déploiement
|
|
|
|
# si on ne précise pas ce qu'il faut déployer, il faudra utiliser une valeur par
|
|
# défaut: @default pour root, @userdefault pour les autres utilisateurs
|
|
runs_defaults=
|
|
if [ -z "$*" -a -z "${rscripts[*]}" -a -z "${recipes[*]}" ]; then
|
|
runs_defaults=1
|
|
[ "$init" == auto ] && init=1
|
|
init_sysinfos=1
|
|
fi
|
|
[ "$init" == auto ] && init=
|
|
|
|
ac_set_tmpdir exportdir
|
|
exportdir="$exportdir/runsexport"
|
|
|
|
runs_initdomains
|
|
for userhost in "${hosts[@]}"; do
|
|
continue=
|
|
|
|
etitle "$userhost"
|
|
|
|
# à cause des 'continue' ci-dessous, il est possible que le nettoyage ne
|
|
# soit pas encore fait. Le faire ici
|
|
if [ -d "$exportdir" -o -n "$archive" ]; then
|
|
etitle "Nettoyage de l'export"
|
|
rm -rf "$exportdir"
|
|
[ -n "$archive" ] && rm -f "$archive"
|
|
archive=
|
|
eend
|
|
fi
|
|
|
|
splituserhost "$userhost" user host
|
|
prevhost="$host"
|
|
if [ "$host" == "localhost" ]; then
|
|
[ -n "$user" ] || user="$USER"
|
|
else
|
|
[ -n "$user" ] || user=root
|
|
|
|
splithost "$host" hostname domain
|
|
if [ -z "$domain" ]; then
|
|
host="$(runs_find_host "$host")"
|
|
splithost "$host" hostname domain
|
|
fi
|
|
[ -n "$domain" ] || host="$(runs_add_domain "$host")"
|
|
fi
|
|
if [ "$host" != "$prevhost" ]; then
|
|
enote "Le nom d'hôte utilisé est $host"
|
|
fi
|
|
|
|
if [ "$host" != "localhost" ]; then
|
|
if [ -n "$init" ]; then
|
|
action=--init
|
|
else
|
|
action=--verify
|
|
fi
|
|
|
|
estep "Vérification de la configuration de l'hôte pour runs"
|
|
"$scriptdir/runs" \
|
|
${runsscriptspath:+--runsscriptspath "$runsscriptspath"} \
|
|
${runsmodulespath:+--runsmodulespath "$runsmodulespath"} \
|
|
${runshostspath:+--runshostspath "$runshostspath"} \
|
|
$action -h "$host" || { eend; continue; }
|
|
fi
|
|
|
|
if [ -n "$runs_defaults" ]; then
|
|
# Si on n'a pas précisé ce qu'il faut déployer, calculer une valeur par
|
|
# défaut
|
|
if [ "$user" == "root" ]; then
|
|
set -- @default
|
|
else
|
|
set -- @userdefault
|
|
fi
|
|
fi
|
|
|
|
# Création de l'export
|
|
etitle "Préparation de l'export"
|
|
args=(--export --runsexportdir "$exportdir"
|
|
${runsscriptspath:+--runsscriptspath "$runsscriptspath"}
|
|
${runsmodulespath:+--runsmodulespath "$runsmodulespath"}
|
|
${runshostspath:+--runshostspath "$runshostspath"}
|
|
)
|
|
[ "$host" != "localhost" ] && args=("${args[@]}" -h "$host")
|
|
[ -n "$runsreset" ] && args=("${args[@]}" -z)
|
|
for rscript in "${rscripts[@]}"; do
|
|
args=("${args[@]}" -f "$rscript")
|
|
done
|
|
for recipe in "${recipes[@]}"; do
|
|
args=("${args[@]}" -r "$recipe")
|
|
done
|
|
"$scriptdir/runs" "${args[@]}" -- "$@" || { eend; eend; continue; }
|
|
eend
|
|
|
|
# Lancement des scripts locaux
|
|
etitle "Lancement des scripts locaux"
|
|
(cd "$exportdir"; ./runs-local) || { eend; eend; continue; }
|
|
eend
|
|
|
|
# Création de l'archive
|
|
make_archive=
|
|
if [ -n "$force_make_archive" ]; then
|
|
make_archive=1
|
|
elif [ "$host" != "localhost" ]; then
|
|
make_archive=1
|
|
elif [ -n "$output" ]; then
|
|
make_archive=1
|
|
fi
|
|
|
|
archive=
|
|
archivename=
|
|
if [ -n "$make_archive" ]; then
|
|
if [ -n "$output" ]; then
|
|
if [ "${#hosts[*]}" -gt 1 ]; then
|
|
archive="$(dirname "$output")"
|
|
archivename="$(basename "$output")"
|
|
splitname "$archivename" basename ext
|
|
archive="$archive/$basename-$host${ext:+.$ext}"
|
|
else
|
|
archive="$output"
|
|
fi
|
|
else
|
|
ac_set_tmpfile archive
|
|
fi
|
|
archivename="$(basename "$archive")"
|
|
|
|
[ -z "$output" ] && tmp_archive=1 || tmp_archive=
|
|
etitle "Création de l'archive pour le déploiement" \
|
|
"$scriptdir/mkusfx" --bare ${tmp_archive:+--tmp-archive} -o "$archive" \
|
|
"$exportdir" ./runs-remote || continue
|
|
fi
|
|
|
|
if [ -z "$output" ]; then
|
|
# Déploiement
|
|
if [ "$host" == "localhost" ]; then
|
|
etitle "Déploiement sur l'hôte local"
|
|
if [ -n "$force_make_archive" ]; then
|
|
"$archive" ${tmproot:+--tmproot "$tmproot"}
|
|
else
|
|
(cd "$exportdir"; ./runs-remote)
|
|
fi
|
|
eend
|
|
# Réinitialiser sysinfos_data, la valeur de l'hôte courant sera
|
|
# utilisé par runs
|
|
sysinfos_data=
|
|
else
|
|
etitle "Déploiement sur $user@$host"
|
|
estep "Copie de l'archive"
|
|
if scp -S "$SSH" "$archive" "$user@$host:"; then
|
|
estep "Lancement du script de déploiement"
|
|
"$SSH" -qt "$user@$host" "\
|
|
__estack=$(quoted_arg "$__estack")
|
|
__tlevel=$(quoted_arg "$__tlevel")
|
|
export __estack __tlevel
|
|
${UTOOLS_LANG:+UTOOLS_LANG='$UTOOLS_LANG'; export UTOOLS_LANG
|
|
}$(quoted_args "./$archivename" ${tmproot:+--tmproot "$tmproot"})"
|
|
if [ -n "$init_sysinfos" ]; then
|
|
estep "Calcul des informations de l'hôte distant"
|
|
sysinfos_script='
|
|
source /etc/ulib 2>/dev/null && urequire DEFAULTS || exit 1
|
|
echo "\
|
|
sysname=(${MYSYSNAME[*]})
|
|
sysdist=(${MYSYSDIST[*]})
|
|
sysver=(${MYSYSVER[*]})
|
|
bits=$MYBITS"'
|
|
sysinfos_data="$("$SSH" -qt "$user@$host" "$sysinfos_script" | _nl2lf)"
|
|
fi
|
|
fi
|
|
eend
|
|
fi
|
|
|
|
if [ -n "$init_sysinfos" -a -n "$sysinfos_data" ]; then
|
|
args=(-y --init --sysinfos "$sysinfos_data")
|
|
[ "$host" != "localhost" ] && args=("${args[@]}" -h "$host")
|
|
etitle "Configuration de sysinfos.conf" \
|
|
"$scriptdir/runs" "${args[@]}"
|
|
fi
|
|
|
|
etitle "Nettoyage de l'export"
|
|
rm -rf "$exportdir"
|
|
[ -n "$archive" ] && rm -f "$archive"
|
|
archive=
|
|
eend
|
|
fi
|
|
|
|
eend
|
|
done
|