github.com/khulnasoft-lab/khulnasoft@v26.0.1-0.20240328202558-330a6f959fe0+incompatible/libnetwork/libnetwork_internal_test.go (about)

     1  package libnetwork
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"net"
     8  	"reflect"
     9  	"runtime"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/docker/docker/internal/testutils/netnsutils"
    14  	"github.com/docker/docker/libnetwork/driverapi"
    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/scope"
    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  		iface: &EndpointInterface{
   196  			mac: []byte{11, 12, 13, 14, 15, 16},
   197  			addr: &net.IPNet{
   198  				IP:   net.IP{10, 0, 1, 23},
   199  				Mask: net.IPMask{255, 255, 255, 0},
   200  			},
   201  			addrv6:    nw6,
   202  			srcName:   "veth12ab1314",
   203  			dstPrefix: "eth",
   204  			v4PoolID:  "poolpool",
   205  			v6PoolID:  "poolv6",
   206  			llAddrs:   lla,
   207  		},
   208  		dnsNames: []string{"test", "foobar", "baz"},
   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 || !reflect.DeepEqual(e.dnsNames, ee.dnsNames) || !compareEndpointInterface(e.iface, ee.iface) {
   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  	defer netnsutils.SetupTestOSContext(t)()
   316  
   317  	c, err := New(OptionBoltdbWithRandomDBFile(t))
   318  	if err != nil {
   319  		t.Fatal(err)
   320  	}
   321  	defer c.Stop()
   322  
   323  	n := &Network{ipamType: ipamapi.DefaultIPAM, networkType: "bridge", ctrlr: c}
   324  
   325  	input := []struct {
   326  		masterPool   string
   327  		subPool      string
   328  		auxAddresses map[string]string
   329  		good         bool
   330  	}{
   331  		{"192.168.0.0/16", "", map[string]string{"goodOne": "192.168.2.2"}, true},
   332  		{"192.168.0.0/16", "", map[string]string{"badOne": "192.169.2.3"}, false},
   333  		{"192.168.0.0/16", "192.168.1.0/24", map[string]string{"goodOne": "192.168.1.2"}, true},
   334  		{"192.168.0.0/16", "192.168.1.0/24", map[string]string{"stillGood": "192.168.2.4"}, true},
   335  		{"192.168.0.0/16", "192.168.1.0/24", map[string]string{"badOne": "192.169.2.4"}, false},
   336  	}
   337  
   338  	for _, i := range input {
   339  		n.ipamV4Config = []*IpamConf{{PreferredPool: i.masterPool, SubPool: i.subPool, AuxAddresses: i.auxAddresses}}
   340  
   341  		err = n.ipamAllocate()
   342  
   343  		if i.good != (err == nil) {
   344  			t.Fatalf("Unexpected result for %v: %v", i, err)
   345  		}
   346  
   347  		n.ipamRelease()
   348  	}
   349  }
   350  
   351  func TestSRVServiceQuery(t *testing.T) {
   352  	skip.If(t, runtime.GOOS == "windows", "test only works on linux")
   353  
   354  	defer netnsutils.SetupTestOSContext(t)()
   355  
   356  	c, err := New(OptionBoltdbWithRandomDBFile(t))
   357  	if err != nil {
   358  		t.Fatal(err)
   359  	}
   360  	defer c.Stop()
   361  
   362  	n, err := c.NewNetwork("bridge", "net1", "", nil)
   363  	if err != nil {
   364  		t.Fatal(err)
   365  	}
   366  	defer func() {
   367  		if err := n.Delete(); err != nil {
   368  			t.Fatal(err)
   369  		}
   370  	}()
   371  
   372  	ep, err := n.CreateEndpoint("testep")
   373  	if err != nil {
   374  		t.Fatal(err)
   375  	}
   376  
   377  	sb, err := c.NewSandbox("c1")
   378  	if err != nil {
   379  		t.Fatal(err)
   380  	}
   381  	defer func() {
   382  		if err := sb.Delete(); err != nil {
   383  			t.Fatal(err)
   384  		}
   385  	}()
   386  
   387  	err = ep.Join(sb)
   388  	if err != nil {
   389  		t.Fatal(err)
   390  	}
   391  
   392  	sr := &svcInfo{
   393  		service: make(map[string][]servicePorts),
   394  	}
   395  	// backing container for the service
   396  	cTarget := serviceTarget{
   397  		name: "task1.web.swarm",
   398  		ip:   net.ParseIP("192.168.10.2"),
   399  		port: 80,
   400  	}
   401  	// backing host for the service
   402  	hTarget := serviceTarget{
   403  		name: "node1.docker-cluster",
   404  		ip:   net.ParseIP("10.10.10.2"),
   405  		port: 45321,
   406  	}
   407  	httpPort := servicePorts{
   408  		portName: "_http",
   409  		proto:    "_tcp",
   410  		target:   []serviceTarget{cTarget},
   411  	}
   412  
   413  	extHTTPPort := servicePorts{
   414  		portName: "_host_http",
   415  		proto:    "_tcp",
   416  		target:   []serviceTarget{hTarget},
   417  	}
   418  	sr.service["web.swarm"] = append(sr.service["web.swarm"], httpPort)
   419  	sr.service["web.swarm"] = append(sr.service["web.swarm"], extHTTPPort)
   420  
   421  	c.svcRecords[n.ID()] = sr
   422  
   423  	ctx := context.Background()
   424  	_, ip := ep.Info().Sandbox().ResolveService(ctx, "_http._tcp.web.swarm")
   425  
   426  	if len(ip) == 0 {
   427  		t.Fatal(err)
   428  	}
   429  	if ip[0].String() != "192.168.10.2" {
   430  		t.Fatal(err)
   431  	}
   432  
   433  	_, ip = ep.Info().Sandbox().ResolveService(ctx, "_host_http._tcp.web.swarm")
   434  
   435  	if len(ip) == 0 {
   436  		t.Fatal(err)
   437  	}
   438  	if ip[0].String() != "10.10.10.2" {
   439  		t.Fatal(err)
   440  	}
   441  
   442  	// Service name with invalid protocol name. Should fail without error
   443  	_, ip = ep.Info().Sandbox().ResolveService(ctx, "_http._icmp.web.swarm")
   444  	if len(ip) != 0 {
   445  		t.Fatal("Valid response for invalid service name")
   446  	}
   447  }
   448  
   449  func TestServiceVIPReuse(t *testing.T) {
   450  	skip.If(t, runtime.GOOS == "windows", "test only works on linux")
   451  
   452  	defer netnsutils.SetupTestOSContext(t)()
   453  
   454  	c, err := New(OptionBoltdbWithRandomDBFile(t))
   455  	if err != nil {
   456  		t.Fatal(err)
   457  	}
   458  	defer c.Stop()
   459  
   460  	n, err := c.NewNetwork("bridge", "net1", "", nil)
   461  	if err != nil {
   462  		t.Fatal(err)
   463  	}
   464  	defer func() {
   465  		if err := n.Delete(); err != nil {
   466  			t.Fatal(err)
   467  		}
   468  	}()
   469  
   470  	ep, err := n.CreateEndpoint("testep")
   471  	if err != nil {
   472  		t.Fatal(err)
   473  	}
   474  
   475  	sb, err := c.NewSandbox("c1")
   476  	if err != nil {
   477  		t.Fatal(err)
   478  	}
   479  	defer func() {
   480  		if err := sb.Delete(); err != nil {
   481  			t.Fatal(err)
   482  		}
   483  	}()
   484  
   485  	err = ep.Join(sb)
   486  	if err != nil {
   487  		t.Fatal(err)
   488  	}
   489  
   490  	// Add 2 services with same name but different service ID to share the same VIP
   491  	n.addSvcRecords("ep1", "service_test", "serviceID1", net.ParseIP("192.168.0.1"), net.IP{}, true, "test")
   492  	n.addSvcRecords("ep2", "service_test", "serviceID2", net.ParseIP("192.168.0.1"), net.IP{}, true, "test")
   493  
   494  	ipToResolve := netutils.ReverseIP("192.168.0.1")
   495  
   496  	ctx := context.Background()
   497  	ipList, _ := n.ResolveName(ctx, "service_test", types.IPv4)
   498  	if len(ipList) == 0 {
   499  		t.Fatal("There must be the VIP")
   500  	}
   501  	if len(ipList) != 1 {
   502  		t.Fatal("It must return only 1 VIP")
   503  	}
   504  	if ipList[0].String() != "192.168.0.1" {
   505  		t.Fatal("The service VIP is 192.168.0.1")
   506  	}
   507  	name := n.ResolveIP(ctx, ipToResolve)
   508  	if name == "" {
   509  		t.Fatal("It must return a name")
   510  	}
   511  	if name != "service_test.net1" {
   512  		t.Fatalf("It must return the service_test.net1 != %s", name)
   513  	}
   514  
   515  	// Delete service record for one of the services, the IP should remain because one service is still associated with it
   516  	n.deleteSvcRecords("ep1", "service_test", "serviceID1", net.ParseIP("192.168.0.1"), net.IP{}, true, "test")
   517  	ipList, _ = n.ResolveName(ctx, "service_test", types.IPv4)
   518  	if len(ipList) == 0 {
   519  		t.Fatal("There must be the VIP")
   520  	}
   521  	if len(ipList) != 1 {
   522  		t.Fatal("It must return only 1 VIP")
   523  	}
   524  	if ipList[0].String() != "192.168.0.1" {
   525  		t.Fatal("The service VIP is 192.168.0.1")
   526  	}
   527  	name = n.ResolveIP(ctx, ipToResolve)
   528  	if name == "" {
   529  		t.Fatal("It must return a name")
   530  	}
   531  	if name != "service_test.net1" {
   532  		t.Fatalf("It must return the service_test.net1 != %s", name)
   533  	}
   534  
   535  	// Delete again the service using the previous service ID, nothing should happen
   536  	n.deleteSvcRecords("ep2", "service_test", "serviceID1", net.ParseIP("192.168.0.1"), net.IP{}, true, "test")
   537  	ipList, _ = n.ResolveName(ctx, "service_test", types.IPv4)
   538  	if len(ipList) == 0 {
   539  		t.Fatal("There must be the VIP")
   540  	}
   541  	if len(ipList) != 1 {
   542  		t.Fatal("It must return only 1 VIP")
   543  	}
   544  	if ipList[0].String() != "192.168.0.1" {
   545  		t.Fatal("The service VIP is 192.168.0.1")
   546  	}
   547  	name = n.ResolveIP(ctx, ipToResolve)
   548  	if name == "" {
   549  		t.Fatal("It must return a name")
   550  	}
   551  	if name != "service_test.net1" {
   552  		t.Fatalf("It must return the service_test.net1 != %s", name)
   553  	}
   554  
   555  	// Delete now using the second service ID, now all the entries should be gone
   556  	n.deleteSvcRecords("ep2", "service_test", "serviceID2", net.ParseIP("192.168.0.1"), net.IP{}, true, "test")
   557  	ipList, _ = n.ResolveName(ctx, "service_test", types.IPv4)
   558  	if len(ipList) != 0 {
   559  		t.Fatal("All the VIPs should be gone now")
   560  	}
   561  	name = n.ResolveIP(ctx, ipToResolve)
   562  	if name != "" {
   563  		t.Fatalf("It must return empty no more services associated, instead:%s", name)
   564  	}
   565  }
   566  
   567  func TestIpamReleaseOnNetDriverFailures(t *testing.T) {
   568  	skip.If(t, runtime.GOOS == "windows", "test only works on linux")
   569  
   570  	defer netnsutils.SetupTestOSContext(t)()
   571  
   572  	c, err := New(OptionBoltdbWithRandomDBFile(t))
   573  	if err != nil {
   574  		t.Fatal(err)
   575  	}
   576  	defer c.Stop()
   577  
   578  	if err := badDriverRegister(&c.drvRegistry); err != nil {
   579  		t.Fatal(err)
   580  	}
   581  
   582  	// Test whether ipam state release is invoked  on network create failure from net driver
   583  	// by checking whether subsequent network creation requesting same gateway IP succeeds
   584  	ipamOpt := NetworkOptionIpam(ipamapi.DefaultIPAM, "", []*IpamConf{{PreferredPool: "10.34.0.0/16", Gateway: "10.34.255.254"}}, nil, nil)
   585  	if _, err := c.NewNetwork(badDriverName, "badnet1", "", ipamOpt); err == nil {
   586  		t.Fatalf("bad network driver should have failed network creation")
   587  	}
   588  
   589  	gnw, err := c.NewNetwork("bridge", "goodnet1", "", ipamOpt)
   590  	if err != nil {
   591  		t.Fatal(err)
   592  	}
   593  	if err := gnw.Delete(); err != nil {
   594  		t.Fatal(err)
   595  	}
   596  
   597  	// Now check whether ipam release works on endpoint creation failure
   598  	bd.failNetworkCreation = false
   599  	bnw, err := c.NewNetwork(badDriverName, "badnet2", "", ipamOpt)
   600  	if err != nil {
   601  		t.Fatal(err)
   602  	}
   603  	defer func() {
   604  		if err := bnw.Delete(); err != nil {
   605  			t.Fatal(err)
   606  		}
   607  	}()
   608  
   609  	if _, err := bnw.CreateEndpoint("ep0"); err == nil {
   610  		t.Fatalf("bad network driver should have failed endpoint creation")
   611  	}
   612  
   613  	// Now create good bridge network with different gateway
   614  	ipamOpt2 := NetworkOptionIpam(ipamapi.DefaultIPAM, "", []*IpamConf{{PreferredPool: "10.35.0.0/16", Gateway: "10.35.255.253"}}, nil, nil)
   615  	gnw, err = c.NewNetwork("bridge", "goodnet2", "", ipamOpt2)
   616  	if err != nil {
   617  		t.Fatal(err)
   618  	}
   619  	defer func() {
   620  		if err := gnw.Delete(); err != nil {
   621  			t.Fatal(err)
   622  		}
   623  	}()
   624  
   625  	ep, err := gnw.CreateEndpoint("ep1")
   626  	if err != nil {
   627  		t.Fatal(err)
   628  	}
   629  	defer ep.Delete(false) //nolint:errcheck
   630  
   631  	expectedIP, _ := types.ParseCIDR("10.35.0.1/16")
   632  	if !types.CompareIPNet(ep.Info().Iface().Address(), expectedIP) {
   633  		t.Fatalf("Ipam release must have failed, endpoint has unexpected address: %v", ep.Info().Iface().Address())
   634  	}
   635  }
   636  
   637  var badDriverName = "bad network driver"
   638  
   639  type badDriver struct {
   640  	failNetworkCreation bool
   641  }
   642  
   643  var bd = badDriver{failNetworkCreation: true}
   644  
   645  func badDriverRegister(reg driverapi.Registerer) error {
   646  	return reg.RegisterDriver(badDriverName, &bd, driverapi.Capability{DataScope: scope.Local})
   647  }
   648  
   649  func (b *badDriver) CreateNetwork(nid string, options map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
   650  	if b.failNetworkCreation {
   651  		return fmt.Errorf("I will not create any network")
   652  	}
   653  	return nil
   654  }
   655  
   656  func (b *badDriver) DeleteNetwork(nid string) error {
   657  	return nil
   658  }
   659  
   660  func (b *badDriver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, options map[string]interface{}) error {
   661  	return fmt.Errorf("I will not create any endpoint")
   662  }
   663  
   664  func (b *badDriver) DeleteEndpoint(nid, eid string) error {
   665  	return nil
   666  }
   667  
   668  func (b *badDriver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) {
   669  	return nil, nil
   670  }
   671  
   672  func (b *badDriver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
   673  	return fmt.Errorf("I will not allow any join")
   674  }
   675  
   676  func (b *badDriver) Leave(nid, eid string) error {
   677  	return nil
   678  }
   679  
   680  func (b *badDriver) Type() string {
   681  	return badDriverName
   682  }
   683  
   684  func (b *badDriver) IsBuiltIn() bool {
   685  	return false
   686  }
   687  
   688  func (b *badDriver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error {
   689  	return nil
   690  }
   691  
   692  func (b *badDriver) RevokeExternalConnectivity(nid, eid string) error {
   693  	return nil
   694  }
   695  
   696  func (b *badDriver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {
   697  	return nil, types.NotImplementedErrorf("not implemented")
   698  }
   699  
   700  func (b *badDriver) NetworkFree(id string) error {
   701  	return types.NotImplementedErrorf("not implemented")
   702  }
   703  
   704  func (b *badDriver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
   705  }
   706  
   707  func (b *badDriver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
   708  	return "", nil
   709  }