#!/bin/bash
# Copyright (C) 2019 Checkmk GmbH - License: GNU General Public License v2
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
# conditions defined in the file COPYING, which is part of this source code package.

# Runs attached command in a Docker container.
# By default the 'reference image' will be used to create the container, but this
# behavior can be customized using either @IMAGE_ALIAS or @IMAGE_ID as follows:
#   run-in-docker.sh <CMD>                                     will use reference image
#   IMAGE_ALIAS=IMAGE_SLES_15sp6 run-in-docker.sh <CMD>        will use dereferenced image alias IMAGE_SLES_15sp6
#   IMAGE_ID=ubuntu-22.04:master-latest run-in-docker.sh <CMD>  will use provided image id directly
# Also DOCKER_RUN_ADDOPTS can be set to add additional arguments to be passed to `docker run`
#
# To re-build the container from scratch:
#   docker buildx prune --all
#   docker rmi $(docker images -f "dangling=true" -q)
#   rm -r container_shadow_workspace_local/venv/
#   ./scripts/run-in-docker.sh <CMD>
#
# Get more details about the reference image and other things with:
#   VERBOSE=1 ./scripts/run-in-docker.sh <CMD>

set -e

CHECKOUT_ROOT="$(git rev-parse --show-toplevel)"

: "${IMAGE_ID:="$(
    if [ -n "${IMAGE_ALIAS}" ]; then
        "${CHECKOUT_ROOT}"/buildscripts/docker_image_aliases/resolve.py "${IMAGE_ALIAS}"
    else
        "${CHECKOUT_ROOT}"/defines/dev-images/reference-image-id
    fi
)"}"

IMAGE_VERSION="$(docker run --rm -v "${CHECKOUT_ROOT}/omd:/tmp" "${IMAGE_ID}" /tmp/distro '-')"

# in case of worktrees $CHECKOUT_ROOT might not contain the actual repository clone
GIT_COMMON_DIR="$(realpath "$(git rev-parse --git-common-dir)")"

CMD="${*:-bash}"

# Create directories for build artifacts which we want to have separated
# in native and containerized builds
# Here might be coming more, you keep an open eye, too, please
# Create directories which otherwise would get created by root
# rendering the native build broken

if [ "$USER" == "jenkins" ]; then
    # CI - the `_local` suffix just indicates that the folder had been generated by this script,
    # rather than the CI-solution that should be used
    CONTAINER_SHADOW_WORKSPACE="$(dirname "${CHECKOUT_ROOT}")/container_shadow_workspace_local/${IMAGE_VERSION}"
    echo >&2 "WARNING: run-in-docker.sh used in CI. This should not happen. Use CI native tools instead"
else
    # LOCAL
    CONTAINER_SHADOW_WORKSPACE="${CHECKOUT_ROOT}/container_shadow_workspace_local/${IMAGE_VERSION}"
fi

: "${CONTAINER_NAME:="ref-$(basename "$(pwd)")-$(sha1sum <<<"${CONTAINER_SHADOW_WORKSPACE}" | cut -c1-10)"}"

# Don't map ~/.cache but create a temporary folder inside the shadow workspace
# BEGIN COMMON CODE with docker_image_aliases_helper.groovy
if [ -e "${CONTAINER_SHADOW_WORKSPACE}/cache" ]; then
    # Bazel creates files without write permission
    chmod -R a+w "${CONTAINER_SHADOW_WORKSPACE}/cache"
fi

# Create bind source and targets to avoid dockerd creating them with root ownership
mkdir -p "${CONTAINER_SHADOW_WORKSPACE}/home"
touch "${CONTAINER_SHADOW_WORKSPACE}/home/.cmk-credentials"
mkdir -p "${CONTAINER_SHADOW_WORKSPACE}/home/.cache"
mkdir -p "${CONTAINER_SHADOW_WORKSPACE}/home_cache"
mkdir -p "${CONTAINER_SHADOW_WORKSPACE}/home_cache/bazel"
mkdir -p "${CONTAINER_SHADOW_WORKSPACE}/omd_build"
mkdir -p "${CHECKOUT_ROOT}/shared_cargo_folder"
mkdir -p "${CHECKOUT_ROOT}/omd/build"
mkdir -p "${CONTAINER_SHADOW_WORKSPACE}/home/$(realpath -s --relative-to="${HOME}" "${CHECKOUT_ROOT}")"
mkdir -p "${CONTAINER_SHADOW_WORKSPACE}/home/$(realpath -s --relative-to="${HOME}" "${CHECKOUT_ROOT}/omd/build")"
mkdir -p "${CONTAINER_SHADOW_WORKSPACE}/home/$(realpath -s --relative-to="${HOME}" "${GIT_COMMON_DIR}")"
# END COMMON CODE with docker_image_aliases_helper.groovy

# Needed for .cargo which is shared between workspaces
mkdir -p "${HOME}/shared_cargo_folder"

# UNCONDITIONAL MOUNTS
DOCKER_MOUNT_ARGS="-v ${CONTAINER_SHADOW_WORKSPACE}/home:${HOME}"

# with "--mount" the execution of binaries is not allowed and can not be changed
# use "--tmpfs" instead
# see https://docs.docker.com/engine/storage/tmpfs/#options-for---tmpfs
# mount the bazel cache as tmpfs to save minimum 6GB of diskspace
# use different size locally vs in CI, 15GB locally is to much, but 10GB not enough on CI
DOCKER_MOUNT_ARGS="${DOCKER_MOUNT_ARGS} --tmpfs ${HOME}/.cache:exec,size=10g,mode=777"
# other folders in HOME/.cache are not touched to ensure a fast venv creation

DOCKER_MOUNT_ARGS="${DOCKER_MOUNT_ARGS} -v ${HOME}/shared_cargo_folder:${CHECKOUT_ROOT}/shared_cargo_folder"
DOCKER_MOUNT_ARGS="${DOCKER_MOUNT_ARGS} -v ${CHECKOUT_ROOT}:${CHECKOUT_ROOT}"
DOCKER_MOUNT_ARGS="${DOCKER_MOUNT_ARGS} -v ${GIT_COMMON_DIR}:${GIT_COMMON_DIR}"
DOCKER_MOUNT_ARGS="${DOCKER_MOUNT_ARGS} -v ${CONTAINER_SHADOW_WORKSPACE}/omd_build:${CHECKOUT_ROOT}/omd/build"

if [ -d "${HOME}/.docker" ]; then
    mkdir -p "${CONTAINER_SHADOW_WORKSPACE}/home/.docker"
    DOCKER_MOUNT_ARGS="${DOCKER_MOUNT_ARGS} -v ${HOME}/.docker:${HOME}/.docker"
fi

# We're using git reference clones, see also jenkins/global-defaults.yml in checkmk_ci.
# That's why we need to mount the reference repos.
GIT_REFERENCE_CLONE_PATH="${HOME}/git_reference_clones/check_mk.git"
if [ -d "${GIT_REFERENCE_CLONE_PATH}" ]; then
    mkdir -p "${CONTAINER_SHADOW_WORKSPACE}/home/$(realpath -s --relative-to="${HOME}" "${GIT_REFERENCE_CLONE_PATH}")"
    DOCKER_MOUNT_ARGS="${DOCKER_MOUNT_ARGS} -v ${GIT_REFERENCE_CLONE_PATH}:${GIT_REFERENCE_CLONE_PATH}:ro"
fi

: "${TERMINAL_FLAG:="$([ -t 0 ] && echo ""--interactive --tty"" || echo "")"}"
: "${CPU_LIMITATION:="--cpus="$(($(nproc) - 3))""}"

if [ -t 0 ]; then
    echo "Running in Docker container from image ${IMAGE_ID} (cmd=${CMD}) (workdir=${PWD})"
fi

function cleanup {
    # FIXME: eventually created image is not being cleaned up
    ROOT_ARTIFACTS=$(find . -user root)
    if [ -n "${ROOT_ARTIFACTS}" ]; then
        echo >&2 "WARNING: there are files/directories owned by root:"
        echo >&2 "${ROOT_ARTIFACTS}"
    fi
}
trap cleanup EXIT

# shellcheck disable=SC2086
docker run -a stdout -a stderr \
    --rm \
    --name $CONTAINER_NAME \
    ${CPU_LIMITATION} \
    --ulimit nofile=8192:8192 \
    ${TERMINAL_FLAG} \
    --init \
    -u "$(id -u):$(id -g)" \
    ${DOCKER_MOUNT_ARGS} \
    -v "/var/run/docker.sock:/var/run/docker.sock" \
    -v "/etc/passwd:/etc/passwd:ro" \
    -v "/etc/group:/etc/group:ro" \
    --group-add="$(getent group docker | cut -d: -f3)" \
    -e USER \
    -e CI \
    -e BANDIT_OUTPUT_ARGS \
    -e SEMGREP_OUTPUT_ARGS \
    -e GROOVYLINT_OUTPUT_ARGS \
    -e JUNIT_XML \
    -e PYLINT_ARGS \
    -e PYTEST_ADDOPTS \
    -e DOCKER_ADDOPTS \
    -e MYPY_ADDOPTS \
    -e CI_TEST_SQL_DB_ENDPOINT \
    -e CI_ORA2_DB_TEST \
    -e PYTHON_FILES \
    -e CHANGED_FILES \
    -e RESULTS \
    -e RUFF_OUTPUT_FORMAT \
    -e GERRIT_BRANCH \
    -e DOCKER_REGISTRY_NO_HTTPS \
    -w "${PWD}" \
    ${DOCKER_RUN_ADDOPTS} \
    "${IMAGE_ID}" \
    sh -c "${CMD}"
