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 {
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 {
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 {
var defaultPluginExec = &PluginExec{
RawExec: &RawExec{Stderr: os.Stderr},
- VersionDecoder: &version.Decoder{},
+ VersionDecoder: &version.PluginDecoder{},
}
type PluginExec struct {
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",
},
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
// NetConf describes a network.
type NetConf struct {
+ CNIVersion string `json:"cniVersion,omitempty"`
+
Name string `json:"name,omitempty"`
Type string `json:"type,omitempty"`
IPAM struct {
--- /dev/null
+// 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
+}
. "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() {
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
// 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
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")
}
}
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...))
}
"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"
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())
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"))
+ })
})
})