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:
parent
44297f6ba3
commit
af692de1b8
@ -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
|
||||||
|
|
||||||
|
@ -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,56 +191,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
|
||||||
@ -252,9 +278,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 == "" {
|
||||||
@ -285,16 +315,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"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user