Merge pull request #220 from hwchiu/host-device-support-ipam
Support the IPAM in the host-device
This commit is contained in:
commit
534bfafe99
@ -17,6 +17,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
@ -28,10 +29,12 @@ import (
|
|||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/types/current"
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
"github.com/containernetworking/cni/pkg/version"
|
"github.com/containernetworking/cni/pkg/version"
|
||||||
|
"github.com/containernetworking/plugins/pkg/ipam"
|
||||||
"github.com/containernetworking/plugins/pkg/ns"
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//NetConf for host-device config, look the README to learn how to use those parameters
|
||||||
type NetConf struct {
|
type NetConf struct {
|
||||||
types.NetConf
|
types.NetConf
|
||||||
Device string `json:"device"` // Device-Name, something like eth0 or can0 etc.
|
Device string `json:"device"` // Device-Name, something like eth0 or can0 etc.
|
||||||
@ -77,11 +80,57 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to move link %v", err)
|
return fmt.Errorf("failed to move link %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// run the IPAM plugin and get back the config to apply
|
||||||
|
if cfg.IPAM.Type != "" {
|
||||||
|
r, err := ipam.ExecAdd(cfg.IPAM.Type, args.StdinData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoke ipam del if err to avoid ip leak
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
ipam.ExecDel(cfg.IPAM.Type, args.StdinData)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 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{{
|
||||||
|
Name: contDev.Attrs().Name,
|
||||||
|
Mac: contDev.Attrs().HardwareAddr.String(),
|
||||||
|
Sandbox: containerNs.Path(),
|
||||||
|
}}
|
||||||
|
for _, ipc := range result.IPs {
|
||||||
|
// All addresses apply to the container interface (move from host)
|
||||||
|
ipc.Interface = current.Int(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = containerNs.Do(func(_ ns.NetNS) error {
|
||||||
|
if err := ipam.ConfigureIface(args.IfName, result); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return printLink(contDev, cfg.CNIVersion, containerNs)
|
return printLink(contDev, cfg.CNIVersion, containerNs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdDel(args *skel.CmdArgs) error {
|
func cmdDel(args *skel.CmdArgs) error {
|
||||||
_, err := loadConf(args.StdinData)
|
cfg, err := loadConf(args.StdinData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -98,6 +147,12 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.IPAM.Type != "" {
|
||||||
|
if err := ipam.ExecDel(cfg.IPAM.Type, args.StdinData); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ var _ = Describe("base functionality", func() {
|
|||||||
originalNS.Close()
|
originalNS.Close()
|
||||||
})
|
})
|
||||||
|
|
||||||
It("Works with a valid config", func() {
|
It("Works with a valid config without IPAM", func() {
|
||||||
var origLink netlink.Link
|
var origLink netlink.Link
|
||||||
|
|
||||||
// prepare ifname in original namespace
|
// prepare ifname in original namespace
|
||||||
@ -68,7 +68,7 @@ var _ = Describe("base functionality", func() {
|
|||||||
targetNS, err := testutils.NewNS()
|
targetNS, err := testutils.NewNS()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
CNI_IFNAME := "eth0"
|
cniName := "eth0"
|
||||||
conf := fmt.Sprintf(`{
|
conf := fmt.Sprintf(`{
|
||||||
"cniVersion": "0.3.0",
|
"cniVersion": "0.3.0",
|
||||||
"name": "cni-plugin-host-device-test",
|
"name": "cni-plugin-host-device-test",
|
||||||
@ -78,7 +78,7 @@ var _ = Describe("base functionality", func() {
|
|||||||
args := &skel.CmdArgs{
|
args := &skel.CmdArgs{
|
||||||
ContainerID: "dummy",
|
ContainerID: "dummy",
|
||||||
Netns: targetNS.Path(),
|
Netns: targetNS.Path(),
|
||||||
IfName: CNI_IFNAME,
|
IfName: cniName,
|
||||||
StdinData: []byte(conf),
|
StdinData: []byte(conf),
|
||||||
}
|
}
|
||||||
var resI types.Result
|
var resI types.Result
|
||||||
@ -95,7 +95,7 @@ var _ = Describe("base functionality", func() {
|
|||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(res.Interfaces).To(Equal([]*current.Interface{
|
Expect(res.Interfaces).To(Equal([]*current.Interface{
|
||||||
{
|
{
|
||||||
Name: CNI_IFNAME,
|
Name: cniName,
|
||||||
Mac: origLink.Attrs().HardwareAddr.String(),
|
Mac: origLink.Attrs().HardwareAddr.String(),
|
||||||
Sandbox: targetNS.Path(),
|
Sandbox: targetNS.Path(),
|
||||||
},
|
},
|
||||||
@ -104,7 +104,7 @@ var _ = Describe("base functionality", func() {
|
|||||||
// assert that dummy0 is now in the target namespace
|
// assert that dummy0 is now in the target namespace
|
||||||
err = targetNS.Do(func(ns.NetNS) error {
|
err = targetNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
link, err := netlink.LinkByName(CNI_IFNAME)
|
link, err := netlink.LinkByName(cniName)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr))
|
Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr))
|
||||||
return nil
|
return nil
|
||||||
@ -136,6 +136,114 @@ var _ = Describe("base functionality", func() {
|
|||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("Works with a valid config with IPAM", func() {
|
||||||
|
var origLink netlink.Link
|
||||||
|
|
||||||
|
// prepare ifname in original namespace
|
||||||
|
err := originalNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
err := netlink.LinkAdd(&netlink.Dummy{
|
||||||
|
LinkAttrs: netlink.LinkAttrs{
|
||||||
|
Name: ifname,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
origLink, err = netlink.LinkByName(ifname)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
err = netlink.LinkSetUp(origLink)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
// call CmdAdd
|
||||||
|
targetNS, err := testutils.NewNS()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
targetIP := "10.10.0.1/24"
|
||||||
|
cniName := "eth0"
|
||||||
|
conf := fmt.Sprintf(`{
|
||||||
|
"cniVersion": "0.3.0",
|
||||||
|
"name": "cni-plugin-host-device-test",
|
||||||
|
"type": "host-device",
|
||||||
|
"ipam": {
|
||||||
|
"type": "static",
|
||||||
|
"addresses": [
|
||||||
|
{
|
||||||
|
"address":"`+targetIP+`",
|
||||||
|
"gateway": "10.10.0.254"
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
"device": %q
|
||||||
|
}`, ifname)
|
||||||
|
args := &skel.CmdArgs{
|
||||||
|
ContainerID: "dummy",
|
||||||
|
Netns: targetNS.Path(),
|
||||||
|
IfName: cniName,
|
||||||
|
StdinData: []byte(conf),
|
||||||
|
}
|
||||||
|
var resI types.Result
|
||||||
|
err = originalNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
var err error
|
||||||
|
resI, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) })
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
// check that the result was sane
|
||||||
|
res, err := current.NewResultFromResult(resI)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(res.Interfaces).To(Equal([]*current.Interface{
|
||||||
|
{
|
||||||
|
Name: cniName,
|
||||||
|
Mac: origLink.Attrs().HardwareAddr.String(),
|
||||||
|
Sandbox: targetNS.Path(),
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
// assert that dummy0 is now in the target namespace
|
||||||
|
err = targetNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
link, err := netlink.LinkByName(cniName)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr))
|
||||||
|
|
||||||
|
//get the IP address of the interface in the target namespace
|
||||||
|
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
addr := addrs[0].IPNet.String()
|
||||||
|
//assert that IP address is what we set
|
||||||
|
Expect(addr).To(Equal(targetIP))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
// assert that dummy0 is now NOT in the original namespace anymore
|
||||||
|
err = originalNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
_, err := netlink.LinkByName(ifname)
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
// Check that deleting the device moves it back and restores the name
|
||||||
|
err = originalNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
err = testutils.CmdDelWithArgs(args, func() error {
|
||||||
|
return cmdDel(args)
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
_, err := netlink.LinkByName(ifname)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
It("fails an invalid config", func() {
|
It("fails an invalid config", func() {
|
||||||
conf := `{
|
conf := `{
|
||||||
"cniVersion": "0.3.0",
|
"cniVersion": "0.3.0",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user