diff --git a/CHANGES.md b/CHANGES.md index d719243..3006981 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,67 @@ +## Version 9.4.1 du 20/01/2020-14:46 + +* `6f01bac` dk: si la commande est inconnue, la transmettre à docker +* `175f6ed` cssh: traiter le cas où screen n'est pas disponible +* `73db728` umail: possibilité de spécifier le type de body +* `5fc55cc` dk: support des fonctions de nettoyage par profil +* `21274af` dk: APPS peut avoir une valeur dépendante du profil +* `f7f8889` support des filtres avec wildcards +* `747c563` repoctl: divers bugs +* `4fdd23c` dk: support limité de podman +* `5457228` dk: support des aliases pour les mappings pff +* `a55f9c4` dk: ajout de get-profile +* `ac7efad` dk: possibilité de forcer la suppression des images +* `5284838` typo +* `d5c5162` dk: dans build.env, une variable peut en mentionner une autre +* `f7d036a` repoctl: cosmetic +* `87b8d5b` Intégration de la branche update-repoctl + * `0866bbd` finaliser edit et implémenter get + * `8daf4ec` début implémentation edit +* `ef1c8f2` utiliser dk composer, et ne pas utiliser de container par défaut +* `269379f` dk: rendre le code overridable +* `aae3703` bug avec traitement des variables de docker-machine -u +* `f875093` dk: par défaut, forcer l'utilisation de la machine locale pour dk composer +* `c202a27` dk: support COMPOSER_SETUP et COMPOSER_CMD +* `921caa1` dk: le shell est lancé en root +* `ce98445` dk: support de composer shell +* `2b5ed18` dk: ajouter un frontend pour composer +* `29c54e7` dmctl: import/export de docker machines +* `de53bae` Intégration de la branche add-repoctl + * `600b824` support limité de pcrone et pclone + * `914f635` implémentation initiale de create, list, delete + * `a774497` squelette +* `5a444a6` dk: support pff +* `425e8a5` dk: maj format ps +* `52c3d7a` dk: ps, ls et rm prennent des filtres. ajout de pull +* `d5f8fa4` cx-updatedev: ajout de l'option -g +* `5fe5137` dk: support de COMPOSER_ACTION et sqlmig NOFIX +* `74cf35b` typo +* `0468623` alias pour docer-machine +* `3dfe542` dk: support de scripts génériques +* `488a257` délai pour éviter les maj intempestives d'eclipse +* `13ce5b8` dk: tagger aussi avec la distribution +* `d03b04f` dk: ajouter automatiquement le suffixe -$DIST à la version +* `6879491` maj doc +* `7fad363` cssh: compat avec les vieilles versionsd de linux +* `f65dda6` bug +* `bc7eebc` ajouter --with-registry-auth à dk update +* `8af50f4` nettoyage de logs webobjects +* `458ccd3` dk: support de certaines options pour dk run +* `c97bc6a` dk: support de la commande run +* `ae70f66` synchro des dépendances uniquement pour le type composer +* `f508dae` support des mises à jour en mode devel +* `60a4c73` cx-conndev: ajouter l'option -d +* `85ac283` cx-conndev: renommer l'option en --mysql-cmd +* `80db462` cx-conndev: ajout de l'option --cmd +* `4400ba7` ajout de ensure-vip +* `ae7ffeb` cx-updatedev: remplacer --ru par -w, --ud, --ur en fonction des besoins +* `9bfd515` dk -c est auto-complete friendly +* `a5a41d9` dk: support de fichiers compose non défaut +* `7e5859e` bug +* `b342960` bug +* `c44d1d3` dk: par défaut, ne pas ajouter le profil au nom de projet +* `aa4eb4a` cx-updatedev: ajout des mises à jour récursives + ## Version 9.4.0 du 05/06/2019-10:15 * `fb96852` Intégration de la branche dk-deploy diff --git a/VERSION.txt b/VERSION.txt index 8148c55..ccfb75e 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -9.4.0 +9.4.1 diff --git a/cssh b/cssh index aba5c67..e2c5c12 100755 --- a/cssh +++ b/cssh @@ -32,9 +32,22 @@ function __ask() { } function __auto_screen() { + # Si screen pas installé, ne rien faire + if [ -z "$(which screen 2>/dev/null)" ]; then + echo "'"$COULEUR_JAUNE"'WARNING'"$COULEUR_NORMALE"' screen introuvable. une session bash classique sera lancée" + exec /bin/bash -l + fi + # Si on est déjà dans screen, ne rien faire [ -z "$STY" ] || return + # corriger TERM pour les vieilles versions de Linux + case "$TERM" in + xterm*) TERM=xterm;; + screen*) TERM=screen;; + esac + export TERM + local msgprefix local screens count diff --git a/cx-conndev b/cx-conndev index d5b2118..ce9f83e 100755 --- a/cx-conndev +++ b/cx-conndev @@ -8,16 +8,34 @@ function display_help() { USAGE $scriptname [DATABASE] + $scriptname -c|-d [-uUSER [-pPASSWORD]] ... OPTIONS -h, -s, --service SERVICE - Spécifier le nom du service. La valeur par défaut est db" + Spécifier le nom du service. La valeur par défaut est db + -c, --mysql-cmd + Lancer 'mysql -hSERVICE_IP' avec les arguments supplémentaires fournis + sur la ligne de commande + -d, --mysqldump-cmd + Lancer 'mysqldump --databases --add-drop-database -hSERVICE_IP' avec + les arguments supplémentaires fournis sur la ligne de commande + -u, --user USER + -p, --password PASSWORD + Paramètres supplémentaires utilisés uniquement avec les options -c et -d" } service=db +mysql_cmd= +mysqldump_cmd= +user= +password= args=( --help '$exit_with display_help' -h:,-s:,--service: service= + -c,--mysql-cmd mysql_cmd=1 + -d,--mysqldump-cmd mysqldump_cmd=1 + -u:,--user: user= + -p:,--password: password= ) parse_args "$@"; set -- "${args[@]}" @@ -44,7 +62,7 @@ fi ### -function die_not_found() { die "$service: service introuvable"; } +function die_not_found() { die "$service: service introuvable. vérifiez que le projet est actuellement up"; } setx cid=docker-compose ps -q "$service" 2>/dev/null || die_not_found [ -n "$cid" ] || die_not_found edebug "$service id: $cid" @@ -54,7 +72,15 @@ setx ip=docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{en [ -n "$ip" ] || die_not_found edebug "$service ip: $ip" -mysqlcmd="$(php bin/conndev.php "$ip" "$@")" -edebug "mysqlcmd: $mysqlcmd" +if [ -n "$mysql_cmd" ]; then + mysql -h"$ip" ${user:+-u"$user"} ${password:+-p"$password"} "$@" -eval "$mysqlcmd" +elif [ -n "$mysqldump_cmd" ]; then + mysqldump --databases --add-drop-database -h"$ip" ${user:+-u"$user"} ${password:+-p"$password"} "$@" + +else + mysql_cmd="$(php bin/conndev.php "$ip" "$@")" + edebug "mysql_cmd: $mysql_cmd" + + eval "$mysql_cmd" +fi diff --git a/cx-updatedev b/cx-updatedev index 4200657..263ad55 100755 --- a/cx-updatedev +++ b/cx-updatedev @@ -17,34 +17,75 @@ OPTIONS Afficher simplement ce qui serait fait -q, --quiet Ne pas lancer rsync en mode verbose + -g, --no-wait + Ne pas attendre avant de faire git commit + --rsync + Mettre à jour les répertoires dans vendor/ qui ne sont pas des liens + symboliques à partir des projets dépendants correspondants. Les liens + symboliques créés avec --link sont ignorés. C'est l'option par défaut. -l, --link - Transformer les clones de dépôts en liens directs vers les projets + Transformer les clones de dépôts dans vendor/ en liens symboliques vers + les projets dépendants correpondants. -k, --copy - Transformer les liens directs vers les projets en copies des projets + Transformer les liens symboliques dans vendor/ en copies des projets + dépendants correspondants. Les répertoires qui ne sont pas des liens + symboliques sont ignorés. + Cette option peut être considérée comme le contraire de --link -i, --install - Supprimer les répertoires et les faire recréer par composer i + Supprimer les répertoires des projets dépendants dans vendor/, qu'il + s'agisse de liens symboliques ou de répertoires normaux, puis les faire + recréer par 'composer i' + Celà permet de ramener le projet à l'état original. -j, --reinstall-link - Supprimer les répertoires et les faire recréer par composer i, - uniquement s'il s'agit de liens + Supprimer les répertoires des projets dépendants dans vendor/ s'il + s'agit de liens symboliques, puis les faire recréer par 'composer i' + Cette variante est plus rapide que --install puisqu'on ne cherche pas à + tout recréer. -u, --update - Supprimer les répertoires et les faire recréer par composer u" + Supprimer les répertoires des projets dépendants dans vendor/, qu'il + s'agisse de liens symboliques ou de répertoires normaux, puis les faire + recréer par 'composer u' + Celà permet de mettre à jour le projet sans les erreurs qui résultent + des modifications faites par les autres actions. + -w, --update-commit + Mettre à jour le projet avec --update puis enregistrer les modifications + éventuelles dans git + --ud, --update-deps + Mettre à jour les projets dépendants avec --update-commit avant de + mettre à jour le projet courant. + --ur, --update-recursive + Mettre à jour de façon récursive tous les projets dépendants. + NB: les modifications dans les projets dépendants seront automatiquement + enregistrées dans git." } fake= verbose=1 -action= +nowait= +action=rsync args=( --help '$exit_with display_help' + -d:,--project-dir: projdir= -n,--fake fake=1 -q,--quiet verbose= + -g,--no-wait nowait=1 -l,--link action=link -k,--copy action=copy -i,--install action=install -j,--reinstall-link action=reinstall-link -u,--update action=update + -w,--update-commit action=update-commit + --ud,--update-deps action=update-deps + --ur,--update-recursive action=update-recursive ) parse_args "$@"; set -- "${args[@]}" +if [ -d "$projdir" ]; then + cd "$projdir" +elif [ -e "$projdir" ]; then + die "$projdir: répertoire introuvable" +fi + found= first=1 while true; do @@ -62,11 +103,11 @@ if [ -z "$first" ]; then enote "Le répertoire du projet est $(ppath . ~)" fi -case "$action" in -install|update) - [ -x ./composer.phar ] || die "Impossible de trouver composer.phar" - ;; -esac +if [ "$action" == update-deps -o "$action" == update-recursive ]; then + # avec update-deps et update-recursive, tous les modules doivent être + # considérés + set -- +fi ### @@ -91,6 +132,7 @@ function update_with_rsync() { } deps=() +modules=() for m in "$@"; do m="${m#vendor/}" # pour permettre de spécifier le chemin directement m="${m//\//-}" @@ -98,10 +140,19 @@ for m in "$@"; do ur-*|lib-*) ;; *) m="ur-$m";; esac + modules+=("$m") p="${m//-/\/}" check_module case "$action" in + rsync) + # Action par défaut: ignorer les liens et synchroniser les copies + if [ ! -L "vendor/$p" ]; then + etitle "$m" + update_with_rsync + eend + fi + ;; link) # Ignorer les liens et transformer les copies en liens if [ ! -L "vendor/$p" ]; then @@ -153,7 +204,7 @@ for m in "$@"; do array_add deps "$p" eend ;; - update) + update|update-commit|update-deps|update-recursive) # Supprimer les liens et répertoires... etitle "$m" if [ -e "vendor/$p" ]; then @@ -163,28 +214,70 @@ for m in "$@"; do array_add deps "$p" eend ;; - *) - # Action par défaut: ignorer les liens et synchroniser les copies - if [ ! -L "vendor/$p" ]; then - etitle "$m" - update_with_rsync - eend - fi - ;; + *) die "$action: action inconnue";; esac done case "$action" in install|reinstall-link) # ... puis installer les dépendances - etitle "Installation des dépendances" - ./composer.phar i - eend + estep "Installation des dépendances" + "$scriptdir/dk" ci || die ;; -update) +update|update-commit) # ... puis mettre à jour les dépendances - etitle "Mise à jour des dépendances" - ./composer.phar u "${deps[@]}" - eend + estep "Mise à jour des dépendances" + "$scriptdir/dk" cu "${deps[@]}" || die + + if [ "$action" == update-commit ]; then + if [ -z "$nowait" ]; then + # laisser le temps à @$*! d'eclipse qui met à jour automatiquement le projet... + estep "Attendre 10 secondes avant git commit" + sleep 10 + fi + + estep "Enregistrement dans git" + pci -A "maj deps" + fi + ;; +update-deps) + # mettre à jour les dépendances directes + for m in "${modules[@]}"; do + etitle "Mise à jour projet dépendant: $m" + "$script" -d "../$m" -w + eend + done + + estep "Mise à jour des dépendances" + "$scriptdir/dk" cu || die + + if [ -z "$nowait" ]; then + # laisser le temps à @$*! d'eclipse qui met à jour automatiquement le projet... + estep "Attendre 10 secondes avant git commit" + sleep 10 + fi + + estep "Enregistrement dans git" + pci -A "maj deps" + ;; +update-recursive) + # mettre à jour les dépendances de façon récursive + for m in "${modules[@]}"; do + etitle "Mise à jour récursive: $m" + "$script" -d "../$m" --ur + eend + done + + estep "Mise à jour des dépendances" + "$scriptdir/dk" cu || die + + if [ -z "$nowait" ]; then + # laisser le temps à @$*! d'eclipse qui met à jour automatiquement le projet... + estep "Attendre 10 secondes avant git commit" + sleep 10 + fi + + estep "Enregistrement dans git" + pci -A "maj deps" ;; esac diff --git a/dk b/dk index 0245668..eae9ae9 100755 --- a/dk +++ b/dk @@ -1,6 +1,7 @@ #!/bin/bash # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 source "$(dirname "$0")/lib/ulib/auto" || exit 1 +urequire pff function display_help() { uecho "$scriptname: outil pour faciliter l'utilisation de docker @@ -9,6 +10,8 @@ USAGE $scriptname CMDs... COMMANDES + get-profile + Afficher le profil courant b|build [SERVICE] Construire les images p|push @@ -18,7 +21,7 @@ COMMANDES La deuxième syntaxe est utilisée avec un projet docker-compose. Elle permet de pousser les images correspondant aux services qui ont été construit vers le serveur. - s|start|run [SERVICE] + s|start [SERVICE] Démarrer le(s) service(s) k|stop [SERVICE] Arrêter le(s) service(s) @@ -30,6 +33,18 @@ COMMANDES Afficher les logs 0|down Arrêter les services et supprimer l'environnement + r|run SERVICE [COMMAND] + Démarrer le service en mode interactif avec la commande spécifiée + Les options suivantes sont supportées, mais il faut les spécifier avant + SERVICE: + * pour docker et docker-compose: --volume + * uniquement pour docker-compose: --detach, -e, --label, --no-deps, + --service-ports, --use-aliases + Par défaut, --rm est activé; utiliser --no-rm pour ne pas supprimer le + container après utilisation. + x|exec SERVICE COMMAND + Lancer une commande dans le container correspondant au service spécifié, + qui doit être en fonctionnement d|brd Construire les images (comme avec build), démarrer les services et suivre les logs de façon interactive (comme avec up). Dès que l'on @@ -41,6 +56,10 @@ COMMANDES Construire les images (comme avec build) puis démarrer les services (comme avec start) Equivalent à -- build -- start [args] + br SERVICE [COMMAND] + Construire les images (comme avec build) puis démarrer le service avec + la commande spécifiée (comme avec run) + Equivalent à -- build -- run [args] y|deploy [args...] (Re)déployer un stack. Cette commande ne fonctionne qu'en mode swarm. Implique --stack @@ -69,33 +88,117 @@ COMMANDES 'service scale SERVICE=REPLICAS' ip|show-ip [SERVICE] Afficher l'adresse IP interne du service - x|exec SERVICE COMMAND - Lancer la commande dans le container spécifié systemd|systemd-unit Générer une unité systemd qui démarre les services. A priori, ce n'est nécessaire que si aucune politique de redémarrage n'a été définie. - ps + ps [filter|name=value] Afficher les containers en cours d'exécution - ls + Le filtre est une expression régulière de type awk qui est mise en + correspondace avec les noms de l'image et de la tâche. + Il est aussi possible d'utiliser un filtre docker de la forme name=value + - ancestor=([:tag]|| ) + containers created from an image or a descendant. + - before=(|) + - expose=([/]|/[]) + - exited= an exit code of + - health=(starting|healthy|unhealthy|none) + - id= a container's ID + - is-task=(true|false) + - label= or label== + - name= a container's name + - network=(|) + - publish=([/]|/[]) + - since=(|) + - status=(created|restarting|removing|running|paused|exited) + - volume=(|) + ls [filter|name=value] Lister les images actuellement présentes - rm - Supprimer une image + Le filtre est une expression régulière de type awk qui est mise en + correspondance avec le nom de l'image. Un suffixe :tag permet de ne + sélectionner que les images correspondant au filtre qui ont le tag + spécifié. + Il est aussi possible d'utiliser un filtre docker de la forme name=value + - dangling=(true|false) - find unused images + - label= or label== + - before=([:tag]|| ) + - since=([:tag]|| ) + - reference=(pattern of an image reference) + pull filter + Mettre à jour une ou plusieurs images + Le filtre est une expression régulière de type awk qui est mise en + correspondance avec le nom de l'image. Un suffixe :tag permet de ne + sélectionner que les images correspondant au filtre qui ont le tag + spécifié. + rm filter + Supprimer une ou plusieurs images + Le filtre est une expression régulière de type awk qui est mise en + correspondance avec le nom de l'image. Un suffixe :tag permet de ne + sélectionner que les images correspondant au filtre qui ont le tag + spécifié. X|prune Supprimer les containers et les images inutilisées + composer|ci|cu|cs [args...] + Frontend pour lancer composer à l'intérieur d'un container. Les + commandes 'ci', 'cu' et 'cs' sont respectivement des alias pour + 'composer install', 'composer update' et 'composer shell' + S'il existe un fichier .composer.conf dans le répertoire du projet, il + est sourcé. Ce fichier définit des variables qui indiquent comment la + commande composer est lancée. Les variables suivantes peuvent être + définies: + * COMPOSER_IMAGE -- Image utilisée pour lancer composer. La valeur par + défaut est: + $DEFAULT_COMPOSER_IMAGE + Spécifier 'none' pour lancer directement composer sans passer par une + image docker. + L'image spécifiée doit disposer de la commande 'su-exec' afin de + pouvoir lancer la commande avec l'utilisateur courant. Le répertoire + \$HOME est monté à l'intérieur du container et le script composer.phar + du projet est utilisé le cas échéant. + * COMPOSER_MACHINE -- Nom de la docker machine sur laquelle se connecter + pour lancer l'image docker. La valeur par défaut est -u, ce qui force + l'utilisation de l'instance docker locale. + * COMPOSER_CMD -- Chemin vers l'exécutable composer. Par défaut, + utiliser composer.phar s'il existe dans le répertoire du projet. Sinon + utiliser /usr/bin/composer + * COMPOSER_SETUP -- Liste de commandes à lancer pour configurer le + container. Dans ce cas, un container ayant pour base \$COMPOSER_IMAGE + et nommé d'après le nom du projet est préparé et les commandes + spécifiées y sont lancées. Ce container est réutilisé à chaque fois. + Ce paramétrage est utilisé pour par exemple installer certains + packages nécessaire au projet. + La commande 'shell' est une extension qui lance un shell bash au lieu de + lancer la commande composer, ce qui permet de faire des opérations plus + complexes si le besoin s'en fait sentir. NB: le shell est lancé avec + l'utilisateur root. La commande alternative 'ushell' lance le shell avec + le compte utilisateur. + Pour faciliter l'utilisation dans un script, les premiers arguments + peuvent être utilisés pour redéfinir les variables COMPOSER_*, e.g + $scriptname composer COMPOSER_IMAGE=none install + OPTIONS générales (ces options sont communes à toutes les commandes) -d, --chdir PROJDIR Spécifier le répertoire du projet + -c, --config CONFIG[.yml] + Spécifier le fichier de configuration à utiliser. le fichier de profil + CONFIG.PROFILE.yml est chargé aussi s'il existe. + NB: il n'est pas nécessaire d'ajouter l'extension .yml au nom, cela + permet de faciliter l'utilisation avec l'auto-complétion quand il existe + des fichiers de profil + Si cette option n'est pas spécifiée, docker-compose.yml est utilisé par + défaut (ou avec l'option --stack docker-stack.yml s'il existe) -p, --profile PROFILE -P, --prod -T, --test Spécifier le profil -m, --set-machine MACHINE Choisir l'environnement docker-machine spécifié avant de lancer les - commandes + commandes. Utiliser -u pour desélectionner la machine en cours, e.g -m-u -n, --fake Ne pas lancer les commandes, simplement les afficher + -f, --force + Forcer l'opération (là où cela a du sens) -h, --host HOST Spécifier l'hôte pour la commande systemd-unit @@ -114,18 +217,30 @@ OPTIONS build -g, --ug, --no-update-apps ne pas mettre à jour les dépôts dépendants. ces dépôts sont définis dans le fichier update-apps.conf qui a le format suivant: + DEFAULT_DEVEL_SRCDIR= + DEFAULT_ORIGIN= DEFAULT_BRANCH= - APPS=() # liste d'applications à mettre à jour - app_URL= # url du dépôt - app_DEST= # répertoire dans lequel faire le checkout - app_PROFILE_ORIGIN= # origine spécifique à un profil - app_PROFILE_BRANCH= # branche spécifique à un profil - app_ORIGIN= # ou... origine par défaut de la branche - app_BRANCH= # ou... branche par défaut - app_TYPE= # type de projet (composer par défaut) - app_AFTER_UPDATE=() # liste de commandes à lancer après le checkout + DEFAULT_COMPOSER_ACTION= + PROFILE_CLEAN= # fonction de nettoyage spécifique au profil + CLEAN= # ou... fonction de nettoyage par défaut + APPS=() # applications à mettre à jour par défaut + PROFILE_APPS=() # ou... spécifique au profil 'PROFILE' + app_URL= # url du dépôt + app_DEVEL_SRCDIR= # répertoire source en mode devel + app_DEST= # répertoire dans lequel faire le checkout + app_PROFILE_ORIGIN= # origine spécifique au profil 'PROFILE' + app_ORIGIN= # ou... origine par défaut de la branche + app_PROFILE_BRANCH= # branche spécifique au profil 'PROFILE' + app_BRANCH= # ou... branche par défaut + app_TYPE= # type de projet (composer|none) + app_AFTER_UPDATE=() # liste de commandes à lancer après le checkout + app_COMPOSER_ACTION= # action projet composer (install|update|none) -u, --uu, --update-apps-only Ne faire que la mise à jour depuis les dépôts dépendants. + -w, --ww, --update-apps-devel + Faire la mise à jour en mode devel: le projet ainsi que ses fichiers des + répertoires vendor/lib et vendor/ur sont synchronisés via rsync depuis + \$DEFAULT_DEVEL_SRCDIR qui vaut par défaut \$HOME/wop/php --uo, --update-apps-origin ORIGIN Spécifier l'origine par défaut pour update-apps --ub, --update-apps-branch BRANCH @@ -137,24 +252,62 @@ OPTIONS deploy (c'est à dire ne pas utiliser l'option --with-registry-auth) VARIABLES de update-apps.conf + DEVEL_SRCDIR + répertoire source pour le mode devel. attention, il s'agit du répertoire + du projet, alors que DEFAULT_DEVEL_SRCDIR est le répertoire de base par + défaut des projets ORIGIN vaut 'origin' par défaut BRANCH vaut 'develop' par défaut TYPE vaut 'composer' par défaut si le fichier composer.json existe à la - racine du projet. sinon vaut 'inconnu' par défaut + racine du projet. sinon vaut 'none' par défaut AFTER_UPDATE Cette variable est une liste de commandes à lancer après la maj du dépôt - si le chemin est absolu ou relatif, lancer la commande telle quelle - s'il n'y a pas de chemin, alors ce doit être le nom d'une fonction existante auquel on enlève le préfixe update_apps_func_ + Au moment où la commande est lancée, le répertoire courant est celui du + projet. Les variables suivantes sont disponibles: + URL= # url du dépôt + DEVEL_SRCDIR= # répertoire source en mode devel + DEST= # répertoire dans lequel faire le checkout + ORIGIN= # origine de la branche + BRANCH= # branche sélectionnée dans le dépôt + TYPE= # type de projet (composer|none) + COMPOSER_ACTION + vaut 'install' par défaut. Indique ce qu'il faut faire pour un projet de + type 'composer' après avoir lancé les commandes de AFTER_UPDATE. Les + directives supportées sont 'install', 'update' et 'none' + FONCTIONS de update-apps.conf - sqlmig [DESTDIR [SRCDIR]] + sqlmig [DESTDIR [SRCDIR [NOFIX]]] Copier les définitions des bases de données au format sqlmig de - SRCDIR/config/sqlmig vers DESTDIR/config/mariadb/sqlmig" + SRCDIR/config/sqlmig vers DESTDIR/config/mariadb/sqlmig + Si SRCDIR ne se termine pas par '/config/sqlmig' ou si DESTDIR ne se + termine pas par '/config/mariadb/sqlmig', rajouter ces suffixes + automatiquement, sauf si une valeur NOFIX est spécifiée. + pff [MAPS [PFFDIR]] + Si PFFDIR est un projet pff, mettre à jour le profil pff en fonction du + profil de déploiement. + MAPS détermine le mapping entre profil de déploiement (prod, test, + devel) et le profil pff. Il s'agit d'une liste de valeurs séparées par + des virgules de la forme DEST[:SRC] + - Une valeur de la forme 'DEST:SRC' fait correspondre le profil de + déploiement SRC au profil pff 'DEST' + - Une valeur de la forme 'DEST' force le choix du profil pff DEST quel + que soit le profil de déploiement + - Pour simplifier l'écriture des mappings, les aliases suivants sont + reconnus:"' + PTD est remplacé par prod:prod,test:test,devel:devel + PT " " " prod:prod,test:test + '"Si aucun profil de déploiement ne correspond, le comportement par défaut + est de forcer le premier profil défini dans le projet pff" } +function echo_lines() { local IFS=$'\n'; echo "$*"; } + function get_version() { local GIT_DIR; unset GIT_DIR if git rev-parse --git-dir >/dev/null 2>&1; then @@ -185,7 +338,7 @@ function docker_parse_build_args() { cat "$1" | grep -v '^#' | grep -v '^$' | - sed -r 's/([^=]+)=(.*)/replace_build_args+=(--build-arg \1="\2"); \1="\2"/' + sed -r 's/([^=]+)=(.*)/\1=\2; replace_build_args+=(--build-arg \1="$\1")/' } function docker_parse_env_args() { [ -f .build.env ] && eval "$(docker_parse_build_args .build.env)" @@ -212,7 +365,7 @@ function docker_check_name() { } function compose_set_project_name() { - local PROJECT_NAME= PROJECT_NAME_REMOVE_SUFFIX=.service PROJECT_NAME_ADD_PROFILE=1 + local PROJECT_NAME= PROJECT_NAME_REMOVE_SUFFIX=.service PROJECT_NAME_ADD_PROFILE= [ -f .compose.env ] && source ./.compose.env [ -n "$PROJECT_NAME" ] || PROJECT_NAME="$(basename -- "$(pwd)")" @@ -232,42 +385,64 @@ function compose_set_project_name() { fi } function compose_set_env_args() { - if [ -n "$USE_STACK" -a -f docker-stack.yml ]; then - replace_env_args+=(-f docker-stack.yml) + if [ -n "$CONFIG" ]; then + # autocomplétion friendly + [ ! -f "$CONFIG" -a -f "${CONFIG}yml" ] && CONFIG="${CONFIG}yml" + [ ! -f "$CONFIG" -a -f "$CONFIG.yml" ] && CONFIG="$CONFIG.yml" + ## + replace_env_args+=(-f "$CONFIG") + if [ -n "$PROFILE" -a -f "${CONFIG%.yml}.$PROFILE.yml" ]; then + replace_env_args+=(-f "${CONFIG%.yml}.$PROFILE.yml") + fi else - replace_env_args+=(-f docker-compose.yml) - fi - if [ -n "$USE_STACK" -a -f docker-stack.override.yml ]; then - replace_env_args+=(-f docker-stack.override.yml) - elif [ -f docker-compose.override.yml ]; then - replace_env_args+=(-f docker-compose.override.yml) - fi - if [ -n "$PROFILE" ]; then - if [ -n "$USE_STACK" -a -f "docker-stack.$PROFILE.yml" ]; then - replace_env_args+=(-f "docker-stack.$PROFILE.yml") - elif [ -f "docker-compose.$PROFILE.yml" ]; then - replace_env_args+=(-f "docker-compose.$PROFILE.yml") + if [ -n "$USE_STACK" -a -f docker-stack.yml ]; then + replace_env_args+=(-f docker-stack.yml) + else + replace_env_args+=(-f docker-compose.yml) + fi + if [ -n "$USE_STACK" -a -f docker-stack.override.yml ]; then + replace_env_args+=(-f docker-stack.override.yml) + elif [ -f docker-compose.override.yml ]; then + replace_env_args+=(-f docker-compose.override.yml) + fi + if [ -n "$PROFILE" ]; then + if [ -n "$USE_STACK" -a -f "docker-stack.$PROFILE.yml" ]; then + replace_env_args+=(-f "docker-stack.$PROFILE.yml") + elif [ -f "docker-compose.$PROFILE.yml" ]; then + replace_env_args+=(-f "docker-compose.$PROFILE.yml") + fi fi fi compose_set_project_name "$@" } function docker_set_deploy_args() { - if [ -n "$USE_STACK" -a -f docker-stack.yml ]; then - replace_deploy_args+=(-c docker-stack.yml) + if [ -n "$CONFIG" ]; then + # autocomplétion friendly + [ ! -f "$CONFIG" -a -f "${CONFIG}yml" ] && CONFIG="${CONFIG}yml" + [ ! -f "$CONFIG" -a -f "$CONFIG.yml" ] && CONFIG="$CONFIG.yml" + ## + replace_deploy_args+=(-c "$CONFIG") + if [ -n "$PROFILE" -a -f "${CONFIG%.yml}.$PROFILE.yml" ]; then + replace_deploy_args+=(-c "${CONFIG%.yml}.$PROFILE.yml") + fi else - replace_deploy_args+=(-c docker-compose.yml) - fi - if [ -n "$USE_STACK" -a -f docker-stack.override.yml ]; then - replace_deploy_args+=(-c docker-stack.override.yml) - elif [ -f docker-compose.override.yml ]; then - replace_deploy_args+=(-c docker-compose.override.yml) - fi - if [ -n "$PROFILE" ]; then - if [ -n "$USE_STACK" -a -f "docker-stack.$PROFILE.yml" ]; then - replace_env_args+=(-c "docker-stack.$PROFILE.yml") - elif [ -f "docker-compose.$PROFILE.yml" ]; then - replace_env_args+=(-c "docker-compose.$PROFILE.yml") + if [ -n "$USE_STACK" -a -f docker-stack.yml ]; then + replace_deploy_args+=(-c docker-stack.yml) + else + replace_deploy_args+=(-c docker-compose.yml) + fi + if [ -n "$USE_STACK" -a -f docker-stack.override.yml ]; then + replace_deploy_args+=(-c docker-stack.override.yml) + elif [ -f docker-compose.override.yml ]; then + replace_deploy_args+=(-c docker-compose.override.yml) + fi + if [ -n "$PROFILE" ]; then + if [ -n "$USE_STACK" -a -f "docker-stack.$PROFILE.yml" ]; then + replace_deploy_args+=(-c "docker-stack.$PROFILE.yml") + elif [ -f "docker-compose.$PROFILE.yml" ]; then + replace_deploy_args+=(-c "docker-compose.$PROFILE.yml") + fi fi fi @@ -325,22 +500,25 @@ BUILD_BUILD= UPDATE_APPS_ORIGIN= UPDATE_APPS_BRANCH= function build_set_options() { - case "$1" in - u) BUILD_UPDATE_APPS=1; BUILD_BUILD=;; - b) BUILD_UPDATE_APPS=; BUILD_BUILD=1;; - *) BUILD_UPDATE_APPS=1; BUILD_BUILD=1;; - esac + BUILD_BUILD= + BUILD_UPDATE_APPS= + BUILD_UPDATE_DEVEL= + [[ "$1" == *b* ]] && BUILD_BUILD=1 + [[ "$1" == *u* ]] && BUILD_UPDATE_APPS=1 + [[ "$1" == *w* ]] && BUILD_UPDATE_DEVEL=1 UPDATE_APPS_ORIGIN="$2" UPDATE_APPS_BRANCH="$3" } function update_apps_func_sqlmig() { - local destdir="$1" srcdir="$2" + local destdir="$1" srcdir="$2" nofix="$3" [ -n "$destdir" ] || destdir=db - [ "${destdir%/config/mariadb/sqlmig}" != "$destdir" ] || destdir="$destdir/config/mariadb/sqlmig" + [ -n "$srcdir" ] || srcdir="$DEST" - [ -n "$srcdir" ] || srcdir="$dest" - [ "${srcdir%/config/sqlmig}" != "$srcdir" ] || srcdir="$srcdir/config/sqlmig" + if [ -z "$nofix" ]; then + [ "${destdir%/config/mariadb/sqlmig}" != "$destdir" ] || destdir="$destdir/config/mariadb/sqlmig" + [ "${srcdir%/config/sqlmig}" != "$srcdir" ] || srcdir="$srcdir/config/sqlmig" + fi if [ ! -d "$srcdir" ]; then eerror "$srcdir: répertoire introuvable" @@ -361,6 +539,64 @@ function update_apps_func_sqlmig() { return 0 } +function __maps_replace() { + if [ "${maps#$1,}" != "$maps" ]; then + maps="$2,${maps#$1,}" + elif [ "${maps%,$1}" != "$maps" ]; then + maps="${maps%,$1},$2" + elif [ "${maps/,$1,/}" != "$maps" ]; then + maps="${maps/,$1,/,$2,}" + elif [ "$maps" == "$1" ]; then + maps="$2" + fi +} + +function update_apps_func_pff() { + local maps="$1" pffdir="${2:-$DEST}" + [ -f "$pffdir/$PFF_CONF" ] || return 0 + + # aliases + __maps_replace PTD prod:prod,test:test,devel:devel + __maps_replace PT prod:prod,test:test + + source "$pffdir/$PFF_CONF" + pff_autofix "$pffdir" + + local map src dest + array_split maps "$maps" , + for map in "${maps[@]}"; do + if [ -z "$map" ]; then + continue + elif [[ "$map" == *:* ]]; then + # mapping de profil + splitpair "$map" dest src + if [ "$src" == "$PROFILE" ]; then + if array_contains PROFILES "$dest"; then + estep "Sélection du profil pff $dest" + pff_select_profile "$dest" "$pffdir" + return 0 + fi + eerror "$dest: profil invalide, il a été ignoré" + fi + else + # forcer le profil + dest="$map" + if array_contains PROFILES "$dest"; then + estep "Sélection du profil pff $dest" + pff_select_profile "$dest" "$pffdir" + return 0 + fi + eerror "$dest: profil invalide, il a été ignoré" + fi + done + # sélectionner le premier profil + setx dest=pff_get_first_profile "$pffdir" + if [ -n "$dest" ]; then + estep "Sélection du profil pff $dest" + pff_select_profile "$dest" "$pffdir" + fi +} + function build_update_apps() { [ -n "$BUILD_UPDATE_APPS" ] || return 0 [ -f update-apps.conf ] || return 0 @@ -373,8 +609,15 @@ function build_update_apps() { #XXX à terme, on déploiera la branche master en prod [ -z "$DEFAULT_BRANCH" -a "$PROFILE" == prod ] && DEFAULT_BRANCH=develop #master [ -z "$DEFAULT_BRANCH" ] && DEFAULT_BRANCH=develop + DEFAULT_DEVEL_SRCDIR="$HOME/wop/php" + DEFAULT_COMPOSER_ACTION=install APPS=() + CLEAN= [ -f update-apps.conf ] && source ./update-apps.conf + + local apps # liste des applications spécifique au profil + apps="${PROFILE}_APPS[@]"; apps=("${!apps}") + [ ${#apps[*]} -gt 0 ] && APPS=("${apps[@]}") [ ${#APPS[*]} -gt 0 ] || return 0 local PRODUCTION DEVELOPMENT @@ -384,86 +627,146 @@ function build_update_apps() { devel) PRODUCTION=; DEVELOPMENT=;; esac + local clean + clean="${PROFILE}_CLEAN"; clean="${!clean}" + [ -n "$clean" ] && CLEAN="$clean" + if [ -n "$CLEAN" ]; then + etitle "Nettoyage" + "$CLEAN" + eend + fi + etitle "Mise à jour des dépendances" - local app var type url dest branch after_update after_updates + local app var URL DEVEL_SRCDIR DEST ORIGIN BRANCH TYPE after_update after_updates composer_action for app in "${APPS[@]}"; do etitle "$app" var="${app//-/_}" - url="${var}_URL"; url="${!url}" - [ -n "$url" ] || { + URL="${var}_URL"; URL="${!URL}" + [ -n "$URL" ] || { ewarn "$app: vous devez définir l'url" eend; return 1 } - dest="${var}_DEST"; dest="${!dest}" - [ -n "$dest" ] || dest="$app/b" - mkdir -p "$dest" || { eend; return 1; } + DEVEL_SRCDIR="${var}_DEVEL_SRCDIR"; DEVEL_SRCDIR="${!DEVEL_SRCDIR}" + [ -n "$DEVEL_SRCDIR" ] || DEVEL_SRCDIR="$DEFAULT_DEVEL_SRCDIR/${URL##*/}" - origin="${var}_${PROFILE}_ORIGIN"; origin="${!origin}" - [ -n "$origin" ] || { origin="${var}_ORIGIN"; origin="${!origin}"; } - [ -n "$origin" ] || origin="$DEFAULT_ORIGIN" + DEST="${var}_DEST"; DEST="${!DEST}" + [ -n "$DEST" ] || DEST="$app/b" + mkdir -p "$DEST" || { eend; return 1; } - branch="${var}_${PROFILE}_BRANCH"; branch="${!branch}" - [ -n "$branch" ] || { branch="${var}_BRANCH"; branch="${!branch}"; } - [ -n "$branch" ] || branch="$DEFAULT_BRANCH" + ORIGIN="${var}_${PROFILE}_ORIGIN"; ORIGIN="${!ORIGIN}" + [ -n "$ORIGIN" ] || { ORIGIN="${var}_ORIGIN"; ORIGIN="${!ORIGIN}"; } + [ -n "$ORIGIN" ] || ORIGIN="$DEFAULT_ORIGIN" - dest="$dest/$app" - if [ ! -d "$dest" ]; then - # clonage initial - estep "Clonage $url:$branch --> $dest" - git clone -o "$origin" -b "$branch" "$url" "$dest" || { eend; return 1; } + BRANCH="${var}_${PROFILE}_BRANCH"; BRANCH="${!BRANCH}" + [ -n "$BRANCH" ] || { BRANCH="${var}_BRANCH"; BRANCH="${!BRANCH}"; } + [ -n "$BRANCH" ] || BRANCH="$DEFAULT_BRANCH" + + # calculer le type maintenant, on en a besoin pour le mode devel + TYPE="${var}_TYPE"; TYPE="${!TYPE}" + + DEST="$DEST/$app" + if [ -n "$BUILD_UPDATE_DEVEL" ]; then + # synchronisation en mode devel + local -a rsync_opts; rsync_opts=(-a --delete --exclude .git/ --delete-excluded) + estep "Synchro $DEVEL_SRCDIR --> $DEST" + rsync "${rsync_opts[@]}" "$DEVEL_SRCDIR/" "$DEST" || { eend; return 1; } + + if [ -z "$TYPE" ]; then + # possible de détecter le type quand on a le projet + # en cas de maj ici, mettre à jour aussi le code ci-dessous + if [ -f "$DEST/composer.json" ]; then TYPE=composer + else TYPE=none + fi + fi + + if [ "$TYPE" == composer ]; then + # Synchronisation des dépendances + local -a depdirs; local depdir pname + setx -a depdirs=ls -d "$DEST/vendor/"{lib,ur}/* 2>/dev/null + for depdir in "${depdirs[@]}"; do + [ -L "$depdir" ] && rm "$depdir" + pname="${depdir#$DEST/vendor/}"; pname="${pname/\//-}" + estep "Synchro $DEFAULT_DEVEL_SRCDIR/$pname --> $depdir" + rsync "${rsync_opts[@]}" --exclude /vendor/ "$DEFAULT_DEVEL_SRCDIR/$pname/" "$depdir" + done + fi else - # mise à jour - estep "Maj dépôt $url:$branch --> $dest" - setx cwd=pwd - cd "$dest" - git fetch --all -p -f || { eend; return 1; } - git reset --hard "$origin/$branch" || { eend; return 1; } - cd "$cwd" - fi - - type="${var}_TYPE"; type="${!type}" - if [ -z "$type" ]; then - if [ -f "$dest/composer.json" ]; then - type=composer + if [ -d "$DEST" -a -d "$DEST/.git" ]; then + # mise à jour + estep "Maj dépôt $URL:$BRANCH --> $DEST" + setx cwd=pwd + cd "$DEST" + git fetch --all -p -f || { eend; return 1; } + git reset --hard "$ORIGIN/$BRANCH" || { eend; return 1; } + cd "$cwd" else - type=inconnu + # reliquat mode devel? + [ -d "$DEST" ] && rm -rf "$DEST" + # clonage initial + estep "Clonage $URL:$BRANCH --> $DEST" + git clone -o "$ORIGIN" -b "$BRANCH" "$URL" "$DEST" || { eend; return 1; } + fi + + if [ -z "$TYPE" ]; then + # possible de détecter le type quand on a le projet + # en cas de maj ici, mettre à jour aussi le code ci-dessus + if [ -f "$DEST/composer.json" ]; then TYPE=composer + else TYPE=none + fi fi fi after_updates="${var}_AFTER_UPDATE" if is_defined "$after_updates"; then after_updates="$after_updates[@]"; after_updates=("${!after_updates}") - elif [ "$type" == composer ]; then + elif [ "$TYPE" == composer ]; then after_updates=(sqlmig) else after_updates=() fi - estep "Type de dépôt: $type" - if [ "$type" == composer ]; then - composer=/usr/bin/composer - [ -x "$dest/composer.phar" ] && composer="$dest/composer.phar" + estep "Type de dépôt: $TYPE" + if [ "$TYPE" == composer ]; then + composer_action="${var}_${PROFILE}_COMPOSER_ACTION"; composer_action="${!composer_action}" + [ -n "$composer_action" ] || { composer_action="${var}_COMPOSER_ACTION"; composer_action="${!composer_action}"; } + [ -n "$composer_action" ] || composer_action="$DEFAULT_COMPOSER_ACTION" - estep "Installation des dépendances composer" - "$composer" -d"$dest" install ${PRODUCTION:+--no-dev -o} || { eend; return 1; } + if [ -z "$BUILD_UPDATE_DEVEL" ]; then + case "${composer_action:-install}" in + i|install) composer_action=install;; + u|update) composer_action=update;; + none|nop) composer_action=;; + *) ewarn "$composer_action: action invalide"; composer_action=;; + esac + if [ -n "$composer_action" ]; then + setx cwd=pwd + cd "$DEST" + estep "Installation des dépendances composer" + auto_composer "$composer_action" ${PRODUCTION:+--no-dev -o} || { eend; return 1; } + cd "$cwd" + fi + fi fi for after_update in "${after_updates[@]}"; do if [ "${after_update#/}" != "$after_update" ]; then # commande absolue, la lancer telle quelle - estep "$after_update" - eval "$after_update" || { eend; return 1; } + etitle "$after_update" + eval "$after_update" || { eend; eend; return 1; } + eend elif [ "${after_update#./}" != "$after_update" ]; then # commande relative, la lancer telle quelle - estep "$after_update" - eval "$after_update" || { eend; return 1; } + etitle "$after_update" + eval "$after_update" || { eend; eend; return 1; } + eend else # c'est une fonction update_apps_func_* - estep "$after_update" - eval "update_apps_func_$after_update" || { eend; return 1; } + etitle "$after_update" + eval "update_apps_func_$after_update" || { eend; eend; return 1; } + eend fi done @@ -477,8 +780,14 @@ function initialize_build_env() { NAME= TAGS=(latest) VERSION= + DIST= } function default_update_build_env() { + if [ -n "$DIST" ]; then + [ -n "$VERSION" ] && VERSION="$VERSION-" + VERSION="$VERSION$DIST" + TAGS+=("$DIST") + fi [ -n "$VERSION" ] || docker_add_build_arg VERSION "$(get_version)" [ -n "$VERSION" ] && TAGS+=("$VERSION") } @@ -497,7 +806,7 @@ function default_docker_build() { for tag in "${TAGS[@]}"; do replace_build_args+=(-t "$NAME:$tag") done - ${FAKE:+qvals} docker build \ + ${FAKE:+qvals} "$DOCKER" build \ ${NO_CACHE:+--no-cache} \ "${replace_env_args[@]}" "${env_args[@]}" \ "${replace_build_args[@]}" "${build_args[@]}" \ @@ -537,7 +846,7 @@ function default_compose_push() { "$@" } function default_docker_push() { - ${FAKE:+qvals} docker push \ + ${FAKE:+qvals} "$DOCKER" push \ "${replace_env_args[@]}" "${env_args[@]}" \ "$@" } @@ -573,7 +882,7 @@ function default_compose_up() { "${replace_user_args[@]}" "${user_args[@]}" "$@" } function default_docker_up() { - ${FAKE:+qvals} docker run \ + ${FAKE:+qvals} "$DOCKER" run \ "${replace_env_args[@]}" "${env_args[@]}" \ "${replace_run_args[@]}" "${run_args[@]}" \ "$NAME" \ @@ -605,7 +914,7 @@ function default_compose_stop() { "$@" } function default_docker_stop() { - ${FAKE:+qvals} docker container stop \ + ${FAKE:+qvals} "$DOCKER" container stop \ "${replace_stop_args[@]}" "${stop_args[@]}" \ "$container_name" "$@" } @@ -632,7 +941,7 @@ function default_compose_logs() { "$@" } function default_docker_logs() { - ${FAKE:+qvals} docker logs \ + ${FAKE:+qvals} "$DOCKER" logs \ "${replace_logs_args[@]}" "${logs_args[@]}" \ "$container_name" "$@" } @@ -662,11 +971,11 @@ function default_compose_down() { } function default_docker_down() { estep "stop" - ${FAKE:+qvals} docker container stop \ + ${FAKE:+qvals} "$DOCKER" container stop \ "${replace_down_args[@]}" "${down_args[@]}" \ "$container_name" "$@" estep "rm" - ${FAKE:+qvals} docker container rm \ + ${FAKE:+qvals} "$DOCKER" container rm \ "${replace_rm_args[@]}" "${rm_args[@]}" \ "$container_name" } @@ -687,8 +996,97 @@ function auto_down() { fi } +function default_compose_run() { + ${FAKE:+qvals} docker-compose \ + "${replace_env_args[@]}" "${env_args[@]}" \ + run "${replace_run_args[@]}" "${run_args[@]}" \ + "${replace_user_args[@]}" "${user_args[@]}" "$@" +} +function default_docker_run() { + ${FAKE:+qvals} "$DOCKER" run \ + "${replace_env_args[@]}" "${env_args[@]}" \ + "${replace_run_args[@]}" "${run_args[@]}" \ + "$NAME" \ + "${replace_user_args[@]}" "${user_args[@]}" "$@" +} +function compose_run() { default_compose_run "$@"; } +function docker_run() { default_docker_run "$@"; } +function auto_run() { + eval "$(utools_local parse_opts)" + local detach no_deps rm=1 service_ports use_aliases volume + local -a envs labels + args=( + + + -d,--detach detach=1 + -e: '$add@ envs -e; add@ envs' + -l:,--label: '$add@ labels -l; add@ labels' + --no-deps no_deps=1 + --no-rm rm= + --rm rm=1 + --service-ports service_ports=1 + --use-aliases use_aliases=1 + -v:,--volume: volume= + @ args -- "$@" + ) + parse_opts "${args[@]}" && set -- "${args[@]}" || { eerror "$args"; return 1; } + + local -a replace_env_args env_args + local -a replace_run_args run_args + local -a replace_user_args user_args + local project_name container_name + if [ -f docker-compose.yml ]; then + compose_set_env_args + replace_run_args=( + ${detach:+-d} + "${envs[@]}" "${labels[@]}" + ${no_deps:+--no-deps} + ${rm:+--rm} + ${service_ports:+--service-ports} + ${use_aliases:+--use-aliases} + ${volume:+-v "$volume"} + ) + compose_run "$@" + else + docker_set_env_args + docker_check_name set_container_name + replace_run_args=( + --name "$container_name" + ${rm:+--rm} + ${volume:+-v "$volume"} + ) + docker_run "$@" + fi +} + +function default_compose_exec() { + ${FAKE:+qvals} docker-compose \ + "${replace_env_args[@]}" "${env_args[@]}" \ + exec "${replace_exec_args[@]}" "${exec_args[@]}" \ + "$@" +} +function default_docker_exec() { + ${FAKE:+qvals} "$DOCKER" container exec \ + "${replace_exec_args[@]}" "${exec_args[@]}" \ + "$container_name" "$@" +} +function compose_exec() { default_compose_exec "$@"; } +function docker_exec() { default_docker_exec "$@"; } +function auto_exec() { + local -a replace_env_args env_args + local -a replace_exec_args exec_args + local project_name container_name + if [ -f docker-compose.yml ]; then + compose_set_env_args + compose_exec "$@" + else + docker_set_env_args + docker_check_name set_container_name + docker_exec "$@" + fi +} + function default_docker_deploy() { - ${FAKE:+qvals} docker \ + ${FAKE:+qvals} "$DOCKER" \ stack deploy \ "${replace_deploy_args[@]}" "${deploy_args[@]}" \ "$container_name" "$@" @@ -708,19 +1106,18 @@ function auto_deploy() { } function default_docker_service() { - ${FAKE:+qvals} docker service "$@" + ${FAKE:+qvals} "$DOCKER" service "$@" } function docker_service() { default_docker_service "$@"; } function auto_service() { - local -a replace_env_args env_args - local -a replace_deploy_args deploy_args - local project_name container_name if [ -f docker-compose.yml -o -f docker-stack.yml ]; then local command="$1"; shift [ -n "$command" ] || { eerror "Vous devez spécifier la commande" return 1 } + local -a command_args + [ "$command" == update -a -n "$WITH_REGISTRY_AUTH" ] && command_args+=(--with-registry-auth) case "$command" in ls) # ces commandes n'ont pas besoin du nom de service docker_service "$command" "$@" @@ -731,9 +1128,12 @@ function auto_service() { eerror "Vous devez spécifier le nom du service" return 1 } + local -a replace_env_args env_args + local -a replace_deploy_args deploy_args + local project_name container_name docker_set_deploy_args set_container_name service="${container_name}_${service#${container_name}_}" - docker_service "$command" "$@" "$service" + docker_service "$command" "${command_args[@]}" "$@" "$service" ;; esac else @@ -754,12 +1154,12 @@ function default_compose_show_ip() { else setx -a cids="${cmd[@]}" 2>/dev/null for cid in "${cids[@]}"; do - docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$cid" + "$DOCKER" inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$cid" done fi } function default_docker_show_ip() { - ${FAKE:+qvals} docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' \ + ${FAKE:+qvals} "$DOCKER" inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' \ "${replace_show_ip_args[@]}" "${show_ip_args[@]}" \ "$container_name" "$@" } @@ -779,33 +1179,6 @@ function auto_show_ip() { fi } -function default_compose_exec() { - ${FAKE:+qvals} docker-compose \ - "${replace_env_args[@]}" "${env_args[@]}" \ - exec "${replace_exec_args[@]}" "${exec_args[@]}" \ - "$@" -} -function default_docker_exec() { - ${FAKE:+qvals} docker container exec \ - "${replace_exec_args[@]}" "${exec_args[@]}" \ - "$container_name" "$@" -} -function compose_exec() { default_compose_exec "$@"; } -function docker_exec() { default_docker_exec "$@"; } -function auto_exec() { - local -a replace_env_args env_args - local -a replace_exec_args exec_args - local project_name container_name - if [ -f docker-compose.yml ]; then - compose_set_env_args - compose_exec "$@" - else - docker_set_env_args - docker_check_name set_container_name - docker_exec "$@" - fi -} - function default_compose_systemd_unit() { local docker_compose="$(which docker-compose 2>/dev/null)" if [ -z "$docker_compose" ]; then @@ -824,7 +1197,7 @@ function default_compose_systemd_unit() { setx stopcmd=qvals "$docker_compose" down } function default_docker_systemd_unit() { - local docker="$(which docker 2>/dev/null)" + local docker="$(which "$DOCKER" 2>/dev/null)" if [ -z "$docker" ]; then if [ -x /usr/bin/docker ]; then docker=/usr/bin/docker @@ -899,35 +1272,206 @@ EOF local_run "$HOST" "$tmpscript" } +function default_local_composer() { + # lancement direct + case "$1" in + rootshell|shell|rootbash|bash) + # ewarn parce qu'on est pas root dans ce shell contrairement à ce qui est demandé + ewarn "Lancement d'un shell utilisateur" + bash "$@" + ;; + usershell|ushell|userbash|ubash) + estep "Lancement d'un shell utilisateur" + bash "$@" + ;; + *) + if [ -n "$COMPOSER_CMD" ]; then : + elif [ -x composer.phar ]; then COMPOSER_CMD=./composer.phar + elif [ -x /usr/bin/composer ]; then COMPOSER_CMD=/usr/bin/composer + else + eerror "Impossible de trouver composer" + return 1 + fi + "$COMPOSER_CMD" "$@" + ;; + esac +} +function default_docker_composer() { + # lancement dans un container + local user group projdir actualcmd args + setx user=id -un; setx user=getent passwd "$user" + setx group=id -gn; setx group=getent group "$group" + setx projdir=pwd + case "$1" in + rootshell|shell|rootbash|bash) + actualcmd='eval "bash $args"' + shift + ;; + usershell|ushell|userbash|ubash) + actualcmd='eval "su-exec \"$user\" bash $args"' + shift + ;; + *) + actualcmd='eval "su-exec \"$user\" \"$composer\" $args"' + ;; + esac + setx args=qvals "$@" + + local -a basecmd cmd setupscript runscript + basecmd=( + -e user="$user" + -e group="$group" + -e projdir="$projdir" + -e setup="$COMPOSER_SETUP" + -e composer="$COMPOSER_CMD" + -e args="$args" + -v "$HOME:$HOME" + ) + if [ "${projdir#$HOME/}" == "$projdir" ]; then + # si le répertoire de projet ne se trouve pas dans $HOME, le monter aussi + cmd+=(-v "$projdir:$projdir") + fi + setupscript='eval "$setup"' + runscript=' +echo "$user" >>/etc/passwd; user="${user%%:*}" +echo "$group" >>/etc/group; group="${group%%:*}" + +cd "$projdir" +if [ -n "$composer" ]; then : +elif [ -x composer.phar ]; then composer=./composer.phar +elif [ -x /usr/bin/composer ]; then composer=/usr/bin/composer +else + echo "ERROR: Impossible de trouver composer" + exit 1 +fi +'"$actualcmd" + + if [ -n "$COMPOSER_SETUP" ]; then + # lancement dans un container docker à préparer + local NAME project_name container_name dkid + if [ -f docker-compose.yml ]; then + compose_set_project_name set_container_name + else + NAME="$(basename -- "$(pwd)")" + docker_check_name set_container_name + fi + container_name="dk_composer_${container_name}" + + # vérifier l'existence de l'image + setx dkid=docker image ls --format '{{.ID}}' "${container_name}_image" + + # créer le container le cas échéant + if [ -z "$dkid" ]; then + estep "Création du container $container_name avec l'image $COMPOSER_IMAGE" + cmd=( + "$DOCKER" create -it --name "${container_name}_ct" + "${basecmd[@]}" + "$COMPOSER_IMAGE" + bash -c "$setupscript" + ) + setx dkid="${cmd[@]}" || return 1 + "$DOCKER" container start -ai "$dkid" || return 1 + "$DOCKER" container commit "$dkid" "${container_name}_image" || return 1 + "$DOCKER" container rm "$dkid" || return 1 + fi + + # prendre comme image le container créé + COMPOSER_IMAGE="${container_name}_image" + fi + + cmd=( + "$DOCKER" run -it --rm + "${basecmd[@]}" + "$COMPOSER_IMAGE" + bash -c "$runscript" + ) + "${cmd[@]}" +} +function local_composer() { default_local_composer "$@"; } +function docker_composer() { default_docker_composer "$@"; } +function auto_composer() { + local COMPOSER_IMAGE="$DEFAULT_COMPOSER_IMAGE" + local COMPOSER_MACHINE=-u + local COMPOSER_CMD= + local COMPOSER_SETUP= + [ -f .composer.conf ] && source ./.composer.conf + # les premiers arguments peuvent service à redéfinir les variables + while [ $# -gt 0 ]; do + case "$1" in + COMPOSER_IMAGE=*) setv "$1"; shift;; + COMPOSER_MACHINE=*) setv "$1"; shift;; + COMPOSER_CMD=*) setv "$1"; shift;; + COMPOSER_SETUP=*) setv "$1"; shift;; + *) break;; + esac + done + + if [ "$COMPOSER_IMAGE" == none ]; then + local_composer "$@" + + else + local PREVIOUS_DOCKER_MACHINE_NAME="$DOCKER_MACHINE_NAME" + if [ -n "$COMPOSER_MACHINE" -a "$DOCKER_MACHINE_NAME" != "$COMPOSER_MACHINE" ]; then + local -x DOCKER_TLS_VERIFY= DOCKER_HOST= DOCKER_CERT_PATH= DOCKER_MACHINE_NAME= + if [ "$COMPOSER_MACHINE" != -u ]; then + local env + setx env=docker-machine env "$COMPOSER_MACHINE" 2>/dev/null || { + eerror "$COMPOSER_MACHINE: une erreur s'est produite lors de la sélection du noeud avec docker-machine" + return 1 + } + eval "$env" + fi + fi + + docker_composer "$@" + fi +} + +################################################################################ + +# support limité docker / podman +if progexists docker; then DOCKER=docker +elif progexists podman; then DOCKER=podman +else DOCKER=docker +fi + DEFAULT_PROFILE=devel +# pour le moment ne pas lancer composer dans un container par défaut +DEFAULT_COMPOSER_IMAGE=none #docker.univ-reunion.fr/image/utils-icmycas-php-apache:d9 PROFILE= DM_PROFILES=() set_defaults dk export PROFILE chdir= +CONFIG= DM_SET_MACHINE= USE_STACK= FAKE= +FORCE= NO_CACHE= HOST= WITH_REGISTRY_AUTH=1 update_apps_mode=ub +update_apps_devel= update_apps_origin= update_apps_branch= args=( --help '$exit_with display_help' -d:,--chdir: chdir= + -c:,--config: CONFIG= -p:,--profile: PROFILE= -P,--prod PROFILE=prod -T,--test PROFILE=test -m:,--set-machine: DM_SET_MACHINE= --stack USE_STACK=1 -n,--fake FAKE=1 + -f,--force FORCE=1 -j,--no-cache NO_CACHE=1 -h:,--host: HOST= -g,--ug,--no-update-apps update_apps_mode=b -u,--uu,--update-apps-only update_apps_mode=u + -w,--uw,--update-apps-devel update_apps_devel=1 --uo:,--update-apps-origin: update_apps_origin= --ub:,--update-apps-branch: update_apps_branch= -l,--without-registry-auth WITH_REGISTRY_AUTH= @@ -937,6 +1481,7 @@ parse_args "$@"; set -- "${args[@]}" progexists docker-machine && DM_AVAILABLE=1 || DM_AVAILABLE= if [ -n "$DM_SET_MACHINE" ]; then [ -n "$DM_AVAILABLE" ] || die "docker-machine n'est pas disponible" + [ "$DM_SET_MACHINE" == - ] && DM_SET_MACHINE=-u setx dm_env=docker-machine env "$DM_SET_MACHINE" || die eval "$dm_env" fi @@ -955,13 +1500,21 @@ fi [ -n "$PROFILE" ] || PROFILE="$DEFAULT_PROFILE" [ -n "$chdir" ] && { cd "$chdir" || die; } +update_apps_mode="${update_apps_mode}${update_apps_devel:+w}" + +[ -f .dk.scripts.sh ] && source ./.dk.scripts.sh +[ -f dk.scripts.sh ] && source ./dk.scripts.sh while [ $# -gt 0 ]; do [ "$1" == -- ] && { shift; continue; } cmd="$1"; shift case "$cmd" in + get-profile|get_profile|profile) + echo "$PROFILE" + exit 0 + ;; b|build) - build_set_options "$update_apps_mode" "$update_apps_origin" "$update_apps_branch" + build_set_options "$update_apps_mode" "$update_apps_origin" "$update_apps_branch" [ -f .build.scripts.sh ] && source ./.build.scripts.sh [ -f build.scripts.sh ] && source ./build.scripts.sh args=() @@ -981,7 +1534,7 @@ while [ $# -gt 0 ]; do enote "Profil $PROFILE" auto_push "${args[@]}" || die ;; - s|run|start) + s|start) args=() while [ $# -gt 0 -a "$1" != -- ]; do args+=("$1"); shift @@ -1021,6 +1574,22 @@ while [ $# -gt 0 ]; do enote "Profil $PROFILE" auto_down "${args[@]}" || die ;; + r|run) + args=() + while [ $# -gt 0 -a "$1" != -- ]; do + args+=("$1"); shift + done + enote "Profil $PROFILE" + auto_run "${args[@]}" || die + ;; + x|exec) + args=() + while [ $# -gt 0 -a "$1" != -- ]; do + args+=("$1"); shift + done + enote "Profil $PROFILE" + auto_exec "${args[@]}" || die + ;; d|brd) do_auto_down=1 function auto_down_trap() { @@ -1028,7 +1597,7 @@ while [ $# -gt 0 ]; do } trap auto_down_trap 1 3 15 EXIT - build_set_options "$update_apps_mode" "$update_apps_origin" "$update_apps_branch" + build_set_options "$update_apps_mode" "$update_apps_origin" "$update_apps_branch" [ -f .build.scripts.sh ] && source ./.build.scripts.sh [ -f build.scripts.sh ] && source ./build.scripts.sh args=() @@ -1043,7 +1612,7 @@ while [ $# -gt 0 ]; do fi ;; bs) - build_set_options "$update_apps_mode" "$update_apps_origin" "$update_apps_branch" + build_set_options "$update_apps_mode" "$update_apps_origin" "$update_apps_branch" [ -f .build.scripts.sh ] && source ./.build.scripts.sh [ -f build.scripts.sh ] && source ./build.scripts.sh USE_STACK=1 @@ -1054,6 +1623,18 @@ while [ $# -gt 0 ]; do enote "Profil $PROFILE" auto_build && auto_up "${args[@]}" ;; + br) + build_set_options "$update_apps_mode" "$update_apps_origin" "$update_apps_branch" + [ -f .build.scripts.sh ] && source ./.build.scripts.sh + [ -f build.scripts.sh ] && source ./build.scripts.sh + USE_STACK=1 + args=() + while [ $# -gt 0 -a "$1" != -- ]; do + args+=("$1"); shift + done + enote "Profil $PROFILE" + auto_build && auto_run "${args[@]}" + ;; y|deploy) USE_STACK=1 args=() @@ -1064,7 +1645,7 @@ while [ $# -gt 0 ]; do auto_deploy "${args[@]}" || die ;; by|bd) - build_set_options "$update_apps_mode" "$update_apps_origin" "$update_apps_branch" + build_set_options "$update_apps_mode" "$update_apps_origin" "$update_apps_branch" [ -f .build.scripts.sh ] && source ./.build.scripts.sh [ -f build.scripts.sh ] && source ./build.scripts.sh USE_STACK=1 @@ -1076,7 +1657,7 @@ while [ $# -gt 0 ]; do auto_build && auto_deploy "${args[@]}" ;; bp) - build_set_options "$update_apps_mode" "$update_apps_origin" "$update_apps_branch" + build_set_options "$update_apps_mode" "$update_apps_origin" "$update_apps_branch" [ -f .build.scripts.sh ] && source ./.build.scripts.sh [ -f build.scripts.sh ] && source ./build.scripts.sh args=() @@ -1087,7 +1668,7 @@ while [ $# -gt 0 ]; do auto_build && auto_push "${args[@]}" ;; bpy|bpd) - build_set_options "$update_apps_mode" "$update_apps_origin" "$update_apps_branch" + build_set_options "$update_apps_mode" "$update_apps_origin" "$update_apps_branch" [ -f .build.scripts.sh ] && source ./.build.scripts.sh [ -f build.scripts.sh ] && source ./build.scripts.sh args=() @@ -1129,14 +1710,6 @@ while [ $# -gt 0 ]; do enote "Profil $PROFILE" auto_show_ip "${args[@]}" || die ;; - x|exec) - args=() - while [ $# -gt 0 -a "$1" != -- ]; do - args+=("$1"); shift - done - enote "Profil $PROFILE" - auto_exec "${args[@]}" || die - ;; systemd-unit|systemd) args=() while [ $# -gt 0 -a "$1" != -- ]; do @@ -1145,19 +1718,161 @@ while [ $# -gt 0 ]; do enote "Profil $PROFILE" auto_systemd_unit "${args[@]}" || die ;; - ps) docker container ps -a || die;; - ls) docker image ls || die;; + ps) + pscmd=( + "$DOCKER" container ps -a + --format "table {{.ID}}\t{{.Image}}\t{{.Names}}\t{{.Status}}\t{{.RunningFor}}\t{{.Ports}}" + ) + filtercmd=(cat) + awkscript=' +NR == 1 { print; next } +($2 ~ filter || $3 ~ filter) { print } +' + if [ $# -eq 0 ]; then + # pas de filtre + : + elif [ "$1" == -- ]; then + # fin de la commande + shift + elif [[ "$1" == *=* ]]; then + # filtre docker + pscmd+=(--filter "$1") + shift + else + # expression régulière + filtercmd=(awk -v filter="$1" "$awkscript") + shift + fi + set -o pipefail + "${pscmd[@]}" | "${filtercmd[@]}" || die + ;; + ls) + lscmd=(docker image ls) + filtercmd=(cat) + awkscript=' +BEGIN { + if (split(filter, parts, /:/) > 1) { + filter = parts[1] + tag = parts[2] + } else { + tag = "" + } +} +NR == 1 { print; next } +(filter == "" || $1 ~ filter) && (tag == "" || $2 ~ tag) { print } +' + if [ $# -eq 0 ]; then + # pas de filtre + : + elif [ "$1" == -- ]; then + # fin de la commande + shift + elif [[ "$1" == *=* ]]; then + # filtre docker + lscmd+=(--filter "$1") + shift + else + # expression régulière + filtercmd=(awk -v filter="$1" "$awkscript") + shift + fi + set -o pipefail + "${lscmd[@]}" | "${filtercmd[@]}" || die + ;; + pull) + lscmd=(docker image ls) + filtercmd=(awk 'NR == 1 { print; next } $2 !~ / 1) { + filter = parts[1] + tag = parts[2] + } else { + tag = "" + } +} +NR == 1 { print; next } +(filter == "" || $1 ~ filter) && (tag == "" || $2 ~ tag) && $2 !~ / 1) { + filter = parts[1] + tag = parts[2] + } else { + tag = "" + } +} +NR == 1 { print; next } +(filter == "" || $1 ~ filter) && (tag == "" || $2 ~ tag) { print } +' + if [ $# -eq 0 -o "$1" == -- ]; then + # pas de filtre + ewarn "rm: Vous devez spécifier l'image à supprimer" + continue + elif [[ "$1" == *=* ]]; then + # filtre docker + lscmd+=(--filter "$1") + shift + else + # expression régulière + filtercmd=(awk -v filter="$1" "$awkscript") + shift + fi + all="$("${lscmd[@]}" | "${filtercmd[@]}")" + setx -a images awk 'NR == 1 { next } { print $3 }' <<<"$all" + if [ ${#images[*]} -gt 0 ]; then + echo "$all" + ask_yesno "Etes-vous sûr de vouloir supprimer ces images?" || die + "$DOCKER" image rm ${FORCE:+--force} "${images[@]}" || die + fi + ;; + X|prune) + "$DOCKER" container prune -f || die + "$DOCKER" image prune -f || die + ;; + composer|c|ci|cu|cs) + build_set_options "$update_apps_mode" "$update_apps_origin" "$update_apps_branch" + [ -f .build.scripts.sh ] && source ./.build.scripts.sh + [ -f build.scripts.sh ] && source ./build.scripts.sh args=() + if [ "$cmd" == ci ]; then args+=(install) + elif [ "$cmd" == cu ]; then args+=(update) + elif [ "$cmd" == cs ]; then args+=(shell) + fi while [ $# -gt 0 -a "$1" != -- ]; do args+=("$1"); shift done - docker image rm "${args[@]}" || die + auto_composer "${args[@]}" ;; - X|prune) - docker container prune -f || die - docker image prune -f || die - ;; - *) die "$cmd: commande inconnue";; + *) + # transmettre directement à docker pour les autres commandes + exec "$DOCKER" "$@" esac done diff --git a/dmctl b/dmctl new file mode 100755 index 0000000..056d188 --- /dev/null +++ b/dmctl @@ -0,0 +1,116 @@ +#!/bin/bash +# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 +source /etc/ulibauto || exit 1 + +function display_help() { + uecho "$scriptname: import/exporter une docker-machine + +USAGE + $scriptname -d NAME + $scriptname -l ARCHIVE + +OPTIONS + -d, --dump + Exporter la machine + -l, --load + Import la machine" +} + +action= +args=( + --help '$exit_with display_help' + -d,--dump,-x,--export,-s,--save action=dump + -l,--load,-i,--import action=load +) +parse_args "$@"; set -- "${args[@]}" + +if [ -z "$action" ]; then + case "$1" in + d|dump|x|export|s|save) action=dump; shift;; + l|load|i|import) action=load; shift;; + *) die "Vous devez spécifier l'action à effectuer";; + esac +fi + +case "$action" in +dump) + ############################################################################ + name="$1" + [ -n "$name" ] || die "Vous devez spécifier le nom de la machine" + + srcdir="$HOME/.docker/machine/machines/$name" + [ -d "$srcdir" ] || die "$name: nom de machine incorrect" + + ac_set_tmpdir workdir + destdir="$workdir/$name" + + archive="${name}-machine.tar.gz" + + estep "Copie des fichiers" + cp -r "$srcdir" "$workdir" || die + for i in ca-key.pem ca.pem key.pem cert.pem; do + [ -f "$destdir/$i" ] || { + cp "$HOME/.docker/machine/certs/$i" "$destdir" || die + } + done + + estep "Correction des chemins" + sed -i "\ +s|$HOME/.docker/machine|DOCKER_MACHINE_HOME|g +s|DOCKER_MACHINE_HOME/certs/|DOCKER_MACHINE_HOME/machines/$name/|g +" "$destdir/config.json" || die + + estep "Génération de $archive" + tar czf "$archive" -C "$workdir" "$name" || die + + ac_clean "$workdir" + ;; + +load) + ############################################################################ + archive="$1" + [ -n "$archive" ] || die "Vous devez spécifier l'archive à importer" + + setx archivename=basename -- "$archive" + if [[ "$archivename" == *-machine.tar.gz ]]; then + name="${archivename%-machine.tar.gz}" + else + die "$archive: n'est pas une archive docker-machine" + fi + + destdir="$HOME/.docker/machine/machines" + machinedir="$destdir/$name" + origdir="$HOME/${name}-machine.orig" + if [ -d "$machinedir" ]; then + ewarn "$name: une machine du même nom existe déjà" + ask_yesno "Voulez-vous l'écraser?" N || die + + [ -d "$origdir" ] && rm -rf "$origdir" + mv "$machinedir" "$origdir" + fi + + ac_set_tmpdir workdir + srcdir="$workdir/$name" + + estep "Extraction de l'archive" + tar xzf "$archive" -C "$workdir" + + [ -d "$srcdir" ] || die "L'archive n'est pas pour la machine $name" + + estep "Corriger les chemins" + sed -i "\ +s|DOCKER_MACHINE_HOME|$HOME/.docker/machine|g +" "$srcdir/config.json" || die + + estep "Installation de la machine" + mkdir -p "$destdir" + mv "$srcdir" "$destdir" + + if [ -d "$origdir" ]; then + enote "Une fois que la machine aura été testée, vous pouvez supprimer ce répertoire: + $origdir" + fi + + ac_clean "$workdir" + ;; +esac diff --git a/ensure-vip b/ensure-vip new file mode 100755 index 0000000..0c9a356 --- /dev/null +++ b/ensure-vip @@ -0,0 +1,122 @@ +#!/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 ipcalc + +function display_help() { + uecho "$scriptname: s'assurer que l'hôte courant a l'adresse IP spécifiée + +USAGE + $scriptname IPs... + +NB: ce script ne supporte que la modification d'interfaces qui ne sont pas des +bridges. il est conçu avant tout pour utilisation sur un serveur, pas un poste +de travail. + +OPTIONS + -i, --ignore-iface IFACE + Ajouter une interface à la liste des interfaces à ignorer. Si cette + option est utilisée, la liste par défaut n'est pas utilisée, sauf si + l'option -d est utilisée aussi. + -d, --defaults + Ajouter les valeurs suivantes à liste des interfaces à ignorer: + ${DEFAULT_IGNORE_IFACES[*]} + Cette option est automatiquement activée si l'option -i n'est pas + utilisée + -n, --fake + Afficher simplement les modification à effectuer" +} + +DEFAULT_IGNORE_IFACES=( + "docker*" + "br-*" + "veth*" +) +DEFAULT_PROFILES=(prod test devel) + +ignore_ifaces=() +fake= +args=( + --help '$exit_with display_help' + -i:,--ignore-iface: ignore_ifaces + -d,--defaults '$array_extend ignore_ifaces DEFAULT_IGNORE_IFACES' + -n,--fake fake=1 +) +parse_args "$@"; set -- "${args[@]}" + +require_debian + +for vip in "$@"; do + [ "${vip%/*}" != "$vip" ] || vip="$vip/32" + vips+=("$vip") +done + +array_fix_paths ignore_ifaces +[ ${#ignore_ifaces[*]} -gt 0 ] || ignore_ifaces=("${DEFAULT_IGNORE_IFACES[@]}") + +if [ -n "$fake" ]; then + set_verbosity --debug + __NETWORK_DEVEL_SHOW_MODIFS=1 + __DEBIAN_NETWORK_DEVEL_SHOW_MODIFS=1 +fi + +################################################################################ +# réimplémentation "corrigée" de network_config_partial() + +nifile="$__DEBIAN_NETWORK_INTERFACES" + +network_set_confips +network_set_confbrs +tmpconfips=() +for confip in "${confips[@]}"; do + network_parse_confip "$confip" iface gateway ipsuffixes + continue= + for ignore_iface in "${ignore_ifaces[@]}"; do + if eval "[[ \"\$iface\" == $(qwc "$ignore_iface") ]]"; then + continue=1 + break + fi + done + [ -n "$continue" ] && continue + + tmpconfips+=("$confip") + network_parse_ipsuffix "${ipsuffixes[0]}" mainip suffix + for vip in "${vips[@]}"; do + if [ "${vip%.*}" == "${mainip%.*}" ]; then + tmpconfips+=("$iface:$vip") + fi + done +done +confips=("${tmpconfips[@]}") +confbrs=() +network_fix_confs + +network_config "" confips confbrs || exit 0 + +# des modifications ont eu lieu, tenter de configurer les nouvelles adresses +# d'abord calculer les adresses ips qui sont activées +network_set_confips +for confip in "${confips[@]}"; do + network_parse_confip "$confip" iface gateway ipsuffixes + for ipsuffix in "${ipsuffixes[@]}"; do + network_parse_ipsuffix "$ipsuffix" ip suffix + array_addu ips "$ip" + done +done +# puis lister les commandes correspondant aux adresses ips non activées +array_from_lines ups "$(awkrun <"$nifile" -f ips[@] ' +$1 == "up" && $2 == "ip" && $3 == "addr" && $4 == "add" { + ip = $5; sub(/(\/[0-9.]+)?$/, "", ip) # enlever suffixe + if (! in_array(ip, ips)) { + $1 = "" + print + } +}')" +if [ ${#ups[*]} -gt 0 ]; then + etitle "Configuration des nouvelles adresses" + for up in "${ups[@]}"; do + estep "$up" + eval "$up" + done + eend +fi diff --git a/foreach b/foreach index f6a77b0..1fc0de4 100755 --- a/foreach +++ b/foreach @@ -87,7 +87,7 @@ OPTIONS -C, --composer-projects Equivalent à '--ptitle -p */composer.json --' --cc, --composer-cmd - Equivalent à '--ptitle -p */composer.phar -- ./composer.phar'" + Equivalent à '--ptitle -p */composer.json -- dk composer'" } basedir= @@ -129,7 +129,7 @@ composer) [ "$title" == auto ] && title=p ;; composer-cmd) - set -- */composer.phar -- ./composer.phar "$@" + set -- */composer.json -- "$scriptdir/dk" composer "$@" parentdir=1 [ "$title" == auto ] && title=p ;; diff --git a/lib/bashrc.d/docker_aliases.shared b/lib/bashrc.d/docker_aliases.shared new file mode 100644 index 0000000..6bab1fc --- /dev/null +++ b/lib/bashrc.d/docker_aliases.shared @@ -0,0 +1,13 @@ +# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 + +if [ -n "$UTOOLS_DOCKER_ALIASES" ]; then + if [ "$DOCKER_MACHINE_WRAPPED" == true ]; then + alias dm=__docker_machine_wrapper + else + alias dm=docker-machine + fi + + if [ -n "$UTOOLS_BASH_COMPLETION" ]; then + complete -F _docker_machine dm + fi +fi diff --git a/lib/bashrc.d/nutools.userconf b/lib/bashrc.d/nutools.userconf index 97d620b..d75d954 100644 --- a/lib/bashrc.d/nutools.userconf +++ b/lib/bashrc.d/nutools.userconf @@ -14,3 +14,6 @@ # _root. Par défaut, ce n'est le cas que si sudo n'est pas installé. Si sudo est # configuré, il est préférable de ne pas utiliser su. #export UTOOLS_USES_SU=false + +# Activer les aliases pour l'utilisation de docker +#export UTOOLS_DOCKER_ALIASES=1 diff --git a/lib/default/dk b/lib/default/dk index 39be464..2f1dcc9 100644 --- a/lib/default/dk +++ b/lib/default/dk @@ -5,3 +5,6 @@ # Profiles pour docker-machine #DM_PROFILES=(name:profile...) + +# Image à utiliser pour lancer composer +#COMPOSER_IMAGE=docker.univ-reunion.fr/image/utils-icmycas-php-apache:d9 diff --git a/lib/default/pcrone b/lib/default/repoctl similarity index 71% rename from lib/default/pcrone rename to lib/default/repoctl index f64e5ae..cea1682 100644 --- a/lib/default/pcrone +++ b/lib/default/repoctl @@ -1,18 +1,21 @@ # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 # Liste de préfixes permettant de taper le nom d'un dépôt plus rapidement e.g -# pcrone g/ssi-php/myproj -# pcrone v:modules/myproj +# repoctl create g/ssi-php/myproj +# repoctl create v:modules/myproj # sont équivalents à -# pcrone https://git.univ-reunion.fr/ssi-php/myproj -# pcrone git@vcs.univ.run:modules/myproj -# Ces définitions fonctionnent aussi pour pclone +# repoctl create https://git.univ-reunion.fr/ssi-php/myproj +# repoctl create git@vcs.univ.run:modules/myproj # Le format est ALIAS=ACTUAL +# +# Ces définitions fonctionnent aussi pour tous les scripts qui utilisent +# repoctl, dont notamment pclone et pcrone REPO_PREFIXES=( s:=git@git.univ-reunion.fr: g/=https://git.univ-reunion.fr/ v:=git@vcs.univ.run: av/=https://vcs.univ-reunion.fr/anongit/ p:=pgit@vcs.univ.run: ap/=https://pvcs.univ-reunion.fr/anongit/ + j/=https://git.jclain.fr/ ) # Définitions des types de dépôt. Le format est NAME:TYPE:PREFIX @@ -21,6 +24,7 @@ REPO_PREFIXES=( # type par défaut est 'gitolite' REPO_TYPES=( ur:gitea:https://git.univ-reunion.fr/ + jclain:gitea:https://git.jclain.fr/ ) # Configuration de l'accès à l'API gogs diff --git a/lib/pyulib/src/uapps/umail.py b/lib/pyulib/src/uapps/umail.py index 23e9452..d0e75af 100755 --- a/lib/pyulib/src/uapps/umail.py +++ b/lib/pyulib/src/uapps/umail.py @@ -77,6 +77,7 @@ def run_umail(): ('a:', 'attach=', "Attacher un fichier"), ('t:', 'content-type=', "Spécifier le type de contenu du fichier"), ('f:', 'body=', "Spécifier un fichier contenant le corps du message"), + (None, 'html', "Indiquer que le corps du message est du type text/html. Par défaut, il s'agit de text/plain"), (None, 'gencmd', "Générer une commande à évaluer pour envoyer le mail"), ]) options, args = get_args(None, options, longoptions) @@ -87,6 +88,7 @@ def run_umail(): mbccs = [] body = None bodyfile = None + bodymtype = 'plain' afiles = [] amimetypes = [] gencmd = False @@ -110,6 +112,8 @@ def run_umail(): bodyfile = None elif not path.exists(bodyfile): die("%s: fichier introuvable" % bodyfile) + elif option in ('--html',): + bodymtype = 'html' elif option in ('--gencmd',): gencmd = True @@ -133,14 +137,14 @@ def run_umail(): if not afiles: # Sans attachement, faire un message simple - msg = MIMEText('\n'.join(lines), 'plain') + msg = MIMEText('\n'.join(lines), bodymtype) msg.set_charset('utf-8') else: # Il y a des attachement, faire un multipart msg = MIMEMultipart() #msg.set_charset('utf-8') if lines: - body = MIMEText('\n'.join(lines), 'plain') + body = MIMEText('\n'.join(lines), bodymtype) body.set_charset('utf-8') msg.attach(body) i = 0 diff --git a/lib/repoctl/json_build.php b/lib/repoctl/json_build.php new file mode 100755 index 0000000..607c941 --- /dev/null +++ b/lib/repoctl/json_build.php @@ -0,0 +1,48 @@ +#!/usr/bin/php + $newurl" + repourl="$newurl" + break + fi + fi + done + + local rnametypeprefix tmp found + for rnametypeprefix in "${REPO_TYPES[@]}"; do + splitfsep "$rnametypeprefix" : rname tmp + splitfsep "$tmp" : rtype rprefix + if [ "${repourl#$rprefix}" != "$repourl" ]; then + found=1 + break + fi + done + if [ -z "$found" ]; then + rname= + rtype=gitolite + rprefix= + fi +} + +function curlto() { + local url="$1"; shift + local payload="$1"; shift + local outfile="$1"; shift + local tmpfile + if [ -z "$outfile" ]; then + ac_set_tmpfile tmpfile + outfile="$tmpfile" + fi + + local -a args + local r http_code + args=(-s -w '%{http_code}' -o "$outfile") + [ -n "$payload" ] && args+=(-d "$payload") + [ -n "$HTTP_METHOD" ] && args+=(-X "$HTTP_METHOD") + args+=("$@" "$url") + setx http_code=curl "${args[@]}" + + case "$http_code" in + 2*) r=0;; + 4*) r=1;; + 5*) r=3;; + *) r=11;; + esac + if [ -n "$tmpfile" ]; then + cat "$tmpfile" + ac_clean "$tmpfile" + fi + + upvar http_code "$http_code" + return "$r" +} + +function gogs_setvars() { + gogs_url="${rname}_GOGS_URL"; gogs_url="${!gogs_url}" + gogs_user="${rname}_GOGS_USER"; gogs_user="${!gogs_user}" + gogs_key="${rname}_GOGS_KEY"; gogs_key="${!gogs_key}" + userpath="${repourl#$rprefix}" + splitfsep "$userpath" / user path +} + +################################################################################ + +function create_action() { + case "$rtype" in + #gitolite) ;; + gogs|gitea) + gogs_setvars + gogs_create_action "$@" + ;; + *) die "$rtype: type de dépôt non supporté";; + esac +} +function gogs_create_action() { + local url repourl desc payload result + local -a vars + if [ -n "$user" -a "$user" != "$gogs_user" ]; then + # créer un dépôt dans une organisation + url="$gogs_url/api/v1/org/$user/repos" + repourl="$gogs_url/$user/$path" + else + # créer un dépôt pour un utilisateur + url="$gogs_url/api/v1/user/repos" + repourl="$gogs_url/$gogs_user/$path" + fi + vars=(name="$path" private=true) + [ -n "$1" ] && vars+=(description="$1"); shift + setx payload=json_build "${vars[@]}" + + [ $# -gt 0 ] && vars=("$@") || vars=("") + setx result=curlto "$url" "$payload" "" \ + -H 'Content-Type: application/json' \ + -H "Authorization: token $gogs_key" || \ + die "Une erreur s'est produite lors de la tentative de création du dépôt +url: $url +payload: $payload +result: $result" + isatty && estep "Création du dépôt $repourl" + echo "$result" | json_get "${vars[@]}" +} + +################################################################################ + +function list_action() { + case "$rtype" in + #gitolite) ;; + gogs|gitea) + gogs_setvars + gogs_list_action "$@" + ;; + *) die "$rtype: type de dépôt non supporté";; + esac +} +function gogs_list_action() { + local url result + local -a vars + if [ -n "$user" -a "$user" != "$gogs_user" ]; then + # lister les dépôts d'une organisation + url="$gogs_url/api/v1/orgs/$user/repos" + else + # lister les dépôts accessibles par l'utilisateur + url="$gogs_url/api/v1/user/repos" + fi + + [ $# -gt 0 ] && vars=("$@") || vars=(full_name) + setx result=curlto "$url" "" "" \ + -H 'Content-Type: application/json' \ + -H "Authorization: token $gogs_key" || \ + die "Une erreur s'est produite lors de la tentative de listage des dépôts +url: $url +result: $result" + echo "$result" | json_each "${vars[@]}" +} + +################################################################################ + +function get_action() { + case "$rtype" in + #gitolite) ;; + gogs|gitea) + gogs_setvars + gogs_get_action "$@" + ;; + *) die "$rtype: type de dépôt non supporté";; + esac +} +function gogs_get_action() { + local url repourl payload result + local -a vars + url="$gogs_url/api/v1/repos/$user/$path" + repourl="$gogs_url/$user/$path" + + [ $# -gt 0 ] && vars=("$@") || vars=("") + local HTTP_METHOD=GET + setx result=curlto "$url" "" "" \ + -H 'Content-Type: application/json' \ + -H "Authorization: token $gogs_key" || \ + die "Une erreur s'est produite lors de la tentative de déplacement du dépôt +url: $url +payload: $payload +result: $result" + isatty && estep "Attributs du dépôt $repourl" + echo "$result" | json_get "${vars[@]}" +} + +################################################################################ + +function edit_action() { + case "$rtype" in + #gitolite) ;; + gogs|gitea) + gogs_setvars + gogs_edit_action "$@" + ;; + *) die "$rtype: type de dépôt non supporté";; + esac +} +function gogs_edit_action() { + local url repourl payload result + local -a vars + url="$gogs_url/api/v1/repos/$user/$path" + repourl="$gogs_url/$user/$path" + vars=() + while [[ "$1" == *=* ]]; do + vars+=("$1") + shift + done + setx payload=json_build "${vars[@]}" + + [ $# -gt 0 ] && vars=("$@") || vars=("") + local HTTP_METHOD=PATCH + setx result=curlto "$url" "$payload" "" \ + -H 'Content-Type: application/json' \ + -H "Authorization: token $gogs_key" || \ + die "Une erreur s'est produite lors de la tentative de déplacement du dépôt +url: $url +payload: $payload +result: $result" + isatty && estep "Mise à jour du dépôt $repourl" + echo "$result" | json_get "${vars[@]}" +} + +################################################################################ + +function delete_action() { + case "$rtype" in + #gitolite) ;; + gogs|gitea) + gogs_setvars + gogs_delete_action "$@" + ;; + *) die "$rtype: type de dépôt non supporté";; + esac +} +function gogs_delete_action() { + local url repourl payload result + url="$gogs_url/api/v1/repos/$user/$path" + repourl="$gogs_url/$user/$path" + + local HTTP_METHOD=DELETE + setx result=curlto "$url" "" "" \ + -H 'Content-Type: application/json' \ + -H "Authorization: token $gogs_key" || \ + die "Une erreur s'est produite lors de la tentative de suppression du dépôt +url: $url +payload: $payload +result: $result" + isatty && estep "Suppression du dépôt $repourl" + [ -n "$result" ] && echo "$result" + return 0 +} + +################################################################################ + +action= +args=( + --help '$exit_with display_help' + -c,--create action=create + -l,--list action=list + -g,--get action=get + -e,--edit action=edit + -d,--delete action=delete +) +parse_args "$@"; set -- "${args[@]}" + +if [ -z "$action" ]; then + action="$1"; shift +fi +[ -n "$action" ] || action=list + +repoctl_init "$1"; shift +[ -n "$repourl" ] || die "Vous devez spécifier l'url du dépôt" + +case "$action" in +c|create) create_action "$@";; +l|list) list_action "$@";; +g|get|s|show) get_action "$@";; +e|edit) edit_action "$@";; +d|del|delete|rm|remove) delete_action "$@";; +esac diff --git a/sqlcsv b/sqlcsv index efb0944..b57fffb 100755 --- a/sqlcsv +++ b/sqlcsv @@ -1159,7 +1159,7 @@ public class sqlcsv { int i = 0, max = args.length; while (i < max) { String arg = args[i]; - if (arg.substring(0, 2).equals("-J") || arg.substring(0, 2).equals("+J")) { + if (arg.length() >= 2 && (arg.substring(0, 2).equals("-J") || arg.substring(0, 2).equals("+J"))) { die("L'option -J doit être spécifiée en premier", null); } else if (arg.equals("-C") || arg.equals("--config")) { config = getArg(args, ++i, "Vous devez spécifier le fichier de configuration"); @@ -1316,7 +1316,7 @@ public class sqlcsv { if (resetPrefs) throw new Exit(); else throw new Exit("Vous devez spécifier l'url de connexion"); } - if (usePrefs) { + if (usePrefs && connid != null) { log.fine("Enregistrement de la valeur connid=" + connid); prefs.put("lastConnid", connid); } diff --git a/uproject b/uproject index 8e81620..40f8764 100755 --- a/uproject +++ b/uproject @@ -250,7 +250,7 @@ function cxone_init() { REPO_PREFIXES=() REPO_TYPES=() - set_defaults pcrone + set_defaults repoctl # Traduire les aliases éventuels local asrcdest asrc adest diff --git a/uscrontab b/uscrontab index c963b67..79310b3 100755 --- a/uscrontab +++ b/uscrontab @@ -202,7 +202,7 @@ Les lignes commençant par # sont des commentaires et sont ignorées 0 1 * * * osvar FIRST=default SECOND - echo \"first is $FIRST and second is $SECOND\" + echo \"first is \$FIRST and second is \$SECOND\" En temps normal, FIRST vaut la valeur par défaut 'default' et SECOND vaut la chaine vide ''. Si on appelle '$scriptname -v FIRST=1 -v SECOND=2' alors ces diff --git a/woclean-logs b/woclean-logs new file mode 100755 index 0000000..e40315d --- /dev/null +++ b/woclean-logs @@ -0,0 +1,90 @@ +#!/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: supprimer les fichiers de log inutiles dans /var/log/WebObjects + +USAGE + $scriptname [options] + +OPTIONS + -u, --useless + Supprimer les logs inutiles. C'est l'option par défaut + -a, --all + Supprimer tous les logs + -t, --today + Supprimer tous les logs du jour" +} + +what=useless +args=( + --help '$exit_with display_help' + -u,--useless what=useless + -a,--all what=all + -t,--today what=today +) +parse_args "$@"; set -- "${args[@]}" + +run_as_root "$@" + +count=0 +cd /var/log/WebObjects || die + +case "$what" in + useless) + einfo "Suppression des fichiers de log inutiles dans /var/log/WebObjects" + ask_yesno "Voulez-vous continuer?" O || exit + + /bin/ls | while read f; do + [ "${f%-[0-9]}" != "$f" ] && continue + + # Tester la presence de "Waiting for requests..." + if ! grep -q 'Waiting for requests...$' "$f"; then + echo "$f: no 'Waiting for Requests...'" + /bin/rm -f "$f" || die + count=$(($count + 1)) + continue + fi + + # Tester la presence de contenu apres Waiting for requests..." + if grep -v "CookieParser: Found a null cookie value in: " "$f" | tail -n 1 | grep -q 'Waiting for requests...$'; then + echo "$f: empty log" + /bin/rm -f "$f" || die + count=$(($count + 1)) + continue + fi + done + ;; + + all) + einfo "Suppression de TOUS les fichiers de logs dans /var/log/WebObjects" + ask_yesno "Voulez-vous continuer?" O || exit + + /bin/ls | while read f; do + if [ "${f%-[0-9]}" = "$f" ]; then + echo "$f" + /bin/rm -f "$f" || exit 1 + count=$(($count + 1)) + fi + done + ;; + + today) + einfo "Suppression des fichiers de logs DU JOUR dans /var/log/WebObjects" + ask_yesno "Voulez-vous continuer?" O || exit + + now="$(date +%Y%m%d)" + + /bin/ls | grep -- "-[0-9]*\\.$now[0-9]*$" | while read f; do + echo "$f" + /bin/rm -f "$f" || exit 1 + count=$(($count + 1)) + done + ;; +esac + +if [ $count -gt 0 ]; then + enote "$count fichiers supprimés" +fi