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
}
ValidateNetwork checks that a configuration is reasonably valid.
It uses the same logic as ValidateNetworkList)
Returns a list of capabilities
package libcni
import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"github.com/containernetworking/cni/pkg/invoke"
	"github.com/containernetworking/cni/pkg/types"
	"github.com/containernetworking/cni/pkg/types/create"
	"github.com/containernetworking/cni/pkg/utils"
	"github.com/containernetworking/cni/pkg/version"
)
var (
	CacheDir = "/var/lib/cni"
)
const (
	CNICacheV1 = "cniCacheV1"
)
type RuntimeConf struct {
	ContainerID string
	NetNS       string
	IfName      string
	Args        [][2]string
	CapabilityArgs map[string]interface{}
	CacheDir string
}
type NetworkConfig struct {
	Network *types.NetConf
	Bytes   []byte
}
type NetworkConfigList struct {
	Name         string
	CNIVersion   string
	DisableCheck bool
	Plugins      []*NetworkConfig
	Bytes        []byte
}
type CNI interface {
	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
	GetNetworkListCachedResult(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
	GetNetworkListCachedConfig(net *NetworkConfigList, rt *RuntimeConf) ([]byte, *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)
	GetNetworkCachedConfig(net *NetworkConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error)
	ValidateNetworkList(ctx context.Context, net *NetworkConfigList) ([]string, error)
	ValidateNetwork(ctx context.Context, net *NetworkConfig) ([]string, error)
}
type CNIConfig struct {
	Path     []string
	exec     invoke.Exec
	cacheDir string
}
var _ CNI = &CNIConfig{}
func NewCNIConfig(path []string, exec invoke.Exec) *CNIConfig {
	return NewCNIConfigWithCacheDir(path, "", exec)
}
func NewCNIConfigWithCacheDir(path []string, cacheDir string, exec invoke.Exec) *CNIConfig {
	return &CNIConfig{
		Path:     path,
		cacheDir: cacheDir,
		exec:     exec,
	}
}
func buildOneConfig(name, cniVersion string, orig *NetworkConfig, prevResult types.Result, rt *RuntimeConf) (*NetworkConfig, error) {
	var err error
	inject := map[string]interface{}{
		"name":       name,
		"cniVersion": cniVersion,
	}
	if prevResult != nil {
		inject["prevResult"] = prevResult
	}
	orig, err = InjectConf(orig, inject)
	if err != nil {
		return nil, err
	}
	return injectRuntimeConfig(orig, rt)
}
func injectRuntimeConfig(orig *NetworkConfig, rt *RuntimeConf) (*NetworkConfig, error) {
	var err error
	rc := make(map[string]interface{})
	for capability, supported := range orig.Network.Capabilities {
		if !supported {
			continue
		}
		if data, ok := rt.CapabilityArgs[capability]; ok {
			rc[capability] = data
		}
	}
	if len(rc) > 0 {
		orig, err = InjectConf(orig, map[string]interface{}{"runtimeConfig": rc})
		if err != nil {
			return nil, err
		}
	}
	return orig, nil
}
func (c *CNIConfig) ensureExec() invoke.Exec {
	if c.exec == nil {
		c.exec = &invoke.DefaultExec{
			RawExec:       &invoke.RawExec{Stderr: os.Stderr},
			PluginDecoder: version.PluginDecoder{},
		}
	}
	return c.exec
}
type cachedInfo struct {
	Kind           string                 `json:"kind"`
	ContainerID    string                 `json:"containerId"`
	Config         []byte                 `json:"config"`
	IfName         string                 `json:"ifName"`
	NetworkName    string                 `json:"networkName"`
	CniArgs        [][2]string            `json:"cniArgs,omitempty"`
	CapabilityArgs map[string]interface{} `json:"capabilityArgs,omitempty"`
	RawResult      map[string]interface{} `json:"result,omitempty"`
	Result         types.Result           `json:"-"`
}
func (c *CNIConfig) getCacheDir(rt *RuntimeConf) string {
	if c.cacheDir != "" {
		return c.cacheDir
	}
	if rt.CacheDir != "" {
		return rt.CacheDir
	}
	return CacheDir
}
func (c *CNIConfig) getCacheFilePath(netName string, rt *RuntimeConf) (string, error) {
	if netName == "" || rt.ContainerID == "" || rt.IfName == "" {
		return "", fmt.Errorf("cache file path requires network name (%q), container ID (%q), and interface name (%q)", netName, rt.ContainerID, rt.IfName)
	}
	return filepath.Join(c.getCacheDir(rt), "results", fmt.Sprintf("%s-%s-%s", netName, rt.ContainerID, rt.IfName)), nil
}
func (c *CNIConfig) cacheAdd(result types.Result, config []byte, netName string, rt *RuntimeConf) error {
	cached := cachedInfo{
		Kind:           CNICacheV1,
		ContainerID:    rt.ContainerID,
		Config:         config,
		IfName:         rt.IfName,
		NetworkName:    netName,
		CniArgs:        rt.Args,
		CapabilityArgs: rt.CapabilityArgs,
	}
	data, err := json.Marshal(result)
	if err != nil {
		return err
	}
	err = json.Unmarshal(data, &cached.RawResult)
	if err != nil {
		return err
	}
	newBytes, err := json.Marshal(&cached)
	if err != nil {
		return err
	}
	fname, err := c.getCacheFilePath(netName, rt)
	if err != nil {
		return err
	}
	if err := os.MkdirAll(filepath.Dir(fname), 0700); err != nil {
		return err
	}
	return ioutil.WriteFile(fname, newBytes, 0600)
}
func (c *CNIConfig) cacheDel(netName string, rt *RuntimeConf) error {
	fname, err := c.getCacheFilePath(netName, rt)
	if err != nil {
		return nil
	}
	return os.Remove(fname)
}
func (c *CNIConfig) getCachedConfig(netName string, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
	var bytes []byte
	fname, err := c.getCacheFilePath(netName, rt)
	if err != nil {
		return nil, nil, err
	}
	bytes, err = ioutil.ReadFile(fname)
	if err != nil {
		return nil, nil, nil
	}
	unmarshaled := cachedInfo{}
	if err := json.Unmarshal(bytes, &unmarshaled); err != nil {
		return nil, nil, fmt.Errorf("failed to unmarshal cached network %q config: %w", netName, err)
	}
	if unmarshaled.Kind != CNICacheV1 {
		return nil, nil, fmt.Errorf("read cached network %q config has wrong kind: %v", netName, unmarshaled.Kind)
	}
	newRt := *rt
	if unmarshaled.CniArgs != nil {
		newRt.Args = unmarshaled.CniArgs
	}
	newRt.CapabilityArgs = unmarshaled.CapabilityArgs
	return unmarshaled.Config, &newRt, nil
}
func (c *CNIConfig) getLegacyCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result, error) {
	fname, err := c.getCacheFilePath(netName, rt)
	if err != nil {
		return nil, err
	}
	data, err := ioutil.ReadFile(fname)
	if err != nil {
		return nil, nil
	}
	result, err := create.CreateFromBytes(data)
	if err != nil {
		return nil, err
	}
	result, err = result.GetAsVersion(cniVersion)
	if err != nil {
		return nil, fmt.Errorf("failed to convert cached result to config version %q: %w", cniVersion, err)
	}
	return result, nil
}
func (c *CNIConfig) getCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result, error) {
	fname, err := c.getCacheFilePath(netName, rt)
	if err != nil {
		return nil, err
	}
	fdata, err := ioutil.ReadFile(fname)
	if err != nil {
		return nil, nil
	}
	cachedInfo := cachedInfo{}
	if err := json.Unmarshal(fdata, &cachedInfo); err != nil || cachedInfo.Kind != CNICacheV1 {
		return c.getLegacyCachedResult(netName, cniVersion, rt)
	}
	newBytes, err := json.Marshal(&cachedInfo.RawResult)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal cached network %q config: %w", netName, err)
	}
	result, err := create.CreateFromBytes(newBytes)
	if err != nil {
		return nil, err
	}
	result, err = result.GetAsVersion(cniVersion)
	if err != nil {
		return nil, fmt.Errorf("failed to convert cached result to config version %q: %w", cniVersion, err)
	}
	return result, nil
}
func (c *CNIConfig) GetNetworkListCachedResult(list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) {
	return c.getCachedResult(list.Name, list.CNIVersion, rt)
}
func (c *CNIConfig) GetNetworkCachedResult(net *NetworkConfig, rt *RuntimeConf) (types.Result, error) {
	return c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
}
func (c *CNIConfig) GetNetworkListCachedConfig(list *NetworkConfigList, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
	return c.getCachedConfig(list.Name, rt)
}
func (c *CNIConfig) GetNetworkCachedConfig(net *NetworkConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
	return c.getCachedConfig(net.Network.Name, 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
	}
	if err := utils.ValidateContainerID(rt.ContainerID); err != nil {
		return nil, err
	}
	if err := utils.ValidateNetworkName(name); err != nil {
		return nil, err
	}
	if err := utils.ValidateInterfaceName(rt.IfName); 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)
}
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, fmt.Errorf("plugin %s failed (add): %w", pluginDescription(net.Network), err)
		}
	}
	if err = c.cacheAdd(result, list.Bytes, list.Name, rt); err != nil {
		return nil, fmt.Errorf("failed to set network %q cached result: %w", list.Name, err)
	}
	return result, nil
}
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
	}
	newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt)
	if err != nil {
		return err
	}
	return invoke.ExecPluginWithoutResult(ctx, pluginPath, newConf.Bytes, c.args("CHECK", rt), c.exec)
}
func (c *CNIConfig) CheckNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) error {
	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 := c.getCachedResult(list.Name, list.CNIVersion, rt)
	if err != nil {
		return fmt.Errorf("failed to get network %q cached result: %w", 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)
}
func (c *CNIConfig) DelNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) error {
	var cachedResult types.Result
	if gtet, err := version.GreaterThanOrEqualTo(list.CNIVersion, "0.4.0"); err != nil {
		return err
	} else if gtet {
		cachedResult, err = c.getCachedResult(list.Name, list.CNIVersion, rt)
		if err != nil {
			return fmt.Errorf("failed to get network %q cached result: %w", 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 fmt.Errorf("plugin %s failed (delete): %w", pluginDescription(net.Network), err)
		}
	}
	_ = c.cacheDel(list.Name, rt)
	return nil
}
func pluginDescription(net *types.NetConf) string {
	if net == nil {
		return "<missing>"
	}
	pluginType := net.Type
	out := fmt.Sprintf("type=%q", pluginType)
	name := net.Name
	if name != "" {
		out += fmt.Sprintf(" name=%q", name)
	}
	return out
}
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 = c.cacheAdd(result, net.Bytes, net.Network.Name, rt); err != nil {
		return nil, fmt.Errorf("failed to set network %q cached result: %w", net.Network.Name, err)
	}
	return result, nil
}
func (c *CNIConfig) CheckNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error {
	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 := c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
	if err != nil {
		return fmt.Errorf("failed to get network %q cached result: %w", net.Network.Name, err)
	}
	return c.checkNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, cachedResult, rt)
}
func (c *CNIConfig) DelNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error {
	var cachedResult types.Result
	if gtet, err := version.GreaterThanOrEqualTo(net.Network.CNIVersion, "0.4.0"); err != nil {
		return err
	} else if gtet {
		cachedResult, err = c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
		if err != nil {
			return fmt.Errorf("failed to get network %q cached result: %w", net.Network.Name, err)
		}
	}
	if err := c.delNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, cachedResult, rt); err != nil {
		return err
	}
	_ = c.cacheDel(net.Network.Name, rt)
	return nil
}
func (c *CNIConfig) ValidateNetworkList(ctx context.Context, list *NetworkConfigList) ([]string, error) {
	version := list.CNIVersion
	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)
	}
	cc := make([]string, 0, len(caps))
	for c := range caps {
		cc = append(cc, c)
	}
	return cc, nil
}