#!/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] [-- 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