summaryrefslogtreecommitdiff
path: root/libpod/network/cni/run_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'libpod/network/cni/run_test.go')
-rw-r--r--libpod/network/cni/run_test.go1326
1 files changed, 1326 insertions, 0 deletions
diff --git a/libpod/network/cni/run_test.go b/libpod/network/cni/run_test.go
new file mode 100644
index 000000000..32e88ca61
--- /dev/null
+++ b/libpod/network/cni/run_test.go
@@ -0,0 +1,1326 @@
+// +build linux
+
+package cni_test
+
+// The tests have to be run as root.
+// For each test there will be two network namespaces created,
+// netNSTest and netNSContainer. Each test must be run inside
+// netNSTest to prevent leakage in the host netns, therefore
+// it should use the following structure:
+// It("test name", func() {
+// runTest(func() {
+// // add test logic here
+// })
+// })
+
+import (
+ "bytes"
+ "io/ioutil"
+ "net"
+ "os"
+ "path/filepath"
+ "strconv"
+ "sync"
+ "time"
+
+ "github.com/containernetworking/plugins/pkg/ns"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ "github.com/sirupsen/logrus"
+ "github.com/vishvananda/netlink"
+ "golang.org/x/sys/unix"
+
+ "github.com/containers/podman/v3/libpod/network/types"
+ "github.com/containers/podman/v3/pkg/netns"
+ "github.com/containers/podman/v3/pkg/rootless"
+ "github.com/containers/storage/pkg/stringid"
+)
+
+var _ = Describe("run CNI", func() {
+ var (
+ libpodNet types.ContainerNetwork
+ cniConfDir string
+ logBuffer bytes.Buffer
+ netNSTest ns.NetNS
+ netNSContainer ns.NetNS
+ )
+ const cniVarDir = "/var/lib/cni"
+
+ // runTest is a helper function to run a test. It ensures that each test
+ // is run in its own netns. It also creates a mountns to mount a tmpfs to /var/lib/cni.
+ runTest := func(run func()) {
+ netNSTest.Do(func(_ ns.NetNS) error {
+ defer GinkgoRecover()
+ err := os.MkdirAll(cniVarDir, 0755)
+ Expect(err).To(BeNil(), "Failed to create cniVarDir")
+ err = unix.Unshare(unix.CLONE_NEWNS)
+ Expect(err).To(BeNil(), "Failed to create new mountns")
+ err = unix.Mount("tmpfs", cniVarDir, "tmpfs", unix.MS_NOEXEC|unix.MS_NOSUID|unix.MS_NODEV, "")
+ Expect(err).To(BeNil(), "Failed to mount tmpfs for cniVarDir")
+ defer unix.Unmount(cniVarDir, 0)
+
+ // we have to setup the loopback adapter in this netns to use port forwarding
+ link, err := netlink.LinkByName("lo")
+ Expect(err).To(BeNil(), "Failed to get loopback adapter")
+ err = netlink.LinkSetUp(link)
+ Expect(err).To(BeNil(), "Failed to set loopback adapter up")
+ run()
+ return nil
+ })
+ }
+
+ BeforeEach(func() {
+ // The tests need root privileges.
+ // Technically we could work around that by using user namespaces and
+ // the rootless cni code but this is to much work to get it right for a unit test.
+ if rootless.IsRootless() {
+ Skip("this test needs to be run as root")
+ }
+
+ var err error
+ cniConfDir, err = ioutil.TempDir("", "podman_cni_test")
+ if err != nil {
+ Fail("Failed to create tmpdir")
+ }
+ logBuffer = bytes.Buffer{}
+ logrus.SetOutput(&logBuffer)
+
+ netNSTest, err = netns.NewNS()
+ if err != nil {
+ Fail("Failed to create netns")
+ }
+
+ netNSContainer, err = netns.NewNS()
+ if err != nil {
+ Fail("Failed to create netns")
+ }
+ })
+
+ JustBeforeEach(func() {
+ var err error
+ libpodNet, err = getNetworkInterface(cniConfDir, false)
+ if err != nil {
+ Fail("Failed to create NewCNINetworkInterface")
+ }
+ })
+
+ AfterEach(func() {
+ os.RemoveAll(cniConfDir)
+
+ netns.UnmountNS(netNSTest)
+ netNSTest.Close()
+
+ netns.UnmountNS(netNSContainer)
+ netNSContainer.Close()
+ })
+
+ Context("network setup test", func() {
+
+ It("run with default config", func() {
+ runTest(func() {
+ defNet := types.DefaultNetworkName
+ intName := "eth0"
+ setupOpts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: stringid.GenerateNonCryptoID(),
+ Networks: map[string]types.PerNetworkOptions{
+ defNet: {InterfaceName: intName},
+ },
+ },
+ }
+ res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
+ Expect(err).To(BeNil())
+ Expect(res).To(HaveLen(1))
+ Expect(res).To(HaveKey(defNet))
+ Expect(res[defNet].Interfaces).To(HaveKey(intName))
+ Expect(res[defNet].Interfaces[intName].Networks).To(HaveLen(1))
+ Expect(res[defNet].Interfaces[intName].Networks[0].Subnet.IP.String()).To(ContainSubstring("10.88.0."))
+ Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6))
+ // default network has no dns
+ Expect(res[defNet].DNSServerIPs).To(BeEmpty())
+ Expect(res[defNet].DNSSearchDomains).To(BeEmpty())
+
+ err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts))
+ Expect(err).To(BeNil())
+ })
+ })
+
+ It("run with default config and static ip", func() {
+ runTest(func() {
+ defNet := types.DefaultNetworkName
+ intName := "eth0"
+ ip := net.ParseIP("10.88.5.5")
+ setupOpts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: stringid.GenerateNonCryptoID(),
+ Networks: map[string]types.PerNetworkOptions{
+ defNet: {
+ InterfaceName: intName,
+ StaticIPs: []net.IP{ip},
+ },
+ },
+ },
+ }
+ res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
+ Expect(err).To(BeNil())
+ Expect(res).To(HaveLen(1))
+ Expect(res).To(HaveKey(defNet))
+ Expect(res[defNet].Interfaces).To(HaveKey(intName))
+ Expect(res[defNet].Interfaces[intName].Networks).To(HaveLen(1))
+ Expect(res[defNet].Interfaces[intName].Networks[0].Subnet.IP).To(Equal(ip))
+ Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6))
+ // default network has no dns
+ Expect(res[defNet].DNSServerIPs).To(BeEmpty())
+ Expect(res[defNet].DNSSearchDomains).To(BeEmpty())
+
+ err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts))
+ Expect(err).To(BeNil())
+ })
+ })
+
+ for _, proto := range []string{"tcp", "udp"} {
+ // copy proto to extra var to keep correct references in the goroutines
+ protocol := proto
+ It("run with exposed ports protocol "+protocol, func() {
+ runTest(func() {
+ testdata := stringid.GenerateNonCryptoID()
+ defNet := types.DefaultNetworkName
+ intName := "eth0"
+ setupOpts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: stringid.GenerateNonCryptoID(),
+ PortMappings: []types.PortMapping{{
+ Protocol: protocol,
+ HostIP: "127.0.0.1",
+ HostPort: 5000,
+ ContainerPort: 5000,
+ }},
+ Networks: map[string]types.PerNetworkOptions{
+ defNet: {InterfaceName: intName},
+ },
+ },
+ }
+ res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
+ Expect(err).To(BeNil())
+ Expect(res).To(HaveLen(1))
+ Expect(res).To(HaveKey(defNet))
+ Expect(res[defNet].Interfaces).To(HaveKey(intName))
+ Expect(res[defNet].Interfaces[intName].Networks).To(HaveLen(1))
+ Expect(res[defNet].Interfaces[intName].Networks[0].Subnet.IP.String()).To(ContainSubstring("10.88.0."))
+ Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6))
+ // default network has no dns
+ Expect(res[defNet].DNSServerIPs).To(BeEmpty())
+ Expect(res[defNet].DNSSearchDomains).To(BeEmpty())
+ var wg sync.WaitGroup
+ wg.Add(1)
+ // start a listener in the container ns
+ err = netNSContainer.Do(func(_ ns.NetNS) error {
+ defer GinkgoRecover()
+ runNetListener(&wg, protocol, "0.0.0.0", 5000, testdata)
+ return nil
+ })
+ Expect(err).To(BeNil())
+
+ conn, err := net.Dial(protocol, "127.0.0.1:5000")
+ Expect(err).To(BeNil())
+ _, err = conn.Write([]byte(testdata))
+ Expect(err).To(BeNil())
+ conn.Close()
+
+ // wait for the listener to finish
+ wg.Wait()
+
+ err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts))
+ Expect(err).To(BeNil())
+ })
+ })
+
+ It("run with range ports protocol "+protocol, func() {
+ runTest(func() {
+ defNet := types.DefaultNetworkName
+ intName := "eth0"
+ setupOpts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: stringid.GenerateNonCryptoID(),
+ PortMappings: []types.PortMapping{{
+ Protocol: protocol,
+ HostIP: "127.0.0.1",
+ HostPort: 5001,
+ ContainerPort: 5000,
+ Range: 3,
+ }},
+ Networks: map[string]types.PerNetworkOptions{
+ defNet: {InterfaceName: intName},
+ },
+ },
+ }
+ res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
+ Expect(err).To(BeNil())
+ Expect(res).To(HaveLen(1))
+ Expect(res).To(HaveKey(defNet))
+ Expect(res[defNet].Interfaces).To(HaveKey(intName))
+ Expect(res[defNet].Interfaces[intName].Networks).To(HaveLen(1))
+ containerIP := res[defNet].Interfaces[intName].Networks[0].Subnet.IP.String()
+ Expect(containerIP).To(ContainSubstring("10.88.0."))
+ Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6))
+ // default network has no dns
+ Expect(res[defNet].DNSServerIPs).To(BeEmpty())
+ Expect(res[defNet].DNSSearchDomains).To(BeEmpty())
+
+ // loop over all ports
+ for p := 5001; p < 5004; p++ {
+ port := p
+ var wg sync.WaitGroup
+ wg.Add(1)
+ testdata := stringid.GenerateNonCryptoID()
+ // start a listener in the container ns
+ err = netNSContainer.Do(func(_ ns.NetNS) error {
+ defer GinkgoRecover()
+ runNetListener(&wg, protocol, containerIP, port-1, testdata)
+ return nil
+ })
+ Expect(err).To(BeNil())
+
+ conn, err := net.Dial(protocol, net.JoinHostPort("127.0.0.1", strconv.Itoa(port)))
+ Expect(err).To(BeNil())
+ _, err = conn.Write([]byte(testdata))
+ Expect(err).To(BeNil())
+ conn.Close()
+
+ // wait for the listener to finish
+ wg.Wait()
+ }
+
+ err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts))
+ Expect(err).To(BeNil())
+ })
+ })
+ }
+
+ It("run with comma separated port protocol", func() {
+ runTest(func() {
+ defNet := types.DefaultNetworkName
+ intName := "eth0"
+ setupOpts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: stringid.GenerateNonCryptoID(),
+ PortMappings: []types.PortMapping{{
+ Protocol: "tcp,udp",
+ HostIP: "127.0.0.1",
+ HostPort: 5000,
+ ContainerPort: 5000,
+ }},
+ Networks: map[string]types.PerNetworkOptions{
+ defNet: {InterfaceName: intName},
+ },
+ },
+ }
+ res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
+ Expect(err).To(BeNil())
+ Expect(res).To(HaveLen(1))
+ Expect(res).To(HaveKey(defNet))
+ Expect(res[defNet].Interfaces).To(HaveKey(intName))
+ Expect(res[defNet].Interfaces[intName].Networks).To(HaveLen(1))
+ Expect(res[defNet].Interfaces[intName].Networks[0].Subnet.IP.String()).To(ContainSubstring("10.88.0."))
+ Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6))
+
+ for _, proto := range []string{"tcp", "udp"} {
+ // copy proto to extra var to keep correct references in the goroutines
+ protocol := proto
+
+ testdata := stringid.GenerateNonCryptoID()
+ var wg sync.WaitGroup
+ wg.Add(1)
+ // start tcp listener in the container ns
+ err = netNSContainer.Do(func(_ ns.NetNS) error {
+ defer GinkgoRecover()
+ runNetListener(&wg, protocol, "0.0.0.0", 5000, testdata)
+ return nil
+ })
+ Expect(err).To(BeNil())
+
+ conn, err := net.Dial(protocol, "127.0.0.1:5000")
+ Expect(err).To(BeNil())
+ _, err = conn.Write([]byte(testdata))
+ Expect(err).To(BeNil())
+ conn.Close()
+
+ // wait for the listener to finish
+ wg.Wait()
+ }
+
+ err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts))
+ Expect(err).To(BeNil())
+ })
+ })
+
+ It("call setup twice", func() {
+ runTest(func() {
+ network := types.Network{}
+ network1, err := libpodNet.NetworkCreate(network)
+ Expect(err).To(BeNil())
+
+ intName1 := "eth0"
+ netName1 := network1.Name
+
+ containerID := stringid.GenerateNonCryptoID()
+
+ setupOpts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: containerID,
+ Networks: map[string]types.PerNetworkOptions{
+ netName1: {
+ InterfaceName: intName1,
+ },
+ },
+ },
+ }
+
+ res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
+ Expect(err).To(BeNil())
+ Expect(res).To(HaveLen(1))
+
+ Expect(res).To(HaveKey(netName1))
+ Expect(res[netName1].Interfaces).To(HaveKey(intName1))
+ Expect(res[netName1].Interfaces[intName1].Networks).To(HaveLen(1))
+ ipInt1 := res[netName1].Interfaces[intName1].Networks[0].Subnet.IP
+ Expect(ipInt1).ToNot(BeEmpty())
+ macInt1 := res[netName1].Interfaces[intName1].MacAddress
+ Expect(macInt1).To(HaveLen(6))
+
+ // check in the container namespace if the settings are applied
+ err = netNSContainer.Do(func(_ ns.NetNS) error {
+ defer GinkgoRecover()
+ i, err := net.InterfaceByName(intName1)
+ Expect(err).To(BeNil())
+ Expect(i.Name).To(Equal(intName1))
+ Expect(i.HardwareAddr).To(Equal(macInt1))
+ addrs, err := i.Addrs()
+ Expect(err).To(BeNil())
+ subnet := &net.IPNet{
+ IP: ipInt1,
+ Mask: net.CIDRMask(24, 32),
+ }
+ Expect(addrs).To(ContainElements(subnet))
+
+ // check loopback adapter
+ i, err = net.InterfaceByName("lo")
+ Expect(err).To(BeNil())
+ Expect(i.Name).To(Equal("lo"))
+ Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback))
+ Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up")
+ return nil
+ })
+ Expect(err).To(BeNil())
+
+ network = types.Network{}
+ network2, err := libpodNet.NetworkCreate(network)
+ Expect(err).To(BeNil())
+
+ intName2 := "eth1"
+ netName2 := network2.Name
+
+ setupOpts.Networks = map[string]types.PerNetworkOptions{
+ netName2: {
+ InterfaceName: intName2,
+ },
+ }
+
+ res, err = libpodNet.Setup(netNSContainer.Path(), setupOpts)
+ Expect(err).To(BeNil())
+ Expect(res).To(HaveLen(1))
+
+ Expect(res).To(HaveKey(netName2))
+ Expect(res[netName2].Interfaces).To(HaveKey(intName2))
+ Expect(res[netName2].Interfaces[intName2].Networks).To(HaveLen(1))
+ ipInt2 := res[netName2].Interfaces[intName2].Networks[0].Subnet.IP
+ Expect(ipInt2).ToNot(BeEmpty())
+ macInt2 := res[netName2].Interfaces[intName2].MacAddress
+ Expect(macInt2).To(HaveLen(6))
+
+ // check in the container namespace if the settings are applied
+ err = netNSContainer.Do(func(_ ns.NetNS) error {
+ defer GinkgoRecover()
+ i, err := net.InterfaceByName(intName1)
+ Expect(err).To(BeNil())
+ Expect(i.Name).To(Equal(intName1))
+ Expect(i.HardwareAddr).To(Equal(macInt1))
+ addrs, err := i.Addrs()
+ Expect(err).To(BeNil())
+ subnet := &net.IPNet{
+ IP: ipInt1,
+ Mask: net.CIDRMask(24, 32),
+ }
+ Expect(addrs).To(ContainElements(subnet))
+
+ i, err = net.InterfaceByName(intName2)
+ Expect(err).To(BeNil())
+ Expect(i.Name).To(Equal(intName2))
+ Expect(i.HardwareAddr).To(Equal(macInt2))
+ addrs, err = i.Addrs()
+ Expect(err).To(BeNil())
+ subnet = &net.IPNet{
+ IP: ipInt2,
+ Mask: net.CIDRMask(24, 32),
+ }
+ Expect(addrs).To(ContainElements(subnet))
+
+ // check loopback adapter
+ i, err = net.InterfaceByName("lo")
+ Expect(err).To(BeNil())
+ Expect(i.Name).To(Equal("lo"))
+ Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback))
+ Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up")
+ return nil
+ })
+ Expect(err).To(BeNil())
+
+ teatdownOpts := types.TeardownOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: containerID,
+ Networks: map[string]types.PerNetworkOptions{
+ netName1: {
+ InterfaceName: intName1,
+ },
+ netName2: {
+ InterfaceName: intName2,
+ },
+ },
+ },
+ }
+
+ err = libpodNet.Teardown(netNSContainer.Path(), teatdownOpts)
+ Expect(err).To(BeNil())
+ logString := logBuffer.String()
+ Expect(logString).To(BeEmpty())
+
+ // check in the container namespace that the interface is removed
+ err = netNSContainer.Do(func(_ ns.NetNS) error {
+ defer GinkgoRecover()
+ _, err := net.InterfaceByName(intName1)
+ Expect(err).To(HaveOccurred())
+ _, err = net.InterfaceByName(intName2)
+ Expect(err).To(HaveOccurred())
+
+ // check that only the loopback adapter is left
+ ints, err := net.Interfaces()
+ Expect(err).To(BeNil())
+ Expect(ints).To(HaveLen(1))
+ Expect(ints[0].Name).To(Equal("lo"))
+ Expect(ints[0].Flags & net.FlagLoopback).To(Equal(net.FlagLoopback))
+ Expect(ints[0].Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up")
+
+ return nil
+ })
+ Expect(err).To(BeNil())
+
+ err = libpodNet.NetworkRemove(netName1)
+ Expect(err).To(BeNil())
+ err = libpodNet.NetworkRemove(netName2)
+ Expect(err).To(BeNil())
+
+ // check that the interfaces are removed in the host ns
+ _, err = net.InterfaceByName(network1.NetworkInterface)
+ Expect(err).To(HaveOccurred())
+ _, err = net.InterfaceByName(network2.NetworkInterface)
+ Expect(err).To(HaveOccurred())
+ })
+ })
+
+ It("setup two networks with one setup call", func() {
+ runTest(func() {
+ subnet1, _ := types.ParseCIDR("192.168.0.0/24")
+ subnet2, _ := types.ParseCIDR("192.168.1.0/24")
+ network := types.Network{
+ Subnets: []types.Subnet{
+ {Subnet: subnet1},
+ },
+ }
+ network1, err := libpodNet.NetworkCreate(network)
+ Expect(err).To(BeNil())
+
+ network = types.Network{
+ Subnets: []types.Subnet{
+ {Subnet: subnet2},
+ },
+ }
+ network2, err := libpodNet.NetworkCreate(network)
+ Expect(err).To(BeNil())
+
+ intName1 := "eth0"
+ intName2 := "eth1"
+ netName1 := network1.Name
+ netName2 := network2.Name
+
+ setupOpts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: stringid.GenerateNonCryptoID(),
+ Networks: map[string]types.PerNetworkOptions{
+ netName1: {
+ InterfaceName: intName1,
+ },
+ netName2: {
+ InterfaceName: intName2,
+ },
+ },
+ },
+ }
+
+ res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
+ Expect(err).To(BeNil())
+ Expect(res).To(HaveLen(2))
+
+ Expect(res).To(HaveKey(netName1))
+ Expect(res[netName1].Interfaces).To(HaveKey(intName1))
+ Expect(res[netName1].Interfaces[intName1].Networks).To(HaveLen(1))
+ ipInt1 := res[netName1].Interfaces[intName1].Networks[0].Subnet.IP
+ Expect(ipInt1.String()).To(ContainSubstring("192.168.0."))
+ macInt1 := res[netName1].Interfaces[intName1].MacAddress
+ Expect(macInt1).To(HaveLen(6))
+
+ Expect(res).To(HaveKey(netName2))
+ Expect(res[netName2].Interfaces).To(HaveKey(intName2))
+ Expect(res[netName2].Interfaces[intName2].Networks).To(HaveLen(1))
+ ipInt2 := res[netName2].Interfaces[intName2].Networks[0].Subnet.IP
+ Expect(ipInt2.String()).To(ContainSubstring("192.168.1."))
+ macInt2 := res[netName2].Interfaces[intName2].MacAddress
+ Expect(macInt2).To(HaveLen(6))
+
+ // default network has no dns
+ Expect(res[netName1].DNSServerIPs).To(BeEmpty())
+ Expect(res[netName1].DNSSearchDomains).To(BeEmpty())
+
+ // check in the container namespace if the settings are applied
+ err = netNSContainer.Do(func(_ ns.NetNS) error {
+ defer GinkgoRecover()
+ i, err := net.InterfaceByName(intName1)
+ Expect(err).To(BeNil())
+ Expect(i.Name).To(Equal(intName1))
+ Expect(i.HardwareAddr).To(Equal(macInt1))
+ addrs, err := i.Addrs()
+ Expect(err).To(BeNil())
+ subnet := &net.IPNet{
+ IP: ipInt1,
+ Mask: net.CIDRMask(24, 32),
+ }
+ Expect(addrs).To(ContainElements(subnet))
+
+ i, err = net.InterfaceByName(intName2)
+ Expect(err).To(BeNil())
+ Expect(i.Name).To(Equal(intName2))
+ Expect(i.HardwareAddr).To(Equal(macInt2))
+ addrs, err = i.Addrs()
+ Expect(err).To(BeNil())
+ subnet = &net.IPNet{
+ IP: ipInt2,
+ Mask: net.CIDRMask(24, 32),
+ }
+ Expect(addrs).To(ContainElements(subnet))
+
+ // check loopback adapter
+ i, err = net.InterfaceByName("lo")
+ Expect(err).To(BeNil())
+ Expect(i.Name).To(Equal("lo"))
+ Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback))
+ Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up")
+ return nil
+ })
+ Expect(err).To(BeNil())
+
+ err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts))
+ Expect(err).To(BeNil())
+ logString := logBuffer.String()
+ Expect(logString).To(BeEmpty())
+
+ // check in the container namespace that the interface is removed
+ err = netNSContainer.Do(func(_ ns.NetNS) error {
+ defer GinkgoRecover()
+ _, err := net.InterfaceByName(intName1)
+ Expect(err).To(HaveOccurred())
+ _, err = net.InterfaceByName(intName2)
+ Expect(err).To(HaveOccurred())
+
+ // check that only the loopback adapter is left
+ ints, err := net.Interfaces()
+ Expect(err).To(BeNil())
+ Expect(ints).To(HaveLen(1))
+ Expect(ints[0].Name).To(Equal("lo"))
+ Expect(ints[0].Flags & net.FlagLoopback).To(Equal(net.FlagLoopback))
+ Expect(ints[0].Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up")
+
+ return nil
+ })
+ Expect(err).To(BeNil())
+ })
+
+ })
+
+ It("dual stack network with static ips", func() {
+ // Version checks for cni plugins are not possible, the plugins do not output
+ // version information and using the package manager does not work across distros.
+ // Fedora has the right version so we use this for now.
+ SkipIfNotFedora("requires cni plugins 1.0.0 or newer for multiple static ips")
+ runTest(func() {
+ subnet1, _ := types.ParseCIDR("192.168.0.0/24")
+ subnet2, _ := types.ParseCIDR("fd41:0a75:2ca0:48a9::/64")
+ network := types.Network{
+ Subnets: []types.Subnet{
+ {Subnet: subnet1}, {Subnet: subnet2},
+ },
+ }
+ network1, err := libpodNet.NetworkCreate(network)
+ Expect(err).To(BeNil())
+
+ mac, _ := net.ParseMAC("40:15:2f:d8:42:36")
+ interfaceName := "eth0"
+
+ ip1 := net.ParseIP("192.168.0.5")
+ ip2 := net.ParseIP("fd41:0a75:2ca0:48a9::5")
+
+ netName := network1.Name
+ setupOpts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerName: "mycon",
+ ContainerID: stringid.GenerateNonCryptoID(),
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {
+ InterfaceName: interfaceName,
+ StaticIPs: []net.IP{ip1, ip2},
+ StaticMAC: mac,
+ },
+ },
+ },
+ }
+
+ res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
+ Expect(err).To(BeNil())
+ Expect(res).To(HaveLen(1))
+ Expect(res).To(HaveKey(netName))
+ Expect(res[netName].Interfaces).To(HaveKey(interfaceName))
+ Expect(res[netName].Interfaces[interfaceName].Networks).To(HaveLen(2))
+ Expect(res[netName].Interfaces[interfaceName].Networks[0].Subnet.IP.String()).To(Equal(ip1.String()))
+ Expect(res[netName].Interfaces[interfaceName].Networks[0].Subnet.Mask).To(Equal(subnet1.Mask))
+ Expect(res[netName].Interfaces[interfaceName].Networks[0].Gateway).To(Equal(net.ParseIP("192.168.0.1")))
+ Expect(res[netName].Interfaces[interfaceName].Networks[1].Subnet.IP.String()).To(Equal(ip2.String()))
+ Expect(res[netName].Interfaces[interfaceName].Networks[1].Subnet.Mask).To(Equal(subnet2.Mask))
+ Expect(res[netName].Interfaces[interfaceName].Networks[1].Gateway).To(Equal(net.ParseIP("fd41:0a75:2ca0:48a9::1")))
+ Expect(res[netName].Interfaces[interfaceName].MacAddress).To(Equal(mac))
+ // default network has no dns
+ Expect(res[netName].DNSServerIPs).To(BeEmpty())
+ Expect(res[netName].DNSSearchDomains).To(BeEmpty())
+
+ // check in the container namespace if the settings are applied
+ err = netNSContainer.Do(func(_ ns.NetNS) error {
+ defer GinkgoRecover()
+ i, err := net.InterfaceByName(interfaceName)
+ Expect(err).To(BeNil())
+ Expect(i.Name).To(Equal(interfaceName))
+ Expect(i.HardwareAddr).To(Equal(mac))
+ addrs, err := i.Addrs()
+ Expect(err).To(BeNil())
+ subnet1 := &net.IPNet{
+ IP: ip1,
+ Mask: net.CIDRMask(24, 32),
+ }
+ subnet2 := &net.IPNet{
+ IP: ip2,
+ Mask: net.CIDRMask(64, 128),
+ }
+ Expect(addrs).To(ContainElements(subnet1, subnet2))
+
+ // check loopback adapter
+ i, err = net.InterfaceByName("lo")
+ Expect(err).To(BeNil())
+ Expect(i.Name).To(Equal("lo"))
+ Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback))
+ Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up")
+ return nil
+ })
+ Expect(err).To(BeNil())
+
+ err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts))
+ Expect(err).To(BeNil())
+ logString := logBuffer.String()
+ Expect(logString).To(BeEmpty())
+
+ // check in the container namespace that the interface is removed
+ err = netNSContainer.Do(func(_ ns.NetNS) error {
+ defer GinkgoRecover()
+ _, err := net.InterfaceByName(interfaceName)
+ Expect(err).To(HaveOccurred())
+
+ // check that only the loopback adapter is left
+ ints, err := net.Interfaces()
+ Expect(err).To(BeNil())
+ Expect(ints).To(HaveLen(1))
+ Expect(ints[0].Name).To(Equal("lo"))
+ Expect(ints[0].Flags & net.FlagLoopback).To(Equal(net.FlagLoopback))
+ Expect(ints[0].Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up")
+
+ return nil
+ })
+ Expect(err).To(BeNil())
+ })
+ })
+
+ It("CNI_ARGS from environment variable", func() {
+ runTest(func() {
+ subnet1, _ := types.ParseCIDR("172.16.1.0/24")
+ ip := "172.16.1.5"
+ network := types.Network{
+ Subnets: []types.Subnet{
+ {Subnet: subnet1},
+ },
+ }
+ network1, err := libpodNet.NetworkCreate(network)
+ Expect(err).To(BeNil())
+ netName := network1.Name
+ intName := "eth0"
+ setupOpts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: stringid.GenerateNonCryptoID(),
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {
+ InterfaceName: intName,
+ },
+ },
+ },
+ }
+
+ os.Setenv("CNI_ARGS", "IP="+ip)
+ defer os.Unsetenv("CNI_ARGS")
+
+ res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
+ Expect(err).To(BeNil())
+ Expect(res).To(HaveLen(1))
+ Expect(res).To(HaveKey(netName))
+ Expect(res[netName].Interfaces).To(HaveKey(intName))
+ Expect(res[netName].Interfaces[intName].Networks).To(HaveLen(1))
+ Expect(res[netName].Interfaces[intName].Networks[0].Subnet.IP.String()).To(Equal(ip))
+ Expect(res[netName].Interfaces[intName].Networks[0].Subnet.Mask).To(Equal(net.CIDRMask(24, 32)))
+
+ // check in the container namespace if the settings are applied
+ err = netNSContainer.Do(func(_ ns.NetNS) error {
+ defer GinkgoRecover()
+ i, err := net.InterfaceByName(intName)
+ Expect(err).To(BeNil())
+ Expect(i.Name).To(Equal(intName))
+ addrs, err := i.Addrs()
+ Expect(err).To(BeNil())
+ subnet := &net.IPNet{
+ IP: net.ParseIP(ip),
+ Mask: net.CIDRMask(24, 32),
+ }
+ Expect(addrs).To(ContainElements(subnet))
+
+ // check loopback adapter
+ i, err = net.InterfaceByName("lo")
+ Expect(err).To(BeNil())
+ Expect(i.Name).To(Equal("lo"))
+ Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback))
+ Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up")
+ return nil
+ })
+ Expect(err).To(BeNil())
+ })
+ })
+ })
+
+ Context("network setup test with networks from disk", func() {
+
+ BeforeEach(func() {
+ dir := "testfiles/valid"
+ files, err := ioutil.ReadDir(dir)
+ if err != nil {
+ Fail("Failed to read test directory")
+ }
+ for _, file := range files {
+ filename := file.Name()
+ data, err := ioutil.ReadFile(filepath.Join(dir, filename))
+ if err != nil {
+ Fail("Failed to copy test files")
+ }
+ err = ioutil.WriteFile(filepath.Join(cniConfDir, filename), data, 0700)
+ if err != nil {
+ Fail("Failed to copy test files")
+ }
+ }
+ })
+
+ It("dualstack setup with static ip and dns", func() {
+ SkipIfNoDnsname()
+ // Version checks for cni plugins are not possible, the plugins do not output
+ // version information and using the package manager does not work across distros.
+ // Fedora has the right version so we use this for now.
+ SkipIfNotFedora("requires cni plugins 1.0.0 or newer for multiple static ips")
+ runTest(func() {
+ interfaceName := "eth0"
+
+ ip1 := net.ParseIP("fd10:88:a::11")
+ ip2 := net.ParseIP("10.89.19.15")
+
+ containerName := "myname"
+ aliases := []string{"aliasname"}
+
+ netName := "dualstack"
+ setupOpts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: stringid.GenerateNonCryptoID(),
+ ContainerName: containerName,
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {
+ InterfaceName: interfaceName,
+ StaticIPs: []net.IP{ip1, ip2},
+ Aliases: aliases,
+ },
+ },
+ },
+ }
+
+ network, err := libpodNet.NetworkInspect(netName)
+ Expect(err).To(BeNil())
+ Expect(network.Name).To(Equal(netName))
+ Expect(network.DNSEnabled).To(BeTrue())
+ Expect(network.Subnets).To(HaveLen(2))
+ gw1 := network.Subnets[0].Gateway
+ Expect(gw1).To(HaveLen(16))
+ mask1 := network.Subnets[0].Subnet.Mask
+ Expect(mask1).To(HaveLen(16))
+ gw2 := network.Subnets[1].Gateway
+ Expect(gw2).To(HaveLen(4))
+ mask2 := network.Subnets[1].Subnet.Mask
+ Expect(mask2).To(HaveLen(4))
+
+ // because this net has dns we should always teardown otherwise we leak a dnsmasq process
+ defer libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts))
+ res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
+ Expect(err).To(BeNil())
+ Expect(res).To(HaveLen(1))
+ Expect(res).To(HaveKey(netName))
+ Expect(res[netName].Interfaces).To(HaveKey(interfaceName))
+ Expect(res[netName].Interfaces[interfaceName].Networks).To(HaveLen(2))
+ Expect(res[netName].Interfaces[interfaceName].Networks[0].Subnet.IP.String()).To(Equal(ip1.String()))
+ Expect(res[netName].Interfaces[interfaceName].Networks[0].Subnet.Mask).To(Equal(mask1))
+ Expect(res[netName].Interfaces[interfaceName].Networks[1].Subnet.IP.String()).To(Equal(ip2.String()))
+ Expect(res[netName].Interfaces[interfaceName].Networks[1].Subnet.Mask).To(Equal(mask2))
+ // dualstack network dns
+ Expect(res[netName].DNSServerIPs).To(HaveLen(2))
+ Expect(res[netName].DNSSearchDomains).To(HaveLen(1))
+ Expect(res[netName].DNSSearchDomains).To(ConsistOf("dns.podman"))
+
+ // check in the container namespace if the settings are applied
+ err = netNSContainer.Do(func(_ ns.NetNS) error {
+ defer GinkgoRecover()
+ i, err := net.InterfaceByName(interfaceName)
+ Expect(err).To(BeNil())
+ Expect(i.Name).To(Equal(interfaceName))
+ addrs, err := i.Addrs()
+ Expect(err).To(BeNil())
+ subnet1 := &net.IPNet{
+ IP: ip1,
+ Mask: net.CIDRMask(64, 128),
+ }
+ subnet2 := &net.IPNet{
+ IP: ip2,
+ Mask: net.CIDRMask(24, 32),
+ }
+ Expect(addrs).To(ContainElements(subnet1, subnet2))
+
+ // check loopback adapter
+ i, err = net.InterfaceByName("lo")
+ Expect(err).To(BeNil())
+ Expect(i.Name).To(Equal("lo"))
+ Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback))
+ Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up")
+
+ return nil
+ })
+ Expect(err).To(BeNil())
+
+ err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts))
+ Expect(err).To(BeNil())
+ logString := logBuffer.String()
+ Expect(logString).To(BeEmpty())
+
+ // check in the container namespace that the interface is removed
+ err = netNSContainer.Do(func(_ ns.NetNS) error {
+ defer GinkgoRecover()
+ _, err := net.InterfaceByName(interfaceName)
+ Expect(err).To(HaveOccurred())
+
+ // check that only the loopback adapter is left
+ ints, err := net.Interfaces()
+ Expect(err).To(BeNil())
+ Expect(ints).To(HaveLen(1))
+ Expect(ints[0].Name).To(Equal("lo"))
+ Expect(ints[0].Flags & net.FlagLoopback).To(Equal(net.FlagLoopback))
+ Expect(ints[0].Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up")
+
+ return nil
+ })
+ Expect(err).To(BeNil())
+ })
+ })
+
+ })
+
+ Context("invalid network setup test", func() {
+
+ It("static ip not in subnet", func() {
+ runTest(func() {
+ defNet := types.DefaultNetworkName
+ intName := "eth0"
+ ip := "1.1.1.1"
+ setupOpts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: stringid.GenerateNonCryptoID(),
+ Networks: map[string]types.PerNetworkOptions{
+ defNet: {
+ InterfaceName: intName,
+ StaticIPs: []net.IP{net.ParseIP(ip)},
+ },
+ },
+ },
+ }
+ _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("requested static ip %s not in any subnet on network %s", ip, defNet))
+ })
+ })
+
+ It("setup without namespace path", func() {
+ runTest(func() {
+ defNet := types.DefaultNetworkName
+ intName := "eth0"
+ setupOpts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: stringid.GenerateNonCryptoID(),
+ Networks: map[string]types.PerNetworkOptions{
+ defNet: {
+ InterfaceName: intName,
+ },
+ },
+ },
+ }
+ _, err := libpodNet.Setup("", setupOpts)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("namespacePath is empty"))
+ })
+ })
+
+ It("setup with invalid namespace path", func() {
+ runTest(func() {
+ defNet := types.DefaultNetworkName
+ intName := "eth0"
+ setupOpts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: stringid.GenerateNonCryptoID(),
+ Networks: map[string]types.PerNetworkOptions{
+ defNet: {
+ InterfaceName: intName,
+ },
+ },
+ },
+ }
+ _, err := libpodNet.Setup("some path", setupOpts)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring(`"some path": no such file or directory`))
+ })
+ })
+
+ It("setup without container ID", func() {
+ runTest(func() {
+ defNet := types.DefaultNetworkName
+ intName := "eth0"
+ setupOpts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: "",
+ Networks: map[string]types.PerNetworkOptions{
+ defNet: {
+ InterfaceName: intName,
+ },
+ },
+ },
+ }
+ _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("ContainerID is empty"))
+ })
+ })
+
+ It("setup with aliases but dns disabled", func() {
+ runTest(func() {
+ defNet := types.DefaultNetworkName
+ intName := "eth0"
+ setupOpts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: stringid.GenerateNonCryptoID(),
+ Networks: map[string]types.PerNetworkOptions{
+ defNet: {
+ InterfaceName: intName,
+ Aliases: []string{"somealias"},
+ },
+ },
+ },
+ }
+ _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("cannot set aliases on a network without dns enabled"))
+ })
+ })
+
+ It("setup without networks", func() {
+ runTest(func() {
+ setupOpts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: stringid.GenerateNonCryptoID(),
+ },
+ }
+ _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("must specify at least one network"))
+ })
+ })
+
+ It("setup without interface name", func() {
+ runTest(func() {
+ defNet := types.DefaultNetworkName
+ setupOpts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: stringid.GenerateNonCryptoID(),
+ Networks: map[string]types.PerNetworkOptions{
+ defNet: {
+ InterfaceName: "",
+ },
+ },
+ },
+ }
+ _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("interface name on network %s is empty", defNet))
+ })
+ })
+
+ It("setup does teardown on failure", func() {
+ runTest(func() {
+ subnet1, _ := types.ParseCIDR("192.168.0.0/24")
+ network := types.Network{
+ Subnets: []types.Subnet{
+ {Subnet: subnet1},
+ },
+ }
+ network1, err := libpodNet.NetworkCreate(network)
+ Expect(err).To(BeNil())
+
+ subnet2, _ := types.ParseCIDR("192.168.1.0/31")
+ network = types.Network{
+ Subnets: []types.Subnet{
+ {Subnet: subnet2},
+ },
+ }
+ network2, err := libpodNet.NetworkCreate(network)
+ Expect(err).To(BeNil())
+
+ intName1 := "eth0"
+ intName2 := "eth1"
+ netName1 := network1.Name
+ netName2 := network2.Name
+
+ setupOpts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: stringid.GenerateNonCryptoID(),
+ Networks: map[string]types.PerNetworkOptions{
+ netName1: {
+ InterfaceName: intName1,
+ },
+ netName2: {
+ InterfaceName: intName2,
+ },
+ },
+ },
+ }
+ _, err = libpodNet.Setup(netNSContainer.Path(), setupOpts)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("Network 192.168.1.0/31 too small to allocate from"))
+ // Note: we call teardown on the failing net and log the error, it should be the same.
+ logString := logBuffer.String()
+ Expect(logString).To(ContainSubstring("Network 192.168.1.0/31 too small to allocate from"))
+
+ // check in the container namespace that no interface is there
+ err = netNSContainer.Do(func(_ ns.NetNS) error {
+ defer GinkgoRecover()
+ _, err := net.InterfaceByName(intName1)
+ Expect(err).To(HaveOccurred())
+
+ // Note: We can check if intName2 is removed because
+ // the cni plugin fails before it removes the interface
+
+ // check loopback adapter
+ i, err := net.InterfaceByName("lo")
+ Expect(err).To(BeNil())
+ Expect(i.Name).To(Equal("lo"))
+ Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback))
+ Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up")
+ return nil
+ })
+ Expect(err).To(BeNil())
+ })
+ })
+
+ It("setup with exposed invalid port protocol", func() {
+ runTest(func() {
+ defNet := types.DefaultNetworkName
+ intName := "eth0"
+ setupOpts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: stringid.GenerateNonCryptoID(),
+ PortMappings: []types.PortMapping{{
+ Protocol: "someproto",
+ HostIP: "127.0.0.1",
+ HostPort: 5000,
+ ContainerPort: 5000,
+ }},
+ Networks: map[string]types.PerNetworkOptions{
+ defNet: {InterfaceName: intName},
+ },
+ },
+ }
+ _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("unknown port protocol someproto"))
+ })
+ })
+
+ It("setup with exposed empty port protocol", func() {
+ runTest(func() {
+ defNet := types.DefaultNetworkName
+ intName := "eth0"
+ setupOpts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: stringid.GenerateNonCryptoID(),
+ PortMappings: []types.PortMapping{{
+ Protocol: "",
+ HostIP: "127.0.0.1",
+ HostPort: 5000,
+ ContainerPort: 5000,
+ }},
+ Networks: map[string]types.PerNetworkOptions{
+ defNet: {InterfaceName: intName},
+ },
+ },
+ }
+ _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("port protocol should not be empty"))
+ })
+ })
+
+ It("setup with unknown network", func() {
+ runTest(func() {
+ defNet := "somenet"
+ intName := "eth0"
+ setupOpts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: stringid.GenerateNonCryptoID(),
+ Networks: map[string]types.PerNetworkOptions{
+ defNet: {InterfaceName: intName},
+ },
+ },
+ }
+ _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("network somenet: network not found"))
+ })
+ })
+
+ It("teardown with unknown network", func() {
+ runTest(func() {
+ interfaceName := "eth0"
+ netName := "somenet"
+ teardownOpts := types.TeardownOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: stringid.GenerateNonCryptoID(),
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {
+ InterfaceName: interfaceName,
+ },
+ },
+ },
+ }
+
+ err := libpodNet.Teardown(netNSContainer.Path(), teardownOpts)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("network somenet: network not found"))
+ logString := logBuffer.String()
+ Expect(logString).To(ContainSubstring("failed to load cached network config"))
+ })
+ })
+
+ It("teardown on not connected network", func() {
+ runTest(func() {
+ network := types.Network{}
+ network1, err := libpodNet.NetworkCreate(network)
+ Expect(err).To(BeNil())
+
+ interfaceName := "eth0"
+ netName := network1.Name
+ teardownOpts := types.TeardownOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: stringid.GenerateNonCryptoID(),
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {
+ InterfaceName: interfaceName,
+ },
+ },
+ },
+ }
+
+ // Most CNI plugins do not error on teardown when there is nothing to do.
+ err = libpodNet.Teardown(netNSContainer.Path(), teardownOpts)
+ Expect(err).To(BeNil())
+ logString := logBuffer.String()
+ Expect(logString).To(ContainSubstring("failed to load cached network config"))
+ })
+ })
+ })
+})
+
+func runNetListener(wg *sync.WaitGroup, protocol, ip string, port int, expectedData string) {
+ switch protocol {
+ case "tcp":
+ ln, err := net.Listen(protocol, net.JoinHostPort(ip, strconv.Itoa(port)))
+ Expect(err).To(BeNil())
+ // make sure to read in a separate goroutine to not block
+ go func() {
+ defer GinkgoRecover()
+ defer wg.Done()
+ conn, err := ln.Accept()
+ Expect(err).To(BeNil())
+ conn.SetDeadline(time.Now().Add(1 * time.Second))
+ data, err := ioutil.ReadAll(conn)
+ Expect(err).To(BeNil())
+ Expect(string(data)).To(Equal(expectedData))
+ conn.Close()
+ ln.Close()
+ }()
+ case "udp":
+ conn, err := net.ListenUDP("udp", &net.UDPAddr{
+ IP: net.ParseIP(ip),
+ Port: port,
+ })
+ Expect(err).To(BeNil())
+ conn.SetDeadline(time.Now().Add(1 * time.Second))
+ go func() {
+ defer GinkgoRecover()
+ defer wg.Done()
+ data := make([]byte, len(expectedData))
+ i, err := conn.Read(data)
+ Expect(err).To(BeNil())
+ Expect(i).To(Equal(len(expectedData)))
+ Expect(string(data)).To(Equal(expectedData))
+ conn.Close()
+ }()
+ default:
+ Fail("unsupported protocol")
+ }
+}