#!/bin/bash
#
# PBA docker run wrapper
#
# (C) ITEG IT-Engineers GmbH 2010-2026
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#   http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# pba-dockerized-run
# Performs some task in a temporary-ish docker container, usually during build time,
# or maybe for unit-tests or installation time purposes.
# To test-run an image you have a project for use pba-docker-run for simple cases
# custom docker calls or even docker-compose setups for more complex situations. 
#
# Call without parameters to get a detailed description

function printSyntax() {
  echo "Syntax: pba-dockerized-run imageToUse commandToRun"
  echo ""
  echo "Runs the specified commandToRun in a docker container using imageToUse"
  echo ""
  echo "Example: pba-dockerized-run debian:trixie-slim uname -a"
  echo ""
  echo "pba-dockerized-run always ..."
  echo "+ uses -u to run command under calling user, passing HOME variable accordingly."
  echo "  Set PBA_DOCKERIZED_USER to use another host system user for both"
  echo "  or put another -u someUser and maybe -e HOME=somedir in PBA_DOCKERIZED_RUNOPTS"
  echo "  to overrule this completely."
  echo "+ mounts the home directory read-write, unless you resp. PBA_DOCKERIZED_USER are 'root'."
  echo "  If you don't want this then sorry, docker-compose might be the better tool then."
  echo "+ mounts the current directory read-write, unless it's below that home directory."
  echo "+ bindmounts /etc/passwd read-only, unless you resp. PBA_DOCKERIZED_USER are 'root'"
  echo "+ sets the current caller directory as working directory using -w."
  echo "  Set PBA_DOCKERIZED_WORKDIR to change this."
  echo "+ If SSH_AUTH_SOCK is set and does exist it is passed through as environment variable"
  echo "  and is bind-mounted read-only, together with /etc/ssh/ssh_known_hosts if that exists."
  echo "+ uses '$(pba-utils-config-get PbaUtilsLogLevel 1)' to set it's log level."
  echo "  Call that without parameters to learn about config files logic." 
  echo "+ Always uses --rm, after learning that even --name=foobar is not helpful multi-step CIs,"
  echo "  in some cases temporary image(s) or compose setup(s) might be the way to go."
  echo ""
  echo "pba-dockerized-run does NOT ..."
  echo "- support multiple comments or complex stuff like sh -c 'foobar $SOMEVAR'."
  echo "  Use a script to run multiple commands in one run or maybe even docker-compose setups,"
  echo "  or call repeatedly with '-n myContainer' in PBA_DOCKERIZED_RUNOPTS to optimize."
  echo "- bind-mount ~ if you resp. PBA_DOCKERIZED_USER are 'root'."
  echo ""
  echo "Advanced wishes come through environment variables:"
  echo "+ paths in PBA_DOCKERIZED_BINDMOUNTS_RO are added to bind mount points in :ro mode."
  echo "+ paths in PBA_DOCKERIZED_BINDMOUNTS_RW are added to bind mount points in :rw mode."
  echo "+ files in PBA_DOCKERIZED_ENVFILES are passed with --env-file"
  echo "+ platform in PBA_DOCKERIZED_PLATFORM is passed with --platform"
  echo "+ anything in PBA_DOCKERIZED_RUNOPTS is put between computed options and image name."
  echo "+ the userid PBA_DOCKERIZED_USER is used for -u and eventually -e HOME=/somedir."
  echo "  You can specify a userid like www-data or a uid like 33, we use id -u."
  echo "+ a directory PBA_DOCKERIZED_WORKDIR is used as workdir in container, i.e. -w."
  echo "  You might want to set it to '$(pwd)' to avoid long and dangerous ~/foo/bar values"
  echo "+ about SSH_AUTH_SOCK see above and 'man ssh'." 
  echo "+ Verbosity can be configured as PbaUtilsLogLevel in /etc/pba/utils.conf,"
  echo "  ~/.pba/utils.conf and ./.pba/pba-utils.conf with the last mention winning".
  echo ""

}

PBA_DOCKERIZED_IMAGE=$1
if [ -z "${PBA_DOCKERIZED_IMAGE}" ] ; then
  echo "ERROR: No docker image specifed as \$1"
  echo ""
  printSyntax
  exit 1
fi

if [ "${PBA_DOCKERIZED_IMAGE}" = "--help" ] ; then
  printSyntax
  exit 0
fi
shift

export PBA_PWD=$(pwd)

PBA_VERBOSITY=$(pba-utils-config-get PbaUtilsLogLevel 1)

# make sure we are back where we started
cd ${PBA_PWD}

export PBA_FULL_DOCKEROPTS=""
# some CIs might reuse containers as a means of optimization,
# but container reuse is way more complicated
# in those situations a Dockerfile creating a temporary image might be more useful
# anyway, finally keep --rm ...
PBA_FULL_DOCKEROPTS="${PBA_FULL_DOCKEROPTS} --rm"

if [ -n "${PBA_DOCKERIZED_ENVFILES}" ] ; then
  test ${PBA_VERBOSITY} -gt 0 && echo "INFO: Adding your environment files from \$PBA_DOCKERIZED_ENVFILES, '${PBA_DOCKERIZED_ENVFILES}'"
  for PBA_ENVFILE in ${PBA_DOCKERIZED_ENVFILES} ; do
    PBA_FULL_DOCKEROPTS="${PBA_FULL_DOCKEROPTS} --env-file=${PBA_ENVFILE}"
  done
fi

#PBA_FULL_DOCKEROPTS="${PBA_FULL_DOCKEROPTS} -v=${PBA_PWD}:${PBA_PWD}:rw -w ${PBA_DOCKERIZED_WORKDIR:-${PBA_PWD}}"
PBA_FULL_DOCKEROPTS="${PBA_FULL_DOCKEROPTS} -w ${PBA_DOCKERIZED_WORKDIR:-${PBA_PWD}}"

if [ -n "${SSH_AUTH_SOCK}" ] ; then
  if [ -e "${SSH_AUTH_SOCK}" ] ; then 
    test ${PBA_VERBOSITY} -gt 0 && echo "INFO: Adding SSH_AUTH_SOCK=${SSH_AUTH_SOCK} as environment variable and readonly bind-mount ..."
    PBA_FULL_DOCKEROPTS="${PBA_FULL_DOCKEROPTS} -v=${SSH_AUTH_SOCK}:${SSH_AUTH_SOCK}:ro -e SSH_AUTH_SOCK=${SSH_AUTH_SOCK}"
    if [ -e "/etc/ssh/ssh_known_hosts" ] ; then 
      test ${PBA_VERBOSITY} -gt 0 && echo "INFO: Adding /etc/ssh/ssh_known_hosts as readonly bind-mount too ..."
      PBA_FULL_DOCKEROPTS="${PBA_FULL_DOCKEROPTS} -v=/etc/ssh/ssh_known_hosts:/etc/ssh/ssh_known_hosts:ro"
    else
      test ${PBA_VERBOSITY} -gt 1 && echo "INFO: No /etc/ssh/ssh_known_hosts found, not adding it as readonly bind-mount."
    fi
  else
    echo "WARNING: \$SSH_AUTH_SOCK set to '${SSH_AUTH_SOCK}' but does not exist, not adding anything there."
  fi
else
  test ${PBA_VERBOSITY} -gt 1 && echo "INFO: \$SSH_AUTH_SOCK not set, not thinking about adding anything there."
fi

if [ -n "${PBA_DOCKERIZED_BINDMOUNTS_RO}" ] ; then
  test ${PBA_VERBOSITY} -gt 0 && echo "INFO: Picking up read-only bind mounts specified as \$PBA_DOCKERIZED_BINDMOUNTS_RO ..."
  for PBA_BINDMOUNT in ${PBA_DOCKERIZED_BINDMOUNTS_RO} ; do
    test ${PBA_VERBOSITY} -gt 0 && echo "INFO: Picking up read-only bind mount ${PBA_BINDMOUNT} ..."
    PBA_FULL_DOCKEROPTS="${PBA_FULL_DOCKEROPTS} -v=${PBA_BINDMOUNT}:${PBA_BINDMOUNT}:ro"
  done
else
  test ${PBA_VERBOSITY} -gt 2 && echo "DEBUG: No read-only bind mounts to add specified as \$PBA_DOCKERIZED_BINDMOUNTS_RO"
fi

if [ -n "${PBA_DOCKERIZED_BINDMOUNTS_RW}" ] ; then
  test ${PBA_VERBOSITY} -gt 0 && echo "INFO: Picking up read-write bind mounts specified as \$PBA_DOCKERIZED_BINDMOUNTS_RW ..."
  for PBA_BINDMOUNT in ${PBA_DOCKERIZED_BINDMOUNTS_RW} ; do
    test ${PBA_VERBOSITY} -gt 0 && echo "INFO: Picking up read-write bind mount ${PBA_BINDMOUNT} ..."
    PBA_FULL_DOCKEROPTS="${PBA_FULL_DOCKEROPTS} -v=${PBA_BINDMOUNT}:${PBA_BINDMOUNT}:rw"
  done
else
  test ${PBA_VERBOSITY} -gt 2 && echo "DEBUG: No read-write bind mounts to add specified as \$PBA_DOCKERIZED_BINDMOUNTS_RW"
fi

export PBA_DOCKERIZED_USER
if [ -z "${PBA_DOCKERIZED_USER}" ] ; then
  test ${PBA_VERBOSITY} -gt 1 && echo "INFO: No user specified as \$PBA_DOCKERIZED_USER, defaulting to myself ..."
  PBA_DOCKERIZED_USER=$(id -u -n)
  test ${PBA_VERBOSITY} -gt 0 && echo "INFO: Computed \$PBA_DOCKERIZED_USER to '${PBA_DOCKERIZED_USER}'"
else
  test ${PBA_VERBOSITY} -gt 0 && echo "INFO: Using \$PBA_DOCKERIZED_USER, '${PBA_DOCKERIZED_USER}'"
fi
if [ -z "${PBA_DOCKERIZED_USER}" ] ; then
  echo -n "ERROR: Failed to auto-set \$PBA_DOCKERIZED_USER ..."
  exit 10
fi
# always for now, but keeping old if-ed indentation, we might need it in the end
  test ${PBA_VERBOSITY} -gt 0 && echo "INFO: Collecting id and home of specified \$PBA_DOCKERIZED_USER, ${PBA_DOCKERIZED_USER} ..."
  PBA_DOCKERIZED_USER_UID=$(id -u ${PBA_DOCKERIZED_USER})
  if [ -z "${PBA_DOCKERIZED_USER_UID}" ] ; then
    echo "ERROR: Failed to detect id of specified \$PBA_DOCKERIZED_USER, ${PBA_DOCKERIZED_USER}"
    exit 11
  fi
  test ${PBA_VERBOSITY} -gt 1 && echo "INFO: Detected id of specified \$PBA_DOCKERIZED_USER, ${PBA_DOCKERIZED_USER} to be ${PBA_DOCKERIZED_USER_UID} ..."
  PBA_DOCKERIZED_USER_HOME=$(getent passwd ${PBA_DOCKERIZED_USER} |cut -f6 -d':')
  if [ -z "${PBA_DOCKERIZED_USER_HOME}" ] ; then
    echo "ERROR: Failed to detect home dir of specified \$PBA_DOCKERIZED_USER, ${PBA_DOCKERIZED_USER}"
    exit 12
  fi
  if [ "${PBA_DOCKERIZED_USER_HOME}" = "/" ] ; then
    echo "ERROR: Refusing to work with user home '/'"
    exit 13
  fi
  test ${PBA_VERBOSITY} -gt 1 && echo "INFO: Detected homedir of specified \$PBA_DOCKERIZED_USER, ${PBA_DOCKERIZED_USER} to be ${PBA_DOCKERIZED_USER_HOME} ..."
  if [ ! -d "${PBA_DOCKERIZED_USER_HOME}" ] ; then
    echo "ERROR: Detect home dir of specified \$PBA_DOCKERIZED_USER, ${PBA_DOCKERIZED_USER}, to be '${PBA_DOCKERIZED_USER_HOME}' but it does not exist"
    exit 14
  fi
  PBA_FULL_DOCKEROPTS="${PBA_FULL_DOCKEROPTS} -u ${PBA_DOCKERIZED_USER_UID} -e HOME=${PBA_DOCKERIZED_USER_HOME}"
  if [ "${PBA_DOCKERIZED_USER}" = "root" -o "${PBA_DOCKERIZED_USER}" = "0" ] ; then
    test ${PBA_VERBOSITY} -gt 0 && echo "INFO: With \$PBA_DOCKERIZED_USER set or detected to 'root' not passing through /etc/passwd as read-only bindmount ..."
    test ${PBA_VERBOSITY} -gt 0 && echo "      but read-write-mounting current dir '${PBA_PWD}'"  
    PBA_FULL_DOCKEROPTS="${PBA_FULL_DOCKEROPTS} -v=${PBA_PWD}:${PBA_PWD}:rw"
    
  else
    test ${PBA_VERBOSITY} -gt 0 && echo "INFO: With \$PBA_DOCKERIZED_USER not root also adding /etc/passwd as read-only bindmount and ${PBA_DOCKERIZED_USER_HOME} to read-write mounts ..."
    PBA_FULL_DOCKEROPTS="${PBA_FULL_DOCKEROPTS} -v=/etc/passwd:/etc/passwd:ro -v=${PBA_DOCKERIZED_USER_HOME}:${PBA_DOCKERIZED_USER_HOME}:rw"
    if echo ${PBA_PWD} |egrep "^${PBA_DOCKERIZED_USER_HOME}" >/dev/null ; then
       test ${PBA_VERBOSITY} -gt 0 && echo "INFO: Homedir ${PBA_DOCKERIZED_USER_HOME} is parent of current dir ${PBA_PWD}, not bindmounting the latter too."
    else
       test ${PBA_VERBOSITY} -gt 0 && echo "INFO: Homedir ${PBA_DOCKERIZED_USER_HOME} is NO parent of current dir ${PBA_PWD}, bindmounting the latter too."
       PBA_FULL_DOCKEROPTS="${PBA_FULL_DOCKEROPTS} -v=${PBA_PWD}:${PBA_PWD}:rw"
    fi
    
  fi
#fi

if [ -n "${PBA_DOCKERIZED_RUNOPTS}" ] ; then
  test ${PBA_VERBOSITY} -gt 0 && echo "INFO: Adding your docker options from \$PBA_DOCKERIZED_RUNOPTS, '${PBA_DOCKERIZED_RUNOPTS}'"
  PBA_FULL_DOCKEROPTS="${PBA_FULL_DOCKEROPTS} ${PBA_DOCKERIZED_RUNOPTS}"
else
  test ${PBA_VERBOSITY} -gt 2 && echo "DEBUG: No docker run options specified as \$PBA_DOCKERIZED_RUNOPTS to add"
fi

if [ -n "${PBA_DOCKERIZED_PLATFORM}" ] ; then
  test ${PBA_VERBOSITY} -gt 0 && echo "INFO: Adding --platform to your docker options from \$PBA_DOCKERIZED_PLATFORM, '${PBA_DOCKERIZED_PLATFORM}'"
  PBA_FULL_DOCKEROPTS="${PBA_FULL_DOCKEROPTS} --platform ${PBA_DOCKERIZED_PLATFORM}"
else
  test ${PBA_VERBOSITY} -gt 2 && echo "DEBUG: No platform specified as \$PBA_DOCKERIZED_PLATFORM, using native platform."
fi

test ${PBA_VERBOSITY} -gt 2 && echo "DEBUG: Final full docker run options are: '${PBA_FULL_DOCKEROPTS}'"

test ${PBA_VERBOSITY} -gt 0 && echo "INFO: Calling   docker run ${PBA_FULL_DOCKEROPTS} ${PBA_DOCKERIZED_IMAGE} $*   ..."
docker run ${PBA_FULL_DOCKEROPTS} ${PBA_DOCKERIZED_IMAGE} $*

RC=$?

echo ""

if [ "$RC" = "0" ] ; then
  test ${PBA_VERBOSITY} -gt 0 && echo "GOOD: Got good exit code 0 from   docker run ${PBA_FULL_DOCKEROPTS} ${PBA_DOCKERIZED_IMAGE} $*"
  exit 0
fi

echo "ERROR: Got and forwarding bad exit code ${RC} from   docker run ${PBA_FULL_DOCKEROPTS} ${PBA_DOCKERIZED_IMAGE} $*"
exit $RC

