From 3995efc7c19bb1857efff29f85240cf29b863a34 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Wed, 14 Aug 2019 14:32:08 +0000 Subject: [PATCH] Update cni and go-cni to the v0.7.1 release Closes #1236 Signed-off-by: Michael Crosby --- pkg/server/sandbox_run.go | 6 +- pkg/server/sandbox_stop.go | 6 +- pkg/server/testing/fake_cni_plugin.go | 6 +- vendor.conf | 4 +- vendor/github.com/containerd/go-cni/README.md | 4 +- vendor/github.com/containerd/go-cni/cni.go | 13 +- vendor/github.com/containerd/go-cni/helper.go | 4 +- .../github.com/containerd/go-cni/namespace.go | 10 +- vendor/github.com/containerd/go-cni/opts.go | 4 +- vendor/github.com/containerd/go-cni/result.go | 4 +- .../github.com/containerd/go-cni/vendor.conf | 2 +- .../containernetworking/cni/README.md | 29 +- .../containernetworking/cni/libcni/api.go | 384 +++++++++++++++--- .../containernetworking/cni/libcni/conf.go | 18 +- .../cni/pkg/invoke/args.go | 68 +++- .../cni/pkg/invoke/delegate.go | 57 ++- .../cni/pkg/invoke/exec.go | 105 +++-- .../cni/pkg/invoke/os_unix.go | 2 +- .../cni/pkg/invoke/raw_exec.go | 25 +- .../cni/pkg/types/020/types.go | 7 +- .../cni/pkg/types/current/types.go | 29 +- .../cni/pkg/types/types.go | 26 +- .../cni/pkg/version/plugin.go | 63 +++ .../cni/pkg/version/version.go | 26 +- 24 files changed, 715 insertions(+), 187 deletions(-) diff --git a/pkg/server/sandbox_run.go b/pkg/server/sandbox_run.go index b0189a39b..39e03a116 100644 --- a/pkg/server/sandbox_run.go +++ b/pkg/server/sandbox_run.go @@ -146,7 +146,7 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox defer func() { if retErr != nil { // Teardown network if an error is returned. - if err := c.teardownPod(id, sandbox.NetNSPath, config); err != nil { + if err := c.teardownPod(ctx, id, sandbox.NetNSPath, config); err != nil { log.G(ctx).WithError(err).Errorf("Failed to destroy network for sandbox %q", id) } } @@ -559,7 +559,7 @@ func (c *criService) setupPod(ctx context.Context, id string, path string, confi return "", nil, errors.Wrap(err, "failed to get bandwidth info from annotations") } - result, err := c.netPlugin.Setup(id, + result, err := c.netPlugin.Setup(ctx, id, path, cni.WithLabels(labels), cni.WithCapabilityPortMap(toCNIPortMappings(config.GetPortMappings())), @@ -575,7 +575,7 @@ func (c *criService) setupPod(ctx context.Context, id string, path string, confi return selectPodIP(configs.IPConfigs), result, nil } // If it comes here then the result was invalid so destroy the pod network and return error - if err := c.teardownPod(id, path, config); err != nil { + if err := c.teardownPod(ctx, id, path, config); err != nil { log.G(ctx).WithError(err).Errorf("Failed to destroy network for sandbox %q", id) } return "", result, errors.Errorf("failed to find network info for sandbox %q", id) diff --git a/pkg/server/sandbox_stop.go b/pkg/server/sandbox_stop.go index 3ff8c0225..0796f5cfd 100644 --- a/pkg/server/sandbox_stop.go +++ b/pkg/server/sandbox_stop.go @@ -80,7 +80,7 @@ func (c *criService) StopPodSandbox(ctx context.Context, r *runtime.StopPodSandb } else if closed { netNSPath = "" } - if err := c.teardownPod(id, netNSPath, sandbox.Config); err != nil { + if err := c.teardownPod(ctx, id, netNSPath, sandbox.Config); err != nil { return nil, errors.Wrapf(err, "failed to destroy network for sandbox %q", id) } if err = sandbox.NetNS.Remove(); err != nil { @@ -157,13 +157,13 @@ func (c *criService) waitSandboxStop(ctx context.Context, sandbox sandboxstore.S } // teardownPod removes the network from the pod -func (c *criService) teardownPod(id string, path string, config *runtime.PodSandboxConfig) error { +func (c *criService) teardownPod(ctx context.Context, id string, path string, config *runtime.PodSandboxConfig) error { if c.netPlugin == nil { return errors.New("cni config not initialized") } labels := getPodCNILabels(id, config) - return c.netPlugin.Remove(id, + return c.netPlugin.Remove(ctx, id, path, cni.WithLabels(labels), cni.WithCapabilityPortMap(toCNIPortMappings(config.GetPortMappings()))) diff --git a/pkg/server/testing/fake_cni_plugin.go b/pkg/server/testing/fake_cni_plugin.go index 22250b2eb..f9df581c0 100644 --- a/pkg/server/testing/fake_cni_plugin.go +++ b/pkg/server/testing/fake_cni_plugin.go @@ -17,6 +17,8 @@ limitations under the License. package testing import ( + "context" + cni "github.com/containerd/go-cni" ) @@ -32,12 +34,12 @@ func NewFakeCNIPlugin() *FakeCNIPlugin { } // Setup setups the network of PodSandbox. -func (f *FakeCNIPlugin) Setup(id, path string, opts ...cni.NamespaceOpts) (*cni.CNIResult, error) { +func (f *FakeCNIPlugin) Setup(ctx context.Context, id, path string, opts ...cni.NamespaceOpts) (*cni.CNIResult, error) { return nil, nil } // Remove teardown the network of PodSandbox. -func (f *FakeCNIPlugin) Remove(id, path string, opts ...cni.NamespaceOpts) error { +func (f *FakeCNIPlugin) Remove(ctx context.Context, id, path string, opts ...cni.NamespaceOpts) error { return nil } diff --git a/vendor.conf b/vendor.conf index 2034a5f11..f9144479b 100644 --- a/vendor.conf +++ b/vendor.conf @@ -76,5 +76,5 @@ github.com/davecgh/go-spew v1.1.1 # cni dependencies github.com/containernetworking/plugins v0.7.5 -github.com/containernetworking/cni v0.6.0 -github.com/containerd/go-cni 22460c018b64cf8bf4151b3ff9c4d077e6a88cbf +github.com/containernetworking/cni v0.7.1 +github.com/containerd/go-cni 49fbd9b210f3c8ee3b7fd3cd797aabaf364627c1 diff --git a/vendor/github.com/containerd/go-cni/README.md b/vendor/github.com/containerd/go-cni/README.md index fea3ce29f..1bd2f0013 100644 --- a/vendor/github.com/containerd/go-cni/README.md +++ b/vendor/github.com/containerd/go-cni/README.md @@ -12,7 +12,7 @@ A generic CNI library to provide APIs for CNI plugin interactions. The library p go-cni aims to support plugins that implement [Container Network Interface](https://github.com/containernetworking/cni) ## Usage -``` +```go func main() { id := "123456" netns := "/proc/9999/ns/net" @@ -24,7 +24,7 @@ func main() { gocni.WithDefaultIfName(defaultIfName)) // Load the cni configuration - err:= l.Load(gocni.WithLoNetwork,gocni.WithDefaultConf) + err:= l.Load(gocni.WithLoNetwork, gocni.WithDefaultConf) if err != nil{ log.Errorf("failed to load cni configuration: %v", err) return diff --git a/vendor/github.com/containerd/go-cni/cni.go b/vendor/github.com/containerd/go-cni/cni.go index bdd63b841..8acc83b33 100644 --- a/vendor/github.com/containerd/go-cni/cni.go +++ b/vendor/github.com/containerd/go-cni/cni.go @@ -17,6 +17,7 @@ package cni import ( + "context" "fmt" "strings" "sync" @@ -29,9 +30,9 @@ import ( type CNI interface { // Setup setup the network for the namespace - Setup(id string, path string, opts ...NamespaceOpts) (*CNIResult, error) + Setup(ctx context.Context, id string, path string, opts ...NamespaceOpts) (*CNIResult, error) // Remove tears down the network of the namespace. - Remove(id string, path string, opts ...NamespaceOpts) error + Remove(ctx context.Context, id string, path string, opts ...NamespaceOpts) error // Load loads the cni network config Load(opts ...CNIOpt) error // Status checks the status of the cni initialization @@ -139,7 +140,7 @@ func (c *libcni) Networks() []*Network { } // Setup setups the network in the namespace -func (c *libcni) Setup(id string, path string, opts ...NamespaceOpts) (*CNIResult, error) { +func (c *libcni) Setup(ctx context.Context, id string, path string, opts ...NamespaceOpts) (*CNIResult, error) { if err := c.Status(); err != nil { return nil, err } @@ -149,7 +150,7 @@ func (c *libcni) Setup(id string, path string, opts ...NamespaceOpts) (*CNIResul } var results []*current.Result for _, network := range c.Networks() { - r, err := network.Attach(ns) + r, err := network.Attach(ctx, ns) if err != nil { return nil, err } @@ -159,7 +160,7 @@ func (c *libcni) Setup(id string, path string, opts ...NamespaceOpts) (*CNIResul } // Remove removes the network config from the namespace -func (c *libcni) Remove(id string, path string, opts ...NamespaceOpts) error { +func (c *libcni) Remove(ctx context.Context, id string, path string, opts ...NamespaceOpts) error { if err := c.Status(); err != nil { return err } @@ -168,7 +169,7 @@ func (c *libcni) Remove(id string, path string, opts ...NamespaceOpts) error { return err } for _, network := range c.Networks() { - if err := network.Remove(ns); err != nil { + if err := network.Remove(ctx, ns); err != nil { // Based on CNI spec v0.7.0, empty network namespace is allowed to // do best effort cleanup. However, it is not handled consistently // right now: diff --git a/vendor/github.com/containerd/go-cni/helper.go b/vendor/github.com/containerd/go-cni/helper.go index 6cde2b332..088cb9bc5 100644 --- a/vendor/github.com/containerd/go-cni/helper.go +++ b/vendor/github.com/containerd/go-cni/helper.go @@ -24,10 +24,10 @@ import ( func validateInterfaceConfig(ipConf *current.IPConfig, ifs int) error { if ipConf == nil { - return fmt.Errorf("invalid IP configuration") + return fmt.Errorf("invalid IP configuration (nil)") } if ipConf.Interface != nil && *ipConf.Interface > ifs { - return fmt.Errorf("invalid IP configuration with invalid interface %d", *ipConf.Interface) + return fmt.Errorf("invalid IP configuration (interface number %d is > number of interfaces %d)", *ipConf.Interface, ifs) } return nil } diff --git a/vendor/github.com/containerd/go-cni/namespace.go b/vendor/github.com/containerd/go-cni/namespace.go index 746c995eb..ff14b01c1 100644 --- a/vendor/github.com/containerd/go-cni/namespace.go +++ b/vendor/github.com/containerd/go-cni/namespace.go @@ -17,6 +17,8 @@ package cni import ( + "context" + cnilibrary "github.com/containernetworking/cni/libcni" "github.com/containernetworking/cni/pkg/types/current" ) @@ -27,16 +29,16 @@ type Network struct { ifName string } -func (n *Network) Attach(ns *Namespace) (*current.Result, error) { - r, err := n.cni.AddNetworkList(n.config, ns.config(n.ifName)) +func (n *Network) Attach(ctx context.Context, ns *Namespace) (*current.Result, error) { + r, err := n.cni.AddNetworkList(ctx, n.config, ns.config(n.ifName)) if err != nil { return nil, err } return current.NewResultFromResult(r) } -func (n *Network) Remove(ns *Namespace) error { - return n.cni.DelNetworkList(n.config, ns.config(n.ifName)) +func (n *Network) Remove(ctx context.Context, ns *Namespace) error { + return n.cni.DelNetworkList(ctx, n.config, ns.config(n.ifName)) } type Namespace struct { diff --git a/vendor/github.com/containerd/go-cni/opts.go b/vendor/github.com/containerd/go-cni/opts.go index 4a6f66d05..5222df1e9 100644 --- a/vendor/github.com/containerd/go-cni/opts.go +++ b/vendor/github.com/containerd/go-cni/opts.go @@ -220,11 +220,11 @@ func loadFromConfDir(c *libcni, max int) error { confList, err = cnilibrary.ConfListFromConf(conf) if err != nil { - return errors.Wrapf(ErrInvalidConfig, "failed to convert CNI config file %s to list: %v", confFile, err) + return errors.Wrapf(ErrInvalidConfig, "failed to convert CNI config file %s to CNI config list: %v", confFile, err) } } if len(confList.Plugins) == 0 { - return errors.Wrapf(ErrInvalidConfig, "CNI config list %s has no networks, skipping", confFile) + return errors.Wrapf(ErrInvalidConfig, "CNI config list in config file %s has no networks, skipping", confFile) } networks = append(networks, &Network{ diff --git a/vendor/github.com/containerd/go-cni/result.go b/vendor/github.com/containerd/go-cni/result.go index 1e958dc76..c2ac94864 100644 --- a/vendor/github.com/containerd/go-cni/result.go +++ b/vendor/github.com/containerd/go-cni/result.go @@ -79,7 +79,7 @@ func (c *libcni) GetCNIResultFromResults(results []*current.Result) (*CNIResult, // interfaces for _, ipConf := range result.IPs { if err := validateInterfaceConfig(ipConf, len(result.Interfaces)); err != nil { - return nil, errors.Wrapf(ErrInvalidResult, "failed to valid interface config: %v", err) + return nil, errors.Wrapf(ErrInvalidResult, "invalid interface config: %v", err) } name := c.getInterfaceName(result.Interfaces, ipConf) r.Interfaces[name].IPConfigs = append(r.Interfaces[name].IPConfigs, @@ -89,7 +89,7 @@ func (c *libcni) GetCNIResultFromResults(results []*current.Result) (*CNIResult, r.Routes = append(r.Routes, result.Routes...) } if _, ok := r.Interfaces[defaultInterface(c.prefix)]; !ok { - return nil, errors.Wrapf(ErrNotFound, "default network not found") + return nil, errors.Wrapf(ErrNotFound, "default network not found for: %s", defaultInterface(c.prefix)) } return r, nil } diff --git a/vendor/github.com/containerd/go-cni/vendor.conf b/vendor/github.com/containerd/go-cni/vendor.conf index aefe9a108..31d06e0fc 100644 --- a/vendor/github.com/containerd/go-cni/vendor.conf +++ b/vendor/github.com/containerd/go-cni/vendor.conf @@ -2,5 +2,5 @@ github.com/stretchr/testify b89eecf5ca5db6d3ba60b237ffe3df7bafb7662f github.com/davecgh/go-spew 8991bc29aa16c548c550c7ff78260e27b9ab7c73 github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2 github.com/stretchr/objx 8a3f7159479fbc75b30357fbc48f380b7320f08e -github.com/containernetworking/cni 142cde0c766cd6055cc7fdfdcb44579c0c9c35bf +github.com/containernetworking/cni v0.7.1 github.com/pkg/errors v0.8.0 diff --git a/vendor/github.com/containernetworking/cni/README.md b/vendor/github.com/containernetworking/cni/README.md index 793187c79..4d300a10d 100644 --- a/vendor/github.com/containernetworking/cni/README.md +++ b/vendor/github.com/containernetworking/cni/README.md @@ -1,4 +1,5 @@ -[![Build Status](https://travis-ci.org/containernetworking/cni.svg?branch=master)](https://travis-ci.org/containernetworking/cni) +[![Linux Build Status](https://travis-ci.org/containernetworking/cni.svg?branch=master)](https://travis-ci.org/containernetworking/cni) +[![Windows Build Status](https://ci.appveyor.com/api/projects/status/wtrkou8oow7x533e/branch/master?svg=true)](https://ci.appveyor.com/project/cni-bot/cni/branch/master) [![Coverage Status](https://coveralls.io/repos/github/containernetworking/cni/badge.svg?branch=master)](https://coveralls.io/github/containernetworking/cni?branch=master) [![Slack Status](https://cryptic-tundra-43194.herokuapp.com/badge.svg)](https://cryptic-tundra-43194.herokuapp.com/) @@ -8,7 +9,9 @@ # Community Sync Meeting -There is a community sync meeting for users and developers every 1-2 months. The next meeting will help on a Google Hangout and the link is in the [agenda](https://docs.google.com/document/d/10ECyT2mBGewsJUcmYmS8QNo1AcNgy2ZIe2xS7lShYhE/edit?usp=sharing) (Notes from previous meeting are also in this doc). The next meeting will be held on *Wednesday, June 21th* at *3:00pm UTC* [Add to Calendar]https://www.worldtimebuddy.com/?qm=1&lid=100,5,2643743,5391959&h=100&date=2017-6-21&sln=15-16). +There is a community sync meeting for users and developers every 1-2 months. The next meeting will help on a Google Hangout and the link is in the [agenda](https://docs.google.com/document/d/10ECyT2mBGewsJUcmYmS8QNo1AcNgy2ZIe2xS7lShYhE/edit?usp=sharing) (Notes from previous meeting are also in this doc). + +The next meeting will be held on *Wednesday, January 30th, 2019* at *4:00pm UTC / 11:00am EDT / 8:00am PDT* [Add to Calendar](https://www.worldtimebuddy.com/?qm=1&lid=100,5,2643743,5391959&h=100&date=2019-01-30&sln=16-17). --- @@ -35,11 +38,13 @@ To avoid duplication, we think it is prudent to define a common interface betwee ## Who is using CNI? ### Container runtimes - [rkt - container engine](https://coreos.com/blog/rkt-cni-networking.html) -- [Kurma - container runtime](http://kurma.io/) -- [Kubernetes - a system to simplify container operations](http://kubernetes.io/docs/admin/network-plugins/) +- [Kubernetes - a system to simplify container operations](https://kubernetes.io/docs/admin/network-plugins/) - [OpenShift - Kubernetes with additional enterprise features](https://github.com/openshift/origin/blob/master/docs/openshift_networking_requirements.md) - [Cloud Foundry - a platform for cloud applications](https://github.com/cloudfoundry-incubator/cf-networking-release) -- [Mesos - a distributed systems kernel](https://github.com/apache/mesos/blob/master/docs/cni.md) +- [Apache Mesos - a distributed systems kernel](https://github.com/apache/mesos/blob/master/docs/cni.md) +- [Amazon ECS - a highly scalable, high performance container management service](https://aws.amazon.com/ecs/) +- [Singularity - container platform optimized for HPC, EPC, and AI](https://github.com/sylabs/singularity) +- [OpenSVC - orchestrator for legacy and containerized application stacks](https://docs.opensvc.com/latest/fr/agent.configure.cni.html) ### 3rd party plugins - [Project Calico - a layer 3 virtual network](https://github.com/projectcalico/calico-cni) @@ -54,6 +59,15 @@ To avoid duplication, we think it is prudent to define a common interface betwee - [Nuage CNI - Nuage Networks SDN plugin for network policy kubernetes support ](https://github.com/nuagenetworks/nuage-cni) - [Silk - a CNI plugin designed for Cloud Foundry](https://github.com/cloudfoundry-incubator/silk) - [Linen - a CNI plugin designed for overlay networks with Open vSwitch and fit in SDN/OpenFlow network environment](https://github.com/John-Lin/linen-cni) +- [Vhostuser - a Dataplane network plugin - Supports OVS-DPDK & VPP](https://github.com/intel/vhost-user-net-plugin) +- [Amazon ECS CNI Plugins - a collection of CNI Plugins to configure containers with Amazon EC2 elastic network interfaces (ENIs)](https://github.com/aws/amazon-ecs-cni-plugins) +- [Bonding CNI - a Link aggregating plugin to address failover and high availability network](https://github.com/Intel-Corp/bond-cni) +- [ovn-kubernetes - an container network plugin built on Open vSwitch (OVS) and Open Virtual Networking (OVN) with support for both Linux and Windows](https://github.com/openvswitch/ovn-kubernetes) +- [Juniper Contrail](https://www.juniper.net/cloud) / [TungstenFabric](https://tungstenfabric.io) - Provides overlay SDN solution, delivering multicloud networking, hybrid cloud networking, simultaneous overlay-underlay support, network policy enforcement, network isolation, service chaining and flexible load balancing +- [Knitter - a CNI plugin supporting multiple networking for Kubernetes](https://github.com/ZTE/Knitter) +- [DANM - a CNI-compliant networking solution for TelCo workloads running on Kubernetes](https://github.com/nokia/danm) +- [VMware NSX – a CNI plugin that enables automated NSX L2/L3 networking and L4/L7 Load Balancing; network isolation at the pod, node, and cluster level; and zero-trust security policy for your Kubernetes cluster.](https://docs.vmware.com/en/VMware-NSX-T/2.2/com.vmware.nsxt.ncp_kubernetes.doc/GUID-6AFA724E-BB62-4693-B95C-321E8DDEA7E1.html) +- [cni-route-override - a meta CNI plugin that override route information](https://github.com/redhat-nfvpe/cni-route-override) The CNI team also maintains some [core plugins in a separate repository](https://github.com/containernetworking/plugins). @@ -67,7 +81,7 @@ If you intend to contribute to code or documentation, please read [CONTRIBUTING. ### Requirements -The CNI spec is language agnostic. To use the Go language libraries in this repository, you'll need a recent version of Go. Our [automated tests](https://travis-ci.org/containernetworking/cni/builds) cover Go versions 1.7 and 1.8. +The CNI spec is language agnostic. To use the Go language libraries in this repository, you'll need a recent version of Go. You can find the Go versions covered by our [automated tests](https://travis-ci.org/containernetworking/cni/builds) in [.travis.yaml](.travis.yml). ### Reference Plugins @@ -104,6 +118,7 @@ EOF $ cat >/etc/cni/net.d/99-loopback.conf <= 0; i-- { - net := list.Plugins[i] +func (c *CNIConfig) checkNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) error { + c.ensureExec() + pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path) + if err != nil { + return err + } - pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path) - if err != nil { - return err - } + newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt) + if err != nil { + return err + } - newConf, err := buildOneConfig(list, net, nil, rt) - if err != nil { - return err - } + return invoke.ExecPluginWithoutResult(ctx, pluginPath, newConf.Bytes, c.args("CHECK", rt), c.exec) +} - if err := invoke.ExecPluginWithoutResult(pluginPath, newConf.Bytes, c.args("DEL", rt)); err != nil { +// CheckNetworkList executes a sequence of plugins with the CHECK command +func (c *CNIConfig) CheckNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) error { + // CHECK was added in CNI spec version 0.4.0 and higher + if gtet, err := version.GreaterThanOrEqualTo(list.CNIVersion, "0.4.0"); err != nil { + return err + } else if !gtet { + return fmt.Errorf("configuration version %q does not support the CHECK command", list.CNIVersion) + } + + if list.DisableCheck { + return nil + } + + cachedResult, err := getCachedResult(list.Name, list.CNIVersion, rt) + if err != nil { + return fmt.Errorf("failed to get network %q cached result: %v", list.Name, err) + } + + for _, net := range list.Plugins { + if err := c.checkNetwork(ctx, list.Name, list.CNIVersion, net, cachedResult, rt); err != nil { return err } } @@ -165,45 +309,179 @@ func (c *CNIConfig) DelNetworkList(list *NetworkConfigList, rt *RuntimeConf) err return nil } +func (c *CNIConfig) delNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) error { + c.ensureExec() + pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path) + if err != nil { + return err + } + + newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt) + if err != nil { + return err + } + + return invoke.ExecPluginWithoutResult(ctx, pluginPath, newConf.Bytes, c.args("DEL", rt), c.exec) +} + +// DelNetworkList executes a sequence of plugins with the DEL command +func (c *CNIConfig) DelNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) error { + var cachedResult types.Result + + // Cached result on DEL was added in CNI spec version 0.4.0 and higher + if gtet, err := version.GreaterThanOrEqualTo(list.CNIVersion, "0.4.0"); err != nil { + return err + } else if gtet { + cachedResult, err = getCachedResult(list.Name, list.CNIVersion, rt) + if err != nil { + return fmt.Errorf("failed to get network %q cached result: %v", list.Name, err) + } + } + + for i := len(list.Plugins) - 1; i >= 0; i-- { + net := list.Plugins[i] + if err := c.delNetwork(ctx, list.Name, list.CNIVersion, net, cachedResult, rt); err != nil { + return err + } + } + _ = delCachedResult(list.Name, rt) + + return nil +} + // 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) +func (c *CNIConfig) AddNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) (types.Result, error) { + result, err := c.addNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, nil, rt) if err != nil { return nil, err } - net, err = injectRuntimeConfig(net, rt) - if err != nil { - return nil, err + if err = setCachedResult(result, net.Network.Name, rt); err != nil { + return nil, fmt.Errorf("failed to set network %q cached result: %v", net.Network.Name, err) } - return invoke.ExecPluginWithResult(pluginPath, net.Bytes, c.args("ADD", rt)) + return result, nil +} + +// CheckNetwork executes the plugin with the CHECK command +func (c *CNIConfig) CheckNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error { + // CHECK was added in CNI spec version 0.4.0 and higher + if gtet, err := version.GreaterThanOrEqualTo(net.Network.CNIVersion, "0.4.0"); err != nil { + return err + } else if !gtet { + return fmt.Errorf("configuration version %q does not support the CHECK command", net.Network.CNIVersion) + } + + cachedResult, err := getCachedResult(net.Network.Name, net.Network.CNIVersion, rt) + if err != nil { + return fmt.Errorf("failed to get network %q cached result: %v", net.Network.Name, err) + } + return c.checkNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, cachedResult, 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) +func (c *CNIConfig) DelNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error { + var cachedResult types.Result + + // Cached result on DEL was added in CNI spec version 0.4.0 and higher + if gtet, err := version.GreaterThanOrEqualTo(net.Network.CNIVersion, "0.4.0"); err != nil { + return err + } else if gtet { + cachedResult, err = getCachedResult(net.Network.Name, net.Network.CNIVersion, rt) + if err != nil { + return fmt.Errorf("failed to get network %q cached result: %v", net.Network.Name, err) + } + } + + if err := c.delNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, cachedResult, rt); err != nil { + return err + } + _ = delCachedResult(net.Network.Name, rt) + return nil +} + +// ValidateNetworkList checks that a configuration is reasonably valid. +// - all the specified plugins exist on disk +// - every plugin supports the desired version. +// +// Returns a list of all capabilities supported by the configuration, or error +func (c *CNIConfig) ValidateNetworkList(ctx context.Context, list *NetworkConfigList) ([]string, error) { + version := list.CNIVersion + + // holding map for seen caps (in case of duplicates) + caps := map[string]interface{}{} + + errs := []error{} + for _, net := range list.Plugins { + if err := c.validatePlugin(ctx, net.Network.Type, version); err != nil { + errs = append(errs, err) + } + for c, enabled := range net.Network.Capabilities { + if !enabled { + continue + } + caps[c] = struct{}{} + } + } + + if len(errs) > 0 { + return nil, fmt.Errorf("%v", errs) + } + + // make caps list + cc := make([]string, 0, len(caps)) + for c := range caps { + cc = append(cc, c) + } + + return cc, nil +} + +// ValidateNetwork checks that a configuration is reasonably valid. +// It uses the same logic as ValidateNetworkList) +// Returns a list of capabilities +func (c *CNIConfig) ValidateNetwork(ctx context.Context, net *NetworkConfig) ([]string, error) { + caps := []string{} + for c, ok := range net.Network.Capabilities { + if ok { + caps = append(caps, c) + } + } + if err := c.validatePlugin(ctx, net.Network.Type, net.Network.CNIVersion); err != nil { + return nil, err + } + return caps, nil +} + +// validatePlugin checks that an individual plugin's configuration is sane +func (c *CNIConfig) validatePlugin(ctx context.Context, pluginName, expectedVersion string) error { + pluginPath, err := invoke.FindInPath(pluginName, c.Path) if err != nil { return err } - net, err = injectRuntimeConfig(net, rt) + vi, err := invoke.GetVersionInfo(ctx, pluginPath, c.exec) if err != nil { return err } - - return invoke.ExecPluginWithoutResult(pluginPath, net.Bytes, c.args("DEL", rt)) + for _, vers := range vi.SupportedVersions() { + if vers == expectedVersion { + return nil + } + } + return fmt.Errorf("plugin %s does not support config version %q", pluginName, expectedVersion) } // 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) +func (c *CNIConfig) GetVersionInfo(ctx context.Context, pluginType string) (version.PluginInfo, error) { + c.ensureExec() + pluginPath, err := c.exec.FindInPath(pluginType, c.Path) if err != nil { return nil, err } - return invoke.GetVersionInfo(pluginPath) + return invoke.GetVersionInfo(ctx, pluginPath, c.exec) } // ===== diff --git a/vendor/github.com/containernetworking/cni/libcni/conf.go b/vendor/github.com/containernetworking/cni/libcni/conf.go index c7738c665..ea56c509d 100644 --- a/vendor/github.com/containernetworking/cni/libcni/conf.go +++ b/vendor/github.com/containernetworking/cni/libcni/conf.go @@ -45,6 +45,9 @@ func ConfFromBytes(bytes []byte) (*NetworkConfig, error) { if err := json.Unmarshal(bytes, &conf.Network); err != nil { return nil, fmt.Errorf("error parsing configuration: %s", err) } + if conf.Network.Type == "" { + return nil, fmt.Errorf("error parsing configuration: missing 'type'") + } return conf, nil } @@ -80,10 +83,19 @@ func ConfListFromBytes(bytes []byte) (*NetworkConfigList, error) { } } + disableCheck := false + if rawDisableCheck, ok := rawList["disableCheck"]; ok { + disableCheck, ok = rawDisableCheck.(bool) + if !ok { + return nil, fmt.Errorf("error parsing configuration list: invalid disableCheck type %T", rawDisableCheck) + } + } + list := &NetworkConfigList{ - Name: name, - CNIVersion: cniVersion, - Bytes: bytes, + Name: name, + DisableCheck: disableCheck, + CNIVersion: cniVersion, + Bytes: bytes, } var plugins []interface{} diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/args.go b/vendor/github.com/containernetworking/cni/pkg/invoke/args.go index 39b639723..913528c1d 100644 --- a/vendor/github.com/containernetworking/cni/pkg/invoke/args.go +++ b/vendor/github.com/containernetworking/cni/pkg/invoke/args.go @@ -15,6 +15,7 @@ package invoke import ( + "fmt" "os" "strings" ) @@ -22,6 +23,8 @@ import ( type CNIArgs interface { // For use with os/exec; i.e., return nil to inherit the // environment from this process + // For use in delegation; inherit the environment from this + // process and allow overrides AsEnv() []string } @@ -57,17 +60,17 @@ func (args *Args) AsEnv() []string { pluginArgsStr = stringify(args.PluginArgs) } - // Ensure that the custom values are first, so any value present in - // the process environment won't override them. - env = append([]string{ - "CNI_COMMAND=" + args.Command, - "CNI_CONTAINERID=" + args.ContainerID, - "CNI_NETNS=" + args.NetNS, - "CNI_ARGS=" + pluginArgsStr, - "CNI_IFNAME=" + args.IfName, - "CNI_PATH=" + args.Path, - }, env...) - return env + // Duplicated values which come first will be overrided, so we must put the + // custom values in the end to avoid being overrided by the process environments. + env = append(env, + "CNI_COMMAND="+args.Command, + "CNI_CONTAINERID="+args.ContainerID, + "CNI_NETNS="+args.NetNS, + "CNI_ARGS="+pluginArgsStr, + "CNI_IFNAME="+args.IfName, + "CNI_PATH="+args.Path, + ) + return dedupEnv(env) } // taken from rkt/networking/net_plugin.go @@ -80,3 +83,46 @@ func stringify(pluginArgs [][2]string) string { return strings.Join(entries, ";") } + +// DelegateArgs implements the CNIArgs interface +// used for delegation to inherit from environments +// and allow some overrides like CNI_COMMAND +var _ CNIArgs = &DelegateArgs{} + +type DelegateArgs struct { + Command string +} + +func (d *DelegateArgs) AsEnv() []string { + env := os.Environ() + + // The custom values should come in the end to override the existing + // process environment of the same key. + env = append(env, + "CNI_COMMAND="+d.Command, + ) + return dedupEnv(env) +} + +// dedupEnv returns a copy of env with any duplicates removed, in favor of later values. +// Items not of the normal environment "key=value" form are preserved unchanged. +func dedupEnv(env []string) []string { + out := make([]string, 0, len(env)) + envMap := map[string]string{} + + for _, kv := range env { + // find the first "=" in environment, if not, just keep it + eq := strings.Index(kv, "=") + if eq < 0 { + out = append(out, kv) + continue + } + envMap[kv[:eq]] = kv[eq+1:] + } + + for k, v := range envMap { + out = append(out, fmt.Sprintf("%s=%s", k, v)) + } + + return out +} diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go b/vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go index c78a69eeb..8defe4dd3 100644 --- a/vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go +++ b/vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go @@ -15,39 +15,66 @@ package invoke import ( - "fmt" + "context" "os" "path/filepath" "github.com/containernetworking/cni/pkg/types" ) -func DelegateAdd(delegatePlugin string, netconf []byte) (types.Result, error) { - if os.Getenv("CNI_COMMAND") != "ADD" { - return nil, fmt.Errorf("CNI_COMMAND is not ADD") +func delegateCommon(delegatePlugin string, exec Exec) (string, Exec, error) { + if exec == nil { + exec = defaultExec } paths := filepath.SplitList(os.Getenv("CNI_PATH")) + pluginPath, err := exec.FindInPath(delegatePlugin, paths) + if err != nil { + return "", nil, err + } - pluginPath, err := FindInPath(delegatePlugin, paths) + return pluginPath, exec, nil +} + +// DelegateAdd calls the given delegate plugin with the CNI ADD action and +// JSON configuration +func DelegateAdd(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) (types.Result, error) { + pluginPath, realExec, err := delegateCommon(delegatePlugin, exec) if err != nil { return nil, err } - return ExecPluginWithResult(pluginPath, netconf, ArgsFromEnv()) + // DelegateAdd will override the original "CNI_COMMAND" env from process with ADD + return ExecPluginWithResult(ctx, pluginPath, netconf, delegateArgs("ADD"), realExec) } -func DelegateDel(delegatePlugin string, netconf []byte) error { - if os.Getenv("CNI_COMMAND") != "DEL" { - return fmt.Errorf("CNI_COMMAND is not DEL") - } - - paths := filepath.SplitList(os.Getenv("CNI_PATH")) - - pluginPath, err := FindInPath(delegatePlugin, paths) +// DelegateCheck calls the given delegate plugin with the CNI CHECK action and +// JSON configuration +func DelegateCheck(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error { + pluginPath, realExec, err := delegateCommon(delegatePlugin, exec) if err != nil { return err } - return ExecPluginWithoutResult(pluginPath, netconf, ArgsFromEnv()) + // DelegateCheck will override the original CNI_COMMAND env from process with CHECK + return ExecPluginWithoutResult(ctx, pluginPath, netconf, delegateArgs("CHECK"), realExec) +} + +// DelegateDel calls the given delegate plugin with the CNI DEL action and +// JSON configuration +func DelegateDel(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error { + pluginPath, realExec, err := delegateCommon(delegatePlugin, exec) + if err != nil { + return err + } + + // DelegateDel will override the original CNI_COMMAND env from process with DEL + return ExecPluginWithoutResult(ctx, pluginPath, netconf, delegateArgs("DEL"), realExec) +} + +// return CNIArgs used by delegation +func delegateArgs(action string) *DelegateArgs { + return &DelegateArgs{ + Command: action, + } } diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/exec.go b/vendor/github.com/containernetworking/cni/pkg/invoke/exec.go index fc47e7c82..8e6d30b82 100644 --- a/vendor/github.com/containernetworking/cni/pkg/invoke/exec.go +++ b/vendor/github.com/containernetworking/cni/pkg/invoke/exec.go @@ -15,6 +15,7 @@ package invoke import ( + "context" "fmt" "os" @@ -22,34 +23,62 @@ import ( "github.com/containernetworking/cni/pkg/version" ) -func ExecPluginWithResult(pluginPath string, netconf []byte, args CNIArgs) (types.Result, error) { - return defaultPluginExec.WithResult(pluginPath, netconf, args) +// Exec is an interface encapsulates all operations that deal with finding +// and executing a CNI plugin. Tests may provide a fake implementation +// to avoid writing fake plugins to temporary directories during the test. +type Exec interface { + ExecPlugin(ctx context.Context, pluginPath string, stdinData []byte, environ []string) ([]byte, error) + FindInPath(plugin string, paths []string) (string, error) + Decode(jsonBytes []byte) (version.PluginInfo, error) } -func ExecPluginWithoutResult(pluginPath string, netconf []byte, args CNIArgs) error { - return defaultPluginExec.WithoutResult(pluginPath, netconf, args) -} +// For example, a testcase could pass an instance of the following fakeExec +// object to ExecPluginWithResult() to verify the incoming stdin and environment +// and provide a tailored response: +// +//import ( +// "encoding/json" +// "path" +// "strings" +//) +// +//type fakeExec struct { +// version.PluginDecoder +//} +// +//func (f *fakeExec) ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error) { +// net := &types.NetConf{} +// err := json.Unmarshal(stdinData, net) +// if err != nil { +// return nil, fmt.Errorf("failed to unmarshal configuration: %v", err) +// } +// pluginName := path.Base(pluginPath) +// if pluginName != net.Type { +// return nil, fmt.Errorf("plugin name %q did not match config type %q", pluginName, net.Type) +// } +// for _, e := range environ { +// // Check environment for forced failure request +// parts := strings.Split(e, "=") +// if len(parts) > 0 && parts[0] == "FAIL" { +// return nil, fmt.Errorf("failed to execute plugin %s", pluginName) +// } +// } +// return []byte("{\"CNIVersion\":\"0.4.0\"}"), nil +//} +// +//func (f *fakeExec) FindInPath(plugin string, paths []string) (string, error) { +// if len(paths) > 0 { +// return path.Join(paths[0], plugin), nil +// } +// return "", fmt.Errorf("failed to find plugin %s in paths %v", plugin, paths) +//} -func GetVersionInfo(pluginPath string) (version.PluginInfo, error) { - return defaultPluginExec.GetVersionInfo(pluginPath) -} - -var defaultPluginExec = &PluginExec{ - RawExec: &RawExec{Stderr: os.Stderr}, - VersionDecoder: &version.PluginDecoder{}, -} - -type PluginExec struct { - RawExec interface { - ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error) +func ExecPluginWithResult(ctx context.Context, pluginPath string, netconf []byte, args CNIArgs, exec Exec) (types.Result, error) { + if exec == nil { + exec = defaultExec } - VersionDecoder interface { - Decode(jsonBytes []byte) (version.PluginInfo, error) - } -} -func (e *PluginExec) WithResult(pluginPath string, netconf []byte, args CNIArgs) (types.Result, error) { - stdoutBytes, err := e.RawExec.ExecPlugin(pluginPath, netconf, args.AsEnv()) + stdoutBytes, err := exec.ExecPlugin(ctx, pluginPath, netconf, args.AsEnv()) if err != nil { return nil, err } @@ -64,8 +93,11 @@ func (e *PluginExec) WithResult(pluginPath string, netconf []byte, args CNIArgs) return version.NewResult(confVersion, stdoutBytes) } -func (e *PluginExec) WithoutResult(pluginPath string, netconf []byte, args CNIArgs) error { - _, err := e.RawExec.ExecPlugin(pluginPath, netconf, args.AsEnv()) +func ExecPluginWithoutResult(ctx context.Context, pluginPath string, netconf []byte, args CNIArgs, exec Exec) error { + if exec == nil { + exec = defaultExec + } + _, err := exec.ExecPlugin(ctx, pluginPath, netconf, args.AsEnv()) return err } @@ -73,7 +105,10 @@ func (e *PluginExec) WithoutResult(pluginPath string, netconf []byte, args CNIAr // 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 GetVersionInfo(ctx context.Context, pluginPath string, exec Exec) (version.PluginInfo, error) { + if exec == nil { + exec = defaultExec + } args := &Args{ Command: "VERSION", @@ -83,7 +118,7 @@ func (e *PluginExec) GetVersionInfo(pluginPath string) (version.PluginInfo, erro Path: "dummy", } stdin := []byte(fmt.Sprintf(`{"cniVersion":%q}`, version.Current())) - stdoutBytes, err := e.RawExec.ExecPlugin(pluginPath, stdin, args.AsEnv()) + stdoutBytes, err := exec.ExecPlugin(ctx, pluginPath, stdin, args.AsEnv()) if err != nil { if err.Error() == "unknown CNI_COMMAND: VERSION" { return version.PluginSupports("0.1.0"), nil @@ -91,5 +126,19 @@ func (e *PluginExec) GetVersionInfo(pluginPath string) (version.PluginInfo, erro return nil, err } - return e.VersionDecoder.Decode(stdoutBytes) + return exec.Decode(stdoutBytes) +} + +// DefaultExec is an object that implements the Exec interface which looks +// for and executes plugins from disk. +type DefaultExec struct { + *RawExec + version.PluginDecoder +} + +// DefaultExec implements the Exec interface +var _ Exec = &DefaultExec{} + +var defaultExec = &DefaultExec{ + RawExec: &RawExec{Stderr: os.Stderr}, } diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/os_unix.go b/vendor/github.com/containernetworking/cni/pkg/invoke/os_unix.go index bab5737a9..9bcfb4553 100644 --- a/vendor/github.com/containernetworking/cni/pkg/invoke/os_unix.go +++ b/vendor/github.com/containernetworking/cni/pkg/invoke/os_unix.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// +build darwin dragonfly freebsd linux netbsd opensbd solaris +// +build darwin dragonfly freebsd linux netbsd openbsd solaris package invoke diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec.go b/vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec.go index 93f1e75d9..ad8498ba2 100644 --- a/vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec.go +++ b/vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec.go @@ -16,6 +16,7 @@ package invoke import ( "bytes" + "context" "encoding/json" "fmt" "io" @@ -28,17 +29,13 @@ type RawExec struct { Stderr io.Writer } -func (e *RawExec) ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error) { +func (e *RawExec) ExecPlugin(ctx context.Context, pluginPath string, stdinData []byte, environ []string) ([]byte, error) { stdout := &bytes.Buffer{} - - c := exec.Cmd{ - Env: environ, - Path: pluginPath, - Args: []string{pluginPath}, - Stdin: bytes.NewBuffer(stdinData), - Stdout: stdout, - Stderr: e.Stderr, - } + c := exec.CommandContext(ctx, pluginPath) + c.Env = environ + c.Stdin = bytes.NewBuffer(stdinData) + c.Stdout = stdout + c.Stderr = e.Stderr if err := c.Run(); err != nil { return nil, pluginErr(err, stdout.Bytes()) } @@ -49,7 +46,9 @@ func (e *RawExec) ExecPlugin(pluginPath string, stdinData []byte, environ []stri func pluginErr(err error, output []byte) error { if _, ok := err.(*exec.ExitError); ok { emsg := types.Error{} - if perr := json.Unmarshal(output, &emsg); perr != nil { + if len(output) == 0 { + emsg.Msg = "netplugin failed with no error message" + } else if perr := json.Unmarshal(output, &emsg); perr != nil { emsg.Msg = fmt.Sprintf("netplugin failed but error parsing its diagnostic message %q: %v", string(output), perr) } return &emsg @@ -57,3 +56,7 @@ func pluginErr(err error, output []byte) error { return err } + +func (e *RawExec) FindInPath(plugin string, paths []string) (string, error) { + return FindInPath(plugin, paths) +} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/020/types.go b/vendor/github.com/containernetworking/cni/pkg/types/020/types.go index 2833aba78..53256167f 100644 --- a/vendor/github.com/containernetworking/cni/pkg/types/020/types.go +++ b/vendor/github.com/containernetworking/cni/pkg/types/020/types.go @@ -17,6 +17,7 @@ package types020 import ( "encoding/json" "fmt" + "io" "net" "os" @@ -73,11 +74,15 @@ func (r *Result) GetAsVersion(version string) (types.Result, error) { } func (r *Result) Print() error { + return r.PrintTo(os.Stdout) +} + +func (r *Result) PrintTo(writer io.Writer) error { data, err := json.MarshalIndent(r, "", " ") if err != nil { return err } - _, err = os.Stdout.Write(data) + _, err = writer.Write(data) return err } diff --git a/vendor/github.com/containernetworking/cni/pkg/types/current/types.go b/vendor/github.com/containernetworking/cni/pkg/types/current/types.go index caac92ba7..7267a2e6d 100644 --- a/vendor/github.com/containernetworking/cni/pkg/types/current/types.go +++ b/vendor/github.com/containernetworking/cni/pkg/types/current/types.go @@ -17,6 +17,7 @@ package current import ( "encoding/json" "fmt" + "io" "net" "os" @@ -24,9 +25,9 @@ import ( "github.com/containernetworking/cni/pkg/types/020" ) -const ImplementedSpecVersion string = "0.3.1" +const ImplementedSpecVersion string = "0.4.0" -var SupportedVersions = []string{"0.3.0", ImplementedSpecVersion} +var SupportedVersions = []string{"0.3.0", "0.3.1", ImplementedSpecVersion} func NewResult(data []byte) (types.Result, error) { result := &Result{} @@ -75,13 +76,9 @@ func convertFrom020(result types.Result) (*Result, error) { Gateway: oldResult.IP4.Gateway, }) for _, route := range oldResult.IP4.Routes { - gw := route.GW - if gw == nil { - gw = oldResult.IP4.Gateway - } newResult.Routes = append(newResult.Routes, &types.Route{ Dst: route.Dst, - GW: gw, + GW: route.GW, }) } } @@ -93,21 +90,13 @@ func convertFrom020(result types.Result) (*Result, error) { Gateway: oldResult.IP6.Gateway, }) for _, route := range oldResult.IP6.Routes { - gw := route.GW - if gw == nil { - gw = oldResult.IP6.Gateway - } newResult.Routes = append(newResult.Routes, &types.Route{ Dst: route.Dst, - GW: gw, + GW: route.GW, }) } } - if len(newResult.IPs) == 0 { - return nil, fmt.Errorf("cannot convert: no valid IP addresses") - } - return newResult, nil } @@ -196,7 +185,7 @@ func (r *Result) Version() string { func (r *Result) GetAsVersion(version string) (types.Result, error) { switch version { - case "0.3.0", ImplementedSpecVersion: + case "0.3.0", "0.3.1", ImplementedSpecVersion: r.CNIVersion = version return r, nil case types020.SupportedVersions[0], types020.SupportedVersions[1], types020.SupportedVersions[2]: @@ -206,11 +195,15 @@ func (r *Result) GetAsVersion(version string) (types.Result, error) { } func (r *Result) Print() error { + return r.PrintTo(os.Stdout) +} + +func (r *Result) PrintTo(writer io.Writer) error { data, err := json.MarshalIndent(r, "", " ") if err != nil { return err } - _, err = os.Stdout.Write(data) + _, err = writer.Write(data) return err } diff --git a/vendor/github.com/containernetworking/cni/pkg/types/types.go b/vendor/github.com/containernetworking/cni/pkg/types/types.go index 641275600..d0d11006a 100644 --- a/vendor/github.com/containernetworking/cni/pkg/types/types.go +++ b/vendor/github.com/containernetworking/cni/pkg/types/types.go @@ -18,6 +18,7 @@ import ( "encoding/json" "errors" "fmt" + "io" "net" "os" ) @@ -63,25 +64,31 @@ type NetConf struct { Name string `json:"name,omitempty"` Type string `json:"type,omitempty"` Capabilities map[string]bool `json:"capabilities,omitempty"` - IPAM struct { - Type string `json:"type,omitempty"` - } `json:"ipam,omitempty"` - DNS DNS `json:"dns"` + IPAM IPAM `json:"ipam,omitempty"` + DNS DNS `json:"dns"` + + RawPrevResult map[string]interface{} `json:"prevResult,omitempty"` + PrevResult Result `json:"-"` +} + +type IPAM struct { + Type string `json:"type,omitempty"` } // NetConfList describes an ordered list of networks. type NetConfList struct { CNIVersion string `json:"cniVersion,omitempty"` - Name string `json:"name,omitempty"` - Plugins []*NetConf `json:"plugins,omitempty"` + Name string `json:"name,omitempty"` + DisableCheck bool `json:"disableCheck,omitempty"` + Plugins []*NetConf `json:"plugins,omitempty"` } type ResultFactoryFunc func([]byte) (Result, error) // Result is an interface that provides the result of plugin execution type Result interface { - // The highest CNI specification result verison the result supports + // The highest CNI specification result version the result supports // without having to convert Version() string @@ -92,6 +99,9 @@ type Result interface { // Prints the result in JSON format to stdout Print() error + // Prints the result in JSON format to provided writer + PrintTo(writer io.Writer) error + // Returns a JSON string representation of the result String() string } @@ -167,7 +177,7 @@ func (r *Route) UnmarshalJSON(data []byte) error { return nil } -func (r *Route) MarshalJSON() ([]byte, error) { +func (r Route) MarshalJSON() ([]byte, error) { rt := route{ Dst: IPNet(r.Dst), GW: r.GW, diff --git a/vendor/github.com/containernetworking/cni/pkg/version/plugin.go b/vendor/github.com/containernetworking/cni/pkg/version/plugin.go index 8a4672810..1df427243 100644 --- a/vendor/github.com/containernetworking/cni/pkg/version/plugin.go +++ b/vendor/github.com/containernetworking/cni/pkg/version/plugin.go @@ -18,6 +18,8 @@ import ( "encoding/json" "fmt" "io" + "strconv" + "strings" ) // PluginInfo reports information about CNI versioning @@ -79,3 +81,64 @@ func (*PluginDecoder) Decode(jsonBytes []byte) (PluginInfo, error) { } return &info, nil } + +// ParseVersion parses a version string like "3.0.1" or "0.4.5" into major, +// minor, and micro numbers or returns an error +func ParseVersion(version string) (int, int, int, error) { + var major, minor, micro int + if version == "" { + return -1, -1, -1, fmt.Errorf("invalid version %q: the version is empty", version) + } + + parts := strings.Split(version, ".") + if len(parts) >= 4 { + return -1, -1, -1, fmt.Errorf("invalid version %q: too many parts", version) + } + + major, err := strconv.Atoi(parts[0]) + if err != nil { + return -1, -1, -1, fmt.Errorf("failed to convert major version part %q: %v", parts[0], err) + } + + if len(parts) >= 2 { + minor, err = strconv.Atoi(parts[1]) + if err != nil { + return -1, -1, -1, fmt.Errorf("failed to convert minor version part %q: %v", parts[1], err) + } + } + + if len(parts) >= 3 { + micro, err = strconv.Atoi(parts[2]) + if err != nil { + return -1, -1, -1, fmt.Errorf("failed to convert micro version part %q: %v", parts[2], err) + } + } + + return major, minor, micro, nil +} + +// GreaterThanOrEqualTo takes two string versions, parses them into major/minor/micro +// numbers, and compares them to determine whether the first version is greater +// than or equal to the second +func GreaterThanOrEqualTo(version, otherVersion string) (bool, error) { + firstMajor, firstMinor, firstMicro, err := ParseVersion(version) + if err != nil { + return false, err + } + + secondMajor, secondMinor, secondMicro, err := ParseVersion(otherVersion) + if err != nil { + return false, err + } + + if firstMajor > secondMajor { + return true, nil + } else if firstMajor == secondMajor { + if firstMinor > secondMinor { + return true, nil + } else if firstMinor == secondMinor && firstMicro >= secondMicro { + return true, nil + } + } + return false, nil +} diff --git a/vendor/github.com/containernetworking/cni/pkg/version/version.go b/vendor/github.com/containernetworking/cni/pkg/version/version.go index efe8ea871..8f3508e61 100644 --- a/vendor/github.com/containernetworking/cni/pkg/version/version.go +++ b/vendor/github.com/containernetworking/cni/pkg/version/version.go @@ -15,6 +15,7 @@ package version import ( + "encoding/json" "fmt" "github.com/containernetworking/cni/pkg/types" @@ -24,7 +25,7 @@ import ( // Current reports the version of the CNI spec implemented by this library func Current() string { - return "0.3.1" + return "0.4.0" } // Legacy PluginInfo describes a plugin that is backwards compatible with the @@ -35,7 +36,7 @@ func Current() string { // 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 All = PluginSupports("0.1.0", "0.2.0", "0.3.0", "0.3.1") +var All = PluginSupports("0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0") var resultFactories = []struct { supportedVersions []string @@ -59,3 +60,24 @@ func NewResult(version string, resultBytes []byte) (types.Result, error) { return nil, fmt.Errorf("unsupported CNI result version %q", version) } + +// ParsePrevResult parses a prevResult in a NetConf structure and sets +// the NetConf's PrevResult member to the parsed Result object. +func ParsePrevResult(conf *types.NetConf) error { + if conf.RawPrevResult == nil { + return nil + } + + resultBytes, err := json.Marshal(conf.RawPrevResult) + if err != nil { + return fmt.Errorf("could not serialize prevResult: %v", err) + } + + conf.RawPrevResult = nil + conf.PrevResult, err = NewResult(conf.CNIVersion, resultBytes) + if err != nil { + return fmt.Errorf("could not parse prevResult: %v", err) + } + + return nil +}