déploiement d'application webpyapp

This commit is contained in:
Jephté Clain 2018-04-26 23:20:26 +04:00
parent 1c3c07f8b2
commit 7356c1da73
2 changed files with 510 additions and 0 deletions

312
rwyinst Executable file
View File

@ -0,0 +1,312 @@
#!/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
function display_help() {
uecho "$scriptname: Déploiement distant avec wyinst
USAGE
$scriptname [-h host] [-G tmproot] <archive|dir> [-- options de wyinst]
note: à cause d'une limitation de makeself, les options de wyinst ne devraient
pas contenir d'espaces ni de caractères spéciaux. L'échappement de ces
caractères n'est pas garanti.
OPTIONS
-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 'wyinst.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
}
action=deploy
tmproot=
SSH=
hosts=()
ddb_enable=1
ddb_profile=NONE
ddb_conf=wyinst.conf
parse_opts "${PRETTYOPTS[@]}" \
--help '$exit_with display_help' \
-G:,--tmproot: tmproot= \
-S:,--ssh: SSH= \
-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}"
## wyapp à déployer et hôtes sur lesquels faire le déploiement
# quels informations avons-nous?
array_isempty hosts && has_hosts= || has_hosts=1
if [ $# -eq 0 ] || [[ "$1" == -* ]]; then
# pas d'argument, ou c'est une option (qui fait donc partie des arguments de wyinst)
has_wyapp=
else
wyapp="$1"
has_wyapp=1
shift
fi
# configuration locale de déploiement
if [ -n "$ddb_enable" ]; then
deploydb=(
"$scriptdir/lib/nulib/deploydb"
--missing-ok
${ddb_conf:+-c "$ddb_conf"}
-m wyinst
--run -r wyinst.query_rwyinst "$script"
)
cmds=()
if [ -n "$has_hosts" ]; then
fix_hosts
for host in "${hosts[@]}"; do
array_from_lines tmpcmds "$("${deploydb[@]}" "$wyapp" "$host" "$ddb_profile")"
array_extend cmds tmpcmds
done
elif [ -n "$has_wyapp" ]; then
array_from_lines tmpcmds "$("${deploydb[@]}" "$wyapp" "" "$ddb_profile")"
array_extend cmds tmpcmds
else
read_value "Entrez le répertoire à déployer" wyapp . O
has_wyapp=1
array_from_lines tmpcmds "$("${deploydb[@]}" "$wyapp" "" "$ddb_profile")"
array_extend cmds tmpcmds
fi
if [ ${#cmds[*]} -gt 0 ]; then
[ $# -gt 0 ] && ewarn "Les options supplémentaires '$*' 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_wyapp" ]; then
# pas d'argument, ou c'est une option (qui fait donc partie des arguments de wyinst)
read_value "Entrez le répertoire à déployer" wyapp . O
has_wyapp=1
fi
[ -n "$has_wyapp" ] && set -- "$wyapp" "$@"
array_isempty hosts && read_value "Entrez une liste d'hôtes séparés par ':'" hosts "localhost"
fix_hosts
## Création de l'archive
etitle "Création du répertoire de travail"
ac_set_tmpdir workdir
wyapps=()
while [ $# -gt 0 ]; do
if [ "$1" == "--" ]; then
# début des options de wyinst
shift
break
elif [[ "$1" == -* ]]; then
# début des options de wyinst
break
fi
osrc="$1"; shift
setx src=abspath "$osrc"
setx srcname=basename -- "$src"
if [ -f "$src" ] && is_archive "$src"; then
setx waname=get_archive_appname "$src"
estep "$(ppath "$src") --> $waname"
wadir="$workdir/$waname"
mkdir "$wadir"
extract_archive "$src" "$wadir" || die
src="$wadir"
elif [ -d "$src" ]; then
estep "$(ppath2 "$src")"
cpdirnovcs "$src" "$workdir/$srcname" || die
elif [ -e "$src" ]; then
die "$osrc: fichier invalide"
else
die "$osrc: fichier ou répertoire introuvable"
fi
array_add wyapps "$srcname"
done
estep "Copie de l'environnement de déploiement"
mkdir "$workdir/lib"
ulibsync "$workdir/lib"
cp "$scriptdir/wyinst" "$workdir"
chmod +x "$workdir/wyinst"
eend
ac_set_tmpfile archive
archivename="$(basename "$archive")"
etitle "Création de l'archive pour le déploiement"
"$scriptdir/mkusfx" --bare --tmp-archive -o "$archive" "$workdir" -- ./wyinst || die
eend
## 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"
"$archive" ${tmproot:+--tmproot "$tmproot"} -- "${wyapps[@]}" "$@"
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"} -- "${wyapps[@]}" "$@")"
eend
fi
done

198
wyinst Executable file
View File

@ -0,0 +1,198 @@
#!/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 debian service conf
function display_help() {
uecho "$scriptname: installer une webpyapp et le service associé
USAGE
$scriptname [options] [APPDIR]
OPTIONS
-c, --config CONFIG
Spécifier un fichier de configuration à utiliser pour installer le
service. Par défaut, prendre APPDIR/config/server.conf
-n, --name NAME
Spécifier le nom du service
--port PORT
Spécifier le port sur lequel doit écouter le serveur
-p, --profile PROFILE
Spécifier le profil avec lequel l'application doit tourner
-P, --prod-profile
-T, --test-profile
Raccourcis pour -pprod et -ptest respectivement
-u, --owner USER[:GROUP]
Spécifier l'utilisateur qui doit faire tourner le serveur. La valeur par
défaut est $DEFAULT_OWNER
-d, --destdir DESTDIR
Spécifier le répertoire d'installation. Par défaut, l'installation se
fait dans le répertoire $DEFAULT_DESTDIR
-s, --start
Activer et démarrer le service après installation
-k, --no-restart
Ne pas redémarrer le service s'il était déjà en train de tourner"
}
DEFAULT_DESTDIR=/opt
DEFAULT_OWNER=root
DEFAULT_PYTHON=python2.7
serverconf=
name=
port=
profile=
owner=
mode=u=rwX,go=rX
destdir=
overwrite_config=
enable=
start=
norestart=
args=(
--help '$exit_with display_help'
-c:,--config: serverconf=
-n:,--name: name=
--port: port=
-p:,--profile: profile=
-P,--prod-profile profile=prod
-T,--test-profile profile=test
-u:,--owner: owner=
-d:,--destdir: destdir=
--overwrite-config overwrite_config=1
-s,--start '$enable=1; start=1'
-k,--no-restart norestart=1
)
parse_args "$@"; set -- "${args[@]}"
appdir="${1:-.}"; shift
[ -d "$appdir" ] || die "$appdir: répertoire inexistant"
[ -d "$appdir/config" -a -d "$appdir/python" ] || die "$appdir: ne semble pas être une webpyapp"
setx appdir=abspath "$appdir"
[ -n "$serverconf" ] || serverconf="$appdir/config/server.conf"
[ -f "$serverconf" ] || die "$(ppath "$serverconf"): impossible de trouver le fichier de configuration"
NAME=
PORT=
PROFILE=
OWNER=
VIRTUAL_ENV=
PYTHON=
source "$serverconf"
[ -n "$name" ] || name="$NAME"
[ -n "$name" ] || setx name=basename "$appdir"
[ -n "$port" ] || port="$PORT"
[ -n "$profile" ] || profile="$PROFILE"
[ -n "$owner" ] || owner="$OWNER"
[ -n "$destdir" ] || destdir="$DEFAULT_DESTDIR"
run_as_root -c "$serverconf" -n "$name" ${port:+--port "$port"} \
${profile:+-p "$profile"} \
${owner:+-u "$owner"} \
-d "$destdir" ${overwrite_config:+--overwrite-config} \
${start:+-s} ${norestart:+-k} \
"$appdir" "$@"
: "${PYTHON:=$DEFAULT_PYTHON}"
if [ -n "$VIRTUAL_ENV" ]; then
etitle "Environnement virtuel"
packages=(python-virtualenv)
if ! pkg_check "${packages[@]}"; then
estep "Installation ${packages[*]}"
pkg_install "${packages[@]}" || die
fi
if [ ! -d "$VIRTUAL_ENV" ]; then
estep "Installation dans $VIRTUAL_ENV"
virtualenv -p "$PYTHON" "$VIRTUAL_ENV" || die
fi
eend
fi
[ -n "$VIRTUAL_ENV" ] && PYTHON="$VIRTUAL_ENV/bin/python"
restart=
if [ -z "$norestart" ]; then
if service "$name" check; then
estep "Arrêt du service"
service "$name" stop
start=1
restart=1
fi
fi
etitle "Copie des fichiers"
destdir="$destdir/$name"
mkdir -p "$destdir" || die
rsync -a --delete-after \
--exclude /config/ --exclude /nulib --exclude /.devel \
-f "P /var/**" \
"$appdir/" "$destdir"
eend
etitle "Vérification de la configuration"
estep "Dans $destdir/config:"
array_from_lines configs "$(list_files "$appdir/config")"
for config in "${configs[@]}"; do
if [ -n "$overwrite_config" -o ! -f "$destdir/config/$config" ]; then
estepi "$config: copie initiale"
mkdir -p "$destdir/config"
if [ "$config" == server.conf ]; then
# pour ce cas particulier, prendre le fichier $serverconf
cp "$serverconf" "$destdir/config/$config"
estep "Mise à jour de la configuration"
conf_enable "$destdir/config/$config" \
NAME="$name" ${port:+PORT="$port"} \
${profile:+PROFILE="$profile"} \
${owner:+OWNER="$owner"}
else
cp "$appdir/config/$config" "$destdir/config/$config"
fi
else
estepw "$config: refus d'écraser la configuration existante"
fi
done
eend
[ -n "$owner" ] || owner="$DEFAULT_OWNER"
splitpair "$owner" user group
sdowner="User=$user"
[ -n "$group" ] && sdowner="$sdowner\\
Group=$group"
etitle "Compilation des classes"
"$PYTHON" -m compileall "$destdir/config"
"$PYTHON" -m compileall "$destdir/python"
eend
etitle "Correction des permissions"
chown -R "$user:$group" "$destdir"
chmod -R "$mode" "$destdir"
eend
etitle "Configuration du service"
estep "Vérification du fichier unit"
if [ -d /etc/systemd/system ]; then
service="/etc/systemd/system/$name.service"
ac_set_tmpfile tmpfile
sed "\
s|@@destdir@@|$destdir|g
/^User=@@owner@@\$/c\\
$sdowner
s|@@name@@|$name|g" "$destdir/support/server.service" >"$tmpfile"
if testupdated "$tmpfile" "$service"; then
estep "$service"
cat "$tmpfile" >"$service"
systemctl daemon-reload
fi
ac_clean "$tmpfile"
fi
if [ -n "$enable" ]; then
estep "Activation du service"
service_enable "$name"
fi
if [ -n "$start" ]; then
if [ -n "$restart" ]; then estep "Redémarrage du service"
else estep "Démarrage du service"
fi
service "$name" start
fi
eend