Merge pull request #307 from SchSeba/l2-macvlan

Allow to configure empty ipam for macvlan
This commit is contained in:
Casey Callendrello
2019-05-08 17:27:52 +02:00
committed by GitHub
3 changed files with 166 additions and 49 deletions

View File

@ -26,7 +26,7 @@ Since each macvlan interface has its own MAC address, it makes it easy to use wi
* `master` (string, optional): name of the host interface to enslave. Defaults to default route interace. * `master` (string, optional): name of the host interface to enslave. Defaults to default route interace.
* `mode` (string, optional): one of "bridge", "private", "vepa", "passthru". Defaults to "bridge". * `mode` (string, optional): one of "bridge", "private", "vepa", "passthru". Defaults to "bridge".
* `mtu` (integer, optional): explicitly set MTU to the specified value. Defaults to the value chosen by the kernel. * `mtu` (integer, optional): explicitly set MTU to the specified value. Defaults to the value chosen by the kernel.
* `ipam` (dictionary, required): IPAM configuration to be used for this network. * `ipam` (dictionary, required): IPAM configuration to be used for this network. For interface only without ip address, create empty dictionary.
## Notes ## Notes

View File

@ -192,6 +192,8 @@ func cmdAdd(args *skel.CmdArgs) error {
return err return err
} }
isLayer3 := n.IPAM.Type != ""
netns, err := ns.GetNS(args.Netns) netns, err := ns.GetNS(args.Netns)
if err != nil { if err != nil {
return fmt.Errorf("failed to open netns %q: %v", netns, err) return fmt.Errorf("failed to open netns %q: %v", netns, err)
@ -212,56 +214,80 @@ func cmdAdd(args *skel.CmdArgs) error {
} }
}() }()
// run the IPAM plugin and get back the config to apply // Assume L2 interface only
r, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData) result := &current.Result{CNIVersion: cniVersion, Interfaces: []*current.Interface{macvlanInterface}}
if err != nil {
return err
}
// Invoke ipam del if err to avoid ip leak if isLayer3 {
defer func() { // run the IPAM plugin and get back the config to apply
r, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData)
if err != nil { if err != nil {
os.Setenv("CNI_COMMAND", "DEL")
ipam.ExecDel(n.IPAM.Type, args.StdinData)
os.Setenv("CNI_COMMAND", "ADD")
}
}()
// Convert whatever the IPAM result was into the current Result type
result, err := current.NewResultFromResult(r)
if err != nil {
return err
}
if len(result.IPs) == 0 {
return errors.New("IPAM plugin returned missing IP config")
}
result.Interfaces = []*current.Interface{macvlanInterface}
for _, ipc := range result.IPs {
// All addresses apply to the container macvlan interface
ipc.Interface = current.Int(0)
}
err = netns.Do(func(_ ns.NetNS) error {
if err := ipam.ConfigureIface(args.IfName, result); err != nil {
return err return err
} }
contVeth, err := net.InterfaceByName(args.IfName) // Invoke ipam del if err to avoid ip leak
defer func() {
if err != nil {
os.Setenv("CNI_COMMAND", "DEL")
ipam.ExecDel(n.IPAM.Type, args.StdinData)
os.Setenv("CNI_COMMAND", "ADD")
}
}()
// Convert whatever the IPAM result was into the current Result type
ipamResult, err := current.NewResultFromResult(r)
if err != nil { if err != nil {
return fmt.Errorf("failed to look up %q: %v", args.IfName, err) return err
} }
for _, ipc := range result.IPs { if len(ipamResult.IPs) == 0 {
if ipc.Version == "4" { return errors.New("IPAM plugin returned missing IP config")
_ = arping.GratuitousArpOverIface(ipc.Address.IP, *contVeth) }
}
result.IPs = ipamResult.IPs
result.Routes = ipamResult.Routes
for _, ipc := range result.IPs {
// All addresses apply to the container macvlan interface
ipc.Interface = current.Int(0)
}
err = netns.Do(func(_ ns.NetNS) error {
if err := ipam.ConfigureIface(args.IfName, result); err != nil {
return err
}
contVeth, err := net.InterfaceByName(args.IfName)
if err != nil {
return fmt.Errorf("failed to look up %q: %v", args.IfName, err)
}
for _, ipc := range result.IPs {
if ipc.Version == "4" {
_ = arping.GratuitousArpOverIface(ipc.Address.IP, *contVeth)
}
}
return nil
})
if err != nil {
return err
}
} else {
// For L2 just change interface status to up
err = netns.Do(func(_ ns.NetNS) error {
macvlanInterfaceLink, err := netlink.LinkByName(args.IfName)
if err != nil {
return fmt.Errorf("failed to find interface name %q: %v", macvlanInterface.Name, err)
}
if err := netlink.LinkSetUp(macvlanInterfaceLink); err != nil {
return fmt.Errorf("failed to set %q UP: %v", args.IfName, err)
}
return nil
})
if err != nil {
return err
} }
return nil
})
if err != nil {
return err
} }
result.DNS = n.DNS result.DNS = n.DNS
@ -275,9 +301,13 @@ func cmdDel(args *skel.CmdArgs) error {
return err return err
} }
err = ipam.ExecDel(n.IPAM.Type, args.StdinData) isLayer3 := n.IPAM.Type != ""
if err != nil {
return err if isLayer3 {
err = ipam.ExecDel(n.IPAM.Type, args.StdinData)
if err != nil {
return err
}
} }
if args.Netns == "" { if args.Netns == "" {
@ -308,16 +338,20 @@ func cmdCheck(args *skel.CmdArgs) error {
if err != nil { if err != nil {
return err return err
} }
isLayer3 := n.IPAM.Type != ""
netns, err := ns.GetNS(args.Netns) netns, err := ns.GetNS(args.Netns)
if err != nil { if err != nil {
return fmt.Errorf("failed to open netns %q: %v", args.Netns, err) return fmt.Errorf("failed to open netns %q: %v", args.Netns, err)
} }
defer netns.Close() defer netns.Close()
// run the IPAM plugin and get back the config to apply if isLayer3 {
err = ipam.ExecCheck(n.IPAM.Type, args.StdinData) // run the IPAM plugin and get back the config to apply
if err != nil { err = ipam.ExecCheck(n.IPAM.Type, args.StdinData)
return err if err != nil {
return err
}
} }
// Parse previous result. // Parse previous result.

View File

@ -286,6 +286,89 @@ var _ = Describe("macvlan Operations", func() {
}) })
It("configures and deconfigures a l2 macvlan link with ADD/DEL", func() {
const IFNAME = "macvl0"
conf := fmt.Sprintf(`{
"cniVersion": "0.3.1",
"name": "mynet",
"type": "macvlan",
"master": "%s",
"ipam": {}
}`, MASTER_NAME)
targetNs, err := testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
defer targetNs.Close()
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNs.Path(),
IfName: IFNAME,
StdinData: []byte(conf),
}
var result *current.Result
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
r, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
result, err = current.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(len(result.Interfaces)).To(Equal(1))
Expect(result.Interfaces[0].Name).To(Equal(IFNAME))
Expect(len(result.IPs)).To(Equal(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))
hwaddr, err := net.ParseMAC(result.Interfaces[0].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())
})
It("configures and deconfigures a cniVersion 0.4.0 macvlan link with ADD/DEL", func() { It("configures and deconfigures a cniVersion 0.4.0 macvlan link with ADD/DEL", func() {
const IFNAME = "macvl0" const IFNAME = "macvl0"