nutools/dk

2472 lines
89 KiB
Bash
Executable File

#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
source "$(dirname "$0")/lib/ulib/auto" || exit 1
urequire pff json
function display_help() {
uecho "$scriptname: outil pour faciliter l'utilisation de docker
USAGE
$scriptname CMDs...
Si une commande commence ou se termine par un underscore, e.g '_ls', elle est
passée telle quelle à docker (en enlevant l'underscore d'abord, bien sûr). Si
une commande ne figure pas dans la liste ci-dessous, elle est passée telle
quelle à docker.
Certaines commandes ci-dessous ont le même nom que des commandes docker, et
nécessitent un projet docker ou docker-compose. Si ces commandes sont lancées
alors qu'on n'est pas dans un projet docker, alors elles sont passées telle
quelles à docker. ATTENTION: comme la commande est exécutée par docker, cela
veut dire qu'elle n'aura pas la même sématique.
COMMANDES
get-profile
Afficher le profil courant
b|build [NAME=VALUE...] [SERVICE]
Construire les images. Les variables NAME=VALUE sont des 'build args'
p|push
p|push [SERVICES...]
La première syntaxe est utilisable avec un projet docker. Elle permet de
pousser l'image qui a été construite avec build vers le serveur
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 [SERVICE]
Démarrer le(s) service(s)
k|stop [SERVICE]
Arrêter le(s) service(s)
1|up
Créer l'environnement, démarrer les services et suivre les logs de façon
interactive.
Vaguement équivalent à -- start -- logs
l|logs [SERVICE]
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. Si la commande n'est pas spécifiée, la
valeur par défaut est bash. Avec les options --select-service et
--select-machine, l'argument SERVICE vaut par défaut 'SS'
Un pseudo-tty est alloué pour la commande et STDIN est ouvert.
d|brd [NAME=VALUE...]
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
arrête l'affichage des logs avec Ctrl+C, arrêter les services et
supprimer l'environnement (comme avec down)
Vaguement équivalent à -- build -- start [args] -- logs
suivi de -- down
bs [NAME=VALUE...]
Construire les images (comme avec build) puis démarrer les services
(comme avec start)
Equivalent à -- build -- start [args]
br [NAME=VALUE...] SERVICE [COMMAND]
Construire les images (comme avec build) puis démarrer le service avec
la commande spécifiée (comme avec run)
Equivalent à -- build [NAME=VALUE...] -- run [args]
y|deploy [args...]
(Re)déployer un stack. Cette commande ne fonctionne qu'en mode swarm.
Implique --stack
by|bd [NAME=VALUE...] [args...]
Equivalent à --stack -- build [NAME=VALUE...] -- deploy args...
Utilisable notamment en développement
bp [NAME=VALUE...] [args...]
Equivalent à --stack -- build [NAME=VALUE...] -- push args...
bpy|bpd [NAME=VALUE...] [args...]
Equivalent à --stack -- build [NAME=VALUE...] -- push -- deploy args...
ser|service COMMAND SERVICE [args...]
Frontend pour 'docker service COMMAND args... SERVICE'
Cette commande ne fonctionne qu'en mode swarm. Il n'est pas nécessaire
de préfixer le nom du service avec le nom du stack, pour être cohérent
avec les autres commandes
IMPORTANT: notez que les arguments sont placés avant le nom du service.
Celà signifie qu'on ne peut spécifier que des options à la commande.
Penser aussi à protéger ces options de l'analyse eg.
$scriptname -- service logs web -f
Pour des cas d'utilisation plus complexe, il faut lancer directement
docker service
sta|status [SERVICE] [num]
Afficher le status du service spécifié: pour chaque occurence, sur quel
noeud il tourne (ou tournait), quel est le statut actuel ainsi que le
message associé. Si le service n'est pas spécifié, prendre la liste des
services affichée par docker service ls
'num' est le numéro de l'instance de service. Si ce numéro est précisé,
afficher les informations uniquement pour cette instance là, ainsi que
les logs
Il est possible de spécifier un filtre docker name=value avant 'num'. Ce
filtre est utilisé pour sélectionner certaines instances du service.
- id=<ID> a task's ID ou prefix
- name=<string> a task's name or prefix
- node=<string> a node's name or ID
- desired-state=(running|shutdown|accepted)
Pour faciliter l'utilisation des filtres, certains aliases sont définis.
'=r', '=k' et '=a' sont des aliases pour desired-state=running,
desired-state=shutdown et desired-state=accepted respectivement
u|update SERVICE [args...]
Mettre à jour un service, équivalent à 'service update SERVICE'
scale SERVICE=REPLICAS [args...]
Mettre à jour le nom de réplicas d'un service, équivalent à la commande
'service scale SERVICE=REPLICAS'
ip|show-ip [SERVICE]
Afficher l'adresse IP interne du service
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.
cp|copy CONTAINER:SRC DEST
cp|copy SRC CONTAINER:DEST
Copier un fichier ou un répertoire depuis/vers un container. Avec les
options --select-service et --select-machine, il est possible d'utiliser
'SS' pour signifier le container sélectionné e.g
$scriptname -s X_Y -- cp file SS:path/to/dir
ps [filter|name=value]
Afficher les containers en cours d'exécution
Le filtre est une expression régulière de type awk qui est mise en
correspondance 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=(<image-name>[:tag]|<image-id>| <image@digest>)
containers created from an image or a descendant.
- before=(<container-name>|<container-id>)
- expose=(<port>[/<proto>]|<startport-endport>/[<proto>])
- exited=<int> an exit code of <int>
- health=(starting|healthy|unhealthy|none)
- id=<ID> a container's ID
- is-task=(true|false)
- label=<key> or label=<key>=<value>
- name=<string> a container's name
- network=(<network-id>|<network-name>)
- publish=(<port>[/<proto>]|<startport-endport>/[<proto>])
- since=(<container-name>|<container-id>)
- status=(created|restarting|removing|running|paused|exited)
- volume=(<volume name>|<mount point destination>)
ls [filter|name=value]
Lister les images actuellement présentes
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=<key> or label=<key>=<value>
- before=(<image-name>[:tag]|<image-id>| <image@digest>)
- since=(<image-name>[:tag]|<image-id>| <image@digest>)
- 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|cr|cs [args...]
Frontend pour lancer composer à l'intérieur d'un container. Les
commandes 'ci', 'cu', 'cr', 'cs' sont des alias pour 'composer install',
'composer update', 'composer rshell' et 'composer shell' respectivement
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_PHP -- Version de PHP en dessous de laquelle COMPOSER_IMAGE
est utilisé. En d'autres termes, c'est la version minimum de PHP
nécessaire pour faire tourner composer. L'idée est que si la version
de PHP installée est suffisante, il n'est pas nécessaire de passer par
une image docker.
Cette valeur doit être spécifiée avec le format de PHP_VERSION_ID i.e
70300 pour PHP 7.3
Spécifier 'any' ou 'force' pour forcer l'utilisation de l'image docker
* COMPOSER_PHP_MAX -- Version de PHP à partir de laquelle COMPOSER_IMAGE
est utilisée. En d'autres termes, c'est la version maximum de PHP, à
partir de laquelle il faut passer par une image docker. L'idée est que
si la version de PHP installée est trop récente, ça peut poser
problème avec le calcul des dépendances.
Cette valeur doit être spécifiée avec le format de PHP_VERSION_ID i.e
70300 pour PHP 7.3
Si la valeur n'est pas spécifiée ou vaut 'none', elle est ignorée.
* 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 'rshell' 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 'shell' 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. Utiliser -u pour desélectionner la machine en cours, e.g -m-u
-s, --select-service SERVICE[.N]
Sélectionner le service spécifié. Si le service a plusieurs instances,
il est possible de sélectionner une instance en particulier avec le
suffixe '.N' (par défaut, la première instance est sélectionnée)
Quand un service est sélectionné, dans les commandes 'cp' et 'exec', la
chaine 'SS' est remplacée par le nom du container correspondant e.g
$scriptname -s X_Y exec SS bash
$scriptname -s X_Y -- cp -L SS:path/to/file destdir
-t, --select-machine SERVICE[.N]
Sélectionner le service spécifié, puis choisir l'environnement
docker-machine correspondant. Cette option a la priorité sur l'option
--select-service
Cette option est traitée après l'option --set-machine, ce qui permet de
se connecter sur le manager d'un cluster puis de sélectionner le worker
sur lequel tourne un service
-n, --fake
Ne pas lancer les commandes, simplement les afficher
-e, --build-arg, --env VAR=VALUE
(Re)définir un argument pour le build ou une variable d'environnement
-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
OPTIONS build
(ces options ne sont valides que pour les commandes build, brd, bs, bd, bpd)
--stack
Indiquer que le build est fait pour un déploiement avec deploy.
S'il existe un fichier docker-stack.yml, il est utilisé de préférence à
la place de docker-compose.yml. De même, les fichiers de profil de la
forme docker-stack.PROFILE.yml sont utilisés de préférence aux fichiers
docker-compose.PROFILE.yml
Cette option n'est nécessaire qu'avec build puisque les commandes
deploy, bd, bpd et update impliquent --stack
-j, --no-cache
Ne pas utiliser le cache lors du 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=
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 du dépôt en mode devel
app_SRC= # répertoire/fichier source (si URL='')
app_DEST= # répertoire dans lequel faire le checkout
# ou destination si synchro avec app_SRC
app_NAME= # nom du répertoire dest si checkout; par
# défaut prendre la valeur 'app'
app_RSYNC_OPTS= # options de rsync si synchro (avec app_SRC
# ou app_DEVEL_SRCDIR)
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)
app_COMPOSER_ARGS=() # arguments de composer install|update
-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
Spécifier la branche par défaut pour update-apps
OPTIONS deploy
-l, --without-registry-auth
Ne pas transporter les informations d'autorisation aux agents swarm
(c'est à dire ne pas utiliser l'option --with-registry-auth)
VARIABLES de update-apps.conf
SRC
répertoire/fichier source, si URL n'est pas défini. si ce chemin est
relatif, il doit être exprimé par rapport au répertoire du projet.
IMPORTANT: dans ce cas, DEST n'est pas le répertoire de base du
checkout, mais le répertoire destination pour la synchro
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 '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
SRC= # répertoire/fichier source (si URL='')
DEST= # répertoire dans lequel faire le checkout
# ou destination si URL=''
NAME= # nom du répertoire dest si checkout
RSYNC_OPTS= # options de rsync si synchro avec SRC ou
# DEVEL_SRCDIR
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'
COMPOSER_ARGS
options à utiliser avec composer install|update. La valeur par défaut
dépend du profil:
prod: --no-dev -o
test: --no-dev -o
autres: (pas d'options)
FONCTIONS de update-apps.conf
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
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
mvn [ARGS]
Lancer maven dans le répertoire destination avec les arguments spécifiés"
}
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
local head commit tag
commit="$(git rev-list --tags --max-count=1 2>/dev/null)"
if [ -n "$commit" ]; then
tag="$(git describe --tags "$commit" 2>/dev/null)"
if [ -n "$tag" ]; then
head="$(git rev-parse HEAD)"
if [ "$commit" != "$head" ]; then
echo "$tag-develop"
else
echo "$tag"
fi
return
fi
fi
elif [ -f VERSION.txt ]; then
cat VERSION.txt
fi
echo develop
}
function docker_add_build_arg() {
eval "replace_build_args+=(--build-arg $1=\"$2\"); $1=\"$2\""
}
function docker_parse_build_args() {
cat "$1" |
grep -v '^#' |
grep -v '^$' |
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)"
[ -f build.env ] && eval "$(docker_parse_build_args build.env)"
[ -n "$PROFILE" -a -f ".build.$PROFILE.env" ] && eval "$(docker_parse_build_args ".build.$PROFILE.env")"
[ -n "$PROFILE" -a -f "build.$PROFILE.env" ] && eval "$(docker_parse_build_args "build.$PROFILE.env")"
}
function docker_set_env_args() {
[ -f .build.env ] && source ./.build.env
[ -f build.env ] && source ./build.env
[ -n "$PROFILE" -a -f ".build.$PROFILE.env" ] && source "./.build.$PROFILE.env"
[ -n "$PROFILE" -a -f "build.$PROFILE.env" ] && source "./build.$PROFILE.env"
}
function docker_set_run_args() {
replace_run_args+=(--env-file "$1")
source "$1"
}
function docker_check_name() {
[ -n "$NAME" ] || die "Vous devez définir NAME dans .build.env"
if [ "$1" == set_container_name ]; then
project_name="$NAME"
container_name="${project_name//[^a-zA-Z0-9_-]/}"
[ -n "$PROFILE" ] && container_name="${container_name}_$PROFILE"
fi
}
function compose_set_project_name() {
local PROJECT_NAME= PROJECT_NAME_REMOVE_SUFFIX=--defaults-- PROJECT_NAME_ADD_PROFILE=
[ -f .compose.env ] && source ./.compose.env
[ -n "$PROJECT_NAME" ] || PROJECT_NAME="$(basename -- "$(pwd)")"
if [ "$PROJECT_NAME_REMOVE_SUFFIX" == --defaults-- ]; then
if [ "${PROJECT_NAME%.service}" != "$PROJECT_NAME" ]; then PROJECT_NAME="${PROJECT_NAME%.service}"
elif [ "${PROJECT_NAME%.stack}" != "$PROJECT_NAME" ]; then PROJECT_NAME="${PROJECT_NAME%.stack}"
elif [ "${PROJECT_NAME%.network}" != "$PROJECT_NAME" ]; then PROJECT_NAME="${PROJECT_NAME%.network}"
fi
else
PROJECT_NAME="${PROJECT_NAME%$PROJECT_NAME_REMOVE_SUFFIX}"
fi
if [ -n "$PROFILE" ]; then
[ -n "$COMPOSE_PROJECT_NAME" ] || COMPOSE_PROJECT_NAME="$PROJECT_NAME${PROJECT_NAME_ADD_PROFILE:+_${PROFILE}}"
else
[ -n "$COMPOSE_PROJECT_NAME" ] || COMPOSE_PROJECT_NAME="$PROJECT_NAME"
fi
export COMPOSE_PROJECT_NAME
if [ "$1" == set_container_name ]; then
project_name="$PROJECT_NAME"
container_name="${project_name//[^a-zA-Z0-9_-]/}"
[ -n "$PROFILE" -a -n "$PROJECT_NAME_ADD_PROFILE" ] && container_name="${container_name}_$PROFILE"
fi
}
function compose_set_env_args() {
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
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 "$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
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
compose_set_project_name "$@"
}
function host_run() {
# lancer le script $2..@ sur l'hôte $1
# si $1 n'est pas défini ou est le nom de l'hôte local ou vaut 'localhost',
# lancer le script en local avec les droits root
# sinon, si docker-machine existe, l'hôte doit correspondre à la machine active
# sinon, lancer inconditionnellement le script sur l'hôte distant
local host="$1" script="$2"; shift; shift
if [ -n "$host" -a "$host" != localhost ]; then
if check_hostname "$host"; then
estep "Lancement de $script localement"
runscript_as_root "$script" "$@"
elif progexists docker-machine; then
if [ "${host%%.*}" == "$DOCKER_MACHINE_NAME" ]; then
estep "Copie du script vers root@$host"
scp "$script" "root@$host:/tmp/tmp-dk-service-script" || die
estep "Lancement du script à distance"
local -a args; args=(/tmp/tmp-dk-service-script "$@")
ssh -qt "root@$host" "$(qvals "${args[@]}"); rm -f /tmp/tmp-dk-service-script"
else
ewarn "La machine active ($DOCKER_MACHINE_NAME) n'est pas la destination ($host)"
fi
else
estep "Copie du script vers root@$host"
scp "$script" "root@$host:/tmp/tmp-dk-service-script" || die
estep "Lancement du script à distance"
local -a args; args=(/tmp/tmp-dk-service-script "$@")
ssh -qt "root@$host" "$(qvals "${args[@]}"); rm -f /tmp/tmp-dk-service-script"
fi
else
estep "Lancement de $script localement"
runscript_as_root "$script" "$@"
fi
}
function local_run() {
# lancer le script $2..@ sur l'hôte $1 uniquement si c'est l'hôte courant
local host="$1" script="$2"; shift; shift
if [ -n "$host" ]; then
if ! check_hostname "$host"; then
eerror "Cette commande doit obligatoirement être lancée depuis l'hôte $host"
return 1
fi
fi
estep "Lancement de $script localement"
runscript_as_root "$script" "$@"
}
BUILD_UPDATE_APPS=
BUILD_BUILD=
UPDATE_APPS_ORIGIN=
UPDATE_APPS_BRANCH=
function build_set_options() {
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" nofix="$3"
[ -n "$destdir" ] || destdir=db
[ -n "$srcdir" ] || srcdir="$DEST"
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"
return 1
fi
local -a sqlmigs; local sqlmig name
array_lsall sqlmigs "$srcdir"
mkdir -p "$destdir" || return 1
for sqlmig in "${sqlmigs[@]}"; do
if [ -d "$sqlmig" ]; then
setx name=basename -- "$sqlmig"
rsync -av --delete-after "$sqlmig/" "$destdir/$name"
else
rsync -av "$sqlmig" "$destdir"
fi
done
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 update_apps_func_mvn() {
local cwd="$(pwd)"
cd "$DEST"
mvn "$@" || die
cd "$cwd"
}
function build_update_apps() {
[ -n "$BUILD_UPDATE_APPS" ] || return 0
[ -f update-apps.conf ] || return 0
# charger le fichier de configuration
local DEFAULT_ORIGIN DEFAULT_BRANCH APPS
DEFAULT_ORIGIN="$UPDATE_APPS_ORIGIN"
[ -z "$DEFAULT_ORIGIN" ] && DEFAULT_ORIGIN=origin
DEFAULT_BRANCH="$UPDATE_APPS_BRANCH"
#XXX à terme, ne déployer en prod que la branche master
[ -z "$DEFAULT_BRANCH" -a "$PROFILE" == prod ] && DEFAULT_BRANCH=develop #XXX 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
case "$PROFILE" in
prod) PRODUCTION=1; DEVELOPMENT=; is_defined DEFAULT_COMPOSER_ARGS || DEFAULT_COMPOSER_ARGS=(--no-dev -o);;
test) PRODUCTION=1; DEVELOPMENT=1; is_defined DEFAULT_COMPOSER_ARGS || DEFAULT_COMPOSER_ARGS=(--no-dev -o);;
devel) PRODUCTION=; DEVELOPMENT=1; is_defined DEFAULT_COMPOSER_ARGS || DEFAULT_COMPOSER_ARGS=();;
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 URL SRC DEVEL_SRCDIR DEST NAME have_RSYNC_OPTS RSYNC_OPTS ORIGIN BRANCH TYPE after_update after_updates composer_action
local -a composer_args
for app in "${APPS[@]}"; do
etitle "$app"
var="${app//-/_}"
URL="${var}_URL"; URL="${!URL}"
if [ -z "$URL" ]; then
SRC="${var}_SRC"; SRC="${!SRC}"
if [ -z "$SRC" ]; then
ewarn "$app: vous devez définir ${var}_URL ou ${var}_SRC"
eend; return 1
fi
fi
DEVEL_SRCDIR="${var}_DEVEL_SRCDIR"; DEVEL_SRCDIR="${!DEVEL_SRCDIR}"
[ -n "$DEVEL_SRCDIR" ] || DEVEL_SRCDIR="$DEFAULT_DEVEL_SRCDIR/${URL##*/}"
DEST="${var}_DEST"; DEST="${!DEST}"
[ -n "$DEST" ] || DEST="$app/b"
NAME="${var}_NAME"; NAME="${!NAME}"
[ -n "$NAME" ] || NAME="$app"
if is_defined "${var}_RSYNC_OPTS"; then
have_RSYNC_OPTS=1
RSYNC_OPTS="${var}_RSYNC_OPTS[@]"; RSYNC_OPTS=("${!RSYNC_OPTS}")
else
have_RSYNC_OPTS=
RSYNC_OPTS=()
fi
ORIGIN="${var}_${PROFILE}_ORIGIN"; ORIGIN="${!ORIGIN}"
[ -n "$ORIGIN" ] || { ORIGIN="${var}_ORIGIN"; ORIGIN="${!ORIGIN}"; }
[ -n "$ORIGIN" ] || ORIGIN="$DEFAULT_ORIGIN"
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}"
if [ -n "$BUILD_UPDATE_DEVEL" ]; then
mkdir -p "$DEST" || { eend; return 1; }
DEST="$DEST/$NAME"
# synchronisation en mode devel
[ -n "$have_RSYNC_OPTS" ] || RSYNC_OPTS=(--delete --delete-excluded)
local -a rsync_opts; rsync_opts=(-a --exclude .git/ "${RSYNC_OPTS[@]}")
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
elif [ -n "$URL" ]; then
mkdir -p "$DEST" || { eend; return 1; }
DEST="$DEST/$NAME"
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
# 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 et ci-dessous
if [ -f "$DEST/composer.json" ]; then TYPE=composer
else TYPE=none
fi
fi
elif [ -n "$SRC" ]; then
if [ -d "$SRC" ]; then
[ -n "$have_RSYNC_OPTS" ] || RSYNC_OPTS=(--delete --delete-excluded)
local -a rsync_opts; rsync_opts=(-a --exclude .git/ "${RSYNC_OPTS[@]}")
SRC="${SRC%/}/"
DEST="${DEST%/}"
if [ ! -d "$DEST" ]; then
# s'assurer que le répertoire parent existe
mkdir -p "$(dirname -- "$DEST")" || { eend; return 1; }
fi
DEST="$DEST/"
estep "Synchro $SRC --> $DEST"
rsync "${rsync_opts[@]}" "$SRC" "$DEST" || { eend; return 1; }
elif [ -f "$SRC" ]; then
[ -n "$have_RSYNC_OPTS" ] || RSYNC_OPTS=()
local -a rsync_opts; rsync_opts=(-a "${RSYNC_OPTS[@]}")
if [ "${DEST%/}" != "$DEST" -a ! -d "$DEST" ]; then
# on demande à ce que DEST soit un répertoire mais il
# n'existe pas: le créer
DEST="${DEST%/}"
mkdir -p "$DEST" || { eend; return 1; }
fi
if [ -d "$DEST" ]; then
DEST="$DEST/"
elif [ ! -f "$DEST" ]; then
# la destination n'existe pas: s'assurer que le répertoire
# parent existe
mkdir -p "$(dirname -- "$DEST")" || { eend; return 1; }
fi
estep "Synchro $SRC --> $DEST"
rsync "${rsync_opts[@]}" "$SRC" "$DEST" || { eend; return 1; }
else
eerror "$app: $SRC: répertoire/fichier introuvable"
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
else
# ne devrait pas se produire
die "ni URL ni SRC ne sont définis"
fi
after_updates="${var}_AFTER_UPDATE"
if is_defined "$after_updates"; then
after_updates="$after_updates[@]"; after_updates=("${!after_updates}")
elif [ "$TYPE" == composer ]; then
after_updates=(sqlmig)
else
after_updates=()
fi
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"
composer_args="${var}_${PROFILE}_COMPOSER_ARGS"
is_defined "$composer_args" || composer_args="${var}_COMPOSER_ARGS"
is_defined "$composer_args" || composer_args="DEFAULT_COMPOSER_ARGS"
composer_args="${composer_args}[@]"; composer_args=("${!composer_args}")
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" "${composer_args[@]}" || { 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
etitle "$after_update"
eval "$after_update" || { eend; eend; return 1; }
eend
elif [ "${after_update#./}" != "$after_update" ]; then
# commande relative, la lancer telle quelle
etitle "$after_update"
eval "$after_update" || { eend; eend; return 1; }
eend
else
# c'est une fonction update_apps_func_*
etitle "$after_update"
eval "update_apps_func_$after_update" || { eend; eend; return 1; }
eend
fi
done
eend
done
eend
}
function initialize_build_env() {
CTXDIR=.
NAME=
TAGS=(latest)
VERSION=
DIST=
}
function default_update_build_env() {
local var
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")
# Variables en ligne de commande
for var in "${VARS[@]}" "$@"; do
docker_add_build_arg "${var%%=*}" "${var#*=}"
done
}
function update_build_env() { default_update_build_env "$@"; }
function default_compose_build() {
${FAKE:+qvals} docker-compose \
"${replace_env_args[@]}" "${env_args[@]}" \
build \
${NO_CACHE:+--no-cache} \
"${replace_build_args[@]}" "${build_args[@]}" \
"$@"
}
function default_docker_build() {
local tag
for tag in "${TAGS[@]}"; do
replace_build_args+=(-t "$NAME:$tag")
done
${FAKE:+qvals} "$DOCKER" build \
${NO_CACHE:+--no-cache} \
"${replace_env_args[@]}" "${env_args[@]}" \
"${replace_build_args[@]}" "${build_args[@]}" \
"$@" "$CTXDIR"
}
function compose_build() {
[ -n "$BUILD_BUILD" ] || return 0
default_compose_build "$@"
}
function docker_build() {
[ -n "$BUILD_BUILD" ] || return 0
default_docker_build "$@"
}
function __parse_bargs() {
bargs=()
args=()
while [ $# -gt 0 ]; do
[[ "$1" == *=* ]] && bargs+=("$1") || args+=("$1")
shift
done
}
function auto_build() {
local -a replace_env_args env_args
local -a replace_build_args build_args
initialize_build_env
local -a args bargs; local barg
__parse_bargs "$@"
set -- "${args[@]}"
if [ -f docker-compose.yml ]; then
compose_set_env_args
update_build_env "${bargs[@]}"
build_update_apps || return 1
compose_build "$@"
elif [ -f Dockerfile ]; then
docker_parse_env_args
docker_check_name
docker_add_build_arg build_date "$(date +%y%m%d)"
update_build_env "${bargs[@]}"
build_update_apps || return 1
docker_build "$@"
else
for barg in "${bargs[@]}"; do
replace_build_args+=(--build-arg "$barg")
done
${FAKE:+qvals} "$DOCKER" build \
${NO_CACHE:+--no-cache} \
"${replace_build_args[@]}" "$@"
fi
}
function default_compose_push() {
${FAKE:+qvals} docker-compose \
"${replace_env_args[@]}" "${env_args[@]}" \
push \
"$@"
}
function default_docker_push() {
${FAKE:+qvals} "$DOCKER" push \
"${replace_env_args[@]}" "${env_args[@]}" \
"$@"
}
function compose_push() {
default_compose_push "$@"
}
function docker_push() {
local tag
for tag in "${TAGS[@]}"; do
default_docker_push "$NAME:$tag" "$@"
done
}
function auto_push() {
local -a replace_env_args env_args
local -a replace_build_args build_args
initialize_build_env
if [ -f docker-compose.yml ]; then
compose_set_env_args
update_build_env
compose_push "$@"
elif [ -f Dockerfile ]; then
docker_parse_env_args
docker_check_name
update_build_env
docker_push "$@"
else
${FAKE:+qvals} "$DOCKER" push "$@"
fi
}
function default_compose_up() {
${FAKE:+qvals} docker-compose \
"${replace_env_args[@]}" "${env_args[@]}" \
up "${replace_run_args[@]}" "${run_args[@]}" \
"${replace_user_args[@]}" "${user_args[@]}" "$@"
}
function default_docker_up() {
${FAKE:+qvals} "$DOCKER" run \
"${replace_env_args[@]}" "${env_args[@]}" \
"${replace_run_args[@]}" "${run_args[@]}" \
"$NAME" \
"${replace_user_args[@]}" "${user_args[@]}" "$@"
}
function compose_up() { default_compose_up "$@"; }
function docker_up() { default_docker_up "$@"; }
function auto_up() {
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=(-d)
compose_up "$@"
elif [ -f Dockerfile ]; then
docker_set_env_args
docker_check_name set_container_name
replace_run_args=(-d --name "$container_name")
docker_up "$@"
else
${FAKE:+qvals} "$DOCKER" run "$@"
fi
}
function default_compose_stop() {
${FAKE:+qvals} docker-compose \
"${replace_env_args[@]}" "${env_args[@]}" \
stop "${replace_stop_args[@]}" "${stop_args[@]}" \
"$@"
}
function default_docker_stop() {
${FAKE:+qvals} "$DOCKER" container stop \
"${replace_stop_args[@]}" "${stop_args[@]}" \
"$container_name" "$@"
}
function compose_stop() { default_compose_stop "$@"; }
function docker_stop() { default_docker_stop "$@"; }
function auto_stop() {
local -a replace_env_args env_args
local -a replace_stop_args stop_args
local project_name container_name
if [ -f docker-compose.yml ]; then
compose_set_env_args
compose_stop "$@"
elif [ -f Dockerfile ]; then
docker_set_env_args
docker_check_name set_container_name
docker_stop "$@"
else
${FAKE:+qvals} "$DOCKER" stop "$@"
fi
}
function default_compose_logs() {
${FAKE:+qvals} docker-compose \
"${replace_env_args[@]}" "${env_args[@]}" \
logs "${replace_logs_args[@]}" "${logs_args[@]}" \
"$@"
}
function default_docker_logs() {
${FAKE:+qvals} "$DOCKER" logs \
"${replace_logs_args[@]}" "${logs_args[@]}" \
"$container_name" "$@"
}
function compose_logs() { default_compose_logs "$@"; }
function docker_logs() { default_docker_logs "$@"; }
function auto_logs() {
local -a replace_env_args env_args
local -a replace_logs_args logs_args
local project_name container_name
if [ -f docker-compose.yml ]; then
compose_set_env_args
replace_logs_args=(-f)
compose_logs "$@"
elif [ -f Dockerfile ]; then
docker_set_env_args
docker_check_name set_container_name
replace_logs_args=(-f)
docker_logs "$@"
else
${FAKE:+qvals} "$DOCKER" logs "$@"
fi
}
function default_compose_down() {
${FAKE:+qvals} docker-compose \
"${replace_env_args[@]}" "${env_args[@]}" \
down "${replace_down_args[@]}" "${down_args[@]}" \
"$@"
}
function default_docker_down() {
estep "stop"
${FAKE:+qvals} "$DOCKER" container stop \
"${replace_down_args[@]}" "${down_args[@]}" \
"$container_name" "$@"
estep "rm"
${FAKE:+qvals} "$DOCKER" container rm \
"${replace_rm_args[@]}" "${rm_args[@]}" \
"$container_name"
}
function compose_down() { default_compose_down "$@"; }
function docker_down() { default_docker_down "$@"; }
function auto_down() {
local -a replace_env_args env_args
local -a replace_down_args down_args
local -a replace_rm_args rm_args
local project_name container_name
if [ -f docker-compose.yml ]; then
compose_set_env_args
compose_down "$@"
elif [ -f Dockerfile ]; then
docker_set_env_args
docker_check_name set_container_name
docker_down "$@"
else
${FAKE:+qvals} "$DOCKER" stop "$@"
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 disable_tty
local -a envs labels
local var
for var in "${VARS[@]}"; do
envs+=(-e "$var")
done
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=
-T disable_tty=1
@ 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"}
${disable_tty:+-T}
)
compose_run "$@"
elif [ -f Dockerfile ]; then
docker_set_env_args
docker_check_name set_container_name
replace_run_args=(
--name "$container_name"
${rm:+--rm}
${volume:+-v "$volume"}
)
docker_run "$@"
else
${FAKE:+qvals} "$DOCKER" run ${rm:+--rm} ${volume:+-v "$volume"} "$@"
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
local container="$1"; shift
if [ -n "$SELECT_CONTAINER" ]; then
[ -z "$container" -o "$container" == SS ] && container="$SELECT_CONTAINER"
fi
local command="$1"; shift
[ -n "$command" ] || command=bash
compose_exec "$container" "$command" "$@"
elif [ -f Dockerfile ]; then
docker_set_env_args
docker_check_name set_container_name
exec_args=(-it)
local command="$1"; shift
[ -n "$command" ] || command=bash
docker_exec "$command" "$@"
else
local container="$1"; shift
if [ -n "$SELECT_CONTAINER" ]; then
[ -z "$container" -o "$container" == SS ] && container="$SELECT_CONTAINER"
fi
local command="$1"; shift
[ -n "$command" ] || command=bash
${FAKE:+qvals} "$DOCKER" exec -it "$container" "$command" "$@"
fi
}
function default_docker_deploy() {
${FAKE:+qvals} "$DOCKER" \
stack deploy \
"${replace_deploy_args[@]}" "${deploy_args[@]}" \
"$container_name" "$@"
}
function docker_deploy() { default_docker_deploy "$@"; }
function auto_deploy() {
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
docker_set_deploy_args set_container_name
[ -n "$WITH_REGISTRY_AUTH" ] && replace_deploy_args+=(--with-registry-auth)
docker_deploy "$@"
else
die "Impossible de trouver ni docker-compose.yml ni docker-stack.yml"
fi
}
function default_docker_service() {
${FAKE:+qvals} "$DOCKER" service "$@"
}
function docker_service() { default_docker_service "$@"; }
function auto_service() {
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" "$@"
;;
*) # ces commandes ont besoin du nom du service
local service="$1"; shift
[ -n "$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" "${command_args[@]}" "$@" "$service"
;;
esac
else
${FAKE:+qvals} "$DOCKER" service "$@"
fi
}
function __format() {
# formatter une liste de valeur. chaque argument est de la forme [SIZE:]VALUE
# il ne faudrait utiliser la forme VALUE que pour le dernier argument
local size value
for value in "$@"; do
if [[ "$value" == *:* ]]; then
size="${value%%:*}"
value="${value#*:}"
else
size=
fi
if [ -n "$size" ]; then
while [ "${#value}" -lt "$size" ]; do
value="$value "
done
if [ "${#value}" -gt "$size" ]; then
[ "$size" -gt 1 ] && value="${value:0:$(($size - 1))}"
value="$value"$'\xe2\x80\xa6'
fi
fi
echo -n "${value} "
done
echo
}
function __status_query_task() {
setx taskData="$DOCKER" inspect "$taskID"
setx taskID=json_get 0.ID <<<"$taskData"
setx taskSlot=json_get 0.Slot <<<"$taskData"
setx taskStatus=json_get 0.Status.Err <<<"$taskData"
setx serviceID=json_get 0.ServiceID <<<"$taskData"
}
function __status_query_service() {
[ "$serviceID" == "$pServiceID" ] && return
setx serviceData="$DOCKER" inspect "$serviceID"
setx serviceName=json_get 0.Spec.Name <<<"$serviceData"
setx serviceUpdateStatus=json_get 0.UpdateStatus.Message <<<"$serviceData"
pServiceID="$serviceID"
}
function __status_process_data() {
__status_query_task
__status_query_service
if [ -n "$serviceUpdateStatus" ]; then
echo "\
================
$serviceName: $serviceUpdateStatus
================"
serviceUpdateStatus=
fi
if [ "$num" -eq 0 ]; then
# afficher les en-têtes
__format 3:num 48:taskName 32:node 8:dState 20:cState :error
fi
if [ -z "$fnum" -o "$num" == "$fnum" ]; then
taskName="$serviceName.$taskSlot"
__format 3:"$num" 48:"$taskName.$taskID" 32:"$node" 8:"$desiredState" 20:"$currentState" :"$taskStatus"
fi
if [ "$num" == "$fnum" ]; then
"$DOCKER" service logs -f "$taskID"
fi
let num=num+1
}
function auto_status() {
local -a services
local service
if [[ "$1" == *=* ]]; then
: # le premier argument est un filtre
elif [ -n "$1" -a -z "${1//[0-9]/}" ]; then
: # le premier argument est un numéro d'instance
else
# le premier argument est un nom de service
service="$1"; shift
fi
if [ -n "$service" ]; then
if [ -f docker-compose.yml -o -f docker-stack.yml ]; then
# si on est dans répertoire de projet, il est possible de spécifier
# le service sans préfixe
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}_}"
fi
services=("$service")
else
# lister les services qui tournent actuellement
setx -a services=docker service ls --format "{{.Name}}"
fi
local -a psargs
local filter fnum
psargs=(
--format "taskID={{.ID}};node={{.Node}};desiredState='{{.DesiredState}}';currentState='{{.CurrentState}}';__status_process_data"
)
while [[ "$1" == *=* ]]; do
filter="$1"
case "$filter" in
=r) filter="desired-state=running";;
=k) filter="desired-state=shutdown";;
=a) filter="desired-state=accepted";;
esac
psargs+=(--filter "$filter")
shift
done
fnum="$1"; shift
local num=0 taskData taskStatus taskSlot pServiceID serviceID serviceData serviceName serviceUpdateStatus
for service in "${services[@]}"; do
eval "$("$DOCKER" service ps "$service" "${psargs[@]}")"
done
}
function default_compose_show_ip() {
local -a cmd cids; local cid
cmd=(
docker-compose
"${replace_env_args[@]}" "${env_args[@]}"
ps -q "${replace_show_ip_args[@]}" "${show_ip_args[@]}"
"$@"
)
if [ -n "$FAKE" ]; then
echo "docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' \"\$($(qvals "${cmd[@]}"))\""
else
setx -a cids="${cmd[@]}" 2>/dev/null
for cid in "${cids[@]}"; do
"$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}}' \
"${replace_show_ip_args[@]}" "${show_ip_args[@]}" \
"$container_name" "$@"
}
function compose_show_ip() { default_compose_show_ip "$@"; }
function docker_show_ip() { default_docker_show_ip "$@"; }
function auto_show_ip() {
local -a replace_env_args env_args
local -a replace_show_ip_args show_ip_args
local project_name container_name
if [ -f docker-compose.yml ]; then
compose_set_env_args
compose_show_ip "$@"
else
docker_set_env_args
docker_check_name set_container_name
docker_show_ip "$@"
fi
}
function default_compose_systemd_unit() {
local docker_compose="$(which docker-compose 2>/dev/null)"
if [ -z "$docker_compose" ]; then
if [ -x /usr/bin/docker-compose ]; then
docker_compose=/usr/bin/docker-compose
elif [ -x /usr/local/bin/docker-compose ]; then
docker_compose=/usr/local/bin/docker-compose
else
die "Impossible de trouver docker-compose"
fi
fi
setx startcmd=qvals "$docker_compose" \
"${replace_env_args[@]}" "${env_args[@]}" \
up "${replace_run_args[@]}" "${run_args[@]}" \
"${replace_user_args[@]}" "${user_args[@]}" "$@"
setx stopcmd=qvals "$docker_compose" down
}
function default_docker_systemd_unit() {
local docker="$(which "$DOCKER" 2>/dev/null)"
if [ -z "$docker" ]; then
if [ -x /usr/bin/docker ]; then
docker=/usr/bin/docker
elif [ -x /usr/local/bin/docker ]; then
docker=/usr/local/bin/docker
else
die "Impossible de trouver docker"
fi
fi
setx startcmd=qvals "$docker" run \
"${replace_env_args[@]}" "${env_args[@]}" \
"${replace_run_args[@]}" "${run_args[@]}" \
"$NAME" \
"${replace_user_args[@]}" "${user_args[@]}" "$@"
setx stopcmd=qvals "$docker" stop "$container_name"
}
function compose_systemd_unit() { default_compose_systemd_unit "$@"; }
function docker_systemd_unit() { default_docker_systemd_unit "$@"; }
function auto_systemd_unit() {
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 startcmd stopcmd
local tmpscript; ac_set_tmpfile tmpscript
estep "Génération de l'unité systemd"
export COMPOSE_PROJECT_NAME=
if [ -f docker-compose.yml ]; then
compose_set_env_args set_container_name
replace_run_args=(-d --no-color)
compose_systemd_unit "$@"
if [ -z "$HOST" -a -f .env ]; then
source ./.env
if [ -n "$PROFILE" ]; then
HOST="${PROFILE^^}_HOST"; HOST="${!HOST}"
fi
fi
else
docker_set_env_args
docker_check_name set_container_name
replace_run_args=(-d --name "$container_name")
docker_systemd_unit "$@"
fi
[ -n "$COMPOSE_PROJECT_NAME" ] || COMPOSE_PROJECT_NAME="$project_name"
chmod 755 "$tmpscript"
cat >"$tmpscript" <<EOF
#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
cat >/etc/systemd/system/$container_name.service <<EOD
[Unit]
Description=$project_name stack ($PROFILE)
Requires=docker.service
After=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=$(pwd)
Environment=$(qval "COMPOSE_PROJECT_NAME=$COMPOSE_PROJECT_NAME")
ExecStart=$startcmd
ExecStop=$stopcmd
TimeoutStopSec=300
[Install]
WantedBy=multi-user.target
EOD
systemctl daemon-reload
systemctl enable $container_name.service
EOF
estep "Installation de l'unité systemd"
local_run "$HOST" "$tmpscript"
}
function auto_copy() {
local src="$1"; shift
local dest="$1"; shift
if [ -n "$SELECT_CONTAINER" ]; then
if [ "${src#SS:}" != "$src" ]; then
src="$SELECT_CONTAINER${src#SS}"
elif [ "${src#:}" != "$src" ]; then
src="$SELECT_CONTAINER$src"
fi
if [ "${dest#SS:}" != "$dest" ]; then
dest="$SELECT_CONTAINER${dest#SS}"
elif [ "${dest#:}" != "$dest" ]; then
dest="$SELECT_CONTAINER$dest"
fi
fi
"$DOCKER" cp "$src" "$dest" "$@"
}
function default_local_composer() {
# lancement direct
case "$1" in
rootshell|rshell|rootbash|rbash)
shift
# ewarn parce qu'on est pas root dans ce shell contrairement à ce qui est demandé
ewarn "Lancement d'un shell utilisateur alors qu'un shell root est demandé"
bash "$@"
;;
usershell|shell|userbash|bash)
shift
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|rshell|rootbash|rbash)
shift
actualcmd='eval "bash $args"'
;;
usershell|shell|userbash|bash)
shift
actualcmd='eval "su-exec \"$user\" bash $args"'
;;
*)
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_PHP=
local COMPOSER_PHP_MAX=
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_PHP=*) setv "$1"; shift;;
COMPOSER_PHP_MAX=*) setv "$1"; shift;;
COMPOSER_IMAGE=*) setv "$1"; shift;;
COMPOSER_MACHINE=*) setv "$1"; shift;;
COMPOSER_CMD=*) setv "$1"; shift;;
COMPOSER_SETUP=*) setv "$1"; shift;;
*) break;;
esac
done
local use_image
if [ -n "$COMPOSER_PHP_MAX" -a "$COMPOSER_PHP_MAX" != none ]; then
# Vérifier la version de PHP
php -r '
$version = $argv[1];
if (strpos($version, ".") !== false) {
$version = explode(".", $version);
$version = $version[0] * 10000 + $version[1] * 100 + (isset($version[2])? $version[2]: 0);
}
exit((PHP_VERSION_ID > $version)? 0: 1);
' -- "$COMPOSER_PHP_MAX" && use_image=1
fi
if [ -n "$use_image" ]; then
: # ok, on a déjà décidé qu'il faut utiliser une image
elif [ -z "$COMPOSER_PHP" ]; then
# pas de version minimum, tester simplement la valeur de COMPOSER_IMAGE
[ "$COMPOSER_IMAGE" != none ] && use_image=1
elif [ "$COMPOSER_PHP" == force -o "$COMPOSER_PHP" == any ]; then
use_image=1
else
# Vérifier la version de PHP
php -r '
$version = $argv[1];
if (strpos($version, ".") !== false) {
$version = explode(".", $version);
$version = $version[0] * 10000 + $version[1] * 100 + (isset($version[2])? $version[2]: 0);
}
exit((PHP_VERSION_ID < $version)? 0: 1);
' -- "$COMPOSER_PHP" && use_image=1
fi
if [ -n "$use_image" ]; then
[ "$COMPOSER_IMAGE" != none ] || die "Vous devez spécifier l'image à utiliser pour composer"
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 "$@"
else
local_composer "$@"
fi
}
################################################################################
function resolve_dm_alias() {
local alias_dm alias dm
# chercher d'abord dans les définitions par défaut
for alias_dm in "${DM_ALIASES[@]}"; do
alias="${alias_dm%%:*}"
dm="${alias_dm#*:}"
if [ "$1" == "$alias" ]; then
echo "$dm"
return 0
fi
done
# puis chercher dans les répertoires de cluster
local clusterdir DM_ALIASES
for clusterdir in "${CLUSTERDIRS[@]}"; do
DM_ALIASES=()
[ -f "$clusterdir/0config/configure.conf" ] || continue
source "$clusterdir/0config/configure.conf"
for alias_dm in "${DM_ALIASES[@]}"; do
alias="${alias_dm%%:*}"
dm="${alias_dm#*:}"
if [ "$1" == "$alias" ]; then
echo "$dm"
return 0
fi
done
done
# sinon, laisser en l'état
echo "$1"
return 1
}
function resolve_dm_profile() {
local dm_profile dm profile
# chercher d'abord dans les définitions par défaut
for dm_profile in "${DM_PROFILES[@]}"; do
dm="${dm_profile%%:*}"
profile="${dm_profile#*:}"
if [ "$1" == "$dm" ]; then
echo "$profile"
return 0
fi
done
# puis chercher dans les répertoires de cluster
local clusterdir DM_PROFILES
for clusterdir in "${CLUSTERDIRS[@]}"; do
DM_PROFILES=()
[ -f "$clusterdir/0config/configure.conf" ] || continue
source "$clusterdir/0config/configure.conf"
for dm_profile in "${DM_PROFILES[@]}"; do
dm="${dm_profile%%:*}"
profile="${dm_profile#*:}"
if [ "$1" == "$dm" ]; then
echo "$profile"
return 0
fi
done
done
# sinon, afficher le profil par défaut
echo "$DEFAULT_PROFILE"
return 1
}
################################################################################
# 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/apache-php-myiccas-utils:d10
PROFILE=
DM_ALIASES=()
DM_PROFILES=()
CLUSTERDIRS=()
set_defaults dk
export PROFILE
chdir=
CONFIG=
DM_SET_MACHINE=
SELECT_CONTAINER=
SELECT_MACHINE=
USE_STACK=
FAKE=
VARS=()
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=
-s:,--select-service: SELECT_CONTAINER=
-t:,--select-machine: SELECT_MACHINE=
--stack USE_STACK=1
-n,--fake FAKE=1
-e:,--build-arg:,--env: VARS
-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=
)
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"
setx DM_SET_MACHINE=resolve_dm_alias "$DM_SET_MACHINE"
[ "$DM_SET_MACHINE" == - ] && DM_SET_MACHINE=-u
setx dm_env=docker-machine env "$DM_SET_MACHINE" || die
eval "$dm_env"
# pour warning ci-dessous
[ "$DM_SET_MACHINE" == -u ] && DM_SET_MACHINE=
fi
if [ -n "$SELECT_MACHINE" -o -n "$SELECT_CONTAINER" ]; then
function __ss_process_data() {
[ -n "$found" ] && return
__status_query_task
__status_query_service
if [ -n "$slot" ]; then
if [ "$taskSlot" == "$slot" ]; then
# ne prendre que le slot qui correspond
found=1
fi
else
# prendre le premier
found=1
fi
taskName="$serviceName.$taskSlot"
}
if [ -n "$SELECT_MACHINE" ]; then service="$SELECT_MACHINE"
elif [ -n "$SELECT_CONTAINER" ]; then service="$SELECT_CONTAINER"
fi
if [ -f docker-compose.yml -o -f docker-stack.yml ]; then
# si on est dans répertoire de projet, il est possible de spécifier
# le service sans préfixe
docker_set_deploy_args set_container_name
service="${container_name}_${service#${container_name}_}"
fi
if [[ "$service" == *.* ]]; then
slot="${service##*.}"
service="${service%.*}"
else
slot=
fi
psargs=(
--filter desired-state=running
--format "taskID={{.ID}};node={{.Node}};__ss_process_data"
)
found=
eval "$("$DOCKER" service ps "$service" "${psargs[@]}" 2>/dev/null)"
if [ -n "$found" ]; then
SELECT_CONTAINER="$serviceName.$taskSlot.$taskID"
if [ -n "$SELECT_MACHINE" ]; then
DM_SET_MACHINE="$node"
else
enote "Sélection du container $SELECT_CONTAINER"
fi
else
die "$service${slot:+.$slot}: service introuvable"
fi
fi
if [ -n "$SELECT_MACHINE" ]; then
enote "Sélection de la machine $DM_SET_MACHINE"
[ -n "$SELECT_CONTAINER" ] && enote "Sélection du container $SELECT_CONTAINER"
[ -n "$DM_AVAILABLE" ] || die "docker-machine n'est pas disponible"
setx dm_env=docker-machine env "$DM_SET_MACHINE" || die
eval "$dm_env"
fi
if [ -n "$DM_AVAILABLE" ]; then
setx DEFAULT_PROFILE=resolve_dm_profile "$DOCKER_MACHINE_NAME" && found=1 || found=
if [ -n "$DM_SET_MACHINE" -a -z "$PROFILE" -a -z "$found" ]; then
ewarn "\
Aucun profil n'a été défini pour $DM_SET_MACHINE dans ~/etc/default/dk
== Le profil $DEFAULT_PROFILE sera utilisé =="
sleep 3
fi
fi
# construire par défaut
[ $# -eq 0 ] && set -- build
[ -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"
[ -f .build.scripts.sh ] && source ./.build.scripts.sh
[ -f build.scripts.sh ] && source ./build.scripts.sh
args=()
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
enote "Profil $PROFILE"
auto_build "${args[@]}" || die
;;
p|push)
[ -f .build.scripts.sh ] && source ./.build.scripts.sh
[ -f build.scripts.sh ] && source ./build.scripts.sh
args=()
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
enote "Profil $PROFILE"
auto_push "${args[@]}" || die
;;
s|start)
args=()
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
enote "Profil $PROFILE"
auto_up "${args[@]}" || die
;;
k|stop)
args=()
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
enote "Profil $PROFILE"
auto_stop "${args[@]}" || die
;;
1|up)
args=()
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
enote "Profil $PROFILE"
auto_up "${args[@]}" && auto_logs || die
;;
l|logs)
args=()
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
enote "Profil $PROFILE"
auto_logs "${args[@]}" || die
;;
0|down)
args=()
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
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() {
[ -n "$do_auto_down" ] && auto_down
}
trap auto_down_trap 1 3 15 EXIT
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=()
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
enote "Profil $PROFILE"
__parse_bargs "${args[@]}"
if auto_build "${bargs[@]}"; then
auto_up "${args[@]}" && auto_logs || die
else
do_auto_down=
fi
;;
bs)
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"
__parse_bargs "${args[@]}"
auto_build "${bargs[@]}" && auto_up "${args[@]}" || die
;;
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"
__parse_bargs "${args[@]}"
auto_build "${bargs[@]}" && auto_run "${args[@]}" || die
;;
y|deploy)
USE_STACK=1
args=()
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
enote "Profil $PROFILE"
auto_deploy "${args[@]}" || die
;;
by|bd)
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"
__parse_bargs "${args[@]}"
auto_build "${bargs[@]}" && auto_deploy "${args[@]}" || die
;;
bp)
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=()
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
enote "Profil $PROFILE"
__parse_bargs "${args[@]}"
auto_build "${bargs[@]}" && auto_push "${args[@]}" || die
;;
bpy|bpd)
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=()
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
enote "Profil $PROFILE"
__parse_bargs "${args[@]}"
auto_build "${bargs[@]}" && auto_push && auto_deploy "${args[@]}" || die
;;
ser|service)
args=()
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
enote "Profil $PROFILE"
auto_service "${args[@]}" || die
;;
sta|status)
args=()
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
enote "Profil $PROFILE"
auto_status "${args[@]}" || die
;;
u|update)
args=()
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
enote "Profil $PROFILE"
auto_service update "${args[@]}" || die
;;
scale)
args=()
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
enote "Profil $PROFILE"
auto_service scale "${args[@]}" || die
;;
ip|show-ip)
args=()
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
enote "Profil $PROFILE"
auto_show_ip "${args[@]}" || die
;;
systemd-unit|systemd)
args=()
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
enote "Profil $PROFILE"
auto_systemd_unit "${args[@]}" || die
;;
cp|copy)
args=()
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
enote "Profil $PROFILE"
auto_copy "${args[@]}" || 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 !~ /</ { print }')
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) && $2 !~ /</ { print }
'
if [ $# -eq 0 -o "$1" == -- ]; then
# pas de filtre
ewarn "pull: Vous devez spécifier l'image à mettre à jour"
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 $1 ":" $2 }' <<<"$all"
if [ ${#images[*]} -gt 0 ]; then
echo "$all"
ask_yesno "Etes-vous sûr de vouloir mettre à jour ces images?" O || die
for image in "${images[@]}"; do
"$DOCKER" pull "$image" || die
done
fi
;;
rm)
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 -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 } { if ($1 == "<none>" || $2 == "<none>") { print $3 } else { print $1 ":" $2 } }' <<<"$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|cr|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" == cr ]; then args+=(rshell)
elif [ "$cmd" == cs ]; then args+=(shell)
fi
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
auto_composer "${args[@]}"
;;
_*|*_)
# transmettre directement à docker
cmd="${cmd#_}"
cmd="${cmd%_}"
args=()
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
"$DOCKER" "$cmd" "${args[@]}"
;;
*)
# transmettre directement à docker
args=()
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
"$DOCKER" "$cmd" "${args[@]}"
esac
done