Create a plugin for up'ing a lo device
authorZachary Gershman <zgershman@pivotal.io>
Fri, 12 Feb 2016 17:30:10 +0000 (09:30 -0800)
committerzachgersh <zachgersh@gmail.com>
Mon, 29 Feb 2016 17:29:06 +0000 (12:29 -0500)
- Believe we need sudo to create netns
- Use syscall instead of relying on ip netns
- Add sudo to .travis.yml
- Needs more -E
- Revert Godeps GoVersion to 1.4.2
- in travis, test command is run with all necessary env vars
- Loopback plugin only works on 'lo' interface
- Update README, add loopback plugin config
- note script dependency on jq

Signed-off-by: Gabe Rosenhouse <grosenhouse@pivotal.io>
.travis.yml
Godeps/Godeps.json
README.md
plugins/main/loopback/loopback.go [new file with mode: 0644]
plugins/main/loopback/loopback_suite_test.go [new file with mode: 0644]
plugins/main/loopback/loopback_test.go [new file with mode: 0644]
scripts/priv-net-run.sh

index 34249a2..2c79690 100644 (file)
@@ -1,4 +1,5 @@
 language: go
+sudo: required
 
 go:
   - 1.4
@@ -12,4 +13,7 @@ install:
  - go get ${TOOLS_CMD}/vet
 
 script:
- - ./test
+ - sudo -E /bin/bash -c 'PATH=$GOROOT/bin:$PATH ./test'
+
+notifications:
+  email: false
index ae04ed0..69a1c72 100644 (file)
@@ -1,6 +1,6 @@
 {
        "ImportPath": "github.com/appc/cni",
-       "GoVersion": "go1.5.3",
+       "GoVersion": "go1.4.2",
        "Packages": [
                "./..."
        ],
index 933165e..5c1c25b 100644 (file)
--- a/README.md
+++ b/README.md
@@ -24,7 +24,9 @@ This repository includes a number of common plugins that can be found in plugins
 Please see Documentation/ folder for documentation about particular plugins.
 
 ## Running the plugins
-The scripts/ directory contains two scripts, priv-net-run.sh and docker-run.sh, that can be used to exercise the plugins.
+The scripts/ directory contains two scripts, `priv-net-run.sh` and `docker-run.sh`, that can be used to exercise the plugins.
+
+**note - priv-net-run.sh depends on `jq`**
 
 Start out by creating a netconf file to describe a network:
 
@@ -46,6 +48,11 @@ $ cat >/etc/cni/net.d/10-mynet.conf <<EOF
        }
 }
 EOF
+$ cat >/etc/cni/net.d/99-loopback.conf <<EOF
+{
+       "type": "loopback"
+}
+EOF
 ```
 
 The directory `/etc/cni/net.d` is the default location in which the scripts will look for net configurations.
diff --git a/plugins/main/loopback/loopback.go b/plugins/main/loopback/loopback.go
new file mode 100644 (file)
index 0000000..d0e9646
--- /dev/null
@@ -0,0 +1,40 @@
+package main
+
+import (
+       "os"
+
+       "github.com/appc/cni/pkg/ns"
+       "github.com/appc/cni/pkg/skel"
+       "github.com/vishvananda/netlink"
+)
+
+func cmdAdd(args *skel.CmdArgs) error {
+       args.IfName = "lo" // ignore config, this only works for loopback
+       err := ns.WithNetNSPath(args.Netns, false, func(hostNS *os.File) error {
+               link, err := netlink.LinkByName(args.IfName)
+               if err != nil {
+                       return err // not tested
+               }
+
+               err = netlink.LinkSetUp(link)
+               if err != nil {
+                       return err // not tested
+               }
+
+               return nil
+       })
+       if err != nil {
+               return err // not tested
+       }
+
+       return nil
+}
+
+func cmdDel(args *skel.CmdArgs) error {
+       // del does nothing, we're going to destroy the device anyway
+       return nil
+}
+
+func main() {
+       skel.PluginMain(cmdAdd, cmdDel)
+}
diff --git a/plugins/main/loopback/loopback_suite_test.go b/plugins/main/loopback/loopback_suite_test.go
new file mode 100644 (file)
index 0000000..c9c3438
--- /dev/null
@@ -0,0 +1,58 @@
+package main_test
+
+import (
+       "os"
+
+       "github.com/onsi/gomega/gexec"
+
+       . "github.com/onsi/ginkgo"
+       . "github.com/onsi/gomega"
+
+       "testing"
+
+       "golang.org/x/sys/unix"
+)
+
+var pathToLoPlugin string
+
+func TestLoopback(t *testing.T) {
+       RegisterFailHandler(Fail)
+       RunSpecs(t, "Loopback Suite")
+}
+
+var _ = BeforeSuite(func() {
+       var err error
+       pathToLoPlugin, err = gexec.Build("github.com/appc/cni/plugins/main/loopback")
+       Expect(err).NotTo(HaveOccurred())
+})
+
+var _ = AfterSuite(func() {
+       gexec.CleanupBuildArtifacts()
+})
+
+func makeNetworkNS(containerID string) string {
+       namespace := "/var/run/netns/" + containerID
+
+       err := os.MkdirAll("/var/run/netns", 0600)
+       Expect(err).NotTo(HaveOccurred())
+
+       err = unix.Unshare(unix.CLONE_NEWNET)
+       Expect(err).NotTo(HaveOccurred())
+
+       fd, err := os.Create(namespace)
+       Expect(err).NotTo(HaveOccurred())
+       defer fd.Close()
+
+       err = unix.Mount("/proc/self/ns/net", namespace, "none", unix.MS_BIND, "")
+       Expect(err).NotTo(HaveOccurred())
+
+       Expect(namespace).To(BeAnExistingFile())
+       return namespace
+}
+
+func removeNetworkNS(networkNS string) error {
+       err := unix.Unmount(networkNS, unix.MNT_DETACH)
+
+       err = os.RemoveAll(networkNS)
+       return err
+}
diff --git a/plugins/main/loopback/loopback_test.go b/plugins/main/loopback/loopback_test.go
new file mode 100644 (file)
index 0000000..5ed2215
--- /dev/null
@@ -0,0 +1,70 @@
+package main_test
+
+import (
+       "fmt"
+       "net"
+       "os"
+       "os/exec"
+       "strings"
+
+       "github.com/appc/cni/pkg/ns"
+       . "github.com/onsi/ginkgo"
+       . "github.com/onsi/gomega"
+       "github.com/onsi/gomega/gexec"
+)
+
+var _ = Describe("Loopback", func() {
+       var (
+               networkNS   string
+               containerID string
+               command     *exec.Cmd
+       )
+
+       BeforeEach(func() {
+               command = exec.Command(pathToLoPlugin)
+
+               environ := os.Environ()
+
+               containerID = "some-container-id"
+               networkNS = makeNetworkNS(containerID)
+
+               cniEnvVars := []string{
+                       fmt.Sprintf("CNI_COMMAND=%s", "ADD"),
+                       fmt.Sprintf("CNI_CONTAINERID=%s", containerID),
+                       fmt.Sprintf("CNI_NETNS=%s", networkNS),
+                       fmt.Sprintf("CNI_IFNAME=%s", "this is ignored"),
+                       fmt.Sprintf("CNI_ARGS=%s", "none"),
+                       fmt.Sprintf("CNI_PATH=%s", "/some/test/path"),
+               }
+
+               for _, v := range cniEnvVars {
+                       environ = append(environ, v)
+               }
+
+               command.Env = environ
+               command.Stdin = strings.NewReader("this doesn't matter")
+       })
+
+       AfterEach(func() {
+               Expect(removeNetworkNS(networkNS)).To(Succeed())
+       })
+
+       Context("when given a network namespace", func() {
+               It("sets the lo device to UP", func() {
+                       session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
+                       Expect(err).NotTo(HaveOccurred())
+
+                       Eventually(session).Should(gexec.Exit(0))
+
+                       var lo *net.Interface
+                       err = ns.WithNetNSPath(networkNS, false, func(hostNS *os.File) error {
+                               var err error
+                               lo, err = net.InterfaceByName("lo")
+                               return err
+                       })
+                       Expect(err).NotTo(HaveOccurred())
+
+                       Expect(lo.Flags & net.FlagUp).To(Equal(net.FlagUp))
+               })
+       })
+})
index 5a27a78..607a352 100755 (executable)
@@ -8,7 +8,6 @@ contid=$(printf '%x%x%x%x' $RANDOM $RANDOM $RANDOM $RANDOM)
 netnspath=/var/run/netns/$contid
 
 ip netns add $contid
-ip netns exec $contid ip link set lo up
 ./exec-plugins.sh add $contid $netnspath