Skip to content

Commit

Permalink
Merge pull request #220 from hwchiu/host-device-support-ipam
Browse files Browse the repository at this point in the history
Support the IPAM in the host-device
  • Loading branch information
dcbw committed Nov 7, 2018
2 parents 44757b9 + d3284f7 commit 534bfaf
Show file tree
Hide file tree
Showing 2 changed files with 169 additions and 6 deletions.
57 changes: 56 additions & 1 deletion plugins/main/host-device/host-device.go
Expand Up @@ -17,6 +17,7 @@ package main
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net"
Expand All @@ -28,10 +29,12 @@ import (
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/ipam"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/vishvananda/netlink"
)

//NetConf for host-device config, look the README to learn how to use those parameters
type NetConf struct {
types.NetConf
Device string `json:"device"` // Device-Name, something like eth0 or can0 etc.
Expand Down Expand Up @@ -77,11 +80,57 @@ func cmdAdd(args *skel.CmdArgs) error {
if err != nil {
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)
}

func cmdDel(args *skel.CmdArgs) error {
_, err := loadConf(args.StdinData)
cfg, err := loadConf(args.StdinData)
if err != nil {
return err
}
Expand All @@ -98,6 +147,12 @@ func cmdDel(args *skel.CmdArgs) error {
return err
}

if cfg.IPAM.Type != "" {
if err := ipam.ExecDel(cfg.IPAM.Type, args.StdinData); err != nil {
return err
}
}

return nil
}

Expand Down
118 changes: 113 additions & 5 deletions plugins/main/host-device/host-device_test.go
Expand Up @@ -44,7 +44,7 @@ var _ = Describe("base functionality", func() {
originalNS.Close()
})

It("Works with a valid config", func() {
It("Works with a valid config without IPAM", func() {
var origLink netlink.Link

// prepare ifname in original namespace
Expand All @@ -68,7 +68,7 @@ var _ = Describe("base functionality", func() {
targetNS, err := testutils.NewNS()
Expect(err).NotTo(HaveOccurred())

CNI_IFNAME := "eth0"
cniName := "eth0"
conf := fmt.Sprintf(`{
"cniVersion": "0.3.0",
"name": "cni-plugin-host-device-test",
Expand All @@ -78,7 +78,7 @@ var _ = Describe("base functionality", func() {
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: CNI_IFNAME,
IfName: cniName,
StdinData: []byte(conf),
}
var resI types.Result
Expand All @@ -95,7 +95,7 @@ var _ = Describe("base functionality", func() {
Expect(err).NotTo(HaveOccurred())
Expect(res.Interfaces).To(Equal([]*current.Interface{
{
Name: CNI_IFNAME,
Name: cniName,
Mac: origLink.Attrs().HardwareAddr.String(),
Sandbox: targetNS.Path(),
},
Expand All @@ -104,7 +104,7 @@ var _ = Describe("base functionality", func() {
// assert that dummy0 is now in the target namespace
err = targetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
link, err := netlink.LinkByName(CNI_IFNAME)
link, err := netlink.LinkByName(cniName)
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr))
return nil
Expand Down Expand Up @@ -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() {
conf := `{
"cniVersion": "0.3.0",
Expand Down

0 comments on commit 534bfaf

Please sign in to comment.