-
Notifications
You must be signed in to change notification settings - Fork 2
/
docker-cli
executable file
·251 lines (206 loc) · 7.51 KB
/
docker-cli
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
#!/usr/bin/env bash
##
## @version $Version: 2022.12.12$
## @author Mauricio Villegas <[email protected]>
## @copyright Copyright(c) 2018-present, Mauricio Villegas <[email protected]>
## @license MIT License
##
FN="${0##*/}";
VERSION="Version: 2022.12.12";
## When script is sourced, only enable bash completion by reusing the one from docker ##
if [ "${BASH_SOURCE[0]}" != "$0" ]; then
if [ $(complete -p | grep -c ' -F _docker docker') != 0 ]; then
_docker_cli () {
local words cword;
_get_comp_words_by_ref -n : words cword;
COMP_WORDS=(docker run "${words[@]:1}");
COMP_CWORD=$((cword+1));
COMP_LINE="${COMP_WORDS[@]}";
_docker;
}
complete -F _docker_cli ${BASH_SOURCE[0]##*/};
fi
return;
fi
set -u;
DGID=$(stat -c "%g" . 2>/dev/null);
[ "$?" != 0 ] && DGID=$(stat -f "%g" .);
GID=$(id -G | awk -v dg=$DGID '{ id=$1; for(n=2;n<=NF;n++) if($n==dg) id=dg; print id; }');
RUN_USER=$(id -u):$GID;
ADD_UID="";
TMP_HOME="";
CLI_DEBUG="no";
## Tool usage message ##
tool_help () {
echo "
$VERSION
DESCRIPTION
===========
This is a simple script intended to ease the execution from the command line
of commands inside docker containers. It makes the execution behave mostly
like any other host command. The main characteristics are:
- Automatic access to user files in the host.
- Generated files owned by the current user.
- Support input from stdin and redirection of stderr and stdout.
- Support of complex command arguments, e.g. spaces, quotes, etc.
The basic system requirement is that docker must be installed and configured
to not require sudo.
A new container is created for each call to $FN and
when execution finishes the container is automatically removed. The command is
executed as the current user from the current working directory. Executed
commands will only work if it is executed from and referencing only files in
the host's /Users, /home, /mnt, /media or /tmp directories.
The interface can also be used to execute external scripts inside the
container. For this to work, the script must be located in a directory
accessible to the container (see above) and be in a directory in your \$DPATH
environment variable or be referenced with its full or relative path.
INSTALLATION
============
By installation it is meant to make the $FN
command runnable from any location. If you cloned the github repository, it is
recommended that you create a symlink from the clone to any directory in your
path, i.e.
cd \$HOME/.local/bin
ln -s path_to_repo/$FN
Copying the script to any directory in your path would also work.
**Bash completion:**
It is very useful to enable bash completion for example to ease typing of
image names and tags. This can be enabled by sourcing the script, though this
functionality depends on the docker bash completion, so this one needs to be
enabled first. To make it permanent you could add to your .bashrc the
following (adapting to your case):
source /usr/share/bash-completion/completions/docker
source \$HOME/.local/bin/$FN
SYNOPSIS
========
$FN [OPTIONS] -- DOCKER_IMAGE COMMAND ARGUMENTS
$FN [OPTIONS] -- DOCKER_IMAGE bash # Prompt becomes USER_ID@CONTAINER_ID$
OPTIONS
=======
--add-uid Run add_uid_to_passwd at startup.
--tmp-home Create /tmp/HOME directory and set HOME=/tmp/HOME.
--user UID:GID (default: current user and current directory's group)
--gpus=GPUS (e.g. all)
--ipc=DOCKER_IPC (e.g. host)
--publish=LIST (e.g. 8080:8080)
--cli-debug Prints the docker run command right before execution.
--help Prints this help message and exits.
--version Prints the version of $FN and exits.
In general most of the options that docker run accepts should work. For more
details check docker run --help.
EXAMPLES
========
# Get the version of some command in the container
$FN -- ubuntu:16.04 sed --version
# Container command as part of a pipe
echo 'hello world!' | $FN -- ubuntu:16.04 grep hello | xargs echo
# Create files in container
echo 'hello world!' | $FN -- ubuntu:16.04 tee test.txt
ls -l test.txt
rm test.txt
# Use of nvidia GPU in container
$FN --runtime=nvidia -- nvidia/cuda:10.1-runtime-ubuntu16.04 nvidia-smi # old format
$FN --gpus=all -- nvidia/cuda:10.1-runtime-ubuntu16.04 nvidia-smi
# Run a jupyter notebook in container
$FN --publish=8888:8888 -- DOCKER_IMAGE bash
export HOME=\$(pwd)
jupyter notebook --ip 0.0.0.0 --no-browser
";
}
[ "$#" = 0 ] && tool_help && exit 0;
## Error function ##
throw_error () { [ "$1" != "" ] && echo "$FN: error: $1"; exit 1; }
## Parse input arguments ##
OPTS=( run );
while [ "$#" -gt 0 ]; do
case "$1" in
--add-uid ) ADD_UID="yes"; ;;
--tmp-home ) TMP_HOME="yes"; ;;
--cli-debug ) CLI_DEBUG="yes"; ;;
--version ) echo ${VERSION#* }; exit 0; ;;
-h | --help ) tool_help; exit 0; ;;
-u | --user ) RUN_USER="$2"; shift; ;;
-- ) shift; break; ;;
* ) OPTS+=( "$1" ); ;;
esac
shift;
done
[ "$#" -lt 2 ] &&
echo "Error: not enough input arguments." &&
echo "Usage: $FN [OPTIONS] -- DOCKER_IMAGE ( COMMAND ARGUMENTS | bash | sh | --help )" &&
exit 1;
IMAGE="$1";
shift;
SQ="'";
CMD=("$@");
CMD=("${CMD[@]//$SQ/$SQ\"$SQ\"$SQ}");
CMD=("${CMD[@]/#/$SQ}");
CMD=("${CMD[@]/%/$SQ}");
## Prepare options for docker ##
OPTS+=( --user "$RUN_USER" );
OPTS+=( --rm );
OPTS+=( --workdir "$(pwd)" );
OPTS+=( --interactive );
OPTS+=( --log-driver none );
if ! ( [ "${#CMD[@]}" = 1 ] && ( [ "${CMD[0]}" = "'bash'" ] || [ "${CMD[0]}" = "'sh'" ] ) ); then
OPTS+=( --env TERM=xterm-256color );
fi
OPTS+=( --entrypoint sh );
[ -d "/Users" ] && OPTS+=( -v /Users:/Users );
[ -d "/home" ] && OPTS+=( -v /home:/home );
[ -d "/mnt" ] && OPTS+=( -v /mnt:/mnt );
[ -d "/media" ] && OPTS+=( -v /media:/media );
[ -d "/tmp" ] && OPTS+=( -v /tmp:/tmp );
if [ "$ADD_UID" = "yes" ]; then
[ $(docker volume ls -q -f name=add_uid_to_passwd | wc -l) = 0 ] &&
docker run \
--rm \
--mount source=add_uid_to_passwd,destination=/opt/ops/ \
-it omniushq/add_uid_to_passwd:1.1.1;
ADD_UID="/opt/ops/add_uid_to_passwd $(id -u -n);";
OPTS+=( --mount source=add_uid_to_passwd,destination=/opt/ops/ );
fi
TPATH="";
if [ "$TMP_HOME" = "yes" ]; then
mkdir -p /tmp/HOME;
OPTS+=( -e HOME=/tmp/HOME );
TPATH='PATH="/tmp/HOME/.local/bin:$PATH";';
fi
if [ -z "${DPATH+x}" ]; then
DPATH="";
else
DPATH="PATH=\"$DPATH:\$PATH\" ";
fi
## Run docker ##
if [ "${#CMD[@]}" = 1 ] && [ "${CMD[0]}" = "'bash'" ]; then
[ "$DPATH" != "" ] && DPATH="export $DPATH;";
TMP=$(mktemp $FN.init_XXXXXX);
_rm_init_tmp () { rm -f "$TMP"; };
trap _rm_init_tmp EXIT;
echo "
$ADD_UID
$DPATH
$TPATH
if [ \$(id -u) = 0 ]; then
PS1='root@\h# ';
else
PS1=\$(id -u)'@\h$ ';
fi
alias ls='ls --color=auto';
rm $TMP;
" > $TMP;
CMD=( docker "${OPTS[@]}" -t "$IMAGE" \
-c "bash --init-file $TMP" );
elif [ "${#CMD[@]}" = 1 ] && [ "${CMD[0]}" = "'sh'" ]; then
CMD=( docker "${OPTS[@]}" -t "$IMAGE" \
-c "$ADD_UID$DPATH${CMD[*]}" );
else
CMD=( docker "${OPTS[@]}" "$IMAGE" \
-c "$ADD_UID$DPATH${CMD[*]}" );
fi
if [ "$CLI_DEBUG" = "yes" ]; then
CLI_DEBUG=("${CMD[@]}");
CLI_DEBUG[-1]="\"${CLI_DEBUG[-1]}\"";
echo "$FN: running command: ${CLI_DEBUG[@]}";
fi
"${CMD[@]}";