versioning: plugins report a list of supported versions
authorGabe Rosenhouse <rosenhouse@gmail.com>
Mon, 22 Aug 2016 06:48:04 +0000 (23:48 -0700)
committerGabe Rosenhouse <rosenhouse@gmail.com>
Fri, 2 Sep 2016 15:26:17 +0000 (11:26 -0400)
Further progress on versioning support (Issue #266).
Bump CNI spec version to 0.3.0

20 files changed:
SPEC.md
libcni/api.go
libcni/api_test.go
pkg/invoke/exec.go
pkg/skel/skel.go
pkg/skel/skel_test.go
pkg/version/version.go
pkg/version/version_suite_test.go [new file with mode: 0644]
pkg/version/version_test.go [new file with mode: 0644]
plugins/ipam/dhcp/main.go
plugins/ipam/host-local/main.go
plugins/main/bridge/bridge.go
plugins/main/ipvlan/ipvlan.go
plugins/main/loopback/loopback.go
plugins/main/macvlan/macvlan.go
plugins/main/ptp/ptp.go
plugins/meta/flannel/flannel.go
plugins/meta/tuning/tuning.go
plugins/test/noop/main.go
test

diff --git a/SPEC.md b/SPEC.md
index cf75222..91c85d3 100644 (file)
--- a/SPEC.md
+++ b/SPEC.md
@@ -64,8 +64,14 @@ The operations that the CNI plugin needs to support are:
 
 - Report version
   - Parameters: NONE.
-  - Result:
-    - The version of the CNI spec implemented by the plugin: `{ "cniVersion": "0.2.0" }`
+  - Result: information about the CNI spec versions supported by the plugin
+
+      ```
+      {
+        "cniVersion": "0.3.0", // the version of the CNI spec in use for this output
+        "supportedVersions": [ "0.1.0", "0.2.0", "0.3.0" ] // the list of CNI spec versions that this plugin supports
+      }
+      ```
 
 The executable command-line API uses the type of network (see [Network Configuration](#network-configuration) below) as the name of the executable to invoke.
 It will then look for this executable in a list of predefined directories. Once found, it will invoke the executable using the following environment variables for argument passing:
@@ -85,7 +91,7 @@ Success is indicated by a return code of zero and the following JSON printed to
 
 ```
 {
-  "cniVersion": "0.2.0",
+  "cniVersion": "0.3.0",
   "ip4": {
     "ip": <ipv4-and-subnet-in-CIDR>,
     "gateway": <ipv4-of-the-gateway>,  (optional)
@@ -114,7 +120,7 @@ Examples include generating an `/etc/resolv.conf` file to be injected into the c
 Errors are indicated by a non-zero return code and the following JSON being printed to stdout:
 ```
 {
-  "cniVersion": "0.2.0",
+  "cniVersion": "0.3.0",
   "code": <numeric-error-code>,
   "msg": <short-error-message>,
   "details": <long-error-message> (optional)
@@ -151,7 +157,7 @@ Plugins may define additional fields that they accept and may generate an error
 
 ```json
 {
-  "cniVersion": "0.2.0",
+  "cniVersion": "0.3.0",
   "name": "dbnet",
   "type": "bridge",
   // type (plugin) specific
@@ -170,7 +176,7 @@ Plugins may define additional fields that they accept and may generate an error
 
 ```json
 {
-  "cniVersion": "0.2.0",
+  "cniVersion": "0.3.0",
   "name": "pci",
   "type": "ovs",
   // type (plugin) specific
@@ -220,7 +226,7 @@ Success is indicated by a zero return code and the following JSON being printed
 
 ```
 {
-  "cniVersion": "0.2.0",
+  "cniVersion": "0.3.0",
   "ip4": {
     "ip": <ipv4-and-subnet-in-CIDR>,
     "gateway": <ipv4-of-the-gateway>,  (optional)
index 340a20c..6e616da 100644 (file)
@@ -19,6 +19,7 @@ import (
 
        "github.com/containernetworking/cni/pkg/invoke"
        "github.com/containernetworking/cni/pkg/types"
+       "github.com/containernetworking/cni/pkg/version"
 )
 
 type RuntimeConf struct {
@@ -60,6 +61,17 @@ func (c *CNIConfig) DelNetwork(net *NetworkConfig, rt *RuntimeConf) error {
        return invoke.ExecPluginWithoutResult(pluginPath, net.Bytes, c.args("DEL", rt))
 }
 
+func (c *CNIConfig) GetVersionInfo(pluginType string) (version.PluginInfo, error) {
+       pluginPath, err := invoke.FindInPath(pluginType, c.Path)
+       if err != nil {
+               return nil, err
+       }
+
+       // TODO: if error is because plugin is old and VERSION command is unrecognized
+       // then do the right thing and return version.PluginSupports("0.1.0"), nil
+       return invoke.ExecPluginForVersion(pluginPath)
+}
+
 // =====
 func (c *CNIConfig) args(action string, rt *RuntimeConf) *invoke.Args {
        return &invoke.Args{
index 2df6b4e..5e0dd0f 100644 (file)
@@ -155,4 +155,23 @@ var _ = Describe("Invoking the plugin", func() {
                        })
                })
        })
+
+       Describe("GetVersionInfo", func() {
+               It("executes the plugin with the command VERSION", func() {
+                       versionInfo, err := cniConfig.GetVersionInfo("noop")
+                       Expect(err).NotTo(HaveOccurred())
+
+                       Expect(versionInfo).NotTo(BeNil())
+                       Expect(versionInfo.SupportedVersions()).To(Equal([]string{
+                               "0.-42.0", "0.1.0", "0.2.0", "0.3.0",
+                       }))
+               })
+
+               Context("when finding the plugin fails", func() {
+                       It("returns the error", func() {
+                               _, err := cniConfig.GetVersionInfo("does-not-exist")
+                               Expect(err).To(MatchError(ContainSubstring(`failed to find plugin "does-not-exist"`)))
+                       })
+               })
+       })
 })
index d7e38f2..3d32ab7 100644 (file)
@@ -23,6 +23,7 @@ import (
        "os/exec"
 
        "github.com/containernetworking/cni/pkg/types"
+       "github.com/containernetworking/cni/pkg/version"
 )
 
 func pluginErr(err error, output []byte) error {
@@ -57,6 +58,15 @@ func ExecPluginWithoutResult(pluginPath string, netconf []byte, args CNIArgs) er
        return err
 }
 
+func ExecPluginForVersion(pluginPath string) (version.PluginInfo, error) {
+       stdoutBytes, err := execPlugin(pluginPath, nil, &Args{Command: "VERSION"})
+       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())
 }
index 19ddf24..de64d7d 100644 (file)
@@ -39,11 +39,10 @@ type CmdArgs struct {
 }
 
 type dispatcher struct {
-       Getenv    func(string) string
-       Stdin     io.Reader
-       Stdout    io.Writer
-       Stderr    io.Writer
-       Versioner version.PluginVersioner
+       Getenv func(string) string
+       Stdin  io.Reader
+       Stdout io.Writer
+       Stderr io.Writer
 }
 
 type reqForCmdEntry map[string]bool
@@ -144,7 +143,7 @@ func createTypedError(f string, args ...interface{}) *types.Error {
        }
 }
 
-func (t *dispatcher) pluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error) *types.Error {
+func (t *dispatcher) pluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo) *types.Error {
        cmd, cmdArgs, err := t.getCmdArgsFromEnv()
        if err != nil {
                return createTypedError(err.Error())
@@ -158,7 +157,7 @@ func (t *dispatcher) pluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error) *types.Er
                err = cmdDel(cmdArgs)
 
        case "VERSION":
-               err = t.Versioner.Encode(t.Stdout)
+               err = versionInfo.Encode(t.Stdout)
 
        default:
                return createTypedError("unknown CNI_COMMAND: %v", cmd)
@@ -176,16 +175,15 @@ func (t *dispatcher) pluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error) *types.Er
 
 // PluginMain is the "main" for a plugin. It accepts
 // two callback functions for add and del commands.
-func PluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error) {
+func PluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo) {
        caller := dispatcher{
-               Getenv:    os.Getenv,
-               Stdin:     os.Stdin,
-               Stdout:    os.Stdout,
-               Stderr:    os.Stderr,
-               Versioner: version.DefaultPluginVersioner,
+               Getenv: os.Getenv,
+               Stdin:  os.Stdin,
+               Stdout: os.Stdout,
+               Stderr: os.Stderr,
        }
 
-       err := caller.pluginMain(cmdAdd, cmdDel)
+       err := caller.pluginMain(cmdAdd, cmdDel, versionInfo)
        if err != nil {
                dieErr(err)
        }
index e6304f5..0431abb 100644 (file)
@@ -53,6 +53,7 @@ var _ = Describe("dispatching to the correct callback", func() {
                cmdAdd, cmdDel  *fakeCmd
                dispatch        *dispatcher
                expectedCmdArgs *CmdArgs
+               versionInfo     version.PluginInfo
        )
 
        BeforeEach(func() {
@@ -67,13 +68,12 @@ var _ = Describe("dispatching to the correct callback", func() {
                stdin = strings.NewReader(`{ "some": "config" }`)
                stdout = &bytes.Buffer{}
                stderr = &bytes.Buffer{}
-               versioner := &version.BasicVersioner{CNIVersion: "9.8.7"}
+               versionInfo = version.PluginSupports("9.8.7")
                dispatch = &dispatcher{
-                       Getenv:    func(key string) string { return environment[key] },
-                       Stdin:     stdin,
-                       Stdout:    stdout,
-                       Stderr:    stderr,
-                       Versioner: versioner,
+                       Getenv: func(key string) string { return environment[key] },
+                       Stdin:  stdin,
+                       Stdout: stdout,
+                       Stderr: stderr,
                }
                cmdAdd = &fakeCmd{}
                cmdDel = &fakeCmd{}
@@ -90,7 +90,7 @@ var _ = Describe("dispatching to the correct callback", func() {
        var envVarChecker = func(envVar string, isRequired bool) {
                delete(environment, envVar)
 
-               err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func)
+               err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
                if isRequired {
                        Expect(err).To(Equal(&types.Error{
                                Code: 100,
@@ -104,7 +104,7 @@ var _ = Describe("dispatching to the correct callback", func() {
 
        Context("when the CNI_COMMAND is ADD", func() {
                It("extracts env vars and stdin data and calls cmdAdd", func() {
-                       err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func)
+                       err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
 
                        Expect(err).NotTo(HaveOccurred())
                        Expect(cmdAdd.CallCount).To(Equal(1))
@@ -113,7 +113,7 @@ var _ = Describe("dispatching to the correct callback", func() {
                })
 
                It("does not call cmdDel", func() {
-                       err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func)
+                       err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
 
                        Expect(err).NotTo(HaveOccurred())
                        Expect(cmdDel.CallCount).To(Equal(0))
@@ -136,7 +136,7 @@ var _ = Describe("dispatching to the correct callback", func() {
                        })
 
                        It("reports that all of them are missing, not just the first", func() {
-                               Expect(dispatch.pluginMain(cmdAdd.Func, cmdDel.Func)).NotTo(Succeed())
+                               Expect(dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)).NotTo(Succeed())
                                log := stderr.String()
                                Expect(log).To(ContainSubstring("CNI_NETNS env variable missing\n"))
                                Expect(log).To(ContainSubstring("CNI_IFNAME env variable missing\n"))
@@ -152,7 +152,7 @@ var _ = Describe("dispatching to the correct callback", func() {
                })
 
                It("calls cmdDel with the env vars and stdin data", func() {
-                       err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func)
+                       err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
 
                        Expect(err).NotTo(HaveOccurred())
                        Expect(cmdDel.CallCount).To(Equal(1))
@@ -160,7 +160,7 @@ var _ = Describe("dispatching to the correct callback", func() {
                })
 
                It("does not call cmdAdd", func() {
-                       err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func)
+                       err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
 
                        Expect(err).NotTo(HaveOccurred())
                        Expect(cmdAdd.CallCount).To(Equal(0))
@@ -182,14 +182,17 @@ var _ = Describe("dispatching to the correct callback", func() {
                })
 
                It("prints the version to stdout", func() {
-                       err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func)
+                       err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
 
                        Expect(err).NotTo(HaveOccurred())
-                       Expect(stdout).To(MatchJSON(`{ "cniVersion": "9.8.7" }`))
+                       Expect(stdout).To(MatchJSON(`{
+                               "cniVersion": "0.3.0",
+                               "supportedVersions": ["9.8.7"]
+                       }`))
                })
 
                It("does not call cmdAdd or cmdDel", func() {
-                       err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func)
+                       err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
 
                        Expect(err).NotTo(HaveOccurred())
                        Expect(cmdAdd.CallCount).To(Equal(0))
@@ -212,14 +215,14 @@ var _ = Describe("dispatching to the correct callback", func() {
                })
 
                It("does not call any cmd callback", func() {
-                       dispatch.pluginMain(cmdAdd.Func, cmdDel.Func)
+                       dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
 
                        Expect(cmdAdd.CallCount).To(Equal(0))
                        Expect(cmdDel.CallCount).To(Equal(0))
                })
 
                It("returns an error", func() {
-                       err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func)
+                       err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
 
                        Expect(err).To(Equal(&types.Error{
                                Code: 100,
@@ -234,14 +237,14 @@ var _ = Describe("dispatching to the correct callback", func() {
                })
 
                It("does not call any cmd callback", func() {
-                       dispatch.pluginMain(cmdAdd.Func, cmdDel.Func)
+                       dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
 
                        Expect(cmdAdd.CallCount).To(Equal(0))
                        Expect(cmdDel.CallCount).To(Equal(0))
                })
 
                It("wraps and returns the error", func() {
-                       err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func)
+                       err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
 
                        Expect(err).To(Equal(&types.Error{
                                Code: 100,
@@ -260,7 +263,7 @@ var _ = Describe("dispatching to the correct callback", func() {
                        })
 
                        It("returns the error as-is", func() {
-                               err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func)
+                               err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
 
                                Expect(err).To(Equal(&types.Error{
                                        Code: 1234,
@@ -275,7 +278,7 @@ var _ = Describe("dispatching to the correct callback", func() {
                        })
 
                        It("wraps and returns the error", func() {
-                               err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func)
+                               err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
 
                                Expect(err).To(Equal(&types.Error{
                                        Code: 100,
index 2cb075f..72b6e9b 100644 (file)
@@ -16,27 +16,67 @@ package version
 
 import (
        "encoding/json"
+       "fmt"
        "io"
 )
 
-// A PluginVersioner can encode information about its version
-type PluginVersioner interface {
+// 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
 }
 
-// BasicVersioner is a PluginVersioner which reports a single cniVersion string
-type BasicVersioner struct {
-       CNIVersion string `json:"cniVersion"`
+type simple struct {
+       CNIVersion_        string   `json:"cniVersion"`
+       SupportedVersions_ []string `json:"supportedVersions,omitempty"`
 }
 
-func (p *BasicVersioner) Encode(w io.Writer) error {
+func (p *simple) 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"
+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,
+       }
+}
+
+func 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
 }
 
-// DefaultPluginVersioner reports the Current library spec version as the cniVersion
-var DefaultPluginVersioner = &BasicVersioner{CNIVersion: Current()}
+var Legacy = PluginSupports("0.1.0", "0.2.0", "0.3.0")
diff --git a/pkg/version/version_suite_test.go b/pkg/version/version_suite_test.go
new file mode 100644 (file)
index 0000000..25d503c
--- /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 version_test
+
+import (
+       . "github.com/onsi/ginkgo"
+       . "github.com/onsi/gomega"
+
+       "testing"
+)
+
+func TestVersion(t *testing.T) {
+       RegisterFailHandler(Fail)
+       RunSpecs(t, "Version Suite")
+}
diff --git a/pkg/version/version_test.go b/pkg/version/version_test.go
new file mode 100644 (file)
index 0000000..08b89ae
--- /dev/null
@@ -0,0 +1,72 @@
+// 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_test
+
+import (
+       "github.com/containernetworking/cni/pkg/version"
+       . "github.com/onsi/ginkgo"
+       . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Decode", func() {
+       It("returns a PluginInfo that represents the given json bytes", func() {
+               pluginInfo, err := version.Decode([]byte(`{
+                       "cniVersion": "some-library-version",
+                       "supportedVersions": [ "some-version", "some-other-version" ]
+               }`))
+               Expect(err).NotTo(HaveOccurred())
+               Expect(pluginInfo).NotTo(BeNil())
+               Expect(pluginInfo.SupportedVersions()).To(Equal([]string{
+                       "some-version",
+                       "some-other-version",
+               }))
+       })
+
+       Context("when the bytes cannot be decoded as json", func() {
+               It("returns a meaningful error", func() {
+                       _, err := version.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" ] }`))
+                       Expect(err).To(MatchError("decoding version info: missing field cniVersion"))
+               })
+       })
+
+       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" }`))
+                               Expect(err).NotTo(HaveOccurred())
+                               Expect(pluginInfo).NotTo(BeNil())
+                               Expect(pluginInfo.SupportedVersions()).To(Equal([]string{
+                                       "0.1.0",
+                                       "0.2.0",
+                               }))
+                       })
+               })
+
+               Context("when the cniVersion is >= 0.3.0", func() {
+                       It("returns a meaningful error", func() {
+                               _, err := version.Decode([]byte(`{ "cniVersion": "0.3.0" }`))
+                               Expect(err).To(MatchError("decoding version info: missing field supportedVersions"))
+                       })
+               })
+       })
+
+})
index b537831..0e46af9 100644 (file)
@@ -22,6 +22,7 @@ import (
 
        "github.com/containernetworking/cni/pkg/skel"
        "github.com/containernetworking/cni/pkg/types"
+       "github.com/containernetworking/cni/pkg/version"
 )
 
 const socketPath = "/run/cni/dhcp.sock"
@@ -30,7 +31,7 @@ func main() {
        if len(os.Args) > 1 && os.Args[1] == "daemon" {
                runDaemon()
        } else {
-               skel.PluginMain(cmdAdd, cmdDel)
+               skel.PluginMain(cmdAdd, cmdDel, version.Legacy)
        }
 }
 
index d2f3c30..0e1b063 100644 (file)
@@ -19,10 +19,11 @@ import (
 
        "github.com/containernetworking/cni/pkg/skel"
        "github.com/containernetworking/cni/pkg/types"
+       "github.com/containernetworking/cni/pkg/version"
 )
 
 func main() {
-       skel.PluginMain(cmdAdd, cmdDel)
+       skel.PluginMain(cmdAdd, cmdDel, version.Legacy)
 }
 
 func cmdAdd(args *skel.CmdArgs) error {
index 2fef878..bc17301 100644 (file)
@@ -28,6 +28,7 @@ import (
        "github.com/containernetworking/cni/pkg/skel"
        "github.com/containernetworking/cni/pkg/types"
        "github.com/containernetworking/cni/pkg/utils"
+       "github.com/containernetworking/cni/pkg/version"
        "github.com/vishvananda/netlink"
 )
 
@@ -354,5 +355,5 @@ func cmdDel(args *skel.CmdArgs) error {
 }
 
 func main() {
-       skel.PluginMain(cmdAdd, cmdDel)
+       skel.PluginMain(cmdAdd, cmdDel, version.Legacy)
 }
index d7cfc39..4c58109 100644 (file)
@@ -25,6 +25,7 @@ import (
        "github.com/containernetworking/cni/pkg/ns"
        "github.com/containernetworking/cni/pkg/skel"
        "github.com/containernetworking/cni/pkg/types"
+       "github.com/containernetworking/cni/pkg/version"
        "github.com/vishvananda/netlink"
 )
 
@@ -171,5 +172,5 @@ func renameLink(curName, newName string) error {
 }
 
 func main() {
-       skel.PluginMain(cmdAdd, cmdDel)
+       skel.PluginMain(cmdAdd, cmdDel, version.Legacy)
 }
index 186fd54..1344c13 100644 (file)
@@ -18,6 +18,7 @@ import (
        "github.com/containernetworking/cni/pkg/ns"
        "github.com/containernetworking/cni/pkg/skel"
        "github.com/containernetworking/cni/pkg/types"
+       "github.com/containernetworking/cni/pkg/version"
        "github.com/vishvananda/netlink"
 )
 
@@ -67,5 +68,5 @@ func cmdDel(args *skel.CmdArgs) error {
 }
 
 func main() {
-       skel.PluginMain(cmdAdd, cmdDel)
+       skel.PluginMain(cmdAdd, cmdDel, version.Legacy)
 }
index 63c9f7f..ef01269 100644 (file)
@@ -26,6 +26,7 @@ import (
        "github.com/containernetworking/cni/pkg/skel"
        "github.com/containernetworking/cni/pkg/types"
        "github.com/containernetworking/cni/pkg/utils/sysctl"
+       "github.com/containernetworking/cni/pkg/version"
        "github.com/vishvananda/netlink"
 )
 
@@ -193,5 +194,5 @@ func renameLink(curName, newName string) error {
 }
 
 func main() {
-       skel.PluginMain(cmdAdd, cmdDel)
+       skel.PluginMain(cmdAdd, cmdDel, version.Legacy)
 }
index 014b472..a26b09e 100644 (file)
@@ -30,6 +30,7 @@ import (
        "github.com/containernetworking/cni/pkg/skel"
        "github.com/containernetworking/cni/pkg/types"
        "github.com/containernetworking/cni/pkg/utils"
+       "github.com/containernetworking/cni/pkg/version"
 )
 
 func init() {
@@ -236,5 +237,5 @@ func cmdDel(args *skel.CmdArgs) error {
 }
 
 func main() {
-       skel.PluginMain(cmdAdd, cmdDel)
+       skel.PluginMain(cmdAdd, cmdDel, version.Legacy)
 }
index 096fe6d..334bd41 100644 (file)
@@ -32,6 +32,7 @@ import (
        "github.com/containernetworking/cni/pkg/invoke"
        "github.com/containernetworking/cni/pkg/skel"
        "github.com/containernetworking/cni/pkg/types"
+       "github.com/containernetworking/cni/pkg/version"
 )
 
 const (
@@ -249,5 +250,5 @@ func cmdDel(args *skel.CmdArgs) error {
 }
 
 func main() {
-       skel.PluginMain(cmdAdd, cmdDel)
+       skel.PluginMain(cmdAdd, cmdDel, version.Legacy)
 }
index 75ba852..98e92ec 100644 (file)
@@ -27,6 +27,7 @@ import (
        "github.com/containernetworking/cni/pkg/ns"
        "github.com/containernetworking/cni/pkg/skel"
        "github.com/containernetworking/cni/pkg/types"
+       "github.com/containernetworking/cni/pkg/version"
 )
 
 // TuningConf represents the network tuning configuration.
@@ -78,5 +79,5 @@ func cmdDel(args *skel.CmdArgs) error {
 }
 
 func main() {
-       skel.PluginMain(cmdAdd, cmdDel)
+       skel.PluginMain(cmdAdd, cmdDel, version.Legacy)
 }
index ddb1aca..1ebc735 100644 (file)
@@ -28,6 +28,7 @@ import (
        "strings"
 
        "github.com/containernetworking/cni/pkg/skel"
+       "github.com/containernetworking/cni/pkg/version"
        "github.com/containernetworking/cni/plugins/test/noop/debug"
 )
 
@@ -71,5 +72,6 @@ func cmdDel(args *skel.CmdArgs) error {
 }
 
 func main() {
-       skel.PluginMain(cmdAdd, cmdDel)
+       skel.PluginMain(cmdAdd, cmdDel,
+               version.PluginSupports("0.-42.0", "0.1.0", "0.2.0", "0.3.0"))
 }
diff --git a/test b/test
index afb1c94..7537b40 100755 (executable)
--- a/test
+++ b/test
@@ -11,7 +11,7 @@ set -e
 
 source ./build
 
-TESTABLE="libcni 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 pkg/utils/hwaddr pkg/ip"
+TESTABLE="libcni 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 pkg/utils/hwaddr pkg/ip pkg/version"
 FORMATTABLE="$TESTABLE pkg/testutils plugins/meta/flannel plugins/meta/tuning"
 
 # user has not provided PKG override