diff --git a/invoke/exec.go b/invoke/exec.go index 68efbb9b..7eb06156 100644 --- a/invoke/exec.go +++ b/invoke/exec.go @@ -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", diff --git a/invoke/exec_integration_test.go b/invoke/get_version_integration_test.go similarity index 78% rename from invoke/exec_integration_test.go rename to invoke/get_version_integration_test.go index f02374c0..d10826dd 100644 --- a/invoke/exec_integration_test.go +++ b/invoke/get_version_integration_test.go @@ -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 diff --git a/types/types.go b/types/types.go index 6948dcb1..ba02580a 100644 --- a/types/types.go +++ b/types/types.go @@ -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/version/plugin.go b/version/plugin.go new file mode 100644 index 00000000..9bd7dc83 --- /dev/null +++ b/version/plugin.go @@ -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 +} diff --git a/version/version_test.go b/version/plugin_test.go similarity index 94% rename from version/version_test.go rename to version/plugin_test.go index 98a386d7..a58bd35a 100644 --- a/version/version_test.go +++ b/version/plugin_test.go @@ -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() { diff --git a/version/version.go b/version/version.go index cdb531c0..5f937f7e 100644 --- a/version/version.go +++ b/version/version.go @@ -14,73 +14,11 @@ 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