versioning: misc cleanups
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
This commit is contained in:
parent
bf31ed1591
commit
d5e2e375d4
@ -43,6 +43,7 @@ type CNIConfig struct {
|
|||||||
Path []string
|
Path []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddNetwork executes the plugin with the ADD command
|
||||||
func (c *CNIConfig) AddNetwork(net *NetworkConfig, rt *RuntimeConf) (*types.Result, error) {
|
func (c *CNIConfig) AddNetwork(net *NetworkConfig, rt *RuntimeConf) (*types.Result, error) {
|
||||||
pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
|
pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
|
||||||
if err != nil {
|
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))
|
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 {
|
func (c *CNIConfig) DelNetwork(net *NetworkConfig, rt *RuntimeConf) error {
|
||||||
pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
|
pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
|
||||||
if err != nil {
|
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))
|
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) {
|
func (c *CNIConfig) GetVersionInfo(pluginType string) (version.PluginInfo, error) {
|
||||||
pluginPath, err := invoke.FindInPath(pluginType, c.Path)
|
pluginPath, err := invoke.FindInPath(pluginType, c.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -36,7 +36,7 @@ func GetVersionInfo(pluginPath string) (version.PluginInfo, error) {
|
|||||||
|
|
||||||
var defaultPluginExec = &PluginExec{
|
var defaultPluginExec = &PluginExec{
|
||||||
RawExec: &RawExec{Stderr: os.Stderr},
|
RawExec: &RawExec{Stderr: os.Stderr},
|
||||||
VersionDecoder: &version.Decoder{},
|
VersionDecoder: &version.PluginDecoder{},
|
||||||
}
|
}
|
||||||
|
|
||||||
type PluginExec struct {
|
type PluginExec struct {
|
||||||
@ -64,6 +64,10 @@ func (e *PluginExec) WithoutResult(pluginPath string, netconf []byte, args CNIAr
|
|||||||
return err
|
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) {
|
func (e *PluginExec) GetVersionInfo(pluginPath string) (version.PluginInfo, error) {
|
||||||
args := &Args{
|
args := &Args{
|
||||||
Command: "VERSION",
|
Command: "VERSION",
|
||||||
|
@ -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("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 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
|
// a minimal 0.1.0 / 0.2.0 plugin
|
||||||
const plugin_source_v010 = `package main
|
const plugin_source_v010 = `package main
|
||||||
|
|
@ -57,6 +57,8 @@ func (n *IPNet) UnmarshalJSON(data []byte) error {
|
|||||||
|
|
||||||
// NetConf describes a network.
|
// NetConf describes a network.
|
||||||
type NetConf struct {
|
type NetConf struct {
|
||||||
|
CNIVersion string `json:"cniVersion,omitempty"`
|
||||||
|
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
Type string `json:"type,omitempty"`
|
Type string `json:"type,omitempty"`
|
||||||
IPAM struct {
|
IPAM struct {
|
||||||
|
77
pkg/version/plugin.go
Normal file
77
pkg/version/plugin.go
Normal file
@ -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
|
||||||
|
}
|
@ -20,11 +20,11 @@ import (
|
|||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("Decode", func() {
|
var _ = Describe("Decoding versions reported by a plugin", func() {
|
||||||
var decoder *version.Decoder
|
var decoder *version.PluginDecoder
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
decoder = &version.Decoder{}
|
decoder = &version.PluginDecoder{}
|
||||||
})
|
})
|
||||||
|
|
||||||
It("returns a PluginInfo that represents the given json bytes", func() {
|
It("returns a PluginInfo that represents the given json bytes", func() {
|
@ -14,73 +14,11 @@
|
|||||||
|
|
||||||
package version
|
package version
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Current reports the version of the CNI spec implemented by this library
|
// Current reports the version of the CNI spec implemented by this library
|
||||||
func Current() string {
|
func Current() string {
|
||||||
return "0.3.0"
|
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
|
// 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
|
// 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
|
// library ought to work correctly with a plugin that reports support for
|
||||||
|
@ -24,11 +24,17 @@ import (
|
|||||||
|
|
||||||
// Debug is used to control and record the behavior of the noop plugin
|
// Debug is used to control and record the behavior of the noop plugin
|
||||||
type Debug struct {
|
type Debug struct {
|
||||||
ReportResult string
|
// Report* fields allow the test to control the behavior of the no-op plugin
|
||||||
ReportError string
|
ReportResult string
|
||||||
ReportStderr string
|
ReportError string
|
||||||
Command string
|
ReportStderr string
|
||||||
CmdArgs skel.CmdArgs
|
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
|
// ReadDebug will return a debug file recorded by the noop plugin
|
||||||
|
@ -63,6 +63,23 @@ func debugBehavior(args *skel.CmdArgs, command string) error {
|
|||||||
return nil
|
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 {
|
func cmdAdd(args *skel.CmdArgs) error {
|
||||||
return debugBehavior(args, "ADD")
|
return debugBehavior(args, "ADD")
|
||||||
}
|
}
|
||||||
@ -72,6 +89,6 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
skel.PluginMain(cmdAdd, cmdDel,
|
supportedVersions := debugGetSupportedVersions()
|
||||||
version.PluginSupports("0.-42.0", "0.1.0", "0.2.0", "0.3.0"))
|
skel.PluginMain(cmdAdd, cmdDel, version.PluginSupports(supportedVersions...))
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
|
"github.com/containernetworking/cni/pkg/version"
|
||||||
noop_debug "github.com/containernetworking/cni/plugins/test/noop/debug"
|
noop_debug "github.com/containernetworking/cni/plugins/test/noop/debug"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
@ -38,7 +39,10 @@ var _ = Describe("No-op plugin", func() {
|
|||||||
const reportResult = `{ "ip4": { "ip": "10.1.2.3/24" }, "dns": {} }`
|
const reportResult = `{ "ip4": { "ip": "10.1.2.3/24" }, "dns": {} }`
|
||||||
|
|
||||||
BeforeEach(func() {
|
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")
|
debugFile, err := ioutil.TempFile("", "cni_debug")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
@ -122,6 +126,25 @@ var _ = Describe("No-op plugin", func() {
|
|||||||
Expect(debug.Command).To(Equal("DEL"))
|
Expect(debug.Command).To(Equal("DEL"))
|
||||||
Expect(debug.CmdArgs).To(Equal(expectedCmdArgs))
|
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"))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user