#!/bin/bash
# shellcheck disable=SC2086,SC1090
# we ignore SC2086 because ${DOCKER_BUILD_ARGUMENTS:-} is intended to
# be evaluated into multiple strings, not a single argument.
# we ignore SC1090 because "source" is validated independently

set -eu

export PATH="/opt/venvs/securedrop-app-code/bin:$PATH"

TOPLEVEL=$(git rev-parse --show-toplevel)
USE_TOR="${USE_TOR:-}"
USE_PODMAN="${USE_PODMAN:-}"
SLIM_BUILD="${SLIM_BUILD:-}"
DOCKER_RUN_ARGUMENTS="${DOCKER_RUN_ARGUMENTS:-}"
UBUNTU_VERSION="${UBUNTU_VERSION:-focal}"

# Allow opting into using podman with USE_PODMAN=1
if  [[ -n "${USE_PODMAN}" ]]; then
    DOCKER_BIN="podman"
    # Make sure host UID/GID are mapped into container,
    # see podman-run(1) manual.
    DOCKER_RUN_ARGUMENTS="${DOCKER_RUN_ARGUMENTS} --userns=keep-id"
else
    DOCKER_BIN="docker"
fi

# Whenever we're not on the platform we expect, explicitly tell Docker what
# platform we need
# This is also relevant for CI, as the --platform argument is only available
# from Docker API 1.32 onward, while at the time of writing CI still on API 1.23
if [[ "$(uname -sm)" != "Linux x86_64" ]]; then
    DOCKER_RUN_ARGUMENTS="${DOCKER_RUN_ARGUMENTS} --platform linux/amd64"
    DOCKER_BUILD_ARGUMENTS="${DOCKER_BUILD_ARGUMENTS:-} --platform linux/amd64"
fi

## Get an integer offset for exposed ports, to support multiple containers
get_port_offset() {
    tries=0
    while true
    do
        tries=$((tries + 1))
    port_offset=$((tries * 100))
        vnc=$((port_offset + 5909))
        nc -z localhost "$vnc" || break
    done
    echo "$port_offset"
}

function docker_image() {
    if [ "${DOCKER_BUILD_VERBOSE:-'false'}" = "true" ]; then
        out="/dev/stdout"
    else
        out="/dev/null"
    fi

    $DOCKER_BIN build \
           ${DOCKER_BUILD_ARGUMENTS:-} \
           --build-arg=USER_ID="$(id -u)" \
           --build-arg=USER_NAME="${USER:-root}" \
           --build-arg=UBUNTU_VERSION="${UBUNTU_VERSION}" \
           -t "${1}" \
           --file "${TOPLEVEL}/securedrop/dockerfiles/focal/python3/${2}" \
           "${TOPLEVEL}/securedrop" > $out
}

function docker_run() {
    find . \( -name '*.pyc' -o -name __pycache__ \) -delete
    if [ "${OFFSET_PORTS:-true}" = "true" ]
    then
       port_offset="$(get_port_offset)"
    else
       port_offset=0
    fi

    SD_CONTAINER="securedrop-dev-${port_offset}"
    SD_HOSTPORT_JI=$((port_offset + 8081))
    SD_HOSTPORT_SI=$((port_offset + 8080))
    SD_HOSTPORT_VNC=$((port_offset + 5909))

    if [ "${DOCKER_BUILD_VERBOSE:-'false'}" = "true" ]
    then
        echo "************************************************************"
        echo "Exposed services will be available on localhost at"
        echo "Source interface: http://127.0.0.1:$SD_HOSTPORT_SI"
        echo "Journalist interface: http://127.0.0.1:$SD_HOSTPORT_JI"
        if  [[ -z "${SLIM_BUILD}" ]]; then
            echo "VNC: port $SD_HOSTPORT_VNC"
        fi
        echo "************************************************************"
    fi

    # If this is a CI run, pass CodeCov's required vars into the container.
    if [ -n "${CIRCLE_BRANCH:-}" ] ; then
        : "${CIRCLE_PULL_REQUEST:=}"
        ci_env="-e CI=true \
                -e CIRCLECI=true \
                -e CIRCLE_BRANCH=${CIRCLE_BRANCH:-} \
                -e CIRCLE_SHA1=${CIRCLE_SHA1:-} \
                -e CIRCLE_PR_USERNAME=${CIRCLE_PR_USERNAME:-} \
                -e CIRCLE_PROJECT_REPONAME=${CIRCLE_PROJECT_REPONAME:-} \
                -e CIRCLE_PROJECT_USERNAME=${CIRCLE_PROJECT_USERNAME:-} \
                -e CIRCLE_REPOSITORY_URL=${CIRCLE_REPOSITORY_URL:-} \
                -e CIRCLE_BUILD_NUM=${CIRCLE_BUILD_NUM:-} \
                -e CIRCLE_NODE_INDEX=${CIRCLE_NODE_INDEX:-} \
                -e CIRCLE_PR_NUMBER=${CIRCLE_PULL_REQUEST##*/} \
                -e CIRCLE_BUILD_URL=${CIRCLE_BUILD_URL:-} \
               "

    else
        ci_env=""
    fi

    # Pass -it if we're a tty
    if test -t 0; then
        DOCKER_RUN_ARGUMENTS="${DOCKER_RUN_ARGUMENTS} -it"
    fi

    if [ -n "${USE_TOR:-}" ]; then
        # Mount persistent onion services
        $DOCKER_BIN volume inspect sd-onion-services -f " " || $DOCKER_BIN volume create sd-onion-services
        DOCKER_RUN_ARGUMENTS="${DOCKER_RUN_ARGUMENTS} --volume sd-onion-services:/var/lib/tor/services"
    fi

    # The --shm-size argument sets up dedicated shared memory for the
    # container. Our tests can fail with the default of 64m.
    echo "Starting ${UBUNTU_VERSION} container..."
    $DOCKER_BIN run $ci_env \
           --shm-size 2g \
           --rm \
           -p "127.0.0.1:${SD_HOSTPORT_VNC}:5909" \
           -p "127.0.0.1:${SD_HOSTPORT_SI}:8080" \
           -p "127.0.0.1:${SD_HOSTPORT_JI}:8081" \
           -e USE_TOR=$USE_TOR \
           -e NUM_JOURNALISTS \
           -e NUM_SOURCES \
           -e LOADDATA_ARGS \
           -e LC_ALL=C.UTF-8 \
           -e LANG=C.UTF-8 \
           -e TEST_LOCALES \
           -e PATH \
           -e BASE_OS="focal" \
           --user "${USER:-root}" \
           --volume "${TOPLEVEL}:${TOPLEVEL}:Z" \
           --workdir "${TOPLEVEL}/securedrop" \
           --name "${SD_CONTAINER}" \
           $DOCKER_RUN_ARGUMENTS "${1}" "${@:2}"
}

image="securedrop-slim-${UBUNTU_VERSION}-py3"
docker_image "$image" "SlimDockerfile"
if  [[ -z "${SLIM_BUILD}" ]]; then
    image="securedrop-test-${UBUNTU_VERSION}-py3"
    docker_image "$image" "Dockerfile"
fi

docker_run "$image" "$@"
