Allow to configure empty ipam for macvlan

This PR add the option to configure an empty ipam for the macvlan cni plugin.

When using the macvlan cni plugin with an empty ipam the requeted pod will get the macvlan interface but without any ip address.

One of the use cases for this feature is for projects that runs a dhcp server inside the pod like KubeVirt.
In KubeVirt we need to let the vm running inside the pod to make the dhcp request so it will be able to make a release an renew request when needed.
This commit is contained in:
Sebastian Sch 2019-04-29 00:26:47 +03:00
parent 44297f6ba3
commit af692de1b8
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, required): name of the host interface to enslave * `master` (string, required): name of the host interface to enslave
* `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

@ -169,6 +169,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)
@ -189,6 +191,10 @@ func cmdAdd(args *skel.CmdArgs) error {
} }
}() }()
// Assume L2 interface only
result := &current.Result{CNIVersion: cniVersion, Interfaces: []*current.Interface{macvlanInterface}}
if isLayer3 {
// run the IPAM plugin and get back the config to apply // run the IPAM plugin and get back the config to apply
r, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData) r, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData)
if err != nil { if err != nil {
@ -205,15 +211,17 @@ func cmdAdd(args *skel.CmdArgs) error {
}() }()
// Convert whatever the IPAM result was into the current Result type // Convert whatever the IPAM result was into the current Result type
result, err := current.NewResultFromResult(r) ipamResult, err := current.NewResultFromResult(r)
if err != nil { if err != nil {
return err return err
} }
if len(result.IPs) == 0 { if len(ipamResult.IPs) == 0 {
return errors.New("IPAM plugin returned missing IP config") return errors.New("IPAM plugin returned missing IP config")
} }
result.Interfaces = []*current.Interface{macvlanInterface}
result.IPs = ipamResult.IPs
result.Routes = ipamResult.Routes
for _, ipc := range result.IPs { for _, ipc := range result.IPs {
// All addresses apply to the container macvlan interface // All addresses apply to the container macvlan interface
@ -240,6 +248,24 @@ func cmdAdd(args *skel.CmdArgs) error {
if err != nil { if err != nil {
return err 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
}
}
result.DNS = n.DNS result.DNS = n.DNS
@ -252,10 +278,14 @@ func cmdDel(args *skel.CmdArgs) error {
return err return err
} }
isLayer3 := n.IPAM.Type != ""
if isLayer3 {
err = ipam.ExecDel(n.IPAM.Type, args.StdinData) err = ipam.ExecDel(n.IPAM.Type, args.StdinData)
if err != nil { if err != nil {
return err return err
} }
}
if args.Netns == "" { if args.Netns == "" {
return nil return nil
@ -285,17 +315,21 @@ 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()
if isLayer3 {
// run the IPAM plugin and get back the config to apply // run the IPAM plugin and get back the config to apply
err = ipam.ExecCheck(n.IPAM.Type, args.StdinData) err = ipam.ExecCheck(n.IPAM.Type, args.StdinData)
if err != nil { if err != nil {
return err return err
} }
}
// Parse previous result. // Parse previous result.
if n.NetConf.RawPrevResult == nil { if n.NetConf.RawPrevResult == nil {

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"