nutools/ruinst

392 lines
14 KiB
Plaintext
Raw Normal View History

2013-08-27 15:14:44 +04:00
#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
source "$(dirname -- "$0")/lib/ulib/auto" || exit 1
urequire pyulib/pyulib
2013-08-27 15:14:44 +04:00
function display_help() {
uecho "$scriptname: Déploiement distant avec uinst
USAGE
2018-04-26 23:21:38 +04:00
$scriptname [-h host] [-G tmproot] <file|archive|dir> [-- options de uinst]
2013-08-27 15:14:44 +04:00
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é
2013-08-27 15:14:44 +04:00
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
2015-08-19 10:23:07 +04:00
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
2018-04-26 23:21:38 +04:00
-G, --tmproot TMPROOT
2013-08-27 15:14:44 +04:00
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
2013-08-27 15:14:44 +04:00
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'
2013-08-27 15:14:44 +04:00
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.
2018-04-26 23:21:38 +04:00
--deploydb
--nd, --no-deploydb
Autoriser (respectivement interdire) l'utilisation de la configuration
2018-04-26 23:21:38 +04:00
locale de déploiement pour identifier la source et/ou la destination
s'ils ne sont pas spécifiés. Par défaut, la configuration locale de
déploiement est utilisée.
-p, --dp, --deploydb-profile PROFILENAME
Spécifier un ou plusieurs profils séparés par des virgules pour le
déploiement avec la configuration locale de déploiement. NONE est la
valeur par défaut et signifie de ne sélectionner que les définitions
sans profil. ALL signifie de ne pas tenir compte des profils dans les
définitions.
-A, --all-profiles
-P, --prod
-T, --test
Raccourcis respectivement pour -pALL, -pprod et -ptest
-c, --dc, --deploydb-config CONFNAME
Cette option permet de spécifier un fichier de configuration ou le nom
de la configuration locale de déploiement à utiliser pour effectuer la
requête. Par défaut, utiliser le nom 'uinst.conf'"
2013-08-27 15:14:44 +04:00
}
__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")"
2015-08-20 07:58:17 +04:00
2013-08-27 15:14:44 +04:00
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
}
2013-08-27 15:14:44 +04:00
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 " "
2013-08-27 15:14:44 +04:00
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
}
set_defaults pubkeys
action=deploy
confuser=
uses_su=
tmproot=
SSH=
force_make_archive=
hosts=()
2018-04-26 23:21:38 +04:00
ddb_enable=1
ddb_profile=NONE
ddb_conf=uinst.conf
parse_opts "${PRETTYOPTS[@]}" \
--help '$exit_with display_help' \
-C,--configure-user action=configure \
--configure: '$set@ confuser;action=configure' \
--uses-su uses_su=1 \
2018-04-26 23:21:38 +04:00
-G:,--tmproot: tmproot= \
-S:,--ssh: SSH= \
--force-make-archive force_make_archive=1 \
-h:,-H:,--host: hosts \
2018-04-26 23:21:38 +04:00
--deploydb ddb_enable=1 \
--nd,--no-deploydb ddb_enable= \
-p:,--dp:,--deploydb-profile ddb_profile= \
-A,--all-profiles ddb_profile=ALL \
-P,--prod ddb_profile=prod \
-T,--test ddb_profile=test \
-c:,--dc:,--deploydb-config ddb_conf= \
@ args -- "$@" && set -- "${args[@]}" || die "$args"
: "${SSH:=ssh}"
2013-08-27 15:14:44 +04:00
################################################################################
# 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}' \
2015-08-19 10:23:07 +04:00
<"$ULIBDIR/support/install-pubkeys.sh" >"$IPKS"
2013-08-27 15:14:44 +04:00
chmod +x "$IPKS"
}
function check_pubkey_access() {
2015-08-19 10:23:07 +04:00
local user= host="$1" confuser="$2" ipks="$3" uses_su="$4"
2013-08-27 15:14:44 +04:00
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:" &&
2015-08-19 10:23:07 +04:00
"$SSH" -o ConnectTimeout=2 -qt "$user@$host" "./$(basename "$ipks") '$confuser' ${uses_su:+su}" ||
2013-08-27 15:14:44 +04:00
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é."
2013-08-27 15:14:44 +04:00
[ -n "${hosts[*]}" ] || hosts=("$@")
[ -n "${hosts[*]}" ] || die "Vous devez spécifier la liste des hôtes à configurer"
fix_hosts
IPKS=
[ -n "$confuser" ] && prepare_pubkey_access
2018-04-26 23:21:38 +04:00
etitle "Configuration de l'accès par clé ssh"
2013-08-27 15:14:44 +04:00
for host in "${hosts[@]}"; do
2015-08-19 10:23:07 +04:00
etitle "$host" check_pubkey_access "$host" "$confuser" "$IPKS" "$uses_su"
2013-08-27 15:14:44 +04:00
done
eend
exit 0
fi
################################################################################
# Déploiement
2018-04-26 23:21:38 +04:00
# quels informations avons-nous?
array_isempty hosts && has_hosts= || has_hosts=1
if [ $# -eq 0 ] || [[ "$1" == -* ]] || [[ "$1" == *=* ]]; then
# pas d'argument, ou c'est une option (qui fait donc partie des arguments de uinst)
2018-04-26 23:21:38 +04:00
has_src=
2013-08-27 15:14:44 +04:00
else
# Enlever le répertoire à déployer. Le reste est pour uinst
src="$1"
2018-04-26 23:21:38 +04:00
has_src=1
2013-08-27 15:14:44 +04:00
shift
fi
2018-04-26 23:21:38 +04:00
# configuration locale de déploiement
ddb_enable= #XXX 05/04/2021 désactiver deploydb pour le moment
2018-04-26 23:21:38 +04:00
if [ -n "$ddb_enable" ]; then
# filtrer les options... elles seront ignorées si on passe finalement par
# deploydb
vars=()
options=()
for arg in "$@"; do
if [[ "$arg" == *=* ]]; then
array_add vars "$arg"
else
array_add options "$arg"
fi
2018-04-26 23:21:38 +04:00
done
deploydb=(
"$scriptdir/lib/nulib/deploydb"
--missing-ok
${ddb_conf:+-c "$ddb_conf"}
-m uinst
--run -r uinst.query_xuinst "$script"
)
cmds=()
if [ -n "$has_hosts" ]; then
fix_hosts
for host in "${hosts[@]}"; do
array_from_lines tmpcmds "$("${deploydb[@]}" "$src" "$host" "$ddb_profile" "${vars[@]}")"
array_extend cmds tmpcmds
done
elif [ -n "$has_src" ]; then
array_from_lines tmpcmds "$("${deploydb[@]}" "$src" "" "$ddb_profile" "${vars[@]}")"
array_extend cmds tmpcmds
else
read_value "Entrez le répertoire à déployer" src . O
has_src=1
array_from_lines tmpcmds "$("${deploydb[@]}" "$src" "" "$ddb_profile" "${vars[@]}")"
array_extend cmds tmpcmds
fi
if [ ${#cmds[*]} -gt 0 ]; then
[ ${#options[*]} -gt 0 ] && ewarn "Les options supplémentaires '${options[*]}' seront ignorées"
if check_interaction -c; then
if [ ${#cmds[*]} -eq 1 ]; then
einfo "La commande suivante va être lancée:"
eecho "\$ $script --no-deploydb \\"
for cmd in "${cmds[0]}"; do
eecho " ${cmd#$script --no-deploydb }"
done
else
einfo "Les commandes suivantes seront lancées:"
eecho "\$ $script --no-deploydb \\"
for cmd in "${cmds[@]}"; do
eecho " ... ${cmd#$script --no-deploydb }"
done
fi
read -p "Confirmez ou attendez 4 secondes [On] " -t 4 r
if [ $? -gt 128 ]; then
echo # cosmetic
elif [ $? -le 128 -a -n "$r" ]; then
is_yes "$r" || die
fi
fi
r=0
for cmd in "${cmds[@]}"; do
einfo "$cmd"
eval "$cmd" || r=1
done
exit $r
elif [ "$ddb_profile" != "ALL" ]; then
ewarn "Aucune configuration locale de déploiement n'a été trouvée pour le profil $ddb_profile"
fi
fi
2018-04-26 23:21:38 +04:00
## Répertoires à déployer et hôtes sur lesquels faire le déploiement
if [ -z "$has_src" ]; then
# pas d'argument, ou c'est une option (qui fait donc partie des arguments de uinst)
2018-04-26 23:21:38 +04:00
read_value "Entrez le répertoire à déployer" src . O
has_src=1
fi
2013-08-27 15:14:44 +04:00
array_isempty hosts && read_value "Entrez une liste d'hôtes séparés par ':'" hosts "localhost"
fix_hosts
## Création de l'archive?
make_archive=
2013-08-27 15:14:44 +04:00
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
eend
2013-08-27 15:14:44 +04:00
fi
## Déploiement
# sur l'hôte distant, ne rendre non interactif qu'à partir de -yy
rinteraction=$__interaction
[ $rinteraction -lt 2 ] && rinteraction=$(($rinteraction + 1))
2013-08-27 15:14:44 +04:00
for host in "${hosts[@]}"; do
if [ "$host" == "localhost" ]; then
2018-04-26 23:21:38 +04:00
etitle "Déploiement sur l'hôte local"
2013-08-27 15:14:44 +04:00
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
2018-04-26 23:21:38 +04:00
etitle "Déploiement sur $user@$host"
2013-08-27 15:14:44 +04:00
estep "Copie de l'archive"
scp -S "$SSH" "$archive" "$user@$host:" || die
2013-08-27 15:14:44 +04:00
estep "Lancement du script de déploiement"
"$SSH" -qt "$user@$host" "\
__interaction=$rinteraction
__estack=$(qval "$__estack")
__tlevel=$(qval "$__tlevel")
export __interaction __estack __tlevel
2013-08-27 15:14:44 +04:00
${UTOOLS_LANG:+UTOOLS_LANG='$UTOOLS_LANG'; export UTOOLS_LANG
}$(qvals "./$archivename" ${tmproot:+--tmproot "$tmproot"} -- MYHOST="$host" "$@")"
2013-08-27 15:14:44 +04:00
eend
fi
done