#!/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 : OR [:,...] 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"