From ac7cf825319070ee5f02521facf5dd42f25148dc Mon Sep 17 00:00:00 2001 From: mmirecki Date: Wed, 18 Jan 2023 14:48:47 +0100 Subject: [PATCH] Add support for in-container master for macvlans Signed-off-by: mmirecki --- plugins/main/macvlan/macvlan.go | 96 ++- plugins/main/macvlan/macvlan_test.go | 1041 +++++++++++++------------- 2 files changed, 621 insertions(+), 516 deletions(-) diff --git a/plugins/main/macvlan/macvlan.go b/plugins/main/macvlan/macvlan.go index 63e224ff..4fe11772 100644 --- a/plugins/main/macvlan/macvlan.go +++ b/plugins/main/macvlan/macvlan.go @@ -37,10 +37,11 @@ import ( type NetConf struct { types.NetConf - Master string `json:"master"` - Mode string `json:"mode"` - MTU int `json:"mtu"` - Mac string `json:"mac,omitempty"` + Master string `json:"master"` + Mode string `json:"mode"` + MTU int `json:"mtu"` + Mac string `json:"mac,omitempty"` + LinkContNs bool `json:"linkInContainer,omitempty"` RuntimeConfig struct { Mac string `json:"mac,omitempty"` @@ -79,13 +80,36 @@ func getDefaultRouteInterfaceName() (string, error) { return "", fmt.Errorf("no default route interface found") } -func loadConf(bytes []byte, envArgs string) (*NetConf, string, error) { +func getNamespacedDefaultRouteInterfaceName(namespace string, inContainer bool) (string, error) { + if !inContainer { + return getDefaultRouteInterfaceName() + } + netns, err := ns.GetNS(namespace) + if err != nil { + return "", fmt.Errorf("failed to open netns %q: %v", netns, err) + } + defer netns.Close() + var defaultRouteInterface string + err = netns.Do(func(_ ns.NetNS) error { + defaultRouteInterface, err = getDefaultRouteInterfaceName() + if err != nil { + return err + } + return nil + }) + if err != nil { + return "", err + } + return defaultRouteInterface, nil +} + +func loadConf(args *skel.CmdArgs, envArgs string) (*NetConf, string, error) { n := &NetConf{} - if err := json.Unmarshal(bytes, n); err != nil { + if err := json.Unmarshal(args.StdinData, n); err != nil { return nil, "", fmt.Errorf("failed to load netconf: %v", err) } if n.Master == "" { - defaultRouteInterface, err := getDefaultRouteInterfaceName() + defaultRouteInterface, err := getNamespacedDefaultRouteInterfaceName(args.Netns, n.LinkContNs) if err != nil { return nil, "", err } @@ -93,7 +117,7 @@ func loadConf(bytes []byte, envArgs string) (*NetConf, string, error) { } // check existing and MTU of master interface - masterMTU, err := getMTUByName(n.Master) + masterMTU, err := getMTUByName(n.Master, args.Netns, n.LinkContNs) if err != nil { return nil, "", err } @@ -120,8 +144,23 @@ func loadConf(bytes []byte, envArgs string) (*NetConf, string, error) { return n, n.CNIVersion, nil } -func getMTUByName(ifName string) (int, error) { - link, err := netlink.LinkByName(ifName) +func getMTUByName(ifName string, namespace string, inContainer bool) (int, error) { + var link netlink.Link + var err error + if inContainer { + netns, err := ns.GetNS(namespace) + if err != nil { + return 0, fmt.Errorf("failed to open netns %q: %v", netns, err) + } + defer netns.Close() + + err = netns.Do(func(_ ns.NetNS) error { + link, err = netlink.LinkByName(ifName) + return err + }) + } else { + link, err = netlink.LinkByName(ifName) + } if err != nil { return 0, err } @@ -166,7 +205,15 @@ func createMacvlan(conf *NetConf, ifName string, netns ns.NetNS) (*current.Inter return nil, err } - m, err := netlink.LinkByName(conf.Master) + var m netlink.Link + if conf.LinkContNs { + err = netns.Do(func(_ ns.NetNS) error { + m, err = netlink.LinkByName(conf.Master) + return err + }) + } else { + m, err = netlink.LinkByName(conf.Master) + } if err != nil { return nil, fmt.Errorf("failed to lookup master %q: %v", conf.Master, err) } @@ -198,7 +245,16 @@ func createMacvlan(conf *NetConf, ifName string, netns ns.NetNS) (*current.Inter Mode: mode, } - if err := netlink.LinkAdd(mv); err != nil { + if conf.LinkContNs { + err = netns.Do(func(_ ns.NetNS) error { + return netlink.LinkAdd(mv) + }) + } else { + if err = netlink.LinkAdd(mv); err != nil { + return nil, fmt.Errorf("failed to create macvlan: %v", err) + } + } + if err != nil { return nil, fmt.Errorf("failed to create macvlan: %v", err) } @@ -228,7 +284,7 @@ func createMacvlan(conf *NetConf, ifName string, netns ns.NetNS) (*current.Inter } func cmdAdd(args *skel.CmdArgs) error { - n, cniVersion, err := loadConf(args.StdinData, args.Args) + n, cniVersion, err := loadConf(args, args.Args) if err != nil { return err } @@ -329,7 +385,7 @@ func cmdAdd(args *skel.CmdArgs) error { } func cmdDel(args *skel.CmdArgs) error { - n, _, err := loadConf(args.StdinData, args.Args) + n, _, err := loadConf(args, args.Args) if err != nil { return err } @@ -378,7 +434,7 @@ func main() { func cmdCheck(args *skel.CmdArgs) error { - n, _, err := loadConf(args.StdinData, args.Args) + n, _, err := loadConf(args, args.Args) if err != nil { return err } @@ -429,7 +485,15 @@ func cmdCheck(args *skel.CmdArgs) error { contMap.Sandbox, args.Netns) } - m, err := netlink.LinkByName(n.Master) + var m netlink.Link + if n.LinkContNs { + err = netns.Do(func(_ ns.NetNS) error { + m, err = netlink.LinkByName(n.Master) + return err + }) + } else { + m, err = netlink.LinkByName(n.Master) + } if err != nil { return fmt.Errorf("failed to lookup master %q: %v", n.Master, err) } diff --git a/plugins/main/macvlan/macvlan_test.go b/plugins/main/macvlan/macvlan_test.go index eec9449c..d67be678 100644 --- a/plugins/main/macvlan/macvlan_test.go +++ b/plugins/main/macvlan/macvlan_test.go @@ -38,6 +38,7 @@ import ( ) const MASTER_NAME = "eth0" +const MASTER_NAME_INCONTAINER = "eth1" type Net struct { Name string `json:"name"` @@ -54,6 +55,7 @@ type Net struct { DNS types.DNS `json:"dns"` RawPrevResult map[string]interface{} `json:"prevResult,omitempty"` PrevResult types100.Result `json:"-"` + LinkContNs bool `json:"linkInContainer"` } func buildOneConfig(netName string, cniVersion string, orig *Net, prevResult types.Result) (*Net, error) { @@ -216,6 +218,22 @@ var _ = Describe("macvlan Operations", func() { return nil }) Expect(err).NotTo(HaveOccurred()) + + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + // Add master + err = netlink.LinkAdd(&netlink.Dummy{ + LinkAttrs: netlink.LinkAttrs{ + Name: MASTER_NAME_INCONTAINER, + }, + }) + Expect(err).NotTo(HaveOccurred()) + _, err = netlink.LinkByName(MASTER_NAME_INCONTAINER) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) }) AfterEach(func() { @@ -225,544 +243,565 @@ var _ = Describe("macvlan Operations", func() { Expect(targetNS.Close()).To(Succeed()) Expect(testutils.UnmountNS(targetNS)).To(Succeed()) }) + f, t := false, true + for _, inContainer := range []*bool{&f, &t, nil} { + isInContainer := inContainer + masterInterface := MASTER_NAME + if inContainer != nil && *inContainer { + masterInterface = MASTER_NAME_INCONTAINER + } + linkInContainer := "" + if isInContainer != nil { + linkInContainer = fmt.Sprintf("\"linkInContainer\": %t,", *isInContainer) + } + for _, ver := range testutils.AllSpecVersions { + // Redefine ver inside for scope so real value is picked up by each dynamically defined It() + // See Gingkgo's "Patterns for dynamically generating tests" documentation. + ver := ver - for _, ver := range testutils.AllSpecVersions { - // Redefine ver inside for scope so real value is picked up by each dynamically defined It() - // See Gingkgo's "Patterns for dynamically generating tests" documentation. - ver := ver - - It(fmt.Sprintf("[%s] creates an macvlan link in a non-default namespace", ver), func() { - conf := &NetConf{ - NetConf: types.NetConf{ - CNIVersion: ver, - Name: "testConfig", - Type: "macvlan", - }, - Master: MASTER_NAME, - Mode: "bridge", - MTU: 1500, - } - - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - _, err := createMacvlan(conf, "foobar0", targetNS) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - // Make sure macvlan link exists in the target namespace - err = targetNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName("foobar0") - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal("foobar0")) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It(fmt.Sprintf("[%s] configures and deconfigures a macvlan link with ADD/DEL", ver), func() { - const IFNAME = "macvl0" - - conf := fmt.Sprintf(`{ - "cniVersion": "%s", - "name": "mynet", - "type": "macvlan", - "master": "%s", - "ipam": { - "type": "host-local", - "subnet": "10.1.2.0/24", - "dataDir": "%s" - } - }`, ver, MASTER_NAME, dataDir) - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: IFNAME, - StdinData: []byte(conf), - } - - var macAddress string - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - result, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - - t := newTesterByVersion(ver) - macAddress = t.verifyResult(result, err, IFNAME, 1) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - // Make sure macvlan link exists in the target namespace - err = targetNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal(IFNAME)) - - if macAddress != "" { - hwaddr, err := net.ParseMAC(macAddress) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) + It(fmt.Sprintf("[%s] creates an macvlan link in a non-default namespace", ver), func() { + conf := &NetConf{ + NetConf: types.NetConf{ + CNIVersion: ver, + Name: "testConfig", + Type: "macvlan", + }, + Master: masterInterface, + Mode: "bridge", + MTU: 1500, + LinkContNs: isInContainer != nil && *isInContainer, } - addrs, err := netlink.AddrList(link, syscall.AF_INET) - Expect(err).NotTo(HaveOccurred()) - Expect(len(addrs)).To(Equal(1)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - err := testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) + _, err := createMacvlan(conf, "foobar0", targetNS) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure macvlan link exists in the target namespace + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName("foobar0") + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal("foobar0")) + return nil }) Expect(err).NotTo(HaveOccurred()) - return nil }) - Expect(err).NotTo(HaveOccurred()) - // Make sure macvlan link has been deleted - err = targetNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + It(fmt.Sprintf("[%s] configures and deconfigures a macvlan link with ADD/DEL", ver), func() { + const IFNAME = "macvl0" - link, err := netlink.LinkByName(IFNAME) - Expect(err).To(HaveOccurred()) - Expect(link).To(BeNil()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It(fmt.Sprintf("[%s] deconfigures an unconfigured macvlan link with DEL", ver), func() { - const IFNAME = "macvl0" - - conf := fmt.Sprintf(`{ + conf := fmt.Sprintf(`{ "cniVersion": "%s", "name": "mynet", "type": "macvlan", "master": "%s", + %s "ipam": { "type": "host-local", "subnet": "10.1.2.0/24", "dataDir": "%s" } - }`, ver, MASTER_NAME, dataDir) + }`, ver, masterInterface, linkInContainer, dataDir) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: IFNAME, - StdinData: []byte(conf), - } + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: []byte(conf), + } - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + var macAddress string + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - err := testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) + result, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + + t := newTesterByVersion(ver) + macAddress = t.verifyResult(result, err, IFNAME, 1) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure macvlan link exists in the target namespace + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal(IFNAME)) + + if macAddress != "" { + hwaddr, err := net.ParseMAC(macAddress) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) + } + + addrs, err := netlink.AddrList(link, syscall.AF_INET) + Expect(err).NotTo(HaveOccurred()) + Expect(len(addrs)).To(Equal(1)) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + err := testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure macvlan link has been deleted + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).To(HaveOccurred()) + Expect(link).To(BeNil()) + return nil }) Expect(err).NotTo(HaveOccurred()) - return nil }) - Expect(err).NotTo(HaveOccurred()) - }) + It(fmt.Sprintf("[%s] deconfigures an unconfigured macvlan link with DEL", ver), func() { + const IFNAME = "macvl0" - It(fmt.Sprintf("[%s] configures and deconfigures a l2 macvlan link with ADD/DEL", ver), func() { - const IFNAME = "macvl0" - - conf := fmt.Sprintf(`{ + conf := fmt.Sprintf(`{ "cniVersion": "%s", "name": "mynet", "type": "macvlan", "master": "%s", + %s + "ipam": { + "type": "host-local", + "subnet": "10.1.2.0/24", + "dataDir": "%s" + } + }`, ver, masterInterface, linkInContainer, dataDir) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: []byte(conf), + } + + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + err := testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + }) + + It(fmt.Sprintf("[%s] configures and deconfigures a l2 macvlan link with ADD/DEL", ver), func() { + const IFNAME = "macvl0" + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "macvlan", + "master": "%s", + %s "ipam": {} - }`, ver, MASTER_NAME) + }`, ver, masterInterface, linkInContainer) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: IFNAME, - StdinData: []byte(conf), - } - - var macAddress string - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - result, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - - t := newTesterByVersion(ver) - macAddress = t.verifyResult(result, err, IFNAME, 0) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - // Make sure macvlan link exists in the target namespace - err = targetNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal(IFNAME)) - - if macAddress != "" { - hwaddr, err := net.ParseMAC(macAddress) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: []byte(conf), } - addrs, err := netlink.AddrList(link, syscall.AF_INET) - Expect(err).NotTo(HaveOccurred()) - Expect(len(addrs)).To(Equal(0)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) + var macAddress string + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + result, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) - err := testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) + t := newTesterByVersion(ver) + macAddress = t.verifyResult(result, err, IFNAME, 0) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure macvlan link exists in the target namespace + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal(IFNAME)) + + if macAddress != "" { + hwaddr, err := net.ParseMAC(macAddress) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) + } + + addrs, err := netlink.AddrList(link, syscall.AF_INET) + Expect(err).NotTo(HaveOccurred()) + Expect(len(addrs)).To(Equal(0)) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + err := testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure macvlan link has been deleted + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).To(HaveOccurred()) + Expect(link).To(BeNil()) + return nil }) Expect(err).NotTo(HaveOccurred()) - return nil }) - Expect(err).NotTo(HaveOccurred()) - // Make sure macvlan link has been deleted - err = targetNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + It(fmt.Sprintf("[%s] configures and deconfigures a macvlan link with ADD/CHECK/DEL", ver), func() { + const IFNAME = "macvl0" - link, err := netlink.LinkByName(IFNAME) - Expect(err).To(HaveOccurred()) - Expect(link).To(BeNil()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It(fmt.Sprintf("[%s] configures and deconfigures a macvlan link with ADD/CHECK/DEL", ver), func() { - const IFNAME = "macvl0" - - conf := fmt.Sprintf(`{ + conf := fmt.Sprintf(`{ "cniVersion": "%s", "name": "macvlanTestv4", "type": "macvlan", "master": "%s", + %s "ipam": { "type": "host-local", "ranges": [[ {"subnet": "10.1.2.0/24", "gateway": "10.1.2.1"} ]], "dataDir": "%s" } - }`, ver, MASTER_NAME, dataDir) + }`, ver, masterInterface, linkInContainer, dataDir) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: IFNAME, - StdinData: []byte(conf), - } - - var ( - macAddress string - t tester - result types.Result - ) - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - var err error - result, _, err = testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - - t = newTesterByVersion(ver) - macAddress = t.verifyResult(result, err, IFNAME, 1) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - // Make sure macvlan link exists in the target namespace - err = targetNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal(IFNAME)) - - if macAddress != "" { - hwaddr, err := net.ParseMAC(macAddress) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: []byte(conf), } - addrs, err := netlink.AddrList(link, syscall.AF_INET) - Expect(err).NotTo(HaveOccurred()) - Expect(len(addrs)).To(Equal(1)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) + var ( + macAddress string + t tester + result types.Result + ) + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - n := &Net{} - err = json.Unmarshal([]byte(conf), &n) - Expect(err).NotTo(HaveOccurred()) + var err error + result, _, err = testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) - n.IPAM, _, err = allocator.LoadIPAMConfig([]byte(conf), "") - Expect(err).NotTo(HaveOccurred()) - - newConf, err := buildOneConfig("macvlanTestv4", ver, n, result) - Expect(err).NotTo(HaveOccurred()) - - confString, err := json.Marshal(newConf) - Expect(err).NotTo(HaveOccurred()) - - args.StdinData = confString - - // CNI Check on macvlan in the target namespace - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - return testutils.CmdCheckWithArgs(args, func() error { - return cmdCheck(args) - }) - }) - if testutils.SpecVersionHasCHECK(ver) { - Expect(err).NotTo(HaveOccurred()) - } else { - Expect(err).To(MatchError("config version does not allow CHECK")) - } - - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - err := testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) + t = newTesterByVersion(ver) + macAddress = t.verifyResult(result, err, IFNAME, 1) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure macvlan link exists in the target namespace + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal(IFNAME)) + + if macAddress != "" { + hwaddr, err := net.ParseMAC(macAddress) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) + } + + addrs, err := netlink.AddrList(link, syscall.AF_INET) + Expect(err).NotTo(HaveOccurred()) + Expect(len(addrs)).To(Equal(1)) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + n := &Net{} + err = json.Unmarshal([]byte(conf), &n) + Expect(err).NotTo(HaveOccurred()) + + n.IPAM, _, err = allocator.LoadIPAMConfig([]byte(conf), "") + Expect(err).NotTo(HaveOccurred()) + + newConf, err := buildOneConfig("macvlanTestv4", ver, n, result) + Expect(err).NotTo(HaveOccurred()) + + confString, err := json.Marshal(newConf) + Expect(err).NotTo(HaveOccurred()) + + args.StdinData = confString + + // CNI Check on macvlan in the target namespace + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + return testutils.CmdCheckWithArgs(args, func() error { + return cmdCheck(args) + }) + }) + if testutils.SpecVersionHasCHECK(ver) { + Expect(err).NotTo(HaveOccurred()) + } else { + Expect(err).To(MatchError("config version does not allow CHECK")) + } + + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + err := testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure macvlan link has been deleted + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).To(HaveOccurred()) + Expect(link).To(BeNil()) + return nil }) Expect(err).NotTo(HaveOccurred()) - return nil }) - Expect(err).NotTo(HaveOccurred()) - // Make sure macvlan link has been deleted - err = targetNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + It(fmt.Sprintf("[%s] configures and deconfigures a macvlan link with ADD/DEL, without master config", ver), func() { + const IFNAME = "macvl0" - link, err := netlink.LinkByName(IFNAME) - Expect(err).To(HaveOccurred()) - Expect(link).To(BeNil()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It(fmt.Sprintf("[%s] configures and deconfigures a macvlan link with ADD/DEL, without master config", ver), func() { - const IFNAME = "macvl0" - - conf := fmt.Sprintf(`{ + conf := fmt.Sprintf(`{ "cniVersion": "%s", "name": "mynet", "type": "macvlan", + %s "ipam": { "type": "host-local", "subnet": "10.1.2.0/24", "dataDir": "%s" } - }`, ver, dataDir) + }`, ver, linkInContainer, dataDir) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: IFNAME, - StdinData: []byte(conf), - } - - // Make MASTER_NAME as default route interface - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName(MASTER_NAME) - Expect(err).NotTo(HaveOccurred()) - err = netlink.LinkSetUp(link) - Expect(err).NotTo(HaveOccurred()) - - var address = &net.IPNet{IP: net.IPv4(192, 0, 0, 1), Mask: net.CIDRMask(24, 32)} - var addr = &netlink.Addr{IPNet: address} - err = netlink.AddrAdd(link, addr) - Expect(err).NotTo(HaveOccurred()) - - // add default gateway into MASTER - dst := &net.IPNet{ - IP: net.IPv4(0, 0, 0, 0), - Mask: net.CIDRMask(0, 0), + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: []byte(conf), + } + currentNs := originalNS + if isInContainer != nil && *isInContainer { + currentNs = targetNS } - ip := net.IPv4(192, 0, 0, 254) - route := netlink.Route{LinkIndex: link.Attrs().Index, Dst: dst, Gw: ip} - err = netlink.RouteAdd(&route) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - var macAddress string - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + // Make master as default route interface + err := currentNs.Do(func(ns.NetNS) error { + defer GinkgoRecover() - result, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - - t := newTesterByVersion(ver) - macAddress = t.verifyResult(result, err, IFNAME, 1) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - // Make sure macvlan link exists in the target namespace - err = targetNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal(IFNAME)) - - if macAddress != "" { - hwaddr, err := net.ParseMAC(macAddress) + link, err := netlink.LinkByName(masterInterface) + Expect(err).NotTo(HaveOccurred()) + err = netlink.LinkSetUp(link) Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) - } - addrs, err := netlink.AddrList(link, syscall.AF_INET) - Expect(err).NotTo(HaveOccurred()) - Expect(len(addrs)).To(Equal(1)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) + var address = &net.IPNet{IP: net.IPv4(192, 0, 0, 1), Mask: net.CIDRMask(24, 32)} + var addr = &netlink.Addr{IPNet: address} + err = netlink.AddrAdd(link, addr) + Expect(err).NotTo(HaveOccurred()) - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - err := testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) + // add default gateway into MASTER + dst := &net.IPNet{ + IP: net.IPv4(0, 0, 0, 0), + Mask: net.CIDRMask(0, 0), + } + ip := net.IPv4(192, 0, 0, 254) + route := netlink.Route{LinkIndex: link.Attrs().Index, Dst: dst, Gw: ip} + err = netlink.RouteAdd(&route) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + var macAddress string + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + result, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + + t := newTesterByVersion(ver) + macAddress = t.verifyResult(result, err, IFNAME, 1) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure macvlan link exists in the target namespace + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal(IFNAME)) + + if macAddress != "" { + hwaddr, err := net.ParseMAC(macAddress) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) + } + + addrs, err := netlink.AddrList(link, syscall.AF_INET) + Expect(err).NotTo(HaveOccurred()) + Expect(len(addrs)).To(Equal(1)) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + err := testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure macvlan link has been deleted + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).To(HaveOccurred()) + Expect(link).To(BeNil()) + return nil }) Expect(err).NotTo(HaveOccurred()) - return nil }) - Expect(err).NotTo(HaveOccurred()) - // Make sure macvlan link has been deleted - err = targetNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + It(fmt.Sprintf("[%s] configures and deconfigures l2 macvlan link with mac address (from CNI_ARGS) with ADD/DEL", ver), func() { + const ( + IFNAME = "macvl0" + EXPECTED_MAC = "c2:11:22:33:44:55" + ) - link, err := netlink.LinkByName(IFNAME) - Expect(err).To(HaveOccurred()) - Expect(link).To(BeNil()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It(fmt.Sprintf("[%s] configures and deconfigures l2 macvlan link with mac address (from CNI_ARGS) with ADD/DEL", ver), func() { - const ( - IFNAME = "macvl0" - EXPECTED_MAC = "c2:11:22:33:44:55" - ) - - conf := fmt.Sprintf(`{ + conf := fmt.Sprintf(`{ "cniVersion": "%s", "name": "mynet", "type": "macvlan", "master": "%s", + %s "ipam": {} - }`, ver, MASTER_NAME) + }`, ver, masterInterface, linkInContainer) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: IFNAME, - StdinData: []byte(conf), - Args: fmt.Sprintf("IgnoreUnknown=true;MAC=%s", EXPECTED_MAC), - } - - var macAddress string - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - result, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - - t := newTesterByVersion(ver) - macAddress = t.verifyResult(result, err, IFNAME, 0) - if macAddress != "" { - Expect(macAddress).To(Equal(EXPECTED_MAC)) + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: []byte(conf), + Args: fmt.Sprintf("IgnoreUnknown=true;MAC=%s", EXPECTED_MAC), } - return nil - }) - Expect(err).NotTo(HaveOccurred()) - // Make sure macvlan link exists in the target namespace - err = targetNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + var macAddress string + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal(IFNAME)) + result, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) - hwaddr, err := net.ParseMAC(EXPECTED_MAC) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) - - addrs, err := netlink.AddrList(link, syscall.AF_INET) - Expect(err).NotTo(HaveOccurred()) - Expect(len(addrs)).To(Equal(0)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - err := testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) + t := newTesterByVersion(ver) + macAddress = t.verifyResult(result, err, IFNAME, 0) + if macAddress != "" { + Expect(macAddress).To(Equal(EXPECTED_MAC)) + } + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure macvlan link exists in the target namespace + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal(IFNAME)) + + hwaddr, err := net.ParseMAC(EXPECTED_MAC) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) + + addrs, err := netlink.AddrList(link, syscall.AF_INET) + Expect(err).NotTo(HaveOccurred()) + Expect(len(addrs)).To(Equal(0)) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + err := testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure macvlan link has been deleted + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).To(HaveOccurred()) + Expect(link).To(BeNil()) + return nil }) Expect(err).NotTo(HaveOccurred()) - return nil }) - Expect(err).NotTo(HaveOccurred()) - // Make sure macvlan link has been deleted - err = targetNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + It(fmt.Sprintf("[%s] configures and deconfigures l2 macvlan link with mac address (from RuntimeConfig) with ADD/DEL", ver), func() { + const ( + IFNAME = "macvl0" + EXPECTED_MAC = "c2:11:22:33:44:55" + ) - link, err := netlink.LinkByName(IFNAME) - Expect(err).To(HaveOccurred()) - Expect(link).To(BeNil()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It(fmt.Sprintf("[%s] configures and deconfigures l2 macvlan link with mac address (from RuntimeConfig) with ADD/DEL", ver), func() { - const ( - IFNAME = "macvl0" - EXPECTED_MAC = "c2:11:22:33:44:55" - ) - - conf := fmt.Sprintf(`{ + conf := fmt.Sprintf(`{ "cniVersion": "%s", "capabilities": {"mac": true}, "RuntimeConfig": { @@ -771,73 +810,75 @@ var _ = Describe("macvlan Operations", func() { "name": "mynet", "type": "macvlan", "master": "%s", + %s "ipam": {} - }`, ver, MASTER_NAME) + }`, ver, masterInterface, linkInContainer) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: IFNAME, - StdinData: []byte(conf), - } - - var macAddress string - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - result, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - - t := newTesterByVersion(ver) - macAddress = t.verifyResult(result, err, IFNAME, 0) - if macAddress != "" { - Expect(macAddress).To(Equal(EXPECTED_MAC)) + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: []byte(conf), } - return nil - }) - Expect(err).NotTo(HaveOccurred()) - // Make sure macvlan link exists in the target namespace - err = targetNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + var macAddress string + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal(IFNAME)) + result, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) - hwaddr, err := net.ParseMAC(EXPECTED_MAC) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) - - addrs, err := netlink.AddrList(link, syscall.AF_INET) - Expect(err).NotTo(HaveOccurred()) - Expect(len(addrs)).To(Equal(0)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - err := testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) + t := newTesterByVersion(ver) + macAddress = t.verifyResult(result, err, IFNAME, 0) + if macAddress != "" { + Expect(macAddress).To(Equal(EXPECTED_MAC)) + } + return nil }) Expect(err).NotTo(HaveOccurred()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - // Make sure macvlan link has been deleted - err = targetNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + // Make sure macvlan link exists in the target namespace + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - link, err := netlink.LinkByName(IFNAME) - Expect(err).To(HaveOccurred()) - Expect(link).To(BeNil()) - return nil + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal(IFNAME)) + + hwaddr, err := net.ParseMAC(EXPECTED_MAC) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) + + addrs, err := netlink.AddrList(link, syscall.AF_INET) + Expect(err).NotTo(HaveOccurred()) + Expect(len(addrs)).To(Equal(0)) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + err := testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure macvlan link has been deleted + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).To(HaveOccurred()) + Expect(link).To(BeNil()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) }) - Expect(err).NotTo(HaveOccurred()) - }) + } } })