|
| 1 | +#!/bin/bash |
| 2 | +set -eEuo pipefail |
| 3 | + |
| 4 | +PROG="$(basename "$0")" |
| 5 | +readonly PROG_VERSION='2.4.0-dev' |
| 6 | + |
| 7 | +READLINK_CMD=readlink |
| 8 | +if command -v greadlink >/dev/null; then |
| 9 | + READLINK_CMD=greadlink |
| 10 | +fi |
| 11 | + |
| 12 | +################################################################################ |
| 13 | +# util functions |
| 14 | +################################################################################ |
| 15 | + |
| 16 | +# NOTE: $'foo' is the escape sequence syntax of bash |
| 17 | +readonly ec=$'\033' # escape char |
| 18 | +readonly eend=$'\033[0m' # escape end |
| 19 | +readonly nl=$'\n' # new line |
| 20 | + |
| 21 | +redEcho() { |
| 22 | + # -t check: is a terminal device? |
| 23 | + [ -t 1 ] && echo "${ec}[1;31m$*$eend" || echo "$*" |
| 24 | +} |
| 25 | + |
| 26 | +die() { |
| 27 | + redEcho "Error: $*" 1>&2 |
| 28 | + exit 1 |
| 29 | +} |
| 30 | + |
| 31 | +usage() { |
| 32 | + local -r exit_code="${1:-0}" |
| 33 | + (($# > 0)) && shift |
| 34 | + # shellcheck disable=SC2015 |
| 35 | + [ "$exit_code" != 0 ] && local -r out=/dev/stderr || local -r out=/dev/stdout |
| 36 | + |
| 37 | + (($# > 0)) && redEcho "$*$nl" >$out |
| 38 | + |
| 39 | + cat >$out <<EOF |
| 40 | +Usage: ${PROG} [OPTION]... command [command-args]... |
| 41 | +
|
| 42 | +Copy the command into docker container |
| 43 | +and run the command in container. |
| 44 | +
|
| 45 | +Example: |
| 46 | + ${PROG} -c container_foo command_copied_into_container command_arg1 |
| 47 | +
|
| 48 | +docker options: |
| 49 | + -c, --container destination docker container |
| 50 | + -u, --docker-user docker username or UID to run command |
| 51 | + optional, docker default is (maybe) root user |
| 52 | + -w, --workdir working directory inside the container |
| 53 | + optional, docker default is (maybe) root dir |
| 54 | + -t, --tmpdir tmp dir in docker to copy command |
| 55 | + optional, default is /tmp |
| 56 | +
|
| 57 | +run options: |
| 58 | + -v, --verbose show operation step infos |
| 59 | +
|
| 60 | +miscellaneous: |
| 61 | + -h, --help display this help and exit |
| 62 | + -V, --version display version information and exit |
| 63 | +EOF |
| 64 | + |
| 65 | + exit "$exit_code" |
| 66 | +} |
| 67 | + |
| 68 | +progVersion() { |
| 69 | + echo "$PROG $PROG_VERSION" |
| 70 | + exit |
| 71 | +} |
| 72 | + |
| 73 | +################################################################################ |
| 74 | +# parse options |
| 75 | +################################################################################ |
| 76 | + |
| 77 | +container_name= |
| 78 | +docker_user= |
| 79 | +docker_workdir= |
| 80 | +docker_tmpdir=/tmp |
| 81 | +verbose=false |
| 82 | +declare -a args=() |
| 83 | + |
| 84 | +while (($# > 0)); do |
| 85 | + case "$1" in |
| 86 | + -c | --container) |
| 87 | + container_name="$2" |
| 88 | + shift 2 |
| 89 | + ;; |
| 90 | + -u | --docker-user) |
| 91 | + docker_user="$2" |
| 92 | + shift 2 |
| 93 | + ;; |
| 94 | + -w | --workdir) |
| 95 | + docker_workdir="$2" |
| 96 | + shift 2 |
| 97 | + ;; |
| 98 | + -t | --tmpdir) |
| 99 | + docker_tmpdir="$2" |
| 100 | + shift 2 |
| 101 | + ;; |
| 102 | + -v | --verbose) |
| 103 | + verbose=true |
| 104 | + shift |
| 105 | + ;; |
| 106 | + -h | --help) |
| 107 | + usage |
| 108 | + ;; |
| 109 | + -V | --version) |
| 110 | + progVersion |
| 111 | + ;; |
| 112 | + --) |
| 113 | + shift |
| 114 | + args=(${args[@]:+"${args[@]}"} "$@") |
| 115 | + break |
| 116 | + ;; |
| 117 | + -*) |
| 118 | + usage 2 "${PROG}: unrecognized option '$1'" |
| 119 | + ;; |
| 120 | + *) |
| 121 | + # if not option, treat all follow args as command |
| 122 | + args=(${args[@]:+"${args[@]}"} "$@") |
| 123 | + break |
| 124 | + ;; |
| 125 | + esac |
| 126 | +done |
| 127 | + |
| 128 | +################################################################################ |
| 129 | +# biz logic |
| 130 | +################################################################################ |
| 131 | + |
| 132 | +######################################## |
| 133 | +# check docker command existence |
| 134 | +######################################## |
| 135 | + |
| 136 | +command -v docker &>/dev/null || die 'docker command not found!' |
| 137 | + |
| 138 | +######################################## |
| 139 | +# prepare vars for docker operation |
| 140 | +######################################## |
| 141 | + |
| 142 | +readonly specified_run_command="${args[0]}" |
| 143 | +run_command="$specified_run_command" |
| 144 | +if [ ! -f "$specified_run_command" ]; then |
| 145 | + which "$specified_run_command" &>/dev/null || |
| 146 | + die "specified command not exists and not found in PATH: $specified_run_command" |
| 147 | + |
| 148 | + run_command="$(which "$specified_run_command")" |
| 149 | +fi |
| 150 | +run_command="$($READLINK_CMD -f "$run_command")" |
| 151 | + |
| 152 | +run_timestamp="$(date "+%Y%m%d_%H%M%S")" |
| 153 | +readonly uuid="${PROG}_${run_timestamp}_${$}_${RANDOM}" |
| 154 | + |
| 155 | +run_command_base_name="$(basename "$run_command")" |
| 156 | +readonly run_command_dir_in_docker="$docker_tmpdir/$uuid" |
| 157 | +readonly run_command_in_docker="$run_command_dir_in_docker/$run_command_base_name" |
| 158 | + |
| 159 | +cleanupWhenExit() { |
| 160 | + # remove tmp dir in docker by root user |
| 161 | + docker exec "${container_name}" rm -rf -- "$run_command_dir_in_docker" &>/dev/null |
| 162 | +} |
| 163 | +trap "cleanupWhenExit" EXIT |
| 164 | + |
| 165 | +######################################## |
| 166 | +# docker operations |
| 167 | +######################################## |
| 168 | + |
| 169 | +logAndRun() { |
| 170 | + $verbose && echo "[$PROG] $*" 1>&2 |
| 171 | + "$@" |
| 172 | +} |
| 173 | + |
| 174 | +logAndRun docker exec ${docker_user:+"--user=$docker_user"} "$container_name" \ |
| 175 | + mkdir -p -- "$run_command_dir_in_docker" |
| 176 | +logAndRun docker cp "$run_command" "$container_name:$run_command_in_docker" |
| 177 | +logAndRun docker exec ${docker_user:+"--user=$docker_user"} "$container_name" \ |
| 178 | + chmod +x "$run_command_in_docker" |
| 179 | + |
| 180 | +logAndRun docker exec -i -t \ |
| 181 | + ${docker_user:+"--user=$docker_user"} \ |
| 182 | + ${docker_workdir:+"--workdir=$docker_workdir"} \ |
| 183 | + "$container_name" \ |
| 184 | + "$run_command_in_docker" "${args[@]:1:${#args[@]}}" |
0 commit comments