pkg/invoke: refactor plugin exec and backfill unit tests
authorGabe Rosenhouse <rosenhouse@gmail.com>
Fri, 2 Sep 2016 17:12:14 +0000 (13:12 -0400)
committerGabe Rosenhouse <rosenhouse@gmail.com>
Fri, 2 Sep 2016 19:59:25 +0000 (15:59 -0400)
pkg/invoke/exec.go
pkg/invoke/exec_test.go
pkg/invoke/fakes/cni_args.go [new file with mode: 0644]
pkg/invoke/fakes/raw_exec.go [new file with mode: 0644]
pkg/invoke/fakes/version_decoder.go [new file with mode: 0644]
pkg/invoke/raw_exec.go [new file with mode: 0644]
pkg/invoke/raw_exec_test.go [new file with mode: 0644]
pkg/version/version.go
pkg/version/version_test.go

index 3d32ab7..f95dec1 100644 (file)
 package invoke
 
 import (
-       "bytes"
        "encoding/json"
-       "fmt"
-       "io"
        "os"
-       "os/exec"
 
        "github.com/containernetworking/cni/pkg/types"
        "github.com/containernetworking/cni/pkg/version"
 )
 
-func pluginErr(err error, output []byte) error {
-       if _, ok := err.(*exec.ExitError); ok {
-               emsg := types.Error{}
-               if perr := json.Unmarshal(output, &emsg); perr != nil {
-                       return fmt.Errorf("netplugin failed but error parsing its diagnostic message %q: %v", string(output), perr)
-               }
-               details := ""
-               if emsg.Details != "" {
-                       details = fmt.Sprintf("; %v", emsg.Details)
-               }
-               return fmt.Errorf("%v%v", emsg.Msg, details)
-       }
+func ExecPluginWithResult(pluginPath string, netconf []byte, args CNIArgs) (*types.Result, error) {
+       return defaultPluginExec.WithResult(pluginPath, netconf, args)
+}
 
-       return err
+func ExecPluginWithoutResult(pluginPath string, netconf []byte, args CNIArgs) error {
+       return defaultPluginExec.WithoutResult(pluginPath, netconf, args)
 }
 
-func ExecPluginWithResult(pluginPath string, netconf []byte, args CNIArgs) (*types.Result, error) {
-       stdoutBytes, err := execPlugin(pluginPath, netconf, args)
+func ExecPluginForVersion(pluginPath string) (version.PluginInfo, error) {
+       return defaultPluginExec.GetVersion(pluginPath)
+}
+
+var defaultPluginExec = &PluginExec{
+       RawExec:        &RawExec{Stderr: os.Stderr},
+       VersionDecoder: &version.Decoder{},
+}
+
+type PluginExec struct {
+       RawExec interface {
+               ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error)
+       }
+       VersionDecoder interface {
+               Decode(jsonBytes []byte) (version.PluginInfo, error)
+       }
+}
+
+func (e *PluginExec) WithResult(pluginPath string, netconf []byte, args CNIArgs) (*types.Result, error) {
+       stdoutBytes, err := e.RawExec.ExecPlugin(pluginPath, netconf, args.AsEnv())
        if err != nil {
                return nil, err
        }
@@ -53,44 +59,17 @@ func ExecPluginWithResult(pluginPath string, netconf []byte, args CNIArgs) (*typ
        return res, err
 }
 
-func ExecPluginWithoutResult(pluginPath string, netconf []byte, args CNIArgs) error {
-       _, err := execPlugin(pluginPath, netconf, args)
+func (e *PluginExec) WithoutResult(pluginPath string, netconf []byte, args CNIArgs) error {
+       _, err := e.RawExec.ExecPlugin(pluginPath, netconf, args.AsEnv())
        return err
 }
 
-func ExecPluginForVersion(pluginPath string) (version.PluginInfo, error) {
-       stdoutBytes, err := execPlugin(pluginPath, nil, &Args{Command: "VERSION"})
+func (e *PluginExec) GetVersion(pluginPath string) (version.PluginInfo, error) {
+       args := &Args{Command: "VERSION"}
+       stdoutBytes, err := e.RawExec.ExecPlugin(pluginPath, nil, args.AsEnv())
        if err != nil {
                return nil, err
        }
 
-       return version.Decode(stdoutBytes)
-}
-
-func execPlugin(pluginPath string, netconf []byte, args CNIArgs) ([]byte, error) {
-       return defaultRawExec.ExecPlugin(pluginPath, netconf, args.AsEnv())
-}
-
-var defaultRawExec = &RawExec{Stderr: os.Stderr}
-
-type RawExec struct {
-       Stderr io.Writer
-}
-
-func (e *RawExec) ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error) {
-       stdout := &bytes.Buffer{}
-
-       c := exec.Cmd{
-               Env:    environ,
-               Path:   pluginPath,
-               Args:   []string{pluginPath},
-               Stdin:  bytes.NewBuffer(stdinData),
-               Stdout: stdout,
-               Stderr: e.Stderr,
-       }
-       if err := c.Run(); err != nil {
-               return nil, pluginErr(err, stdout.Bytes())
-       }
-
-       return stdout.Bytes(), nil
+       return e.VersionDecoder.Decode(stdoutBytes)
 }
index 7df60a1..bff3fb7 100644 (file)
 package invoke_test
 
 import (
-       "bytes"
-       "io/ioutil"
-       "os"
+       "errors"
 
        "github.com/containernetworking/cni/pkg/invoke"
-
-       noop_debug "github.com/containernetworking/cni/plugins/test/noop/debug"
+       "github.com/containernetworking/cni/pkg/invoke/fakes"
+       "github.com/containernetworking/cni/pkg/version"
 
        . "github.com/onsi/ginkgo"
        . "github.com/onsi/gomega"
 )
 
-var _ = Describe("RawExec", func() {
+var _ = Describe("Executing a plugin", func() {
        var (
-               debugFileName string
-               debug         *noop_debug.Debug
-               environ       []string
-               stdin         []byte
-               execer        *invoke.RawExec
-       )
+               pluginExec     *invoke.PluginExec
+               rawExec        *fakes.RawExec
+               versionDecoder *fakes.VersionDecoder
 
-       const reportResult = `{ "some": "result" }`
+               pluginPath string
+               netconf    []byte
+               cniargs    *fakes.CNIArgs
+       )
 
        BeforeEach(func() {
-               debugFile, err := ioutil.TempFile("", "cni_debug")
-               Expect(err).NotTo(HaveOccurred())
-               Expect(debugFile.Close()).To(Succeed())
-               debugFileName = debugFile.Name()
-
-               debug = &noop_debug.Debug{
-                       ReportResult: reportResult,
-                       ReportStderr: "some stderr message",
-               }
-               Expect(debug.WriteDebug(debugFileName)).To(Succeed())
-
-               environ = []string{
-                       "CNI_COMMAND=ADD",
-                       "CNI_CONTAINERID=some-container-id",
-                       "CNI_ARGS=DEBUG=" + debugFileName,
-                       "CNI_NETNS=/some/netns/path",
-                       "CNI_PATH=/some/bin/path",
-                       "CNI_IFNAME=some-eth0",
-               }
-               stdin = []byte(`{"some":"stdin-json"}`)
-               execer = &invoke.RawExec{}
-       })
+               rawExec = &fakes.RawExec{}
+               rawExec.ExecPluginCall.Returns.ResultBytes = []byte(`{ "ip4": { "ip": "1.2.3.4/24" } }`)
 
-       AfterEach(func() {
-               Expect(os.Remove(debugFileName)).To(Succeed())
-       })
-
-       It("runs the plugin with the given stdin and environment", func() {
-               _, err := execer.ExecPlugin(pathToPlugin, stdin, environ)
-               Expect(err).NotTo(HaveOccurred())
+               versionDecoder = &fakes.VersionDecoder{}
+               versionDecoder.DecodeCall.Returns.PluginInfo = version.PluginSupports("0.42.0")
 
-               debug, err := noop_debug.ReadDebug(debugFileName)
-               Expect(err).NotTo(HaveOccurred())
-               Expect(debug.Command).To(Equal("ADD"))
-               Expect(debug.CmdArgs.StdinData).To(Equal(stdin))
-               Expect(debug.CmdArgs.Netns).To(Equal("/some/netns/path"))
+               pluginExec = &invoke.PluginExec{
+                       RawExec:        rawExec,
+                       VersionDecoder: versionDecoder,
+               }
+               pluginPath = "/some/plugin/path"
+               netconf = []byte(`{ "some": "stdin" }`)
+               cniargs = &fakes.CNIArgs{}
+               cniargs.AsEnvCall.Returns.Env = []string{"SOME=ENV"}
        })
 
-       It("returns the resulting stdout as bytes", func() {
-               resultBytes, err := execer.ExecPlugin(pathToPlugin, stdin, environ)
-               Expect(err).NotTo(HaveOccurred())
-
-               Expect(resultBytes).To(BeEquivalentTo(reportResult))
-       })
+       Describe("returning a result", func() {
+               It("unmarshals the result bytes into the Result type", func() {
+                       result, err := pluginExec.WithResult(pluginPath, netconf, cniargs)
+                       Expect(err).NotTo(HaveOccurred())
+                       Expect(result.IP4.IP.IP.String()).To(Equal("1.2.3.4"))
+               })
 
-       Context("when the Stderr writer is set", func() {
-               var stderrBuffer *bytes.Buffer
+               It("passes its arguments through to the rawExec", func() {
+                       pluginExec.WithResult(pluginPath, netconf, cniargs)
+                       Expect(rawExec.ExecPluginCall.Received.PluginPath).To(Equal(pluginPath))
+                       Expect(rawExec.ExecPluginCall.Received.StdinData).To(Equal(netconf))
+                       Expect(rawExec.ExecPluginCall.Received.Environ).To(Equal([]string{"SOME=ENV"}))
+               })
 
-               BeforeEach(func() {
-                       stderrBuffer = &bytes.Buffer{}
-                       execer.Stderr = stderrBuffer
+               Context("when the rawExec fails", func() {
+                       BeforeEach(func() {
+                               rawExec.ExecPluginCall.Returns.Error = errors.New("banana")
+                       })
+                       It("returns the error", func() {
+                               _, err := pluginExec.WithResult(pluginPath, netconf, cniargs)
+                               Expect(err).To(MatchError("banana"))
+                       })
                })
+       })
 
-               It("forwards any stderr bytes to the Stderr writer", func() {
-                       _, err := execer.ExecPlugin(pathToPlugin, stdin, environ)
-                       Expect(err).NotTo(HaveOccurred())
+       Describe("without returning a result", func() {
+               It("passes its arguments through to the rawExec", func() {
+                       pluginExec.WithoutResult(pluginPath, netconf, cniargs)
+                       Expect(rawExec.ExecPluginCall.Received.PluginPath).To(Equal(pluginPath))
+                       Expect(rawExec.ExecPluginCall.Received.StdinData).To(Equal(netconf))
+                       Expect(rawExec.ExecPluginCall.Received.Environ).To(Equal([]string{"SOME=ENV"}))
+               })
 
-                       Expect(stderrBuffer.String()).To(Equal("some stderr message"))
+               Context("when the rawExec fails", func() {
+                       BeforeEach(func() {
+                               rawExec.ExecPluginCall.Returns.Error = errors.New("banana")
+                       })
+                       It("returns the error", func() {
+                               err := pluginExec.WithoutResult(pluginPath, netconf, cniargs)
+                               Expect(err).To(MatchError("banana"))
+                       })
                })
        })
 
-       Context("when the plugin errors", func() {
+       Describe("discovering the plugin version", func() {
                BeforeEach(func() {
-                       debug.ReportError = "banana"
-                       Expect(debug.WriteDebug(debugFileName)).To(Succeed())
+                       rawExec.ExecPluginCall.Returns.ResultBytes = []byte(`{ "some": "version-info" }`)
                })
 
-               It("wraps and returns the error", func() {
-                       _, err := execer.ExecPlugin(pathToPlugin, stdin, environ)
-                       Expect(err).To(HaveOccurred())
-                       Expect(err).To(MatchError("banana"))
+               It("execs the plugin with the command VERSION", func() {
+                       pluginExec.GetVersion(pluginPath)
+                       Expect(rawExec.ExecPluginCall.Received.PluginPath).To(Equal(pluginPath))
+                       Expect(rawExec.ExecPluginCall.Received.StdinData).To(BeNil())
+                       Expect(rawExec.ExecPluginCall.Received.Environ).To(ContainElement("CNI_COMMAND=VERSION"))
+               })
+
+               It("decodes and returns the version info", func() {
+                       versionInfo, err := pluginExec.GetVersion(pluginPath)
+                       Expect(err).NotTo(HaveOccurred())
+                       Expect(versionInfo.SupportedVersions()).To(Equal([]string{"0.42.0"}))
+                       Expect(versionDecoder.DecodeCall.Received.JSONBytes).To(MatchJSON(`{ "some": "version-info" }`))
                })
-       })
 
-       Context("when the system is unable to execute the plugin", func() {
-               It("returns the error", func() {
-                       _, err := execer.ExecPlugin("/tmp/some/invalid/plugin/path", stdin, environ)
-                       Expect(err).To(HaveOccurred())
-                       Expect(err).To(MatchError(ContainSubstring("/tmp/some/invalid/plugin/path")))
+               Context("when the rawExec fails", func() {
+                       BeforeEach(func() {
+                               rawExec.ExecPluginCall.Returns.Error = errors.New("banana")
+                       })
+                       It("returns the error", func() {
+                               _, err := pluginExec.GetVersion(pluginPath)
+                               Expect(err).To(MatchError("banana"))
+                       })
                })
        })
 })
diff --git a/pkg/invoke/fakes/cni_args.go b/pkg/invoke/fakes/cni_args.go
new file mode 100644 (file)
index 0000000..5b1ba29
--- /dev/null
@@ -0,0 +1,27 @@
+// 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 fakes
+
+type CNIArgs struct {
+       AsEnvCall struct {
+               Returns struct {
+                       Env []string
+               }
+       }
+}
+
+func (a *CNIArgs) AsEnv() []string {
+       return a.AsEnvCall.Returns.Env
+}
diff --git a/pkg/invoke/fakes/raw_exec.go b/pkg/invoke/fakes/raw_exec.go
new file mode 100644 (file)
index 0000000..5432cdf
--- /dev/null
@@ -0,0 +1,36 @@
+// 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 fakes
+
+type RawExec struct {
+       ExecPluginCall struct {
+               Received struct {
+                       PluginPath string
+                       StdinData  []byte
+                       Environ    []string
+               }
+               Returns struct {
+                       ResultBytes []byte
+                       Error       error
+               }
+       }
+}
+
+func (e *RawExec) ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error) {
+       e.ExecPluginCall.Received.PluginPath = pluginPath
+       e.ExecPluginCall.Received.StdinData = stdinData
+       e.ExecPluginCall.Received.Environ = environ
+       return e.ExecPluginCall.Returns.ResultBytes, e.ExecPluginCall.Returns.Error
+}
diff --git a/pkg/invoke/fakes/version_decoder.go b/pkg/invoke/fakes/version_decoder.go
new file mode 100644 (file)
index 0000000..72d2973
--- /dev/null
@@ -0,0 +1,34 @@
+// 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 fakes
+
+import "github.com/containernetworking/cni/pkg/version"
+
+type VersionDecoder struct {
+       DecodeCall struct {
+               Received struct {
+                       JSONBytes []byte
+               }
+               Returns struct {
+                       PluginInfo version.PluginInfo
+                       Error      error
+               }
+       }
+}
+
+func (e *VersionDecoder) Decode(jsonData []byte) (version.PluginInfo, error) {
+       e.DecodeCall.Received.JSONBytes = jsonData
+       return e.DecodeCall.Returns.PluginInfo, e.DecodeCall.Returns.Error
+}
diff --git a/pkg/invoke/raw_exec.go b/pkg/invoke/raw_exec.go
new file mode 100644 (file)
index 0000000..d1bd860
--- /dev/null
@@ -0,0 +1,63 @@
+// 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 invoke
+
+import (
+       "bytes"
+       "encoding/json"
+       "fmt"
+       "io"
+       "os/exec"
+
+       "github.com/containernetworking/cni/pkg/types"
+)
+
+type RawExec struct {
+       Stderr io.Writer
+}
+
+func (e *RawExec) ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error) {
+       stdout := &bytes.Buffer{}
+
+       c := exec.Cmd{
+               Env:    environ,
+               Path:   pluginPath,
+               Args:   []string{pluginPath},
+               Stdin:  bytes.NewBuffer(stdinData),
+               Stdout: stdout,
+               Stderr: e.Stderr,
+       }
+       if err := c.Run(); err != nil {
+               return nil, pluginErr(err, stdout.Bytes())
+       }
+
+       return stdout.Bytes(), nil
+}
+
+func pluginErr(err error, output []byte) error {
+       if _, ok := err.(*exec.ExitError); ok {
+               emsg := types.Error{}
+               if perr := json.Unmarshal(output, &emsg); perr != nil {
+                       return fmt.Errorf("netplugin failed but error parsing its diagnostic message %q: %v", string(output), perr)
+               }
+               details := ""
+               if emsg.Details != "" {
+                       details = fmt.Sprintf("; %v", emsg.Details)
+               }
+               return fmt.Errorf("%v%v", emsg.Msg, details)
+       }
+
+       return err
+}
diff --git a/pkg/invoke/raw_exec_test.go b/pkg/invoke/raw_exec_test.go
new file mode 100644 (file)
index 0000000..7df60a1
--- /dev/null
@@ -0,0 +1,123 @@
+// 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 invoke_test
+
+import (
+       "bytes"
+       "io/ioutil"
+       "os"
+
+       "github.com/containernetworking/cni/pkg/invoke"
+
+       noop_debug "github.com/containernetworking/cni/plugins/test/noop/debug"
+
+       . "github.com/onsi/ginkgo"
+       . "github.com/onsi/gomega"
+)
+
+var _ = Describe("RawExec", func() {
+       var (
+               debugFileName string
+               debug         *noop_debug.Debug
+               environ       []string
+               stdin         []byte
+               execer        *invoke.RawExec
+       )
+
+       const reportResult = `{ "some": "result" }`
+
+       BeforeEach(func() {
+               debugFile, err := ioutil.TempFile("", "cni_debug")
+               Expect(err).NotTo(HaveOccurred())
+               Expect(debugFile.Close()).To(Succeed())
+               debugFileName = debugFile.Name()
+
+               debug = &noop_debug.Debug{
+                       ReportResult: reportResult,
+                       ReportStderr: "some stderr message",
+               }
+               Expect(debug.WriteDebug(debugFileName)).To(Succeed())
+
+               environ = []string{
+                       "CNI_COMMAND=ADD",
+                       "CNI_CONTAINERID=some-container-id",
+                       "CNI_ARGS=DEBUG=" + debugFileName,
+                       "CNI_NETNS=/some/netns/path",
+                       "CNI_PATH=/some/bin/path",
+                       "CNI_IFNAME=some-eth0",
+               }
+               stdin = []byte(`{"some":"stdin-json"}`)
+               execer = &invoke.RawExec{}
+       })
+
+       AfterEach(func() {
+               Expect(os.Remove(debugFileName)).To(Succeed())
+       })
+
+       It("runs the plugin with the given stdin and environment", func() {
+               _, err := execer.ExecPlugin(pathToPlugin, stdin, environ)
+               Expect(err).NotTo(HaveOccurred())
+
+               debug, err := noop_debug.ReadDebug(debugFileName)
+               Expect(err).NotTo(HaveOccurred())
+               Expect(debug.Command).To(Equal("ADD"))
+               Expect(debug.CmdArgs.StdinData).To(Equal(stdin))
+               Expect(debug.CmdArgs.Netns).To(Equal("/some/netns/path"))
+       })
+
+       It("returns the resulting stdout as bytes", func() {
+               resultBytes, err := execer.ExecPlugin(pathToPlugin, stdin, environ)
+               Expect(err).NotTo(HaveOccurred())
+
+               Expect(resultBytes).To(BeEquivalentTo(reportResult))
+       })
+
+       Context("when the Stderr writer is set", func() {
+               var stderrBuffer *bytes.Buffer
+
+               BeforeEach(func() {
+                       stderrBuffer = &bytes.Buffer{}
+                       execer.Stderr = stderrBuffer
+               })
+
+               It("forwards any stderr bytes to the Stderr writer", func() {
+                       _, err := execer.ExecPlugin(pathToPlugin, stdin, environ)
+                       Expect(err).NotTo(HaveOccurred())
+
+                       Expect(stderrBuffer.String()).To(Equal("some stderr message"))
+               })
+       })
+
+       Context("when the plugin errors", func() {
+               BeforeEach(func() {
+                       debug.ReportError = "banana"
+                       Expect(debug.WriteDebug(debugFileName)).To(Succeed())
+               })
+
+               It("wraps and returns the error", func() {
+                       _, err := execer.ExecPlugin(pathToPlugin, stdin, environ)
+                       Expect(err).To(HaveOccurred())
+                       Expect(err).To(MatchError("banana"))
+               })
+       })
+
+       Context("when the system is unable to execute the plugin", func() {
+               It("returns the error", func() {
+                       _, err := execer.ExecPlugin("/tmp/some/invalid/plugin/path", stdin, environ)
+                       Expect(err).To(HaveOccurred())
+                       Expect(err).To(MatchError(ContainSubstring("/tmp/some/invalid/plugin/path")))
+               })
+       })
+})
index 62bf8bb..cdb531c 100644 (file)
@@ -61,7 +61,9 @@ func PluginSupports(supportedVersions ...string) PluginInfo {
        }
 }
 
-func Decode(jsonBytes []byte) (PluginInfo, error) {
+type Decoder struct{}
+
+func (_ *Decoder) Decode(jsonBytes []byte) (PluginInfo, error) {
        var info simple
        err := json.Unmarshal(jsonBytes, &info)
        if err != nil {
index 08b89ae..98a386d 100644 (file)
@@ -21,8 +21,14 @@ import (
 )
 
 var _ = Describe("Decode", func() {
+       var decoder *version.Decoder
+
+       BeforeEach(func() {
+               decoder = &version.Decoder{}
+       })
+
        It("returns a PluginInfo that represents the given json bytes", func() {
-               pluginInfo, err := version.Decode([]byte(`{
+               pluginInfo, err := decoder.Decode([]byte(`{
                        "cniVersion": "some-library-version",
                        "supportedVersions": [ "some-version", "some-other-version" ]
                }`))
@@ -36,14 +42,14 @@ var _ = Describe("Decode", func() {
 
        Context("when the bytes cannot be decoded as json", func() {
                It("returns a meaningful error", func() {
-                       _, err := version.Decode([]byte(`{{{`))
+                       _, err := decoder.Decode([]byte(`{{{`))
                        Expect(err).To(MatchError("decoding version info: invalid character '{' looking for beginning of object key string"))
                })
        })
 
        Context("when the json bytes are missing the required CNIVersion field", func() {
                It("returns a meaningful error", func() {
-                       _, err := version.Decode([]byte(`{ "supportedVersions": [ "foo" ] }`))
+                       _, err := decoder.Decode([]byte(`{ "supportedVersions": [ "foo" ] }`))
                        Expect(err).To(MatchError("decoding version info: missing field cniVersion"))
                })
        })
@@ -51,7 +57,7 @@ var _ = Describe("Decode", func() {
        Context("when there are no supported versions", func() {
                Context("when the cniVersion is 0.2.0", func() {
                        It("infers the supported versions are 0.1.0 and 0.2.0", func() {
-                               pluginInfo, err := version.Decode([]byte(`{ "cniVersion": "0.2.0" }`))
+                               pluginInfo, err := decoder.Decode([]byte(`{ "cniVersion": "0.2.0" }`))
                                Expect(err).NotTo(HaveOccurred())
                                Expect(pluginInfo).NotTo(BeNil())
                                Expect(pluginInfo.SupportedVersions()).To(Equal([]string{
@@ -63,7 +69,7 @@ var _ = Describe("Decode", func() {
 
                Context("when the cniVersion is >= 0.3.0", func() {
                        It("returns a meaningful error", func() {
-                               _, err := version.Decode([]byte(`{ "cniVersion": "0.3.0" }`))
+                               _, err := decoder.Decode([]byte(`{ "cniVersion": "0.3.0" }`))
                                Expect(err).To(MatchError("decoding version info: missing field supportedVersions"))
                        })
                })