adding conflist support

This commit is contained in:
rkamudhan
2018-07-27 00:04:52 +01:00
committed by Kuralamudhan Ramakrishnan
parent 42e6894962
commit 7372922617
6 changed files with 232 additions and 52 deletions

View File

@@ -0,0 +1,30 @@
---
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: ptp-tuning-conflist
spec:
config: '{
"cniVersion": "0.3.1",
"name": "ptp-tuning-conflist",
"plugins": [{
"type": "ptp",
"ipMasq": true,
"mtu": 512,
"ipam": {
"type": "host-local",
"subnet": "172.16.0.0/24"
},
"dns": {
"nameservers": ["172.16.1.1"]
}
},
{
"name": "mytuning",
"type": "tuning",
"sysctl": {
"net.core.somaxconn": "500"
}
}
]
}'

View File

@@ -0,0 +1,15 @@
---
apiVersion: v1
kind: Pod
metadata:
name: pod-case-05
annotations:
k8s.v1.cni.cncf.io/networks: '[
{ "name": "ptp-tuning-conflist" }
]'
spec:
containers:
- name: pod-case-05
image: docker.io/centos/tools:latest
command:
- /sbin/init

View File

@@ -55,7 +55,7 @@ func (d *defaultKubeClient) GetPod(namespace, name string) (*v1.Pod, error) {
return d.client.Core().Pods(namespace).Get(name, metav1.GetOptions{})
}
func getPodNetworkAnnotation(client KubeClient, k8sArgs types.K8sArgs) (string, string, error) {
func getPodNetworkAnnotation(client KubeClient, k8sArgs *types.K8sArgs) (string, string, error) {
var err error
pod, err := client.GetPod(string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME))
@@ -152,10 +152,8 @@ func getCNIConfigFromFile(name string, confdir string) ([]byte, error) {
// or .config (in that order) file on-disk whose JSON
// “name” key matches this Network objects name.
//Todo
// support conflist for chaining mechanism
// In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go#getDefaultCNINetwork
files, err := libcni.ConfFiles(confdir, []string{".conf", ".json"})
files, err := libcni.ConfFiles(confdir, []string{".conf", ".json", ".conflist"})
switch {
case err != nil:
return nil, fmt.Errorf("No networks found in %s", confdir)
@@ -164,18 +162,31 @@ func getCNIConfigFromFile(name string, confdir string) ([]byte, error) {
}
for _, confFile := range files {
conf, err := libcni.ConfFromFile(confFile)
if err != nil {
return nil, fmt.Errorf("Error loading CNI config file %s: %v", confFile, err)
}
if conf.Network.Name == name {
// Ensure the config has a "type" so we know what plugin to run.
// Also catches the case where somebody put a conflist into a conf file.
if conf.Network.Type == "" {
return nil, fmt.Errorf("Error loading CNI config file %s: no 'type'; perhaps this is a .conflist?", confFile)
var confList *libcni.NetworkConfigList
if strings.HasSuffix(confFile, ".conflist") {
confList, err = libcni.ConfListFromFile(confFile)
if err != nil {
return nil, fmt.Errorf("Error loading CNI conflist file %s: %v", confFile, err)
}
if confList.Name == name {
return confList.Bytes, nil
}
} else {
conf, err := libcni.ConfFromFile(confFile)
if err != nil {
return nil, fmt.Errorf("Error loading CNI config file %s: %v", confFile, err)
}
if conf.Network.Name == name {
// Ensure the config has a "type" so we know what plugin to run.
// Also catches the case where somebody put a conflist into a conf file.
if conf.Network.Type == "" {
return nil, fmt.Errorf("Error loading CNI config file %s: no 'type'; perhaps this is a .conflist?", confFile)
}
return conf.Bytes, nil
}
return conf.Bytes, nil
}
}
@@ -211,7 +222,7 @@ func cniConfigFromNetworkResource(customResource *types.NetworkAttachmentDefinit
var err error
emptySpec := types.NetworkAttachmentDefinitionSpec{}
if (customResource.Spec == emptySpec) {
if customResource.Spec == emptySpec {
// Network Spec empty; generate delegate from CNI JSON config
// from the configuration directory that has the same network
// name as the custom resource
@@ -262,6 +273,17 @@ type KubeClient interface {
GetPod(namespace, name string) (*v1.Pod, error)
}
func GetK8sArgs(args *skel.CmdArgs) (*types.K8sArgs, error) {
k8sArgs := &types.K8sArgs{}
err := cnitypes.LoadArgs(args.Args, k8sArgs)
if err != nil {
return nil, err
}
return k8sArgs, nil
}
func GetK8sClient(kubeconfig string, kubeClient KubeClient) (KubeClient, error) {
// If we get a valid kubeClient (eg from testcases) just return that
// one.
@@ -299,13 +321,7 @@ func GetK8sClient(kubeconfig string, kubeClient KubeClient) (KubeClient, error)
return &defaultKubeClient{client: client}, nil
}
func GetK8sNetwork(k8sclient KubeClient, args *skel.CmdArgs, confdir string) ([]*types.DelegateNetConf, error) {
k8sArgs := types.K8sArgs{}
err := cnitypes.LoadArgs(args.Args, &k8sArgs)
if err != nil {
return nil, err
}
func GetK8sNetwork(k8sclient KubeClient, k8sArgs *types.K8sArgs, confdir string) ([]*types.DelegateNetConf, error) {
netAnnot, defaultNamespace, err := getPodNetworkAnnotation(k8sclient, k8sArgs)
if err != nil {

View File

@@ -25,6 +25,7 @@ import (
"os"
"path/filepath"
"github.com/containernetworking/cni/libcni"
"github.com/containernetworking/cni/pkg/invoke"
"github.com/containernetworking/cni/pkg/skel"
cnitypes "github.com/containernetworking/cni/pkg/types"
@@ -104,43 +105,98 @@ func validateIfName(nsname string, ifname string) error {
return err
}
func delegateAdd(exec invoke.Exec, ifName string, delegate *types.DelegateNetConf) (cnitypes.Result, error) {
if os.Setenv("CNI_IFNAME", ifName) != nil {
return nil, fmt.Errorf("Multus: error in setting CNI_IFNAME")
}
func conflistAdd(rt *libcni.RuntimeConf, rawnetconflist []byte, binDir string) (cnitypes.Result, error) {
// In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go
binDirs := []string{binDir}
cniNet := libcni.CNIConfig{Path: binDirs}
if err := validateIfName(os.Getenv("CNI_NETNS"), ifName); err != nil {
return nil, fmt.Errorf("cannot set %q ifname to %q: %v", delegate.Type, ifName, err)
}
result, err := invoke.DelegateAdd(delegate.Type, delegate.Bytes, exec)
confList, err := libcni.ConfListFromBytes(rawnetconflist)
if err != nil {
return nil, fmt.Errorf("Multus: error in invoke Delegate add - %q: %v", delegate.Type, err)
return nil, fmt.Errorf("error in converting the raw bytes to conflist: %v", err)
}
result, err := cniNet.AddNetworkList(confList, rt)
if err != nil {
return nil, fmt.Errorf("error in getting result from AddNetworkList: %v", err)
}
return result, nil
}
func delegateDel(exec invoke.Exec, ifName string, delegateConf *types.DelegateNetConf) error {
func conflistDel(rt *libcni.RuntimeConf, rawnetconflist []byte, binDir string) error {
// In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go
binDirs := []string{binDir}
cniNet := libcni.CNIConfig{Path: binDirs}
confList, err := libcni.ConfListFromBytes(rawnetconflist)
if err != nil {
return fmt.Errorf("error in converting the raw bytes to conflist: %v", err)
}
err = cniNet.DelNetworkList(confList, rt)
if err != nil {
return fmt.Errorf("error in getting result from DelNetworkList: %v", err)
}
return err
}
func delegateAdd(exec invoke.Exec, ifName string, delegate *types.DelegateNetConf, rt *libcni.RuntimeConf, binDir string) (cnitypes.Result, error) {
if os.Setenv("CNI_IFNAME", ifName) != nil {
return nil, fmt.Errorf("Multus: error in setting CNI_IFNAME")
}
if err := validateIfName(os.Getenv("CNI_NETNS"), ifName); err != nil {
return nil, fmt.Errorf("cannot set %q ifname to %q: %v", delegate.Conf.Type, ifName, err)
}
if delegate.ConfListPlugin != false {
result, err := conflistAdd(rt, delegate.Bytes, binDir)
if err != nil {
return nil, fmt.Errorf("Multus: error in invoke Conflist add - %q: %v", delegate.ConfList.Name, err)
}
return result, nil
}
result, err := invoke.DelegateAdd(delegate.Conf.Type, delegate.Bytes, exec)
if err != nil {
return nil, fmt.Errorf("Multus: error in invoke Delegate add - %q: %v", delegate.Conf.Type, err)
}
return result, nil
}
func delegateDel(exec invoke.Exec, ifName string, delegateConf *types.DelegateNetConf, rt *libcni.RuntimeConf, binDir string) error {
if os.Setenv("CNI_IFNAME", ifName) != nil {
return fmt.Errorf("Multus: error in setting CNI_IFNAME")
}
if err := invoke.DelegateDel(delegateConf.Type, delegateConf.Bytes, exec); err != nil {
return fmt.Errorf("Multus: error in invoke Delegate del - %q: %v", delegateConf.Type, err)
if delegateConf.ConfListPlugin != false {
err := conflistDel(rt, delegateConf.Bytes, binDir)
if err != nil {
return fmt.Errorf("Multus: error in invoke Conflist Del - %q: %v", delegateConf.ConfList.Name, err)
}
return err
}
if err := invoke.DelegateDel(delegateConf.Conf.Type, delegateConf.Bytes, exec); err != nil {
return fmt.Errorf("Multus: error in invoke Delegate del - %q: %v", delegateConf.Conf.Type, err)
}
return nil
}
func delPlugins(exec invoke.Exec, argIfname string, delegates []*types.DelegateNetConf, lastIdx int) error {
func delPlugins(exec invoke.Exec, argIfname string, delegates []*types.DelegateNetConf, lastIdx int, rt *libcni.RuntimeConf, binDir string) error {
if os.Setenv("CNI_COMMAND", "DEL") != nil {
return fmt.Errorf("Multus: error in setting CNI_COMMAND to DEL")
}
for idx := lastIdx; idx >= 0; idx-- {
ifName := getIfname(delegates[idx], argIfname, idx)
if err := delegateDel(exec, ifName, delegates[idx]); err != nil {
rt.IfName = ifName
if err := delegateDel(exec, ifName, delegates[idx], rt, binDir); err != nil {
return err
}
}
@@ -150,7 +206,7 @@ func delPlugins(exec invoke.Exec, argIfname string, delegates []*types.DelegateN
// Attempts to load Kubernetes-defined delegates and add them to the Multus config.
// Returns the number of Kubernetes-defined delegates added or an error.
func tryLoadK8sDelegates(args *skel.CmdArgs, conf *types.NetConf, kubeClient k8s.KubeClient) (int, error) {
func tryLoadK8sDelegates(k8sArgs *types.K8sArgs, conf *types.NetConf, kubeClient k8s.KubeClient) (int, error) {
var err error
kubeClient, err = k8s.GetK8sClient(conf.Kubeconfig, kubeClient)
@@ -166,7 +222,7 @@ func tryLoadK8sDelegates(args *skel.CmdArgs, conf *types.NetConf, kubeClient k8s
return 0, nil
}
delegates, err := k8s.GetK8sNetwork(kubeClient, args, conf.ConfDir)
delegates, err := k8s.GetK8sNetwork(kubeClient, k8sArgs, conf.ConfDir)
if err != nil {
if _, ok := err.(*k8s.NoK8sNetworkError); ok {
return 0, nil
@@ -187,7 +243,12 @@ func cmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) (cn
return nil, fmt.Errorf("err in loading netconf: %v", err)
}
numK8sDelegates, err := tryLoadK8sDelegates(args, n, kubeClient)
k8sArgs, err := k8s.GetK8sArgs(args)
if err != nil {
return nil, fmt.Errorf("Multus: Err in getting k8s args: %v", err)
}
numK8sDelegates, err := tryLoadK8sDelegates(k8sArgs, n, kubeClient)
if err != nil {
return nil, err
}
@@ -200,11 +261,13 @@ func cmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) (cn
}
var result, tmpResult cnitypes.Result
var rt *libcni.RuntimeConf
lastIdx := 0
for idx, delegate := range n.Delegates {
lastIdx = idx
ifName := getIfname(delegate, args.IfName, idx)
tmpResult, err = delegateAdd(exec, ifName, delegate)
rt, _ = types.LoadCNIRuntimeConf(args, k8sArgs, ifName)
tmpResult, err = delegateAdd(exec, ifName, delegate, rt, n.BinDir)
if err != nil {
break
}
@@ -217,7 +280,7 @@ func cmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) (cn
if err != nil {
// Ignore errors; DEL must be idempotent anyway
_ = delPlugins(exec, args.IfName, n.Delegates, lastIdx)
_ = delPlugins(exec, args.IfName, n.Delegates, lastIdx, rt, n.BinDir)
return nil, err
}
@@ -241,7 +304,12 @@ func cmdDel(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) err
return err
}
numK8sDelegates, err := tryLoadK8sDelegates(args, in, kubeClient)
k8sArgs, err := k8s.GetK8sArgs(args)
if err != nil {
return fmt.Errorf("Multus: Err in getting k8s args: %v", err)
}
numK8sDelegates, err := tryLoadK8sDelegates(k8sArgs, in, kubeClient)
if err != nil {
return err
}
@@ -262,7 +330,8 @@ func cmdDel(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) err
}
}
return delPlugins(exec, args.IfName, in.Delegates, len(in.Delegates)-1)
rt, _ := types.LoadCNIRuntimeConf(args, k8sArgs, "")
return delPlugins(exec, args.IfName, in.Delegates, len(in.Delegates)-1, rt, in.BinDir)
}
func main() {

View File

@@ -19,33 +19,74 @@ import (
"encoding/json"
"fmt"
"github.com/intel/multus-cni/logging"
"github.com/containernetworking/cni/libcni"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/cni/pkg/version"
"github.com/intel/multus-cni/logging"
)
const (
defaultCNIDir = "/var/lib/cni/multus"
defaultConfDir = "/etc/cni/multus/net.d"
defaultBinDir = "/opt/cni/bin"
)
func LoadDelegateNetConfList(bytes []byte, delegateConf *DelegateNetConf) error {
if err := json.Unmarshal(bytes, &delegateConf.ConfList); err != nil {
return fmt.Errorf("err in unmarshalling delegate conflist: %v", err)
}
if delegateConf.ConfList.Plugins == nil {
return fmt.Errorf("delegate must have the 'type'or 'Plugin' field")
}
if delegateConf.ConfList.Plugins[0].Type == "" {
return fmt.Errorf("a plugin delegate must have the 'type' field")
}
delegateConf.ConfListPlugin = true
return nil
}
func LoadCNIRuntimeConf(args *skel.CmdArgs, k8sArgs *K8sArgs, ifName string) (*libcni.RuntimeConf, error) {
// In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go#buildCNIRuntimeConf
// Todo
// ingress, egress and bandwidth capability features as same as kubelet.
rt := &libcni.RuntimeConf{
ContainerID: args.ContainerID,
NetNS: args.Netns,
IfName: ifName,
Args: [][2]string{
{"IgnoreUnknown", "1"},
{"K8S_POD_NAMESPACE", string(k8sArgs.K8S_POD_NAMESPACE)},
{"K8S_POD_NAME", string(k8sArgs.K8S_POD_NAME)},
{"K8S_POD_INFRA_CONTAINER_ID", string(k8sArgs.K8S_POD_INFRA_CONTAINER_ID)},
},
}
return rt, nil
}
// Convert raw CNI JSON into a DelegateNetConf structure
func LoadDelegateNetConf(bytes []byte, ifnameRequest string) (*DelegateNetConf, error) {
delegateConf := &DelegateNetConf{}
if err := json.Unmarshal(bytes, delegateConf); err != nil {
return nil, fmt.Errorf("error unmarshalling delegate config: %v", err)
if err := json.Unmarshal(bytes, &delegateConf.Conf); err != nil {
return nil, fmt.Errorf("error in LoadDelegateNetConf - unmarshalling delegate config: %v", err)
}
delegateConf.Bytes = bytes
// Do some minimal validation
if delegateConf.Type == "" {
return nil, fmt.Errorf("delegate must have the 'type' field")
if delegateConf.Conf.Type == "" {
if err := LoadDelegateNetConfList(bytes, delegateConf); err != nil {
return nil, fmt.Errorf("error in LoadDelegateNetConf: %v")
}
}
if ifnameRequest != "" {
delegateConf.IfnameRequest = ifnameRequest
}
delegateConf.Bytes = bytes
return delegateConf, nil
}
@@ -93,10 +134,15 @@ func LoadNetConf(bytes []byte) (*NetConf, error) {
if netconf.CNIDir == "" {
netconf.CNIDir = defaultCNIDir
}
if netconf.ConfDir == "" {
netconf.ConfDir = defaultConfDir
}
if netconf.BinDir == "" {
netconf.BinDir = defaultBinDir
}
for idx, rawConf := range netconf.RawDelegates {
bytes, err := json.Marshal(rawConf)
if err != nil {

View File

@@ -34,6 +34,7 @@ type NetConf struct {
ConfDir string `json:"confDir"`
CNIDir string `json:"cniDir"`
BinDir string `json:"binDir"`
// RawDelegates is private to the NetConf class; use Delegates instead
RawDelegates []map[string]interface{} `json:"delegates"`
Delegates []*DelegateNetConf `json:"-"`
@@ -43,10 +44,13 @@ type NetConf struct {
}
type DelegateNetConf struct {
types.NetConf
Conf types.NetConf
ConfList types.NetConfList
IfnameRequest string `json:"ifnameRequest,omitempty"`
// MasterPlugin is only used internal housekeeping
MasterPlugin bool `json:"-"`
// Conflist plugin is only used internal housekeeping
ConfListPlugin bool `json:"-"`
// Raw JSON
Bytes []byte