Merge pull request #307 from SchSeba/l2-macvlan
Allow to configure empty ipam for macvlan
This commit is contained in:
@ -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
|
||||||
|
|
||||||
|
@ -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 := ¤t.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.
|
||||||
|
@ -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"
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user