pkg/skel: plugins now respond to VERSION command
authorGabe Rosenhouse <rosenhouse@gmail.com>
Thu, 14 Jul 2016 03:50:54 +0000 (23:50 -0400)
committerGabe Rosenhouse <rosenhouse@gmail.com>
Thu, 14 Jul 2016 04:24:32 +0000 (00:24 -0400)
To support CNI spec versioning, plugins must be able to report version
information to container runtimes.

skel/skel.go
skel/skel_test.go
version/version.go [new file with mode: 0644]

index 4325ec6..b5d6ecf 100644 (file)
@@ -24,6 +24,7 @@ import (
        "os"
 
        "github.com/containernetworking/cni/pkg/types"
+       "github.com/containernetworking/cni/pkg/version"
 )
 
 // CmdArgs captures all the arguments passed in to the plugin
@@ -38,9 +39,11 @@ type CmdArgs struct {
 }
 
 type dispatcher struct {
-       Getenv func(string) string
-       Stdin  io.Reader
-       Stderr io.Writer
+       Getenv    func(string) string
+       Stdin     io.Reader
+       Stdout    io.Writer
+       Stderr    io.Writer
+       Versioner version.PluginVersioner
 }
 
 type reqForCmdEntry map[string]bool
@@ -154,6 +157,9 @@ func (t *dispatcher) pluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error) *types.Er
        case "DEL":
                err = cmdDel(cmdArgs)
 
+       case "VERSION":
+               err = t.Versioner.Encode(t.Stdout)
+
        default:
                return createTypedError("unknown CNI_COMMAND: %v", cmd)
        }
@@ -172,9 +178,11 @@ func (t *dispatcher) pluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error) *types.Er
 // two callback functions for add and del commands.
 func PluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error) {
        caller := dispatcher{
-               Getenv: os.Getenv,
-               Stdin:  os.Stdin,
-               Stderr: os.Stderr,
+               Getenv:    os.Getenv,
+               Stdin:     os.Stdin,
+               Stdout:    os.Stdout,
+               Stderr:    os.Stderr,
+               Versioner: version.DefaultPluginVersioner,
        }
 
        err := caller.pluginMain(cmdAdd, cmdDel)
index 39df271..e6304f5 100644 (file)
@@ -21,6 +21,7 @@ import (
        "strings"
 
        "github.com/containernetworking/cni/pkg/types"
+       "github.com/containernetworking/cni/pkg/version"
 
        "github.com/containernetworking/cni/pkg/testutils"
        . "github.com/onsi/ginkgo"
@@ -48,7 +49,7 @@ var _ = Describe("dispatching to the correct callback", func() {
        var (
                environment     map[string]string
                stdin           io.Reader
-               stderr          *bytes.Buffer
+               stdout, stderr  *bytes.Buffer
                cmdAdd, cmdDel  *fakeCmd
                dispatch        *dispatcher
                expectedCmdArgs *CmdArgs
@@ -64,11 +65,15 @@ var _ = Describe("dispatching to the correct callback", func() {
                        "CNI_PATH":        "/some/cni/path",
                }
                stdin = strings.NewReader(`{ "some": "config" }`)
+               stdout = &bytes.Buffer{}
                stderr = &bytes.Buffer{}
+               versioner := &version.BasicVersioner{CNIVersion: "9.8.7"}
                dispatch = &dispatcher{
-                       Getenv: func(key string) string { return environment[key] },
-                       Stdin:  stdin,
-                       Stderr: stderr,
+                       Getenv:    func(key string) string { return environment[key] },
+                       Stdin:     stdin,
+                       Stdout:    stdout,
+                       Stderr:    stderr,
+                       Versioner: versioner,
                }
                cmdAdd = &fakeCmd{}
                cmdDel = &fakeCmd{}
@@ -171,6 +176,36 @@ var _ = Describe("dispatching to the correct callback", func() {
                )
        })
 
+       Context("when the CNI_COMMAND is VERSION", func() {
+               BeforeEach(func() {
+                       environment["CNI_COMMAND"] = "VERSION"
+               })
+
+               It("prints the version to stdout", func() {
+                       err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func)
+
+                       Expect(err).NotTo(HaveOccurred())
+                       Expect(stdout).To(MatchJSON(`{ "cniVersion": "9.8.7" }`))
+               })
+
+               It("does not call cmdAdd or cmdDel", func() {
+                       err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func)
+
+                       Expect(err).NotTo(HaveOccurred())
+                       Expect(cmdAdd.CallCount).To(Equal(0))
+                       Expect(cmdDel.CallCount).To(Equal(0))
+               })
+
+               DescribeTable("VERSION does not need the usual env vars", envVarChecker,
+                       Entry("command", "CNI_COMMAND", true),
+                       Entry("container id", "CNI_CONTAINER_ID", false),
+                       Entry("net ns", "CNI_NETNS", false),
+                       Entry("if name", "CNI_IFNAME", false),
+                       Entry("args", "CNI_ARGS", false),
+                       Entry("path", "CNI_PATH", false),
+               )
+       })
+
        Context("when the CNI_COMMAND is unrecognized", func() {
                BeforeEach(func() {
                        environment["CNI_COMMAND"] = "NOPE"
diff --git a/version/version.go b/version/version.go
new file mode 100644 (file)
index 0000000..2cb075f
--- /dev/null
@@ -0,0 +1,42 @@
+// 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"
+       "io"
+)
+
+// A PluginVersioner can encode information about its version
+type PluginVersioner interface {
+       Encode(io.Writer) error
+}
+
+// BasicVersioner is a PluginVersioner which reports a single cniVersion string
+type BasicVersioner struct {
+       CNIVersion string `json:"cniVersion"`
+}
+
+func (p *BasicVersioner) Encode(w io.Writer) error {
+       return json.NewEncoder(w).Encode(p)
+}
+
+// Current reports the version of the CNI spec implemented by this library
+func Current() string {
+       return "0.2.0"
+}
+
+// DefaultPluginVersioner reports the Current library spec version as the cniVersion
+var DefaultPluginVersioner = &BasicVersioner{CNIVersion: Current()}