Skip to content

Commit

Permalink
Invert multicast option in plugin
Browse files Browse the repository at this point in the history
Now it's off by default, and there is an option to enable the
multicast route per-network.
  • Loading branch information
bboreham committed Jun 2, 2016
1 parent b1fa417 commit 6bc689a
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 18 deletions.
94 changes: 85 additions & 9 deletions plugin/net/driver.go
Expand Up @@ -2,9 +2,11 @@ package plugin

import (
"fmt"
"strconv"
"sync"

"github.com/docker/libnetwork/drivers/remote/api"
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/types"

"github.com/vishvananda/netlink"
Expand All @@ -15,18 +17,28 @@ import (
"github.com/weaveworks/weave/plugin/skel"
)

const (
multicastOption = "works.weave.multicast"
)

type network struct {
hasMulticastRoute bool
}

type driver struct {
scope string
noMulticastRoute bool
scope string
docker *docker.Client
sync.RWMutex
endpoints map[string]struct{}
networks map[string]network
}

func New(client *docker.Client, weave *weaveapi.Client, scope string, noMulticastRoute bool) (skel.Driver, error) {
func New(client *docker.Client, weave *weaveapi.Client, scope string) (skel.Driver, error) {
driver := &driver{
noMulticastRoute: noMulticastRoute,
scope: scope,
endpoints: make(map[string]struct{}),
scope: scope,
docker: client,
endpoints: make(map[string]struct{}),
networks: make(map[string]network),
}

_, err := NewWatcher(client, weave, driver)
Expand All @@ -49,11 +61,33 @@ func (driver *driver) GetCapabilities() (*api.GetCapabilityResponse, error) {

func (driver *driver) CreateNetwork(create *api.CreateNetworkRequest) error {
driver.logReq("CreateNetwork", create, create.NetworkID)
_, err := driver.setupNetworkInfo(create.NetworkID, stringOptions(create))
return err
}

// Deal with excessively-generic way the options get decoded from JSON
func stringOptions(create *api.CreateNetworkRequest) map[string]string {
if create.Options != nil {
if data, found := create.Options[netlabel.GenericData]; found {
if options, ok := data.(map[string]interface{}); ok {
out := make(map[string]string, len(options))
for key, value := range options {
if str, ok := value.(string); ok {
out[key] = str
}
}
return out
}
}
}
return nil
}

func (driver *driver) DeleteNetwork(delete *api.DeleteNetworkRequest) error {
driver.logReq("DeleteNetwork", delete, delete.NetworkID)
func (driver *driver) DeleteNetwork(delreq *api.DeleteNetworkRequest) error {
driver.logReq("DeleteNetwork", delreq, delreq.NetworkID)
driver.Lock()
delete(driver.networks, delreq.NetworkID)
driver.Unlock()
return nil
}

Expand Down Expand Up @@ -96,6 +130,10 @@ func (driver *driver) EndpointInfo(req *api.EndpointInfoRequest) (*api.EndpointI
func (driver *driver) JoinEndpoint(j *api.JoinRequest) (*api.JoinResponse, error) {
driver.logReq("JoinEndpoint", j, fmt.Sprintf("%s:%s to %s", j.NetworkID, j.EndpointID, j.SandboxKey))

network, err := driver.findNetworkInfo(j.NetworkID)
if err != nil {
return nil, driver.error("JoinEndpoint", "unable to find network info: %s", err)
}
name, peerName := vethPair(j.EndpointID)
if _, err := weavenet.CreateAndAttachVeth(name, peerName, weavenet.WeaveBridgeName, 0, nil); err != nil {
return nil, driver.error("JoinEndpoint", "%s", err)
Expand All @@ -107,7 +145,7 @@ func (driver *driver) JoinEndpoint(j *api.JoinRequest) (*api.JoinResponse, error
DstPrefix: weavenet.VethName,
},
}
if !driver.noMulticastRoute {
if network.hasMulticastRoute {
multicastRoute := api.StaticRoute{
Destination: "224.0.0.0/4",
RouteType: types.CONNECTED,
Expand All @@ -118,6 +156,44 @@ func (driver *driver) JoinEndpoint(j *api.JoinRequest) (*api.JoinResponse, error
return response, nil
}

func (driver *driver) findNetworkInfo(id string) (network, error) {
driver.Lock()
network, found := driver.networks[id]
driver.Unlock()
if found {
return network, nil
}
info, err := driver.docker.NetworkInfo(id)
if err != nil {
return network, err
}
return driver.setupNetworkInfo(id, info.Options)
}

func (driver *driver) setupNetworkInfo(id string, options map[string]string) (network, error) {
var network network
for key, value := range options {
switch key {
case multicastOption:
if value == "" { // interpret "--opt works.weave.multicast" as "turn it on"
network.hasMulticastRoute = true
} else {
var err error
if network.hasMulticastRoute, err = strconv.ParseBool(value); err == nil {
return network, fmt.Errorf("unrecognized value %q for option %s", value, key)
}

}
default:
driver.warn("setupNetworkInfo", "unrecognized option: %s", key)
}
}
driver.Lock()
driver.networks[id] = network
driver.Unlock()
return network, nil
}

func (driver *driver) LeaveEndpoint(leave *api.LeaveRequest) error {
driver.logReq("LeaveEndpoint", leave, fmt.Sprintf("%s:%s", leave.NetworkID, leave.EndpointID))

Expand Down
17 changes: 10 additions & 7 deletions prog/plugin/main.go
Expand Up @@ -42,7 +42,7 @@ func main() {
flag.StringVar(&logLevel, "log-level", "info", "logging level (debug, info, warning, error)")
flag.StringVar(&address, "socket", "/run/docker/plugins/weave.sock", "socket on which to listen")
flag.StringVar(&meshAddress, "meshsocket", "/run/docker/plugins/weavemesh.sock", "socket on which to listen in mesh mode")
flag.BoolVar(&noMulticastRoute, "no-multicast-route", false, "do not add a multicast route to network endpoints")
flag.BoolVar(&noMulticastRoute, "no-multicast-route", false, "deprecated (this is now the default)")

flag.Parse()

Expand Down Expand Up @@ -73,26 +73,29 @@ func main() {
}

Log.Println("Weave plugin", version, "Command line options:", os.Args[1:])
if noMulticastRoute {
Log.Warning("--no-multicast-route option has been removed; multicast is off by default")
}
Log.Info(dockerClient.Info())

err = run(dockerClient, weave, address, meshAddress, noMulticastRoute)
err = run(dockerClient, weave, address, meshAddress)
if err != nil {
Log.Fatal(err)
}
}

func run(dockerClient *docker.Client, weave *weaveapi.Client, address, meshAddress string, noMulticastRoute bool) error {
func run(dockerClient *docker.Client, weave *weaveapi.Client, address, meshAddress string) error {
endChan := make(chan error, 1)
if address != "" {
globalListener, err := listenAndServe(dockerClient, weave, address, noMulticastRoute, endChan, "global", false)
globalListener, err := listenAndServe(dockerClient, weave, address, endChan, "global", false)
if err != nil {
return err
}
defer os.Remove(address)
defer globalListener.Close()
}
if meshAddress != "" {
meshListener, err := listenAndServe(dockerClient, weave, meshAddress, noMulticastRoute, endChan, "local", true)
meshListener, err := listenAndServe(dockerClient, weave, meshAddress, endChan, "local", true)
if err != nil {
return err
}
Expand All @@ -117,8 +120,8 @@ func run(dockerClient *docker.Client, weave *weaveapi.Client, address, meshAddre
}
}

func listenAndServe(dockerClient *docker.Client, weave *weaveapi.Client, address string, noMulticastRoute bool, endChan chan<- error, scope string, withIpam bool) (net.Listener, error) {
d, err := netplugin.New(dockerClient, weave, scope, noMulticastRoute)
func listenAndServe(dockerClient *docker.Client, weave *weaveapi.Client, address string, endChan chan<- error, scope string, withIpam bool) (net.Listener, error) {
d, err := netplugin.New(dockerClient, weave, scope)
if err != nil {
return nil, err
}
Expand Down
7 changes: 7 additions & 0 deletions site/plugin/plugin-how-it-works.md
Expand Up @@ -26,6 +26,13 @@ store, but the first part of

>**Note:** In the case of multiple networks using the `weave` driver, all containers are on the same virtual network but Docker allocates their addresses on different subnets so they cannot talk to each other directly.
The plugin accepts the following options via `docker network create ... --opt`:

* `works.weave.multicast` -- tells weave to add a static IP
route for multicast traffic onto its interface.

>**Note:** If you connect a container to multiple Weave networks, at
most one of them can have the multicast route enabled.

**See Also**

Expand Down
17 changes: 15 additions & 2 deletions test/195_no_multicast_route_1_test.sh
Expand Up @@ -8,10 +8,13 @@ show_multicast_route_on() {

start_suite "--no-multicast-route operation"

# In case one is left around from a previous test
$SSH $HOST1 docker network rm testmcasttrue testmcastfalse >/dev/null 2>&1 || true

# Ensure containers run either way have no multicast route
weave_on $HOST1 launch-router
weave_on $HOST1 launch-proxy --no-multicast-route
weave_on $HOST1 launch-plugin --no-multicast-route
weave_on $HOST1 launch-plugin # plugin defaults to multicast off

start_container $HOST1 --no-multicast-route --name c1
proxy_start_container $HOST1 --name c2
Expand All @@ -21,6 +24,15 @@ assert "show_multicast_route_on $HOST1 c1"
assert "show_multicast_route_on $HOST1 c2"
assert "show_multicast_route_on $HOST1 c3"

# Now try via docker network options
# using ssh rather than docker -H because CircleCI docker client is older
$SSH $HOST1 docker network create --driver weavemesh --ipam-driver weavemesh --opt works.weave.multicast=false testmcastfalse
$SSH $HOST1 docker run --net=testmcastfalse --name c4 -di $SMALL_IMAGE /bin/sh
assert "show_multicast_route_on $HOST1 c4"
$SSH $HOST1 docker network create --driver weavemesh --ipam-driver weavemesh --opt works.weave.multicast testmcasttrue
$SSH $HOST1 docker run --net=testmcasttrue --name c5 -di $SMALL_IMAGE /bin/sh
assert "show_multicast_route_on $HOST1 c5" "224.0.0.0/4 dev ethwe0 "

# Ensure current proxy options are obeyed on container start
docker_on $HOST1 stop -t 1 c2
weave_on $HOST1 stop-proxy
Expand All @@ -30,6 +42,7 @@ proxy docker_on $HOST1 start c2

assert "show_multicast_route_on $HOST1 c2" "224.0.0.0/4 dev ethwe "

docker_on $HOST1 rm -f c3
docker_on $HOST1 rm -f c3 c4 c5
$SSH $HOST1 docker network rm testmcasttrue testmcastfalse

end_suite

0 comments on commit 6bc689a

Please sign in to comment.