go install "$@" ${REPO_PATH}/cnitool
echo "Building plugins"
-PLUGINS="plugins/meta/* plugins/main/* plugins/ipam/*"
+PLUGINS="plugins/meta/* plugins/main/* plugins/ipam/* plugins/test/*"
for d in $PLUGINS; do
if [ -d $d ]; then
plugin=$(basename $d)
import "errors"
+// BadReader is an io.Reader which always errors
type BadReader struct {
Error error
}
--- /dev/null
+// 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.
+
+// debug supports tests that use the noop plugin
+package debug
+
+import (
+ "encoding/json"
+ "io/ioutil"
+
+ "github.com/containernetworking/cni/pkg/skel"
+)
+
+// Debug is used to control and record the behavior of the noop plugin
+type Debug struct {
+ ReportResult string
+ ReportError string
+ Command string
+ CmdArgs skel.CmdArgs
+}
+
+// ReadDebug will return a debug file recorded by the noop plugin
+func ReadDebug(debugFilePath string) (*Debug, error) {
+ debugBytes, err := ioutil.ReadFile(debugFilePath)
+ if err != nil {
+ return nil, err
+ }
+
+ var debug Debug
+ err = json.Unmarshal(debugBytes, &debug)
+ if err != nil {
+ return nil, err
+ }
+
+ return &debug, nil
+}
+
+// WriteDebug will create a debug file to control the noop plugin
+func (debug *Debug) WriteDebug(debugFilePath string) error {
+ debugBytes, err := json.Marshal(debug)
+ if err != nil {
+ return err
+ }
+
+ err = ioutil.WriteFile(debugFilePath, debugBytes, 0600)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
--- /dev/null
+// 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.
+
+/*
+Noop plugin is a CNI plugin designed for use as a test-double.
+
+When calling, set the CNI_ARGS env var equal to the path of a file containing
+the JSON encoding of a Debug.
+*/
+
+package main
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/containernetworking/cni/pkg/skel"
+ "github.com/containernetworking/cni/plugins/test/noop/debug"
+)
+
+func debugBehavior(args *skel.CmdArgs, command string) error {
+ if !strings.HasPrefix(args.Args, "DEBUG=") {
+ fmt.Printf(`{}`)
+ os.Stderr.WriteString("CNI_ARGS empty, no debug behavior\n")
+ return nil
+ }
+ debugFilePath := strings.TrimPrefix(args.Args, "DEBUG=")
+ debug, err := debug.ReadDebug(debugFilePath)
+ if err != nil {
+ return err
+ }
+
+ debug.CmdArgs = *args
+ debug.Command = command
+
+ err = debug.WriteDebug(debugFilePath)
+ if err != nil {
+ return err
+ }
+
+ if debug.ReportError != "" {
+ return errors.New(debug.ReportError)
+ } else {
+ os.Stdout.WriteString(debug.ReportResult)
+ }
+
+ return nil
+}
+
+func cmdAdd(args *skel.CmdArgs) error {
+ return debugBehavior(args, "ADD")
+}
+
+func cmdDel(args *skel.CmdArgs) error {
+ return debugBehavior(args, "DEL")
+}
+
+func main() {
+ skel.PluginMain(cmdAdd, cmdDel)
+}
--- /dev/null
+// 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_test
+
+import (
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ "github.com/onsi/gomega/gexec"
+
+ "testing"
+)
+
+func TestNoop(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "No-op plugin 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()
+})
--- /dev/null
+// 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_test
+
+import (
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "strings"
+
+ "github.com/containernetworking/cni/pkg/skel"
+ noop_debug "github.com/containernetworking/cni/plugins/test/noop/debug"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ "github.com/onsi/gomega/gexec"
+)
+
+var _ = Describe("No-op plugin", func() {
+ var (
+ cmd *exec.Cmd
+ debugFileName string
+ debug *noop_debug.Debug
+ )
+
+ BeforeEach(func() {
+ debug = &noop_debug.Debug{
+ ReportResult: `{ "ip4": { "ip": "10.1.2.3/24" }, "dns": {} }`,
+ }
+
+ debugFile, err := ioutil.TempFile("", "cni_debug")
+ Expect(err).NotTo(HaveOccurred())
+ Expect(debugFile.Close()).To(Succeed())
+ debugFileName = debugFile.Name()
+
+ Expect(debug.WriteDebug(debugFileName)).To(Succeed())
+
+ cmd = exec.Command(pathToPlugin)
+ cmd.Env = []string{
+ "CNI_COMMAND=ADD",
+ "CNI_CONTAINERID=some-container-id",
+ "CNI_ARGS=DEBUG=" + debugFile.Name(),
+ "CNI_NETNS=/some/netns/path",
+ "CNI_IFNAME=some-eth0",
+ "CNI_PATH=/some/bin/path",
+ }
+ cmd.Stdin = strings.NewReader(`{"some":"stdin-json"}`)
+ })
+
+ AfterEach(func() {
+ os.Remove(debugFileName)
+ })
+
+ It("responds to ADD using the ReportResult debug field", func() {
+ session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
+ Expect(err).NotTo(HaveOccurred())
+ Eventually(session).Should(gexec.Exit(0))
+ Expect(session.Out.Contents()).To(MatchJSON(`{
+ "ip4": { "ip": "10.1.2.3/24" },
+ "dns": {}
+ }`))
+ })
+
+ It("records all the args provided by skel.PluginMain", func() {
+ session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
+ Expect(err).NotTo(HaveOccurred())
+ Eventually(session).Should(gexec.Exit(0))
+
+ debug, err := noop_debug.ReadDebug(debugFileName)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(debug.Command).To(Equal("ADD"))
+ Expect(debug.CmdArgs).To(Equal(skel.CmdArgs{
+ ContainerID: "some-container-id",
+ Netns: "/some/netns/path",
+ IfName: "some-eth0",
+ Args: "DEBUG=" + debugFileName,
+ Path: "/some/bin/path",
+ StdinData: []byte(`{"some":"stdin-json"}`),
+ }))
+ })
+
+ Context("when the ReportError debug field is set", func() {
+ BeforeEach(func() {
+ debug.ReportError = "banana"
+ Expect(debug.WriteDebug(debugFileName)).To(Succeed())
+ })
+
+ It("returns an error to skel.PluginMain, causing the process to exit code 1", func() {
+ session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
+ Expect(err).NotTo(HaveOccurred())
+ Eventually(session).Should(gexec.Exit(1))
+ Expect(session.Out.Contents()).To(MatchJSON(`{ "code": 100, "msg": "banana" }`))
+ })
+ })
+
+ Context("when the CNI_COMMAND is DEL", func() {
+ BeforeEach(func() {
+ cmd.Env[0] = "CNI_COMMAND=DEL"
+ debug.ReportResult = `{ "some": "delete-data" }`
+ Expect(debug.WriteDebug(debugFileName)).To(Succeed())
+ })
+
+ It("still does all the debug behavior", func() {
+ session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
+ Expect(err).NotTo(HaveOccurred())
+ Eventually(session).Should(gexec.Exit(0))
+ Expect(session.Out.Contents()).To(MatchJSON(`{
+ "some": "delete-data"
+ }`))
+ debug, err := noop_debug.ReadDebug(debugFileName)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(debug.Command).To(Equal("DEL"))
+ Expect(debug.CmdArgs).To(Equal(skel.CmdArgs{
+ ContainerID: "some-container-id",
+ Netns: "/some/netns/path",
+ IfName: "some-eth0",
+ Args: "DEBUG=" + debugFileName,
+ Path: "/some/bin/path",
+ StdinData: []byte(`{"some":"stdin-json"}`),
+ }))
+ })
+
+ })
+})
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"
-FORMATTABLE="$TESTABLE libcni pkg/ip pkg/ipam pkg/testutils plugins/ipam/host-local plugins/main/bridge plugins/meta/flannel plugins/meta/tuning"
+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"
+FORMATTABLE="$TESTABLE libcni pkg/ip pkg/ipam pkg/testutils plugins/meta/flannel plugins/meta/tuning"
# user has not provided PKG override
if [ -z "$PKG" ]; then