diff --git a/plugin/net/driver.go b/plugin/net/driver.go index 966fe98b..675ca93d 100644 --- a/plugin/net/driver.go +++ b/plugin/net/driver.go @@ -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" @@ -15,18 +17,28 @@ import ( "github.com/weaveworks/weave/plugin/skel" ) -type driver struct { - scope string - noMulticastRoute bool - sync.RWMutex - endpoints map[string]struct{} +const ( + MulticastOption = "works.weave.multicast" +) + +type network struct { + hasMulticastRoute bool } -func New(client *docker.Client, weave *weaveapi.Client, scope string, noMulticastRoute bool) (skel.Driver, error) { +type driver struct { + 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) (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) @@ -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 } @@ -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) @@ -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, @@ -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)) diff --git a/prog/plugin/main.go b/prog/plugin/main.go index 3eb12a55..8ae47da1 100644 --- a/prog/plugin/main.go +++ b/prog/plugin/main.go @@ -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() @@ -73,18 +73,21 @@ 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 } @@ -92,7 +95,7 @@ func run(dockerClient *docker.Client, weave *weaveapi.Client, address, meshAddre 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 } @@ -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 } diff --git a/prog/weaveutil/plugin_network.go b/prog/weaveutil/plugin_network.go index 76940445..9ff52314 100644 --- a/prog/weaveutil/plugin_network.go +++ b/prog/weaveutil/plugin_network.go @@ -4,7 +4,9 @@ package main import ( "fmt" - "github.com/fsouza/go-dockerclient" + docker "github.com/fsouza/go-dockerclient" + + "github.com/weaveworks/weave/plugin/net" ) func createPluginNetwork(args []string) error { @@ -23,6 +25,7 @@ func createPluginNetwork(args []string) error { CheckDuplicate: true, Driver: driverName, IPAM: docker.IPAMOptions{Driver: driverName}, + Options: map[string]interface{}{plugin.MulticastOption: "true"}, }) if err != docker.ErrNetworkAlreadyExists && err != nil { // Despite appearances to the contrary, CreateNetwork does diff --git a/site/plugin.md b/site/plugin.md index c9f7fa3f..a153c821 100644 --- a/site/plugin.md +++ b/site/plugin.md @@ -22,7 +22,8 @@ After you've launched Weave Net and peered your hosts, you can start containers $ docker run --net=weave -ti ubuntu -on any of the hosts, and they can all communicate with each other. +on any of the hosts, and they can all communicate with each other +using any protocol, even multicast. >**Warning!** It is inadvisable to attach containers to the Weave network using the Weave Docker Networking Plugin and Weave Docker API Proxy simultaneously. Such containers will end up with two Weave network interfaces and two IP addresses, which is rarely desirable. To ensure that the proxy is not being used, do not run `eval $(weave env)`, or `docker $(weave config) ...`. @@ -82,12 +83,6 @@ The plugin configuration parameters are: how much information to emit for debugging. * `--no-restart` -- remove the default policy of `--restart=always`, if you want to control start-up of the plugin yourself - * `--no-multicast-route` -- stops weave from adding a static IP route for - multicast traffic onto its interface - -By default, multicast traffic is routed over the weave network. -To turn this off, e.g. you want to configure your own multicast -route, add the `--no-multicast-route` flag to `weave launch-plugin`. **See Also** diff --git a/site/plugin/plugin-how-it-works.md b/site/plugin/plugin-how-it-works.md index 6b5d8b96..54d9f26a 100644 --- a/site/plugin/plugin-how-it-works.md +++ b/site/plugin/plugin-how-it-works.md @@ -26,6 +26,15 @@ 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. The `weave` + network created when the plugin is first launched has the multicast + option turned on, but for any networks you create it defaults to off. **See Also** diff --git a/test/195_no_multicast_route_1_test.sh b/test/195_no_multicast_route_1_test.sh index a0d38ad7..0cbd46df 100755 --- a/test/195_no_multicast_route_1_test.sh +++ b/test/195_no_multicast_route_1_test.sh @@ -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 except for 'net=weave' start_container $HOST1 --no-multicast-route --name c1 proxy_start_container $HOST1 --name c2 @@ -19,7 +22,16 @@ start_container_local_plugin $HOST1 --name=c3 assert "show_multicast_route_on $HOST1 c1" assert "show_multicast_route_on $HOST1 c2" -assert "show_multicast_route_on $HOST1 c3" +assert "show_multicast_route_on $HOST1 c3" "224.0.0.0/4 dev ethwe0 " + +# 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 @@ -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