The previous name, 'johncolvin/bluetradingsystems', was chosen in a misguided effort to adhere to a naming convention recommended by Docker Hub (https://docs.docker.com/docker-hub/repos/create/). In hindsight, this convention isn't well suited to our use case. Our images are shared by multiple users (e.g., sparkd dev users all use the same sparkd-dev image), so it doesn't make sense to name the repository after an arbitrary user.
231 lines
5.9 KiB
Bash
Executable File
231 lines
5.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
function _exit() {
|
|
if [[ "$#" -gt 1 ]]; then
|
|
# When there are multiple args, the last arg is the exit code.
|
|
echo "${@:1:($# - 1)}"
|
|
exit "${@: -1}"
|
|
fi
|
|
echo "$1"
|
|
exit 0
|
|
}
|
|
|
|
help_text="\
|
|
Start a docker dev container
|
|
Usage: start_docker.sh [OPTIONS] image container
|
|
|
|
Positionals:
|
|
image The image to instantiate
|
|
container The name for the container
|
|
|
|
Options:
|
|
-u,--user USER The user to add in the container (defaults to 'bts')
|
|
-p,--pass PASS The password for USER in the container (defaults to USER)
|
|
-i,--image IMAGE The image to instantiate (defaults to 'bluetradingsystems/sparkd-dev:latest')
|
|
-c,--container CONT The name for the container
|
|
-m,--mount MOUNT The name for the folder (outside the container) to create/mount to the home
|
|
directory in the container. Mount folders are added here: $mount_prefix
|
|
--mount-existing Mount the specified dir even if it already exists
|
|
--port <cont_port>:<host_port> OR [<cont_port>:<host_port>,...] Port mapping(s). May be
|
|
specified multiple times.
|
|
-h,--help Print this help message and exit
|
|
|
|
"
|
|
|
|
mount_prefix="$HOME/.sparkd-dev/mounts"
|
|
declare docker_user=""
|
|
declare docker_pass=""
|
|
declare image_name=""
|
|
declare cont_name=""
|
|
declare mount_name=""
|
|
declare -i mount_existing=0
|
|
declare -a port_mappings=()
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
-u)
|
|
;&
|
|
--user)
|
|
if [[ -z "$2" ]]; then
|
|
_exit "User (-u,--user) must be non-empty" 1
|
|
fi
|
|
docker_user="$2"
|
|
shift 2
|
|
;;
|
|
-p)
|
|
;&
|
|
--pass)
|
|
if [[ -z "$2" ]]; then
|
|
_exit "Password (-p,--pass) must be non-empty" 1
|
|
fi
|
|
docker_pass="$2"
|
|
shift 2
|
|
;;
|
|
-i)
|
|
;&
|
|
--image)
|
|
if [[ -n "$image_name" ]]; then
|
|
_exit "Unexpected argument '$1'" 1
|
|
elif [[ -z "$2" ]]; then
|
|
_exit "Image (-i,--image) must be non-empty" 1
|
|
fi
|
|
image_name="$2"
|
|
shift 2
|
|
;;
|
|
-c)
|
|
;&
|
|
--container)
|
|
if [[ -n "$cont_name" ]]; then
|
|
_exit "Unexpected argument '$1'" 1
|
|
elif [[ -z "$2" ]]; then
|
|
_exit "Container (-c,--container) must be non-empty" 1
|
|
fi
|
|
cont_name="$2"
|
|
shift 2
|
|
;;
|
|
--port)
|
|
# Parse port(s) arg, and append the result to 'port_mappings'
|
|
# Note: port(s) may be specified in bracketed notation:
|
|
# [a:b,c:d,...,y:z]
|
|
next_ports="$(echo "$2" | sed -e 's/\[//g' -e 's/\]//g' -e 's/,/ /g')"
|
|
if [[ "${#port_mappings[@]}" -gt 0 ]]; then
|
|
next_ports=" $next_ports"
|
|
fi
|
|
port_mappings+=("$next_ports")
|
|
shift 2
|
|
;;
|
|
-m)
|
|
;&
|
|
--mount)
|
|
if [[ -z "$2" ]]; then
|
|
echo "Mount (-m,--mount) must be non-empty"
|
|
fi
|
|
mount_name="$2"
|
|
shift 2
|
|
;;
|
|
--mount-existing)
|
|
mount_existing=1
|
|
shift 1
|
|
;;
|
|
-h)
|
|
;&
|
|
--help)
|
|
_exit "$help_text" 0
|
|
;;
|
|
*)
|
|
if [[ -z "$1" ]]; then
|
|
_exit "Invalid positional argument '$1'" "$help_text" 1
|
|
elif [[ -z "$image_name" ]]; then
|
|
image_name="$1"
|
|
elif [[ -z "$cont_name" ]]; then
|
|
cont_name="$1"
|
|
else
|
|
_exit "Unexpected argument '$1'" "$help_text" 1
|
|
fi
|
|
shift 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
|
|
if [[ -z "$image_name" ]]; then
|
|
image_name="bluetradingsystems/sparkd-dev:latest"
|
|
fi
|
|
|
|
host_name="$(hostname)"
|
|
if [[ -n "$cont_name" ]]; then
|
|
host_name="$host_name.$cont_name"
|
|
fi
|
|
|
|
if [[ -z "$mount_name" ]]; then
|
|
mount_name="$cont_name"
|
|
fi
|
|
|
|
if [[ -z "$mount_name" && -z "$cont_name" ]]; then
|
|
_exit "Please specify mount (-m,--mount) and/or container (-c,--container)" 1
|
|
fi
|
|
|
|
if [[ -z "$docker_user" ]]; then
|
|
docker_user="bts"
|
|
fi
|
|
|
|
if [[ "$docker_user" = "root" ]]; then
|
|
_exit "USER (--user) cannot be 'root'" 1
|
|
fi
|
|
|
|
cont_home="/home/$docker_user"
|
|
home_src="$mount_prefix/$mount_name"
|
|
home_tgt="$cont_home"
|
|
this_dir="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
|
|
repo_src="$(cd "$this_dir" && git rev-parse --show-toplevel)"
|
|
repo_tgt="$cont_home/spark"
|
|
working_dir="$cont_home"
|
|
|
|
if [[ $mount_existing -eq 0 && -e "$home_src" ]]; then
|
|
# Don't overwrite existing dir (might be mounted in another container)
|
|
_exit "Mount dir '$home_src' already exists (use --mount-existing to mount \
|
|
an existing directory)" 1
|
|
fi
|
|
|
|
mkdir -p "$home_src"
|
|
|
|
if [[ "${#port_mappings[@]}" -eq 0 ]]; then
|
|
port_mappings=("8000:8000" "8001:8001" "8002:8002" "8003:8003")
|
|
fi
|
|
|
|
# Adapt port_mappings into args for `docker run` by prepending "-p " to each
|
|
# item
|
|
declare -a docker_port_args=()
|
|
for port_mapping in "${port_mappings[@]}"; do
|
|
docker_port_args+=("-p $port_mapping")
|
|
done
|
|
|
|
echo "Starting container '$cont_name'
|
|
image_name : $image_name
|
|
cont_name : $cont_name
|
|
home_src : $home_src
|
|
home_tgt : $home_tgt
|
|
repo_src : $repo_src
|
|
repo_tgt : $repo_tgt
|
|
working dir : $working_dir
|
|
ports : ${port_mappings[*]}
|
|
"
|
|
|
|
# DON'T wrap $docker_port_args in quotes. We want bash to split args by
|
|
# whitespace
|
|
docker run \
|
|
--net bridge \
|
|
-h "$host_name" \
|
|
-e "TZ=America/New_York" \
|
|
--cap-add=SYS_PTRACE \
|
|
--security-opt seccomp=unconfined \
|
|
--name "$cont_name" \
|
|
-v "$home_src:$home_tgt" \
|
|
-v "$repo_src:$repo_tgt" \
|
|
-w "$working_dir" \
|
|
${docker_port_args[*]} \
|
|
--shm-size=4024m \
|
|
-t \
|
|
-i \
|
|
-d \
|
|
-e DISPLAY="$DISPLAY" \
|
|
"$image_name"
|
|
|
|
# Add user w/ same uid and gid as the current user outside of docker, so the
|
|
# user inside the docker has file permissions in the mounted dir.
|
|
docker_group="$docker_user"
|
|
docker_uid="$(id -u "$USER")"
|
|
docker_gid="$(id -g "$USER")"
|
|
if [[ -z "$docker_pass" ]]; then
|
|
docker_pass="$docker_user"
|
|
fi
|
|
docker exec -u root -it "$cont_name" "$repo_tgt/docker/sparkd/dev/set_user.sh" \
|
|
--user "$docker_user" \
|
|
--uid "$docker_uid" \
|
|
--group "$docker_group" \
|
|
--gid "$docker_gid" \
|
|
--pass "$docker_pass"
|
|
|
|
# Run same script VS Code runs to initialize the container
|
|
docker exec -u "$docker_user" -it "$cont_name" "$repo_tgt/docker/sparkd/dev/devcontainer_post_create.sh"
|