305 lines
11 KiB
Bash
Executable File
305 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
|
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
|
|
source "$(dirname "$0")/lib/ulib/ulib" || exit 1
|
|
urequire DEFAULTS pyulib/pyulib
|
|
|
|
function display_help() {
|
|
uecho "$scriptname: Déploiement distant avec uinst
|
|
|
|
USAGE
|
|
$scriptname [-h host] [-T tmproot] <file|archive|dir> [-- options de uinst]
|
|
|
|
note: à cause d'une limitation de makeself, les options de uinst ne devraient
|
|
pas contenir d'espaces ni de caractères spéciaux. L'échappement de ces
|
|
caractères n'est pas garanti.
|
|
|
|
OPTIONS
|
|
-C, --configure-user
|
|
--configure USER [--uses-su]
|
|
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
|
|
Si l'hôte distant n'a pas sudo ou si sudo n'est pas configuré, il faut
|
|
rajouter l'option --uses-su, e.g:
|
|
$scriptname -h user@host --configure root --uses-su
|
|
-T, --tmproot 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 SSH
|
|
Spécifier le programme à utiliser pour la connection par ssh.
|
|
-h, --host hosts
|
|
-h, --host @hostsfile
|
|
Spécifier un ou plusieurs hôtes sur lequels faire le déploiement. Pour
|
|
spécifier plusieurs hôtes, il est possible d'utiliser plusieurs fois
|
|
l'option -h, ou spécifier en une seule fois plusieurs hôtes en les
|
|
séparant par un espace ou le caractère ':', e.g. 'host1 host2' ou
|
|
'host1:host2'. Si la spécification contient les caractères { et },
|
|
l'expansion est effectuée, e.g
|
|
-h 'root@{host1,host2}.univ.run'
|
|
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.
|
|
--deploy
|
|
--no-deploy
|
|
Autoriser (respectivement interdire) l'utilisation de la configuration
|
|
locale de déploiement pour identifier la destination si aucun hôte n'est
|
|
spécifié.
|
|
Par défaut, si aucun hôte n'est spécifié, la configuration locale de
|
|
déploiement est interrogée pour avoir cette information.
|
|
-c, --deploy-config CONFNAME
|
|
Cette option permet de spécifier le nom de la configuration à utiliser
|
|
pour effectuer la requête. Par défaut, utiliser le nom 'ruinst'"
|
|
}
|
|
|
|
set_defaults pubkeys
|
|
|
|
action=deploy
|
|
confuser=
|
|
uses_su=
|
|
tmproot=
|
|
SSH=
|
|
force_make_archive=
|
|
hosts=()
|
|
deploy_enable=1
|
|
deploy_confname=ruinst
|
|
parse_opts "${PRETTYOPTS[@]}" \
|
|
--help '$exit_with display_help' \
|
|
-C,--configure-user action=configure \
|
|
--configure: '$set@ confuser;action=configure' \
|
|
--uses-su uses_su=1 \
|
|
-T:,--tmproot: tmproot= \
|
|
-S:,--ssh: SSH= \
|
|
--force-make-archive force_make_archive=1 \
|
|
-h:,-H:,--host: hosts \
|
|
--deploy deploy_enable=1 \
|
|
--no-deploy deploy_enable= \
|
|
-c:,--deploy-config deploy_confname= \
|
|
@ args -- "$@" && set -- "${args[@]}" || die "$args"
|
|
|
|
: "${SSH:=ssh}"
|
|
|
|
__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 __expand_braces() {
|
|
if [[ "$1" == *{* ]] && [[ "$1" == *}* ]]; then
|
|
eval "echo $1"
|
|
else
|
|
echo "$1"
|
|
fi
|
|
}
|
|
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_map hosts __expand_braces
|
|
array_fix_paths hosts ":"
|
|
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
|
|
|
|
function prepare_pubkey_access() {
|
|
# générer un script d'installation de clé avec la bonne clé
|
|
ac_set_tmpfile IPKS
|
|
awkrun PUBKEYS="$PUBKEYS" '{sub(/@@PUBKEYS@@/, PUBKEYS); } {print}' \
|
|
<"$ULIBDIR/support/install-pubkeys.sh" >"$IPKS"
|
|
chmod +x "$IPKS"
|
|
}
|
|
function check_pubkey_access() {
|
|
local user= host="$1" confuser="$2" ipks="$3" uses_su="$4"
|
|
if [[ "$host" == *@* ]]; then
|
|
user="${host%%@*}"
|
|
host="${host#*@}"
|
|
else
|
|
user="$USER"
|
|
fi
|
|
[ -n "$confuser" ] || confuser="$user"
|
|
|
|
[ "$user" != "$confuser" -a -z "$ipks" ] &&
|
|
die "Il faut préparer le script d'installation de la clé avec prepare_pubkey_access()"
|
|
|
|
if [ "$user" != "$confuser" ]; then
|
|
enote "Vous pouvez être amené à entrer plusieurs fois le mot de passe de $user sur $host"
|
|
scp -S "$SSH" -o StrictHostKeyChecking=no -o ConnectTimeout=2 -q "$ipks" "$user@$host:" &&
|
|
"$SSH" -o ConnectTimeout=2 -qt "$user@$host" "./$(basename "$ipks") '$confuser' ${uses_su:+su}" ||
|
|
eerror "Impossible de copier la clé publique sur l'hôte distant"
|
|
else
|
|
"$SSH" -o StrictHostKeyChecking=no -o ConnectTimeout=2 -q "$user@$host" "PUBKEYS=$(quoted_sarg "$PUBKEYS")"'
|
|
cd ~; mkdir -p .ssh; chmod 700 .ssh
|
|
cd .ssh; touch authorized_keys; chmod 600 authorized_keys
|
|
echo "$PUBKEYS" | while read pubkey; do
|
|
grep -q "$pubkey" authorized_keys ||
|
|
echo "$pubkey" >>authorized_keys
|
|
done' ||
|
|
eerror "Impossible de copier la clé publique sur l'hôte distant"
|
|
fi
|
|
}
|
|
|
|
if [ "$action" == "configure" ]; then
|
|
if [ -z "$PUBKEYS" ]; then
|
|
sshdir=~/.ssh
|
|
for pubkey in id_rsa.pub id_dsa.pub; do
|
|
[ -f "$sshdir/$pubkey" ] && PUBKEYS="${PUBKEYS:+$PUBKEYS
|
|
}$(<"$sshdir/$pubkey")"
|
|
done
|
|
fi
|
|
[ -n "$PUBKEYS" ] || die "La connexion sur les hôtes distants ne peut se faire que par mot de passe parce que vous n'avez pas de clé publique configurée. Modifiez le fichier ~/etc/default/pubkeys si ce message est erroné."
|
|
|
|
[ -n "${hosts[*]}" ] || hosts=("$@")
|
|
[ -n "${hosts[*]}" ] || die "Vous devez spécifier la liste des hôtes à configurer"
|
|
fix_hosts
|
|
|
|
IPKS=
|
|
[ -n "$confuser" ] && prepare_pubkey_access
|
|
|
|
etitle -s "Configuration de l'accès par clé ssh"
|
|
for host in "${hosts[@]}"; do
|
|
etitle "$host" check_pubkey_access "$host" "$confuser" "$IPKS" "$uses_su"
|
|
done
|
|
eend
|
|
exit 0
|
|
fi
|
|
|
|
################################################################################
|
|
# Déploiement
|
|
|
|
make_archive=
|
|
|
|
## Répertoire à déployer
|
|
src="$1"
|
|
if [ -z "$src" ] || [[ "$src" == -* ]]; then
|
|
# pas d'argument, ou c'est une option (qui fait donc partie des arguments de uinst)
|
|
read_value "Entrez le répertoire à déployer" src "."
|
|
else
|
|
# Enlever le répertoire à déployer. Le reste est pour uinst
|
|
shift
|
|
fi
|
|
|
|
## Hôtes sur lesquels faire le déploiement
|
|
if array_isempty hosts && [ -n "$deploy_enable" ]; then
|
|
urequire deploy
|
|
deploy_setconf "$deploy_confname"
|
|
if deploy_loadconf; then
|
|
setxx module=abspath "$src" // basename --
|
|
if eval "$(deploy_query -v host DEST module ruinst_deploy "" shell "$module")"; then
|
|
check_interaction -c && einfo "Ce module sera déployé vers les hôtes suivants:
|
|
$(array_to_lines host "" " ")"
|
|
ask_any "Voulez-vous continuer?" Oq || die
|
|
array_copy hosts host
|
|
fi
|
|
fi
|
|
fi
|
|
array_isempty hosts && read_value "Entrez une liste d'hôtes séparés par ':'" hosts "localhost"
|
|
fix_hosts
|
|
|
|
## Création de l'archive?
|
|
if [ -n "$force_make_archive" ]; then
|
|
make_archive=1
|
|
else
|
|
for host in "${hosts[@]}"; do
|
|
if [ "$host" != "localhost" ]; then
|
|
make_archive=1
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
archive=
|
|
archivename=
|
|
if [ -n "$make_archive" ]; then
|
|
ac_set_tmpfile archive
|
|
archivename="$(basename "$archive")"
|
|
|
|
etitle "Création de l'archive pour le déploiement" \
|
|
"$scriptdir/mkusfx" --tmp-archive --self-contained -o "$archive" "$src" || die
|
|
fi
|
|
|
|
## Déploiement
|
|
|
|
# sur l'hôte distant, ne rendre interactif qu'à partir de -yy
|
|
rinteraction=$__interaction
|
|
[ $rinteraction -lt 2 ] && rinteraction=$(($rinteraction + 1))
|
|
|
|
for host in "${hosts[@]}"; do
|
|
if [ "$host" == "localhost" ]; then
|
|
etitle -s "Déploiement sur l'hôte local"
|
|
if [ -n "$force_make_archive" ]; then
|
|
"$archive" ${tmproot:+--tmproot "$tmproot"} -- "$@"
|
|
else
|
|
"$scriptdir/uinst" "$src" "$@"
|
|
fi
|
|
eend
|
|
else
|
|
splituserhost "$host" user host
|
|
[ -n "$user" ] || user=root
|
|
|
|
etitle -s "Déploiement sur $user@$host"
|
|
estep "Copie de l'archive"
|
|
scp -S "$SSH" "$archive" "$user@$host:" || die
|
|
estep "Lancement du script de déploiement"
|
|
"$SSH" -qt "$user@$host" "\
|
|
__interaction=$rinteraction
|
|
__estack=$(qval "$__estack")
|
|
__tlevel=$(qval "$__tlevel")
|
|
export __interaction __estack __tlevel
|
|
${UTOOLS_LANG:+UTOOLS_LANG='$UTOOLS_LANG'; export UTOOLS_LANG
|
|
}$(qvals "./$archivename" ${tmproot:+--tmproot "$tmproot"} -- MYHOST="$host" "$@")"
|
|
eend
|
|
fi
|
|
done
|