#!/bin/bash
set -e
CAPP_VER=to_replace
COMPOSE_SYSTEMD_VER=v1.6.1
def_min_dca_ver=2
def_cont_mem='10G'
def_cont_proc=1000
me=$(readlink -f "$0")
offset=$(($(sed -rn '/#{10}/=' "$me") + 1))
usage() {
    echo "$0 default_hostname default_email [min_dca_ver=$def_min_dca_ver] [container_max_memory_size=$def_cont_mem] [container_max_process=$def_cont_proc] [nohttps]" >&2
    echo "  container_max_memory_size should be expressed with M or G units" >&2
    echo "-v|--version    show the version" >&2
    echo "-h|--help       show this help message" >&2
    echo "-e|--extract    extract the inner archive to src.tar.xz"
    # shellcheck disable=SC2086
    exit ${1:-1}
}
if [ "$1" = "-v" ] || [ "$1" = "--version" ]; then
    echo "$CAPP_VER"
    exit 0
fi
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
    usage 0
fi
if [ "$1" = "-e" ] || [ "$1" = "--extract" ]; then
    tail -n+$offset "$me" | base64 -d > src.tar.xz
    exit 0
fi
# shellcheck disable=SC2046
if [ $(id -u) -ne 0 ]; then
    echo "Root required" >&2
    exit 1
fi
for bin in useradd usermod base64 curl docker sudo tar unxz; do
    if ! which $bin >/dev/null 2>&1; then
        echo "$bin required" >&2
        exit 1
    fi
done
docker="$(which docker)"
if "$docker" compose version | grep -q 'Docker Compose'; then
  dc="$docker compose"
elif which docker-compose >/dev/null 2>&1; then  # legacy
  dc="$(which docker-compose)"
else
  echo "docker with compose required" >&2
  exit 1
fi
dh="$1"
# shellcheck disable=SC2015
[ -n "$dh" ] && shift || usage
de="$1"
# shellcheck disable=SC2015
[ -n "$dh" ] && shift || usage
min_dca_ver=
cont_mem=
cont_proc=
nohttps=0
while [ -n "$1" ]; do
    p="$1"
    shift
    if [ -z "$min_dca_ver" ]; then
        echo "$p" | grep -E -q '^[0-9](\.[0-9]+)?$' || usage
        min_dca_ver="$p"
    elif [ -z "$cont_mem" ]; then
        echo "$p" | grep -E -q '^[0-9]+[MG]$' || usage
        cont_mem="$p"
    elif [ -z "$cont_proc" ]; then
        echo "$p" | grep -E -q '^[0-9]+$' || usage
        cont_proc="$p"
    elif [ "$p" = "nohttps" ]; then
        nohttps=1
    else
        usage
    fi
done
[ -n "$min_dca_ver" ] || min_dca_ver=$def_min_dca_ver
[ -n "$cont_mem" ] || cont_mem=$def_cont_mem
[ -n "$cont_proc" ] || cont_proc=$def_cont_proc
cat > /etc/capp.conf <<EOF
min_dca_version=${min_dca_ver}
max_mem_size=${cont_mem}
max_proc=${cont_proc}
default_hostname=${dh}
EOF
mkdir -p /etc/capp/users /etc/capp/pubkeys /etc/capp/rights /etc/capp/hooks.d/{pre_deploy,post_deploy,pre_undeploy,post_undeploy}
tmpdir=$(mktemp -d)
cd "$tmpdir"
curl -Ls https://github.com/jrd/compose-systemd/archive/${COMPOSE_SYSTEMD_VER}.tar.gz | tar xzf -
tail -n+$offset "$me" | base64 -d | tar xJf -
cp compose-systemd-*/compose-dirs.conf /etc/
cp compose-systemd-*/compose-dirs /usr/local/bin/
cat > /usr/local/bin/dc <<EOF
#!/bin/sh
exec $dc "\$@"
EOF
chmod +x /usr/local/bin/dc
mkdir -p /var/docker-volumes/nginx-proxy/{vhost.d,certs} /etc/compose /home/deploy/dca
id -u compose >/dev/null 2>&1 || useradd --home-dir /etc/compose --no-create-home --user-group --shell /usr/sbin/nologin -c "Compose" -l compose
chown :compose /var/docker-volumes/nginx-proxy/vhost.d /etc/capp/users /etc/capp/pubkeys /etc/capp/rights
chmod g+w,o=rX /var/docker-volumes/nginx-proxy/vhost.d /etc/capp/users /etc/capp/pubkeys /etc/capp/rights
cp -r proxy dca /etc/compose/
if [ "$nohttps" -eq 1 ]; then
    # comment the letsencrypt service
    sed -ri '/le:/,/certs:rw/s/^/#/' /etc/compose/proxy/docker-compose.yml
    # do not bind the 443 port on the host
    sed -ri '/:443/d' /etc/compose/proxy/docker-compose.yml
    # do not redirect http access to https
    sed -ri '/Env\.HTTPS_METHOD/s/redirect/nohttps/' /etc/compose/proxy/gen/nginx.tmpl
fi
sed -ri "s/dca:1000:1000/dca:$(id -u compose):$(id -g compose)/" /etc/compose/dca/docker-compose.yml
mkdir -p /usr/local/bin
cp capp verify_dca.py get_deploy_keys /usr/local/bin/
chown :compose /usr/local/bin/capp
DOCKER_GEN_VERSION=$(/etc/compose/proxy/gen/docker-gen --version)
cat > /etc/compose/proxy/.env <<EOF
DEFAULT_HOST=$dh
DEFAULT_EMAIL=$de
DOCKER_GEN_VERSION=${DOCKER_GEN_VERSION}
EOF
if [ -f /etc/compose/compose.deps ]; then
    for svc in proxy dca; do
        grep -q "^$svc:" /etc/compose/compose.deps || echo "$svc:" >> /etc/compose/compose.deps
    done
else
    cat > /etc/compose/compose.deps <<EOF
proxy:
dca:
EOF
fi
chown -R compose: ~compose
id -u deploy >/dev/null 2>&1 || useradd --no-create-home --user-group --password '*' -c "Deploy" deploy
cat > ~deploy/capp <<'EOF'
#!/usr/bin/env python3
from os import environ, execvpe
from shlex import split
from sys import exit

args = ['sudo', '-u', 'compose', '/usr/local/bin/capp'] + split(environ.pop('SSH_ORIGINAL_COMMAND', ''))
if 'SHELL' not in environ:
    try:
        environ['SHELL'] = [l.strip() for l in open('/etc/passwd').readlines() if l.startswith(environ['USER'] + ':')][0].split(':')[6]
    except Exception:
        pass
execvpe('sudo', args, environ)
exit(1)
EOF
chmod u=rx,g=rx,o= ~deploy/capp
chmod g+w ~deploy/dca
chown -R deploy: ~deploy
chown -R :compose ~deploy/dca
grep -q '^PermitUserEnvironment yes' /etc/ssh/sshd_config || sed -ri '/^#PermitUserEnvironment /a PermitUserEnvironment yes' /etc/ssh/sshd_config
grep -q '^Match User deploy' /etc/ssh/sshd_config && ! grep -q '^ *AuthorizedKeysCommand' /etc/ssh/sshd_config && sed -ri '/^Match User deploy/,/^.*AuthorizedKeys.*/d' /etc/ssh/sshd_config
grep -q '^Match User deploy' /etc/ssh/sshd_config || cat >> /etc/ssh/sshd_config <<EOF
Match User deploy
        AllowAgentForwarding no
        AllowTcpForwarding no
        X11Forwarding no
        PermitTunnel no
        DisableForwarding yes
        PasswordAuthentication no
        ForceCommand /home/deploy/capp
        AuthorizedKeysFile /dev/null
        AuthorizedKeysCommand /usr/local/bin/get_deploy_keys
        AuthorizedKeysCommandUser compose
EOF
[ -e /etc/compose/dca/users-keys ] && rm /etc/compose/dca/users-keys
[ -e /etc/deploy-authorized-keys ] && rm /etc/deploy-authorized-keys
[ -e /etc/dca-authorized-keys ] && rm /etc/dca-authorized-keys
touch /var/log/capp.log
chown compose: /var/log/capp.log
chmod g+w /var/log/capp.log
groups compose | grep -q '\<docker\>' || usermod -a -G docker compose
groups compose | grep -q '\<systemd-journal\>' || usermod -a -G systemd-journal compose
groups compose | grep -q '\<deploy\>' || usermod -a -G deploy compose
systemctl_path=$(which systemctl)
cat > /etc/sudoers.d/compose <<EOF
# Allow members of compose group to execute compose-dirs command
Cmnd_Alias COMPOSE_DIRS = /usr/local/bin/compose-dirs
Cmnd_Alias COMPOSE_SVC = $systemctl_path start compose@*,\
                         $systemctl_path stop compose@*,\
                         $systemctl_path reload compose@*,\
                         $systemctl_path reload-or-restart compose@*,\
                         $systemctl_path restart compose@*,\
                         $systemctl_path status compose@*
Cmnd_Alias CAPP_HOOKS = /etc/capp/hooks.d/pre_deploy/*,\
                        /etc/capp/hooks.d/post_deploy/*,\
                        /etc/capp/hooks.d/pre_undeploy/*,\
                        /etc/capp/hooks.d/post_undeploy/*
%compose ALL= NOPASSWD: COMPOSE_DIRS, COMPOSE_SVC, CAPP_HOOKS
EOF
cat > /etc/sudoers.d/deploy <<EOF
# Allow members of deploy group to execute capp command
Defaults env_keep="SSH_USER"
Cmnd_Alias CAPP_CMD = /usr/local/bin/capp
%deploy ALL=(compose) NOPASSWD: CAPP_CMD
EOF
chmod u=r,g=r,o= /etc/sudoers.d/{compose,deploy}
set +e
(
    cd /etc/compose/dca
    $dc build --pull --no-cache
    cd ../proxy
    $dc build --pull --no-cache
)
/usr/local/bin/compose-dirs install
/usr/local/bin/compose-dirs update
/usr/local/bin/compose-dirs start
rm -rf "$tmpdir"
exit 0
##########
