diff --git a/plugins/main/macvlan/macvlan.go b/plugins/main/macvlan/macvlan.go index 435024c49..bb4afb857 100644 --- a/plugins/main/macvlan/macvlan.go +++ b/plugins/main/macvlan/macvlan.go @@ -107,12 +107,35 @@ func loadConf(args *skel.CmdArgs, envArgs string) (*NetConf, string, error) { if err := json.Unmarshal(args.StdinData, n); err != nil { return nil, "", fmt.Errorf("failed to load netconf: %v", err) } - if n.Master == "" { - defaultRouteInterface, err := getNamespacedDefaultRouteInterfaceName(args.Netns, n.LinkContNs) + + var result *current.Result + var err error + // Parse previous result + if n.NetConf.RawPrevResult != nil { + if err = version.ParsePrevResult(&n.NetConf); err != nil { + return nil, "", fmt.Errorf("could not parse prevResult: %v", err) + } + + result, err = current.NewResultFromResult(n.PrevResult) if err != nil { - return nil, "", err + return nil, "", fmt.Errorf("could not convert result to current version: %v", err) + } + } + if n.Master == "" { + if result == nil { + var defaultRouteInterface string + defaultRouteInterface, err = getNamespacedDefaultRouteInterfaceName(args.Netns, n.LinkContNs) + if err != nil { + return nil, "", err + } + n.Master = defaultRouteInterface + } else { + if len(result.Interfaces) == 1 && result.Interfaces[0].Name != "" { + n.Master = result.Interfaces[0].Name + } else { + return nil, "", fmt.Errorf("chained master failure. PrevResult lacks a single named interface") + } } - n.Master = defaultRouteInterface } // check existing and MTU of master interface diff --git a/plugins/main/macvlan/macvlan_test.go b/plugins/main/macvlan/macvlan_test.go index ae73f99f2..9a5d2c4da 100644 --- a/plugins/main/macvlan/macvlan_test.go +++ b/plugins/main/macvlan/macvlan_test.go @@ -880,6 +880,98 @@ var _ = Describe("macvlan Operations", func() { }) Expect(err).NotTo(HaveOccurred()) }) + if testutils.SpecVersionHasChaining(ver) { + It(fmt.Sprintf("[%s] configures and deconfigures an l2 macvlan link with ADD/DEL when chained", ver), func() { + const IFNAME = "macvl0" + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "macvlan", + %s + "prevResult": { + "interfaces": [ + { + "name": "%s" + } + ], + "ips": [ + { + "version": "4", + "address": "10.1.2.2/24", + "gateway": "10.1.2.1", + "interface": 0 + } + ], + "routes": [] + } + }`, ver, linkInContainer, masterInterface) + 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)) + } + + addrs, err := netlink.AddrList(link, syscall.AF_INET) + Expect(err).NotTo(HaveOccurred()) + Expect(addrs).To(BeEmpty()) + 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()) + + }) + } + } } })