nutools/dk

664 lines
20 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
function display_help() {
uecho "$scriptname: outil pour faciliter l'utilisation de docker
USAGE
$scriptname CMDs...
COMMANDES
build
Construire les images
push
Pousser les images vers le serveur
start, run [SERVICE]
Démarrer le(s) service(s)
stop [SERVICE]
Arrêter le(s) service(s)
up
Créer l'environnement, démarrer les services et suivre les logs de façon
interactive.
logs [SERVICE]
Afficher les logs
down
Arrêter les services et supprimer l'environnement
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
arrête l'affichage des logs avec Ctrl+C, arrêter les services et
supprimer l'environnement (comme avec down)
bs
Construire les images (comme avec build) puis démarrer les services
(comme avec start)
exec SERVICE COMMAND
Lancer la commande dans le container spécifié
service
Générer une unité systemd qui démarre les services
ps
Afficher les containers en cours d'exécution
ls
Lister les images actuellement présentes
rm
Supprimer une image
prune
Supprimer les containers et les images inutilisées
OPTIONS
-d, --chdir PROJDIR
-p, --profile PROFILE
-P, --prod
-T, --test
-n, --fake
-j, --no-cache
-h, --host HOST"
}
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/([^=]+)=(.*)/replace_build_args+=(--build-arg \1="\2"); \1="\2"/'
}
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")"
}
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"
}
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_env_args() {
replace_env_args+=(-f docker-compose.yml)
if [ -f docker-compose.override.yml ]; then
replace_env_args+=(-f docker-compose.override.yml)
fi
local PROJECT_NAME=--none--
[ -f .compose.env ] && source ./.compose.env
if [ -n "$PROFILE" ]; then
if [ -f "docker-compose.$PROFILE.yml" ]; then
replace_env_args+=(-f "docker-compose.$PROFILE.yml")
fi
if [ "$PROJECT_NAME" != --none-- ]; then
if [ -z "$COMPOSE_PROJECT_NAME" ]; then
[ -n "$PROJECT_NAME" ] || PROJECT_NAME="$(basename -- "$(pwd)")"
COMPOSE_PROJECT_NAME="${PROJECT_NAME}_${PROFILE}"
fi
export COMPOSE_PROJECT_NAME
fi
fi
if [ "$1" == set_container_name ]; then
if [ "$PROJECT_NAME" == --none-- ]; then
project_name="$(basename -- "$(pwd)")"
else
project_name="$PROJECT_NAME"
fi
container_name="${project_name//[^a-zA-Z0-9_-]/}"
[ -n "$PROFILE" ] && container_name="${container_name}_$PROFILE"
fi
}
function host_run() {
# lancer le script $2..@ sur l'hôte $1
# si $1 n'est pas défini ou est le nom l'hôte local, 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" ]; then
if check_hostname "$host"; then
estep "Lancement de $script localement"
runscript_as_root "$script" "$@"
elif progexists docker-machine; then
local dm; setx dm=docker-machine active 2>/dev/null
if [ "${host%%.*}" == "$dm" ]; 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 ($dm) 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" "$@"
}
function initialize_build_env() {
CTXDIR=.
NAME=
TAGS=(latest)
VERSION=
}
function default_update_build_env() {
[ -n "$VERSION" ] || docker_add_build_arg VERSION "$(get_version)"
[ -n "$VERSION" ] && TAGS+=("$VERSION")
}
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() { default_compose_build "$@"; }
function docker_build() { default_docker_build "$@"; }
function auto_build() {
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_build
else
docker_parse_env_args
docker_check_name
docker_add_build_arg build_date "$(date +%y%m%d)"
update_build_env
docker_build
fi
}
function auto_push() {
local -a replace_env_args env_args
local -a replace_build_args build_args
local tag
initialize_build_env
if [ -f docker-compose.yml ]; then
compose_set_env_args
update_build_env
else
docker_parse_env_args
docker_check_name
update_build_env
fi
for tag in "${TAGS[@]}"; do
${FAKE:+qvals} docker push "$NAME:$tag"
done
}
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 "$@"
else
docker_set_env_args
docker_check_name set_container_name
replace_run_args=(-d --name "$container_name")
docker_up "$@"
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 "$@"
else
docker_set_env_args
docker_check_name set_container_name
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 "$@"
else
docker_set_env_args
docker_check_name set_container_name
replace_logs_args=(-f)
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 "$@"
else
docker_set_env_args
docker_check_name set_container_name
docker_down "$@"
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_service() {
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_service() {
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_service() { default_compose_service "$@"; }
function docker_service() { default_docker_service "$@"; }
function auto_service() {
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 du service"
export COMPOSE_PROJECT_NAME=
if [ -f docker-compose.yml ]; then
compose_set_env_args set_container_name
replace_run_args=(-d --no-color)
compose_service "$@"
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_service "$@"
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 du service"
local_run "$HOST" "$tmpscript"
}
DEFAULT_PROFILE=devel
PROFILE=
DM_PROFILES=()
set_defaults dk
export PROFILE
if progexists docker-machine; then
setx active_dm=docker-machine active 2>/dev/null
for dm_profile in "${DM_PROFILES[@]}"; do
splitpair "$dm_profile" dm profile
if [ "$dm" == "$active_dm" ]; then
DEFAULT_PROFILE="$profile"
break
fi
done
fi
chdir=
FAKE=
NO_CACHE=
HOST=
args=(
--help '$exit_with display_help'
-d:,--chdir: chdir=
-p:,--profile: PROFILE=
-P,--prod PROFILE=prod
-T,--test PROFILE=test
-n,--fake FAKE=1
-j,--no-cache NO_CACHE=1
-h:,--host: HOST=
)
parse_args "$@"; set -- "${args[@]}"
# construire par défaut
[ $# -eq 0 ] && set -- build
[ -n "$PROFILE" ] || PROFILE="$DEFAULT_PROFILE"
[ -n "$chdir" ] && { cd "$chdir" || die; }
while [ $# -gt 0 ]; do
[ "$1" == -- ] && { shift; continue; }
cmd="$1"; shift
case "$cmd" in
b|build)
[ -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
;;
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|run|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
;;
d|brd)
do_auto_down=1
function auto_down_trap() {
[ -n "$do_auto_down" ] && auto_down
}
trap auto_down_trap 1 3 15 EXIT
[ -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"
if auto_build; then
auto_up "${args[@]}" && auto_logs || die
else
do_auto_down=
fi
;;
bs)
[ -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 && auto_up "${args[@]}"
;;
x|exec)
args=()
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
enote "Profil $PROFILE"
auto_exec "${args[@]}" || die
;;
service)
args=()
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
enote "Profil $PROFILE"
auto_service "${args[@]}" || die
;;
ps) docker container ps -a || die;;
ls) docker image ls || die;;
rm)
args=()
while [ $# -gt 0 -a "$1" != -- ]; do
args+=("$1"); shift
done
docker image rm "${args[@]}" || die
;;
X|prune)
docker container prune -f || die
docker image prune -f || die
;;
*) die "$cmd: commande inconnue";;
esac
done