diff --git a/site/features.md b/site/features.md
index d95922b7..2b732a07 100644
--- a/site/features.md
+++ b/site/features.md
@@ -90,6 +90,11 @@ each other but not the containers of our first application...
This isolation-through-subnets scheme is an example of carrying over a
well-known technique from the 'on metal' days to containers.
+If desired, a container can be attached to multiple subnets when it is
+started:
+
+ host1# weave run 10.2.2.1/24 10.2.3.1/24 -t -i ubuntu
+
NB: By default docker permits communication between containers on the
same host, via their docker-assigned IP addresses. For complete
isolation between application containers, that feature needs to be
@@ -128,6 +133,12 @@ sharing it between applications:
host1# weave attach 10.2.1.1/24 $C
host1# weave attach 10.2.2.1/24 $C
+Finally, multiple addresses can be attached or detached with a single
+invocation:
+
+ host1# weave attach 10.2.1.1/24 10.2.2.1/24 10.2.3.1/24 $C
+ host1# weave detach 10.2.1.1/24 10.2.2.1/24 10.2.3.1/24 $C
+
### Security
In order to connect containers across untrusted networks, weave peers
@@ -173,6 +184,12 @@ will work. And, more interestingly,
will work too, which is talking to a container that resides on `$HOST1`.
+Multiple subnet addresses can be exposed or hidden with a single
+invocation:
+
+ host2# weave expose 10.2.1.102/24 10.2.2.102/24
+ host2# weave hide 10.2.1.102/24 10.2.2.102/24
+
### Service export
Services running in containers on a weave network can be made
diff --git a/test/500_weave_multi_cidr_test.sh b/test/500_weave_multi_cidr_test.sh
new file mode 100755
index 00000000..bd1532c2
--- /dev/null
+++ b/test/500_weave_multi_cidr_test.sh
@@ -0,0 +1,94 @@
+#! /bin/bash
+
+. ./config.sh
+
+start_suite "Weave run/start/attach/detach with multiple cidr arguments"
+
+# Cleanup from previous run
+weave_on $HOST1 stop || true
+weave_on $HOST1 stop-dns || true
+docker_on $HOST1 rm -f multicidr || true
+
+weave_on $HOST1 launch
+weave_on $HOST1 launch-dns 10.254.254.254/24
+
+# weave_status_on
+weave_status_on() {
+ HOST=$1; shift
+
+ DOCKER_HOST=tcp://$HOST:2375 $WEAVE status
+}
+
+# weave_ps_on
+weave_ps_on() {
+ HOST=$1; shift
+
+ DOCKER_HOST=tcp://$HOST:2375 $WEAVE ps
+}
+
+# assert_container_cidrs [ ...]
+assert_container_cidrs() {
+ HOST=$1; shift
+ CID=$1; shift
+ CIDRS=$@
+
+ # Assert container has attached CIDRs
+ assert_raises "weave_ps_on $HOST | grep \"^$CID [^ ]* $CIDRS$\""
+}
+
+# assert_zone_records [ ...]
+assert_zone_records() {
+ HOST=$1; shift
+ CID=$1; shift
+ FQDN=$1; shift
+
+ # Assert correct number of records exist
+ assert "weave_status_on $HOST | grep \"^$CID\" | wc -l" $#
+
+ # Assert correct records exist
+ for IP in $@; do
+ assert_raises "weave_status_on $HOST | grep \"^$CID $IP $FQDN$\""
+ done
+}
+
+# assert_bridge_cidrs [ ...]
+assert_bridge_cidrs() {
+ HOST=$1; shift
+ DEV=$1; shift
+ CIDRS=$@
+
+ BRIDGE_CIDRS=$($SSH $HOST ip addr show dev $DEV | grep -o 'inet .*' | cut -d ' ' -f 2)
+
+ assert "echo $BRIDGE_CIDRS" "$CIDRS"
+}
+
+# Run container with three cidrs
+CID=$(DOCKER_HOST=tcp://$HOST1:2375 $WEAVE run 10.0.1.1/24 10.0.2.1/24 10.0.3.1/24 -t --name multicidr -h multicidr.weave.local ubuntu | cut -b 1-12)
+assert_container_cidrs $HOST1 $CID 10.0.1.1/24 10.0.2.1/24 10.0.3.1/24
+assert_zone_records $HOST1 $CID multicidr.weave.local. 10.0.1.1 10.0.2.1 10.0.3.1
+
+# Remove two of them
+weave_on $HOST1 detach 10.0.1.1/24 10.0.3.1/24 $CID
+assert_container_cidrs $HOST1 $CID 10.0.2.1/24
+assert_zone_records $HOST1 $CID multicidr.weave.local. 10.0.2.1
+
+# Put them both back
+weave_on $HOST1 attach 10.0.1.1/24 10.0.3.1/24 $CID
+assert_container_cidrs $HOST1 $CID 10.0.2.1/24 10.0.1.1/24 10.0.3.1/24
+assert_zone_records $HOST1 $CID multicidr.weave.local. 10.0.2.1 10.0.1.1 10.0.3.1
+
+# Stop the container, restart with three IPs
+docker_on $HOST1 stop $CID
+weave_on $HOST1 start 10.0.1.1/24 10.0.2.1/24 10.0.3.1/24 $CID
+assert_container_cidrs $HOST1 $CID 10.0.1.1/24 10.0.2.1/24 10.0.3.1/24
+assert_zone_records $HOST1 $CID multicidr.weave.local. 10.0.1.1 10.0.2.1 10.0.3.1
+
+# Expose some cidrs
+weave_on $HOST1 expose 10.0.1.2/24 10.0.2.2/24 10.0.3.2/24
+assert_bridge_cidrs $HOST1 weave 10.0.1.2/24 10.0.2.2/24 10.0.3.2/24
+
+# Hide some cidrs
+weave_on $HOST1 hide 10.0.1.2/24 10.0.3.2/24
+assert_bridge_cidrs $HOST1 weave 10.0.2.2/24
+
+end_suite
diff --git a/weave b/weave
index 6cdc0550..bcbdb08d 100755
--- a/weave
+++ b/weave
@@ -19,10 +19,10 @@ usage() {
echo "weave launch-dns "
echo "weave connect "
echo "weave forget "
- echo "weave run [--with-dns] ..."
- echo "weave start "
- echo "weave attach "
- echo "weave detach "
+ echo "weave run [--with-dns] [ ...] ..."
+ echo "weave start [ ...] "
+ echo "weave attach [ ...] "
+ echo "weave detach [ ...] "
echo "weave expose "
echo "weave hide "
echo "weave ps"
@@ -71,6 +71,7 @@ exec_remote() {
if [ "$1" = "--local" ]; then
LOCAL="$1"
shift 1
+ [ -t 0 ] && stty -onlcr
elif [ "$1" != "run" ]; then
exec_remote "$@"
exit $?
@@ -138,6 +139,16 @@ validate_cidr() {
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
+}
+
run_iptables() {
# -w is recent addition to iptables
if [ -z "$CHECKED_IPTABLES_W" ] ; then
@@ -326,23 +337,33 @@ launch() {
attach() {
if ip netns exec $NETNS ip link show $CONTAINER_IFNAME >/dev/null 2>&1 ; then
- # container already has the expected network interface, so assume we set it up already;
- # just add the IP address.
- if ip netns exec $NETNS ip addr show dev $CONTAINER_IFNAME | grep -F $1 >/dev/null ; then
- # address was there already
- return 0
- fi
+ for ADDR; do
+ # container already has the expected network interface, so assume we set it up already;
+ # just add the IP address.
+ if ip netns exec $NETNS ip addr show dev $CONTAINER_IFNAME | grep -F $ADDR >/dev/null ; then
+ # address was there already
+ continue
+ fi
- if ! ip netns exec $NETNS ip addr add $1 dev $CONTAINER_IFNAME ; then
- return 1
- fi
+ if ! ip netns exec $NETNS ip addr add $ADDR dev $CONTAINER_IFNAME ; then
+ return 1
+ fi
+ done
return 0
fi
- if ! connect_container_to_bridge ||
- ! ip netns exec $NETNS ip addr add $1 dev $CONTAINER_IFNAME ||
- ! ip netns exec $NETNS ip link set $CONTAINER_IFNAME up ; then
+ if ! connect_container_to_bridge; then
+ return 1
+ fi
+
+ for ADDR; do
+ if ! ip netns exec $NETNS ip addr add $ADDR dev $CONTAINER_IFNAME; then
+ return 1
+ fi
+ done
+
+ if ! ip netns exec $NETNS ip link set $CONTAINER_IFNAME up ; then
return 1
fi
@@ -355,14 +376,16 @@ attach() {
}
detach() {
- if ! ip netns exec $NETNS ip addr show dev $CONTAINER_IFNAME | grep -F $1 >/dev/null ; then
- # address is not there, leave the device alone
- return 0
- fi
+ for ADDR; do
+ if ! ip netns exec $NETNS ip addr show dev $CONTAINER_IFNAME | grep -F $ADDR >/dev/null ; then
+ # address is not there, leave the device alone
+ continue
+ fi
- if ! ip netns exec $NETNS ip addr del $1 dev $CONTAINER_IFNAME ; then
- return 1
- fi
+ if ! ip netns exec $NETNS ip addr del $ADDR dev $CONTAINER_IFNAME ; then
+ return 1
+ fi
+ done
if [ -n "$(ip netns exec $NETNS ip -f inet addr show dev $CONTAINER_IFNAME)" ] ; then
# other addresses are left, leave the device alone
@@ -408,15 +431,21 @@ http_call_ip() {
# Perform operation $1 on container ID $2 to local DNS database at address $3
# This function is only called where 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}}' $2 2>/dev/null)
+ CONTAINER=$(docker inspect --format='{{.Id}}' $CONTAINER_ID 2>/dev/null)
# extract IP address and routing prefix from CIDR
MORE_ARGS=$(docker inspect --format='--data-urlencode fqdn={{.Config.Hostname}}.{{.Config.Domainname}}.' $CONTAINER 2>/dev/null) && true
- http_call $DNS_CONTAINER_NAME $DNS_HTTP_PORT $1 /name/$CONTAINER/${3%/*} $MORE_ARGS || 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
@@ -645,16 +674,15 @@ case "$COMMAND" in
DNS_ARGS=$(exec_remote dns-args "$@")
[ "$1" = "--with-dns" ] && shift 1
fi
- CIDR=$1
- shift 1
- validate_cidr $CIDR
+ collect_cidr_args "$@"
+ shift $CIDR_COUNT
CONTAINER=$(docker run $DNS_ARGS -d "$@")
if [ "$LOCAL" = "--local" ]; then
create_bridge
- with_container_netns $CONTAINER attach $CIDR >/dev/null
- tell_dns PUT $CONTAINER $CIDR
+ with_container_netns $CONTAINER attach $CIDR_ARGS >/dev/null
+ tell_dns PUT $CONTAINER $CIDR_ARGS
else
- exec_remote attach $CIDR $CONTAINER
+ exec_remote attach $CIDR_ARGS $CONTAINER
fi
echo $CONTAINER
;;
@@ -663,54 +691,59 @@ case "$COMMAND" in
echo -n $DNS_ARGS
;;
start)
- [ $# -eq 2 ] || usage
- CIDR=$1
- validate_cidr $CIDR
+ [ $# -gt 1 ] || usage
+ collect_cidr_args "$@"
+ shift $CIDR_COUNT
+ CONTAINER_ID="$1"
create_bridge
- CONTAINER=$(docker start $2)
- with_container_netns $CONTAINER attach $CIDR >/dev/null
- tell_dns PUT $CONTAINER $CIDR
+ 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)
- [ $# -eq 2 ] || usage
- CIDR=$1
- validate_cidr $CIDR
+ [ $# -gt 1 ] || usage
+ collect_cidr_args "$@"
+ shift $CIDR_COUNT
+ CONTAINER_ID="$1"
create_bridge
- with_container_netns $2 attach $CIDR >/dev/null
- tell_dns PUT $2 $CIDR
+ with_container_netns $CONTAINER_ID attach $CIDR_ARGS >/dev/null
+ tell_dns PUT $CONTAINER_ID $CIDR_ARGS
;;
detach)
- [ $# -eq 2 ] || usage
- CIDR=$1
- validate_cidr $CIDR
- with_container_netns $2 detach $CIDR >/dev/null
- tell_dns DELETE $2 $CIDR
+ [ $# -gt 1 ] || usage
+ collect_cidr_args "$@"
+ shift $CIDR_COUNT
+ CONTAINER_ID="$1"
+ with_container_netns $CONTAINER_ID detach $CIDR_ARGS >/dev/null
+ tell_dns DELETE $CONTAINER_ID $CIDR_ARGS
;;
expose)
- [ $# -eq 1 ] || usage
- CIDR=$1
- validate_cidr $CIDR
- create_bridge --without-ethtool
- 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 -o $BRIDGE ! -s $CIDR -j MASQUERADE
- add_iptables_rule nat WEAVE -s $CIDR ! -o $BRIDGE -j MASQUERADE
- fi
+ [ $# -gt 0 ] || usage
+ for CIDR; do
+ validate_cidr $CIDR
+ create_bridge --without-ethtool
+ 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 -o $BRIDGE ! -s $CIDR -j MASQUERADE
+ add_iptables_rule nat WEAVE -s $CIDR ! -o $BRIDGE -j MASQUERADE
+ fi
+ done
;;
hide)
- [ $# -eq 1 ] || usage
- CIDR=$1
- validate_cidr $CIDR
- create_bridge --without-ethtool
- if ip addr show dev $BRIDGE | grep -qF $CIDR
- then
- ip addr del dev $BRIDGE $CIDR
- delete_iptables_rule nat WEAVE -o $BRIDGE ! -s $CIDR -j MASQUERADE
- delete_iptables_rule nat WEAVE -s $CIDR ! -o $BRIDGE -j MASQUERADE
- fi
+ [ $# -gt 0 ] || usage
+ for CIDR; do
+ validate_cidr $CIDR
+ create_bridge --without-ethtool
+ if ip addr show dev $BRIDGE | grep -qF $CIDR
+ then
+ ip addr del dev $BRIDGE $CIDR
+ delete_iptables_rule nat WEAVE -o $BRIDGE ! -s $CIDR -j MASQUERADE
+ delete_iptables_rule nat WEAVE -s $CIDR ! -o $BRIDGE -j MASQUERADE
+ fi
+ done
;;
stop)
[ $# -eq 0 ] || usage