J.U.S.T. Entrypoint
- just_entrypoint.sh
Note
This is the default entrypoint for J.U.S.T. projects. By default, the docker entrypoint runs as root, which can introduce a number of obstacles and undesired side effects. Hard coding user information into images is slow and limited. just_entrypoint.sh
will setup everything needed to dynamically make the user inside the container have your desired ids (by default, by querying your ids on the host).
The entrypoint is designed to take care of 5 major problems for you
Creating a user with the right UID/GID such that
Files created in directories (volumes) mounted from the host will have the correct permissions
The container, if given access to the X11 daemon, will be allowed to draw windows (e.g., display figures)
Fix initial permissions of internal volumes
Workaround NFS mounts when squash root is enabled. (Does not currently support kerberos auth)
Remove project environment variables ending in
_DOCKER
Remove
//
from the beginning of environment variables to handle the mingw/cygwin expansion workaround
Here’s a walkthrough of how the entrypoint runs:
The entrypoint starts running as
root
Loads the just environment for root according to
JUST_SETTINGS
. These variables will be removed by the time the entrypoint is executed as the user below, to prevent unexpected variable corruption.Any files in
JUST_ROOT_PATCH_DIR
will be executed (in alphabetical order) if they have the execute flag set, else they are sourced.Runs
just_entrypoint_functions docker_setup_user
to set up the user with the appropriate ids. This is superior to other methods that would build an image with a UID and GID embedded in it, since you do not need to rebuild the image to have it adopt a different id. Furthermore, it will capture all your gids by default, which allows more corner cases to work correctly.In the case that nfs is used, runs
just_entrypoint_functions docker_link_mounts
in conjunction withdocker_functions.bsh Just-docker-compose
to create symlinks where nfs mounts should appear. First,docker_functions.bsh Just-docker-compose
will identify nfs mounts and mount the root of an nfs mount point (assuming squash_root is typically enabled) to a secondary location, and add the mount point toJUST_DOCKER_ENTRYPOINT_LINKS
. Then,just_entrypoint_functions docker_link_mounts
uses the information encoded inJUST_DOCKER_ENTRYPOINT_LINKS
to create symlinks to the nfs subdirectories in the locations they were originally supposed to be mounted.Runs
just_entrypoint_functions docker_setup_data_volumes
to fix initial permission issues for internal data volumes. When an empty internal data volume is initially created by docker, the folder is owned and writable by root. This changes the folder to match the desired user’s permissions.docker_functions.bsh Just-docker-compose
automatically determines the list of internal data volumes and passes them in asJUST_DOCKER_ENTRYPOINT_INTERNAL_VOLUMES
, which it then passes intojust_entrypoint_functions docker_setup_data_volumes
asJUST_DOCKER_ENTRYPOINT_CHOWN_DIRS
andJUST_DOCKER_ENTRYPOINT_CHMOD_DIRS
JUST_DOCKER_ENTRYPOINT_CHOWN_DIRS
- recursively chowns all the files in the volumes listed to match the user. This could be slow with many files, but it only executes when the volumes permissions are bad, typically only the first time the container is started. If you need to customized/disable this feature, set and pass the environment variableJUST_DOCKER_ENTRYPOINT_CHOWN_DIRS
into the container to specify which directories to chown. This will override the default ofJUST_DOCKER_ENTRYPOINT_INTERNAL_VOLUMES
.
JUST_DOCKER_ENTRYPOINT_CHMOD_DIRS
- non-recursively chmod the directories listed to 777 so that any initial ownership issues are avoided. Can also be customized/disabled by passing a value in forJUST_DOCKER_ENTRYPOINT_CHMOD_DIRS
, but being non-recursive, this should never have a noticeable time penalty.These two features give volumes a much more desirable default behavior for non-root users.
Next, the entrypoint (and everything else from here on) is re-executed as the user that was created in
just_entrypoint_functions docker_setup_user
. If you need to give the user root privileges, the suggested method is to givegosu
special permissions (chmod u+s /usr/local/bin/gosu
) and usegosu root {command}
. However, this is not suggested when deploying.Loads the settings for the project/user according to
JUST_SETTINGS
.If
JUST_PROJECT_PREFIX
is set, runsjust_entrypoint_user_functions.bsh filter_docker_variables
to remove project variables ending in_DOCKER
. This can be disabled by settingJUST_FILTER_DOCKER
to0
Replace
//
with / in project variables if running on Windows using mingw or cygwin. Cygwin-like systems have a habit of expanding variables. Extra slashes are added in project environment files usingJUST_PATH_ESC
, which cygwin-like systems evaluate as a/
, else empty. An unfortunate side effect of this is the // is still in the container. While this is usually harmless, there are cases when the extra slash in the variables cause code to crash.Any files in
JUST_USER_PATCH_DIR
will be executed if they have the execute flag set, else they are sourced.If
JUSTFILE
was passed in, this signifies to the entrypoint that an internal just call will be used. This meansjust
is prepended to the command toexec
, so you don’t have to.If
JUSTFILE
is not set, the command arguments are run usingexec
The function sudo
that calls gosu
is also exported, for ease of use.
If you have your own entrypoint that you want to daisy-chain with the just entrypoint, changing the ENTRYPOINT
field in the docker file will allow this.
ENTRYPOINT ["/usr/local/bin/tini", "--", "/usr/bin/env",
"bash", "/vsi/linux/just_files/just_entrypoint.sh",
"bash", "/my_entrypoint.sh"]
CMD some_command
If a project needs to customize the behavior more than is possible, the developer is encouraged to copy the original file into their project, customize it, add it to the image, and call that entrypoint instead of the one in vsi_common.
set -eu
: ${VSI_COMMON_DIR=/vsi}
: ${DOCKER_USERNAME=user}
: ${JUST_ENTRYPOINT_LOCAL_CWD=${PWD}}
export JUST_ENTRYPOINT_LOCAL_CWD
cd /
source "${VSI_COMMON_DIR}/linux/elements.bsh"
if [ -n "${JUST_DEBUG_ENTRYPOINT+set}" ]; then
# No quotes here, so allow things like: JUST_DEBUG_ENTRYPOINT="source /foo/bar/something"
${JUST_DEBUG_ENTRYPOINT:-bash}
fi
if [ -d "/.singularity.d" ] || [ ! -f "/.dockerenv" ] || [ "$(id -u)" != "0" ]; then
export ALREADY_RUN_ONCE=1
# Singularity specific fix. In old versions with no --nvccli, we have to use --nv and it's broken
ld_dirs=(/lib /usr/local/lib /usr/lib) # default from alpine, no 64
if [ -e "/etc/ld.so.cache" ] && \
command -v mount &> /dev/null && \
command -v awk &> /dev/null && \
command -v grep &> /dev/null && \
ldconfig_list=$(ldconfig -p 2>/dev/null); then
# Find every library mounted in /.singularity.d/libs without the words cuda, nvidia, or nv.
# These are the problem .so files that should never have been mounted
library_list=($(mount | awk '/\.singularity\.d\/libs\// && !($3~/nvidia|nv|cuda/) {
split($3, a, "/");
split(a[4], b, "\\.so");
print b[1]}'))
# Find the directory names of these problem .so files
ld_dirs=($(for library in ${library_list[@]+"${library_list[@]}"}; do
libs=($(echo "${ldconfig_list}" | awk "/${library}\.so/"' { print $NF }'))
for lib in ${libs[@]+"${libs[@]}"}; do
dirname "${lib}"
done
done | sort -u))
fi
if (( ${#ld_dirs[@]} )); then
# Add these directories to LD_LIBRARY_PATH to override singularity
IFS=: remove_element LD_LIBRARY_PATH /.singularity.d/libs
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH-}${LD_LIBRARY_PATH:+:}$(IFS=:; echo "${ld_dirs[*]}"):/.singularity.d/libs"
fi
fi
function load_just_settings()
{
local JUST_SETTINGSS
local just_settings
MIFS="${JUST_SETTINGS_SEPARATOR-///}" split_s JUST_SETTINGSS ${JUST_SETTINGS+"${JUST_SETTINGS}"}
for just_settings in ${JUST_SETTINGSS[@]+"${JUST_SETTINGSS[@]}"}; do
source "${VSI_COMMON_DIR}/linux/just_files/just_env" "${just_settings}"
done
}
PS4=$'+\x1b[0m${BASH_SOURCE[1]+}${BASH_SOURCE[0]##*/}:${LINENO}${FUNCNAME[0]:+:${FUNCNAME[0]}()}\t'
if [ "${bash_feature_declare_global}" = "1" ]; then
declare -i extra_args=0
declare -i get_args_args_used
fi
if [ "${ALREADY_RUN_ONCE+set}" != "set" ]; then
if [ -n "${BASH_SOURCE+set}" ]; then
file="${BASH_SOURCE[0]}"
else
file="${0}" # sh compatibility
fi
(
# TODO: This will not source local.env if the src directory were on an nfs
# Not sure ADDing the local files in the Dockerfile is the "right" solution
load_just_settings
# Sorted: https://serverfault.com/a/122743/321910
for patch in "${JUST_ROOT_PATCH_DIR-/usr/local/share/just/root_run_patch}"/*; do
if [ -x "${patch}" ]; then
"${patch}"
# https://www.endpoint.com/blog/2016/12/12/bash-loop-wildcards-nullglob-failglob
# check -e incase glob doesn't expand
elif [ -e "${patch}" ]; then
source "${patch}"
fi
done
# Create the user and associated groups and handle nfs symlinks
# Setup the container to be more friendly to non-root users and
# add other advanced J.U.S.T. features
JUST_DOCKER_ENTRYPOINT_CHOWN_DIRS="${JUST_DOCKER_ENTRYPOINT_CHOWN_DIRS-${JUST_DOCKER_ENTRYPOINT_INTERNAL_VOLUMES-}}" \
JUST_DOCKER_ENTRYPOINT_CHMOD_DIRS="${JUST_DOCKER_ENTRYPOINT_CHMOD_DIRS-${JUST_DOCKER_ENTRYPOINT_INTERNAL_VOLUMES-}}" \
/usr/bin/env bash "${VSI_COMMON_DIR}/linux/just_files/just_entrypoint_functions"
)
# Workaround for gosu blocking a CVE we don't care about
export GOSU_PLEASE_LET_ME_BE_COMPLETELY_INSECURE_I_GET_TO_KEEP_ALL_THE_PIECES="I've seen things you people wouldn't believe. Attack ships on fire off the shoulder of Orion. I watched C-beams glitter in the dark near the Tannhäuser Gate. All those moments will be lost in time, like tears in rain. Time to die."
# Rerun entrypoint as user now, (skipping the root part via ALREADY_RUN_ONCE)
ALREADY_RUN_ONCE=1 exec gosu ${DOCKER_USERNAME} /usr/bin/env bash "${file}" ${@+"${@}"}
fi
function sudo()
{
gosu root ${@+"${@}"}
}
export -f sudo
if [ -n "${JUSTFILE:+set}" ]; then
run_just=1
else
run_just=0
fi
load_just_settings
source "${VSI_COMMON_DIR}/linux/just_files/just_entrypoint_user_functions.bsh"
if [ -n "${JUST_PROJECT_PREFIX+set}" ]; then
# Remove duplicate ${JUST_PROJECT_PREFIX}_*_DOCKER variables
filter_docker_variables
fi
docker_convert_paths
for patch in "${JUST_USER_PATCH_DIR-/usr/local/share/just/user_run_patch}"/*; do
if [ -x "${patch}" ]; then
"${patch}"
# https://www.endpoint.com/blog/2016/12/12/bash-loop-wildcards-nullglob-failglob
# check -e incase glob doesn't expand
elif [ -e "${patch}" ]; then
source "${patch}"
fi
done
unset shell
cd "${JUST_ENTRYPOINT_LOCAL_CWD}"
unset JUST_ENTRYPOINT_LOCAL_CWD
if [ "${run_just}" = "1" ]; then
source "${VSI_COMMON_DIR}/linux/just"
just ${@+"${@}"}
else
exec ${@+"${@}"}
fi
- Parameters:
JUST_SETTINGS
- Location of project env settings file.VSI_COMMON_DIR
- Optional, location of VSI dir, defaults to /vsiDOCKER_USERNAME
- Optional, username for new user, defaults to user
- Internal Use:
ALREADY_RUN_ONCE
- Tracks if entrypoint has already sudo’d to user.JUST_DOCKER_ENTRYPOINT_INTERNAL_VOLUMES
- Passed fromdocker_functions.bsh Just-docker-compose
- JUST_ROOT_PATCH_DIR
just_entrypoint.sh
will source or execute (if the execute bit is set) every file in this directory as root on container start. In singularity, this step is skipped. The default is: /usr/local/share/just/root_run_patch
.
- JUST_USER_PATCH_DIR
just_entrypoint.sh
will source or execute (if the execute bit is set) every file in this directory as the user on container start. The default is: /usr/local/share/just/user_run_patch
Note
The intended use of JUST_ROOT_PATCH_DIR
and JUST_USER_PATCH_DIR
is to add files to them from either docker recipes or in the Dockerfile. While you can mount a folder or volume to this location, that is not the intent. For example, on Windows all files may appear to have execute permissions, which may not be what you want.
- JUST_DEBUG_ENTRYPOINT
just_entrypoint.sh
will execute the unquoted string specified by JUST_DEBUG_ENTRYPOINT
at the beginning of the script for the purposes of debugging. A typical value would be /usr/local/bin/bash
, bash -i
, or source /foo/bar/something
. If set to null, it will default to running bash
.