forked from weaveworks/scope
-
Notifications
You must be signed in to change notification settings - Fork 0
/
scope
executable file
·324 lines (287 loc) · 11.4 KB
/
scope
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
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
#!/bin/sh
set -eu
ARGS="$*"
SCRIPT_VERSION="(unreleased version)"
if [ "$SCRIPT_VERSION" = "(unreleased version)" ]; then
IMAGE_VERSION=latest
else
IMAGE_VERSION="$SCRIPT_VERSION"
fi
IMAGE_VERSION=${VERSION:-$IMAGE_VERSION}
DOCKERHUB_USER=${DOCKERHUB_USER:-weaveworks}
SCOPE_IMAGE_NAME="$DOCKERHUB_USER/scope"
SCOPE_IMAGE="$SCOPE_IMAGE_NAME:$IMAGE_VERSION"
# Careful: it's easy to operate on (e.g. stop) the wrong scope instance
# when SCOPE{_APP,}_CONTAINER_NAME values differ between runs. Handle
# with care.
SCOPE_CONTAINER_NAME="${SCOPE_CONTAINER_NAME:-weavescope}"
SCOPE_APP_CONTAINER_NAME="${SCOPE_APP_CONTAINER_NAME:-weavescope-app}"
IP_REGEXP="[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"
IP_ADDR_CMD="find /sys/class/net -type l | xargs -n1 basename | grep -vE 'docker|veth|lo' | \
xargs -n1 ip addr show | grep inet | awk '{ print \$2 }' | grep -oE '$IP_REGEXP'"
LISTENING_IP_ADDR_CMD="for I in \$( $IP_ADDR_CMD ); do if curl -m 1 -s \${I}:4040 > /dev/null ; then echo \${I}; fi; done"
WEAVESCOPE_DOCKER_ARGS=${WEAVESCOPE_DOCKER_ARGS:-}
# When docker daemon is running with User Namespace enabled, this tool will run into errors:
# "Privileged mode is incompatible with user namespaces" for `docker run --privileged`
# "Cannot share the host's network namespace when user namespaces are enabled" for `docker run --net=host`
# To avoid above errors, use `--userns=host` option to let container use host User Namespace.
# This option(saved in $USERNS_HOST) will be inserted ONLY IF docker support `--userns` option.
USERNS_HOST=""
docker run --help | grep -q -- --userns && USERNS_HOST="--userns=host"
usage() {
name=$(basename "$0")
cat >&2 <<-EOF
Usage:
$name launch {OPTIONS} {PEERS} - Launch Scope
$name stop - Stop Scope
$name command - Print the docker command used to start Scope
$name help - Print usage info
$name version - Print version info
PEERS are of the form HOST[:PORT]
HOST may be an ip or hostname.
PORT defaults to 4040.
Launch options:
EOF
docker run --rm --entrypoint=/home/weave/scope "$SCOPE_IMAGE" -h >&2
}
usage_and_die() {
usage
exit 1
}
[ $# -gt 0 ] || usage_and_die
COMMAND=$1
shift 1
check_docker_access() {
# Extract socket path
DOCKER_SOCK_FILE=""
if [ -z "${DOCKER_HOST+x}" ]; then
DOCKER_SOCK_FILE="/var/run/docker.sock"
else
WITHOUT_PREFIX="${DOCKER_HOST#unix://}"
if [ "$WITHOUT_PREFIX" != "$DOCKER_HOST" ]; then
DOCKER_SOCK_FILE="$WITHOUT_PREFIX"
fi
fi
# shellcheck disable=SC2166
if [ \( -n "$DOCKER_SOCK_FILE" \) -a \( ! -w "$DOCKER_SOCK_FILE" \) ]; then
echo "ERROR: cannot write to docker socket: $DOCKER_SOCK_FILE" >&2
echo "change socket permissions or try using sudo" >&2
exit 1
fi
}
# - The image embeds the weave script & Docker 1.13.1 client (mimicking a 1.10 client)
# - Weave needs 1.10.0 now (image pulling changes)
MIN_DOCKER_VERSION=1.10.0
check_docker_version() {
if ! DOCKER_VERSION=$(docker -v | sed -n 's%^Docker version \([0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\).*$%\1%p') \
|| [ -z "$DOCKER_VERSION" ]; then
echo "ERROR: Unable to parse docker version" >&2
exit 1
fi
DOCKER_VERSION_MAJOR=$(echo "$DOCKER_VERSION" | cut -d. -f 1)
DOCKER_VERSION_MINOR=$(echo "$DOCKER_VERSION" | cut -d. -f 2)
DOCKER_VERSION_PATCH=$(echo "$DOCKER_VERSION" | cut -d. -f 3)
MIN_DOCKER_VERSION_MAJOR=$(echo "$MIN_DOCKER_VERSION" | cut -d. -f 1)
MIN_DOCKER_VERSION_MINOR=$(echo "$MIN_DOCKER_VERSION" | cut -d. -f 2)
MIN_DOCKER_VERSION_PATCH=$(echo "$MIN_DOCKER_VERSION" | cut -d. -f 3)
# shellcheck disable=SC2166
if [ \( "$DOCKER_VERSION_MAJOR" -lt "$MIN_DOCKER_VERSION_MAJOR" \) -o \
\( "$DOCKER_VERSION_MAJOR" -eq "$MIN_DOCKER_VERSION_MAJOR" -a \
\( "$DOCKER_VERSION_MINOR" -lt "$MIN_DOCKER_VERSION_MINOR" -o \
\( "$DOCKER_VERSION_MINOR" -eq "$MIN_DOCKER_VERSION_MINOR" -a \
\( "$DOCKER_VERSION_PATCH" -lt "$MIN_DOCKER_VERSION_PATCH" \) \) \) \) ]; then
echo "ERROR: scope requires Docker version $MIN_DOCKER_VERSION or later; you are running $DOCKER_VERSION" >&2
exit 1
fi
}
check_probe_only() {
echo "${ARGS}" | grep -q -E "\-\-no\-app|\-\-service\-token|\-\-probe\-only"
}
check_listen_address_arg() {
echo "${ARGS}" | grep -q -E "\-\-app\.http\.address"
}
check_docker_for_mac() {
[ "$(uname)" = "Darwin" ] \
&& [ -S /var/run/docker.sock ] \
&& [ ! "${DOCKER_HOST+x}" = x ] \
&& [ "${HOME+x}" = x ] \
&& [ -d "${HOME}/Library/Containers/com.docker.docker/Data" ]
}
# Check that a container named $1 with image $2 is not running
check_not_running() {
case $(docker inspect --format='{{.State.Running}} {{.Config.Image}}' "$1" 2>/dev/null) in
"true $2")
echo "$1 is already running." >&2
exit 1
;;
"true $2:"*)
echo "$1 is already running." >&2
exit 1
;;
"false $2")
docker rm "$1" >/dev/null
;;
"false $2:"*)
docker rm "$1" >/dev/null
;;
true*)
echo "Found another running container named '$1'. Aborting." >&2
exit 1
;;
false*)
echo "Found another container named '$1'. Aborting." >&2
exit 1
;;
esac
}
create_plugins_dir() {
# Docker for Mac (as of beta18) looks for this path on VM first, and when it doesn't
# find it there, it assumes the user referes to the path on Mac OS. Firstly, in most
# cases user won't have /var/run/scope/plugins on their Mac OS filesystem, and
# secondly Docker for Mac would have to be configured to share this path. The result
# of this "feature" is an error message: "The path /var/run/scope/plugins
# is not shared from OS X and does not belong to the system."
# In any case, creating /var/run/scope/plugins on Mac OS would not work, as domain
# sockets do not cross VM boundaries. We need this directory to exits on the VM.
docker run $USERNS_HOST --rm --entrypoint=/bin/sh \
-v /var/run:/var/run \
"$SCOPE_IMAGE" -c "mkdir -p /var/run/scope/plugins"
}
docker_args() {
echo --privileged $USERNS_HOST --net=host --pid=host \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /var/run/scope/plugins:/var/run/scope/plugins \
-v /sys/kernel/debug:/sys/kernel/debug \
-e CHECKPOINT_DISABLE
}
launch_command() {
# shellcheck disable=SC2046,SC2086
echo docker run -d --name="$SCOPE_CONTAINER_NAME" $(docker_args) \
$WEAVESCOPE_DOCKER_ARGS "$SCOPE_IMAGE" --probe.docker=true
}
launch_docker4mac_app_command() {
# shellcheck disable=SC2086
echo docker run -d --name="$SCOPE_APP_CONTAINER_NAME" \
-e CHECKPOINT_DISABLE \
-p 0.0.0.0:4040:4040 \
$WEAVESCOPE_DOCKER_ARGS "$SCOPE_IMAGE" --no-probe
}
launch() {
check_not_running "$SCOPE_CONTAINER_NAME" "$SCOPE_IMAGE_NAME"
docker rm -f "$SCOPE_CONTAINER_NAME" >/dev/null 2>&1 || true
$(launch_command) "$@"
echo "Scope probe started"
}
print_app_endpoints() {
HOST_SUFFIX=""
if [ -n "${DOCKER_HOST+x}" ]; then
DOCKER_HOSTNAME=$(run_in_scope_container hostname)
HOST_SUFFIX=" of host $DOCKER_HOSTNAME"
fi
echo "Weave Scope is listening at the following URL(s)${HOST_SUFFIX}:" >&2
for ip in "$@"; do
echo " * http://$ip:4040/" >&2
done
}
dry_run() {
# Do a dry run of scope in the foreground, so it can parse args etc
# avoiding the entrypoint script in the process.
# shellcheck disable=SC2046
docker run --rm --entrypoint=/home/weave/scope $(docker_args) "$SCOPE_IMAGE" --dry-run "$@"
}
run_in_scope_container() {
docker run --rm $USERNS_HOST --net=host --entrypoint /bin/sh "$SCOPE_IMAGE" -c "$1"
}
# Wait for the scope app to start listening on localhost:4040
wait_for_http() {
for seconds in $(seq 5); do
if run_in_scope_container "curl -m 1 -s localhost:4040" >/dev/null; then
break
fi
sleep 1
done
if [ "$seconds" -eq 5 ]; then
echo "The Scope App is not responding. Consult the container logs for further details."
exit 1
fi
}
check_docker_access
check_docker_version
case "$COMMAND" in
command)
# Most systems should have printf, but the %q specifier isn't mandated by posix
# and can't be guaranteed. Since this is mainly a cosmetic output and the alternative
# is not making any attempt to do escaping at all, we might as well try.
# shellcheck disable=SC2039
quoted=$(printf '%q ' "$@" 2>/dev/null || true)
# printf %q behaves oddly with zero args (it acts as though it received one empty arg)
# so we ignore that case.
if [ -z "$quoted" ] || [ $# -eq 0 ]; then
quoted="$*"
fi
echo "$(launch_command) $quoted"
;;
version)
docker run --rm --entrypoint=/home/weave/scope "$SCOPE_IMAGE" --mode=version
;;
-h | help | -help | --help)
usage
;;
launch)
if check_docker_for_mac; then
create_plugins_dir
if check_probe_only; then
launch "$@"
exit
fi
# Docker for Mac (as of beta9) does not ship vmnet driver and
# thereby only access container ports via a tunnel, preventing
# access to host ports of the VM.
# - https://github.com/weaveworks/scope/issues/1411
# - https://forums.docker.com/t/ports-in-host-network-namespace-are-not-accessible/10789
dry_run "$@"
if check_listen_address_arg; then
echo "--app.http.address argument not supported on Docker for Mac" >&2
exit 1
fi
check_not_running "$SCOPE_APP_CONTAINER_NAME" "$SCOPE_IMAGE_NAME"
check_not_running "$SCOPE_CONTAINER_NAME" "$SCOPE_IMAGE_NAME"
docker rm -f "$SCOPE_APP_CONTAINER_NAME" >/dev/null 2>&1 || true
CONTAINER=$($(launch_docker4mac_app_command) "$@")
echo "Scope probe started"
app_ip=$(docker inspect -f '{{.NetworkSettings.IPAddress}}' "${CONTAINER}")
docker rm -f "$SCOPE_CONTAINER_NAME" >/dev/null 2>&1 || true
# shellcheck disable=SC2091
CONTAINER=$($(launch_command --no-app "$@" "${app_ip}:4040"))
print_app_endpoints "localhost"
exit
fi
dry_run "$@"
launch "$@"
if ! check_probe_only; then
if check_listen_address_arg; then
echo "Weave Scope is listening at the address specified with --app.http.address" >&2
else
wait_for_http
IP_ADDRS=$(run_in_scope_container "$LISTENING_IP_ADDR_CMD")
# shellcheck disable=SC2086
print_app_endpoints $IP_ADDRS
fi
fi
;;
stop)
[ $# -eq 0 ] || usage_and_die
if docker inspect "$SCOPE_CONTAINER_NAME" >/dev/null 2>&1; then
docker stop "$SCOPE_CONTAINER_NAME" >/dev/null
fi
if check_docker_for_mac; then
if docker inspect "$SCOPE_APP_CONTAINER_NAME" >/dev/null 2>&1; then
docker stop "$SCOPE_APP_CONTAINER_NAME" >/dev/null
fi
fi
;;
*)
echo "Unknown scope command '$COMMAND'" >&2
usage_and_die
;;
esac