2
go.mod
2
go.mod
@ -7,7 +7,7 @@ require (
|
||||
github.com/Microsoft/hcsshim v0.8.6
|
||||
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae
|
||||
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44
|
||||
github.com/containernetworking/cni v0.7.1
|
||||
github.com/containernetworking/cni v0.8.0
|
||||
github.com/coreos/go-iptables v0.4.5
|
||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7
|
||||
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c
|
||||
|
4
go.sum
4
go.sum
@ -6,8 +6,8 @@ github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae h1:AMzIhMUq
|
||||
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
|
||||
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44 h1:y853v6rXx+zefEcjET3JuKAqvhj+FKflQijjeaSv2iA=
|
||||
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||
github.com/containernetworking/cni v0.7.1 h1:fE3r16wpSEyaqY4Z4oFrLMmIGfBYIKpPrHK31EJ9FzE=
|
||||
github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
|
||||
github.com/containernetworking/cni v0.8.0 h1:BT9lpgGoH4jw3lFC7Odz2prU5ruiYKcgAjMCbgybcKI=
|
||||
github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
|
||||
github.com/coreos/go-iptables v0.4.5 h1:DpHb9vJrZQEFMcVLFKAAGMUVX0XoRC0ptCthinRYm38=
|
||||
github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
|
||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM=
|
||||
|
238
vendor/github.com/containernetworking/cni/libcni/api.go
generated
vendored
238
vendor/github.com/containernetworking/cni/libcni/api.go
generated
vendored
@ -25,6 +25,7 @@ import (
|
||||
|
||||
"github.com/containernetworking/cni/pkg/invoke"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/utils"
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
)
|
||||
|
||||
@ -32,6 +33,10 @@ var (
|
||||
CacheDir = "/var/lib/cni"
|
||||
)
|
||||
|
||||
const (
|
||||
CNICacheV1 = "cniCacheV1"
|
||||
)
|
||||
|
||||
// A RuntimeConf holds the arguments to one invocation of a CNI plugin
|
||||
// excepting the network configuration, with the nested exception that
|
||||
// the `runtimeConfig` from the network configuration is included
|
||||
@ -48,7 +53,7 @@ type RuntimeConf struct {
|
||||
// to the plugin
|
||||
CapabilityArgs map[string]interface{}
|
||||
|
||||
// A cache directory in which to library data. Defaults to CacheDir
|
||||
// DEPRECATED. Will be removed in a future release.
|
||||
CacheDir string
|
||||
}
|
||||
|
||||
@ -70,19 +75,22 @@ type CNI interface {
|
||||
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
|
||||
Path []string
|
||||
exec invoke.Exec
|
||||
cacheDir string
|
||||
}
|
||||
|
||||
// CNIConfig implements the CNI interface
|
||||
@ -92,9 +100,18 @@ var _ CNI = &CNIConfig{}
|
||||
// in the given paths and use the given exec interface to run those plugins,
|
||||
// or if the exec interface is not given, will use a default exec handler.
|
||||
func NewCNIConfig(path []string, exec invoke.Exec) *CNIConfig {
|
||||
return NewCNIConfigWithCacheDir(path, "", exec)
|
||||
}
|
||||
|
||||
// NewCNIConfigWithCacheDir returns a new CNIConfig object that will search for plugins
|
||||
// in the given paths use the given exec interface to run those plugins,
|
||||
// or if the exec interface is not given, will use a default exec handler.
|
||||
// The given cache directory will be used for temporary data storage when needed.
|
||||
func NewCNIConfigWithCacheDir(path []string, cacheDir string, exec invoke.Exec) *CNIConfig {
|
||||
return &CNIConfig{
|
||||
Path: path,
|
||||
exec: exec,
|
||||
Path: path,
|
||||
cacheDir: cacheDir,
|
||||
exec: exec,
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,33 +182,122 @@ func (c *CNIConfig) ensureExec() invoke.Exec {
|
||||
return c.exec
|
||||
}
|
||||
|
||||
func getResultCacheFilePath(netName string, rt *RuntimeConf) string {
|
||||
cacheDir := rt.CacheDir
|
||||
if cacheDir == "" {
|
||||
cacheDir = CacheDir
|
||||
}
|
||||
return filepath.Join(cacheDir, "results", fmt.Sprintf("%s-%s-%s", netName, rt.ContainerID, rt.IfName))
|
||||
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 setCachedResult(result types.Result, netName string, rt *RuntimeConf) error {
|
||||
// getCacheDir returns the cache directory in this order:
|
||||
// 1) global cacheDir from CNIConfig object
|
||||
// 2) deprecated cacheDir from RuntimeConf object
|
||||
// 3) fall back to default cache directory
|
||||
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,
|
||||
}
|
||||
|
||||
// We need to get type.Result into cachedInfo as JSON map
|
||||
// Marshal to []byte, then Unmarshal into cached.RawResult
|
||||
data, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fname := getResultCacheFilePath(netName, rt)
|
||||
|
||||
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, data, 0600)
|
||||
|
||||
return ioutil.WriteFile(fname, newBytes, 0600)
|
||||
}
|
||||
|
||||
func delCachedResult(netName string, rt *RuntimeConf) error {
|
||||
fname := getResultCacheFilePath(netName, rt)
|
||||
func (c *CNIConfig) cacheDel(netName string, rt *RuntimeConf) error {
|
||||
fname, err := c.getCacheFilePath(netName, rt)
|
||||
if err != nil {
|
||||
// Ignore error
|
||||
return nil
|
||||
}
|
||||
return os.Remove(fname)
|
||||
}
|
||||
|
||||
func getCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result, error) {
|
||||
fname := getResultCacheFilePath(netName, rt)
|
||||
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 {
|
||||
// Ignore read errors; the cached result may not exist on-disk
|
||||
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: %v", 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 {
|
||||
// Ignore read errors; the cached result may not exist on-disk
|
||||
@ -222,16 +328,73 @@ func getCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result,
|
||||
return result, err
|
||||
}
|
||||
|
||||
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 {
|
||||
// Ignore read errors; the cached result may not exist on-disk
|
||||
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: %v", netName, err)
|
||||
}
|
||||
|
||||
// Read the version of the cached result
|
||||
decoder := version.ConfigDecoder{}
|
||||
resultCniVersion, err := decoder.Decode(newBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Ensure we can understand the result
|
||||
result, err := version.NewResult(resultCniVersion, newBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert to the config version to ensure plugins get prevResult
|
||||
// in the same version as the config. The cached result version
|
||||
// should match the config version unless the config was changed
|
||||
// while the container was running.
|
||||
result, err = result.GetAsVersion(cniVersion)
|
||||
if err != nil && resultCniVersion != cniVersion {
|
||||
return nil, fmt.Errorf("failed to convert cached result version %q to config version %q: %v", resultCniVersion, cniVersion, err)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
// GetNetworkListCachedResult returns the cached Result of the previous
|
||||
// previous AddNetworkList() operation for a network list, or an error.
|
||||
// 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)
|
||||
return c.getCachedResult(list.Name, list.CNIVersion, rt)
|
||||
}
|
||||
|
||||
// GetNetworkCachedResult returns the cached Result of the previous
|
||||
// previous AddNetwork() operation for a network, or an error.
|
||||
// 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)
|
||||
return c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
|
||||
}
|
||||
|
||||
// GetNetworkListCachedConfig copies the input RuntimeConf to output
|
||||
// RuntimeConf with fields updated with info from the cached Config.
|
||||
func (c *CNIConfig) GetNetworkListCachedConfig(list *NetworkConfigList, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
|
||||
return c.getCachedConfig(list.Name, rt)
|
||||
}
|
||||
|
||||
// GetNetworkCachedConfig copies the input RuntimeConf to output
|
||||
// RuntimeConf with fields updated with info from the cached Config.
|
||||
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) {
|
||||
@ -240,6 +403,15 @@ func (c *CNIConfig) addNetwork(ctx context.Context, name, cniVersion string, net
|
||||
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 {
|
||||
@ -260,7 +432,7 @@ func (c *CNIConfig) AddNetworkList(ctx context.Context, list *NetworkConfigList,
|
||||
}
|
||||
}
|
||||
|
||||
if err = setCachedResult(result, list.Name, rt); err != nil {
|
||||
if err = c.cacheAdd(result, list.Bytes, list.Name, rt); err != nil {
|
||||
return nil, fmt.Errorf("failed to set network %q cached result: %v", list.Name, err)
|
||||
}
|
||||
|
||||
@ -295,7 +467,7 @@ func (c *CNIConfig) CheckNetworkList(ctx context.Context, list *NetworkConfigLis
|
||||
return nil
|
||||
}
|
||||
|
||||
cachedResult, err := getCachedResult(list.Name, list.CNIVersion, rt)
|
||||
cachedResult, err := c.getCachedResult(list.Name, list.CNIVersion, rt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get network %q cached result: %v", list.Name, err)
|
||||
}
|
||||
@ -332,7 +504,7 @@ func (c *CNIConfig) DelNetworkList(ctx context.Context, list *NetworkConfigList,
|
||||
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)
|
||||
cachedResult, err = c.getCachedResult(list.Name, list.CNIVersion, rt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get network %q cached result: %v", list.Name, err)
|
||||
}
|
||||
@ -344,7 +516,7 @@ func (c *CNIConfig) DelNetworkList(ctx context.Context, list *NetworkConfigList,
|
||||
return err
|
||||
}
|
||||
}
|
||||
_ = delCachedResult(list.Name, rt)
|
||||
_ = c.cacheDel(list.Name, rt)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -356,7 +528,7 @@ func (c *CNIConfig) AddNetwork(ctx context.Context, net *NetworkConfig, rt *Runt
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = setCachedResult(result, net.Network.Name, rt); err != nil {
|
||||
if err = c.cacheAdd(result, net.Bytes, net.Network.Name, rt); err != nil {
|
||||
return nil, fmt.Errorf("failed to set network %q cached result: %v", net.Network.Name, err)
|
||||
}
|
||||
|
||||
@ -372,7 +544,7 @@ func (c *CNIConfig) CheckNetwork(ctx context.Context, net *NetworkConfig, rt *Ru
|
||||
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)
|
||||
cachedResult, err := c.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)
|
||||
}
|
||||
@ -387,7 +559,7 @@ func (c *CNIConfig) DelNetwork(ctx context.Context, net *NetworkConfig, rt *Runt
|
||||
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)
|
||||
cachedResult, err = c.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)
|
||||
}
|
||||
@ -396,7 +568,7 @@ func (c *CNIConfig) DelNetwork(ctx context.Context, net *NetworkConfig, rt *Runt
|
||||
if err := c.delNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, cachedResult, rt); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = delCachedResult(net.Network.Name, rt)
|
||||
_ = c.cacheDel(net.Network.Name, rt)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -455,10 +627,14 @@ func (c *CNIConfig) ValidateNetwork(ctx context.Context, net *NetworkConfig) ([]
|
||||
|
||||
// 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)
|
||||
c.ensureExec()
|
||||
pluginPath, err := c.exec.FindInPath(pluginName, c.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if expectedVersion == "" {
|
||||
expectedVersion = "0.1.0"
|
||||
}
|
||||
|
||||
vi, err := invoke.GetVersionInfo(ctx, pluginPath, c.exec)
|
||||
if err != nil {
|
||||
|
4
vendor/github.com/containernetworking/cni/libcni/conf.go
generated
vendored
4
vendor/github.com/containernetworking/cni/libcni/conf.go
generated
vendored
@ -114,11 +114,11 @@ func ConfListFromBytes(bytes []byte) (*NetworkConfigList, error) {
|
||||
for i, conf := range plugins {
|
||||
newBytes, err := json.Marshal(conf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to marshal plugin config %d: %v", i, err)
|
||||
return nil, fmt.Errorf("failed to marshal plugin config %d: %v", i, err)
|
||||
}
|
||||
netConf, err := ConfFromBytes(newBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to parse plugin config %d: %v", i, err)
|
||||
return nil, fmt.Errorf("failed to parse plugin config %d: %v", i, err)
|
||||
}
|
||||
list.Plugins = append(list.Plugins, netConf)
|
||||
}
|
||||
|
6
vendor/github.com/containernetworking/cni/pkg/invoke/args.go
generated
vendored
6
vendor/github.com/containernetworking/cni/pkg/invoke/args.go
generated
vendored
@ -32,7 +32,7 @@ type inherited struct{}
|
||||
|
||||
var inheritArgsFromEnv inherited
|
||||
|
||||
func (_ *inherited) AsEnv() []string {
|
||||
func (*inherited) AsEnv() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -60,8 +60,8 @@ func (args *Args) AsEnv() []string {
|
||||
pluginArgsStr = stringify(args.PluginArgs)
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Duplicated values which come first will be overridden, so we must put the
|
||||
// custom values in the end to avoid being overridden by the process environments.
|
||||
env = append(env,
|
||||
"CNI_COMMAND="+args.Command,
|
||||
"CNI_CONTAINERID="+args.ContainerID,
|
||||
|
52
vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec.go
generated
vendored
52
vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec.go
generated
vendored
@ -21,6 +21,8 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
)
|
||||
@ -31,30 +33,54 @@ type RawExec struct {
|
||||
|
||||
func (e *RawExec) ExecPlugin(ctx context.Context, pluginPath string, stdinData []byte, environ []string) ([]byte, error) {
|
||||
stdout := &bytes.Buffer{}
|
||||
stderr := &bytes.Buffer{}
|
||||
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())
|
||||
c.Stderr = stderr
|
||||
|
||||
// Retry the command on "text file busy" errors
|
||||
for i := 0; i <= 5; i++ {
|
||||
err := c.Run()
|
||||
|
||||
// Command succeeded
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// If the plugin is currently about to be written, then we wait a
|
||||
// second and try it again
|
||||
if strings.Contains(err.Error(), "text file busy") {
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
// All other errors except than the busy text file
|
||||
return nil, e.pluginErr(err, stdout.Bytes(), stderr.Bytes())
|
||||
}
|
||||
|
||||
// Copy stderr to caller's buffer in case plugin printed to both
|
||||
// stdout and stderr for some reason. Ignore failures as stderr is
|
||||
// only informational.
|
||||
if e.Stderr != nil && stderr.Len() > 0 {
|
||||
_, _ = stderr.WriteTo(e.Stderr)
|
||||
}
|
||||
return stdout.Bytes(), nil
|
||||
}
|
||||
|
||||
func pluginErr(err error, output []byte) error {
|
||||
if _, ok := err.(*exec.ExitError); ok {
|
||||
emsg := types.Error{}
|
||||
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)
|
||||
func (e *RawExec) pluginErr(err error, stdout, stderr []byte) error {
|
||||
emsg := types.Error{}
|
||||
if len(stdout) == 0 {
|
||||
if len(stderr) == 0 {
|
||||
emsg.Msg = fmt.Sprintf("netplugin failed with no error message: %v", err)
|
||||
} else {
|
||||
emsg.Msg = fmt.Sprintf("netplugin failed: %q", string(stderr))
|
||||
}
|
||||
return &emsg
|
||||
} else if perr := json.Unmarshal(stdout, &emsg); perr != nil {
|
||||
emsg.Msg = fmt.Sprintf("netplugin failed but error parsing its diagnostic message %q: %v", string(stdout), perr)
|
||||
}
|
||||
|
||||
return err
|
||||
return &emsg
|
||||
}
|
||||
|
||||
func (e *RawExec) FindInPath(plugin string, paths []string) (string, error) {
|
||||
|
99
vendor/github.com/containernetworking/cni/pkg/skel/skel.go
generated
vendored
99
vendor/github.com/containernetworking/cni/pkg/skel/skel.go
generated
vendored
@ -27,6 +27,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/utils"
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
)
|
||||
|
||||
@ -53,16 +54,7 @@ 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) {
|
||||
func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, *types.Error) {
|
||||
var cmd, contID, netns, ifName, args, path string
|
||||
|
||||
vars := []struct {
|
||||
@ -138,7 +130,7 @@ func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, error) {
|
||||
|
||||
if len(argsMissing) > 0 {
|
||||
joined := strings.Join(argsMissing, ",")
|
||||
return "", nil, missingEnvError{fmt.Sprintf("required env variables [%s] missing", joined)}
|
||||
return "", nil, types.NewError(types.ErrInvalidEnvironmentVariables, fmt.Sprintf("required env variables [%s] missing", joined), "")
|
||||
}
|
||||
|
||||
if cmd == "VERSION" {
|
||||
@ -147,7 +139,7 @@ func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, error) {
|
||||
|
||||
stdinData, err := ioutil.ReadAll(t.Stdin)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("error reading from stdin: %v", err)
|
||||
return "", nil, types.NewError(types.ErrIOFailure, fmt.Sprintf("error reading from stdin: %v", err), "")
|
||||
}
|
||||
|
||||
cmdArgs := &CmdArgs{
|
||||
@ -161,39 +153,39 @@ func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, error) {
|
||||
return cmd, cmdArgs, nil
|
||||
}
|
||||
|
||||
func createTypedError(f string, args ...interface{}) *types.Error {
|
||||
return &types.Error{
|
||||
Code: 100,
|
||||
Msg: fmt.Sprintf(f, args...),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *dispatcher) checkVersionAndCall(cmdArgs *CmdArgs, pluginVersionInfo version.PluginInfo, toCall func(*CmdArgs) error) error {
|
||||
func (t *dispatcher) checkVersionAndCall(cmdArgs *CmdArgs, pluginVersionInfo version.PluginInfo, toCall func(*CmdArgs) error) *types.Error {
|
||||
configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
return types.NewError(types.ErrDecodingFailure, err.Error(), "")
|
||||
}
|
||||
verErr := t.VersionReconciler.Check(configVersion, pluginVersionInfo)
|
||||
if verErr != nil {
|
||||
return &types.Error{
|
||||
Code: types.ErrIncompatibleCNIVersion,
|
||||
Msg: "incompatible CNI versions",
|
||||
Details: verErr.Details(),
|
||||
}
|
||||
return types.NewError(types.ErrIncompatibleCNIVersion, "incompatible CNI versions", verErr.Details())
|
||||
}
|
||||
|
||||
return toCall(cmdArgs)
|
||||
if err = toCall(cmdArgs); err != nil {
|
||||
if e, ok := err.(*types.Error); ok {
|
||||
// don't wrap Error in Error
|
||||
return e
|
||||
}
|
||||
return types.NewError(types.ErrInternal, err.Error(), "")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateConfig(jsonBytes []byte) error {
|
||||
func validateConfig(jsonBytes []byte) *types.Error {
|
||||
var conf struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
if err := json.Unmarshal(jsonBytes, &conf); err != nil {
|
||||
return fmt.Errorf("error reading network config: %s", err)
|
||||
return types.NewError(types.ErrDecodingFailure, fmt.Sprintf("error unmarshall network config: %v", err), "")
|
||||
}
|
||||
if conf.Name == "" {
|
||||
return fmt.Errorf("missing network name")
|
||||
return types.NewError(types.ErrInvalidNetworkConfig, "missing network name", "")
|
||||
}
|
||||
if err := utils.ValidateNetworkName(conf.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -202,17 +194,22 @@ func (t *dispatcher) pluginMain(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error,
|
||||
cmd, cmdArgs, err := t.getCmdArgsFromEnv()
|
||||
if err != nil {
|
||||
// Print the about string to stderr when no command is set
|
||||
if _, ok := err.(missingEnvError); ok && t.Getenv("CNI_COMMAND") == "" && about != "" {
|
||||
fmt.Fprintln(t.Stderr, about)
|
||||
if err.Code == types.ErrInvalidEnvironmentVariables && t.Getenv("CNI_COMMAND") == "" && about != "" {
|
||||
_, _ = fmt.Fprintln(t.Stderr, about)
|
||||
return nil
|
||||
}
|
||||
return createTypedError(err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
if cmd != "VERSION" {
|
||||
err = validateConfig(cmdArgs.StdinData)
|
||||
if err != nil {
|
||||
return createTypedError(err.Error())
|
||||
if err = validateConfig(cmdArgs.StdinData); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = utils.ValidateContainerID(cmdArgs.ContainerID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = utils.ValidateInterfaceName(cmdArgs.IfName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,45 +219,37 @@ func (t *dispatcher) pluginMain(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error,
|
||||
case "CHECK":
|
||||
configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData)
|
||||
if err != nil {
|
||||
return createTypedError(err.Error())
|
||||
return types.NewError(types.ErrDecodingFailure, err.Error(), "")
|
||||
}
|
||||
if gtet, err := version.GreaterThanOrEqualTo(configVersion, "0.4.0"); err != nil {
|
||||
return createTypedError(err.Error())
|
||||
return types.NewError(types.ErrDecodingFailure, err.Error(), "")
|
||||
} else if !gtet {
|
||||
return &types.Error{
|
||||
Code: types.ErrIncompatibleCNIVersion,
|
||||
Msg: "config version does not allow CHECK",
|
||||
}
|
||||
return types.NewError(types.ErrIncompatibleCNIVersion, "config version does not allow CHECK", "")
|
||||
}
|
||||
for _, pluginVersion := range versionInfo.SupportedVersions() {
|
||||
gtet, err := version.GreaterThanOrEqualTo(pluginVersion, configVersion)
|
||||
if err != nil {
|
||||
return createTypedError(err.Error())
|
||||
return types.NewError(types.ErrDecodingFailure, err.Error(), "")
|
||||
} else if gtet {
|
||||
if err := t.checkVersionAndCall(cmdArgs, versionInfo, cmdCheck); err != nil {
|
||||
return createTypedError(err.Error())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return &types.Error{
|
||||
Code: types.ErrIncompatibleCNIVersion,
|
||||
Msg: "plugin version does not allow CHECK",
|
||||
}
|
||||
return types.NewError(types.ErrIncompatibleCNIVersion, "plugin version does not allow CHECK", "")
|
||||
case "DEL":
|
||||
err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdDel)
|
||||
case "VERSION":
|
||||
err = versionInfo.Encode(t.Stdout)
|
||||
if err := versionInfo.Encode(t.Stdout); err != nil {
|
||||
return types.NewError(types.ErrIOFailure, err.Error(), "")
|
||||
}
|
||||
default:
|
||||
return createTypedError("unknown CNI_COMMAND: %v", cmd)
|
||||
return types.NewError(types.ErrInvalidEnvironmentVariables, fmt.Sprintf("unknown CNI_COMMAND: %v", cmd), "")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if e, ok := err.(*types.Error); ok {
|
||||
// don't wrap Error in Error
|
||||
return e
|
||||
}
|
||||
return createTypedError(err.Error())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
14
vendor/github.com/containernetworking/cni/pkg/types/020/types.go
generated
vendored
14
vendor/github.com/containernetworking/cni/pkg/types/020/types.go
generated
vendored
@ -86,20 +86,6 @@ func (r *Result) PrintTo(writer io.Writer) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// String returns a formatted string in the form of "[IP4: $1,][ IP6: $2,] DNS: $3" where
|
||||
// $1 represents the receiver's IPv4, $2 represents the receiver's IPv6 and $3 the
|
||||
// receiver's DNS. If $1 or $2 are nil, they won't be present in the returned string.
|
||||
func (r *Result) String() string {
|
||||
var str string
|
||||
if r.IP4 != nil {
|
||||
str = fmt.Sprintf("IP4:%+v, ", *r.IP4)
|
||||
}
|
||||
if r.IP6 != nil {
|
||||
str += fmt.Sprintf("IP6:%+v, ", *r.IP6)
|
||||
}
|
||||
return fmt.Sprintf("%sDNS:%+v", str, r.DNS)
|
||||
}
|
||||
|
||||
// IPConfig contains values necessary to configure an interface
|
||||
type IPConfig struct {
|
||||
IP net.IPNet
|
||||
|
2
vendor/github.com/containernetworking/cni/pkg/types/args.go
generated
vendored
2
vendor/github.com/containernetworking/cni/pkg/types/args.go
generated
vendored
@ -36,7 +36,7 @@ func (b *UnmarshallableBool) UnmarshalText(data []byte) error {
|
||||
case "0", "false":
|
||||
*b = false
|
||||
default:
|
||||
return fmt.Errorf("Boolean unmarshal error: invalid input %s", s)
|
||||
return fmt.Errorf("boolean unmarshal error: invalid input %s", s)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
17
vendor/github.com/containernetworking/cni/pkg/types/current/types.go
generated
vendored
17
vendor/github.com/containernetworking/cni/pkg/types/current/types.go
generated
vendored
@ -207,23 +207,6 @@ func (r *Result) PrintTo(writer io.Writer) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// String returns a formatted string in the form of "[Interfaces: $1,][ IP: $2,] DNS: $3" where
|
||||
// $1 represents the receiver's Interfaces, $2 represents the receiver's IP addresses and $3 the
|
||||
// receiver's DNS. If $1 or $2 are nil, they won't be present in the returned string.
|
||||
func (r *Result) String() string {
|
||||
var str string
|
||||
if len(r.Interfaces) > 0 {
|
||||
str += fmt.Sprintf("Interfaces:%+v, ", r.Interfaces)
|
||||
}
|
||||
if len(r.IPs) > 0 {
|
||||
str += fmt.Sprintf("IP:%+v, ", r.IPs)
|
||||
}
|
||||
if len(r.Routes) > 0 {
|
||||
str += fmt.Sprintf("Routes:%+v, ", r.Routes)
|
||||
}
|
||||
return fmt.Sprintf("%sDNS:%+v", str, r.DNS)
|
||||
}
|
||||
|
||||
// Convert this old version result to the current CNI version result
|
||||
func (r *Result) Convert() (*Result, error) {
|
||||
return r, nil
|
||||
|
28
vendor/github.com/containernetworking/cni/pkg/types/types.go
generated
vendored
28
vendor/github.com/containernetworking/cni/pkg/types/types.go
generated
vendored
@ -16,7 +16,6 @@ package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
@ -101,9 +100,6 @@ type Result interface {
|
||||
|
||||
// Prints the result in JSON format to provided writer
|
||||
PrintTo(writer io.Writer) error
|
||||
|
||||
// Returns a JSON string representation of the result
|
||||
String() string
|
||||
}
|
||||
|
||||
func PrintResult(result Result, version string) error {
|
||||
@ -134,9 +130,16 @@ func (r *Route) String() string {
|
||||
// Well known error codes
|
||||
// see https://github.com/containernetworking/cni/blob/master/SPEC.md#well-known-error-codes
|
||||
const (
|
||||
ErrUnknown uint = iota // 0
|
||||
ErrIncompatibleCNIVersion // 1
|
||||
ErrUnsupportedField // 2
|
||||
ErrUnknown uint = iota // 0
|
||||
ErrIncompatibleCNIVersion // 1
|
||||
ErrUnsupportedField // 2
|
||||
ErrUnknownContainer // 3
|
||||
ErrInvalidEnvironmentVariables // 4
|
||||
ErrIOFailure // 5
|
||||
ErrDecodingFailure // 6
|
||||
ErrInvalidNetworkConfig // 7
|
||||
ErrTryAgainLater uint = 11
|
||||
ErrInternal uint = 999
|
||||
)
|
||||
|
||||
type Error struct {
|
||||
@ -145,6 +148,14 @@ type Error struct {
|
||||
Details string `json:"details,omitempty"`
|
||||
}
|
||||
|
||||
func NewError(code uint, msg, details string) *Error {
|
||||
return &Error{
|
||||
Code: code,
|
||||
Msg: msg,
|
||||
Details: details,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
details := ""
|
||||
if e.Details != "" {
|
||||
@ -194,6 +205,3 @@ func prettyPrint(obj interface{}) error {
|
||||
_, err = os.Stdout.Write(data)
|
||||
return err
|
||||
}
|
||||
|
||||
// NotImplementedError is used to indicate that a method is not implemented for the given platform
|
||||
var NotImplementedError = errors.New("Not Implemented")
|
||||
|
84
vendor/github.com/containernetworking/cni/pkg/utils/utils.go
generated
vendored
Normal file
84
vendor/github.com/containernetworking/cni/pkg/utils/utils.go
generated
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright 2019 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"unicode"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
)
|
||||
|
||||
const (
|
||||
// cniValidNameChars is the regexp used to validate valid characters in
|
||||
// containerID and networkName
|
||||
cniValidNameChars = `[a-zA-Z0-9][a-zA-Z0-9_.\-]`
|
||||
|
||||
// maxInterfaceNameLength is the length max of a valid interface name
|
||||
maxInterfaceNameLength = 15
|
||||
)
|
||||
|
||||
var cniReg = regexp.MustCompile(`^` + cniValidNameChars + `*$`)
|
||||
|
||||
// ValidateContainerID will validate that the supplied containerID is not empty does not contain invalid characters
|
||||
func ValidateContainerID(containerID string) *types.Error {
|
||||
|
||||
if containerID == "" {
|
||||
return types.NewError(types.ErrUnknownContainer, "missing containerID", "")
|
||||
}
|
||||
if !cniReg.MatchString(containerID) {
|
||||
return types.NewError(types.ErrInvalidEnvironmentVariables, "invalid characters in containerID", containerID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateNetworkName will validate that the supplied networkName does not contain invalid characters
|
||||
func ValidateNetworkName(networkName string) *types.Error {
|
||||
|
||||
if networkName == "" {
|
||||
return types.NewError(types.ErrInvalidNetworkConfig, "missing network name:", "")
|
||||
}
|
||||
if !cniReg.MatchString(networkName) {
|
||||
return types.NewError(types.ErrInvalidNetworkConfig, "invalid characters found in network name", networkName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateInterfaceName will validate the interface name based on the three rules below
|
||||
// 1. The name must not be empty
|
||||
// 2. The name must be less than 16 characters
|
||||
// 3. The name must not be "." or ".."
|
||||
// 3. The name must not contain / or : or any whitespace characters
|
||||
// ref to https://github.com/torvalds/linux/blob/master/net/core/dev.c#L1024
|
||||
func ValidateInterfaceName(ifName string) *types.Error {
|
||||
if len(ifName) == 0 {
|
||||
return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is empty", "")
|
||||
}
|
||||
if len(ifName) > maxInterfaceNameLength {
|
||||
return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is too long", fmt.Sprintf("interface name should be less than %d characters", maxInterfaceNameLength+1))
|
||||
}
|
||||
if ifName == "." || ifName == ".." {
|
||||
return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is . or ..", "")
|
||||
}
|
||||
for _, r := range bytes.Runes([]byte(ifName)) {
|
||||
if r == '/' || r == ':' || unicode.IsSpace(r) {
|
||||
return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name contains / or : or whitespace characters", "")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
3
vendor/modules.txt
vendored
3
vendor/modules.txt
vendored
@ -28,7 +28,7 @@ github.com/alexflint/go-filemutex
|
||||
# github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44
|
||||
## explicit
|
||||
github.com/buger/jsonparser
|
||||
# github.com/containernetworking/cni v0.7.1
|
||||
# github.com/containernetworking/cni v0.8.0
|
||||
## explicit
|
||||
github.com/containernetworking/cni/libcni
|
||||
github.com/containernetworking/cni/pkg/invoke
|
||||
@ -36,6 +36,7 @@ github.com/containernetworking/cni/pkg/skel
|
||||
github.com/containernetworking/cni/pkg/types
|
||||
github.com/containernetworking/cni/pkg/types/020
|
||||
github.com/containernetworking/cni/pkg/types/current
|
||||
github.com/containernetworking/cni/pkg/utils
|
||||
github.com/containernetworking/cni/pkg/version
|
||||
# github.com/coreos/go-iptables v0.4.5
|
||||
## explicit
|
||||
|
Reference in New Issue
Block a user