#!/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 xmlsupport function display_help() { uecho "$scriptname: Déployer une ou plusieurs webapps vers Tomcat USAGE $scriptname [options] ... OPTIONS -c, --config CONFIG Spécifier un fichier de configuration qui contient les informations spécifiées par les options ci-dessous: TOMCAT_PROFILE, CATALINA_BASE, TOMCAT_USER, TOMCAT_GROUP, TOMCAT_VERSION, MANAGER_URL, MANAGER_USER, MANAGER_PASSWORD, WAMAP, BACKUP, RESTART -C, --config-profile TOMCAT_PROFILE Spécifier le profil de configuration à utiliser. Cette valeur détermine la façon dont sont calculés CATALINA_BASE, TOMCAT_USER, TOMCAT_GROUP et TOMCAT_VERSION. Les valeurs valides sont: * debian:tomcatV Tomcat installé par 'apt-get install tomcatV'. L'alias 'd:V' est aussi supporté, e.g 'debian:tomcat7' ou 'd:8' * runs:NAME Tomcat installé par 'runs tomcat/V name=NAME'. Le suffixe NAME est le nom de base des fichiers de tomcat. Pour spécifier le cas échéant la version de Tomcat, il faut utiliser l'option -V ci-dessous. L'alias NAME sans le préfixe runs: est supporté aussi. Le profil par défaut est calculé comme suit: * Si un répertoire de la forme /var/lib/tomcatV existe alors il s'agit de debian:tomcatV. * Si le lien /opt/tomcat --> /opt/apache-tomcat-V existe, vérifier l'existence du script /etc/init.d/tomcatV. Si le script existe, alors utiliser runs:tomcatV comme profil. Sinon, utiliser runs:tomcat. * Sinon, aucun profil n'est sélectionné, et il faut spécifier manuellement les options -d, -U, -G, -V -d, --catalina-base CATALINA_BASE Spécifier le répertoire de base des configurations de Tomcat. La valeur par défaut est celle de CATALINA_HOME. Cette option n'a normalement pas besoin d'être spécifiée. -U, --tomcat-user TOMCAT_USER -G, --tomcat-group TOMCAT_GROUP Spécifier l'utilisateur et le groupe propriétaires des fichiers de Tomcat. Ces options n'ont normalement pas besoin d'être spécifiées. -V, --tomcat-version TOMCAT_VERSION Spécifier la version de tomcat installée, e.g 8.0.37 pour une version complète ou simplement 8 si on ne donne que la version majeure. Cette option peut s'avérer nécessaire avec le profil runs:tomcat bien que les meilleurs efforts soient faits pour détecter la valeur effective. -g, --manager-url MANAGER_URL Spécifier l'url du manager, e.g http://localhost:8080/manager/text Par défaut, le fichier server.xml est consulté pour connaitre sur quel port écoute le serveur. -u, --user MANAGER_USER -p, --password MANAGER_PASSWORD Spécifier le compte et le mot de passe à utiliser pour piloter le manager Tomcat, notamment pour redémarrer l'application venant d'être déployée. Ce compte doit avoir le rôle manager-script à partir de Tomcat 6.0.30, et le rôle manager-gui dans les version antérieurs. Par défaut, le fichier tomcat-users.xml est consulté pour avoir cette information. -m, --wamap WAMAPS Ajouter un ou plusieurs mappings de la forme src:dest. Avec ce mapping, la webapp src est déployée avec le nom dest. Plusieurs mappings peuvent être spécifiés en les séparant par des virgules. Si src est mentionné plusieurs fois, e.g src:dest1,src:dest2 alors la webapp src est déployée plusieurs fois avec des noms distincts. --exclude EXCLUDES --replace-excludes EXCLUDES Spécifier un ensemble de fichier à exclure de la source lors du déploiement. La variante --replace-excludes permet de construire la liste des fichiers à exclure depuis zéro. La liste par défaut est: ${DEFAULT_EXCLUDES[*]} --protect PROTECTS Spécifier un ensemble de fichier à protéger dans la destination: ils ne sont jamais écrasés par les fichiers sources. Cela permet de gérer des applications qui écrivent des fichiers dans leur répertoire. --rsync-option RSYNC_OPTIONS --replace-rsync-options RSYNC_OPTIONS Spécifier les options de rsync à utiliser pour le déploiement. La variante --replace-rsync-options permet de construire la liste des options depuis zéro. La liste par défaut est: ${DEFAULT_RSYNC_OPTIONS[*]} -b, --backup --no-backup Avant le déploiement du service numérique, faire (resp. ne pas faire) une sauvegarde des fichiers actuellement déployés. -B, --backup-only Ne pas faire le déploiement. Faire uniquement la sauvegarde. -n, --no-restart Après le déploiement de la webapp, ne pas redémarrer l'application. -r, --restart-only Ne pas faire le déploiement. Redémarrer uniquement l'application. --fake Afficher simplement ce qui serait fait. Ne pas le faire réellement. --config-template Créer un modèle de configuration dans le répertoire des webapp spécifiées. Si aucune webapp n'est spécifiée, créer le fichier dans le répertoire courant." } DEFAULT_EXCLUDES=(/.git/ /.toinst.conf) DEFAULT_RSYNC_OPTIONS=(-rptL --delete) VARS=( TOMCAT_PROFILE CATALINA_BASE TOMCAT_USER TOMCAT_GROUP TOMCAT_VERSION MANAGER_URL MANAGER_USER MANAGER_PASSWORD BACKUP RESTART ) ARRAYS=( WAMAPS EXCLUDES REPLACE_EXCLUDES PROTECTS RSYNC_OPTIONS REPLACE_RSYNC_OPTIONS ) function __reset_vars() { local var for var in "${VARS[@]}"; do eval "$var=" done for var in "${ARRAYS[@]}"; do eval "$var=()" done } function __dump_vars() { local var for var in "${VARS[@]}"; do eecho "$(echo_setv2 "$var")" done for var in "${ARRAYS[@]}"; do eecho "$(echo_seta2 "$var")" done } function use_manager_script() { local v="$TOMCAT_VERSION" if [ -z "$v" ]; then return 0 elif [[ "$v" == 6.0.* ]]; then if [ "${v#6.0.}" -ge 30 ]; then return 0 else return 1 fi elif [ "${v%%.*}" -gt 6 ]; then return 0 else return 1 fi } function parse_server() { local -a lines local serverxml port serverxml="$CATALINA_BASE/conf/server.xml" array_from_lines lines "$(xpathtool -g "/Server/Service[@name='Catalina']/Connector/@port" "$serverxml")" port="${lines[0]}" if [ -n "$port" ]; then if use_manager_script; then MANAGER_URL="http://localhost:$port/manager/text" else MANAGER_URL="http://localhost:$port/manager/html" fi fi } function parse_tomcat_users() { local -a lines local usersxml role usersxml="$CATALINA_BASE/conf/tomcat-users.xml" if use_manager_script; then role=manager-script else role=manager-gui fi array_from_lines lines "$(xpathtool -g "/tomcat-users/user[contains(@roles,'$role')]/@username" "$usersxml")" MANAGER_USER="${lines[0]}" if [ -n "$MANAGER_USER" ]; then array_from_lines lines "$(xpathtool -g "/tomcat-users/user[@username='$MANAGER_USER']/@password" "$usersxml")" MANAGER_PASSWORD="${lines[0]}" fi } function build_rsync_options() { local -a excludes protects; local exclude protect if [ ${#REPLACE_EXCLUDES[*]} -gt 0 ]; then array_copy excludes REPLACE_EXCLUDES else array_copy excludes DEFAULT_EXCLUDES fi array_extend excludes EXCLUDES array_copy protects PROTECTS if [ ${#REPLACE_RSYNC_OPTIONS[*]} -gt 0 ]; then array_copy rsync_options REPLACE_RSYNC_OPTIONS else array_copy rsync_options DEFAULT_RSYNC_OPTIONS fi array_extend rsync_options RSYNC_OPTIONS local toinstconf="$1/.toinst.conf" if [ -f "$toinstconf" ]; then eval "$( EXCLUDES=() PROTECTS=() RSYNC_OPTIONS=() source "$toinstconf" array_extend excludes EXCLUDES array_extend protects PROTECTS array_extend rsync_options RSYNC_OPTIONS echo_seta2 excludes echo_seta2 protects echo_seta2 rsync_options )" fi for exclude in "${excludes[@]}"; do array_add rsync_options --exclude "$exclude" done for protect in "${PROTECTS[@]}"; do array_add rsync_options -f "P $protect" done } args=(% --help '$exit_with display_help' --config-template action=config-template --private-parse-server '$action=parse; parse_server=1' --private-parse-tomcat-users '$action=parse; parse_tomcat_users=1' --is-tmpdir '$istmpdir=1; tmpdir=.' -c:,--config: config= -C:,--config-profile: tomcat_profile= -d:,--catalina-base:,--catalina-home: catalina_base= -U:,--tomcat-user: tomcat_user= -G:,--tomcat-group: tomcat_group= -V:,--tomcat-version: tomcat_version= -g:,--manager-url: manager_url= -u:,--user: manager_user= -p:,--password: manager_password= -m:,--wamap: wamaps --exclude: excludes --replace-excludes: replace_excludes --protect: protects --rsync-option: rsync_options --replace-rsync-options: replace_rsync_options -b,--backup backup=1 --no-backup backup=0 -B,--backup-only action=backup --restart restart=1 -n,--no-restart restart=0 -r,--restart-only action=restart --fake fake=1 ) parse_args "$@"; set -- "${args[@]}" [ -n "$action" ] || action=deploy if [ "$action" == parse ]; then CATALINA_BASE="$1" TOMCAT_VERSION="$2" if [ -n "$parse_server" ]; then parse_server echo_setv2 MANAGER_URL fi if [ -n "$parse_tomcat_users" ]; then parse_tomcat_users echo_setv2 MANAGER_USER echo_setv2 MANAGER_PASSWORD fi exit 0 fi # Charger la configuration __reset_vars if [ -n "$config" ]; then eval "$( __reset_vars CATALINA_HOME= source "$config" 2>/dev/null || exit 1 [ -n "$CATALINA_BASE" ] || CATALINA_BASE="$CATALINA_HOME" __dump_vars )" fi # Traiter la ligne de commande for var in "${VARS[@]}"; do lvar="${var,,}" if [ -n "${!lvar}" ]; then setv "$var" "${!lvar}" fi done [ -n "$RESTART" ] || RESTART=1 # valeur par défaut normyesvals BACKUP RESTART for var in "${ARRAYS[@]}"; do lvar="${var,,}" if ! array_isempty "$lvar"; then array_copy "$var" "$lvar" fi done array_fix_paths WAMAPS , # Calculer le profil de connexion if [ -z "$TOMCAT_PROFILE" ]; then for v in 8 7; do if [ -d /var/lib/tomcat$v ]; then TOMCAT_PROFILE=debian:tomcat$v break fi done fi if [ -z "$TOMCAT_PROFILE" ]; then v= if [ -L /opt/tomcat ]; then setx v=readlink /opt/tomcat setx v=basename -- "$v" else array_lsdirs ts /opt "apache-tomcat-*" if [ ${#ts[*]} -gt 0 ]; then [ -n "$CATALINA_BASE" ] || CATALINA_BASE="${ts[0]}" v="$CATALINA_BASE" [ -L "$v" ] && setx v=readlink "$v" setx v=basename -- "$v" fi fi if [ -n "$v" ] && [[ "$v" == apache-tomcat-* ]]; then v=${v#apache-tomcat-} mv=${v%%.*} if [ -f /etc/init.d/tomcat$mv ]; then TOMCAT_PROFILE=runs:tomcat$mv else TOMCAT_PROFILE=runs:tomcat fi [ -z "$TOMCAT_VERSION" ] && TOMCAT_VERSION="$v" fi fi if [ -n "$TOMCAT_PROFILE" ]; then case "$TOMCAT_PROFILE" in debian:*|runs:*) ;; d:*) TOMCAT_PROFILE="debian:tomcat${TOMCAT_PROFILE#d:}";; *) TOMCAT_PROFILE="runs:$TOMCAT_PROFILE";; esac fi # Calculer les valeurs dérivées du profil de connexion case "$TOMCAT_PROFILE" in debian:*) t="${TOMCAT_PROFILE#debian:}" v="${t#tomcat}" [ -z "$CATALINA_BASE" ] && CATALINA_BASE="/var/lib/$t" eval "$( setv "${t^^}_USER" setv "${t^^}_GROUP" source "/etc/default/$t" 2>/dev/null eval "echo tomcat_user=\"\$${t^^}_USER\"" eval "echo tomcat_group=\"\$${t^^}_GROUP\"" )" [ -z "$TOMCAT_USER" ] && TOMCAT_USER="$tomcat_user" [ -z "$TOMCAT_GROUP" ] && TOMCAT_GROUP="$tomcat_group" [ -z "$TOMCAT_VERSION" ] && TOMCAT_VERSION="$v" ;; runs:*) v="${TOMCAT_PROFILE#runs:tomcat}" if [ -z "$CATALINA_BASE" -a -n "$v" -a "$v" != "$TOMCAT_PROFILE" ]; then array_lsdirs ts /opt "apache-tomcat-$v*" CATALINA_BASE="${ts[0]}" fi if [ -z "$CATALINA_BASE" ]; then if [ -L /opt/tomcat ]; then setx CATALINA_BASE=readlink /opt/tomcat else array_lsdirs ts /opt "apache-tomcat-*" CATALINA_BASE="${ts[0]}" fi fi name="${TOMCAT_PROFILE#runs:}" eval "$( TOMCAT_USER= source "/etc/default/$name" 2>/dev/null echo tomcat_user="$TOMCAT_USER" )" [ -z "$TOMCAT_USER" ] && TOMCAT_USER="$tomcat_user" if [ -z "$TOMCAT_VERSION" ]; then v="$CATALINA_BASE" [ -L "$v" ] && setx v=readlink "$v" setx v=basename -- "$v" if [[ "$v" == apache-tomcat-* ]]; then TOMCAT_VERSION="${v#apache-tomcat-}" fi fi ;; esac # Calculer les valeurs dérivées des fichiers de $CATALINA_BASE if [ -n "$CATALINA_BASE" -a -n "$TOMCAT_VERSION" ]; then if [ -z "$MANAGER_URL" -o -z "$MANAGER_USER" ]; then # lire les fichiers de configuratione et déterminer MANAGER_URL, # MANAGER_USER et MANAGER_PASSWORD if is_root; then [ -z "$MANAGER_URL" ] && parse_server [ -z "$MANAGER_USER" ] && parse_tomcat_users else args=() [ -z "$MANAGER_URL" ] && array_add args --private-parse-server [ -z "$MANAGER_USER" ] && array_add args --private-parse-tomcat-users array_add args "$CATALINA_BASE" "$TOMCAT_VERSION" eval "$(run_as root --noexec "${args[@]}")" fi fi fi # A partir de la liste des webapps et de WAMAPS, construire une liste de couples # waname:src pour la sauvegarde et/ou le déploiement [ -n "$fake" \ -o "$action" == config-template \ -o "$action" == restart \ -o "$action" == backup ] && will_deploy= || will_deploy=1 if [ $# -eq 0 -a -n "$will_deploy" ]; then [ -d WEB-INF ] && src=. || src= read_value "Veuillez entrer la webapp à déployer" src "$src" O set -- "$src" elif [ $# -eq 0 -a "$action" == config-template ]; then [ -d WEB-INF ] && set . fi srcs=() [ -n "$istmpdir" -o -z "$will_deploy" ] || ac_set_tmpdir tmpdir for src in "$@"; do if [ -f "$src" ] && is_archive "$src"; then setx waname=get_archive_appname "$src" if [ -z "$will_deploy" ]; then setxx wadir=dirname -- "$src" // abspath array_add srcs "$wadir/$waname" else wadir="$tmpdir/$waname" mkdir "$wadir" if extract_archive "$src" "$wadir"; then array_add srcs "$wadir" else eerror "$src: erreur lors de l'extraction de l'archive" fi fi elif [ -d "$src" ]; then array_add srcs "$(abspath "$src")" elif [ -z "$will_deploy" ]; then array_add srcs "$(abspath "$src")" elif [ -e "$src" ]; then eerror "$src: fichier invalide" else eerror "$src: fichier ou répertoire introuvable" fi done [ ${#srcs[*]} -gt 0 -o -z "$will_deploy" ] || die deploymaps=() for src in "${srcs[@]}"; do setx waname=basename -- "$src" found= for wamap in "${WAMAPS[@]}"; do splitpair "$wamap" from to if [ "$waname" == "$from" ]; then found=1 array_add deploymaps "$to:$src" fi done if [ -z "$found" ]; then array_add deploymaps "$waname:$src" fi done ################################################################################ # Maintenant, nous pouvons faire les actions if [ "$action" == config-template ]; then if [ ${#deploymaps[*]} -gt 0 ]; then toinstconfname=.toinst.conf else toinstconfname=toinst.conf deploymaps=(:.) fi for deploymap in "${deploymaps[@]}"; do splitpair "$deploymap" waname src toinstconf="$src/$toinstconfname" if [ -f "$toinstconf" ]; then estepi "$(ppath "$toinstconf"): configuration déjà existante" else estep "$(ppath "$toinstconf")" echo >"$toinstconf" "# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 # Fichiers à exclure de la source lors du déploiement #EXCLUDES=() # Fichiers à protéger dans la destination lors du déploiement #PROTECTS=() # Options supplémentaires de rsync à utiliser pour le déploiement #RSYNC_OPTIONS=()" fi done exit 0 fi ## Sauvegarde if [ -n "$BACKUP" -o "$action" == backup ]; then [ -n "$CATALINA_BASE" -o -z "$will_deploy" ] || die "Vous devez spécifier l'option --catalina-base" home_bckdir="$HOME/tomcat-backups" if [ -n "$istmpdir" ]; then # déploiement distant: sauvegarder dans le répertoire utilisateur default_bckdir="$home_bckdir" else # déploiement local: ça dépend de la webapp default_bckdir= fi setx cwd=pwd etitle "Sauvegarde des webapps" for deploymap in "${deploymaps[@]}"; do splitpair "$deploymap" waname src wadir="$CATALINA_BASE/webapps/$waname" if [ ! -d "$wadir" ]; then einfo "$waname: la webapp n'a pas encore été déployée. elle ne sera pas sauvegardée" continue fi bckdir="$default_bckdir" if [ -z "$bckdir" ]; then if withinpath "$tmpdir" "$src"; then bckdir="$home_bckdir" elif withinpath "$src" "$cwd"; then setx bckdir=dirname "$src" else bckdir=. fi fi mkdir -p "$bckdir" setx base=date +%y%m%d i=0 bckname="$base.$profile$i-$waname" while [ -d "$bckdir/$bckname" ]; do let i=$i+1 bckname="$base.$profile$i-$waname" done if [ -n "$fake" ]; then evalx qvals rsync -a "$wadir/" "$bckdir/$bckname" // eecho else estep "$wadir --> $(ppath "$bckdir/$bckname")" if ! rsync -a "$wadir/" "$bckdir/$bckname"; then ewarn "Une erreur s'est produite pendant la sauvegarde" ask_yesno "Voulez-vous continuer le déploiement?" N || die fi fi done eend fi ## Déploiement if [ "$action" == deploy ]; then [ -n "$CATALINA_BASE" -o -z "$will_deploy" ] || die "Vous devez spécifier l'option --catalina-base" if [ -n "$fake" ]; then # Afficher ce qu'il faut faire etitle "Configuration globale" __dump_vars etitle "Déploiements des webapps" for deploymap in "${deploymaps[@]}"; do splitpair "$deploymap" waname src setx srcname=basename -- "$src" dest="${CATALINA_BASE:-CATALINA_BASE}/webapps/$waname" etitle "$waname" if [ -f "$src/.toinst.conf" ]; then etitle "Configuration de la webapp" ( EXCLUDES=() PROTECTS=() RSYNC_OPTIONS=() source "$src/.toinst.conf" for var in EXCLUDES PROTECTS RSYNC_OPTIONS; do eecho "$(echo_seta2 "$var")" done ) eend fi build_rsync_options "$src" evalx qvals rsync "${rsync_options[@]}" "$src/" "$dest" // eecho evalx qvals chmod -R u=rwX,g=rX,o=rX "$dest" // eecho if [ -n "$TOMCAT_USER" ]; then evalx qvals chown -R "$TOMCAT_USER:$TOMCAT_GROUP" "$dest" // eecho fi eend done eend elif ! is_root; then # le déploiement doit être fait en root args=( -C "$TOMCAT_PROFILE" -d "$CATALINA_BASE" -U "$TOMCAT_USER" -G "$TOMCAT_GROUP" -V "$TOMCAT_VERSION" -g "$MANAGER_URL" -u "$MANAGER_USER" -p "$MANAGER_PASSWORD" -m "$(array_join WAMAPS ,)" --no-backup --no-restart ) run_as root --noexec "${args[@]}" "${srcs[@]}" || die else # faire le déploiement etitle "Déploiements des webapps" for deploymap in "${deploymaps[@]}"; do splitpair "$deploymap" waname src setx srcname=basename -- "$src" dest="$CATALINA_BASE/webapps/$waname" etitle "$srcname" estepi "Déploiement vers $dest" estep "Copie des fichiers" build_rsync_options "$src" if ! rsync "${rsync_options[@]}" "$src/" "$dest"; then eerror "Une erreur s'est produite pendant la copie des fichiers" else error= estep "Correction des permissions et du propriétaire" chmod -R u=rwX,g=rX,o=rX "$dest" || error=1 if [ -n "$TOMCAT_USER" ]; then chown -R "$TOMCAT_USER:$TOMCAT_GROUP" "$dest" || error=1 fi if [ -n "$error" ]; then eeror "Une erreur s'est produite pendant la mise à jour" fi fi eend done eend fi fi ## Redémarrage if [ -n "$RESTART" -o "$action" == restart ]; then etitle "Redémarrage des webapps" if [ -z "$MANAGER_URL" -o -z "$MANAGER_USER" ]; then ewarn "Impossible de recharger la webapp parce que l'url du manager et/ou le compte admin n'ont pas pu être déterminés." [ -z "$fake" ] && enote "\ Par défaut, les webapps ne sont rechargées automatiquement que si le fichier web.xml est modifié. Utilisez les options -g, -u, -p pour spécifier les informations manquantes nécessaires au pilotage du manager." elif [ -n "$fake" ]; then estepi "Utilisation du manager à l'adresse $MANAGER_URL" for deploymap in "${deploymaps[@]}"; do splitpair "$deploymap" waname src etitle "$waname" evalx qvals curl -fsS -u "$MANAGER_USER:$MANAGER_PASSWORD" "$MANAGER_URL/reload?path=/$waname" // eecho eend done else estepi "Utilisation du manager à l'adresse $MANAGER_URL" for deploymap in "${deploymaps[@]}"; do splitpair "$deploymap" waname src ebegin "$waname" curl -fsS -u "$MANAGER_USER:$MANAGER_PASSWORD" "$MANAGER_URL/reload?path=/$waname" >/dev/null eend $? done fi eend fi