Skip to content

Commit

Permalink
bridge: convert to netlink ops and add e2e testing
Browse files Browse the repository at this point in the history
  • Loading branch information
dcbw committed Mar 30, 2016
1 parent b6aa469 commit ecd9620
Show file tree
Hide file tree
Showing 4 changed files with 259 additions and 13 deletions.
22 changes: 11 additions & 11 deletions plugins/main/bridge/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import (

"github.com/appc/cni/pkg/ip"
"github.com/appc/cni/pkg/ipam"
"github.com/appc/cni/pkg/ns"
. "github.com/appc/cni/pkg/ops"
"github.com/appc/cni/pkg/skel"
"github.com/appc/cni/pkg/types"
"github.com/vishvananda/netlink"
Expand Down Expand Up @@ -59,7 +59,7 @@ func loadNetConf(bytes []byte) (*NetConf, error) {
}

func ensureBridgeAddr(br *netlink.Bridge, ipn *net.IPNet) error {
addrs, err := netlink.AddrList(br, syscall.AF_INET)
addrs, err := Net().AddrList(br, syscall.AF_INET)
if err != nil && err != syscall.ENOENT {
return fmt.Errorf("could not get list of IP addresses: %v", err)
}
Expand All @@ -77,14 +77,14 @@ func ensureBridgeAddr(br *netlink.Bridge, ipn *net.IPNet) error {
}

addr := &netlink.Addr{IPNet: ipn, Label: ""}
if err := netlink.AddrAdd(br, addr); err != nil {
if err := Net().AddrAdd(br, addr); err != nil {
return fmt.Errorf("could not add IP address to %q: %v", br.Name, err)
}
return nil
}

func bridgeByName(name string) (*netlink.Bridge, error) {
l, err := netlink.LinkByName(name)
l, err := Net().LinkByName(name)
if err != nil {
return nil, fmt.Errorf("could not lookup %q: %v", name, err)
}
Expand All @@ -103,7 +103,7 @@ func ensureBridge(brName string, mtu int) (*netlink.Bridge, error) {
},
}

if err := netlink.LinkAdd(br); err != nil {
if err := Net().LinkAdd(br); err != nil {
if err != syscall.EEXIST {
return nil, fmt.Errorf("could not add %q: %v", brName, err)
}
Expand All @@ -115,7 +115,7 @@ func ensureBridge(brName string, mtu int) (*netlink.Bridge, error) {
}
}

if err := netlink.LinkSetUp(br); err != nil {
if err := Net().LinkSetUp(br); err != nil {
return nil, err
}

Expand All @@ -125,7 +125,7 @@ func ensureBridge(brName string, mtu int) (*netlink.Bridge, error) {
func setupVeth(netns string, br *netlink.Bridge, ifName string, mtu int) error {
var hostVethName string

err := ns.WithNetNSPath(netns, false, func(hostNS *os.File) error {
err := Net().WithNetNSPath(netns, false, func(hostNS *os.File) error {
// create the veth pair in the container and move host end into host netns
hostVeth, _, err := ip.SetupVeth(ifName, mtu, hostNS)
if err != nil {
Expand All @@ -140,13 +140,13 @@ func setupVeth(netns string, br *netlink.Bridge, ifName string, mtu int) error {
}

// need to lookup hostVeth again as its index has changed during ns move
hostVeth, err := netlink.LinkByName(hostVethName)
hostVeth, err := Net().LinkByName(hostVethName)
if err != nil {
return fmt.Errorf("failed to lookup %q: %v", hostVethName, err)
}

// connect host veth end to the bridge
if err = netlink.LinkSetMaster(hostVeth, br); err != nil {
if err = Net().LinkSetMaster(hostVeth, br); err != nil {
return fmt.Errorf("failed to connect %q to bridge %v: %v", hostVethName, br.Attrs().Name, err)
}

Expand Down Expand Up @@ -197,7 +197,7 @@ func cmdAdd(args *skel.CmdArgs) error {
result.IP4.Gateway = calcGatewayIP(&result.IP4.IP)
}

err = ns.WithNetNSPath(args.Netns, false, func(hostNS *os.File) error {
err = Net().WithNetNSPath(args.Netns, false, func(hostNS *os.File) error {
return ipam.ConfigureIface(args.IfName, result)
})
if err != nil {
Expand Down Expand Up @@ -241,7 +241,7 @@ func cmdDel(args *skel.CmdArgs) error {
return err
}

return ns.WithNetNSPath(args.Netns, false, func(hostNS *os.File) error {
return Net().WithNetNSPath(args.Netns, false, func(hostNS *os.File) error {
return ip.DelLinkByName(args.IfName)
})
}
Expand Down
27 changes: 27 additions & 0 deletions plugins/main/bridge/bridge_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"

"testing"
)

func TestBridge(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "bridge Suite")
}
219 changes: 219 additions & 0 deletions plugins/main/bridge/bridge_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
// Copyright 2015 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"encoding/json"
"fmt"
"io/ioutil"
"net"
"os"
"syscall"

"github.com/appc/cni/pkg/ops"
"github.com/appc/cni/pkg/skel"
"github.com/appc/cni/pkg/types"

"github.com/vishvananda/netlink"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("bridge Operations", func() {
var (
tops *ops.TestNetOps
)

BeforeEach(func() {
tops = ops.NewTestNetOps(func(format string, args ...interface{}) {
GinkgoT().Log(fmt.Sprintf(format, args...))
}).(*ops.TestNetOps)
})

AfterEach(func() {
ops.ClearTestNetOps()
})

It("creates a bridge", func() {
const IFNAME = "bridge0"

conf := &NetConf{
NetConf: types.NetConf{
Name: "testConfig",
Type: "bridge",
},
BrName: IFNAME,
IsGW: false,
IPMasq: false,
MTU: 5000,
}

bridge, err := setupBridge(conf)
Expect(err).NotTo(HaveOccurred())
Expect(bridge.Attrs().Name).To(Equal(IFNAME))

// Double check that the link was added
link, err := tops.LinkByName(IFNAME)
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().Name).To(Equal(IFNAME))
})

It("handles an existing bridge", func() {
const IFNAME = "bridge0"

err := tops.LinkAdd(&netlink.Bridge{
LinkAttrs: netlink.LinkAttrs{
Name: IFNAME,
},
})
Expect(err).NotTo(HaveOccurred())

link, err := tops.LinkByName(IFNAME)
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().Name).To(Equal(IFNAME))
ifindex := link.Attrs().Index

conf := &NetConf{
NetConf: types.NetConf{
Name: "testConfig",
Type: "bridge",
},
BrName: IFNAME,
IsGW: false,
IPMasq: false,
}

bridge, err := setupBridge(conf)
Expect(err).NotTo(HaveOccurred())
Expect(bridge.Attrs().Name).To(Equal(IFNAME))
Expect(bridge.Attrs().Index).To(Equal(ifindex))

// Double check that the link has the same ifindex
link, err = tops.LinkByName(IFNAME)
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().Name).To(Equal(IFNAME))
Expect(link.Attrs().Index).To(Equal(ifindex))
})

It("configures and deconfigures a bridge and veth with ADD/DEL", func() {
const NSPATH = "/some/fake/path/1"
const BRNAME = "cni0"
const IFNAME = "eth0"

gwaddr, subnet, err := net.ParseCIDR("10.1.2.1/24")
Expect(err).NotTo(HaveOccurred())

CONF := fmt.Sprintf(`{
"name": "mynet",
"type": "bridge",
"bridge": "%s",
"isGateway": true,
"ipMasq": false,
"ipam": {
"type": "host-local",
"subnet": "%s"
}
}`, BRNAME, subnet.String())

targetNetNS, err := tops.GetNS(NSPATH)
Expect(err).NotTo(HaveOccurred())

args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: NSPATH,
IfName: IFNAME,
StdinData: []byte(CONF),
}

os.Setenv("CNI_COMMAND", "ADD")
// Look for locally built plugins
os.Setenv("CNI_PATH", os.Getenv("PATH"))
os.Setenv("CNI_NETNS", NSPATH)
os.Setenv("CNI_IFNAME", IFNAME)

// Redirect stdout to capture plugin result
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w

err = cmdAdd(args)
w.Close()
Expect(err).NotTo(HaveOccurred())

// parse the result
out, _ := ioutil.ReadAll(r)
os.Stdout = oldStdout
result := types.Result{}
err = json.Unmarshal(out, &result)
Expect(err).NotTo(HaveOccurred())

// Make sure bridge link exists
link, err := tops.LinkByName(BRNAME)
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().Name).To(Equal(BRNAME))

// Ensure bridge has gateway address
addrs, err := tops.AddrList(link, syscall.AF_INET)
Expect(err).NotTo(HaveOccurred())
Expect(len(addrs)).To(BeNumerically(">", 0))
found := false
subnetPrefix, subnetBits := subnet.Mask.Size()
for _, a := range addrs {
aPrefix, aBits := a.IPNet.Mask.Size()
if a.IPNet.IP.Equal(gwaddr) && aPrefix == subnetPrefix && aBits == subnetBits {
found = true
break
}
}
Expect(found).To(Equal(true))

// Check for the veth link in the main namespace
links, err := tops.LinkList()
Expect(err).NotTo(HaveOccurred())
Expect(len(links)).To(Equal(2))
for _, l := range links {
if l.Attrs().Name != BRNAME {
veth, isVeth := l.(*netlink.Veth)
Expect(isVeth).To(Equal(true))
Expect(veth.PeerName).To(Equal(IFNAME))
}
}

// Find the veth peer in the container namespace
var innerErr error
var innerLink netlink.Link
err = tops.WithNetNS(targetNetNS, false, func(*os.File) error {
innerLink, innerErr = tops.LinkByName(IFNAME)
return nil
})
Expect(err).NotTo(HaveOccurred())
Expect(innerErr).NotTo(HaveOccurred())
Expect(innerLink.Attrs().Name).To(Equal(IFNAME))

os.Setenv("CNI_COMMAND", "DEL")
err = cmdDel(args)
Expect(err).NotTo(HaveOccurred())

// Make sure macvlan link has been deleted
err = tops.WithNetNS(targetNetNS, false, func(*os.File) error {
innerLink, innerErr = tops.LinkByName(IFNAME)
return nil
})
Expect(err).NotTo(HaveOccurred())
Expect(innerErr).To(HaveOccurred())
Expect(innerLink).To(BeNil())
})
})
4 changes: 2 additions & 2 deletions test
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ set -e

source ./build

TESTABLE="plugins/ipam/dhcp plugins/main/loopback pkg/invoke pkg/ns pkg/skel pkg/types pkg/ops plugins/main/ipvlan plugins/main/macvlan"
FORMATTABLE="$TESTABLE libcni pkg/ip pkg/ns pkg/types pkg/ipam plugins/ipam/host-local plugins/main/bridge plugins/meta/flannel plugins/meta/tuning"
TESTABLE="plugins/ipam/dhcp plugins/main/loopback pkg/invoke pkg/ns pkg/skel pkg/types pkg/ops plugins/main/ipvlan plugins/main/macvlan plugins/main/bridge"
FORMATTABLE="$TESTABLE libcni pkg/ip pkg/ns pkg/types pkg/ipam plugins/ipam/host-local plugins/meta/flannel plugins/meta/tuning"

# user has not provided PKG override
if [ -z "$PKG" ]; then
Expand Down

0 comments on commit ecd9620

Please sign in to comment.