nutools/toinst

674 lines
23 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 xmlsupport
function display_help() {
uecho "$scriptname: Déployer une ou plusieurs webapps vers Tomcat
USAGE
$scriptname [options] <archive|dir>...
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 "$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