github.com/Mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/pkg/nettools/nettools_test.go (about) 1 /* 2 Copyright 2016 Mirantis 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package nettools 18 19 import ( 20 "fmt" 21 "log" 22 "net" 23 "os/exec" 24 "reflect" 25 "testing" 26 27 "github.com/containernetworking/cni/pkg/ns" 28 cnitypes "github.com/containernetworking/cni/pkg/types" 29 cnicurrent "github.com/containernetworking/cni/pkg/types/current" 30 "github.com/davecgh/go-spew/spew" 31 "github.com/vishvananda/netlink" 32 ) 33 34 const ( 35 innerHwAddr = "42:a4:a6:22:80:2e" 36 outerHwAddr = "42:b5:b7:33:91:3f" 37 secondInnerHwAddr = "42:a4:a6:22:80:2f" 38 secondOuterHwAddr = "42:b5:b7:33:91:3e" 39 dummyInnerHwAddr = "42:a4:a6:22:80:40" 40 dummyOuterHwAddr = "42:b5:b7:33:91:4f" 41 ) 42 43 func expectedExtractedLinkInfo(contNsPath string) *cnicurrent.Result { 44 return &cnicurrent.Result{ 45 Interfaces: []*cnicurrent.Interface{ 46 { 47 Name: "eth0", 48 Mac: innerHwAddr, 49 Sandbox: contNsPath, 50 }, 51 }, 52 IPs: []*cnicurrent.IPConfig{ 53 { 54 Version: "4", 55 Interface: 0, 56 Address: net.IPNet{ 57 IP: net.IP{10, 1, 90, 5}, 58 Mask: net.IPMask{255, 255, 255, 0}, 59 }, 60 Gateway: net.IP{10, 1, 90, 1}, 61 }, 62 }, 63 Routes: []*cnitypes.Route{ 64 { 65 Dst: net.IPNet{ 66 IP: net.IP{0, 0, 0, 0}, 67 Mask: net.IPMask{0, 0, 0, 0}, 68 }, 69 GW: net.IP{10, 1, 90, 1}, 70 }, 71 }, 72 } 73 } 74 75 // withTemporaryNSAvailable creates a new network namespace and 76 // passes is as an argument to the specified function. 77 // It does NOT change current network namespace. 78 func withTempNetNS(t *testing.T, toRun func(ns ns.NetNS)) { 79 ns, err := ns.NewNS() 80 if err != nil { 81 t.Errorf("Error creating network namespace: %v", err) 82 return 83 } 84 defer func() { 85 // We use log.Panicf() instead of t.Fatalf() in these tests 86 // because ns.Do() uses separate goroutine 87 if r := recover(); r != nil { 88 t.Fatal(r) 89 } 90 if err = ns.Close(); err != nil { 91 t.Fatalf("Error closing network namespace: %v", err) 92 } 93 }() 94 toRun(ns) 95 } 96 97 func inNS(netNS ns.NetNS, name string, toRun func()) { 98 var r interface{} 99 if err := netNS.Do(func(ns.NetNS) error { 100 defer func() { 101 //r = recover() 102 }() 103 toRun() 104 return nil 105 }); err != nil { 106 log.Fatalf("failed to enter %s: %v", name, err) 107 } 108 109 // re-panic in the original goroutine 110 if r != nil { 111 log.Panic(r) 112 } 113 } 114 115 // withHostAndContNS creates two namespaces, one serving as 'host' 116 // namespace and one serving as 'container' one, and calls 117 // the specified function in the 'host' namespace, passing both 118 // namespaces to it. 119 func withHostAndContNS(t *testing.T, toRun func(hostNS, contNS ns.NetNS)) { 120 withTempNetNS(t, func(hostNS ns.NetNS) { 121 withTempNetNS(t, func(contNS ns.NetNS) { 122 inNS(hostNS, "hostNS", func() { toRun(hostNS, contNS) }) 123 }) 124 }) 125 } 126 127 func verifyLinkUp(t *testing.T, name, title string) netlink.Link { 128 link, err := netlink.LinkByName(name) 129 if err != nil { 130 log.Panicf("cannot locate link: %s", title) 131 } 132 if link.Attrs().Flags&net.FlagUp == 0 { 133 t.Errorf("link should be up, but it's down: %s", title) 134 } 135 return link 136 } 137 138 func verifyNoLink(t *testing.T, name, title string) { 139 if _, err := netlink.LinkByName(name); err == nil { 140 t.Errorf("link should not be present: %s", title) 141 } 142 } 143 144 func verifyBridgeMember(t *testing.T, name, title string, bridge netlink.Link) netlink.Link { 145 if bridge.Type() != "bridge" { 146 log.Panicf("link %q is not a bridge", bridge.Attrs().Name) 147 } 148 link := verifyLinkUp(t, name, title) 149 if link.Attrs().MasterIndex != bridge.Attrs().Index { 150 t.Errorf("link %q doesn't belong to bridge %q", name, bridge.Attrs().Name) 151 } 152 return link 153 } 154 155 func TestEscapePair(t *testing.T) { 156 withHostAndContNS(t, func(hostNS, contNS ns.NetNS) { 157 hostVeth, contVeth, err := CreateEscapeVethPair(contNS, "esc0", 1500) 158 if err != nil { 159 log.Panicf("Error creating escape veth pair: %v", err) 160 } 161 // need to force hostNS here because of side effects of NetNS.Do() 162 // See https://github.com/vishvananda/netns/issues/17 163 inNS(hostNS, "hostNS", func() { 164 verifyLinkUp(t, hostVeth.Attrs().Name, "host veth") 165 verifyNoLink(t, contVeth.Attrs().Name, "container veth in host namespace") 166 }) 167 inNS(contNS, "contNS", func() { 168 verifyLinkUp(t, contVeth.Attrs().Name, "container veth") 169 verifyNoLink(t, hostVeth.Attrs().Name, "host veth in container namespace") 170 }) 171 }) 172 } 173 174 func makeTestVeth(t *testing.T, base string, index int) netlink.Link { 175 name := fmt.Sprintf("%s%d", base, index) 176 veth := &netlink.Veth{ 177 LinkAttrs: netlink.LinkAttrs{ 178 Name: name, 179 Flags: net.FlagUp, 180 MTU: 1500, 181 }, 182 PeerName: "p" + name, 183 } 184 if err := netlink.LinkAdd(veth); err != nil { 185 log.Panicf("failed to create veth: %v", err) 186 } 187 188 return veth 189 } 190 191 func makeTestBridge(t *testing.T, name string, links []netlink.Link) *netlink.Bridge { 192 br, err := SetupBridge(name, links) 193 if err != nil { 194 log.Panicf("failed to create first bridge: %v", err) 195 } 196 if br.Attrs().Name != name { 197 log.Panicf("bad bridge name: %q instead of %q", br.Attrs().Name, name) 198 } 199 verifyLinkUp(t, name, "bridge") 200 return br 201 } 202 203 func TestSetupBridge(t *testing.T) { 204 withTempNetNS(t, func(hostNS ns.NetNS) { 205 inNS(hostNS, "hostNS", func() { 206 var links []netlink.Link 207 for i := 0; i < 4; i++ { 208 links = append(links, makeTestVeth(t, "veth", i)) 209 } 210 211 brs := []*netlink.Bridge{ 212 makeTestBridge(t, "testbr0", links[0:2]), 213 makeTestBridge(t, "testbr1", links[2:4]), 214 } 215 if brs[0].Attrs().Name == brs[1].Attrs().Name { 216 t.Errorf("bridges have identical name %q", brs[0].Attrs().Name) 217 } 218 if brs[0].Attrs().Index == brs[1].Attrs().Index { 219 t.Errorf("bridges have the same index %d", brs[0].Attrs().Index) 220 } 221 222 for i := 0; i < 4; i++ { 223 name := links[i].Attrs().Name 224 verifyBridgeMember(t, name, name, brs[i/2]) 225 } 226 }) 227 }) 228 } 229 230 func parseAddr(addr string) *netlink.Addr { 231 r, err := netlink.ParseAddr(addr) 232 if err != nil { 233 log.Panicf("failed to parse addr: %v", err) 234 } 235 return r 236 } 237 238 func addTestRoute(t *testing.T, route *netlink.Route) { 239 if err := netlink.RouteAdd(route); err != nil { 240 log.Panicf("Failed to add route %#v: %v", route, err) 241 } 242 } 243 244 func setupLink(hwAddrAsText string, link netlink.Link) netlink.Link { 245 hwAddr, err := net.ParseMAC(hwAddrAsText) 246 if err != nil { 247 log.Panicf("Error parsing hwaddr %q: %v", hwAddrAsText, err) 248 } 249 if err := SetHardwareAddr(link, hwAddr); err != nil { 250 log.Panicf("Error setting hardware address: %v", err) 251 } 252 253 // re-query attrs (including new mac) 254 link, err = netlink.LinkByName(link.Attrs().Name) 255 if err != nil { 256 log.Panicf("cannot locate link: %s", link.Attrs().Name) 257 } 258 259 return link 260 } 261 262 func withFakeCNIVeth(t *testing.T, mtu int, toRun func(hostNS, contNS ns.NetNS, origHostVeth, origContVeth netlink.Link)) { 263 withHostAndContNS(t, func(hostNS, contNS ns.NetNS) { 264 origHostVeth, origContVeth, err := CreateEscapeVethPair(contNS, "eth0", mtu) 265 if err != nil { 266 log.Panicf("failed to create veth pair: %v", err) 267 } 268 // need to force hostNS here because of side effects of NetNS.Do() 269 // See https://github.com/vishvananda/netns/issues/17 270 inNS(hostNS, "hostNS", func() { 271 origHostVeth = setupLink(outerHwAddr, origHostVeth) 272 }) 273 inNS(contNS, "contNS", func() { 274 origContVeth = setupLink(innerHwAddr, origContVeth) 275 276 if err = netlink.AddrAdd(origContVeth, parseAddr("10.1.90.5/24")); err != nil { 277 log.Panicf("failed to add addr for origContVeth: %v", err) 278 } 279 280 toRun(hostNS, contNS, origHostVeth, origContVeth) 281 }) 282 }) 283 } 284 285 func withFakeCNIVethAndGateway(t *testing.T, mtu int, toRun func(hostNS, contNS ns.NetNS, origHostVeth, origContVeth netlink.Link)) { 286 withFakeCNIVeth(t, mtu, func(hostNS, contNS ns.NetNS, origHostVeth, origContVeth netlink.Link) { 287 addTestRoute(t, &netlink.Route{ 288 Gw: parseAddr("10.1.90.1/24").IPNet.IP, 289 Scope: SCOPE_UNIVERSE, 290 }) 291 292 toRun(hostNS, contNS, origHostVeth, origContVeth) 293 }) 294 } 295 296 func verifyNoAddressAndRoutes(t *testing.T, link netlink.Link) { 297 if routes, err := netlink.RouteList(link, FAMILY_V4); err != nil { 298 t.Errorf("failed to get route list: %v", err) 299 } else if len(routes) != 0 { 300 t.Errorf("unexpected routes remain on the interface: %s", spew.Sdump(routes)) 301 } 302 303 if addrs, err := netlink.AddrList(link, FAMILY_V4); err != nil { 304 t.Errorf("failed to get addresses for veth: %v", err) 305 } else if len(addrs) != 0 { 306 t.Errorf("unexpected addresses remain on the interface: %s", spew.Sdump(addrs)) 307 } 308 } 309 310 func TestFindVeth(t *testing.T) { 311 withFakeCNIVethAndGateway(t, defaultMTU, func(hostNS, contNS ns.NetNS, origHostVeth, origContVeth netlink.Link) { 312 allLinks, err := netlink.LinkList() 313 if err != nil { 314 log.Panicf("LinkList() failed: %v", err) 315 } 316 contVeth, err := FindVeth(allLinks) 317 if err != nil { 318 log.Panicf("FindVeth() failed: %v", err) 319 } 320 321 if contVeth.Attrs().Index != origContVeth.Attrs().Index { 322 t.Errorf("GrabInterfaceInfo() didn't return original cont veth. Interface returned: %q", origContVeth.Attrs().Name) 323 } 324 }) 325 } 326 327 func TestStripLink(t *testing.T) { 328 withFakeCNIVethAndGateway(t, defaultMTU, func(hostNS, contNS ns.NetNS, origHostVeth, origContVeth netlink.Link) { 329 if err := StripLink(origContVeth); err != nil { 330 log.Panicf("StripLink() failed: %v", err) 331 } 332 verifyNoAddressAndRoutes(t, origContVeth) 333 }) 334 } 335 336 func TestExtractLinkInfo(t *testing.T) { 337 withFakeCNIVethAndGateway(t, defaultMTU, func(hostNS, contNS ns.NetNS, origHostVeth, origContVeth netlink.Link) { 338 info, err := ExtractLinkInfo(origContVeth, contNS.Path()) 339 if err != nil { 340 log.Panicf("failed to grab interface info: %v", err) 341 } 342 expectedInfo := expectedExtractedLinkInfo(contNS.Path()) 343 if !reflect.DeepEqual(info, expectedInfo) { 344 t.Errorf("interface info mismatch. Expected:\n%s\nActual:\n%s", 345 spew.Sdump(expectedInfo), spew.Sdump(*info)) 346 } 347 }) 348 } 349 350 func verifyContainerSideNetwork(t *testing.T, origContVeth netlink.Link, contNsPath string, hostNS ns.NetNS, mtu int) { 351 allLinks, err := netlink.LinkList() 352 if err != nil { 353 log.Panicf("error listing links: %v", err) 354 } 355 356 origHwAddr := origContVeth.Attrs().HardwareAddr 357 expectedInfo := expectedExtractedLinkInfo(contNsPath) 358 csn, err := SetupContainerSideNetwork(expectedInfo, contNsPath, allLinks, false, hostNS) 359 if err != nil { 360 log.Panicf("failed to set up container side network: %v", err) 361 } 362 expectedInfo = expectedExtractedLinkInfo(contNsPath) 363 if !reflect.DeepEqual(csn.Result, expectedInfo) { 364 t.Errorf("interface info mismatch. Expected:\n%s\nActual:\n%s", 365 spew.Sdump(expectedInfo), spew.Sdump(*csn.Result)) 366 } 367 if !reflect.DeepEqual(origHwAddr, csn.Interfaces[0].HardwareAddr) { 368 t.Errorf("bad hwaddr returned from SetupContainerSideNetwork: %v instead of %v", csn.Interfaces[0].HardwareAddr, origHwAddr) 369 } 370 // re-query origContVeth attrs 371 origContVeth, err = netlink.LinkByName(origContVeth.Attrs().Name) 372 if err != nil { 373 log.Panicf("the original cni veth is gone") 374 } 375 if reflect.DeepEqual(origContVeth.Attrs().HardwareAddr, origHwAddr) { 376 t.Errorf("cni veth hardware address didn't change") 377 } 378 if origContVeth.Attrs().MTU != mtu { 379 t.Errorf("bad veth MTU: %d instead of %d", origContVeth.Attrs().MTU, mtu) 380 } 381 382 verifyNoAddressAndRoutes(t, origContVeth) 383 384 bridge := verifyLinkUp(t, "br0", "in-container bridge") 385 verifyBridgeMember(t, origContVeth.Attrs().Name, "origContVeth", bridge) 386 tap := verifyBridgeMember(t, "tap0", "tap0", bridge) 387 if tap.Type() != "tun" { 388 t.Errorf("tap0 interface must have type tun, but has %q instead", tap.Type()) 389 } 390 if tap.Attrs().MTU != mtu { 391 t.Errorf("bad tap MTU: %d instead of %d", tap.Attrs().MTU, mtu) 392 } 393 394 addrs, err := netlink.AddrList(bridge, FAMILY_V4) 395 if err != nil { 396 t.Errorf("failed to get addresses for dhcp-side veth: %v", err) 397 } 398 expectedAddr := "169.254.254.2/24 br0" 399 if len(addrs) != 1 { 400 t.Errorf("br0 should have exactly one address, but got this instead: %v", spew.Sdump(addrs)) 401 } else if addrs[0].String() != expectedAddr { 402 t.Errorf("bad br0 address %q (expected %q)", addrs[0].String(), expectedAddr) 403 } 404 405 if bridge.Attrs().MTU != mtu { 406 t.Errorf("bad bridge MTU: %d instead of %d", bridge.Attrs().MTU, mtu) 407 } 408 } 409 410 func TestSetUpContainerSideNetworkWithInfo(t *testing.T) { 411 withFakeCNIVethAndGateway(t, defaultMTU, func(hostNS, contNS ns.NetNS, origHostVeth, origContVeth netlink.Link) { 412 if err := StripLink(origContVeth); err != nil { 413 log.Panicf("StripLink() failed: %v", err) 414 } 415 verifyContainerSideNetwork(t, origContVeth, contNS.Path(), hostNS, defaultMTU) 416 }) 417 } 418 419 func TestSetUpContainerSideNetworkMTU(t *testing.T) { 420 withFakeCNIVethAndGateway(t, 9000, func(hostNS, contNS ns.NetNS, origHostVeth, origContVeth netlink.Link) { 421 if err := StripLink(origContVeth); err != nil { 422 log.Panicf("StripLink() failed: %v", err) 423 } 424 verifyContainerSideNetwork(t, origContVeth, contNS.Path(), hostNS, 9000) 425 }) 426 } 427 428 func TestLoopbackInterface(t *testing.T) { 429 withFakeCNIVethAndGateway(t, defaultMTU, func(hostNS, contNS ns.NetNS, origHostVeth, origContVeth netlink.Link) { 430 verifyContainerSideNetwork(t, origContVeth, contNS.Path(), hostNS, defaultMTU) 431 if out, err := exec.Command("ping", "-c", "1", "127.0.0.1").CombinedOutput(); err != nil { 432 log.Panicf("ping 127.0.0.1 failed:\n%s", out) 433 } 434 }) 435 } 436 437 func stringInList(expected string, list []string) bool { 438 for _, element := range list { 439 if element == expected { 440 return true 441 } 442 } 443 444 return false 445 } 446 447 func verifyNoLinks(t *testing.T, linkNames []string) { 448 links, err := netlink.LinkList() 449 if err != nil { 450 log.Panicf("netlink.LinkList failed: %v", err) 451 } 452 453 for _, link := range links { 454 linkName := link.Attrs().Name 455 if stringInList(linkName, linkNames) { 456 t.Errorf("there should not be interface called %s in container namespace", linkName) 457 } 458 } 459 } 460 461 func verifyVethHaveConfiguration(t *testing.T, info *cnicurrent.Result) { 462 allLinks, err := netlink.LinkList() 463 if err != nil { 464 log.Panicf("LinkList() failed: %v", err) 465 } 466 contVeth, err := FindVeth(allLinks) 467 if err != nil { 468 log.Panicf("FindVeth() failed: %v", err) 469 } 470 471 addrList, err := netlink.AddrList(contVeth, FAMILY_V4) 472 if err != nil { 473 log.Panicf("AddrList() failed: %v", err) 474 } 475 476 if len(addrList) != 1 { 477 t.Errorf("veth should have single address but have: %d", len(addrList)) 478 } 479 if !addrList[0].IP.Equal(info.IPs[0].Address.IP) { 480 t.Errorf("veth has ip %s wherever expected is %s", addrList[0].IP.String(), info.IPs[0].Address.IP.String()) 481 } 482 addrMaskSize := addrList[0].Mask.String() 483 desiredMaskSize := info.IPs[0].Address.Mask.String() 484 if addrMaskSize != desiredMaskSize { 485 t.Errorf("veth has ipmask %s wherever expected is %s", addrMaskSize, desiredMaskSize) 486 } 487 488 routeList, err := netlink.RouteList(contVeth, FAMILY_V4) 489 if err != nil { 490 log.Panicf("RouteList() failed: %v", err) 491 } 492 493 for _, route := range routeList { 494 if route.Gw != nil { 495 if route.Gw.String() == info.Routes[0].GW.String() { 496 return 497 } 498 } 499 } 500 501 t.Errorf("not found desired route to: %s", info.Routes[0].GW.String()) 502 } 503 504 func TestTeardownContainerSideNetwork(t *testing.T) { 505 withFakeCNIVethAndGateway(t, defaultMTU, func(hostNS, contNS ns.NetNS, origHostVeth, origContVeth netlink.Link) { 506 if err := StripLink(origContVeth); err != nil { 507 log.Panicf("StripLink() failed: %v", err) 508 } 509 allLinks, err := netlink.LinkList() 510 if err != nil { 511 log.Panicf("error listing links: %v", err) 512 } 513 514 csn, err := SetupContainerSideNetwork(expectedExtractedLinkInfo(contNS.Path()), contNS.Path(), allLinks, false, hostNS) 515 if err != nil { 516 log.Panicf("failed to set up container side network: %v", err) 517 } 518 519 if err := Teardown(csn); err != nil { 520 log.Panicf("failed to tear down container side network: %v", err) 521 } 522 523 verifyNoLinks(t, []string{"br0", "tap0"}) 524 verifyVethHaveConfiguration(t, expectedExtractedLinkInfo(contNS.Path())) 525 526 // re-quiry origContVeth attrs 527 origContVeth, err = netlink.LinkByName(origContVeth.Attrs().Name) 528 if err != nil { 529 log.Panicf("the original cni veth is gone") 530 } 531 if !reflect.DeepEqual(origContVeth.Attrs().HardwareAddr, csn.Interfaces[0].HardwareAddr) { 532 t.Errorf("cni veth hardware address wasn't restored") 533 } 534 }) 535 } 536 537 func TestFindingLinkByAddress(t *testing.T) { 538 withFakeCNIVeth(t, defaultMTU, func(hostNS, contNS ns.NetNS, origHostVeth, origContVeth netlink.Link) { 539 expectedInfo := expectedExtractedLinkInfo(contNS.Path()) 540 allLinks, err := netlink.LinkList() 541 if err != nil { 542 log.Panicf("LinkList() failed: %v", err) 543 } 544 545 link, err := findLinkByAddress(allLinks, expectedInfo.IPs[0].Address) 546 if err != nil { 547 t.Errorf("didn't found preconfigured link: %v", err) 548 } 549 if link == nil { 550 t.Errorf("<nil> where configured link was expected") 551 } 552 553 link, err = findLinkByAddress(allLinks, *parseAddr("1.2.3.4/8").IPNet) 554 if link != nil { 555 t.Errorf("found link with dummy address") 556 } 557 if err == nil { 558 t.Errorf("expected error but received <nil>") 559 } 560 }) 561 } 562 563 func withMultipleInterfacesConfigured(t *testing.T, toRun func(contNS ns.NetNS, innerLinks []netlink.Link)) { 564 withHostAndContNS(t, func(hostNS, contNS ns.NetNS) { 565 var origContVeths [2]netlink.Link 566 for n, vp := range []struct { 567 name string 568 outerHwAddr string 569 innerHwAddr string 570 ip string 571 }{ 572 {"eth0", outerHwAddr, innerHwAddr, "10.1.90.5/24"}, 573 {"eth1", secondOuterHwAddr, secondInnerHwAddr, "192.168.37.8/16"}, 574 } { 575 origHostVeth, origContVeth, err := CreateEscapeVethPair(contNS, vp.name, 1500) 576 if err != nil { 577 log.Panicf("failed to create veth pair %q: %v", vp.name, err) 578 } 579 inNS(hostNS, "hostNS", func() { setupLink(vp.outerHwAddr, origHostVeth) }) 580 inNS(contNS, "contNS", func() { 581 origContVeths[n] = setupLink(vp.innerHwAddr, origContVeth) 582 if err = netlink.AddrAdd(origContVeths[n], parseAddr(vp.ip)); err != nil { 583 log.Panicf("failed to add addr for %q: %v", vp.name, err) 584 } 585 }) 586 } 587 inNS(contNS, "contNS", func() { 588 gwAddr := parseAddr("10.1.90.1/24") 589 addTestRoute(t, &netlink.Route{ 590 Gw: gwAddr.IPNet.IP, 591 Scope: SCOPE_UNIVERSE, 592 }) 593 594 toRun(contNS, origContVeths[:]) 595 }) 596 }) 597 } 598 599 func expectedExtractedLinkInfoForMultipleInterfaces(contNsPath string) *cnicurrent.Result { 600 expectedInfo := expectedExtractedLinkInfo(contNsPath) 601 expectedInfo.IPs = append(expectedInfo.IPs, &cnicurrent.IPConfig{ 602 Version: "4", 603 Interface: 1, 604 Address: net.IPNet{ 605 IP: net.IP{192, 168, 37, 8}, 606 Mask: net.IPMask{255, 255, 0, 0}, 607 }, 608 }) 609 expectedInfo.Interfaces = append(expectedInfo.Interfaces, &cnicurrent.Interface{ 610 Name: "eth1", 611 Mac: secondInnerHwAddr, 612 Sandbox: contNsPath, 613 }) 614 return expectedInfo 615 } 616 617 func expectedExtractedLinkInfoWithMissingInterface(contNsPath string) *cnicurrent.Result { 618 expectedInfo := expectedExtractedLinkInfo(contNsPath) 619 expectedInfo.IPs = append(expectedInfo.IPs, &cnicurrent.IPConfig{ 620 Version: "4", 621 Interface: -1, 622 Address: net.IPNet{ 623 IP: net.IP{192, 168, 37, 8}, 624 Mask: net.IPMask{255, 255, 0, 0}, 625 }, 626 }) 627 return expectedInfo 628 } 629 630 func TestMultiInterfaces(t *testing.T) { 631 withMultipleInterfacesConfigured(t, func(contNS ns.NetNS, innerLinks []netlink.Link) { 632 expectedInfo := expectedExtractedLinkInfoForMultipleInterfaces(contNS.Path()) 633 result, err := ValidateAndFixCNIResult(expectedInfo, contNS.Path(), innerLinks) 634 if err != nil { 635 t.Errorf("error during validate/fix cni result: %v", err) 636 } 637 if !reflect.DeepEqual(result, expectedInfo) { 638 t.Errorf("result different than expected:\nActual:\n%s\nExpected:\n%s", 639 spew.Sdump(result), spew.Sdump(expectedInfo)) 640 } 641 }) 642 } 643 644 func TestMultiInterfacesWithMissingInterface(t *testing.T) { 645 withMultipleInterfacesConfigured(t, func(contNS ns.NetNS, innerLinks []netlink.Link) { 646 infoToFix := expectedExtractedLinkInfoForMultipleInterfaces(contNS.Path()) 647 expectedInfo := expectedExtractedLinkInfoForMultipleInterfaces(contNS.Path()) 648 result, err := ValidateAndFixCNIResult(infoToFix, contNS.Path(), innerLinks) 649 if err != nil { 650 t.Errorf("error during validate/fix cni result: %v", err) 651 } 652 if !reflect.DeepEqual(result, expectedInfo) { 653 t.Errorf("result different than expected:\nActual:\n%s\nExpected:\n%s", 654 spew.Sdump(result), spew.Sdump(expectedInfo)) 655 } 656 }) 657 }