Merge pull request #93 from squeed/host-device
plugins/main/host-device: generate result, fix DEL, other cleanups
This commit is contained in:
commit
92c634042c
21
plugins/main/host-device/README.md
Normal file
21
plugins/main/host-device/README.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# host-device
|
||||||
|
Move an already-existing device in to a container.
|
||||||
|
|
||||||
|
This simple plugin will move the requested device from the host's network namespace
|
||||||
|
to the container's. Nothing else will be done - no IPAM, no addresses.
|
||||||
|
|
||||||
|
The device can be specified with any one of three properties:
|
||||||
|
* `device`: The device name, e.g. `eth0`, `can0`
|
||||||
|
* `hwaddr`: A MAC address
|
||||||
|
* `kernelpath`: The kernel device kobj, e.g. `/sys/devices/pci0000:00/0000:00:1f.6`
|
||||||
|
|
||||||
|
For this plugin, `CNI_IFNAME` will be ignored. Upon DEL, the device will be moved back.
|
||||||
|
|
||||||
|
A sample configuration might look like:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"cniVersion": "0.3.1",
|
||||||
|
"device": "enp0s1"
|
||||||
|
}
|
||||||
|
```
|
@ -25,6 +25,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
|
"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/ns"
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
@ -32,6 +33,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type NetConf struct {
|
type NetConf struct {
|
||||||
|
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.
|
||||||
HWAddr string `json:"hwaddr"` // MAC Address of target network interface
|
HWAddr string `json:"hwaddr"` // MAC Address of target network interface
|
||||||
KernelPath string `json:"kernelpath"` // Kernelpath of the device
|
KernelPath string `json:"kernelpath"` // Kernelpath of the device
|
||||||
@ -65,8 +67,12 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
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 containerNs.Close()
|
defer containerNs.Close()
|
||||||
defer (¤t.Result{}).Print()
|
|
||||||
return addLink(cfg.Device, cfg.HWAddr, cfg.KernelPath, containerNs)
|
dev, err := moveLinkIn(cfg.Device, cfg.HWAddr, cfg.KernelPath, containerNs)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to move link %v", err)
|
||||||
|
}
|
||||||
|
return printLink(dev, cfg.CNIVersion, containerNs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdDel(args *skel.CmdArgs) error {
|
func cmdDel(args *skel.CmdArgs) error {
|
||||||
@ -80,35 +86,58 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
defer containerNs.Close()
|
defer containerNs.Close()
|
||||||
defer fmt.Println(`{}`)
|
defer fmt.Println(`{}`)
|
||||||
return removeLink(cfg.Device, cfg.HWAddr, cfg.KernelPath, containerNs)
|
return moveLinkOut(cfg.Device, cfg.HWAddr, cfg.KernelPath, containerNs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addLink(device, hwAddr, kernelPath string, containerNs ns.NetNS) error {
|
func moveLinkIn(device, hwAddr, kernelPath string, containerNs ns.NetNS) (netlink.Link, error) {
|
||||||
dev, err := getLink(device, hwAddr, kernelPath)
|
dev, err := getLink(device, hwAddr, kernelPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := netlink.LinkSetNsFd(dev, int(containerNs.Fd())); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return netlink.LinkSetNsFd(dev, int(containerNs.Fd()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeLink(device, hwAddr, kernelPath string, containerNs ns.NetNS) error {
|
if err := containerNs.Do(func(_ ns.NetNS) error {
|
||||||
var dev netlink.Link
|
dev, err = netlink.LinkByName(dev.Attrs().Name)
|
||||||
err := containerNs.Do(func(_ ns.NetNS) error {
|
|
||||||
d, err := getLink(device, hwAddr, kernelPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
dev = d
|
|
||||||
return nil
|
return nil
|
||||||
})
|
}); err != nil {
|
||||||
if err != nil {
|
return nil, err
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return dev, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func moveLinkOut(device, hwAddr, kernelPath string, containerNs ns.NetNS) error {
|
||||||
defaultNs, err := ns.GetCurrentNS()
|
defaultNs, err := ns.GetCurrentNS()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return netlink.LinkSetNsFd(dev, int(defaultNs.Fd()))
|
|
||||||
|
return containerNs.Do(func(_ ns.NetNS) error {
|
||||||
|
dev, err := getLink(device, hwAddr, kernelPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return netlink.LinkSetNsFd(dev, int(defaultNs.Fd()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func printLink(dev netlink.Link, cniVersion string, containerNs ns.NetNS) error {
|
||||||
|
result := current.Result{
|
||||||
|
CNIVersion: current.ImplementedSpecVersion,
|
||||||
|
Interfaces: []*current.Interface{
|
||||||
|
{
|
||||||
|
Name: dev.Attrs().Name,
|
||||||
|
Mac: dev.Attrs().HardwareAddr.String(),
|
||||||
|
Sandbox: containerNs.Path(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return types.PrintResult(&result, cniVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLink(devname, hwaddr, kernelpath string) (netlink.Link, error) {
|
func getLink(devname, hwaddr, kernelpath string) (netlink.Link, error) {
|
||||||
|
@ -19,6 +19,8 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
"github.com/containernetworking/plugins/pkg/ns"
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
"github.com/containernetworking/plugins/pkg/testutils"
|
"github.com/containernetworking/plugins/pkg/testutils"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
@ -44,6 +46,8 @@ var _ = Describe("base functionality", func() {
|
|||||||
|
|
||||||
It("Works with a valid config", func() {
|
It("Works with a valid config", func() {
|
||||||
|
|
||||||
|
var origLink netlink.Link
|
||||||
|
|
||||||
// prepare ifname in original namespace
|
// prepare ifname in original namespace
|
||||||
err := originalNS.Do(func(ns.NetNS) error {
|
err := originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
@ -53,9 +57,9 @@ var _ = Describe("base functionality", func() {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
link, err := netlink.LinkByName(ifname)
|
origLink, err = netlink.LinkByName(ifname)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
err = netlink.LinkSetUp(link)
|
err = netlink.LinkSetUp(origLink)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@ -77,13 +81,26 @@ var _ = Describe("base functionality", func() {
|
|||||||
IfName: ifname,
|
IfName: ifname,
|
||||||
StdinData: []byte(conf),
|
StdinData: []byte(conf),
|
||||||
}
|
}
|
||||||
|
var resI types.Result
|
||||||
err = originalNS.Do(func(ns.NetNS) error {
|
err = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
_, _, err := testutils.CmdAddWithResult(targetNS.Path(), ifname, []byte(conf), func() error { return cmdAdd(args) })
|
var err error
|
||||||
|
resI, _, err = testutils.CmdAddWithResult(targetNS.Path(), ifname, []byte(conf), func() error { return cmdAdd(args) })
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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: ifname,
|
||||||
|
Mac: origLink.Attrs().HardwareAddr.String(),
|
||||||
|
Sandbox: targetNS.Path(),
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
// 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()
|
||||||
@ -102,6 +119,19 @@ var _ = Describe("base functionality", func() {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
// Check that deleting the device moves it back
|
||||||
|
err = originalNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
err = testutils.CmdDelWithResult(targetNS.Path(), ifname, 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() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user