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 }