diff --git a/support/Dockerfile.rundk b/support/Dockerfile.rundk new file mode 100644 index 0000000..a3502af --- /dev/null +++ b/support/Dockerfile.rundk @@ -0,0 +1,26 @@ +# -*- coding: utf-8 mode: dockerfile -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 +ARG REGISTRY=pubdocker.univ-reunion.fr +FROM $REGISTRY/src/base as base +FROM $REGISTRY/src/php as php + +################################################################################ +FROM debian:11-slim as builder +COPY --from=base /g/ /g/ +COPY --from=base /src/ /src/ + +RUN /g/build core lite _builder +RUN make -C /src/su-exec + +################################################################################ +FROM debian:11-slim + +COPY --from=builder /src/su-exec/su-exec /g/ +COPY --from=base /g/ /g/ +ARG APT_MIRROR SEC_MIRROR APT_PROXY TIMEZONE +ENV APT_MIRROR=$APT_MIRROR SEC_MIRROR=$SEC_MIRROR APT_PROXY=$APT_PROXY TIMEZONE=$TIMEZONE +RUN /g/build + +COPY --from=php /g/ /g/ +RUN /g/build @php-cli php-utils + +ENTRYPOINT ["/g/entrypoint"] diff --git a/support/dot-build.env.dist b/support/dot-build.env.dist new file mode 100644 index 0000000..b08a53b --- /dev/null +++ b/support/dot-build.env.dist @@ -0,0 +1,18 @@ +# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 +# Exemple de fichier build.env dans le répertoire du projet + +# Source des paquets et proxy +APT_PROXY= +APT_MIRROR=default +SEC_MIRROR=default + +# Timezone du serveur +TIMEZONE=Europe/Paris + +# registre docker privé d'après lequel sont nommées les images +PRIVAREG= + +################################################################################ +# Ne pas toucher à partir d'ici + +REGISTRY=pubdocker.univ-reunion.fr diff --git a/support/dot-dkbuild.env.dist b/support/dot-dkbuild.env.dist new file mode 100644 index 0000000..0536ea4 --- /dev/null +++ b/support/dot-dkbuild.env.dist @@ -0,0 +1,30 @@ +# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 +# Exemple de fichier ~/.dkbuild.env + +default_profile "${DKBUILD_PROFILE:-prod}" + +# Source des paquets et proxy +setenv APT_PROXY= +setenv APT_MIRROR=default +setenv SEC_MIRROR=default + +# Timezone du serveur +setenv TIMEZONE=Europe/Paris + +if profile prod test; then + setenv REGISTRY=pubdocker.univ-reunion.fr + setenv PRIVAREG=pridocker.univ-reunion.fr + host_mappings=( + pridocker.univ-reunion.fr:10.85.1.56 + pubdocker.univ-reunion.fr:10.85.1.57 + repos.univ-reunion.fr:10.85.1.57 + git.univ-reunion.fr:10.85.1.55 + ) + default docker host-mappings="${host_mappings[*]}" +elif profile devel; then + setenv REGISTRY=docker.devel.self + setenv PRIVAREG=docker.devel.self +else + setenv REGISTRY=pubdocker.univ-reunion.fr + setenv PRIVAREG= +fi diff --git a/support/rundk b/support/rundk new file mode 100755 index 0000000..235edd7 --- /dev/null +++ b/support/rundk @@ -0,0 +1,424 @@ +#!/bin/bash +# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 +# Script permettant de lancer une commande dans docker et/ou de bootstrapper +# l'utilisation de nulib dans un projet +# Les fichiers suivants doivent être copiés à un endroit quelconque du projet: +# - rundk (ce script) +# - Dockerfile.rundk +# Les fichiers suivants peuvent être intégrés dans le projet comme exemples: +# - dot-build.env.dist +# - dot-dkbuild.env.dist +# Par défaut, ce script assume que bootstrap est copié dans le répertoire sbin/ +# du projet, et que le fichier composer.json et le répertoire vendor/ sont à la +# racine du projet. Le cas échéant, modifier les valeurs ci-dessous +MYDIR="$(cd "$(dirname -- "$0")"; pwd)" +MYNAME="$(basename -- "$0")" +MYSELF="$MYDIR/$MYNAME" + +################################################################################ +# Modifier les valeurs suivantes si nécessaire + +# répertoire du projet. ce chemin doit être absolu +PROJDIR= + +# composer: répertoire du projet composer (celui qui contient le fichier +# composer.json), chemin de composer.phar et répertoire vendor. ces chemions +# doivent être relatifs à $PROJDIR +COMPOSERDIR= +COMPOSERPHAR= +VENDORDIR= + +# fichier de configuration pour le build +BUILDENV= + +################################################################################ +# Ne pas modifier à partir d'ici + +[ -n "$PROJDIR" ] || PROJDIR="$(dirname -- "$MYDIR")" +[ -n "$COMPOSERDIR" ] || COMPOSERDIR=. +[ -n "$COMPOSERPHAR" ] || COMPOSERPHAR=sbin/composer.phar +[ -n "$VENDORDIR" ] || VENDORDIR=vendor +[ -n "$BUILDENV" ] || BUILDENV=build.env + +[ "$COMPOSERPHAR" == none ] && COMPOSERPHAR= +[ "$BUILDENV" == none ] && BUILDENV= + +function eecho() { echo "$*" 1>&2; } +function eerror() { eecho "ERROR: $*"; } +function die() { [ $# -gt 0 ] && eerror "$*"; exit 1; } +function in_path() { [ -n "$1" -a -x "$(which "$1" 2>/dev/null)" ]; } +function composer() { + cd "$PROJDIR/$COMPOSERDIR" || exit 1 + if [ -x "$PROJDIR/$COMPOSERPHAR" ]; then + "$PROJDIR/$COMPOSERPHAR" "$@" + elif in_path composer; then + command composer "$@" + elif [ -x /usr/bin/composer ]; then + /usr/bin/composer "$@" + elif [ -x /usr/local/bin/composer ]; then + /usr/local/bin/composer "$@" + else + die "impossible de trouver composer" + fi + if [ -f composer.lock ]; then + cp composer.lock "$PROJDIR/.composer.lock.rundk" + fi +} + +bootstrap= +BootstrapOnly=1 +ForcedBootstrap= +args=() +for arg in "$@"; do + if [ "$arg" == --bootstrap ]; then + bootstrap=1 + elif [ "$arg" == --exec ]; then + BootstrapOnly= + else + args+=("$arg") + fi +done +set -- "${args[@]}" + +if [ -z "$bootstrap" ]; then + # si vendor/ n'existe pas, alors on doit faire bootstrap + if [ ! -f "$PROJDIR/$VENDORDIR/nulib/php/load.sh" ]; then + ForcedBootstrap=1 + elif [ ! -f "$PROJDIR/.composer.lock.rundk" ]; then + ForcedBootstrap=1 + elif ! diff -q "$PROJDIR/$COMPOSERDIR/composer.lock" "$PROJDIR/.composer.lock.rundk" >&/dev/null; then + ForcedBootstrap=1 + elif [ -n "$bootstrap" -a -n "$BootstrapOnly" ]; then + # bootstrap inutile + exit 0 + fi + + if [ -n "$ForcedBootstrap" ]; then + [ -z "$_RUNDK_IN_DOCKER" ] && eecho "== rundk bootstrap is needed" + bootstrap=1 + BootstrapOnly= + fi +fi + +if [ -z "$_RUNDK_IN_DOCKER" ]; then + ############################################################################ + # Lancement depuis l'extérieur du container + + ## Charger ~/.dkbuild.env + + APT_PROXY="$RUNDK_APT_PROXY" + APT_MIRROR="$RUNDK_APT_MIRROR" + SEC_MIRROR="$RUNDK_SEC_MIRROR" + TIMEZONE="$RUNDK_TIMEZONE" + PRIVAREG="$RUNDK_PRIVAREG" + REGISTRY="$RUNDK_REGISTRY" + IMAGE="$RUNDK_IMAGE" + PROFILE= + HOST_MAPPINGS=() + function default_profile() { + PROFILE="$1" + } + function profile() { + local profile + for profile in "$@"; do + [ "$profile" == "$PROFILE" ] && return 0 + done + return 1 + } + function setenv() { + eval "export $1" + } + function default() { + local command="$1"; shift + local nv n v + case "$command" in + docker) + for nv in "$@"; do + [[ "$nv" == *=* ]] || continue + n="${nv%%=*}" + v="${nv#*=}" + case "$n" in + host-mappings) + read -a ns <<<"$v" + for v in "${ns[@]}"; do + HOST_MAPPINGS+=("$v") + done + ;; + esac + done + ;; + esac + } + [ -f ~/.dkbuild.env ] && source ~/.dkbuild.env + + [ -n "$APT_PROXY" ] || APT_PROXY= + [ -n "$APT_MIRROR" ] || APT_MIRROR=default + [ -n "$SEC_MIRROR" ] || SEC_MIRROR=default + [ -n "$TIMEZONE" ] || TIMEZONE=Europe/Paris + [ -n "$PRIVAREG" ] || PRIVAREG= + [ -n "$REGISTRY" ] || REGISTRY=pubdocker.univ-reunion.fr + + ## Construire l'image + + if [ -n "$RUNDK_NO_USE_RSLAVE" ]; then + UseRslave= + elif [ -n "$RUNDK_USE_RSLAVE" ]; then + UseRslave=1 + elif [ -e /proc/sys/fs/binfmt_misc/WSLInterop ]; then + # pas de mount propagation sous WSL + UseRslave= + else + UseRslave=1 + fi + IMAGENAME=nulib/rundk + + if [ -n "$bootstrap" ]; then + BUILD_ARGS=( + REGISTRY + APT_PROXY + APT_MIRROR + SEC_MIRROR + TIMEZONE + ) + + SOPTS=+c:UjDx:z:r:pw:v + LOPTS=help,config:,unless-exists,pull,no-cache,plain-output,apt-proxy:,timezone:,privareg:,push,chdir:,verbose,no-use-rslave + args="$(getopt -n "$MYNAME" -o "$SOPTS" -l "$LOPTS" -- "$@")" || exit 1; eval "set -- $args" + + [ -f "$PROJDIR/$BUILDENV" ] && Config="$PROJDIR/$BUILDENV" || Config= + Build= + UnlessExists= + Pull= + NoCache= + PlainOutput= + Chdir= + Verbose= + while [ $# -gt 0 ]; do + case "$1" in + --) shift; break;; + --help) + eecho "\ +rundk: construire l'image docker + +USAGE + $MYNAME --bootstrap [options...] [--exec command [args...]] + +OPTIONS + -c, --config build.env + --unless-exists + -U, --pull + -j, --no-cache + -D, --plain-output + -x, --apt-proxy APT_PROXY + -z, --timezone TIMEZONE + -r, --privareg PRIVAREG + -p, --push + paramètres pour la consruction de l'image + -w, --chdir CHDIR + aller dans le répertoire spécifié avant de lancer la commande + -v, --verbose + afficher plus d'informations" + exit 0 + ;; + -c|--config) shift; Config="$1";; + -0|--build) Build=1;; + --unless-exists) UnlessExists=1;; + -U|--pull) Pull=1;; + -j|--no-cache) NoCache=1;; + -D|--plain-output) PlainOutput=1;; + -x|--apt-proxy) shift; APT_PROXY="$1";; + -z|--timezone) shift; TIMEZONE="$1";; + -r|--privareg) shift; PRIVAREG="$1";; + -p|--push) Push=1;; + -w|--chdir) shift; Chdir="$1";; + -v|--verbose) Verbose=1;; + --no-use-rslave) UseRslave=;; + *) die "$1: option non configurée";; + esac + shift + done + + [ "$Config" == none ] && Config= + if [ -n "$Config" ]; then + source "$Config" || exit 1 + fi + if [ -z "$IMAGE" ]; then + [ -n "$PRIVAREG" ] && IMAGE="$PRIVAREG/$IMAGENAME" || IMAGE="$REGISTRY/$IMAGENAME" + fi + if [ -z "$UnlessExists" -o -z "$(docker image ls --no-trunc --format '{{.Repository}}:{{.Tag}}' "$IMAGE" 2>/dev/null)" ]; then + eecho "== Building $IMAGE" + args=( + -f "$MYDIR/Dockerfile.rundk" + ${Pull:+--pull} + ${NoCache:+--no-cache} + ${BuildPlain:+--progress plain} + -t "$IMAGE" + ) + for arg in "${BUILD_ARGS[@]}"; do + args+=(--build-arg "$arg=${!arg}") + done + mkdir -p /tmp/rundk-build + docker build "${args[@]}" /tmp/rundk-build || exit 1 + + if [ -n "$Push" -a -n "$PRIVAREG" ]; then + eecho "== Pushing $IMAGE" + docker push "$IMAGE" || exit 1 + fi + fi + [ -n "$BootstrapOnly" ] && exit 0 + + else + SOPTS=+w:v + LOPTS=help,chdir:,verbose,no-use-rslave + args="$(getopt -n "$MYNAME" -o "$SOPTS" -l "$LOPTS" -- "$@")" || exit 1; eval "set -- $args" + + Chdir= + Verbose= + while [ $# -gt 0 ]; do + case "$1" in + --) shift; break;; + --help) + eecho "\ +rundk: construire l'image docker + +USAGE + $MYNAME ci|cu|composer + $MYNAME --exec [options...] command [args...] + +COMMANDES COMPOSER + ci + cu + installer/mettre à jour les dépendances du projet avec composer + composer (args...] + lancer composer avec les arguments spécifiés. + +pour les commandes ci-dessus, l'option --chdir est ignorée: le répertoire +courant est forcé au répertoire du projet composer + +OPTIONS + -w, --chdir CHDIR + aller dans le répertoire spécifié avant de lancer la commande + -v, --verbose + afficher plus d'informations" + exit 0 + ;; + -w|--chdir) shift; Chdir="$1";; + -v|--verbose) Verbose=1;; + --no-use-rslave) UseRslave=;; + *) die "$1: option non configurée";; + esac + shift + done + + if [ -z "$IMAGE" ]; then + [ -n "$PRIVAREG" ] && IMAGE="$PRIVAREG/$IMAGENAME" || IMAGE="$REGISTRY/$IMAGENAME" + fi + fi + + ## Lancer la commande + + uid="$(id -u)" + gid="$(id -g)" + args=( + run -it --rm + --name rundk + -e _RUNDK_IN_DOCKER=1 + -e _RUNDK_UID="$uid" + -e _RUNDK_GID="$gid" + ) + for host in "${HOST_MAPPINGS[@]}"; do + args+=(--add-host "$host") + done + + # monter le répertoire qui contient $PROJDIR + mount_composer= + if [ "${PROJDIR#$HOME/}" != "$PROJDIR" -o "$PROJDIR" == "$HOME" ]; then + # bind mount $HOME + args+=(-v "$HOME:$HOME${UseRslave:+:rslave}") + else + # bind mount uniquement le répertoire du projet + args+=(-v "$PROJDIR:$PROJDIR${UseRslave:+:rslave}") + mount_composer=1 + fi + if [ -n "$mount_composer" -a -d "$HOME/.composer" ]; then + # monter la configuration de composer + args+=(-v "$HOME/.composer:$HOME/.composer") + fi + args+=(-w "$(pwd)") + + # lancer avec l'utilisateur courant + if [ $uid -ne 0 ]; then + # si c'est un utilisateur lambda, il faut monter les informations + # nécessaires. composer est déjà monté via $HOME + user="$(id -un)" + userent="$(getent passwd "$user")" + group="$(id -gn)" + groupent="$(getent group "$group")" + args+=( + -e _RUNDK_USER="$user" + -e _RUNDK_USERENT="$userent" + -e _RUNDK_GROUPENT="$groupent" + ) + fi + + args+=( + "$IMAGE" + exec "$MYSELF" ${Chdir:+-w "$Chdir"} + ) + [ -n "$ForcedBootstrap" ] && set -- ci + [ -n "$Verbose" ] && eecho "\$ docker ${args[*]} $*" + exec docker "${args[@]}" "$@" + +else + ############################################################################ + # Lancement depuis l'intérieur du container + + # Ajouter les informations utilisateur le cas échéant + if [ -n "$_RUNDK_USERENT" ]; then + grep -q "^$_RUNDK_USER:" /etc/passwd || echo "$_RUNDK_USERENT" >>/etc/passwd + fi + if [ -n "$_RUNDK_GROUPENT" ]; then + grep -q "^$_RUNDK_GROUP:" /etc/group || echo "$_RUNDK_GROUPENT" >>/etc/group + fi + if [ -n "$_RUNDK_USER" ]; then + user="$_RUNDK_USER" + export _RUNDK_USER= + export _RUNDK_USERENT= + export _RUNDK_GROUPENT= + if in_path su-exec; then + exec su-exec "$user" "$0" "$@" + else + exec runuser -u "$user" -- "$0" "$@" + fi + fi + + SOPTS=+w: + LOPTS=chdir: + args="$(getopt -n "$MYNAME" -o "$SOPTS" -l "$LOPTS" -- "$@")" || exit 1; eval "set -- $args" + + chdir= + action= + while [ $# -gt 0 ]; do + case "$1" in + --) shift; break;; + -w|--chdir) shift; chdir="$1";; + *) die "$1: option non configurée";; + esac + shift + done + + if [ "$1" == ci ]; then + eecho "== installing composer dependencies" + composer i + elif [ "$1" == cu ]; then + eecho "== upgrading composer dependencies" + composer u + elif [ "$1" == composer ]; then + "$@" + else + if [ -n "$chdir" ]; then + cd "$chdir" || exit 1 + fi + command "${@:-bash}" + fi +fi