}
type CNI interface {
- AddNetworkList(net *NetworkConfigList, rt *RuntimeConf) (*types.Result, error)
+ AddNetworkList(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
DelNetworkList(net *NetworkConfigList, rt *RuntimeConf) error
- AddNetwork(net *NetworkConfig, rt *RuntimeConf) (*types.Result, error)
+ AddNetwork(net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
DelNetwork(net *NetworkConfig, rt *RuntimeConf) error
}
// CNIConfig implements the CNI interface
var _ CNI = &CNIConfig{}
-func buildOneConfig(list *NetworkConfigList, orig *NetworkConfig, prevResult *types.Result) (*NetworkConfig, error) {
+func buildOneConfig(list *NetworkConfigList, orig *NetworkConfig, prevResult types.Result) (*NetworkConfig, error) {
var err error
// Ensure every config uses the same name and version
}
// AddNetworkList executes a sequence of plugins with the ADD command
-func (c *CNIConfig) AddNetworkList(list *NetworkConfigList, rt *RuntimeConf) (*types.Result, error) {
- var prevResult *types.Result
+func (c *CNIConfig) AddNetworkList(list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) {
+ var prevResult types.Result
for _, net := range list.Plugins {
pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
if err != nil {
}
// 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)
if err != nil {
return nil, err
"github.com/containernetworking/cni/libcni"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/current"
noop_debug "github.com/containernetworking/cni/plugins/test/noop/debug"
. "github.com/onsi/ginkgo"
Describe("AddNetwork", func() {
It("executes the plugin with command ADD", func() {
- result, err := cniConfig.AddNetwork(netConfig, runtimeConfig)
+ r, err := cniConfig.AddNetwork(netConfig, runtimeConfig)
Expect(err).NotTo(HaveOccurred())
- Expect(result).To(Equal(&types.Result{
- IP4: &types.IPConfig{
+ result, err := current.GetResult(r)
+ Expect(err).NotTo(HaveOccurred())
+
+ Expect(result).To(Equal(¤t.Result{
+ IP4: ¤t.IPConfig{
IP: net.IPNet{
IP: net.ParseIP("10.1.2.3"),
Mask: net.IPv4Mask(255, 255, 255, 0),
Describe("AddNetworkList", func() {
It("executes all plugins with command ADD and returns an intermediate result", func() {
- result, err := cniConfig.AddNetworkList(netConfigList, runtimeConfig)
+ r, err := cniConfig.AddNetworkList(netConfigList, runtimeConfig)
+ Expect(err).NotTo(HaveOccurred())
+
+ result, err := current.GetResult(r)
Expect(err).NotTo(HaveOccurred())
- Expect(result).To(Equal(&types.Result{
+ Expect(result).To(Equal(¤t.Result{
// IP4 added by first plugin
- IP4: &types.IPConfig{
+ IP4: ¤t.IPConfig{
IP: net.IPNet{
IP: net.ParseIP("10.1.2.3"),
Mask: net.IPv4Mask(255, 255, 255, 0),
"github.com/containernetworking/cni/pkg/types"
)
-func DelegateAdd(delegatePlugin string, netconf []byte) (*types.Result, error) {
+func DelegateAdd(delegatePlugin string, netconf []byte) (types.Result, error) {
if os.Getenv("CNI_COMMAND") != "ADD" {
return nil, fmt.Errorf("CNI_COMMAND is not ADD")
}
package invoke
import (
- "encoding/json"
"fmt"
"os"
"github.com/containernetworking/cni/pkg/version"
)
-func ExecPluginWithResult(pluginPath string, netconf []byte, args CNIArgs) (*types.Result, error) {
+func ExecPluginWithResult(pluginPath string, netconf []byte, args CNIArgs) (types.Result, error) {
return defaultPluginExec.WithResult(pluginPath, netconf, args)
}
}
}
-func (e *PluginExec) WithResult(pluginPath string, netconf []byte, args CNIArgs) (*types.Result, error) {
+func (e *PluginExec) WithResult(pluginPath string, netconf []byte, args CNIArgs) (types.Result, error) {
stdoutBytes, err := e.RawExec.ExecPlugin(pluginPath, netconf, args.AsEnv())
if err != nil {
return nil, err
}
- res := &types.Result{}
- err = json.Unmarshal(stdoutBytes, res)
- return res, err
+ // Plugin must return result in same version as specified in netconf
+ versionDecoder := &version.ConfigDecoder{}
+ confVersion, err := versionDecoder.Decode(netconf)
+ if err != nil {
+ return nil, err
+ }
+
+ return version.NewResult(confVersion, stdoutBytes)
}
func (e *PluginExec) WithoutResult(pluginPath string, netconf []byte, args CNIArgs) error {
"github.com/containernetworking/cni/pkg/invoke"
"github.com/containernetworking/cni/pkg/invoke/fakes"
+ "github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/cni/pkg/version"
. "github.com/onsi/ginkgo"
Describe("returning a result", func() {
It("unmarshals the result bytes into the Result type", func() {
- result, err := pluginExec.WithResult(pluginPath, netconf, cniargs)
+ r, err := pluginExec.WithResult(pluginPath, netconf, cniargs)
+ Expect(err).NotTo(HaveOccurred())
+
+ result, err := current.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(result.IP4.IP.IP.String()).To(Equal("1.2.3.4"))
})
"github.com/containernetworking/cni/pkg/invoke"
"github.com/containernetworking/cni/pkg/ip"
"github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/current"
"github.com/vishvananda/netlink"
)
-func ExecAdd(plugin string, netconf []byte) (*types.Result, error) {
+func ExecAdd(plugin string, netconf []byte) (types.Result, error) {
return invoke.DelegateAdd(plugin, netconf)
}
// ConfigureIface takes the result of IPAM plugin and
// applies to the ifName interface
-func ConfigureIface(ifName string, res *types.Result) error {
+func ConfigureIface(ifName string, res *current.Result) error {
link, err := netlink.LinkByName(ifName)
if err != nil {
return fmt.Errorf("failed to lookup %q: %v", ifName, err)
package testutils
import (
- "encoding/json"
"io/ioutil"
"os"
"github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/version"
)
func envCleanup() {
os.Unsetenv("CNI_IFNAME")
}
-func CmdAddWithResult(cniNetns, cniIfname string, conf []byte, f func() error) (*types.Result, []byte, error) {
+func CmdAddWithResult(cniNetns, cniIfname string, conf []byte, f func() error) (types.Result, []byte, error) {
os.Setenv("CNI_COMMAND", "ADD")
os.Setenv("CNI_PATH", os.Getenv("PATH"))
os.Setenv("CNI_NETNS", cniNetns)
return nil, nil, err
}
- result := types.Result{}
- err = json.Unmarshal(out, &result)
+ // Plugin must return result in same version as specified in netconf
+ versionDecoder := &version.ConfigDecoder{}
+ confVersion, err := versionDecoder.Decode(conf)
if err != nil {
return nil, nil, err
}
- return &result, out, nil
+ result, err := version.NewResult(confVersion, out)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return result, out, nil
}
func CmdDelWithResult(cniNetns, cniIfname string, f func() error) error {
--- /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 current
+
+import (
+ "encoding/json"
+ "fmt"
+ "net"
+ "os"
+
+ "github.com/containernetworking/cni/pkg/types"
+)
+
+const implementedSpecVersion string = "0.2.0"
+
+var SupportedVersions = []string{"", "0.1.0", implementedSpecVersion}
+
+func NewResult(data []byte) (types.Result, error) {
+ result := &Result{}
+ if err := json.Unmarshal(data, result); err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
+func GetResult(r types.Result) (*Result, error) {
+ newResult, err := r.GetAsVersion(implementedSpecVersion)
+ if err != nil {
+ return nil, err
+ }
+ result, ok := newResult.(*Result)
+ if !ok {
+ return nil, fmt.Errorf("failed to convert result")
+ }
+ return result, nil
+}
+
+var resultConverters = []struct {
+ versions []string
+ convert func(types.Result) (*Result, error)
+}{
+ {SupportedVersions, convertFrom020},
+}
+
+func convertFrom020(result types.Result) (*Result, error) {
+ newResult, ok := result.(*Result)
+ if !ok {
+ return nil, fmt.Errorf("failed to convert result")
+ }
+ return newResult, nil
+}
+
+func NewResultFromResult(result types.Result) (*Result, error) {
+ version := result.Version()
+ for _, converter := range resultConverters {
+ for _, supportedVersion := range converter.versions {
+ if version == supportedVersion {
+ return converter.convert(result)
+ }
+ }
+ }
+ return nil, fmt.Errorf("unsupported CNI result version %q", version)
+}
+
+// Result is what gets returned from the plugin (via stdout) to the caller
+type Result struct {
+ IP4 *IPConfig `json:"ip4,omitempty"`
+ IP6 *IPConfig `json:"ip6,omitempty"`
+ DNS types.DNS `json:"dns,omitempty"`
+}
+
+func (r *Result) Version() string {
+ return implementedSpecVersion
+}
+
+func (r *Result) GetAsVersion(version string) (types.Result, error) {
+ for _, supportedVersion := range SupportedVersions {
+ if version == supportedVersion {
+ return r, nil
+ }
+ }
+ return nil, fmt.Errorf("cannot convert version %q to %s", SupportedVersions, version)
+}
+
+func (r *Result) Print() error {
+ data, err := json.MarshalIndent(r, "", " ")
+ if err != nil {
+ return err
+ }
+ _, err = os.Stdout.Write(data)
+ return err
+}
+
+// String returns a formatted string in the form of "[IP4: $1,][ IP6: $2,] DNS: $3" where
+// $1 represents the receiver's IPv4, $2 represents the receiver's IPv6 and $3 the
+// receiver's DNS. If $1 or $2 are nil, they won't be present in the returned string.
+func (r *Result) String() string {
+ var str string
+ if r.IP4 != nil {
+ str = fmt.Sprintf("IP4:%+v, ", *r.IP4)
+ }
+ if r.IP6 != nil {
+ str += fmt.Sprintf("IP6:%+v, ", *r.IP6)
+ }
+ return fmt.Sprintf("%sDNS:%+v", str, r.DNS)
+}
+
+// IPConfig contains values necessary to configure an interface
+type IPConfig struct {
+ IP net.IPNet
+ Gateway net.IP
+ Routes []types.Route
+}
+
+// net.IPNet is not JSON (un)marshallable so this duality is needed
+// for our custom IPNet type
+
+// JSON (un)marshallable types
+type ipConfig struct {
+ IP types.IPNet `json:"ip"`
+ Gateway net.IP `json:"gateway,omitempty"`
+ Routes []types.Route `json:"routes,omitempty"`
+}
+
+func (c *IPConfig) MarshalJSON() ([]byte, error) {
+ ipc := ipConfig{
+ IP: types.IPNet(c.IP),
+ Gateway: c.Gateway,
+ Routes: c.Routes,
+ }
+
+ return json.Marshal(ipc)
+}
+
+func (c *IPConfig) UnmarshalJSON(data []byte) error {
+ ipc := ipConfig{}
+ if err := json.Unmarshal(data, &ipc); err != nil {
+ return err
+ }
+
+ c.IP = net.IPNet(ipc.IP)
+ c.Gateway = ipc.Gateway
+ c.Routes = ipc.Routes
+ return nil
+}
--- /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 current_test
+
+import (
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+
+ "testing"
+)
+
+func TestTypes010(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "0.1.0 Types Suite")
+}
--- /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 current_test
+
+import (
+ "io/ioutil"
+ "net"
+ "os"
+
+ "github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/current"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Ensures compatibility with the 0.1.0 spec", func() {
+ It("correctly encodes a 0.1.0 Result", func() {
+ ipv4, err := types.ParseCIDR("1.2.3.30/24")
+ Expect(err).NotTo(HaveOccurred())
+ Expect(ipv4).NotTo(BeNil())
+
+ routegwv4, routev4, err := net.ParseCIDR("15.5.6.8/24")
+ Expect(err).NotTo(HaveOccurred())
+ Expect(routev4).NotTo(BeNil())
+ Expect(routegwv4).NotTo(BeNil())
+
+ ipv6, err := types.ParseCIDR("abcd:1234:ffff::cdde/64")
+ Expect(err).NotTo(HaveOccurred())
+ Expect(ipv6).NotTo(BeNil())
+
+ routegwv6, routev6, err := net.ParseCIDR("1111:dddd::aaaa/80")
+ Expect(err).NotTo(HaveOccurred())
+ Expect(routev6).NotTo(BeNil())
+ Expect(routegwv6).NotTo(BeNil())
+
+ // Set every field of the struct to ensure source compatibility
+ res := current.Result{
+ IP4: ¤t.IPConfig{
+ IP: *ipv4,
+ Gateway: net.ParseIP("1.2.3.1"),
+ Routes: []types.Route{
+ {Dst: *routev4, GW: routegwv4},
+ },
+ },
+ IP6: ¤t.IPConfig{
+ IP: *ipv6,
+ Gateway: net.ParseIP("abcd:1234:ffff::1"),
+ Routes: []types.Route{
+ {Dst: *routev6, GW: routegwv6},
+ },
+ },
+ DNS: types.DNS{
+ Nameservers: []string{"1.2.3.4", "1::cafe"},
+ Domain: "acompany.com",
+ Search: []string{"somedomain.com", "otherdomain.net"},
+ Options: []string{"foo", "bar"},
+ },
+ }
+
+ Expect(res.String()).To(Equal("IP4:{IP:{IP:1.2.3.30 Mask:ffffff00} Gateway:1.2.3.1 Routes:[{Dst:{IP:15.5.6.0 Mask:ffffff00} GW:15.5.6.8}]}, IP6:{IP:{IP:abcd:1234:ffff::cdde Mask:ffffffffffffffff0000000000000000} Gateway:abcd:1234:ffff::1 Routes:[{Dst:{IP:1111:dddd:: Mask:ffffffffffffffffffff000000000000} GW:1111:dddd::aaaa}]}, DNS:{Nameservers:[1.2.3.4 1::cafe] Domain:acompany.com Search:[somedomain.com otherdomain.net] Options:[foo bar]}"))
+
+ // Redirect stdout to capture JSON result
+ oldStdout := os.Stdout
+ r, w, err := os.Pipe()
+ Expect(err).NotTo(HaveOccurred())
+
+ os.Stdout = w
+ err = res.Print()
+ w.Close()
+ Expect(err).NotTo(HaveOccurred())
+
+ // parse the result
+ out, err := ioutil.ReadAll(r)
+ os.Stdout = oldStdout
+ Expect(err).NotTo(HaveOccurred())
+
+ Expect(string(out)).To(Equal(`{
+ "ip4": {
+ "ip": "1.2.3.30/24",
+ "gateway": "1.2.3.1",
+ "routes": [
+ {
+ "dst": "15.5.6.0/24",
+ "gw": "15.5.6.8"
+ }
+ ]
+ },
+ "ip6": {
+ "ip": "abcd:1234:ffff::cdde/64",
+ "gateway": "abcd:1234:ffff::1",
+ "routes": [
+ {
+ "dst": "1111:dddd::/80",
+ "gw": "1111:dddd::aaaa"
+ }
+ ]
+ },
+ "dns": {
+ "nameservers": [
+ "1.2.3.4",
+ "1::cafe"
+ ],
+ "domain": "acompany.com",
+ "search": [
+ "somedomain.com",
+ "otherdomain.net"
+ ],
+ "options": [
+ "foo",
+ "bar"
+ ]
+ }
+}`))
+ })
+})
import (
"encoding/json"
- "fmt"
"net"
"os"
)
type NetConf struct {
CNIVersion string `json:"cniVersion,omitempty"`
- Name string `json:"name,omitempty"`
- Type string `json:"type,omitempty"`
- PrevResult *Result `json:"prevResult,omitempty"`
- IPAM struct {
+ Name string `json:"name,omitempty"`
+ Type string `json:"type,omitempty"`
+ IPAM struct {
Type string `json:"type,omitempty"`
} `json:"ipam,omitempty"`
DNS DNS `json:"dns"`
Plugins []*NetConf `json:"plugins,omitempty"`
}
-// Result is what gets returned from the plugin (via stdout) to the caller
-type Result struct {
- IP4 *IPConfig `json:"ip4,omitempty"`
- IP6 *IPConfig `json:"ip6,omitempty"`
- DNS DNS `json:"dns,omitempty"`
-}
+type ResultFactoryFunc func([]byte) (Result, error)
-func (r *Result) Print() error {
- return prettyPrint(r)
-}
+// Result is an interface that provides the result of plugin execution
+type Result interface {
+ // The highest CNI specification result verison the result supports
+ // without having to convert
+ Version() string
-// String returns a formatted string in the form of "[IP4: $1,][ IP6: $2,] DNS: $3" where
-// $1 represents the receiver's IPv4, $2 represents the receiver's IPv6 and $3 the
-// receiver's DNS. If $1 or $2 are nil, they won't be present in the returned string.
-func (r *Result) String() string {
- var str string
- if r.IP4 != nil {
- str = fmt.Sprintf("IP4:%+v, ", *r.IP4)
- }
- if r.IP6 != nil {
- str += fmt.Sprintf("IP6:%+v, ", *r.IP6)
- }
- return fmt.Sprintf("%sDNS:%+v", str, r.DNS)
+ // Returns the result converted into the requested CNI specification
+ // result version, or an error if conversion failed
+ GetAsVersion(version string) (Result, error)
+
+ // Prints the result in JSON format to stdout
+ Print() error
+
+ // Returns a JSON string representation of the result
+ String() string
}
-// IPConfig contains values necessary to configure an interface
-type IPConfig struct {
- IP net.IPNet
- Gateway net.IP
- Routes []Route
+func PrintResult(result Result, version string) error {
+ newResult, err := result.GetAsVersion(version)
+ if err != nil {
+ return err
+ }
+ return newResult.Print()
}
// DNS contains values interesting for DNS resolvers
// for our custom IPNet type
// JSON (un)marshallable types
-type ipConfig struct {
- IP IPNet `json:"ip"`
- Gateway net.IP `json:"gateway,omitempty"`
- Routes []Route `json:"routes,omitempty"`
-}
-
type route struct {
Dst IPNet `json:"dst"`
GW net.IP `json:"gw,omitempty"`
}
-func (c *IPConfig) MarshalJSON() ([]byte, error) {
- ipc := ipConfig{
- IP: IPNet(c.IP),
- Gateway: c.Gateway,
- Routes: c.Routes,
- }
-
- return json.Marshal(ipc)
-}
-
-func (c *IPConfig) UnmarshalJSON(data []byte) error {
- ipc := ipConfig{}
- if err := json.Unmarshal(data, &ipc); err != nil {
- return err
- }
-
- c.IP = net.IPNet(ipc.IP)
- c.Gateway = ipc.Gateway
- c.Routes = ipc.Routes
- return nil
-}
-
func (r *Route) UnmarshalJSON(data []byte) error {
rt := route{}
if err := json.Unmarshal(data, &rt); err != nil {
"sync"
"github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/cni/pkg/version/testhelpers"
)
//
// As we change the CNI spec, the Result type and this value may change.
// The text of the example plugins should not.
-var ExpectedResult = &types.Result{
- IP4: &types.IPConfig{
+var ExpectedResult = ¤t.Result{
+ IP4: ¤t.IPConfig{
IP: net.IPNet{
IP: net.ParseIP("10.1.2.3"),
Mask: net.CIDRMask(24, 32),
import "fmt"
type ErrorIncompatible struct {
- Config string
- Plugin []string
+ Config string
+ Supported []string
}
func (e *ErrorIncompatible) Details() string {
- return fmt.Sprintf("config is %q, plugin supports %q", e.Config, e.Plugin)
+ return fmt.Sprintf("config is %q, plugin supports %q", e.Config, e.Supported)
}
func (e *ErrorIncompatible) Error() string {
type Reconciler struct{}
-func (*Reconciler) Check(configVersion string, pluginInfo PluginInfo) *ErrorIncompatible {
- pluginVersions := pluginInfo.SupportedVersions()
+func (r *Reconciler) Check(configVersion string, pluginInfo PluginInfo) *ErrorIncompatible {
+ return r.CheckRaw(configVersion, pluginInfo.SupportedVersions())
+}
- for _, pluginVersion := range pluginVersions {
- if configVersion == pluginVersion {
+func (*Reconciler) CheckRaw(configVersion string, supportedVersions []string) *ErrorIncompatible {
+ for _, supportedVersion := range supportedVersions {
+ if configVersion == supportedVersion {
return nil
}
}
return &ErrorIncompatible{
- Config: configVersion,
- Plugin: pluginVersions,
+ Config: configVersion,
+ Supported: supportedVersions,
}
}
err := reconciler.Check("0.1.0", pluginInfo)
Expect(err).To(Equal(&version.ErrorIncompatible{
- Config: "0.1.0",
- Plugin: []string{"1.2.3", "4.3.2"},
+ Config: "0.1.0",
+ Supported: []string{"1.2.3", "4.3.2"},
}))
Expect(err.Error()).To(Equal(`incompatible CNI versions: config is "0.1.0", plugin supports ["1.2.3" "4.3.2"]`))
package version
+import (
+ "fmt"
+
+ "github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/current"
+)
+
// Current reports the version of the CNI spec implemented by this library
func Current() string {
return "0.2.0"
// Any future CNI spec versions which meet this definition should be added to
// this list.
var Legacy = PluginSupports("0.1.0", "0.2.0")
+
+var resultFactories = []struct {
+ supportedVersions []string
+ newResult types.ResultFactoryFunc
+}{
+ {current.SupportedVersions, current.NewResult},
+}
+
+// Finds a Result object matching the requested version (if any) and asks
+// that object to parse the plugin result, returning an error if parsing failed.
+func NewResult(version string, resultBytes []byte) (types.Result, error) {
+ reconciler := &Reconciler{}
+ for _, resultFactory := range resultFactories {
+ err := reconciler.CheckRaw(version, resultFactory.supportedVersions)
+ if err == nil {
+ // Result supports this version
+ return resultFactory.newResult(resultBytes)
+ }
+ }
+
+ return nil, fmt.Errorf("unsupported CNI result version %q", version)
+}
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/current"
"github.com/coreos/go-systemd/activation"
)
// Allocate acquires an IP from a DHCP server for a specified container.
// The acquired lease will be maintained until Release() is called.
-func (d *DHCP) Allocate(args *skel.CmdArgs, result *types.Result) error {
+func (d *DHCP) Allocate(args *skel.CmdArgs, result *current.Result) error {
conf := types.NetConf{}
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
return fmt.Errorf("error parsing netconf: %v", err)
d.setLease(args.ContainerID, conf.Name, l)
- result.IP4 = &types.IPConfig{
+ result.IP4 = ¤t.IPConfig{
IP: *ipn,
Gateway: l.Gateway(),
Routes: l.Routes(),
"path/filepath"
"github.com/containernetworking/cni/pkg/skel"
- "github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/cni/pkg/version"
)
}
func cmdAdd(args *skel.CmdArgs) error {
- result := types.Result{}
- if err := rpcCall("DHCP.Allocate", args, &result); err != nil {
+ result := ¤t.Result{}
+ if err := rpcCall("DHCP.Allocate", args, result); err != nil {
return err
}
return result.Print()
"net"
"github.com/containernetworking/cni/pkg/ip"
- "github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/cni/plugins/ipam/host-local/backend"
)
}
// Returns newly allocated IP along with its config
-func (a *IPAllocator) Get(id string) (*types.IPConfig, error) {
+func (a *IPAllocator) Get(id string) (*current.IPConfig, error) {
a.store.Lock()
defer a.store.Unlock()
}
if reserved {
- return &types.IPConfig{
+ return ¤t.IPConfig{
IP: net.IPNet{IP: requestedIP, Mask: a.conf.Subnet.Mask},
Gateway: gw,
Routes: a.conf.Routes,
return nil, err
}
if reserved {
- return &types.IPConfig{
+ return ¤t.IPConfig{
IP: net.IPNet{IP: cur, Mask: a.conf.Subnet.Mask},
Gateway: gw,
Routes: a.conf.Routes,
import (
"fmt"
"github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/current"
fakestore "github.com/containernetworking/cni/plugins/ipam/host-local/backend/testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
lastIP string
}
-func (t AllocatorTestCase) run() (*types.IPConfig, error) {
+func (t AllocatorTestCase) run() (*current.IPConfig, error) {
subnet, err := types.ParseCIDR(t.subnet)
if err != nil {
return nil, err
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/testutils"
"github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/current"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
}
// Allocate the IP
- result, _, err := testutils.CmdAddWithResult(nspath, ifname, []byte(conf), func() error {
+ r, _, err := testutils.CmdAddWithResult(nspath, ifname, []byte(conf), func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
+ result, err := current.GetResult(r)
+ Expect(err).NotTo(HaveOccurred())
+
expectedAddress, err := types.ParseCIDR("10.1.2.2/24")
Expect(err).NotTo(HaveOccurred())
expectedAddress.IP = expectedAddress.IP.To16()
}
// Allocate the IP
- result, _, err := testutils.CmdAddWithResult(nspath, ifname, []byte(conf), func() error {
+ r, _, err := testutils.CmdAddWithResult(nspath, ifname, []byte(conf), func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
+ result, err := current.GetResult(r)
+ Expect(err).NotTo(HaveOccurred())
+
ipFilePath := filepath.Join(tmpDir, "mynet", result.IP4.IP.IP.String())
contents, err := ioutil.ReadFile(ipFilePath)
Expect(err).NotTo(HaveOccurred())
"github.com/containernetworking/cni/plugins/ipam/host-local/backend/disk"
"github.com/containernetworking/cni/pkg/skel"
- "github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/cni/pkg/version"
)
return err
}
- r := types.Result{}
+ r := ¤t.Result{}
if ipamConf.ResolvConf != "" {
dns, err := parseResolvConf(ipamConf.ResolvConf)
return err
}
- ipConf, err := allocator.Get(args.ContainerID)
+ r.IP4, err = allocator.Get(args.ContainerID)
if err != nil {
return err
}
- r.IP4 = ipConf
return r.Print()
}
"github.com/containernetworking/cni/pkg/ns"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/cni/pkg/utils"
"github.com/containernetworking/cni/pkg/version"
"github.com/vishvananda/netlink"
}
// run the IPAM plugin and get back the config to apply
- result, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData)
+ r, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData)
+ if err != nil {
+ return err
+ }
+
+ result, err := current.GetResult(r)
if err != nil {
return err
}
"github.com/containernetworking/cni/pkg/ns"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/cni/pkg/version"
"github.com/vishvananda/netlink"
)
}
// run the IPAM plugin and get back the config to apply
- result, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData)
+ r, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData)
if err != nil {
return err
}
+ result, err := current.GetResult(r)
+ if err != nil {
+ return err
+ }
+
if result.IP4 == nil {
return errors.New("IPAM plugin returned missing IPv4 config")
}
import (
"github.com/containernetworking/cni/pkg/ns"
"github.com/containernetworking/cni/pkg/skel"
- "github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/cni/pkg/version"
"github.com/vishvananda/netlink"
)
return err // not tested
}
- result := types.Result{}
+ result := current.Result{}
return result.Print()
}
"github.com/containernetworking/cni/pkg/ns"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/cni/pkg/utils/sysctl"
"github.com/containernetworking/cni/pkg/version"
"github.com/vishvananda/netlink"
}
// run the IPAM plugin and get back the config to apply
- result, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData)
+ r, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData)
if err != nil {
return err
}
+ result, err := current.GetResult(r)
+ if err != nil {
+ return err
+ }
+
if result.IP4 == nil {
return errors.New("IPAM plugin returned missing IPv4 config")
}
"github.com/containernetworking/cni/pkg/ns"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/cni/pkg/utils"
"github.com/containernetworking/cni/pkg/version"
)
MTU int `json:"mtu"`
}
-func setupContainerVeth(netns, ifName string, mtu int, pr *types.Result) (string, error) {
+func setupContainerVeth(netns, ifName string, mtu int, pr *current.Result) (string, error) {
// The IPAM result will be something like IP=192.168.3.5/24, GW=192.168.3.1.
// What we want is really a point-to-point link but veth does not support IFF_POINTOPONT.
// Next best thing would be to let it ARP but set interface to 192.168.3.5/32 and
}
for _, r := range []netlink.Route{
- netlink.Route{
+ {
LinkIndex: contVeth.Attrs().Index,
Dst: &net.IPNet{
IP: pr.IP4.Gateway,
Scope: netlink.SCOPE_LINK,
Src: pr.IP4.IP.IP,
},
- netlink.Route{
+ {
LinkIndex: contVeth.Attrs().Index,
Dst: &net.IPNet{
IP: pr.IP4.IP.IP.Mask(pr.IP4.IP.Mask),
return hostVethName, err
}
-func setupHostVeth(vethName string, ipConf *types.IPConfig) error {
+func setupHostVeth(vethName string, ipConf *current.IPConfig) error {
// hostVeth moved namespaces and may have a new ifindex
veth, err := netlink.LinkByName(vethName)
if err != nil {
}
// run the IPAM plugin and get back the config to apply
- result, err := ipam.ExecAdd(conf.IPAM.Type, args.StdinData)
+ r, err := ipam.ExecAdd(conf.IPAM.Type, args.StdinData)
+ if err != nil {
+ return err
+ }
+ result, err := current.GetResult(r)
if err != nil {
return err
}
"github.com/containernetworking/cni/pkg/ns"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/cni/pkg/version"
)
return err
}
- result := types.Result{}
+ result := current.Result{}
return result.Print()
}
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/cni/pkg/version"
noop_debug "github.com/containernetworking/cni/plugins/test/noop/debug"
)
type NetConf struct {
types.NetConf
- DebugFile string `json:"debugFile"`
- PrevResult *types.Result `json:"prevResult,omitempty"`
+ DebugFile string `json:"debugFile"`
+ PrevResult *current.Result `json:"prevResult,omitempty"`
}
func loadConf(bytes []byte) (*NetConf, error) {
return errors.New(debug.ReportError)
} else if debug.ReportResult == "PASSTHROUGH" || debug.ReportResult == "INJECT-DNS" {
if debug.ReportResult == "INJECT-DNS" {
- netConf.PrevResult.DNS.Nameservers = []string{"1.2.3.4"}
+ newResult, err := current.NewResultFromResult(netConf.PrevResult)
+ if err != nil {
+ return err
+ }
+ newResult.DNS.Nameservers = []string{"1.2.3.4"}
+ netConf.PrevResult = newResult
}
newResult, err := json.Marshal(netConf.PrevResult)
if err != nil {
source ./build
-TESTABLE="libcni plugins/ipam/dhcp plugins/ipam/host-local plugins/ipam/host-local/backend/allocator 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 pkg/version/testhelpers plugins/meta/flannel"
+TESTABLE="libcni plugins/ipam/dhcp plugins/ipam/host-local plugins/ipam/host-local/backend/allocator plugins/main/loopback pkg/invoke pkg/ns pkg/skel pkg/types pkg/types/current pkg/utils plugins/main/ipvlan plugins/main/macvlan plugins/main/bridge plugins/main/ptp plugins/test/noop pkg/utils/hwaddr pkg/ip pkg/version pkg/version/testhelpers plugins/meta/flannel"
FORMATTABLE="$TESTABLE pkg/testutils plugins/meta/flannel plugins/meta/tuning"
# user has not provided PKG override