123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- #!/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 -e "--no-app|--service-token|--probe-only"
- }
- check_listen_address_arg() {
- echo "${ARGS}" | grep -q -E -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
- }
- check_plugins_dir() {
- # If plugins dir exists for Docker containers then we will mount it
- # (the context for Docker might be different to that for this script, e.g. when using Docker for Mac)
- if docker run $USERNS_HOST --rm --entrypoint=/bin/sh \
- -v /var/run:/var/run \
- "$SCOPE_IMAGE" -c "test -d /var/run/scope/plugins"; then
- PLUGINS_DIR_EXISTS=true
- fi
- }
- docker_args() {
- echo --privileged $USERNS_HOST --net=host --pid=host \
- -v /var/run/docker.sock:/var/run/docker.sock \
- -v /sys/kernel/debug:/sys/kernel/debug \
- -e CHECKPOINT_DISABLE
- # shellcheck disable=SC2039
- [ -n "${PLUGINS_DIR_EXISTS:-}" ] && echo -v /var/run/scope/plugins:/var/run/scope/plugins
- }
- 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)
- dry_run "$@"
- check_plugins_dir
- if check_docker_for_mac; then
- 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
- 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
- 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
|