391 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			391 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
 | 
						|
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] [-G 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
 | 
						|
    -G, --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.
 | 
						|
    --deploydb
 | 
						|
    --nd, --no-deploydb
 | 
						|
        Autoriser (respectivement interdire) l'utilisation de la configuration
 | 
						|
        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'"
 | 
						|
}
 | 
						|
 | 
						|
__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
 | 
						|
}
 | 
						|
 | 
						|
set_defaults pubkeys
 | 
						|
 | 
						|
action=deploy
 | 
						|
confuser=
 | 
						|
uses_su=
 | 
						|
tmproot=
 | 
						|
SSH=
 | 
						|
force_make_archive=
 | 
						|
hosts=()
 | 
						|
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 \
 | 
						|
    -G:,--tmproot: tmproot= \
 | 
						|
    -S:,--ssh: SSH= \
 | 
						|
    --force-make-archive force_make_archive=1 \
 | 
						|
    -h:,-H:,--host: hosts \
 | 
						|
    --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}"
 | 
						|
 | 
						|
################################################################################
 | 
						|
# 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 "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
 | 
						|
 | 
						|
# 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)
 | 
						|
    has_src=
 | 
						|
else
 | 
						|
    # Enlever le répertoire à déployer. Le reste est pour uinst
 | 
						|
    src="$1"
 | 
						|
    has_src=1
 | 
						|
    shift
 | 
						|
fi
 | 
						|
 | 
						|
# configuration locale de déploiement
 | 
						|
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
 | 
						|
    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
 | 
						|
 | 
						|
## 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)
 | 
						|
    read_value "Entrez le répertoire à déployer" src . O
 | 
						|
    has_src=1
 | 
						|
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?
 | 
						|
make_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
 | 
						|
    eend
 | 
						|
fi
 | 
						|
 | 
						|
## Déploiement
 | 
						|
 | 
						|
# sur l'hôte distant, ne rendre non interactif qu'à partir de -yy
 | 
						|
rinteraction=$__interaction
 | 
						|
[ $rinteraction -lt 2 ] && rinteraction=$(($rinteraction + 1))
 | 
						|
 | 
						|
for host in "${hosts[@]}"; do
 | 
						|
    if [ "$host" == "localhost" ]; then
 | 
						|
        etitle "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 "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
 |