mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
![Flavio Crisciani](/assets/img/avatar_default.png)
In case the file descriptor of the netlink socket is closed the recvfrom is not returning. This may create deadlock conditions. The current solution is to make sure that all the netlink socket used have a proper timeout set on them to have the possibility to return Added test to emulate the watchMiss condition Signed-off-by: Flavio Crisciani <flavio.crisciani@docker.com>
173 lines
3.7 KiB
Go
173 lines
3.7 KiB
Go
package overlay
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/docker/docker/pkg/plugingetter"
|
|
"github.com/docker/libkv/store/consul"
|
|
"github.com/docker/libnetwork/datastore"
|
|
"github.com/docker/libnetwork/discoverapi"
|
|
"github.com/docker/libnetwork/driverapi"
|
|
"github.com/docker/libnetwork/netlabel"
|
|
_ "github.com/docker/libnetwork/testutils"
|
|
"github.com/vishvananda/netlink/nl"
|
|
)
|
|
|
|
func init() {
|
|
consul.Register()
|
|
}
|
|
|
|
type driverTester struct {
|
|
t *testing.T
|
|
d *driver
|
|
}
|
|
|
|
const testNetworkType = "overlay"
|
|
|
|
func setupDriver(t *testing.T) *driverTester {
|
|
dt := &driverTester{t: t}
|
|
config := make(map[string]interface{})
|
|
config[netlabel.GlobalKVClient] = discoverapi.DatastoreConfigData{
|
|
Scope: datastore.GlobalScope,
|
|
Provider: "consul",
|
|
Address: "127.0.0.01:8500",
|
|
}
|
|
|
|
if err := Init(dt, config); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
iface, err := net.InterfaceByName("eth0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
addrs, err := iface.Addrs()
|
|
if err != nil || len(addrs) == 0 {
|
|
t.Fatal(err)
|
|
}
|
|
data := discoverapi.NodeDiscoveryData{
|
|
Address: addrs[0].String(),
|
|
Self: true,
|
|
}
|
|
dt.d.DiscoverNew(discoverapi.NodeDiscovery, data)
|
|
return dt
|
|
}
|
|
|
|
func cleanupDriver(t *testing.T, dt *driverTester) {
|
|
ch := make(chan struct{})
|
|
go func() {
|
|
Fini(dt.d)
|
|
close(ch)
|
|
}()
|
|
|
|
select {
|
|
case <-ch:
|
|
case <-time.After(10 * time.Second):
|
|
t.Fatal("test timed out because Fini() did not return on time")
|
|
}
|
|
}
|
|
|
|
func (dt *driverTester) GetPluginGetter() plugingetter.PluginGetter {
|
|
return nil
|
|
}
|
|
|
|
func (dt *driverTester) RegisterDriver(name string, drv driverapi.Driver,
|
|
cap driverapi.Capability) error {
|
|
if name != testNetworkType {
|
|
dt.t.Fatalf("Expected driver register name to be %q. Instead got %q",
|
|
testNetworkType, name)
|
|
}
|
|
|
|
if _, ok := drv.(*driver); !ok {
|
|
dt.t.Fatalf("Expected driver type to be %T. Instead got %T",
|
|
&driver{}, drv)
|
|
}
|
|
|
|
dt.d = drv.(*driver)
|
|
return nil
|
|
}
|
|
|
|
func TestOverlayInit(t *testing.T) {
|
|
if err := Init(&driverTester{t: t}, nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestOverlayFiniWithoutConfig(t *testing.T) {
|
|
dt := &driverTester{t: t}
|
|
if err := Init(dt, nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cleanupDriver(t, dt)
|
|
}
|
|
|
|
func TestOverlayConfig(t *testing.T) {
|
|
dt := setupDriver(t)
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
d := dt.d
|
|
if d.notifyCh == nil {
|
|
t.Fatal("Driver notify channel wasn't initialzed after Config method")
|
|
}
|
|
|
|
if d.exitCh == nil {
|
|
t.Fatal("Driver serfloop exit channel wasn't initialzed after Config method")
|
|
}
|
|
|
|
if d.serfInstance == nil {
|
|
t.Fatal("Driver serfinstance hasn't been initialized after Config method")
|
|
}
|
|
|
|
cleanupDriver(t, dt)
|
|
}
|
|
|
|
func TestOverlayType(t *testing.T) {
|
|
dt := &driverTester{t: t}
|
|
if err := Init(dt, nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if dt.d.Type() != testNetworkType {
|
|
t.Fatalf("Expected Type() to return %q. Instead got %q", testNetworkType,
|
|
dt.d.Type())
|
|
}
|
|
}
|
|
|
|
// Test that the netlink socket close unblock the watchMiss to avoid deadlock
|
|
func TestNetlinkSocket(t *testing.T) {
|
|
// This is the same code used by the overlay driver to create the netlink interface
|
|
// for the watch miss
|
|
nlSock, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_NEIGH)
|
|
if err != nil {
|
|
t.Fatal()
|
|
}
|
|
// set the receive timeout to not remain stuck on the RecvFrom if the fd gets closed
|
|
tv := syscall.NsecToTimeval(soTimeout.Nanoseconds())
|
|
err = nlSock.SetReceiveTimeout(&tv)
|
|
if err != nil {
|
|
t.Fatal()
|
|
}
|
|
n := &network{id: "testnetid"}
|
|
ch := make(chan error)
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
go func() {
|
|
n.watchMiss(nlSock)
|
|
ch <- nil
|
|
}()
|
|
time.Sleep(5 * time.Second)
|
|
nlSock.Close()
|
|
select {
|
|
case <-ch:
|
|
case <-ctx.Done():
|
|
{
|
|
t.Fatalf("Timeout expired")
|
|
}
|
|
}
|
|
}
|