875 lines
30 KiB
Bash
Executable File
875 lines
30 KiB
Bash
Executable File
#!/bin/sh
|
|
set -e
|
|
|
|
[ -n "$WEAVE_DEBUG" ] && set -x
|
|
|
|
SCRIPT_VERSION="(unreleased version)"
|
|
if [ "$SCRIPT_VERSION" = "(unreleased version)" ] ; then
|
|
IMAGE_VERSION=latest
|
|
else
|
|
IMAGE_VERSION=$SCRIPT_VERSION
|
|
fi
|
|
IMAGE_VERSION=${VERSION:-$IMAGE_VERSION}
|
|
|
|
# These are potentially used for remote execs, and have defaults, so
|
|
# we introduce them here
|
|
DOCKERHUB_USER=weaveworks
|
|
BASE_EXEC_IMAGE=$DOCKERHUB_USER/weaveexec
|
|
EXEC_IMAGE=$BASE_EXEC_IMAGE:$IMAGE_VERSION
|
|
|
|
usage() {
|
|
echo "Usage:"
|
|
echo "weave setup"
|
|
echo "weave launch [-password <password>] [-nickname <nickname>] <peer> ..."
|
|
echo "weave launch-dns <cidr>"
|
|
echo "weave connect <peer>"
|
|
echo "weave forget <peer>"
|
|
echo "weave run [--with-dns] <cidr> [<cidr> ...] <docker run args> ..."
|
|
echo "weave start <cidr> [<cidr> ...] <container_id>"
|
|
echo "weave attach <cidr> [<cidr> ...] <container_id>"
|
|
echo "weave detach <cidr> [<cidr> ...] <container_id>"
|
|
echo "weave expose <cidr> [<cidr> ...] [-h <fqdn>]"
|
|
echo "weave hide <cidr> [<cidr> ...]"
|
|
echo "weave ps [<container_id> ...]"
|
|
echo "weave status"
|
|
echo "weave version"
|
|
echo "weave stop"
|
|
echo "weave stop-dns"
|
|
echo "weave reset"
|
|
echo
|
|
echo "where <peer> is of the form <ip_address_or_fqdn>[:<port>], and"
|
|
echo " <cidr> is of the form <ip_address>/<routing_prefix_length>"
|
|
exit 1
|
|
}
|
|
|
|
exec_remote() {
|
|
docker run --rm --privileged --net=host \
|
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
|
-v /proc:/hostproc \
|
|
-e PROCFS=/hostproc \
|
|
-e VERSION \
|
|
-e WEAVE_DEBUG \
|
|
-e WEAVE_DOCKER_ARGS \
|
|
-e WEAVEDNS_DOCKER_ARGS \
|
|
-e WEAVE_PASSWORD \
|
|
-e WEAVE_PORT \
|
|
-e WEAVE_CONTAINER_NAME \
|
|
-e DOCKER_BRIDGE \
|
|
$EXEC_IMAGE --local "$@"
|
|
}
|
|
|
|
check_docker_version() {
|
|
if ! DOCKER_VERSION=$(docker -v | sed -n -e 's|^Docker version \([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*|\1|p') || [ -z "$DOCKER_VERSION" ] ; then
|
|
echo "ERROR: Unable to parse docker version" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# guard against https://github.com/docker/docker/issues/8632
|
|
if [ "$DOCKER_VERSION" = "1.3.0" ] ; then
|
|
echo "You are running Docker version $DOCKER_VERSION, which contains a bug that prevents weave from working properly. Please upgrade." >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
is_cidr() {
|
|
# The regexp here is far from precise, but good enough.
|
|
echo "$1" | grep -E '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/[0-9]{1,2}$' >/dev/null
|
|
}
|
|
|
|
validate_cidr() {
|
|
if ! is_cidr "$1" ; then
|
|
echo "Invalid CIDR: $1" >&2
|
|
echo "CIDR must of be of form <ip_address>/<routing_prefix_length>, e.g. 10.2.1.1/24" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
collect_cidr_args() {
|
|
CIDR_ARGS=""
|
|
CIDR_COUNT=0
|
|
while is_cidr "$1"; do
|
|
CIDR_ARGS="$CIDR_ARGS $1"
|
|
CIDR_COUNT=$((CIDR_COUNT + 1))
|
|
shift 1
|
|
done
|
|
if [ $CIDR_COUNT -eq 0 ]; then
|
|
[ $# -gt 0 ] && validate_cidr "$1" || usage
|
|
fi
|
|
}
|
|
|
|
# "run" is a special case because we want to use docker directly,
|
|
# rather than the docker in $EXEC_IMAGE remotely. That's because we
|
|
# are passing arbitrary arguments on to docker run, and we can't rely
|
|
# on our baked-in docker to support those arguments.
|
|
if [ "$1" = "run" ]; then
|
|
shift 1
|
|
check_docker_version
|
|
if [ "$1" = "--with-dns" ]; then
|
|
shift 1
|
|
DNS_ARGS=$(exec_remote dns-args "$@")
|
|
fi
|
|
collect_cidr_args "$@"
|
|
shift $CIDR_COUNT
|
|
CONTAINER=$(docker run $DNS_ARGS -d "$@")
|
|
exec_remote attach $CIDR_ARGS $CONTAINER
|
|
echo $CONTAINER
|
|
exit 0
|
|
elif [ "$1" != "--local" ]; then
|
|
exec_remote "$@"
|
|
exit $?
|
|
fi
|
|
|
|
shift 1
|
|
|
|
BASE_IMAGE=$DOCKERHUB_USER/weave
|
|
BASE_DNS_IMAGE=$DOCKERHUB_USER/weavedns
|
|
IMAGE=$BASE_IMAGE:$IMAGE_VERSION
|
|
DNS_IMAGE=$BASE_DNS_IMAGE:$IMAGE_VERSION
|
|
|
|
PROCFS=${PROCFS:-/proc}
|
|
DOCKER_BRIDGE=${DOCKER_BRIDGE:-docker0}
|
|
CONTAINER_NAME=${WEAVE_CONTAINER_NAME:-weave}
|
|
DNS_CONTAINER_NAME=weavedns
|
|
BRIDGE=weave
|
|
CONTAINER_IFNAME=ethwe
|
|
MTU=65535
|
|
PORT=${WEAVE_PORT:-6783}
|
|
HTTP_PORT=6784
|
|
DNS_HTTP_PORT=6785
|
|
|
|
######################################################################
|
|
# general helpers; independent of docker and weave
|
|
######################################################################
|
|
|
|
# utility function to check whether a command can be executed by the shell
|
|
# see http://stackoverflow.com/questions/592620/how-to-check-if-a-program-exists-from-a-bash-script
|
|
command_exists () {
|
|
command -v $1 >/dev/null 2>&1
|
|
}
|
|
|
|
run_iptables() {
|
|
# -w is recent addition to iptables
|
|
if [ -z "$CHECKED_IPTABLES_W" ] ; then
|
|
if iptables -S -w >/dev/null 2>&1 ; then
|
|
IPTABLES_W=-w
|
|
fi
|
|
CHECKED_IPTABLES_W=1
|
|
fi
|
|
|
|
iptables $IPTABLES_W "$@"
|
|
}
|
|
|
|
# Add a rule to iptables, if it doesn't exist already
|
|
add_iptables_rule() {
|
|
IPTABLES_TABLE="$1"
|
|
shift 1
|
|
if ! run_iptables -t $IPTABLES_TABLE -C "$@" >/dev/null 2>&1
|
|
then
|
|
run_iptables -t $IPTABLES_TABLE -A "$@" >/dev/null
|
|
fi
|
|
}
|
|
|
|
# Delete a rule from iptables, if it exist
|
|
delete_iptables_rule() {
|
|
IPTABLES_TABLE="$1"
|
|
shift 1
|
|
if run_iptables -t $IPTABLES_TABLE -C "$@" >/dev/null 2>&1
|
|
then
|
|
run_iptables -t $IPTABLES_TABLE -D "$@" >/dev/null
|
|
fi
|
|
}
|
|
|
|
# Configure the ARP cache parameters for the given interface. This
|
|
# makes containers react more quickly to a change in the MAC address
|
|
# associated with an IP address.
|
|
configure_arp_cache() {
|
|
$2 sh -c "echo 5 >/proc/sys/net/ipv4/neigh/$1/base_reachable_time &&
|
|
echo 2 >/proc/sys/net/ipv4/neigh/$1/delay_first_probe_time &&
|
|
echo 1 >/proc/sys/net/ipv4/neigh/$1/ucast_solicit"
|
|
}
|
|
|
|
# Send out an unsolicated ARP response to update ARP cache entries
|
|
# accross the weave network. We do this in addition to
|
|
# configure_arp_cache because a) with those ARP cache settings it
|
|
# still takes a few seconds to correct a stale ARP mapping, and b)
|
|
# there is a kernel bug that means that the base_reachable_time
|
|
# setting is not promptly obeyed
|
|
# (<https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=4bf6980dd0328530783fd657c776e3719b421d30>>).
|
|
arp_update() {
|
|
# It's not the end of the world if this doesn't run - we configure
|
|
# ARP caches so that stale entries will be noticed quickly.
|
|
if command_exists arping ; then
|
|
$3 arping -U -q -I $1 -c 1 ${2%/*}
|
|
fi
|
|
}
|
|
|
|
# Generate a random MAC value
|
|
random_mac() {
|
|
# In the first byte of the MAC, the 'multicast' bit should be
|
|
# clear and 'locally administered' bit should be set. All other
|
|
# bits should be random.
|
|
od -txC -An -N6 /dev/urandom | sed 's|^ ||;s| |:|;s|[0-3]:|2:|;s|[4-7]:|6:|;s|[89ab]:|a:|;s|[c-f]:|e:|;s| |:|g'
|
|
}
|
|
|
|
######################################################################
|
|
# weave and docker specific helpers
|
|
######################################################################
|
|
|
|
create_bridge() {
|
|
[ ! -d /sys/class/net/$BRIDGE ] && {
|
|
ip link add name $BRIDGE type bridge
|
|
# Set a random MAC address on the bridge. Current Linux
|
|
# kernels already do this when creating a bridge, but there
|
|
# are rumours it was not always so.
|
|
ip link set dev $BRIDGE address $(random_mac)
|
|
# Attempting to set the bridge MTU to a high value directly
|
|
# fails. Bridges take the lowest MTU of their interfaces. So
|
|
# instead we create a temporary interface with the desired
|
|
# MTU, attach that to the bridge, and then remove it again.
|
|
ip link add name v${CONTAINER_IFNAME}du mtu $MTU type dummy
|
|
ip link set dev v${CONTAINER_IFNAME}du master $BRIDGE
|
|
ip link del dev v${CONTAINER_IFNAME}du
|
|
# Work around the situation where there are no rules allowing traffic
|
|
# across our bridge. E.g. ufw
|
|
add_iptables_rule filter FORWARD -i $BRIDGE -o $BRIDGE -j ACCEPT
|
|
# create a chain for masquerading
|
|
run_iptables -t nat -N WEAVE >/dev/null 2>&1 || true
|
|
add_iptables_rule nat POSTROUTING -j WEAVE
|
|
}
|
|
if [ ! "$1" = "--without-ethtool" ] ; then
|
|
ethtool -K $BRIDGE tx off >/dev/null
|
|
fi
|
|
|
|
ip link set dev $BRIDGE up
|
|
|
|
# Configure the ARP cache parameters on the bridge interface for
|
|
# the sake of 'weave expose'
|
|
configure_arp_cache $BRIDGE
|
|
}
|
|
|
|
destroy_bridge() {
|
|
if [ -d /sys/class/net/$BRIDGE ] ; then
|
|
ip link del dev $BRIDGE
|
|
fi
|
|
run_iptables -t filter -D FORWARD -i $BRIDGE -o $BRIDGE -j ACCEPT 2>/dev/null || true
|
|
run_iptables -t nat -F WEAVE >/dev/null 2>&1 || true
|
|
run_iptables -t nat -D POSTROUTING -j WEAVE >/dev/null 2>&1 || true
|
|
run_iptables -t nat -X WEAVE >/dev/null 2>&1 || true
|
|
}
|
|
|
|
docker_bridge_ip() {
|
|
DOCKER_BRIDGE_IP=$(ip -4 addr show dev $DOCKER_BRIDGE | grep -m1 -o 'inet [.0-9]*')
|
|
DOCKER_BRIDGE_IP=${DOCKER_BRIDGE_IP#inet }
|
|
}
|
|
|
|
# the following borrows from https://github.com/jpetazzo/pipework
|
|
|
|
# Set $CONTAINER_PID to the PID of container $1, $LOCAL_IFNAME and
|
|
# $GUEST_IFNAME to suitable names for two ends of a veth pair, specific
|
|
# to the container, and execute args $2 $3 ... as a command. If an error
|
|
# is caused by container dying, swallow output from error.
|
|
with_container_netns () {
|
|
CONTAINER="$1"
|
|
CONTAINER_PID=$(docker inspect --format='{{.State.Pid}}' $CONTAINER)
|
|
|
|
if [ "$CONTAINER_PID" = 0 ] ; then
|
|
echo "Container $CONTAINER not running." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [ "$CONTAINER_PID" = "<no value>" ] ; then
|
|
echo "Container $CONTAINER unknown to Docker." >&2
|
|
exit 1
|
|
fi
|
|
|
|
LOCAL_IFNAME="v${CONTAINER_IFNAME}pl${CONTAINER_PID}"
|
|
GUEST_IFNAME="v${CONTAINER_IFNAME}pg${CONTAINER_PID}"
|
|
IP_TMPOUT=/tmp/weave_ip_out_$$
|
|
IP_TMPERR=/tmp/weave_ip_err_$$
|
|
rm -f $IP_TMPOUT $IP_TMPERR
|
|
|
|
# Run the wrapped command
|
|
STATUS=0
|
|
shift 1
|
|
if ! "$@" >$IP_TMPOUT 2>$IP_TMPERR ; then
|
|
STATUS=1
|
|
if ! kill -0 "$CONTAINER_PID" 2>/dev/null ; then
|
|
echo "Container $CONTAINER died" >&2
|
|
else
|
|
echo "Failure during network configuration for container $CONTAINER:" >&2
|
|
cat $IP_TMPERR >&2
|
|
fi
|
|
else
|
|
cat $IP_TMPOUT
|
|
cat $IP_TMPERR >&2
|
|
fi
|
|
rm -f $IP_TMPOUT $IP_TMPERR
|
|
return $STATUS
|
|
}
|
|
|
|
# Execute arguments as a command within the network namespace of $CONTAINER_PID
|
|
netnsenter() {
|
|
nsenter --net=$PROCFS/$CONTAINER_PID/ns/net "$@"
|
|
}
|
|
|
|
connect_container_to_bridge() {
|
|
if [ -h "$PROCFS/$CONTAINER_PID/ns/net" -a -h "$PROCFS/$$/ns/net" -a "$(readlink $PROCFS/$CONTAINER_PID/ns/net)" = "$(readlink $PROCFS/$$/ns/net)" ] ; then
|
|
echo "Container is running in the host network namespace, and therefore cannot be" >&2
|
|
echo "connected to weave. Perhaps the container was started with --net=host." >&2
|
|
return 1
|
|
fi
|
|
ip link add name $LOCAL_IFNAME mtu $MTU type veth peer name $GUEST_IFNAME mtu $MTU || return 1
|
|
|
|
if ! ethtool -K $GUEST_IFNAME tx off >/dev/null ||
|
|
! ip link set $LOCAL_IFNAME master $BRIDGE ||
|
|
! ip link set $LOCAL_IFNAME up ||
|
|
! ip link set $GUEST_IFNAME netns $PROCFS/$CONTAINER_PID/ns/net ; then
|
|
# failed before we assigned the veth to the container's
|
|
# namespace
|
|
ip link del $LOCAL_IFNAME type veth || true
|
|
return 1
|
|
fi
|
|
|
|
if ! netnsenter ip link set $GUEST_IFNAME name $CONTAINER_IFNAME ||
|
|
! configure_arp_cache $CONTAINER_IFNAME "netnsenter" ; then
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
ask_version() {
|
|
if ! DOCKERIMAGE=$(docker inspect --format='{{.Image}}' $1 2>/dev/null) ; then
|
|
if ! DOCKERIMAGE=$(docker inspect --format='{{.Id}}' $2 2>/dev/null) ; then
|
|
echo "Unable to find $2 image." >&2
|
|
fi
|
|
fi
|
|
[ -n "$DOCKERIMAGE" ] && docker run --rm $DOCKERIMAGE --version
|
|
}
|
|
|
|
######################################################################
|
|
# functions invoked through with_container_netns
|
|
######################################################################
|
|
|
|
launch() {
|
|
if ! netnsenter ip link show eth0 >/dev/null ; then
|
|
echo "Perhaps you are running the docker daemon with container networking disabled (-b=none)." >&2
|
|
return 1
|
|
fi
|
|
connect_container_to_bridge &&
|
|
netnsenter ethtool -K eth0 tx off >/dev/null &&
|
|
netnsenter ip link set $CONTAINER_IFNAME up
|
|
}
|
|
|
|
test_launch() {
|
|
netnsenter ethtool -K eth0 tx off >/dev/null
|
|
}
|
|
|
|
attach() {
|
|
if netnsenter ip link show $CONTAINER_IFNAME >/dev/null 2>&1 ; then
|
|
for ADDR; do
|
|
# container already has the expected network interface, so assume we set it up already;
|
|
# just add the IP address.
|
|
if netnsenter ip addr show dev $CONTAINER_IFNAME | grep -F $ADDR >/dev/null ; then
|
|
# address was there already
|
|
continue
|
|
fi
|
|
netnsenter ip addr add $ADDR dev $CONTAINER_IFNAME || return 1
|
|
done
|
|
|
|
return 0
|
|
fi
|
|
|
|
connect_container_to_bridge || return 1
|
|
|
|
for ADDR; do
|
|
netnsenter ip addr add $ADDR dev $CONTAINER_IFNAME || return 1
|
|
done
|
|
|
|
netnsenter ip link set $CONTAINER_IFNAME up || return 1
|
|
|
|
# Route multicast packets across the weave network.
|
|
if ! netnsenter ip route show | grep '^224\.0\.0\.0/4' >/dev/null ; then
|
|
netnsenter ip route add 224.0.0.0/4 dev $CONTAINER_IFNAME
|
|
fi
|
|
|
|
arp_update $CONTAINER_IFNAME $1 "netnsenter"
|
|
}
|
|
|
|
detach() {
|
|
netnsenter ip link show $CONTAINER_IFNAME >/dev/null 2>&1 || return 0
|
|
|
|
for ADDR; do
|
|
if ! netnsenter ip addr show dev $CONTAINER_IFNAME | grep -F $ADDR >/dev/null ; then
|
|
# address is not there, leave the device alone
|
|
continue
|
|
fi
|
|
netnsenter ip addr del $ADDR dev $CONTAINER_IFNAME || return 1
|
|
done
|
|
|
|
if [ -n "$(netnsenter ip -f inet addr show dev $CONTAINER_IFNAME)" ] ; then
|
|
# other addresses are left, leave the device alone
|
|
return 0
|
|
fi
|
|
|
|
# Deleting the interface will delete the multicast route we set up
|
|
netnsenter ip link del $CONTAINER_IFNAME type veth
|
|
}
|
|
|
|
container_weave_addrs() {
|
|
netnsenter ip addr show dev $CONTAINER_IFNAME
|
|
}
|
|
|
|
######################################################################
|
|
# functions for interacting with containers
|
|
######################################################################
|
|
|
|
# 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
|
|
}
|
|
|
|
http_call_ip() {
|
|
ip="$1"
|
|
port="$2"
|
|
http_verb="$3"
|
|
url="$4"
|
|
shift 4
|
|
curl --connect-timeout 3 -s -X $http_verb "$@" http://$ip:$port$url
|
|
}
|
|
|
|
container_ip() {
|
|
if ! status=$(docker inspect --format='{{.State.Running}} {{.NetworkSettings.IPAddress}}' $1 2>/dev/null); then
|
|
echo "$2" >&2
|
|
return 1
|
|
fi
|
|
case "$status" in
|
|
"true ")
|
|
echo "$1 container has no IP address; is Docker networking enabled?" >&2
|
|
return 1
|
|
;;
|
|
true*)
|
|
CONTAINER_IP="${status#true }"
|
|
;;
|
|
*)
|
|
echo "$3" >&2
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Call url $4 with http verb $3 on container $1 at port $2
|
|
http_call() {
|
|
container_ip $1 \
|
|
"$1 container is not present. Have you launched it?" \
|
|
"$1 container is not running." \
|
|
|| return 1
|
|
shift 1
|
|
http_call_ip $CONTAINER_IP "$@"
|
|
}
|
|
|
|
# Wait until container $1 on port $2 is alive enough to respond to its status call
|
|
wait_for_status() {
|
|
WAIT_TIME=0
|
|
while [ $WAIT_TIME -lt 3 ]; do
|
|
sleep $WAIT_TIME
|
|
container_ip $1 \
|
|
"$1 container has died." \
|
|
"$1 container has died." \
|
|
|| return 1
|
|
http_call_ip $CONTAINER_IP $2 GET /status >/dev/null && return 0
|
|
WAIT_TIME=$((WAIT_TIME+1))
|
|
done
|
|
echo "Timed out waiting for $1 container to start." >&2
|
|
return 1
|
|
}
|
|
|
|
######################################################################
|
|
# weaveDNS helpers
|
|
######################################################################
|
|
|
|
dns_args() {
|
|
if [ \( "$DOCKER_VERSION_MAJOR" -lt 1 \) -o \
|
|
\( "$DOCKER_VERSION_MAJOR" -eq 1 -a \
|
|
"$DOCKER_VERSION_MINOR" -lt 3 \) ] ; then
|
|
# Versions prior to 1.2.0 cannot parse `--dns-search=.`, and
|
|
# versions prior to 1.3.0 fail to include the domain when
|
|
# setting the hostname, so lookups for non-fully-qualified
|
|
# names fail.
|
|
echo "ERROR: The '--with-dns' option requires Docker 1.3.0 or later; you are running $DOCKER_VERSION" >&2
|
|
exit 1
|
|
fi
|
|
docker_bridge_ip
|
|
DNS_ARG="--dns $DOCKER_BRIDGE_IP"
|
|
DNS_SEARCH_ARG="--dns-search=."
|
|
for arg; do
|
|
case $arg in
|
|
--dns-search=*)
|
|
DNS_SEARCH_ARG=""
|
|
;;
|
|
*)
|
|
;;
|
|
esac;
|
|
done
|
|
DNS_ARGS="$DNS_ARG $DNS_SEARCH_ARG"
|
|
}
|
|
|
|
# Perform operation $1 on local DNS database, for container ID $2 at
|
|
# addresses $3, $4, ... This function is only called when we know $2
|
|
# is a valid container name
|
|
tell_dns() {
|
|
METHOD="$1"
|
|
CONTAINER_ID="$2"
|
|
shift 2
|
|
|
|
if ! status=$(docker inspect --format='{{.State.Running}}' $DNS_CONTAINER_NAME 2>/dev/null) || [ "$status" != "true" ] ; then
|
|
# weavedns not running - silently return
|
|
return
|
|
fi
|
|
# get the long form of the container ID
|
|
CONTAINER=$(docker inspect --format='{{.Id}}' $CONTAINER_ID 2>/dev/null)
|
|
MORE_ARGS=$(docker inspect --format='--data-urlencode fqdn={{.Config.Hostname}}.{{.Config.Domainname}}.' $CONTAINER 2>/dev/null) && true
|
|
for ADDR; do
|
|
http_call $DNS_CONTAINER_NAME $DNS_HTTP_PORT $METHOD /name/$CONTAINER/${ADDR%/*} $MORE_ARGS || true
|
|
done
|
|
}
|
|
|
|
# Tell the newly-started weaveDNS about existing weave IPs
|
|
populate_dns() {
|
|
if ! wait_for_status $DNS_CONTAINER_NAME $DNS_HTTP_PORT ; then
|
|
echo "If running, it will not be pre-populated." >&2
|
|
return 0
|
|
fi
|
|
for CONTAINER in $(docker ps -q --no-trunc); do
|
|
MORE_ARGS=$(docker inspect --format='--data-urlencode fqdn={{.Config.Hostname}}.{{.Config.Domainname}}.' $CONTAINER 2>/dev/null) && true
|
|
if CONTAINER_IPS=$(with_container_netns $CONTAINER container_weave_addrs 2>&1 | sed -n -e 's/inet \([^/]*\)\/\(.*\)/\1/p') ; then
|
|
for IP in $CONTAINER_IPS; do
|
|
# NB: CONTAINER_IP is the IP of the weavedns
|
|
# container; it is set by wait_for_status.
|
|
http_call_ip $CONTAINER_IP $DNS_HTTP_PORT PUT /name/$CONTAINER/$IP $MORE_ARGS
|
|
done
|
|
fi
|
|
done
|
|
}
|
|
|
|
##########################################################################################
|
|
|
|
[ $(id -u) = 0 ] || {
|
|
echo "weave must be run as 'root' when run locally" >&2
|
|
exit 1
|
|
}
|
|
|
|
uname -s -r | sed -n -e 's|^\([^ ]*\) \([0-9][0-9]*\)\.\([0-9][0-9]*\).*|\1 \2 \3|p' | {
|
|
if ! read sys maj min ; then
|
|
echo "ERROR: Unable to parse operating system version $(uname -s -r)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [ "$sys" != 'Linux' ] ; then
|
|
echo "ERROR: Operating systems other than Linux are not supported (you have $(uname -s -r))" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! [ \( "$maj" -eq 3 -a "$min" -ge 8 \) -o "$maj" -gt 3 ] ; then
|
|
echo "WARNING: Linux kernel version 3.8 or newer is required (you have ${maj}.${min})" >&2
|
|
fi
|
|
}
|
|
|
|
if ! command_exists ip ; then
|
|
echo "ERROR: ip utility is missing. Please install it." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! ip netns list >/dev/null 2>&1 ; then
|
|
echo "ERROR: $(ip -V) does not support network namespaces." >&2
|
|
echo " Please install iproute2-ss111010 or later." >&2
|
|
exit 1
|
|
fi
|
|
|
|
check_docker_version
|
|
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)
|
|
|
|
[ $# -gt 0 ] || usage
|
|
COMMAND=$1
|
|
shift 1
|
|
|
|
case "$COMMAND" in
|
|
setup)
|
|
for img in $IMAGE $DNS_IMAGE $EXEC_IMAGE ; do
|
|
docker pull $img
|
|
done
|
|
;;
|
|
# intentionally undocumented since it assumes knowledge of weave
|
|
# internals
|
|
create-bridge)
|
|
create_bridge --without-ethtool
|
|
;;
|
|
launch)
|
|
check_not_running $CONTAINER_NAME $BASE_IMAGE
|
|
create_bridge
|
|
# We set the router name to the bridge mac since that is
|
|
# stable across re-creations of the containers.
|
|
#
|
|
# TODO use the mac of one of the physical host interfaces
|
|
# (eth0, wlan0, etc) so the name becomes stable across host
|
|
# restarts.
|
|
MACADDR=$(cat /sys/class/net/$BRIDGE/address)
|
|
# backward compatibility...
|
|
if is_cidr "$1" ; then
|
|
echo "WARNING: $1 parameter ignored; 'weave launch' no longer takes a CIDR as the first parameter" >&2
|
|
shift 1
|
|
fi
|
|
NICKNAME=$(hostname)
|
|
CONTAINER_PORT=$PORT
|
|
while true; do
|
|
case "$1" in
|
|
-password)
|
|
[ $# -gt 1 ] || usage
|
|
WEAVE_PASSWORD="$2"
|
|
export WEAVE_PASSWORD
|
|
shift 2
|
|
;;
|
|
-nickname)
|
|
[ $# -gt 1 ] || usage
|
|
NICKNAME="$2"
|
|
shift 2
|
|
;;
|
|
-port)
|
|
[ $# -gt 1 ] || usage
|
|
CONTAINER_PORT="$2"
|
|
shift 2
|
|
;;
|
|
*)
|
|
break
|
|
;;
|
|
esac
|
|
done
|
|
# Set WEAVE_DOCKER_ARGS in the environment in order to supply
|
|
# additional parameters, such as resource limits, to docker
|
|
# when launching the weave container.
|
|
CONTAINER=$(docker run --privileged -d --name=$CONTAINER_NAME \
|
|
-p $PORT:$CONTAINER_PORT/tcp -p $PORT:$CONTAINER_PORT/udp -e WEAVE_PASSWORD \
|
|
$WEAVE_DOCKER_ARGS $IMAGE -iface $CONTAINER_IFNAME -port $CONTAINER_PORT -name $MACADDR -nickname "$NICKNAME" "$@")
|
|
with_container_netns $CONTAINER launch >/dev/null
|
|
wait_for_status $CONTAINER_NAME $HTTP_PORT
|
|
echo $CONTAINER
|
|
;;
|
|
test-launch)
|
|
# For testing purposes. Allows launching of several weave
|
|
# routers on the same machine, by invoking this multiple times
|
|
# with different WEAVE_CONTAINER_NAMEs
|
|
#
|
|
# Note that the routers are not connected to the $BRIDGE and
|
|
# do not perform any capture/injection. So this is mainly
|
|
# intended for testing of administrative and control plane
|
|
# functionality, rather than routing.
|
|
check_not_running $CONTAINER_NAME $BASE_IMAGE
|
|
MACADDR=$(random_mac)
|
|
CONTAINER=$(docker run --privileged -d --name=$CONTAINER_NAME -e WEAVE_PASSWORD \
|
|
$WEAVE_DOCKER_ARGS $IMAGE -name $MACADDR -nickname "$CONTAINER_NAME" "$@")
|
|
with_container_netns $CONTAINER test_launch >/dev/null
|
|
wait_for_status $CONTAINER $HTTP_PORT
|
|
echo $CONTAINER
|
|
;;
|
|
launch-dns)
|
|
[ $# -gt 0 ] || usage
|
|
CIDR=$1
|
|
validate_cidr $CIDR
|
|
shift 1
|
|
check_not_running $DNS_CONTAINER_NAME $BASE_DNS_IMAGE
|
|
create_bridge
|
|
docker_bridge_ip
|
|
# Set WEAVEDNS_DOCKER_ARGS in the environment in order to supply
|
|
# additional parameters, such as resource limits, to docker
|
|
# when launching the weave container.
|
|
DNS_CONTAINER=$(docker run --privileged -d --name=$DNS_CONTAINER_NAME \
|
|
-p $DOCKER_BRIDGE_IP:53:53/udp -v /var/run/docker.sock:/var/run/docker.sock \
|
|
$WEAVEDNS_DOCKER_ARGS $DNS_IMAGE -iface $CONTAINER_IFNAME "$@")
|
|
with_container_netns $DNS_CONTAINER attach $CIDR >/dev/null
|
|
populate_dns
|
|
echo $DNS_CONTAINER
|
|
;;
|
|
connect)
|
|
[ $# -eq 1 ] || usage
|
|
http_call $CONTAINER_NAME $HTTP_PORT POST /connect -d "peer=$1"
|
|
;;
|
|
forget)
|
|
[ $# -eq 1 ] || usage
|
|
http_call $CONTAINER_NAME $HTTP_PORT POST /forget -d "peer=$1"
|
|
;;
|
|
status)
|
|
http_call $CONTAINER_NAME $HTTP_PORT GET /status || true
|
|
echo
|
|
http_call $DNS_CONTAINER_NAME $DNS_HTTP_PORT GET /status 2>/dev/null || true
|
|
;;
|
|
ps)
|
|
[ $# -eq 0 ] && CONTAINERS="weave:expose $(docker ps -q)" || CONTAINERS="$@"
|
|
for CONTAINER_ID in $CONTAINERS ; do
|
|
if [ "$CONTAINER_ID" = "weave:expose" ]; then
|
|
ADDRS_CMD="ip addr show dev $BRIDGE"
|
|
else
|
|
ADDRS_CMD="with_container_netns $CONTAINER_ID container_weave_addrs"
|
|
fi
|
|
if CONTAINER_ADDRS=$($ADDRS_CMD 2>&1) ; then
|
|
CONTAINER_MAC=$(echo "$CONTAINER_ADDRS" | grep -o 'link/ether .*' | cut -d ' ' -f 2)
|
|
CONTAINER_IPS=$(echo "$CONTAINER_ADDRS" | grep -o 'inet .*' | cut -d ' ' -f 2)
|
|
echo $CONTAINER_ID $CONTAINER_MAC $CONTAINER_IPS
|
|
fi
|
|
done
|
|
;;
|
|
version)
|
|
[ $# -eq 0 ] || usage
|
|
echo weave script $SCRIPT_VERSION
|
|
ask_version $CONTAINER_NAME $IMAGE || true
|
|
ask_version $DNS_CONTAINER_NAME $DNS_IMAGE || true
|
|
if ! EXEC_IMAGE_ID=$(docker inspect --format='{{.Id}}' $EXEC_IMAGE 2>/dev/null) ; then
|
|
echo "Unable to find $EXEC_IMAGE image." >&2
|
|
else
|
|
EXEC_VERSION=$(docker images --no-trunc | grep $EXEC_IMAGE_ID | grep -v latest | tr -s ' ' | cut -d ' ' -f 2 | tr "\n" ' ')
|
|
if [ -n "$EXEC_VERSION" ] ; then
|
|
echo "weave exec $EXEC_VERSION"
|
|
else
|
|
echo "weave exec (unreleased version)"
|
|
fi
|
|
fi
|
|
;;
|
|
run)
|
|
[ $# -gt 0 ] || usage
|
|
if [ "$1" = "--with-dns" ] ; then
|
|
shift 1
|
|
dns_args "$@"
|
|
fi
|
|
collect_cidr_args "$@"
|
|
shift $CIDR_COUNT
|
|
CONTAINER=$(docker run $DNS_ARGS -d "$@")
|
|
create_bridge
|
|
with_container_netns $CONTAINER attach $CIDR_ARGS >/dev/null
|
|
tell_dns PUT $CONTAINER $CIDR_ARGS
|
|
echo $CONTAINER
|
|
;;
|
|
dns-args)
|
|
dns_args "$@"
|
|
echo -n $DNS_ARGS
|
|
;;
|
|
start)
|
|
collect_cidr_args "$@"
|
|
shift $CIDR_COUNT
|
|
[ $# -eq 1 ] || usage
|
|
CONTAINER_ID="$1"
|
|
create_bridge
|
|
CONTAINER=$(docker start $CONTAINER_ID)
|
|
with_container_netns $CONTAINER_ID attach $CIDR_ARGS >/dev/null
|
|
tell_dns PUT $CONTAINER_ID $CIDR_ARGS
|
|
echo $CONTAINER
|
|
;;
|
|
attach)
|
|
collect_cidr_args "$@"
|
|
shift $CIDR_COUNT
|
|
[ $# -eq 1 ] || usage
|
|
CONTAINER_ID="$1"
|
|
create_bridge
|
|
with_container_netns $CONTAINER_ID attach $CIDR_ARGS >/dev/null
|
|
tell_dns PUT $CONTAINER_ID $CIDR_ARGS
|
|
;;
|
|
detach)
|
|
collect_cidr_args "$@"
|
|
shift $CIDR_COUNT
|
|
[ $# -eq 1 ] || usage
|
|
CONTAINER_ID="$1"
|
|
with_container_netns $CONTAINER_ID detach $CIDR_ARGS >/dev/null
|
|
tell_dns DELETE $CONTAINER_ID $CIDR_ARGS
|
|
;;
|
|
expose)
|
|
collect_cidr_args "$@"
|
|
shift $CIDR_COUNT
|
|
if [ $# -eq 0 ]; then
|
|
FQDN=""
|
|
else
|
|
[ $# -eq 2 -a "$1" = "-h" ] || usage
|
|
FQDN="$2"
|
|
fi
|
|
create_bridge --without-ethtool
|
|
for CIDR in $CIDR_ARGS; do
|
|
if ! ip addr show dev $BRIDGE | grep -qF $CIDR
|
|
then
|
|
ip addr add dev $BRIDGE $CIDR
|
|
arp_update $BRIDGE $CIDR
|
|
add_iptables_rule nat WEAVE -d $CIDR ! -s $CIDR -j MASQUERADE
|
|
add_iptables_rule nat WEAVE -s $CIDR ! -d $CIDR -j MASQUERADE
|
|
if [ "$FQDN" ]; then
|
|
http_call $DNS_CONTAINER_NAME $DNS_HTTP_PORT PUT /name/weave:expose/${CIDR%/*} --data-urlencode "fqdn=$FQDN" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
done
|
|
;;
|
|
hide)
|
|
collect_cidr_args "$@"
|
|
shift $CIDR_COUNT
|
|
[ $# -eq 0 ] || usage
|
|
create_bridge --without-ethtool
|
|
for CIDR in $CIDR_ARGS; do
|
|
if ip addr show dev $BRIDGE | grep -qF $CIDR
|
|
then
|
|
ip addr del dev $BRIDGE $CIDR
|
|
delete_iptables_rule nat WEAVE -d $CIDR ! -s $CIDR -j MASQUERADE
|
|
delete_iptables_rule nat WEAVE -s $CIDR ! -d $CIDR -j MASQUERADE
|
|
http_call $DNS_CONTAINER_NAME $DNS_HTTP_PORT DELETE /name/weave:expose/${CIDR%/*} 2>/dev/null || true
|
|
fi
|
|
done
|
|
;;
|
|
stop)
|
|
[ $# -eq 0 ] || usage
|
|
if ! docker stop $CONTAINER_NAME >/dev/null 2>&1 ; then
|
|
echo "Weave is not running." >&2
|
|
fi
|
|
docker rm -f $CONTAINER_NAME >/dev/null 2>&1 || true
|
|
conntrack -D -p udp --dport $PORT >/dev/null 2>&1 || true
|
|
;;
|
|
stop-dns)
|
|
[ $# -eq 0 ] || usage
|
|
if ! docker stop $DNS_CONTAINER_NAME >/dev/null 2>&1 ; then
|
|
echo "WeaveDNS is not running." >&2
|
|
fi
|
|
docker rm -f $DNS_CONTAINER_NAME >/dev/null 2>&1 || true
|
|
;;
|
|
reset)
|
|
[ $# -eq 0 ] || usage
|
|
docker stop $CONTAINER_NAME >/dev/null 2>&1 || true
|
|
docker stop $DNS_CONTAINER_NAME >/dev/null 2>&1 || true
|
|
docker rm -f $CONTAINER_NAME >/dev/null 2>&1 || true
|
|
docker rm -f $DNS_CONTAINER_NAME >/dev/null 2>&1 || true
|
|
conntrack -D -p udp --dport $PORT >/dev/null 2>&1 || true
|
|
destroy_bridge
|
|
for LOCAL_IFNAME in $(ip link show | grep v${CONTAINER_IFNAME}pl | cut -d ' ' -f 2 | tr -d ':') ; do
|
|
ip link del $LOCAL_IFNAME
|
|
done
|
|
;;
|
|
*)
|
|
echo "Unknown weave command '$COMMAND'" >&2
|
|
usage
|
|
;;
|
|
esac
|