github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/libnetwork/libnetwork_internal_test.go (about)

     1  package libnetwork
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net"
     7  	"runtime"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/docker/docker/libnetwork/datastore"
    12  	"github.com/docker/docker/libnetwork/discoverapi"
    13  	"github.com/docker/docker/libnetwork/driverapi"
    14  	"github.com/docker/docker/libnetwork/internal/setmatrix"
    15  	"github.com/docker/docker/libnetwork/ipamapi"
    16  	"github.com/docker/docker/libnetwork/netlabel"
    17  	"github.com/docker/docker/libnetwork/netutils"
    18  	"github.com/docker/docker/libnetwork/testutils"
    19  	"github.com/docker/docker/libnetwork/types"
    20  	"gotest.tools/v3/skip"
    21  )
    22  
    23  func TestNetworkMarshalling(t *testing.T) {
    24  	n := &network{
    25  		name:        "Miao",
    26  		id:          "abccba",
    27  		ipamType:    "default",
    28  		addrSpace:   "viola",
    29  		networkType: "bridge",
    30  		enableIPv6:  true,
    31  		persist:     true,
    32  		configOnly:  true,
    33  		configFrom:  "configOnlyX",
    34  		ipamOptions: map[string]string{
    35  			netlabel.MacAddress: "a:b:c:d:e:f",
    36  			"primary":           "",
    37  		},
    38  		ipamV4Config: []*IpamConf{
    39  			{
    40  				PreferredPool: "10.2.0.0/16",
    41  				SubPool:       "10.2.0.0/24",
    42  				Gateway:       "",
    43  				AuxAddresses:  nil,
    44  			},
    45  			{
    46  				PreferredPool: "10.2.0.0/16",
    47  				SubPool:       "10.2.1.0/24",
    48  				Gateway:       "10.2.1.254",
    49  			},
    50  		},
    51  		ipamV6Config: []*IpamConf{
    52  			{
    53  				PreferredPool: "abcd::/64",
    54  				SubPool:       "abcd:abcd:abcd:abcd:abcd::/80",
    55  				Gateway:       "abcd::29/64",
    56  				AuxAddresses:  nil,
    57  			},
    58  		},
    59  		ipamV4Info: []*IpamInfo{
    60  			{
    61  				PoolID: "ipoolverde123",
    62  				Meta: map[string]string{
    63  					netlabel.Gateway: "10.2.1.255/16",
    64  				},
    65  				IPAMData: driverapi.IPAMData{
    66  					AddressSpace: "viola",
    67  					Pool: &net.IPNet{
    68  						IP:   net.IP{10, 2, 0, 0},
    69  						Mask: net.IPMask{255, 255, 255, 0},
    70  					},
    71  					Gateway:      nil,
    72  					AuxAddresses: nil,
    73  				},
    74  			},
    75  			{
    76  				PoolID: "ipoolblue345",
    77  				Meta: map[string]string{
    78  					netlabel.Gateway: "10.2.1.255/16",
    79  				},
    80  				IPAMData: driverapi.IPAMData{
    81  					AddressSpace: "viola",
    82  					Pool: &net.IPNet{
    83  						IP:   net.IP{10, 2, 1, 0},
    84  						Mask: net.IPMask{255, 255, 255, 0},
    85  					},
    86  					Gateway: &net.IPNet{IP: net.IP{10, 2, 1, 254}, Mask: net.IPMask{255, 255, 255, 0}},
    87  					AuxAddresses: map[string]*net.IPNet{
    88  						"ip3": {IP: net.IP{10, 2, 1, 3}, Mask: net.IPMask{255, 255, 255, 0}},
    89  						"ip5": {IP: net.IP{10, 2, 1, 55}, Mask: net.IPMask{255, 255, 255, 0}},
    90  					},
    91  				},
    92  			},
    93  			{
    94  				PoolID: "weirdinfo",
    95  				IPAMData: driverapi.IPAMData{
    96  					Gateway: &net.IPNet{
    97  						IP:   net.IP{11, 2, 1, 255},
    98  						Mask: net.IPMask{255, 0, 0, 0},
    99  					},
   100  				},
   101  			},
   102  		},
   103  		ipamV6Info: []*IpamInfo{
   104  			{
   105  				PoolID: "ipoolv6",
   106  				IPAMData: driverapi.IPAMData{
   107  					AddressSpace: "viola",
   108  					Pool: &net.IPNet{
   109  						IP:   net.IP{0xab, 0xcd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
   110  						Mask: net.IPMask{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0},
   111  					},
   112  					Gateway: &net.IPNet{
   113  						IP:   net.IP{0xab, 0xcd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29},
   114  						Mask: net.IPMask{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0},
   115  					},
   116  					AuxAddresses: nil,
   117  				},
   118  			},
   119  		},
   120  		labels: map[string]string{
   121  			"color":        "blue",
   122  			"superimposed": "",
   123  		},
   124  		created: time.Now(),
   125  	}
   126  
   127  	b, err := json.Marshal(n)
   128  	if err != nil {
   129  		t.Fatal(err)
   130  	}
   131  
   132  	nn := &network{}
   133  	err = json.Unmarshal(b, nn)
   134  	if err != nil {
   135  		t.Fatal(err)
   136  	}
   137  
   138  	if n.name != nn.name || n.id != nn.id || n.networkType != nn.networkType || n.ipamType != nn.ipamType ||
   139  		n.addrSpace != nn.addrSpace || n.enableIPv6 != nn.enableIPv6 ||
   140  		n.persist != nn.persist || !compareIpamConfList(n.ipamV4Config, nn.ipamV4Config) ||
   141  		!compareIpamInfoList(n.ipamV4Info, nn.ipamV4Info) || !compareIpamConfList(n.ipamV6Config, nn.ipamV6Config) ||
   142  		!compareIpamInfoList(n.ipamV6Info, nn.ipamV6Info) ||
   143  		!compareStringMaps(n.ipamOptions, nn.ipamOptions) ||
   144  		!compareStringMaps(n.labels, nn.labels) ||
   145  		!n.created.Equal(nn.created) ||
   146  		n.configOnly != nn.configOnly || n.configFrom != nn.configFrom {
   147  		t.Fatalf("JSON marsh/unmarsh failed."+
   148  			"\nOriginal:\n%#v\nDecoded:\n%#v"+
   149  			"\nOriginal ipamV4Conf: %#v\n\nDecoded ipamV4Conf: %#v"+
   150  			"\nOriginal ipamV4Info: %s\n\nDecoded ipamV4Info: %s"+
   151  			"\nOriginal ipamV6Conf: %#v\n\nDecoded ipamV6Conf: %#v"+
   152  			"\nOriginal ipamV6Info: %s\n\nDecoded ipamV6Info: %s",
   153  			n, nn, printIpamConf(n.ipamV4Config), printIpamConf(nn.ipamV4Config),
   154  			printIpamInfo(n.ipamV4Info), printIpamInfo(nn.ipamV4Info),
   155  			printIpamConf(n.ipamV6Config), printIpamConf(nn.ipamV6Config),
   156  			printIpamInfo(n.ipamV6Info), printIpamInfo(nn.ipamV6Info))
   157  	}
   158  }
   159  
   160  func printIpamConf(list []*IpamConf) string {
   161  	s := "\n[]*IpamConfig{"
   162  	for _, i := range list {
   163  		s = fmt.Sprintf("%s %v,", s, i)
   164  	}
   165  	s = fmt.Sprintf("%s}", s)
   166  	return s
   167  }
   168  
   169  func printIpamInfo(list []*IpamInfo) string {
   170  	s := "\n[]*IpamInfo{"
   171  	for _, i := range list {
   172  		s = fmt.Sprintf("%s\n{\n%s\n}", s, i)
   173  	}
   174  	s = fmt.Sprintf("%s\n}", s)
   175  	return s
   176  }
   177  
   178  func TestEndpointMarshalling(t *testing.T) {
   179  	ip, nw6, err := net.ParseCIDR("2001:db8:4003::122/64")
   180  	if err != nil {
   181  		t.Fatal(err)
   182  	}
   183  	nw6.IP = ip
   184  
   185  	var lla []*net.IPNet
   186  	for _, nw := range []string{"169.254.0.1/16", "169.254.1.1/16", "169.254.2.2/16"} {
   187  		ll, _ := types.ParseCIDR(nw)
   188  		lla = append(lla, ll)
   189  	}
   190  
   191  	e := &endpoint{
   192  		name:      "Bau",
   193  		id:        "efghijklmno",
   194  		sandboxID: "ambarabaciccicocco",
   195  		anonymous: true,
   196  		iface: &endpointInterface{
   197  			mac: []byte{11, 12, 13, 14, 15, 16},
   198  			addr: &net.IPNet{
   199  				IP:   net.IP{10, 0, 1, 23},
   200  				Mask: net.IPMask{255, 255, 255, 0},
   201  			},
   202  			addrv6:    nw6,
   203  			srcName:   "veth12ab1314",
   204  			dstPrefix: "eth",
   205  			v4PoolID:  "poolpool",
   206  			v6PoolID:  "poolv6",
   207  			llAddrs:   lla,
   208  		},
   209  	}
   210  
   211  	b, err := json.Marshal(e)
   212  	if err != nil {
   213  		t.Fatal(err)
   214  	}
   215  
   216  	ee := &endpoint{}
   217  	err = json.Unmarshal(b, ee)
   218  	if err != nil {
   219  		t.Fatal(err)
   220  	}
   221  
   222  	if e.name != ee.name || e.id != ee.id || e.sandboxID != ee.sandboxID || !compareEndpointInterface(e.iface, ee.iface) || e.anonymous != ee.anonymous {
   223  		t.Fatalf("JSON marsh/unmarsh failed.\nOriginal:\n%#v\nDecoded:\n%#v\nOriginal iface: %#v\nDecodediface:\n%#v", e, ee, e.iface, ee.iface)
   224  	}
   225  }
   226  
   227  func compareEndpointInterface(a, b *endpointInterface) bool {
   228  	if a == b {
   229  		return true
   230  	}
   231  	if a == nil || b == nil {
   232  		return false
   233  	}
   234  	return a.srcName == b.srcName && a.dstPrefix == b.dstPrefix && a.v4PoolID == b.v4PoolID && a.v6PoolID == b.v6PoolID &&
   235  		types.CompareIPNet(a.addr, b.addr) && types.CompareIPNet(a.addrv6, b.addrv6) && compareNwLists(a.llAddrs, b.llAddrs)
   236  }
   237  
   238  func compareIpamConfList(listA, listB []*IpamConf) bool {
   239  	var a, b *IpamConf
   240  	if len(listA) != len(listB) {
   241  		return false
   242  	}
   243  	for i := 0; i < len(listA); i++ {
   244  		a = listA[i]
   245  		b = listB[i]
   246  		if a.PreferredPool != b.PreferredPool ||
   247  			a.SubPool != b.SubPool ||
   248  			a.Gateway != b.Gateway || !compareStringMaps(a.AuxAddresses, b.AuxAddresses) {
   249  			return false
   250  		}
   251  	}
   252  	return true
   253  }
   254  
   255  func compareIpamInfoList(listA, listB []*IpamInfo) bool {
   256  	var a, b *IpamInfo
   257  	if len(listA) != len(listB) {
   258  		return false
   259  	}
   260  	for i := 0; i < len(listA); i++ {
   261  		a = listA[i]
   262  		b = listB[i]
   263  		if a.PoolID != b.PoolID || !compareStringMaps(a.Meta, b.Meta) ||
   264  			!types.CompareIPNet(a.Gateway, b.Gateway) ||
   265  			a.AddressSpace != b.AddressSpace ||
   266  			!types.CompareIPNet(a.Pool, b.Pool) ||
   267  			!compareAddresses(a.AuxAddresses, b.AuxAddresses) {
   268  			return false
   269  		}
   270  	}
   271  	return true
   272  }
   273  
   274  func compareStringMaps(a, b map[string]string) bool {
   275  	if len(a) != len(b) {
   276  		return false
   277  	}
   278  	if len(a) > 0 {
   279  		for k := range a {
   280  			if a[k] != b[k] {
   281  				return false
   282  			}
   283  		}
   284  	}
   285  	return true
   286  }
   287  
   288  func compareAddresses(a, b map[string]*net.IPNet) bool {
   289  	if len(a) != len(b) {
   290  		return false
   291  	}
   292  	if len(a) > 0 {
   293  		for k := range a {
   294  			if !types.CompareIPNet(a[k], b[k]) {
   295  				return false
   296  			}
   297  		}
   298  	}
   299  	return true
   300  }
   301  
   302  func compareNwLists(a, b []*net.IPNet) bool {
   303  	if len(a) != len(b) {
   304  		return false
   305  	}
   306  	for k := range a {
   307  		if !types.CompareIPNet(a[k], b[k]) {
   308  			return false
   309  		}
   310  	}
   311  	return true
   312  }
   313  
   314  func TestAuxAddresses(t *testing.T) {
   315  	c, err := New()
   316  	if err != nil {
   317  		t.Fatal(err)
   318  	}
   319  	defer c.Stop()
   320  
   321  	n := &network{ipamType: ipamapi.DefaultIPAM, networkType: "bridge", ctrlr: c.(*controller)}
   322  
   323  	input := []struct {
   324  		masterPool   string
   325  		subPool      string
   326  		auxAddresses map[string]string
   327  		good         bool
   328  	}{
   329  		{"192.168.0.0/16", "", map[string]string{"goodOne": "192.168.2.2"}, true},
   330  		{"192.168.0.0/16", "", map[string]string{"badOne": "192.169.2.3"}, false},
   331  		{"192.168.0.0/16", "192.168.1.0/24", map[string]string{"goodOne": "192.168.1.2"}, true},
   332  		{"192.168.0.0/16", "192.168.1.0/24", map[string]string{"stillGood": "192.168.2.4"}, true},
   333  		{"192.168.0.0/16", "192.168.1.0/24", map[string]string{"badOne": "192.169.2.4"}, false},
   334  	}
   335  
   336  	for _, i := range input {
   337  		n.ipamV4Config = []*IpamConf{{PreferredPool: i.masterPool, SubPool: i.subPool, AuxAddresses: i.auxAddresses}}
   338  
   339  		err = n.ipamAllocate()
   340  
   341  		if i.good != (err == nil) {
   342  			t.Fatalf("Unexpected result for %v: %v", i, err)
   343  		}
   344  
   345  		n.ipamRelease()
   346  	}
   347  }
   348  
   349  func TestSRVServiceQuery(t *testing.T) {
   350  	skip.If(t, runtime.GOOS == "windows", "test only works on linux")
   351  
   352  	c, err := New()
   353  	if err != nil {
   354  		t.Fatal(err)
   355  	}
   356  	defer c.Stop()
   357  
   358  	n, err := c.NewNetwork("bridge", "net1", "", nil)
   359  	if err != nil {
   360  		t.Fatal(err)
   361  	}
   362  	defer func() {
   363  		if err := n.Delete(); err != nil {
   364  			t.Fatal(err)
   365  		}
   366  	}()
   367  
   368  	ep, err := n.CreateEndpoint("testep")
   369  	if err != nil {
   370  		t.Fatal(err)
   371  	}
   372  
   373  	sb, err := c.NewSandbox("c1")
   374  	if err != nil {
   375  		t.Fatal(err)
   376  	}
   377  	defer func() {
   378  		if err := sb.Delete(); err != nil {
   379  			t.Fatal(err)
   380  		}
   381  	}()
   382  
   383  	err = ep.Join(sb)
   384  	if err != nil {
   385  		t.Fatal(err)
   386  	}
   387  
   388  	sr := svcInfo{
   389  		svcMap:     setmatrix.NewSetMatrix(),
   390  		svcIPv6Map: setmatrix.NewSetMatrix(),
   391  		ipMap:      setmatrix.NewSetMatrix(),
   392  		service:    make(map[string][]servicePorts),
   393  	}
   394  	// backing container for the service
   395  	cTarget := serviceTarget{
   396  		name: "task1.web.swarm",
   397  		ip:   net.ParseIP("192.168.10.2"),
   398  		port: 80,
   399  	}
   400  	// backing host for the service
   401  	hTarget := serviceTarget{
   402  		name: "node1.docker-cluster",
   403  		ip:   net.ParseIP("10.10.10.2"),
   404  		port: 45321,
   405  	}
   406  	httpPort := servicePorts{
   407  		portName: "_http",
   408  		proto:    "_tcp",
   409  		target:   []serviceTarget{cTarget},
   410  	}
   411  
   412  	extHTTPPort := servicePorts{
   413  		portName: "_host_http",
   414  		proto:    "_tcp",
   415  		target:   []serviceTarget{hTarget},
   416  	}
   417  	sr.service["web.swarm"] = append(sr.service["web.swarm"], httpPort)
   418  	sr.service["web.swarm"] = append(sr.service["web.swarm"], extHTTPPort)
   419  
   420  	c.(*controller).svcRecords[n.ID()] = sr
   421  
   422  	_, ip := ep.Info().Sandbox().ResolveService("_http._tcp.web.swarm")
   423  
   424  	if len(ip) == 0 {
   425  		t.Fatal(err)
   426  	}
   427  	if ip[0].String() != "192.168.10.2" {
   428  		t.Fatal(err)
   429  	}
   430  
   431  	_, ip = ep.Info().Sandbox().ResolveService("_host_http._tcp.web.swarm")
   432  
   433  	if len(ip) == 0 {
   434  		t.Fatal(err)
   435  	}
   436  	if ip[0].String() != "10.10.10.2" {
   437  		t.Fatal(err)
   438  	}
   439  
   440  	// Service name with invalid protocol name. Should fail without error
   441  	_, ip = ep.Info().Sandbox().ResolveService("_http._icmp.web.swarm")
   442  	if len(ip) != 0 {
   443  		t.Fatal("Valid response for invalid service name")
   444  	}
   445  }
   446  
   447  func TestServiceVIPReuse(t *testing.T) {
   448  	skip.If(t, runtime.GOOS == "windows", "test only works on linux")
   449  
   450  	c, err := New()
   451  	if err != nil {
   452  		t.Fatal(err)
   453  	}
   454  	defer c.Stop()
   455  
   456  	n, err := c.NewNetwork("bridge", "net1", "", nil)
   457  	if err != nil {
   458  		t.Fatal(err)
   459  	}
   460  	defer func() {
   461  		if err := n.Delete(); err != nil {
   462  			t.Fatal(err)
   463  		}
   464  	}()
   465  
   466  	ep, err := n.CreateEndpoint("testep")
   467  	if err != nil {
   468  		t.Fatal(err)
   469  	}
   470  
   471  	sb, err := c.NewSandbox("c1")
   472  	if err != nil {
   473  		t.Fatal(err)
   474  	}
   475  	defer func() {
   476  		if err := sb.Delete(); err != nil {
   477  			t.Fatal(err)
   478  		}
   479  	}()
   480  
   481  	err = ep.Join(sb)
   482  	if err != nil {
   483  		t.Fatal(err)
   484  	}
   485  
   486  	// Add 2 services with same name but different service ID to share the same VIP
   487  	n.(*network).addSvcRecords("ep1", "service_test", "serviceID1", net.ParseIP("192.168.0.1"), net.IP{}, true, "test")
   488  	n.(*network).addSvcRecords("ep2", "service_test", "serviceID2", net.ParseIP("192.168.0.1"), net.IP{}, true, "test")
   489  
   490  	ipToResolve := netutils.ReverseIP("192.168.0.1")
   491  
   492  	ipList, _ := n.(*network).ResolveName("service_test", types.IPv4)
   493  	if len(ipList) == 0 {
   494  		t.Fatal("There must be the VIP")
   495  	}
   496  	if len(ipList) != 1 {
   497  		t.Fatal("It must return only 1 VIP")
   498  	}
   499  	if ipList[0].String() != "192.168.0.1" {
   500  		t.Fatal("The service VIP is 192.168.0.1")
   501  	}
   502  	name := n.(*network).ResolveIP(ipToResolve)
   503  	if name == "" {
   504  		t.Fatal("It must return a name")
   505  	}
   506  	if name != "service_test.net1" {
   507  		t.Fatalf("It must return the service_test.net1 != %s", name)
   508  	}
   509  
   510  	// Delete service record for one of the services, the IP should remain because one service is still associated with it
   511  	n.(*network).deleteSvcRecords("ep1", "service_test", "serviceID1", net.ParseIP("192.168.0.1"), net.IP{}, true, "test")
   512  	ipList, _ = n.(*network).ResolveName("service_test", types.IPv4)
   513  	if len(ipList) == 0 {
   514  		t.Fatal("There must be the VIP")
   515  	}
   516  	if len(ipList) != 1 {
   517  		t.Fatal("It must return only 1 VIP")
   518  	}
   519  	if ipList[0].String() != "192.168.0.1" {
   520  		t.Fatal("The service VIP is 192.168.0.1")
   521  	}
   522  	name = n.(*network).ResolveIP(ipToResolve)
   523  	if name == "" {
   524  		t.Fatal("It must return a name")
   525  	}
   526  	if name != "service_test.net1" {
   527  		t.Fatalf("It must return the service_test.net1 != %s", name)
   528  	}
   529  
   530  	// Delete again the service using the previous service ID, nothing should happen
   531  	n.(*network).deleteSvcRecords("ep2", "service_test", "serviceID1", net.ParseIP("192.168.0.1"), net.IP{}, true, "test")
   532  	ipList, _ = n.(*network).ResolveName("service_test", types.IPv4)
   533  	if len(ipList) == 0 {
   534  		t.Fatal("There must be the VIP")
   535  	}
   536  	if len(ipList) != 1 {
   537  		t.Fatal("It must return only 1 VIP")
   538  	}
   539  	if ipList[0].String() != "192.168.0.1" {
   540  		t.Fatal("The service VIP is 192.168.0.1")
   541  	}
   542  	name = n.(*network).ResolveIP(ipToResolve)
   543  	if name == "" {
   544  		t.Fatal("It must return a name")
   545  	}
   546  	if name != "service_test.net1" {
   547  		t.Fatalf("It must return the service_test.net1 != %s", name)
   548  	}
   549  
   550  	// Delete now using the second service ID, now all the entries should be gone
   551  	n.(*network).deleteSvcRecords("ep2", "service_test", "serviceID2", net.ParseIP("192.168.0.1"), net.IP{}, true, "test")
   552  	ipList, _ = n.(*network).ResolveName("service_test", types.IPv4)
   553  	if len(ipList) != 0 {
   554  		t.Fatal("All the VIPs should be gone now")
   555  	}
   556  	name = n.(*network).ResolveIP(ipToResolve)
   557  	if name != "" {
   558  		t.Fatalf("It must return empty no more services associated, instead:%s", name)
   559  	}
   560  }
   561  
   562  func TestIpamReleaseOnNetDriverFailures(t *testing.T) {
   563  	skip.If(t, runtime.GOOS == "windows", "test only works on linux")
   564  
   565  	if !testutils.IsRunningInContainer() {
   566  		defer testutils.SetupTestOSContext(t)()
   567  	}
   568  
   569  	cfgOptions, err := OptionBoltdbWithRandomDBFile()
   570  	if err != nil {
   571  		t.Fatal(err)
   572  	}
   573  	c, err := New(cfgOptions...)
   574  	if err != nil {
   575  		t.Fatal(err)
   576  	}
   577  	defer c.Stop()
   578  
   579  	cc := c.(*controller)
   580  
   581  	if err := cc.drvRegistry.AddDriver(badDriverName, badDriverInit, nil); err != nil {
   582  		t.Fatal(err)
   583  	}
   584  
   585  	// Test whether ipam state release is invoked  on network create failure from net driver
   586  	// by checking whether subsequent network creation requesting same gateway IP succeeds
   587  	ipamOpt := NetworkOptionIpam(ipamapi.DefaultIPAM, "", []*IpamConf{{PreferredPool: "10.34.0.0/16", Gateway: "10.34.255.254"}}, nil, nil)
   588  	if _, err := c.NewNetwork(badDriverName, "badnet1", "", ipamOpt); err == nil {
   589  		t.Fatalf("bad network driver should have failed network creation")
   590  	}
   591  
   592  	gnw, err := c.NewNetwork("bridge", "goodnet1", "", ipamOpt)
   593  	if err != nil {
   594  		t.Fatal(err)
   595  	}
   596  	if err := gnw.Delete(); err != nil {
   597  		t.Fatal(err)
   598  	}
   599  
   600  	// Now check whether ipam release works on endpoint creation failure
   601  	bd.failNetworkCreation = false
   602  	bnw, err := c.NewNetwork(badDriverName, "badnet2", "", ipamOpt)
   603  	if err != nil {
   604  		t.Fatal(err)
   605  	}
   606  	defer func() {
   607  		if err := bnw.Delete(); err != nil {
   608  			t.Fatal(err)
   609  		}
   610  	}()
   611  
   612  	if _, err := bnw.CreateEndpoint("ep0"); err == nil {
   613  		t.Fatalf("bad network driver should have failed endpoint creation")
   614  	}
   615  
   616  	// Now create good bridge network with different gateway
   617  	ipamOpt2 := NetworkOptionIpam(ipamapi.DefaultIPAM, "", []*IpamConf{{PreferredPool: "10.35.0.0/16", Gateway: "10.35.255.253"}}, nil, nil)
   618  	gnw, err = c.NewNetwork("bridge", "goodnet2", "", ipamOpt2)
   619  	if err != nil {
   620  		t.Fatal(err)
   621  	}
   622  	defer func() {
   623  		if err := gnw.Delete(); err != nil {
   624  			t.Fatal(err)
   625  		}
   626  	}()
   627  
   628  	ep, err := gnw.CreateEndpoint("ep1")
   629  	if err != nil {
   630  		t.Fatal(err)
   631  	}
   632  	defer ep.Delete(false) //nolint:errcheck
   633  
   634  	expectedIP, _ := types.ParseCIDR("10.35.0.1/16")
   635  	if !types.CompareIPNet(ep.Info().Iface().Address(), expectedIP) {
   636  		t.Fatalf("Ipam release must have failed, endpoint has unexpected address: %v", ep.Info().Iface().Address())
   637  	}
   638  }
   639  
   640  var badDriverName = "bad network driver"
   641  
   642  type badDriver struct {
   643  	failNetworkCreation bool
   644  }
   645  
   646  var bd = badDriver{failNetworkCreation: true}
   647  
   648  func badDriverInit(reg driverapi.DriverCallback, opt map[string]interface{}) error {
   649  	return reg.RegisterDriver(badDriverName, &bd, driverapi.Capability{DataScope: datastore.LocalScope})
   650  }
   651  
   652  func (b *badDriver) CreateNetwork(nid string, options map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
   653  	if b.failNetworkCreation {
   654  		return fmt.Errorf("I will not create any network")
   655  	}
   656  	return nil
   657  }
   658  func (b *badDriver) DeleteNetwork(nid string) error {
   659  	return nil
   660  }
   661  func (b *badDriver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, options map[string]interface{}) error {
   662  	return fmt.Errorf("I will not create any endpoint")
   663  }
   664  func (b *badDriver) DeleteEndpoint(nid, eid string) error {
   665  	return nil
   666  }
   667  func (b *badDriver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) {
   668  	return nil, nil
   669  }
   670  func (b *badDriver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
   671  	return fmt.Errorf("I will not allow any join")
   672  }
   673  func (b *badDriver) Leave(nid, eid string) error {
   674  	return nil
   675  }
   676  func (b *badDriver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error {
   677  	return nil
   678  }
   679  func (b *badDriver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error {
   680  	return nil
   681  }
   682  func (b *badDriver) Type() string {
   683  	return badDriverName
   684  }
   685  func (b *badDriver) IsBuiltIn() bool {
   686  	return false
   687  }
   688  func (b *badDriver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error {
   689  	return nil
   690  }
   691  func (b *badDriver) RevokeExternalConnectivity(nid, eid string) error {
   692  	return nil
   693  }
   694  
   695  func (b *badDriver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {
   696  	return nil, types.NotImplementedErrorf("not implemented")
   697  }
   698  
   699  func (b *badDriver) NetworkFree(id string) error {
   700  	return types.NotImplementedErrorf("not implemented")
   701  }
   702  
   703  func (b *badDriver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
   704  }
   705  
   706  func (b *badDriver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
   707  	return "", nil
   708  }