versioning: misc cleanups
authorGabe Rosenhouse <rosenhouse@gmail.com>
Tue, 6 Sep 2016 15:22:27 +0000 (11:22 -0400)
committerGabe Rosenhouse <rosenhouse@gmail.com>
Tue, 6 Sep 2016 19:35:58 +0000 (15:35 -0400)
highlights:
 - NetConf struct finally includes cniVersion field
 - improve test coverage of current version report behavior
 - godoc a few key functions
 - allow tests to control version list reported by no-op plugin

libcni/api.go
pkg/invoke/exec.go
pkg/invoke/get_version_integration_test.go [moved from pkg/invoke/exec_integration_test.go with 78% similarity]
pkg/types/types.go
pkg/version/plugin.go [new file with mode: 0644]
pkg/version/plugin_test.go [moved from pkg/version/version_test.go with 94% similarity]
pkg/version/version.go
plugins/test/noop/debug/debug.go
plugins/test/noop/main.go
plugins/test/noop/noop_test.go

index 273123d..8ba78b2 100644 (file)
@@ -43,6 +43,7 @@ type CNIConfig struct {
        Path []string
 }
 
+// AddNetwork executes the plugin with the ADD command
 func (c *CNIConfig) AddNetwork(net *NetworkConfig, rt *RuntimeConf) (*types.Result, error) {
        pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
        if err != nil {
@@ -52,6 +53,7 @@ func (c *CNIConfig) AddNetwork(net *NetworkConfig, rt *RuntimeConf) (*types.Resu
        return invoke.ExecPluginWithResult(pluginPath, net.Bytes, c.args("ADD", rt))
 }
 
+// DelNetwork executes the plugin with the DEL command
 func (c *CNIConfig) DelNetwork(net *NetworkConfig, rt *RuntimeConf) error {
        pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
        if err != nil {
@@ -61,6 +63,8 @@ func (c *CNIConfig) DelNetwork(net *NetworkConfig, rt *RuntimeConf) error {
        return invoke.ExecPluginWithoutResult(pluginPath, net.Bytes, c.args("DEL", rt))
 }
 
+// GetVersionInfo reports which versions of the CNI spec are supported by
+// the given plugin.
 func (c *CNIConfig) GetVersionInfo(pluginType string) (version.PluginInfo, error) {
        pluginPath, err := invoke.FindInPath(pluginType, c.Path)
        if err != nil {
index 68efbb9..7eb0615 100644 (file)
@@ -36,7 +36,7 @@ func GetVersionInfo(pluginPath string) (version.PluginInfo, error) {
 
 var defaultPluginExec = &PluginExec{
        RawExec:        &RawExec{Stderr: os.Stderr},
-       VersionDecoder: &version.Decoder{},
+       VersionDecoder: &version.PluginDecoder{},
 }
 
 type PluginExec struct {
@@ -64,6 +64,10 @@ func (e *PluginExec) WithoutResult(pluginPath string, netconf []byte, args CNIAr
        return err
 }
 
+// GetVersionInfo returns the version information available about the plugin.
+// For recent-enough plugins, it uses the information returned by the VERSION
+// command.  For older plugins which do not recognize that command, it reports
+// version 0.1.0
 func (e *PluginExec) GetVersionInfo(pluginPath string) (version.PluginInfo, error) {
        args := &Args{
                Command: "VERSION",
similarity index 78%
rename from pkg/invoke/exec_integration_test.go
rename to pkg/invoke/get_version_integration_test.go
index f02374c..d10826d 100644 (file)
@@ -54,9 +54,26 @@ var _ = Describe("GetVersion, integration tests", func() {
                },
                Entry("old plugin, before VERSION was introduced", git_ref_v010, plugin_source_v010, version.PluginSupports("0.1.0")),
                Entry("when VERSION was introduced", git_ref_v020, plugin_source_v010, version.PluginSupports("0.1.0", "0.2.0")),
+               Entry("when plugins report their own version support", git_ref_v030, plugin_source_v030, version.PluginSupports("0.3.0", "0.999.0")),
+               Entry("HEAD", "HEAD", plugin_source_v030, version.PluginSupports("0.3.0", "0.999.0")),
        )
 })
 
+// a 0.3.0 plugin that can report its own versions
+const plugin_source_v030 = `package main
+
+import (
+       "github.com/containernetworking/cni/pkg/skel"
+       "github.com/containernetworking/cni/pkg/version"
+       "fmt"
+)
+
+func c(_ *skel.CmdArgs) error { fmt.Println("{}"); return nil }
+
+func main() { skel.PluginMain(c, c, version.PluginSupports("0.3.0", "0.999.0")) }
+`
+const git_ref_v030 = "bf31ed15"
+
 // a minimal 0.1.0 / 0.2.0 plugin
 const plugin_source_v010 = `package main
 
index 6948dcb..ba02580 100644 (file)
@@ -57,6 +57,8 @@ func (n *IPNet) UnmarshalJSON(data []byte) error {
 
 // NetConf describes a network.
 type NetConf struct {
+       CNIVersion string `json:"cniVersion,omitempty"`
+
        Name string `json:"name,omitempty"`
        Type string `json:"type,omitempty"`
        IPAM struct {
diff --git a/pkg/version/plugin.go b/pkg/version/plugin.go
new file mode 100644 (file)
index 0000000..9bd7dc8
--- /dev/null
@@ -0,0 +1,77 @@
+// 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 version
+
+import (
+       "encoding/json"
+       "fmt"
+       "io"
+)
+
+// PluginInfo reports information about CNI versioning
+type PluginInfo interface {
+       // SupportedVersions returns one or more CNI spec versions that the plugin
+       // supports.  If input is provided in one of these versions, then the plugin
+       // promises to use the same CNI version in its response
+       SupportedVersions() []string
+
+       // Encode writes this CNI version information as JSON to the given Writer
+       Encode(io.Writer) error
+}
+
+type pluginInfo struct {
+       CNIVersion_        string   `json:"cniVersion"`
+       SupportedVersions_ []string `json:"supportedVersions,omitempty"`
+}
+
+func (p *pluginInfo) Encode(w io.Writer) error {
+       return json.NewEncoder(w).Encode(p)
+}
+
+func (p *pluginInfo) SupportedVersions() []string {
+       return p.SupportedVersions_
+}
+
+// PluginSupports returns a new PluginInfo that will report the given versions
+// as supported
+func PluginSupports(supportedVersions ...string) PluginInfo {
+       if len(supportedVersions) < 1 {
+               panic("programmer error: you must support at least one version")
+       }
+       return &pluginInfo{
+               CNIVersion_:        Current(),
+               SupportedVersions_: supportedVersions,
+       }
+}
+
+type PluginDecoder struct{}
+
+func (_ *PluginDecoder) Decode(jsonBytes []byte) (PluginInfo, error) {
+       var info pluginInfo
+       err := json.Unmarshal(jsonBytes, &info)
+       if err != nil {
+               return nil, fmt.Errorf("decoding version info: %s", err)
+       }
+       if info.CNIVersion_ == "" {
+               return nil, fmt.Errorf("decoding version info: missing field cniVersion")
+       }
+       if len(info.SupportedVersions_) == 0 {
+               if info.CNIVersion_ == "0.2.0" {
+                       return PluginSupports("0.1.0", "0.2.0"), nil
+               }
+               return nil, fmt.Errorf("decoding version info: missing field supportedVersions")
+       }
+       return &info, nil
+}
similarity index 94%
rename from pkg/version/version_test.go
rename to pkg/version/plugin_test.go
index 98a386d..a58bd35 100644 (file)
@@ -20,11 +20,11 @@ import (
        . "github.com/onsi/gomega"
 )
 
-var _ = Describe("Decode", func() {
-       var decoder *version.Decoder
+var _ = Describe("Decoding versions reported by a plugin", func() {
+       var decoder *version.PluginDecoder
 
        BeforeEach(func() {
-               decoder = &version.Decoder{}
+               decoder = &version.PluginDecoder{}
        })
 
        It("returns a PluginInfo that represents the given json bytes", func() {
index cdb531c..5f937f7 100644 (file)
 
 package version
 
-import (
-       "encoding/json"
-       "fmt"
-       "io"
-)
-
 // Current reports the version of the CNI spec implemented by this library
 func Current() string {
        return "0.3.0"
 }
 
-// PluginInfo reports information about CNI versioning
-type PluginInfo interface {
-       // SupportedVersions returns one or more CNI spec versions that the plugin
-       // supports.  If input is provided in one of these versions, then the plugin
-       // promises to use the same CNI version in its response
-       SupportedVersions() []string
-
-       // Encode writes this CNI version information as JSON to the given Writer
-       Encode(io.Writer) error
-}
-
-type simple struct {
-       CNIVersion_        string   `json:"cniVersion"`
-       SupportedVersions_ []string `json:"supportedVersions,omitempty"`
-}
-
-func (p *simple) Encode(w io.Writer) error {
-       return json.NewEncoder(w).Encode(p)
-}
-
-func (p *simple) SupportedVersions() []string {
-       return p.SupportedVersions_
-}
-
-// PluginSupports returns a new PluginInfo that will report the given versions
-// as supported
-func PluginSupports(supportedVersions ...string) PluginInfo {
-       if len(supportedVersions) < 1 {
-               panic("programmer error: you must support at least one version")
-       }
-       return &simple{
-               CNIVersion_:        Current(),
-               SupportedVersions_: supportedVersions,
-       }
-}
-
-type Decoder struct{}
-
-func (_ *Decoder) Decode(jsonBytes []byte) (PluginInfo, error) {
-       var info simple
-       err := json.Unmarshal(jsonBytes, &info)
-       if err != nil {
-               return nil, fmt.Errorf("decoding version info: %s", err)
-       }
-       if info.CNIVersion_ == "" {
-               return nil, fmt.Errorf("decoding version info: missing field cniVersion")
-       }
-       if len(info.SupportedVersions_) == 0 {
-               if info.CNIVersion_ == "0.2.0" {
-                       return PluginSupports("0.1.0", "0.2.0"), nil
-               }
-               return nil, fmt.Errorf("decoding version info: missing field supportedVersions")
-       }
-       return &info, nil
-}
-
 // Legacy PluginInfo describes a plugin that is backwards compatible with the
 // CNI spec version 0.1.0.  In particular, a runtime compiled against the 0.1.0
 // library ought to work correctly with a plugin that reports support for
index 71770bd..5bc6e4f 100644 (file)
@@ -24,11 +24,17 @@ import (
 
 // Debug is used to control and record the behavior of the noop plugin
 type Debug struct {
-       ReportResult string
-       ReportError  string
-       ReportStderr string
-       Command      string
-       CmdArgs      skel.CmdArgs
+       // Report* fields allow the test to control the behavior of the no-op plugin
+       ReportResult         string
+       ReportError          string
+       ReportStderr         string
+       ReportVersionSupport []string
+
+       // Command stores the CNI command that the plugin received
+       Command string
+
+       // CmdArgs stores the CNI Args and Env Vars that the plugin recieved
+       CmdArgs skel.CmdArgs
 }
 
 // ReadDebug will return a debug file recorded by the noop plugin
index 1ebc735..6d6237d 100644 (file)
@@ -63,6 +63,23 @@ func debugBehavior(args *skel.CmdArgs, command string) error {
        return nil
 }
 
+func debugGetSupportedVersions() []string {
+       vers := []string{"0.-42.0", "0.1.0", "0.2.0", "0.3.0"}
+       cniArgs := os.Getenv("CNI_ARGS")
+       if cniArgs == "" {
+               return vers
+       }
+       debugFilePath := strings.TrimPrefix(cniArgs, "DEBUG=")
+       debug, err := debug.ReadDebug(debugFilePath)
+       if err != nil {
+               panic("test setup error: unable to read debug file: " + err.Error())
+       }
+       if debug.ReportVersionSupport == nil {
+               return vers
+       }
+       return debug.ReportVersionSupport
+}
+
 func cmdAdd(args *skel.CmdArgs) error {
        return debugBehavior(args, "ADD")
 }
@@ -72,6 +89,6 @@ func cmdDel(args *skel.CmdArgs) error {
 }
 
 func main() {
-       skel.PluginMain(cmdAdd, cmdDel,
-               version.PluginSupports("0.-42.0", "0.1.0", "0.2.0", "0.3.0"))
+       supportedVersions := debugGetSupportedVersions()
+       skel.PluginMain(cmdAdd, cmdDel, version.PluginSupports(supportedVersions...))
 }
index c6ce3dd..968cc83 100644 (file)
@@ -21,6 +21,7 @@ import (
        "strings"
 
        "github.com/containernetworking/cni/pkg/skel"
+       "github.com/containernetworking/cni/pkg/version"
        noop_debug "github.com/containernetworking/cni/plugins/test/noop/debug"
        . "github.com/onsi/ginkgo"
        . "github.com/onsi/gomega"
@@ -38,7 +39,10 @@ var _ = Describe("No-op plugin", func() {
        const reportResult = `{ "ip4": { "ip": "10.1.2.3/24" }, "dns": {} }`
 
        BeforeEach(func() {
-               debug = &noop_debug.Debug{ReportResult: reportResult}
+               debug = &noop_debug.Debug{
+                       ReportResult:         reportResult,
+                       ReportVersionSupport: []string{"0.1.0", "0.2.0", "0.3.0"},
+               }
 
                debugFile, err := ioutil.TempFile("", "cni_debug")
                Expect(err).NotTo(HaveOccurred())
@@ -122,6 +126,25 @@ var _ = Describe("No-op plugin", func() {
                        Expect(debug.Command).To(Equal("DEL"))
                        Expect(debug.CmdArgs).To(Equal(expectedCmdArgs))
                })
+       })
+
+       Context("when the CNI_COMMAND is VERSION", func() {
+               BeforeEach(func() {
+                       cmd.Env[0] = "CNI_COMMAND=VERSION"
+                       debug.ReportVersionSupport = []string{"0.123.0", "0.3.0"}
+
+                       Expect(debug.WriteDebug(debugFileName)).To(Succeed())
+               })
 
+               It("claims to support the specified versions", func() {
+                       session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
+                       Expect(err).NotTo(HaveOccurred())
+                       Eventually(session).Should(gexec.Exit(0))
+                       decoder := &version.PluginDecoder{}
+                       pluginInfo, err := decoder.Decode(session.Out.Contents())
+                       Expect(err).NotTo(HaveOccurred())
+                       Expect(pluginInfo.SupportedVersions()).To(ConsistOf(
+                               "0.123.0", "0.3.0"))
+               })
        })
 })