libcni: add integration test coverage
authorGabe Rosenhouse <rosenhouse@gmail.com>
Fri, 15 Jul 2016 00:33:16 +0000 (17:33 -0700)
committerGabe Rosenhouse <rosenhouse@gmail.com>
Fri, 15 Jul 2016 20:01:08 +0000 (13:01 -0700)
libcni/api_test.go [new file with mode: 0644]
libcni/conf_test.go [new file with mode: 0644]
libcni/libcni_suite_test.go [new file with mode: 0644]
test

diff --git a/libcni/api_test.go b/libcni/api_test.go
new file mode 100644 (file)
index 0000000..2df6b4e
--- /dev/null
@@ -0,0 +1,158 @@
+// 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 libcni_test
+
+import (
+       "io/ioutil"
+       "net"
+       "path/filepath"
+
+       "github.com/containernetworking/cni/libcni"
+       "github.com/containernetworking/cni/pkg/skel"
+       "github.com/containernetworking/cni/pkg/types"
+       noop_debug "github.com/containernetworking/cni/plugins/test/noop/debug"
+
+       . "github.com/onsi/ginkgo"
+       . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Invoking the plugin", func() {
+       var (
+               debugFilePath string
+               debug         *noop_debug.Debug
+               cniBinPath    string
+               pluginConfig  string
+               cniConfig     libcni.CNIConfig
+               netConfig     *libcni.NetworkConfig
+               runtimeConfig *libcni.RuntimeConf
+
+               expectedCmdArgs skel.CmdArgs
+       )
+
+       BeforeEach(func() {
+               debugFile, err := ioutil.TempFile("", "cni_debug")
+               Expect(err).NotTo(HaveOccurred())
+               Expect(debugFile.Close()).To(Succeed())
+               debugFilePath = debugFile.Name()
+
+               debug = &noop_debug.Debug{
+                       ReportResult: `{ "ip4": { "ip": "10.1.2.3/24" }, "dns": {} }`,
+               }
+               Expect(debug.WriteDebug(debugFilePath)).To(Succeed())
+
+               cniBinPath = filepath.Dir(pathToPlugin)
+               pluginConfig = `{ "type": "noop", "some-key": "some-value" }`
+               cniConfig = libcni.CNIConfig{Path: []string{cniBinPath}}
+               netConfig = &libcni.NetworkConfig{
+                       Network: &types.NetConf{
+                               Type: "noop",
+                       },
+                       Bytes: []byte(pluginConfig),
+               }
+               runtimeConfig = &libcni.RuntimeConf{
+                       ContainerID: "some-container-id",
+                       NetNS:       "/some/netns/path",
+                       IfName:      "some-eth0",
+                       Args:        [][2]string{[2]string{"DEBUG", debugFilePath}},
+               }
+
+               expectedCmdArgs = skel.CmdArgs{
+                       ContainerID: "some-container-id",
+                       Netns:       "/some/netns/path",
+                       IfName:      "some-eth0",
+                       Args:        "DEBUG=" + debugFilePath,
+                       Path:        cniBinPath,
+                       StdinData:   []byte(pluginConfig),
+               }
+       })
+
+       Describe("AddNetwork", func() {
+               It("executes the plugin with command ADD", func() {
+                       result, err := cniConfig.AddNetwork(netConfig, runtimeConfig)
+                       Expect(err).NotTo(HaveOccurred())
+
+                       Expect(result).To(Equal(&types.Result{
+                               IP4: &types.IPConfig{
+                                       IP: net.IPNet{
+                                               IP:   net.ParseIP("10.1.2.3"),
+                                               Mask: net.IPv4Mask(255, 255, 255, 0),
+                                       },
+                               },
+                       }))
+
+                       debug, err := noop_debug.ReadDebug(debugFilePath)
+                       Expect(err).NotTo(HaveOccurred())
+                       Expect(debug.Command).To(Equal("ADD"))
+                       Expect(debug.CmdArgs).To(Equal(expectedCmdArgs))
+               })
+
+               Context("when finding the plugin fails", func() {
+                       BeforeEach(func() {
+                               netConfig.Network.Type = "does-not-exist"
+                       })
+
+                       It("returns the error", func() {
+                               _, err := cniConfig.AddNetwork(netConfig, runtimeConfig)
+                               Expect(err).To(MatchError(ContainSubstring(`failed to find plugin "does-not-exist"`)))
+                       })
+               })
+
+               Context("when the plugin errors", func() {
+                       BeforeEach(func() {
+                               debug.ReportError = "plugin error: banana"
+                               Expect(debug.WriteDebug(debugFilePath)).To(Succeed())
+                       })
+                       It("unmarshals and returns the error", func() {
+                               result, err := cniConfig.AddNetwork(netConfig, runtimeConfig)
+                               Expect(result).To(BeNil())
+                               Expect(err).To(MatchError("plugin error: banana"))
+                       })
+               })
+       })
+
+       Describe("DelNetwork", func() {
+               It("executes the plugin with command DEL", func() {
+                       err := cniConfig.DelNetwork(netConfig, runtimeConfig)
+                       Expect(err).NotTo(HaveOccurred())
+
+                       debug, err := noop_debug.ReadDebug(debugFilePath)
+                       Expect(err).NotTo(HaveOccurred())
+                       Expect(debug.Command).To(Equal("DEL"))
+                       Expect(debug.CmdArgs).To(Equal(expectedCmdArgs))
+               })
+
+               Context("when finding the plugin fails", func() {
+                       BeforeEach(func() {
+                               netConfig.Network.Type = "does-not-exist"
+                       })
+
+                       It("returns the error", func() {
+                               err := cniConfig.DelNetwork(netConfig, runtimeConfig)
+                               Expect(err).To(MatchError(ContainSubstring(`failed to find plugin "does-not-exist"`)))
+                       })
+               })
+
+               Context("when the plugin errors", func() {
+                       BeforeEach(func() {
+                               debug.ReportError = "plugin error: banana"
+                               Expect(debug.WriteDebug(debugFilePath)).To(Succeed())
+                       })
+                       It("unmarshals and returns the error", func() {
+                               err := cniConfig.DelNetwork(netConfig, runtimeConfig)
+                               Expect(err).To(MatchError("plugin error: banana"))
+                       })
+               })
+       })
+})
diff --git a/libcni/conf_test.go b/libcni/conf_test.go
new file mode 100644 (file)
index 0000000..9460aca
--- /dev/null
@@ -0,0 +1,110 @@
+// 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 libcni_test
+
+import (
+       "io/ioutil"
+       "os"
+       "path/filepath"
+
+       "github.com/containernetworking/cni/libcni"
+       "github.com/containernetworking/cni/pkg/types"
+       . "github.com/onsi/ginkgo"
+       . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Loading configuration from disk", func() {
+       var (
+               configDir    string
+               pluginConfig []byte
+       )
+
+       BeforeEach(func() {
+               var err error
+               configDir, err = ioutil.TempDir("", "plugin-conf")
+               Expect(err).NotTo(HaveOccurred())
+
+               pluginConfig = []byte(`{ "name": "some-plugin", "some-key": "some-value" }`)
+               Expect(ioutil.WriteFile(filepath.Join(configDir, "50-whatever.conf"), pluginConfig, 0600)).To(Succeed())
+       })
+
+       AfterEach(func() {
+               Expect(os.RemoveAll(configDir)).To(Succeed())
+       })
+
+       Describe("LoadConf", func() {
+               It("finds the network config file for the plugin of the given type", func() {
+                       netConfig, err := libcni.LoadConf(configDir, "some-plugin")
+                       Expect(err).NotTo(HaveOccurred())
+                       Expect(netConfig).To(Equal(&libcni.NetworkConfig{
+                               Network: &types.NetConf{Name: "some-plugin"},
+                               Bytes:   pluginConfig,
+                       }))
+               })
+
+               Context("when the config directory does not exist", func() {
+                       BeforeEach(func() {
+                               Expect(os.RemoveAll(configDir)).To(Succeed())
+                       })
+
+                       It("returns a useful error", func() {
+                               _, err := libcni.LoadConf(configDir, "some-plugin")
+                               Expect(err).To(MatchError("no net configurations found"))
+                       })
+               })
+
+               Context("when there is no config for the desired plugin", func() {
+                       It("returns a useful error", func() {
+                               _, err := libcni.LoadConf(configDir, "some-other-plugin")
+                               Expect(err).To(MatchError(ContainSubstring(`no net configuration with name "some-other-plugin" in`)))
+                       })
+               })
+
+               Context("when a config file is malformed", func() {
+                       BeforeEach(func() {
+                               Expect(ioutil.WriteFile(filepath.Join(configDir, "00-bad.conf"), []byte(`{`), 0600)).To(Succeed())
+                       })
+
+                       It("returns a useful error", func() {
+                               _, err := libcni.LoadConf(configDir, "some-plugin")
+                               Expect(err).To(MatchError(`error parsing configuration: unexpected end of JSON input`))
+                       })
+               })
+
+               Context("when the config is in a nested subdir", func() {
+                       BeforeEach(func() {
+                               subdir := filepath.Join(configDir, "subdir1", "subdir2")
+                               Expect(os.MkdirAll(subdir, 0700)).To(Succeed())
+
+                               pluginConfig = []byte(`{ "name": "deep", "some-key": "some-value" }`)
+                               Expect(ioutil.WriteFile(filepath.Join(subdir, "90-deep.conf"), pluginConfig, 0600)).To(Succeed())
+                       })
+
+                       It("will not find the config", func() {
+                               _, err := libcni.LoadConf(configDir, "deep")
+                               Expect(err).To(MatchError(HavePrefix("no net configuration with name")))
+                       })
+               })
+       })
+
+       Describe("ConfFromFile", func() {
+               Context("when the file cannot be opened", func() {
+                       It("returns a useful error", func() {
+                               _, err := libcni.ConfFromFile("/tmp/nope/not-here")
+                               Expect(err).To(MatchError(HavePrefix(`error reading /tmp/nope/not-here: open /tmp/nope/not-here`)))
+                       })
+               })
+       })
+})
diff --git a/libcni/libcni_suite_test.go b/libcni/libcni_suite_test.go
new file mode 100644 (file)
index 0000000..f78977b
--- /dev/null
@@ -0,0 +1,45 @@
+// 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 libcni_test
+
+import (
+       . "github.com/onsi/ginkgo"
+       . "github.com/onsi/gomega"
+       "github.com/onsi/gomega/gexec"
+
+       "testing"
+)
+
+func TestLibcni(t *testing.T) {
+       RegisterFailHandler(Fail)
+       RunSpecs(t, "Libcni Suite")
+}
+
+const packagePath = "github.com/containernetworking/cni/plugins/test/noop"
+
+var pathToPlugin string
+
+var _ = SynchronizedBeforeSuite(func() []byte {
+       var err error
+       pathToPlugin, err = gexec.Build(packagePath)
+       Expect(err).NotTo(HaveOccurred())
+       return []byte(pathToPlugin)
+}, func(crossNodeData []byte) {
+       pathToPlugin = string(crossNodeData)
+})
+
+var _ = SynchronizedAfterSuite(func() {}, func() {
+       gexec.CleanupBuildArtifacts()
+})
diff --git a/test b/test
index b7755a2..9122080 100755 (executable)
--- a/test
+++ b/test
@@ -11,7 +11,7 @@ set -e
 
 source ./build
 
-TESTABLE="plugins/ipam/dhcp plugins/ipam/host-local plugins/main/loopback pkg/invoke pkg/ns pkg/skel pkg/types pkg/utils plugins/main/ipvlan plugins/main/macvlan plugins/main/bridge plugins/main/ptp plugins/test/noop"
+TESTABLE="libcni plugins/ipam/dhcp plugins/ipam/host-local plugins/main/loopback pkg/invoke pkg/ns pkg/skel pkg/types pkg/utils plugins/main/ipvlan plugins/main/macvlan plugins/main/bridge plugins/main/ptp plugins/test/noop"
 FORMATTABLE="$TESTABLE libcni pkg/ip pkg/ipam pkg/testutils plugins/meta/flannel plugins/meta/tuning"
 
 # user has not provided PKG override