diff --git a/plugins/test/noop/main.go b/plugins/test/noop/main.go index 2f4dc357..f268890c 100644 --- a/plugins/test/noop/main.go +++ b/plugins/test/noop/main.go @@ -22,19 +22,38 @@ the JSON encoding of a Debug. package main import ( + "encoding/json" "errors" "fmt" + "io/ioutil" "os" "strings" "github.com/containernetworking/cni/pkg/skel" + "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/version" noop_debug "github.com/containernetworking/cni/plugins/test/noop/debug" ) +type NetConf struct { + types.NetConf + DebugFile string `json:"debugFile"` +} + +func loadConf(bytes []byte) (*NetConf, error) { + n := &NetConf{} + if err := json.Unmarshal(bytes, n); err != nil { + return nil, fmt.Errorf("failed to load netconf: %v", err) + } + return n, nil +} + // parse extra args i.e. FOO=BAR;ABC=123 func parseExtraArgs(args string) (map[string]string, error) { m := make(map[string]string) + if len(args) == 0 { + return m, nil + } items := strings.Split(args, ";") for _, item := range items { @@ -47,16 +66,34 @@ func parseExtraArgs(args string) (map[string]string, error) { return m, nil } -func debugBehavior(args *skel.CmdArgs, command string) error { - extraArgs, err := parseExtraArgs(args.Args) +func getDebugFilePath(stdinData []byte, args string) (string, error) { + netConf, err := loadConf(stdinData) if err != nil { - return err + return "", err + } + + extraArgs, err := parseExtraArgs(args) + if err != nil { + return "", err } debugFilePath, ok := extraArgs["DEBUG"] if !ok { + debugFilePath = netConf.DebugFile + } + + return debugFilePath, nil +} + +func debugBehavior(args *skel.CmdArgs, command string) error { + debugFilePath, err := getDebugFilePath(args.StdinData, args.Args) + if err != nil { + return err + } + + if debugFilePath == "" { fmt.Printf(`{}`) - os.Stderr.WriteString("CNI_ARGS empty, no debug behavior\n") + os.Stderr.WriteString("CNI_ARGS or config empty, no debug behavior\n") return nil } @@ -88,21 +125,16 @@ func debugBehavior(args *skel.CmdArgs, command string) error { return nil } -func debugGetSupportedVersions() []string { +func debugGetSupportedVersions(stdinData []byte) []string { vers := []string{"0.-42.0", "0.1.0", "0.2.0"} cniArgs := os.Getenv("CNI_ARGS") if cniArgs == "" { return vers } - extraArgs, err := parseExtraArgs(cniArgs) + debugFilePath, err := getDebugFilePath(stdinData, cniArgs) if err != nil { - panic("test setup error: invalid CNI_ARGS format") - } - - debugFilePath, ok := extraArgs["DEBUG"] - if !ok { - panic("test setup error: missing DEBUG in CNI_ARGS") + panic("test setup error: unable to get debug file path: " + err.Error()) } debug, err := noop_debug.ReadDebug(debugFilePath) @@ -123,7 +155,36 @@ func cmdDel(args *skel.CmdArgs) error { return debugBehavior(args, "DEL") } +func saveStdin() ([]byte, error) { + // Read original stdin + stdinData, err := ioutil.ReadAll(os.Stdin) + if err != nil { + return nil, err + } + + // Make a new pipe for stdin, and write original stdin data to it + r, w, err := os.Pipe() + if err != nil { + return nil, err + } + if _, err := w.Write(stdinData); err != nil { + return nil, err + } + if err := w.Close(); err != nil { + return nil, err + } + + os.Stdin = r + return stdinData, nil +} + func main() { - supportedVersions := debugGetSupportedVersions() + // Grab and read stdin before pkg/skel gets it + stdinData, err := saveStdin() + if err != nil { + panic("test setup error: unable to read stdin: " + err.Error()) + } + + supportedVersions := debugGetSupportedVersions(stdinData) skel.PluginMain(cmdAdd, cmdDel, version.PluginSupports(supportedVersions...)) } diff --git a/plugins/test/noop/noop_test.go b/plugins/test/noop/noop_test.go index c241e631..18f1d0c8 100644 --- a/plugins/test/noop/noop_test.go +++ b/plugins/test/noop/noop_test.go @@ -58,10 +58,11 @@ var _ = Describe("No-op plugin", func() { cmd.Env = []string{ "CNI_COMMAND=ADD", "CNI_CONTAINERID=some-container-id", - "CNI_ARGS=" + args, "CNI_NETNS=/some/netns/path", "CNI_IFNAME=some-eth0", "CNI_PATH=/some/bin/path", + // Keep this last + "CNI_ARGS=" + args, } cmd.Stdin = strings.NewReader(`{"some":"stdin-json", "cniVersion": "0.2.0"}`) expectedCmdArgs = skel.CmdArgs{ @@ -85,6 +86,36 @@ var _ = Describe("No-op plugin", func() { Expect(session.Out.Contents()).To(MatchJSON(reportResult)) }) + It("panics when no debug file is given", func() { + // Remove the DEBUG option from CNI_ARGS and regular args + cmd.Env[len(cmd.Env)-1] = "CNI_ARGS=FOO=BAR" + expectedCmdArgs.Args = "FOO=BAR" + + session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter) + Expect(err).NotTo(HaveOccurred()) + Eventually(session).Should(gexec.Exit(2)) + }) + + It("allows passing debug file in config JSON", func() { + // Remove the DEBUG option from CNI_ARGS and regular args + newArgs := "FOO=BAR" + cmd.Env[len(cmd.Env)-1] = "CNI_ARGS=" + newArgs + newStdin := fmt.Sprintf(`{"some":"stdin-json", "cniVersion": "0.2.0", "debugFile": "%s"}`, debugFileName) + cmd.Stdin = strings.NewReader(newStdin) + expectedCmdArgs.Args = newArgs + expectedCmdArgs.StdinData = []byte(newStdin) + + session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter) + Expect(err).NotTo(HaveOccurred()) + Eventually(session).Should(gexec.Exit(0)) + Expect(session.Out.Contents()).To(MatchJSON(reportResult)) + + debug, err := noop_debug.ReadDebug(debugFileName) + Expect(err).NotTo(HaveOccurred()) + Expect(debug.Command).To(Equal("ADD")) + Expect(debug.CmdArgs).To(Equal(expectedCmdArgs)) + }) + It("records all the args provided by skel.PluginMain", func() { session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter) Expect(err).NotTo(HaveOccurred())