From ddbf22f7f96003460b0ae5b434fbdb97a77cd254 Mon Sep 17 00:00:00 2001 From: Michael Cambria Date: Mon, 5 Nov 2018 15:33:56 -0500 Subject: [PATCH] Vendor github.com/containernetworking/cni libcni and pkg file needed for CHECK Update plugins/tests to deal with changes made to this vendor'ed code --- pkg/ipam/ipam.go | 5 +- .../meta/bandwidth/bandwidth_linux_test.go | 5 +- plugins/meta/flannel/flannel.go | 3 +- plugins/meta/flannel/flannel_linux.go | 3 +- plugins/meta/portmap/portmap_integ_test.go | 5 +- .../containernetworking/cni/libcni/api.go | 265 ++++++++++++------ .../containernetworking/cni/libcni/conf.go | 15 +- .../cni/pkg/invoke/delegate.go | 58 ++-- .../cni/pkg/invoke/exec.go | 15 +- .../cni/pkg/invoke/raw_exec.go | 17 +- .../containernetworking/cni/pkg/skel/skel.go | 81 +++--- .../cni/pkg/types/current/types.go | 4 - .../cni/pkg/types/types.go | 8 +- .../cni/pkg/version/version.go | 22 ++ 14 files changed, 329 insertions(+), 177 deletions(-) diff --git a/pkg/ipam/ipam.go b/pkg/ipam/ipam.go index 5ee713fd..967ad7e4 100644 --- a/pkg/ipam/ipam.go +++ b/pkg/ipam/ipam.go @@ -15,14 +15,15 @@ package ipam import ( + "context" "github.com/containernetworking/cni/pkg/invoke" "github.com/containernetworking/cni/pkg/types" ) func ExecAdd(plugin string, netconf []byte) (types.Result, error) { - return invoke.DelegateAdd(plugin, netconf, nil) + return invoke.DelegateAdd(context.TODO(), plugin, netconf, nil) } func ExecDel(plugin string, netconf []byte) error { - return invoke.DelegateDel(plugin, netconf, nil) + return invoke.DelegateDel(context.TODO(), plugin, netconf, nil) } diff --git a/plugins/meta/bandwidth/bandwidth_linux_test.go b/plugins/meta/bandwidth/bandwidth_linux_test.go index d877b740..c36b31e1 100644 --- a/plugins/meta/bandwidth/bandwidth_linux_test.go +++ b/plugins/meta/bandwidth/bandwidth_linux_test.go @@ -15,6 +15,7 @@ package main import ( + "context" "encoding/json" "fmt" "net" @@ -625,7 +626,7 @@ var _ = Describe("bandwidth test", func() { defer GinkgoRecover() containerWithTbfRes, _, err = testutils.CmdAdd(containerWithTbfNS.Path(), "dummy", containerWithTbfIFName, []byte(ptpConf), func() error { - r, err := invoke.DelegateAdd("ptp", []byte(ptpConf), nil) + r, err := invoke.DelegateAdd(context.TODO(), "ptp", []byte(ptpConf), nil) Expect(r.Print()).To(Succeed()) return err @@ -633,7 +634,7 @@ var _ = Describe("bandwidth test", func() { Expect(err).NotTo(HaveOccurred()) containerWithoutTbfRes, _, err = testutils.CmdAdd(containerWithoutTbfNS.Path(), "dummy2", containerWithoutTbfIFName, []byte(ptpConf), func() error { - r, err := invoke.DelegateAdd("ptp", []byte(ptpConf), nil) + r, err := invoke.DelegateAdd(context.TODO(), "ptp", []byte(ptpConf), nil) Expect(r.Print()).To(Succeed()) return err diff --git a/plugins/meta/flannel/flannel.go b/plugins/meta/flannel/flannel.go index f7d1b957..d0004237 100644 --- a/plugins/meta/flannel/flannel.go +++ b/plugins/meta/flannel/flannel.go @@ -20,6 +20,7 @@ package main import ( "bufio" + "context" "encoding/json" "fmt" "io/ioutil" @@ -160,7 +161,7 @@ func delegateAdd(cid, dataDir string, netconf map[string]interface{}) error { return err } - result, err := invoke.DelegateAdd(netconf["type"].(string), netconfBytes, nil) + result, err := invoke.DelegateAdd(context.TODO(), netconf["type"].(string), netconfBytes, nil) if err != nil { return err } diff --git a/plugins/meta/flannel/flannel_linux.go b/plugins/meta/flannel/flannel_linux.go index f89a5549..b4ee5c09 100644 --- a/plugins/meta/flannel/flannel_linux.go +++ b/plugins/meta/flannel/flannel_linux.go @@ -19,6 +19,7 @@ package main import ( + "context" "encoding/json" "fmt" "github.com/containernetworking/cni/pkg/invoke" @@ -82,5 +83,5 @@ func doCmdDel(args *skel.CmdArgs, n *NetConf) error { return fmt.Errorf("failed to parse netconf: %v", err) } - return invoke.DelegateDel(nc.Type, netconfBytes, nil) + return invoke.DelegateDel(context.TODO(), nc.Type, netconfBytes, nil) } diff --git a/plugins/meta/portmap/portmap_integ_test.go b/plugins/meta/portmap/portmap_integ_test.go index 22c1e1f7..efdb47a6 100644 --- a/plugins/meta/portmap/portmap_integ_test.go +++ b/plugins/meta/portmap/portmap_integ_test.go @@ -16,6 +16,7 @@ package main import ( "bytes" + "context" "fmt" "math/rand" "net" @@ -121,7 +122,7 @@ var _ = Describe("portmap integration tests", func() { return nil } netDeleted = true - return cniConf.DelNetworkList(configList, &runtimeConfig) + return cniConf.DelNetworkList(context.TODO(), configList, &runtimeConfig) } // we'll also manually check the iptables chains @@ -130,7 +131,7 @@ var _ = Describe("portmap integration tests", func() { dnatChainName := genDnatChain("cni-portmap-unit-test", runtimeConfig.ContainerID).name // Create the network - resI, err := cniConf.AddNetworkList(configList, &runtimeConfig) + resI, err := cniConf.AddNetworkList(context.TODO(), configList, &runtimeConfig) Expect(err).NotTo(HaveOccurred()) defer deleteNetwork() diff --git a/vendor/github.com/containernetworking/cni/libcni/api.go b/vendor/github.com/containernetworking/cni/libcni/api.go index d494e43d..dd3e1680 100644 --- a/vendor/github.com/containernetworking/cni/libcni/api.go +++ b/vendor/github.com/containernetworking/cni/libcni/api.go @@ -15,6 +15,7 @@ package libcni import ( + "context" "encoding/json" "fmt" "io/ioutil" @@ -57,20 +58,25 @@ type NetworkConfig struct { } type NetworkConfigList struct { - Name string - CNIVersion string - Plugins []*NetworkConfig - Bytes []byte + Name string + CNIVersion string + DisableCheck bool + Plugins []*NetworkConfig + Bytes []byte } type CNI interface { - AddNetworkList(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error) - GetNetworkList(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error) - DelNetworkList(net *NetworkConfigList, rt *RuntimeConf) error + AddNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) (types.Result, error) + CheckNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error + DelNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error - AddNetwork(net *NetworkConfig, rt *RuntimeConf) (types.Result, error) - GetNetwork(net *NetworkConfig, rt *RuntimeConf) (types.Result, error) - DelNetwork(net *NetworkConfig, rt *RuntimeConf) error + AddNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) (types.Result, error) + CheckNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error + DelNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error + GetNetworkCachedResult(net *NetworkConfig, rt *RuntimeConf) (types.Result, error) + + ValidateNetworkList(ctx context.Context, net *NetworkConfigList) ([]string, error) + ValidateNetwork(ctx context.Context, net *NetworkConfig) ([]string, error) } type CNIConfig struct { @@ -158,40 +164,12 @@ func (c *CNIConfig) ensureExec() invoke.Exec { return c.exec } -func (c *CNIConfig) addOrGetNetwork(command, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) (types.Result, error) { - c.ensureExec() - pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path) - if err != nil { - return nil, err - } - - newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt) - if err != nil { - return nil, err - } - - return invoke.ExecPluginWithResult(pluginPath, newConf.Bytes, c.args(command, rt), c.exec) -} - -// Note that only GET requests should pass an initial prevResult -func (c *CNIConfig) addOrGetNetworkList(command string, prevResult types.Result, list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) { - var err error - for _, net := range list.Plugins { - prevResult, err = c.addOrGetNetwork(command, list.Name, list.CNIVersion, net, prevResult, rt) - if err != nil { - return nil, err - } - } - - return prevResult, nil -} - func getResultCacheFilePath(netName string, rt *RuntimeConf) string { cacheDir := rt.CacheDir if cacheDir == "" { cacheDir = CacheDir } - return filepath.Join(cacheDir, "results", fmt.Sprintf("%s-%s", netName, rt.ContainerID)) + return filepath.Join(cacheDir, "results", fmt.Sprintf("%s-%s-%s", netName, rt.ContainerID, rt.IfName)) } func setCachedResult(result types.Result, netName string, rt *RuntimeConf) error { @@ -243,37 +221,52 @@ func getCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result, return result, err } -// AddNetworkList executes a sequence of plugins with the ADD command -func (c *CNIConfig) AddNetworkList(list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) { - result, err := c.addOrGetNetworkList("ADD", nil, list, rt) +// GetNetworkListCachedResult returns the cached Result of the previous +// previous AddNetworkList() operation for a network list, or an error. +func (c *CNIConfig) GetNetworkListCachedResult(list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) { + return getCachedResult(list.Name, list.CNIVersion, rt) +} + +// GetNetworkCachedResult returns the cached Result of the previous +// previous AddNetwork() operation for a network, or an error. +func (c *CNIConfig) GetNetworkCachedResult(net *NetworkConfig, rt *RuntimeConf) (types.Result, error) { + return getCachedResult(net.Network.Name, net.Network.CNIVersion, rt) +} + +func (c *CNIConfig) addNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) (types.Result, error) { + c.ensureExec() + pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path) if err != nil { return nil, err } + newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt) + if err != nil { + return nil, err + } + + return invoke.ExecPluginWithResult(ctx, pluginPath, newConf.Bytes, c.args("ADD", rt), c.exec) +} + +// AddNetworkList executes a sequence of plugins with the ADD command +func (c *CNIConfig) AddNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) { + var err error + var result types.Result + for _, net := range list.Plugins { + result, err = c.addNetwork(ctx, list.Name, list.CNIVersion, net, result, rt) + if err != nil { + return nil, err + } + } + if err = setCachedResult(result, list.Name, rt); err != nil { - return nil, fmt.Errorf("failed to set network '%s' cached result: %v", list.Name, err) + return nil, fmt.Errorf("failed to set network %q cached result: %v", list.Name, err) } return result, nil } -// GetNetworkList executes a sequence of plugins with the GET command -func (c *CNIConfig) GetNetworkList(list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) { - // GET was added in CNI spec version 0.4.0 and higher - if gtet, err := version.GreaterThanOrEqualTo(list.CNIVersion, "0.4.0"); err != nil { - return nil, err - } else if !gtet { - return nil, fmt.Errorf("configuration version %q does not support the GET command", list.CNIVersion) - } - - cachedResult, err := getCachedResult(list.Name, list.CNIVersion, rt) - if err != nil { - return nil, fmt.Errorf("failed to get network '%s' cached result: %v", list.Name, err) - } - return c.addOrGetNetworkList("GET", cachedResult, list, rt) -} - -func (c *CNIConfig) delNetwork(name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) error { +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 { @@ -285,11 +278,53 @@ func (c *CNIConfig) delNetwork(name, cniVersion string, net *NetworkConfig, prev return err } - return invoke.ExecPluginWithoutResult(pluginPath, newConf.Bytes, c.args("DEL", rt), c.exec) + return invoke.ExecPluginWithoutResult(ctx, pluginPath, newConf.Bytes, c.args("CHECK", rt), c.exec) +} + +// 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 + } + } + + 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(list *NetworkConfigList, rt *RuntimeConf) error { +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 @@ -298,13 +333,13 @@ func (c *CNIConfig) DelNetworkList(list *NetworkConfigList, rt *RuntimeConf) err } else if gtet { cachedResult, err = getCachedResult(list.Name, list.CNIVersion, rt) if err != nil { - return fmt.Errorf("failed to get network '%s' cached result: %v", list.Name, err) + 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(list.Name, list.CNIVersion, net, cachedResult, rt); err != nil { + if err := c.delNetwork(ctx, list.Name, list.CNIVersion, net, cachedResult, rt); err != nil { return err } } @@ -314,37 +349,37 @@ func (c *CNIConfig) DelNetworkList(list *NetworkConfigList, rt *RuntimeConf) err } // AddNetwork executes the plugin with the ADD command -func (c *CNIConfig) AddNetwork(net *NetworkConfig, rt *RuntimeConf) (types.Result, error) { - result, err := c.addOrGetNetwork("ADD", net.Network.Name, net.Network.CNIVersion, net, nil, rt) +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 } if err = setCachedResult(result, net.Network.Name, rt); err != nil { - return nil, fmt.Errorf("failed to set network '%s' cached result: %v", net.Network.Name, err) + return nil, fmt.Errorf("failed to set network %q cached result: %v", net.Network.Name, err) } return result, nil } -// GetNetwork executes the plugin with the GET command -func (c *CNIConfig) GetNetwork(net *NetworkConfig, rt *RuntimeConf) (types.Result, error) { - // GET was added in CNI spec version 0.4.0 and higher +// 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 nil, err + return err } else if !gtet { - return nil, fmt.Errorf("configuration version %q does not support the GET command", net.Network.CNIVersion) + 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 nil, fmt.Errorf("failed to get network '%s' cached result: %v", net.Network.Name, err) + return fmt.Errorf("failed to get network %q cached result: %v", net.Network.Name, err) } - return c.addOrGetNetwork("GET", net.Network.Name, net.Network.CNIVersion, net, cachedResult, rt) + 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 { +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 @@ -353,27 +388,99 @@ func (c *CNIConfig) DelNetwork(net *NetworkConfig, rt *RuntimeConf) error { } else if gtet { cachedResult, err = getCachedResult(net.Network.Name, net.Network.CNIVersion, rt) if err != nil { - return fmt.Errorf("failed to get network '%s' cached result: %v", net.Network.Name, err) + return fmt.Errorf("failed to get network %q cached result: %v", net.Network.Name, err) } } - if err := c.delNetwork(net.Network.Name, net.Network.CNIVersion, net, cachedResult, rt); err != nil { + 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 + } + + vi, err := invoke.GetVersionInfo(ctx, pluginPath, c.exec) + if err != nil { + return err + } + 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) { +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, c.exec) + 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 9834d715..ea56c509 100644 --- a/vendor/github.com/containernetworking/cni/libcni/conf.go +++ b/vendor/github.com/containernetworking/cni/libcni/conf.go @@ -83,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/delegate.go b/vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go index 21efdf80..30b4672f 100644 --- a/vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go +++ b/vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go @@ -15,6 +15,7 @@ package invoke import ( + "context" "fmt" "os" "path/filepath" @@ -22,54 +23,53 @@ import ( "github.com/containernetworking/cni/pkg/types" ) -func delegateAddOrGet(command, delegatePlugin string, netconf []byte, exec Exec) (types.Result, error) { +func delegateCommon(expectedCommand, delegatePlugin string, exec Exec) (string, Exec, error) { if exec == nil { exec = defaultExec } + if os.Getenv("CNI_COMMAND") != expectedCommand { + return "", nil, fmt.Errorf("CNI_COMMAND is not " + expectedCommand) + } + paths := filepath.SplitList(os.Getenv("CNI_PATH")) pluginPath, err := exec.FindInPath(delegatePlugin, paths) if err != nil { - return nil, err + return "", nil, err } - return ExecPluginWithResult(pluginPath, netconf, ArgsFromEnv(), exec) + return pluginPath, exec, nil } // DelegateAdd calls the given delegate plugin with the CNI ADD action and // JSON configuration -func DelegateAdd(delegatePlugin string, netconf []byte, exec Exec) (types.Result, error) { - if os.Getenv("CNI_COMMAND") != "ADD" { - return nil, fmt.Errorf("CNI_COMMAND is not ADD") +func DelegateAdd(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) (types.Result, error) { + pluginPath, realExec, err := delegateCommon("ADD", delegatePlugin, exec) + if err != nil { + return nil, err } - return delegateAddOrGet("ADD", delegatePlugin, netconf, exec) + + return ExecPluginWithResult(ctx, pluginPath, netconf, ArgsFromEnv(), realExec) } -// DelegateGet calls the given delegate plugin with the CNI GET action and +// DelegateCheck calls the given delegate plugin with the CNI CHECK action and // JSON configuration -func DelegateGet(delegatePlugin string, netconf []byte, exec Exec) (types.Result, error) { - if os.Getenv("CNI_COMMAND") != "GET" { - return nil, fmt.Errorf("CNI_COMMAND is not GET") - } - return delegateAddOrGet("GET", delegatePlugin, netconf, exec) -} - -// DelegateDel calls the given delegate plugin with the CNI DEL action and -// JSON configuration -func DelegateDel(delegatePlugin string, netconf []byte, exec Exec) error { - if exec == nil { - exec = defaultExec - } - - if os.Getenv("CNI_COMMAND") != "DEL" { - return fmt.Errorf("CNI_COMMAND is not DEL") - } - - paths := filepath.SplitList(os.Getenv("CNI_PATH")) - pluginPath, err := exec.FindInPath(delegatePlugin, paths) +func DelegateCheck(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error { + pluginPath, realExec, err := delegateCommon("CHECK", delegatePlugin, exec) if err != nil { return err } - return ExecPluginWithoutResult(pluginPath, netconf, ArgsFromEnv(), exec) + return ExecPluginWithoutResult(ctx, pluginPath, netconf, ArgsFromEnv(), 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("DEL", delegatePlugin, exec) + if err != nil { + return err + } + + return ExecPluginWithoutResult(ctx, pluginPath, netconf, ArgsFromEnv(), realExec) } diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/exec.go b/vendor/github.com/containernetworking/cni/pkg/invoke/exec.go index cf019d3a..8e6d30b8 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" @@ -26,7 +27,7 @@ import ( // 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(pluginPath string, stdinData []byte, environ []string) ([]byte, error) + 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) } @@ -72,12 +73,12 @@ type Exec interface { // return "", fmt.Errorf("failed to find plugin %s in paths %v", plugin, paths) //} -func ExecPluginWithResult(pluginPath string, netconf []byte, args CNIArgs, exec Exec) (types.Result, error) { +func ExecPluginWithResult(ctx context.Context, pluginPath string, netconf []byte, args CNIArgs, exec Exec) (types.Result, error) { if exec == nil { exec = defaultExec } - stdoutBytes, err := exec.ExecPlugin(pluginPath, netconf, args.AsEnv()) + stdoutBytes, err := exec.ExecPlugin(ctx, pluginPath, netconf, args.AsEnv()) if err != nil { return nil, err } @@ -92,11 +93,11 @@ func ExecPluginWithResult(pluginPath string, netconf []byte, args CNIArgs, exec return version.NewResult(confVersion, stdoutBytes) } -func ExecPluginWithoutResult(pluginPath string, netconf []byte, args CNIArgs, exec Exec) error { +func ExecPluginWithoutResult(ctx context.Context, pluginPath string, netconf []byte, args CNIArgs, exec Exec) error { if exec == nil { exec = defaultExec } - _, err := exec.ExecPlugin(pluginPath, netconf, args.AsEnv()) + _, err := exec.ExecPlugin(ctx, pluginPath, netconf, args.AsEnv()) return err } @@ -104,7 +105,7 @@ func ExecPluginWithoutResult(pluginPath string, netconf []byte, args CNIArgs, ex // 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 GetVersionInfo(pluginPath string, exec Exec) (version.PluginInfo, error) { +func GetVersionInfo(ctx context.Context, pluginPath string, exec Exec) (version.PluginInfo, error) { if exec == nil { exec = defaultExec } @@ -117,7 +118,7 @@ func GetVersionInfo(pluginPath string, exec Exec) (version.PluginInfo, error) { Path: "dummy", } stdin := []byte(fmt.Sprintf(`{"cniVersion":%q}`, version.Current())) - stdoutBytes, err := exec.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 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 a598f09c..e5b86634 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()) } diff --git a/vendor/github.com/containernetworking/cni/pkg/skel/skel.go b/vendor/github.com/containernetworking/cni/pkg/skel/skel.go index e565c85d..d6062a11 100644 --- a/vendor/github.com/containernetworking/cni/pkg/skel/skel.go +++ b/vendor/github.com/containernetworking/cni/pkg/skel/skel.go @@ -24,6 +24,7 @@ import ( "io/ioutil" "log" "os" + "strings" "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/version" @@ -52,6 +53,15 @@ type dispatcher struct { type reqForCmdEntry map[string]bool +// internal only error to indicate lack of required environment variables +type missingEnvError struct { + msg string +} + +func (e missingEnvError) Error() string { + return e.msg +} + func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, error) { var cmd, contID, netns, ifName, args, path string @@ -64,71 +74,71 @@ func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, error) { "CNI_COMMAND", &cmd, reqForCmdEntry{ - "ADD": true, - "GET": true, - "DEL": true, + "ADD": true, + "CHECK": true, + "DEL": true, }, }, { "CNI_CONTAINERID", &contID, reqForCmdEntry{ - "ADD": true, - "GET": true, - "DEL": true, + "ADD": true, + "CHECK": true, + "DEL": true, }, }, { "CNI_NETNS", &netns, reqForCmdEntry{ - "ADD": true, - "GET": true, - "DEL": false, + "ADD": true, + "CHECK": true, + "DEL": false, }, }, { "CNI_IFNAME", &ifName, reqForCmdEntry{ - "ADD": true, - "GET": true, - "DEL": true, + "ADD": true, + "CHECK": true, + "DEL": true, }, }, { "CNI_ARGS", &args, reqForCmdEntry{ - "ADD": false, - "GET": false, - "DEL": false, + "ADD": false, + "CHECK": false, + "DEL": false, }, }, { "CNI_PATH", &path, reqForCmdEntry{ - "ADD": true, - "GET": true, - "DEL": true, + "ADD": true, + "CHECK": true, + "DEL": true, }, }, } - argsMissing := false + argsMissing := make([]string, 0) for _, v := range vars { *v.val = t.Getenv(v.name) if *v.val == "" { if v.reqForCmd[cmd] || v.name == "CNI_COMMAND" { - fmt.Fprintf(t.Stderr, "%v env variable missing\n", v.name) - argsMissing = true + argsMissing = append(argsMissing, v.name) } } } - if argsMissing { - return "", nil, fmt.Errorf("required env variables missing") + if len(argsMissing) > 0 { + joined := strings.Join(argsMissing, ",") + return "", nil, missingEnvError{fmt.Sprintf("required env variables [%s] missing", joined)} } if cmd == "VERSION" { @@ -188,12 +198,13 @@ func validateConfig(jsonBytes []byte) error { return nil } -func (t *dispatcher) pluginMain(cmdAdd, cmdGet, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo, about string) *types.Error { +func (t *dispatcher) pluginMain(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo, about string) *types.Error { cmd, cmdArgs, err := t.getCmdArgsFromEnv() if err != nil { // Print the about string to stderr when no command is set - if t.Getenv("CNI_COMMAND") == "" && about != "" { + if _, ok := err.(missingEnvError); ok && t.Getenv("CNI_COMMAND") == "" && about != "" { fmt.Fprintln(t.Stderr, about) + return nil } return createTypedError(err.Error()) } @@ -208,7 +219,7 @@ func (t *dispatcher) pluginMain(cmdAdd, cmdGet, cmdDel func(_ *CmdArgs) error, v switch cmd { case "ADD": err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdAdd) - case "GET": + case "CHECK": configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData) if err != nil { return createTypedError(err.Error()) @@ -218,7 +229,7 @@ func (t *dispatcher) pluginMain(cmdAdd, cmdGet, cmdDel func(_ *CmdArgs) error, v } else if !gtet { return &types.Error{ Code: types.ErrIncompatibleCNIVersion, - Msg: "config version does not allow GET", + Msg: "config version does not allow CHECK", } } for _, pluginVersion := range versionInfo.SupportedVersions() { @@ -226,7 +237,7 @@ func (t *dispatcher) pluginMain(cmdAdd, cmdGet, cmdDel func(_ *CmdArgs) error, v if err != nil { return createTypedError(err.Error()) } else if gtet { - if err := t.checkVersionAndCall(cmdArgs, versionInfo, cmdGet); err != nil { + if err := t.checkVersionAndCall(cmdArgs, versionInfo, cmdCheck); err != nil { return createTypedError(err.Error()) } return nil @@ -234,7 +245,7 @@ func (t *dispatcher) pluginMain(cmdAdd, cmdGet, cmdDel func(_ *CmdArgs) error, v } return &types.Error{ Code: types.ErrIncompatibleCNIVersion, - Msg: "plugin version does not allow GET", + Msg: "plugin version does not allow CHECK", } case "DEL": err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdDel) @@ -255,7 +266,7 @@ func (t *dispatcher) pluginMain(cmdAdd, cmdGet, cmdDel func(_ *CmdArgs) error, v } // PluginMainWithError is the core "main" for a plugin. It accepts -// callback functions for add, get, and del CNI commands and returns an error. +// callback functions for add, check, and del CNI commands and returns an error. // // The caller must also specify what CNI spec versions the plugin supports. // @@ -266,13 +277,13 @@ func (t *dispatcher) pluginMain(cmdAdd, cmdGet, cmdDel func(_ *CmdArgs) error, v // // To let this package automatically handle errors and call os.Exit(1) for you, // use PluginMain() instead. -func PluginMainWithError(cmdAdd, cmdGet, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo, about string) *types.Error { +func PluginMainWithError(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo, about string) *types.Error { return (&dispatcher{ Getenv: os.Getenv, Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr, - }).pluginMain(cmdAdd, cmdGet, cmdDel, versionInfo, about) + }).pluginMain(cmdAdd, cmdCheck, cmdDel, versionInfo, about) } // PluginMain is the core "main" for a plugin which includes automatic error handling. @@ -282,12 +293,12 @@ func PluginMainWithError(cmdAdd, cmdGet, cmdDel func(_ *CmdArgs) error, versionI // The caller can specify an "about" string, which is printed on stderr // when no CNI_COMMAND is specified. The reccomended output is "CNI plugin v" // -// When an error occurs in either cmdAdd, cmdGet, or cmdDel, PluginMain will print the error +// When an error occurs in either cmdAdd, cmdCheck, or cmdDel, PluginMain will print the error // as JSON to stdout and call os.Exit(1). // // To have more control over error handling, use PluginMainWithError() instead. -func PluginMain(cmdAdd, cmdGet, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo, about string) { - if e := PluginMainWithError(cmdAdd, cmdGet, cmdDel, versionInfo, about); e != nil { +func PluginMain(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo, about string) { + if e := PluginMainWithError(cmdAdd, cmdCheck, cmdDel, versionInfo, about); e != nil { if err := e.Print(); err != nil { log.Print("Error writing error JSON to stdout: ", 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 92980c1a..71a634fe 100644 --- a/vendor/github.com/containernetworking/cni/pkg/types/current/types.go +++ b/vendor/github.com/containernetworking/cni/pkg/types/current/types.go @@ -104,10 +104,6 @@ func convertFrom020(result types.Result) (*Result, error) { } } - if len(newResult.IPs) == 0 { - return nil, fmt.Errorf("cannot convert: no valid IP addresses") - } - return newResult, nil } diff --git a/vendor/github.com/containernetworking/cni/pkg/types/types.go b/vendor/github.com/containernetworking/cni/pkg/types/types.go index 4684a320..5e49f117 100644 --- a/vendor/github.com/containernetworking/cni/pkg/types/types.go +++ b/vendor/github.com/containernetworking/cni/pkg/types/types.go @@ -65,6 +65,9 @@ type NetConf struct { Capabilities map[string]bool `json:"capabilities,omitempty"` IPAM IPAM `json:"ipam,omitempty"` DNS DNS `json:"dns"` + + RawPrevResult map[string]interface{} `json:"prevResult,omitempty"` + PrevResult Result `json:"-"` } type IPAM struct { @@ -75,8 +78,9 @@ type IPAM struct { 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) diff --git a/vendor/github.com/containernetworking/cni/pkg/version/version.go b/vendor/github.com/containernetworking/cni/pkg/version/version.go index c8e46d55..8f3508e6 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" @@ -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 +}