mirror of
https://github.com/weaveworks/scope.git
synced 2026-07-17 20:41:51 +00:00
rather than in the host netns. This is to allow the probe to listen for control messages.
324 lines
11 KiB
Bash
Executable File
324 lines
11 KiB
Bash
Executable File
#!/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:-}
|
|
|
|
# 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 --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() {
|
|
# FIXME -p flag only wanted when we are running the app
|
|
# shellcheck disable=SC2046,SC2086
|
|
echo docker run -d --name="$SCOPE_CONTAINER_NAME" $(docker_args) \
|
|
-e SCOPE_HOSTNAME=$(hostname) \
|
|
-p 4040:4040 \
|
|
$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 --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
|