Merge pull request #4007 from mccv1r0/v1.4.1-crio114

libpod vendor update for CNI and ocicni
This commit is contained in:
OpenShift Merge Robot
2019-09-13 20:53:49 +02:00
committed by GitHub
16 changed files with 864 additions and 254 deletions

View File

@ -11,7 +11,6 @@ import (
"syscall"
"time"
types2 "github.com/containernetworking/cni/pkg/types"
cp "github.com/containers/image/copy"
"github.com/containers/image/directory"
dockerarchive "github.com/containers/image/docker/archive"
@ -389,11 +388,6 @@ func (i *Image) Remove(ctx context.Context, force bool) error {
return nil
}
// Decompose an Image
func (i *Image) Decompose() error {
return types2.NotImplementedError
}
// TODO: Rework this method to not require an assembly of the fq name with transport
/*
// GetManifest tries to GET an images manifest, returns nil on success and err on failure

View File

@ -27,25 +27,36 @@ import (
)
// Get an OCICNI network config
func (r *Runtime) getPodNetwork(id, name, nsPath string, networks []string, ports []ocicni.PortMapping, staticIP net.IP) ocicni.PodNetwork {
network := ocicni.PodNetwork{
Name: name,
Namespace: name, // TODO is there something else we should put here? We don't know about Kube namespaces
ID: id,
NetNS: nsPath,
PortMappings: ports,
Networks: networks,
func (r *Runtime) getPodNetwork(id, name, nsPath string, config *ContainerConfig, staticIP net.IP) ocicni.PodNetwork {
networks := make([]ocicni.NetAttachment, 0)
for _, netName := range config.Networks {
networks = append(networks, ocicni.NetAttachment{Name: netName})
}
if len(networks) == 0 {
networks = append(networks, ocicni.NetAttachment{Name: r.netPlugin.GetDefaultNetworkName()})
}
firstNetworkName := networks[0].Name
podNetwork := ocicni.PodNetwork{
Name: name,
Namespace: name, // TODO is there something else we should put here? We don't know about Kube namespaces
ID: id,
NetNS: nsPath,
Networks: networks,
RuntimeConfig: map[string]ocicni.RuntimeConfig{
firstNetworkName: {},
},
}
firstNetwork := podNetwork.RuntimeConfig[firstNetworkName]
if len(config.PortMappings) != 0 {
firstNetwork.PortMappings = config.PortMappings
}
if staticIP != nil {
defaultNetwork := r.netPlugin.GetDefaultNetworkName()
network.Networks = []string{defaultNetwork}
network.NetworkConfig = make(map[string]ocicni.NetworkConfig)
network.NetworkConfig[defaultNetwork] = ocicni.NetworkConfig{IP: staticIP.String()}
firstNetwork.IP = staticIP.String()
}
podNetwork.RuntimeConfig[firstNetworkName] = firstNetwork
return network
return podNetwork
}
// Create and configure a new network namespace for a container
@ -59,7 +70,7 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re
requestedIP = ctr.config.StaticIP
}
podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctrNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP)
podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctrNS.Path(), ctr.Config(), requestedIP)
results, err := r.netPlugin.SetUpPod(podNetwork)
if err != nil {
@ -75,10 +86,10 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re
networkStatus := make([]*cnitypes.Result, 0)
for idx, r := range results {
logrus.Debugf("[%d] CNI result: %v", idx, r.String())
resultCurrent, err := cnitypes.GetResult(r)
logrus.Debugf("[%d] CNI result: %v", idx, r.Result)
resultCurrent, err := cnitypes.GetResult(r.Result)
if err != nil {
return nil, errors.Wrapf(err, "error parsing CNI plugin result %q: %v", r.String(), err)
return nil, errors.Wrapf(err, "error parsing CNI plugin result %q: %v", r.Result, err)
}
networkStatus = append(networkStatus, resultCurrent)
}
@ -408,7 +419,7 @@ func (r *Runtime) teardownNetNS(ctr *Container) error {
requestedIP = ctr.config.StaticIP
}
podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP)
podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), ctr.Config(), requestedIP)
// The network may have already been torn down, so don't fail here, just log
if err := r.netPlugin.TearDownPod(podNetwork); err != nil {

View File

@ -13,7 +13,7 @@ github.com/buger/goterm c206103e1f37c0c6c5c039706305ea2aa6e8ad3b
github.com/checkpoint-restore/go-criu v3.11
github.com/containerd/cgroups 4994991857f9b0ae8dc439551e8bebdbb4bf66c1
github.com/containerd/continuity 004b46473808b3e7a4a3049c20e4376c91eb966d
github.com/containernetworking/cni v0.7.0-rc2
github.com/containernetworking/cni 83439463f7840005184ec4c488c9edd6ed6978fc
github.com/containernetworking/plugins v0.7.4
github.com/containers/image v2.0.0
github.com/vbauerster/mpb v3.3.4
@ -23,7 +23,7 @@ github.com/containers/storage v1.12.10
github.com/containers/psgo v1.3.0
github.com/coreos/go-systemd v17
github.com/coreos/pkg v4
github.com/cri-o/ocicni 0c180f981b27ef6036fa5be29bcb4dd666e406eb
github.com/cri-o/ocicni 7bd73e9a7f59107c76605aecd2c8ec3d69ef3ff8
github.com/cyphar/filepath-securejoin v0.2.1
github.com/davecgh/go-spew v1.1.0
github.com/docker/distribution 5f6282db7d65e6d72ad7c2cc66310724a57be716

View File

@ -9,7 +9,7 @@
# Community Sync Meeting
There is a community sync meeting for users and developers every 1-2 months. The next meeting will help on a Google Hangout and the link is in the [agenda](https://docs.google.com/document/d/10ECyT2mBGewsJUcmYmS8QNo1AcNgy2ZIe2xS7lShYhE/edit?usp=sharing) (Notes from previous meeting are also in this doc).
There is a community sync meeting for users and developers every 1-2 months. The next meeting will be held on a Google Hangout and the link is in the [agenda](https://docs.google.com/document/d/10ECyT2mBGewsJUcmYmS8QNo1AcNgy2ZIe2xS7lShYhE/edit?usp=sharing) (Notes from previous meeting are also in this doc).
The next meeting will be held on *Wednesday, January 30th, 2019* at *4:00pm UTC / 11:00am EDT / 8:00am PDT* [Add to Calendar](https://www.worldtimebuddy.com/?qm=1&lid=100,5,2643743,5391959&h=100&date=2019-01-30&sln=16-17).
@ -67,6 +67,8 @@ To avoid duplication, we think it is prudent to define a common interface betwee
- [Knitter - a CNI plugin supporting multiple networking for Kubernetes](https://github.com/ZTE/Knitter)
- [DANM - a CNI-compliant networking solution for TelCo workloads running on Kubernetes](https://github.com/nokia/danm)
- [VMware NSX a CNI plugin that enables automated NSX L2/L3 networking and L4/L7 Load Balancing; network isolation at the pod, node, and cluster level; and zero-trust security policy for your Kubernetes cluster.](https://docs.vmware.com/en/VMware-NSX-T/2.2/com.vmware.nsxt.ncp_kubernetes.doc/GUID-6AFA724E-BB62-4693-B95C-321E8DDEA7E1.html)
- [cni-route-override - a meta CNI plugin that override route information](https://github.com/redhat-nfvpe/cni-route-override)
- [Terway - a collection of CNI Plugins based on alibaba cloud VPC/ECS network product](https://github.com/AliyunContainerService/terway)
The CNI team also maintains some [core plugins in a separate repository](https://github.com/containernetworking/plugins).
@ -189,13 +191,13 @@ lo Link encap:Local Loopback
## What might CNI do in the future?
CNI currently covers a wide range of needs for network configuration due to it simple model and API.
CNI currently covers a wide range of needs for network configuration due to its simple model and API.
However, in the future CNI might want to branch out into other directions:
- Dynamic updates to existing network configuration
- Dynamic policies for network bandwidth and firewall rules
If these topics of are interest, please contact the team via the mailing list or IRC and find some like-minded people in the community to put a proposal together.
If these topics are of interest, please contact the team via the mailing list or IRC and find some like-minded people in the community to put a proposal together.
## Contact

View File

@ -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
}
@ -69,19 +74,23 @@ 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
Path []string
exec invoke.Exec
cacheDir string
}
// CNIConfig implements the CNI interface
@ -91,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,
}
}
@ -164,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
@ -221,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) {
@ -239,6 +403,12 @@ 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
}
newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt)
if err != nil {
@ -259,7 +429,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)
}
@ -294,7 +464,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)
}
@ -331,7 +501,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)
}
@ -343,7 +513,7 @@ func (c *CNIConfig) DelNetworkList(ctx context.Context, list *NetworkConfigList,
return err
}
}
_ = delCachedResult(list.Name, rt)
_ = c.cacheDel(list.Name, rt)
return nil
}
@ -355,7 +525,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)
}
@ -371,7 +541,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)
}
@ -386,7 +556,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)
}
@ -395,7 +565,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
}
@ -454,7 +624,8 @@ 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
}

View File

@ -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)
}

View File

@ -15,6 +15,7 @@
package invoke
import (
"fmt"
"os"
"strings"
)
@ -22,6 +23,8 @@ import (
type CNIArgs interface {
// For use with os/exec; i.e., return nil to inherit the
// environment from this process
// For use in delegation; inherit the environment from this
// process and allow overrides
AsEnv() []string
}
@ -29,7 +32,7 @@ type inherited struct{}
var inheritArgsFromEnv inherited
func (_ *inherited) AsEnv() []string {
func (*inherited) AsEnv() []string {
return nil
}
@ -57,17 +60,17 @@ func (args *Args) AsEnv() []string {
pluginArgsStr = stringify(args.PluginArgs)
}
// Ensure that the custom values are first, so any value present in
// the process environment won't override them.
env = append([]string{
"CNI_COMMAND=" + args.Command,
"CNI_CONTAINERID=" + args.ContainerID,
"CNI_NETNS=" + args.NetNS,
"CNI_ARGS=" + pluginArgsStr,
"CNI_IFNAME=" + args.IfName,
"CNI_PATH=" + args.Path,
}, env...)
return env
// Duplicated values which come first will be overrided, so we must put the
// custom values in the end to avoid being overrided by the process environments.
env = append(env,
"CNI_COMMAND="+args.Command,
"CNI_CONTAINERID="+args.ContainerID,
"CNI_NETNS="+args.NetNS,
"CNI_ARGS="+pluginArgsStr,
"CNI_IFNAME="+args.IfName,
"CNI_PATH="+args.Path,
)
return dedupEnv(env)
}
// taken from rkt/networking/net_plugin.go
@ -80,3 +83,46 @@ func stringify(pluginArgs [][2]string) string {
return strings.Join(entries, ";")
}
// DelegateArgs implements the CNIArgs interface
// used for delegation to inherit from environments
// and allow some overrides like CNI_COMMAND
var _ CNIArgs = &DelegateArgs{}
type DelegateArgs struct {
Command string
}
func (d *DelegateArgs) AsEnv() []string {
env := os.Environ()
// The custom values should come in the end to override the existing
// process environment of the same key.
env = append(env,
"CNI_COMMAND="+d.Command,
)
return dedupEnv(env)
}
// dedupEnv returns a copy of env with any duplicates removed, in favor of later values.
// Items not of the normal environment "key=value" form are preserved unchanged.
func dedupEnv(env []string) []string {
out := make([]string, 0, len(env))
envMap := map[string]string{}
for _, kv := range env {
// find the first "=" in environment, if not, just keep it
eq := strings.Index(kv, "=")
if eq < 0 {
out = append(out, kv)
continue
}
envMap[kv[:eq]] = kv[eq+1:]
}
for k, v := range envMap {
out = append(out, fmt.Sprintf("%s=%s", k, v))
}
return out
}

View File

@ -16,22 +16,17 @@ package invoke
import (
"context"
"fmt"
"os"
"path/filepath"
"github.com/containernetworking/cni/pkg/types"
)
func delegateCommon(expectedCommand, delegatePlugin string, exec Exec) (string, Exec, error) {
func delegateCommon(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 {
@ -44,32 +39,42 @@ func delegateCommon(expectedCommand, delegatePlugin string, exec Exec) (string,
// DelegateAdd calls the given delegate plugin with the CNI ADD action and
// JSON configuration
func DelegateAdd(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) (types.Result, error) {
pluginPath, realExec, err := delegateCommon("ADD", delegatePlugin, exec)
pluginPath, realExec, err := delegateCommon(delegatePlugin, exec)
if err != nil {
return nil, err
}
return ExecPluginWithResult(ctx, pluginPath, netconf, ArgsFromEnv(), realExec)
// DelegateAdd will override the original "CNI_COMMAND" env from process with ADD
return ExecPluginWithResult(ctx, pluginPath, netconf, delegateArgs("ADD"), realExec)
}
// DelegateCheck calls the given delegate plugin with the CNI CHECK action and
// JSON configuration
func DelegateCheck(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error {
pluginPath, realExec, err := delegateCommon("CHECK", delegatePlugin, exec)
pluginPath, realExec, err := delegateCommon(delegatePlugin, exec)
if err != nil {
return err
}
return ExecPluginWithoutResult(ctx, pluginPath, netconf, ArgsFromEnv(), realExec)
// DelegateCheck will override the original CNI_COMMAND env from process with CHECK
return ExecPluginWithoutResult(ctx, pluginPath, netconf, delegateArgs("CHECK"), realExec)
}
// DelegateDel calls the given delegate plugin with the CNI DEL action and
// JSON configuration
func DelegateDel(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error {
pluginPath, realExec, err := delegateCommon("DEL", delegatePlugin, exec)
pluginPath, realExec, err := delegateCommon(delegatePlugin, exec)
if err != nil {
return err
}
return ExecPluginWithoutResult(ctx, pluginPath, netconf, ArgsFromEnv(), realExec)
// DelegateDel will override the original CNI_COMMAND env from process with DEL
return ExecPluginWithoutResult(ctx, pluginPath, netconf, delegateArgs("DEL"), realExec)
}
// return CNIArgs used by delegation
func delegateArgs(action string) *DelegateArgs {
return &DelegateArgs{
Command: action,
}
}

View File

@ -46,7 +46,9 @@ func (e *RawExec) ExecPlugin(ctx context.Context, pluginPath string, stdinData [
func pluginErr(err error, output []byte) error {
if _, ok := err.(*exec.ExitError); ok {
emsg := types.Error{}
if perr := json.Unmarshal(output, &emsg); perr != nil {
if len(output) == 0 {
emsg.Msg = "netplugin failed with no error message"
} else if perr := json.Unmarshal(output, &emsg); perr != nil {
emsg.Msg = fmt.Sprintf("netplugin failed but error parsing its diagnostic message %q: %v", string(output), perr)
}
return &emsg

View File

@ -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
}

View File

@ -16,7 +16,6 @@ package types
import (
"encoding/json"
"errors"
"fmt"
"io"
"net"
@ -134,9 +133,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 +151,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 +208,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")

View File

@ -0,0 +1,51 @@
// 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 (
"regexp"
"github.com/containernetworking/cni/pkg/types"
)
// cniValidNameChars is the regexp used to validate valid characters in
// containerID and networkName
const cniValidNameChars = `[a-zA-Z0-9][a-zA-Z0-9_.\-]`
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
}

11
vendor/github.com/cri-o/ocicni/go.mod generated vendored Normal file
View File

@ -0,0 +1,11 @@
module github.com/cri-o/ocicni
go 1.12
require (
github.com/containernetworking/cni v0.7.2-0.20190725021623-1318d7c94c41
github.com/fsnotify/fsnotify v1.4.7
github.com/onsi/ginkgo v1.8.0
github.com/onsi/gomega v1.5.0
github.com/sirupsen/logrus v1.4.2
)

View File

@ -2,11 +2,14 @@ package ocicni
import (
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net"
"os"
"path"
"path/filepath"
"sort"
"strings"
"sync"
@ -21,10 +24,11 @@ import (
)
type cniNetworkPlugin struct {
cniConfig *libcni.CNIConfig
loNetwork *cniNetwork
sync.RWMutex
defaultNetName string
defaultNetName netName
networks map[string]*cniNetwork
nsManager *nsManager
@ -47,11 +51,15 @@ type cniNetworkPlugin struct {
cacheDir string
}
type netName struct {
name string
changeable bool
}
type cniNetwork struct {
name string
filePath string
NetworkConfig *libcni.NetworkConfigList
CNIConfig *libcni.CNIConfig
name string
filePath string
config *libcni.NetworkConfigList
}
var errMissingDefaultNetwork = errors.New("Missing CNI default network")
@ -186,6 +194,8 @@ func (plugin *cniNetworkPlugin) monitorConfDir(start *sync.WaitGroup) {
// If defaultNetName is not empty, a CNI config with that network name will
// be used as the default CNI network, and container network operations will
// fail until that network config is present and valid.
// If defaultNetName is empty, CNI config files should be reloaded real-time and
// defaultNetName should be changeable and determined by file sorting.
func InitCNI(defaultNetName string, confDir string, binDirs ...string) (CNIPlugin, error) {
return initCNI(nil, "", defaultNetName, confDir, binDirs...)
}
@ -198,17 +208,24 @@ func initCNI(exec cniinvoke.Exec, cacheDir, defaultNetName string, confDir strin
if len(binDirs) == 0 {
binDirs = []string{DefaultBinDir}
}
plugin := &cniNetworkPlugin{
defaultNetName: defaultNetName,
networks: make(map[string]*cniNetwork),
loNetwork: getLoNetwork(exec, binDirs),
confDir: confDir,
binDirs: binDirs,
shutdownChan: make(chan struct{}),
done: &sync.WaitGroup{},
pods: make(map[string]*podLock),
exec: exec,
cacheDir: cacheDir,
cniConfig: libcni.NewCNIConfig(binDirs, exec),
defaultNetName: netName{
name: defaultNetName,
// If defaultNetName is not assigned in initialization,
// it should be changeable
changeable: defaultNetName == "",
},
networks: make(map[string]*cniNetwork),
loNetwork: getLoNetwork(),
confDir: confDir,
binDirs: binDirs,
shutdownChan: make(chan struct{}),
done: &sync.WaitGroup{},
pods: make(map[string]*podLock),
exec: exec,
cacheDir: cacheDir,
}
if exec == nil {
@ -246,7 +263,7 @@ func (plugin *cniNetworkPlugin) Shutdown() error {
return nil
}
func loadNetworks(exec cniinvoke.Exec, confDir string, binDirs []string) (map[string]*cniNetwork, string, error) {
func loadNetworks(confDir string, cni *libcni.CNIConfig) (map[string]*cniNetwork, string, error) {
files, err := libcni.ConfFiles(confDir, []string{".conf", ".conflist", ".json"})
if err != nil {
return nil, "", err
@ -284,17 +301,30 @@ func loadNetworks(exec cniinvoke.Exec, confDir string, binDirs []string) (map[st
logrus.Warningf("CNI config list %s has no networks, skipping", confFile)
continue
}
// Validation on CNI config should be done to pre-check presence
// of plugins which are necessary.
if _, err := cni.ValidateNetworkList(context.TODO(), confList); err != nil {
logrus.Warningf("Error validating CNI config file %s: %v", confFile, err)
continue
}
if confList.Name == "" {
confList.Name = path.Base(confFile)
}
cniNet := &cniNetwork{
name: confList.Name,
filePath: confFile,
config: confList,
}
logrus.Infof("Found CNI network %s (type=%v) at %s", confList.Name, confList.Plugins[0].Network.Type, confFile)
networks[confList.Name] = &cniNetwork{
name: confList.Name,
filePath: confFile,
NetworkConfig: confList,
CNIConfig: libcni.NewCNIConfig(binDirs, exec),
if _, ok := networks[confList.Name]; !ok {
networks[confList.Name] = cniNet
} else {
logrus.Infof("Ignore CNI network %s (type=%v) at %s because already exists", confList.Name, confList.Plugins[0].Network.Type, confFile)
}
if defaultNetName == "" {
@ -305,39 +335,49 @@ func loadNetworks(exec cniinvoke.Exec, confDir string, binDirs []string) (map[st
return networks, defaultNetName, nil
}
func getLoNetwork(exec cniinvoke.Exec, binDirs []string) *cniNetwork {
loConfig, err := libcni.ConfListFromBytes([]byte(`{
"cniVersion": "0.2.0",
"name": "cni-loopback",
const (
loIfname string = "lo"
loNetname string = "cni-loopback"
)
func getLoNetwork() *cniNetwork {
loConfig, err := libcni.ConfListFromBytes([]byte(fmt.Sprintf(`{
"cniVersion": "0.3.1",
"name": "%s",
"plugins": [{
"type": "loopback"
}]
}`))
}`, loNetname)))
if err != nil {
// The hardcoded config above should always be valid and unit tests will
// catch this
panic(err)
}
loNetwork := &cniNetwork{
name: "lo",
NetworkConfig: loConfig,
CNIConfig: libcni.NewCNIConfig(binDirs, exec),
name: loIfname,
config: loConfig,
}
return loNetwork
}
func (plugin *cniNetworkPlugin) syncNetworkConfig() error {
networks, defaultNetName, err := loadNetworks(plugin.exec, plugin.confDir, plugin.binDirs)
networks, defaultNetName, err := loadNetworks(plugin.confDir, plugin.cniConfig)
if err != nil {
return err
}
plugin.Lock()
defer plugin.Unlock()
if plugin.defaultNetName == "" {
plugin.defaultNetName = defaultNetName
// Update defaultNetName if it is changeable
if plugin.defaultNetName.changeable {
plugin.defaultNetName.name = defaultNetName
logrus.Infof("Update default CNI network name to %s", defaultNetName)
} else {
logrus.Warnf("Default CNI network name %s is unchangeable", plugin.defaultNetName.name)
}
plugin.networks = networks
return nil
@ -356,7 +396,7 @@ func (plugin *cniNetworkPlugin) getNetwork(name string) (*cniNetwork, error) {
func (plugin *cniNetworkPlugin) GetDefaultNetworkName() string {
plugin.RLock()
defer plugin.RUnlock()
return plugin.defaultNetName
return plugin.defaultNetName.name
}
func (plugin *cniNetworkPlugin) getDefaultNetwork() *cniNetwork {
@ -382,27 +422,120 @@ func (plugin *cniNetworkPlugin) Name() string {
return CNIPluginName
}
func (plugin *cniNetworkPlugin) forEachNetwork(podNetwork *PodNetwork, forEachFunc func(*cniNetwork, string, *PodNetwork) error) error {
func (plugin *cniNetworkPlugin) loadNetworkFromCache(name string, rt *libcni.RuntimeConf) (*cniNetwork, *libcni.RuntimeConf, error) {
cniNet := &cniNetwork{
name: name,
config: &libcni.NetworkConfigList{
Name: name,
},
}
var confBytes []byte
var err error
confBytes, rt, err = plugin.cniConfig.GetNetworkListCachedConfig(cniNet.config, rt)
if err != nil {
return nil, nil, err
} else if confBytes == nil {
return nil, nil, fmt.Errorf("network %q not found in CNI cache", name)
}
cniNet.config, err = libcni.ConfListFromBytes(confBytes)
if err != nil {
// Might be a plain NetworkConfig
netConf, err := libcni.ConfFromBytes(confBytes)
if err != nil {
return nil, nil, err
}
// Up-convert to a NetworkConfigList
cniNet.config, err = libcni.ConfListFromConf(netConf)
if err != nil {
return nil, nil, err
}
}
return cniNet, rt, nil
}
type forEachNetworkFn func(*cniNetwork, *PodNetwork, *libcni.RuntimeConf) error
func (plugin *cniNetworkPlugin) forEachNetwork(podNetwork *PodNetwork, fromCache bool, actionFn forEachNetworkFn) error {
networks := podNetwork.Networks
if len(networks) == 0 {
networks = append(networks, plugin.GetDefaultNetworkName())
networks = append(networks, NetAttachment{
Name: plugin.GetDefaultNetworkName(),
})
}
for i, netName := range networks {
// Interface names start at "eth0" and count up for each network
ifName := fmt.Sprintf("eth%d", i)
network, err := plugin.getNetwork(netName)
allIfNames := make(map[string]bool)
for _, req := range networks {
if req.Ifname != "" {
// Make sure the requested name isn't already assigned
if allIfNames[req.Ifname] {
return fmt.Errorf("network %q requested interface name %q already assigned", req.Name, req.Ifname)
}
allIfNames[req.Ifname] = true
}
}
for _, network := range networks {
ifName := network.Ifname
if ifName == "" {
for i := 0; i < 10000; i++ {
candidate := fmt.Sprintf("eth%d", i)
if !allIfNames[candidate] {
allIfNames[candidate] = true
ifName = candidate
break
}
}
if ifName == "" {
return fmt.Errorf("failed to find free interface name for network %q", network.Name)
}
}
rt, err := buildCNIRuntimeConf(plugin.cacheDir, podNetwork, ifName, podNetwork.RuntimeConfig[network.Name])
if err != nil {
logrus.Errorf(err.Error())
logrus.Errorf("error building CNI runtime config: %v", err)
return err
}
if err := forEachFunc(network, ifName, podNetwork); err != nil {
var cniNet *cniNetwork
if fromCache {
var newRt *libcni.RuntimeConf
cniNet, newRt, err = plugin.loadNetworkFromCache(network.Name, rt)
if err != nil {
logrus.Errorf("error loading cached network config: %v", err)
// fall back to loading from existing plugins on disk
} else {
// Use the updated RuntimeConf
rt = newRt
}
}
if cniNet == nil {
cniNet, err = plugin.getNetwork(network.Name)
if err != nil {
logrus.Errorf(err.Error())
return err
}
}
if err := actionFn(cniNet, podNetwork, rt); err != nil {
return err
}
}
return nil
}
func (plugin *cniNetworkPlugin) SetUpPod(podNetwork PodNetwork) ([]cnitypes.Result, error) {
func buildLoopbackRuntimeConf(cacheDir string, podNetwork *PodNetwork) *libcni.RuntimeConf {
return &libcni.RuntimeConf{
ContainerID: podNetwork.ID,
NetNS: podNetwork.NetNS,
CacheDir: cacheDir,
IfName: loIfname,
}
}
func (plugin *cniNetworkPlugin) SetUpPod(podNetwork PodNetwork) ([]NetResult, error) {
if err := plugin.networksAvailable(&podNetwork); err != nil {
return nil, err
}
@ -410,89 +543,24 @@ func (plugin *cniNetworkPlugin) SetUpPod(podNetwork PodNetwork) ([]cnitypes.Resu
plugin.podLock(podNetwork).Lock()
defer plugin.podUnlock(podNetwork)
_, err := plugin.loNetwork.addToNetwork(plugin.cacheDir, &podNetwork, "lo", "")
if err != nil {
loRt := buildLoopbackRuntimeConf(plugin.cacheDir, &podNetwork)
if _, err := plugin.loNetwork.addToNetwork(loRt, plugin.cniConfig); err != nil {
logrus.Errorf("Error while adding to cni lo network: %s", err)
return nil, err
}
results := make([]cnitypes.Result, 0)
if err := plugin.forEachNetwork(&podNetwork, func(network *cniNetwork, ifName string, podNetwork *PodNetwork) error {
ip := ""
if conf, ok := podNetwork.NetworkConfig[network.name]; ok {
ip = conf.IP
}
result, err := network.addToNetwork(plugin.cacheDir, podNetwork, ifName, ip)
results := make([]NetResult, 0)
if err := plugin.forEachNetwork(&podNetwork, false, func(network *cniNetwork, podNetwork *PodNetwork, rt *libcni.RuntimeConf) error {
result, err := network.addToNetwork(rt, plugin.cniConfig)
if err != nil {
logrus.Errorf("Error while adding pod to CNI network %q: %s", network.name, err)
return err
}
results = append(results, result)
return nil
}); err != nil {
return nil, err
}
return results, nil
}
func (plugin *cniNetworkPlugin) TearDownPod(podNetwork PodNetwork) error {
if err := plugin.networksAvailable(&podNetwork); err != nil {
return err
}
plugin.podLock(podNetwork).Lock()
defer plugin.podUnlock(podNetwork)
return plugin.forEachNetwork(&podNetwork, func(network *cniNetwork, ifName string, podNetwork *PodNetwork) error {
ip := ""
if conf, ok := podNetwork.NetworkConfig[network.name]; ok {
ip = conf.IP
}
if err := network.deleteFromNetwork(plugin.cacheDir, podNetwork, ifName, ip); err != nil {
logrus.Errorf("Error while removing pod from CNI network %q: %s", network.name, err)
return err
}
return nil
})
}
// GetPodNetworkStatus returns IP addressing and interface details for all
// networks attached to the pod.
func (plugin *cniNetworkPlugin) GetPodNetworkStatus(podNetwork PodNetwork) ([]cnitypes.Result, error) {
plugin.podLock(podNetwork).Lock()
defer plugin.podUnlock(podNetwork)
results := make([]cnitypes.Result, 0)
if err := plugin.forEachNetwork(&podNetwork, func(network *cniNetwork, ifName string, podNetwork *PodNetwork) error {
version := "4"
ip, mac, err := getContainerDetails(plugin.nsManager, podNetwork.NetNS, ifName, "-4")
if err != nil {
ip, mac, err = getContainerDetails(plugin.nsManager, podNetwork.NetNS, ifName, "-6")
if err != nil {
return err
}
version = "6"
}
// Until CNI's GET request lands, construct the Result manually
results = append(results, &cnicurrent.Result{
CNIVersion: "0.3.1",
Interfaces: []*cnicurrent.Interface{
{
Name: ifName,
Mac: mac.String(),
Sandbox: podNetwork.NetNS,
},
},
IPs: []*cnicurrent.IPConfig{
{
Version: version,
Interface: cnicurrent.Int(0),
Address: *ip,
},
results = append(results, NetResult{
Result: result,
NetAttachment: NetAttachment{
Name: network.name,
Ifname: rt.IfName,
},
})
return nil
@ -503,16 +571,139 @@ func (plugin *cniNetworkPlugin) GetPodNetworkStatus(podNetwork PodNetwork) ([]cn
return results, nil
}
func (network *cniNetwork) addToNetwork(cacheDir string, podNetwork *PodNetwork, ifName, ip string) (cnitypes.Result, error) {
rt, err := buildCNIRuntimeConf(cacheDir, podNetwork, ifName, ip)
func (plugin *cniNetworkPlugin) getCachedNetworkInfo(containerID string) ([]NetAttachment, error) {
cacheDir := libcni.CacheDir
if plugin.cacheDir != "" {
cacheDir = plugin.cacheDir
}
dirPath := filepath.Join(cacheDir, "results")
entries, err := ioutil.ReadDir(dirPath)
if err != nil {
logrus.Errorf("Error adding network: %v", err)
return nil, err
}
netconf, cninet := network.NetworkConfig, network.CNIConfig
logrus.Infof("About to add CNI network %s (type=%v)", netconf.Name, netconf.Plugins[0].Network.Type)
res, err := cninet.AddNetworkList(context.Background(), netconf, rt)
fileNames := make([]string, 0, len(entries))
for _, e := range entries {
fileNames = append(fileNames, e.Name())
}
sort.Strings(fileNames)
attachments := []NetAttachment{}
for _, fname := range fileNames {
part := fmt.Sprintf("-%s-", containerID)
pos := strings.Index(fname, part)
if pos <= 0 || pos+len(part) >= len(fname) {
continue
}
cacheFile := filepath.Join(dirPath, fname)
bytes, err := ioutil.ReadFile(cacheFile)
if err != nil {
logrus.Warningf("failed to read CNI cache file %s: %v", cacheFile, err)
continue
}
cachedInfo := struct {
Kind string `json:"kind"`
IfName string `json:"ifName"`
ContainerID string `json:"containerID"`
NetName string `json:"networkName"`
}{}
if err := json.Unmarshal(bytes, &cachedInfo); err != nil {
logrus.Warningf("failed to unmarshal CNI cache file %s: %v", cacheFile, err)
continue
}
if cachedInfo.Kind != libcni.CNICacheV1 {
logrus.Warningf("unknown CNI cache file %s kind %q", cacheFile, cachedInfo.Kind)
continue
}
if cachedInfo.ContainerID != containerID {
continue
}
// Ignore the loopback interface; it's handled separately
if cachedInfo.IfName == loIfname && cachedInfo.NetName == loNetname {
continue
}
if cachedInfo.IfName == "" || cachedInfo.NetName == "" {
logrus.Warningf("missing CNI cache file %s ifname %q or netname %q", cacheFile, cachedInfo.IfName, cachedInfo.NetName)
continue
}
attachments = append(attachments, NetAttachment{
Name: cachedInfo.NetName,
Ifname: cachedInfo.IfName,
})
}
return attachments, nil
}
// TearDownPod tears down pod networks. Prefers cached pod attachment information
// but falls back to given network attachment information.
func (plugin *cniNetworkPlugin) TearDownPod(podNetwork PodNetwork) error {
if len(podNetwork.Networks) == 0 {
attachments, err := plugin.getCachedNetworkInfo(podNetwork.ID)
if err == nil && len(attachments) > 0 {
podNetwork.Networks = attachments
}
}
if err := plugin.networksAvailable(&podNetwork); err != nil {
return err
}
loRt := buildLoopbackRuntimeConf(plugin.cacheDir, &podNetwork)
if err := plugin.loNetwork.deleteFromNetwork(loRt, plugin.cniConfig); err != nil {
logrus.Errorf("Error while removing pod from CNI lo network: %v", err)
// Loopback teardown errors are not fatal
}
plugin.podLock(podNetwork).Lock()
defer plugin.podUnlock(podNetwork)
return plugin.forEachNetwork(&podNetwork, true, func(network *cniNetwork, podNetwork *PodNetwork, rt *libcni.RuntimeConf) error {
if err := network.deleteFromNetwork(rt, plugin.cniConfig); err != nil {
logrus.Errorf("Error while removing pod from CNI network %q: %s", network.name, err)
return err
}
return nil
})
}
// GetPodNetworkStatus returns IP addressing and interface details for all
// networks attached to the pod.
func (plugin *cniNetworkPlugin) GetPodNetworkStatus(podNetwork PodNetwork) ([]NetResult, error) {
plugin.podLock(podNetwork).Lock()
defer plugin.podUnlock(podNetwork)
results := make([]NetResult, 0)
if err := plugin.forEachNetwork(&podNetwork, true, func(network *cniNetwork, podNetwork *PodNetwork, rt *libcni.RuntimeConf) error {
result, err := network.checkNetwork(rt, plugin.cniConfig, plugin.nsManager, podNetwork.NetNS)
if err != nil {
logrus.Errorf("Error while checking pod to CNI network %q: %s", network.name, err)
return err
}
if result != nil {
results = append(results, NetResult{
Result: result,
NetAttachment: NetAttachment{
Name: network.name,
Ifname: rt.IfName,
},
})
}
return nil
}); err != nil {
return nil, err
}
return results, nil
}
func (network *cniNetwork) addToNetwork(rt *libcni.RuntimeConf, cni *libcni.CNIConfig) (cnitypes.Result, error) {
logrus.Infof("About to add CNI network %s (type=%v)", network.name, network.config.Plugins[0].Network.Type)
res, err := cni.AddNetworkList(context.Background(), network.config, rt)
if err != nil {
logrus.Errorf("Error adding network: %v", err)
return nil, err
@ -521,24 +712,88 @@ func (network *cniNetwork) addToNetwork(cacheDir string, podNetwork *PodNetwork,
return res, nil
}
func (network *cniNetwork) deleteFromNetwork(cacheDir string, podNetwork *PodNetwork, ifName, ip string) error {
rt, err := buildCNIRuntimeConf(cacheDir, podNetwork, ifName, ip)
func (network *cniNetwork) checkNetwork(rt *libcni.RuntimeConf, cni *libcni.CNIConfig, nsManager *nsManager, netns string) (cnitypes.Result, error) {
logrus.Infof("About to check CNI network %s (type=%v)", network.name, network.config.Plugins[0].Network.Type)
gtet, err := cniversion.GreaterThanOrEqualTo(network.config.CNIVersion, "0.4.0")
if err != nil {
logrus.Errorf("Error deleting network: %v", err)
return err
return nil, err
}
netconf, cninet := network.NetworkConfig, network.CNIConfig
logrus.Infof("About to del CNI network %s (type=%v)", netconf.Name, netconf.Plugins[0].Network.Type)
err = cninet.DelNetworkList(context.Background(), netconf, rt)
var result cnitypes.Result
// When CNIVersion supports Check, use it. Otherwise fall back on what was done initially.
if gtet {
err = cni.CheckNetworkList(context.Background(), network.config, rt)
logrus.Infof("Checking CNI network %s (config version=%v)", network.name, network.config.CNIVersion)
if err != nil {
logrus.Errorf("Error checking network: %v", err)
return nil, err
}
}
result, err = cni.GetNetworkListCachedResult(network.config, rt)
if err != nil {
logrus.Errorf("Error GetNetworkListCachedResult: %v", err)
return nil, err
} else if result != nil {
return result, nil
}
// result doesn't exist, create one
logrus.Infof("Checking CNI network %s (config version=%v) nsManager=%v", network.name, network.config.CNIVersion, nsManager)
var cniInterface *cnicurrent.Interface
ips := []*cnicurrent.IPConfig{}
errs := []error{}
for _, version := range []string{"4", "6"} {
ip, mac, err := getContainerDetails(nsManager, netns, rt.IfName, "-"+version)
if err == nil {
if cniInterface == nil {
cniInterface = &cnicurrent.Interface{
Name: rt.IfName,
Mac: mac.String(),
Sandbox: netns,
}
}
ips = append(ips, &cnicurrent.IPConfig{
Version: version,
Interface: cnicurrent.Int(0),
Address: *ip,
})
} else {
errs = append(errs, err)
}
}
if cniInterface == nil || len(ips) == 0 {
return nil, fmt.Errorf("neither IPv4 nor IPv6 found when retrieving network status: %v", errs)
}
result = &cnicurrent.Result{
CNIVersion: network.config.CNIVersion,
Interfaces: []*cnicurrent.Interface{cniInterface},
IPs: ips,
}
// Result must be the same CNIVersion as the CNI config
converted, err := result.GetAsVersion(network.config.CNIVersion)
if err != nil {
return nil, err
}
return converted, nil
}
func (network *cniNetwork) deleteFromNetwork(rt *libcni.RuntimeConf, cni *libcni.CNIConfig) error {
logrus.Infof("About to del CNI network %s (type=%v)", network.name, network.config.Plugins[0].Network.Type)
if err := cni.DelNetworkList(context.Background(), network.config, rt); err != nil {
logrus.Errorf("Error deleting network: %v", err)
return err
}
return nil
}
func buildCNIRuntimeConf(cacheDir string, podNetwork *PodNetwork, ifName, ip string) (*libcni.RuntimeConf, error) {
func buildCNIRuntimeConf(cacheDir string, podNetwork *PodNetwork, ifName string, runtimeConfig RuntimeConfig) (*libcni.RuntimeConf, error) {
logrus.Infof("Got pod network %+v", podNetwork)
rt := &libcni.RuntimeConf{
@ -552,9 +807,11 @@ func buildCNIRuntimeConf(cacheDir string, podNetwork *PodNetwork, ifName, ip str
{"K8S_POD_NAME", podNetwork.Name},
{"K8S_POD_INFRA_CONTAINER_ID", podNetwork.ID},
},
CapabilityArgs: map[string]interface{}{},
}
// Add requested static IP to CNI_ARGS
ip := runtimeConfig.IP
if ip != "" {
if tstIP := net.ParseIP(ip); tstIP == nil {
return nil, fmt.Errorf("unable to parse IP address %q", ip)
@ -562,13 +819,26 @@ func buildCNIRuntimeConf(cacheDir string, podNetwork *PodNetwork, ifName, ip str
rt.Args = append(rt.Args, [2]string{"IP", ip})
}
if len(podNetwork.PortMappings) == 0 {
return rt, nil
// Set PortMappings in Capabilities
if len(runtimeConfig.PortMappings) != 0 {
rt.CapabilityArgs["portMappings"] = runtimeConfig.PortMappings
}
rt.CapabilityArgs = map[string]interface{}{
"portMappings": podNetwork.PortMappings,
// Set Bandwidth in Capabilities
if runtimeConfig.Bandwidth != nil {
rt.CapabilityArgs["bandwidth"] = map[string]uint64{
"ingressRate": runtimeConfig.Bandwidth.IngressRate,
"ingressBurst": runtimeConfig.Bandwidth.IngressBurst,
"egressRate": runtimeConfig.Bandwidth.EgressRate,
"egressBurst": runtimeConfig.Bandwidth.EgressBurst,
}
}
// Set IpRanges in Capabilities
if len(runtimeConfig.IpRanges) > 0 {
rt.CapabilityArgs["ipRanges"] = runtimeConfig.IpRanges
}
return rt, nil
}

View File

@ -24,12 +24,44 @@ type PortMapping struct {
HostIP string `json:"hostIP"`
}
// NetworkConfig is additional configuration for a single CNI network.
type NetworkConfig struct {
// IpRange maps to the standard CNI ipRanges Capability
// see: https://github.com/containernetworking/cni/blob/master/CONVENTIONS.md
type IpRange struct {
// Subnet is the whole CIDR
Subnet string `json:"subnet"`
// RangeStart is the first available IP in subnet
RangeStart string `json:"rangeStart,omitempty"`
// RangeEnd is the last available IP in subnet
RangeEnd string `json:"rangeEnd,omitempty"`
// Gateway is the gateway of subnet
Gateway string `json:"gateway,omitempty"`
}
// RuntimeConfig is additional configuration for a single CNI network that
// is pod-specific rather than general to the network.
type RuntimeConfig struct {
// IP is a static IP to be specified in the network. Can only be used
// with the hostlocal IP allocator. If left unset, an IP will be
// dynamically allocated.
IP string
// PortMappings is the port mapping of the sandbox.
PortMappings []PortMapping
// Bandwidth is the bandwidth limiting of the pod
Bandwidth *BandwidthConfig
// IpRanges is the ip range gather which is used for address allocation
IpRanges [][]IpRange
}
// BandwidthConfig maps to the standard CNI bandwidth Capability
// see: https://github.com/containernetworking/cni/blob/master/CONVENTIONS.md
type BandwidthConfig struct {
// IngressRate is a limit for incoming traffic in bps
IngressRate uint64
IngressBurst uint64
// EgressRate is a limit for outgoing traffic in bps
EgressRate uint64
EgressBurst uint64
}
// PodNetwork configures the network of a pod sandbox.
@ -42,17 +74,34 @@ type PodNetwork struct {
ID string
// NetNS is the network namespace path of the sandbox.
NetNS string
// PortMappings is the port mapping of the sandbox.
PortMappings []PortMapping
// Networks is a list of CNI network names to attach to the sandbox
// Leave this list empty to attach the default network to the sandbox
Networks []string
// Networks is a list of CNI network names (and optional interface
// names) to attach to the sandbox. Leave this list empty to attach the
// default network to the sandbox
Networks []NetAttachment
// NetworkConfig is configuration specific to a single CNI network.
// It is optional, and can be omitted for some or all specified networks
// without issue.
NetworkConfig map[string]NetworkConfig
RuntimeConfig map[string]RuntimeConfig
}
// NetAttachment describes a container network attachment
type NetAttachment struct {
// NetName contains the name of the CNI network to which the container
// should be or is attached
Name string
// Ifname contains the optional interface name of the attachment
Ifname string
}
// NetResult contains the result the network attachment operation
type NetResult struct {
// Result is the CNI Result
Result types.Result
// NetAttachment contains the network and interface names of this
// network attachment
NetAttachment
}
// CNIPlugin is the interface that needs to be implemented by a plugin
@ -68,13 +117,13 @@ type CNIPlugin interface {
// SetUpPod is the method called after the sandbox container of
// the pod has been created but before the other containers of the
// pod are launched.
SetUpPod(network PodNetwork) ([]types.Result, error)
SetUpPod(network PodNetwork) ([]NetResult, error)
// TearDownPod is the method called before a pod's sandbox container will be deleted
TearDownPod(network PodNetwork) error
// Status is the method called to obtain the ipv4 or ipv6 addresses of the pod sandbox
GetPodNetworkStatus(network PodNetwork) ([]types.Result, error)
GetPodNetworkStatus(network PodNetwork) ([]NetResult, error)
// NetworkStatus returns error if the network plugin is in error state
Status() error

View File

@ -1,13 +0,0 @@
github.com/containernetworking/cni fbb95fff8a5239a4295c991efa8a397d43118f7e
github.com/fsnotify/fsnotify 1485a34d5d5723fea214f5710708e19a831720e4
github.com/sirupsen/logrus 787e519fa85519b874dead61020de598e9a23944
github.com/onsi/ginkgo eea6ad008b96acdaa524f5b409513bf062b500ad
github.com/onsi/gomega 90e289841c1ed79b7a598a7cd9959750cb5e89e2
golang.org/x/net 63eda1eb0650888965ead1296efd04d0b2b61128
gopkg.in/yaml.v2 51d6538a90f86fe93ac480b35f37b2be17fef232
golang.org/x/text e3703dcdd614d2d7488fff034c75c551ea25da95
golang.org/x/sys f49334f85ddcf0f08d7fb6dd7363e9e6d6b777eb
github.com/hpcloud/tail a1dbeea552b7c8df4b542c66073e393de198a800
gopkg.in/tomb.v1 dd632973f1e7218eb1089048e0798ec9ae7dceb8
gopkg.in/fsnotify/fsnotify.v1 c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9
github.com/konsorten/go-windows-terminal-sequences f55edac94c9bbba5d6182a4be46d86a2c9b5b50e