Support gateway in NetworkSelectionElement

Changes config JSON from gateway to default-route, adds Readme, fixes lint

Co-authored-by: dougbtv <dosmith@redhat.com>
This commit is contained in:
Tomofumi Hayashi 2019-08-27 16:52:58 +09:00 committed by Doug Smith
parent 84c348ce18
commit 165e23b72c
6 changed files with 187 additions and 1 deletions

View File

@ -469,6 +469,72 @@ $ kubectl exec -it pod-case-06 -- ip -d address
| macvlan1 | macvlan interface (macvlan-conf-1) | | macvlan1 | macvlan interface (macvlan-conf-1) |
| net2 | macvlan interface (macvlan-conf-2) | | net2 | macvlan interface (macvlan-conf-2) |
## Specifying a default route for a specific attachment
Typically, the default route for a pod will route traffic over the `eth0` and therefore over the cluster-wide default network. You may wish to specify that a different network attachment will have the default route.
You can achieve this by using the JSON formatted annotation and specifying a `default-route` key.
*NOTE*: It's important that you consider that this may impact some functionality of getting traffic to route over the cluster-wide default network.
For example, we have a this configuration for macvlan:
```
cat <<EOF | kubectl create -f -
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: macvlan-conf
spec:
config: '{
"cniVersion": "0.3.0",
"type": "macvlan",
"master": "eth0",
"mode": "bridge",
"ipam": {
"type": "host-local",
"subnet": "192.168.2.0/24",
"rangeStart": "192.168.2.200",
"rangeEnd": "192.168.2.216",
"routes": [
{ "dst": "0.0.0.0/0" }
],
"gateway": "192.168.2.1"
}
}'
EOF
```
We can then create a pod which uses the `default-route` key in the JSON formatted `k8s.v1.cni.cncf.io/networks` annotation.
```
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: samplepod
annotations:
k8s.v1.cni.cncf.io/networks: '[{
"name": "macvlan-conf",
"default-route": ["192.168.2.1"]
}]'
spec:
containers:
- name: samplepod
command: ["/bin/bash", "-c", "trap : TERM INT; sleep infinity & wait"]
image: dougbtv/centos-network
EOF
```
This will set `192.168.2.1` as the default route over the `net1` interface, such as:
```
$ kubectl exec -it samplepod -- ip route
default via 192.168.2.1 dev net1
10.244.0.0/24 dev eth0 proto kernel scope link src 10.244.0.169
10.244.0.0/16 via 10.244.0.1 dev eth0
```
## Entrypoint Parameters ## Entrypoint Parameters
Multus CNI, when installed using the daemonset-style installation uses an entrypoint script which copies the Multus binary into place, places CNI configurations. This entrypoint takes a variety of parameters for customization. Multus CNI, when installed using the daemonset-style installation uses an entrypoint script which copies the Multus binary into place, places CNI configurations. This entrypoint takes a variety of parameters for customization.

View File

@ -490,8 +490,24 @@ func TryLoadPodDelegates(k8sArgs *types.K8sArgs, conf *types.NetConf, kubeClient
if err = conf.AddDelegates(delegates); err != nil { if err = conf.AddDelegates(delegates); err != nil {
return 0, nil, err return 0, nil, err
} }
// Check gatewayRequest is configured in delegates
// and mark its config if gateway filter is required
isGatewayConfigured := false
for _, delegate := range conf.Delegates {
if delegate.GatewayRequest != nil {
isGatewayConfigured = true
break
}
}
if isGatewayConfigured == true {
types.CheckGatewayConfig(conf.Delegates)
}
return len(delegates), clientInfo, nil return len(delegates), clientInfo, nil
} }
return 0, clientInfo, nil return 0, clientInfo, nil
} }

View File

@ -38,6 +38,7 @@ import (
"github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/ns"
k8s "github.com/intel/multus-cni/k8sclient" k8s "github.com/intel/multus-cni/k8sclient"
"github.com/intel/multus-cni/logging" "github.com/intel/multus-cni/logging"
"github.com/intel/multus-cni/netutils"
"github.com/intel/multus-cni/types" "github.com/intel/multus-cni/types"
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
@ -419,6 +420,14 @@ func cmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) (cn
return nil, logging.Errorf("Multus: error adding pod to network %q: %v", netName, err) return nil, logging.Errorf("Multus: error adding pod to network %q: %v", netName, err)
} }
// Remove gateway from routing table if the gateway is not used
if delegate.IsFilterGateway {
tmpResult, err = netutils.DeleteDefaultGW(args, ifName, &tmpResult)
if err != nil {
return nil, logging.Errorf("Multus: Err in deleting gateway: %v", err)
}
}
// Master plugin result is always used if present // Master plugin result is always used if present
if delegate.MasterPlugin || result == nil { if delegate.MasterPlugin || result == nil {
result = tmpResult result = tmpResult

62
netutils/netutils.go Normal file
View File

@ -0,0 +1,62 @@
// Copyright (c) 2019 Multus 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 netutils
import (
"github.com/containernetworking/cni/pkg/skel"
cnitypes "github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/intel/multus-cni/logging"
"github.com/vishvananda/netlink"
)
// DeleteDefaultGW removes the default gateway from marked interfaces.
func DeleteDefaultGW(args *skel.CmdArgs, ifName string, res *cnitypes.Result) (*current.Result, error) {
logging.Debugf("XXX: DeleteDefaultGW: %s", args.Netns)
result, err := current.NewResultFromResult(*res)
if err != nil {
return nil, logging.Errorf("XXX: %v", err)
}
netns, err := ns.GetNS(args.Netns)
if err != nil {
return nil, logging.Errorf("XXX: %v", err)
}
defer netns.Close()
err = netns.Do(func(_ ns.NetNS) error {
var err error
link, _ := netlink.LinkByName(ifName)
routes, _ := netlink.RouteList(link, netlink.FAMILY_ALL)
for _, nlroute := range routes {
if nlroute.Dst == nil {
err = netlink.RouteDel(&nlroute)
}
}
return err
})
var newRoutes []*cnitypes.Route
for _, route := range result.Routes {
if mask, _ := route.Dst.Mask.Size(); mask != 0 {
newRoutes = append(newRoutes, route)
}
}
result.Routes = newRoutes
return result, err
}

View File

@ -17,6 +17,7 @@ package types
import ( import (
"encoding/json" "encoding/json"
"net"
"github.com/containernetworking/cni/libcni" "github.com/containernetworking/cni/libcni"
"github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/skel"
@ -111,6 +112,9 @@ func LoadDelegateNetConf(bytes []byte, net *NetworkSelectionElement, deviceID st
if net.PortMappingsRequest != nil { if net.PortMappingsRequest != nil {
delegateConf.PortMappingsRequest = net.PortMappingsRequest delegateConf.PortMappingsRequest = net.PortMappingsRequest
} }
if net.GatewayRequest != nil {
delegateConf.GatewayRequest = append(delegateConf.GatewayRequest, net.GatewayRequest...)
}
} }
delegateConf.Bytes = bytes delegateConf.Bytes = bytes
@ -183,6 +187,18 @@ func CreateCNIRuntimeConf(args *skel.CmdArgs, k8sArgs *K8sArgs, ifName string, r
return rt return rt
} }
// GetGatewayFromResult retrieves gateway IP addresses from CNI result
func GetGatewayFromResult(result *current.Result) []net.IP {
var gateways []net.IP
for _, route := range result.Routes {
if mask, _ := route.Dst.Mask.Size(); mask == 0 {
gateways = append(gateways, route.GW)
}
}
return gateways
}
// LoadNetworkStatus create network status from CNI result // LoadNetworkStatus create network status from CNI result
func LoadNetworkStatus(r types.Result, netName string, defaultNet bool) (*NetworkStatus, error) { func LoadNetworkStatus(r types.Result, netName string, defaultNet bool) (*NetworkStatus, error) {
logging.Debugf("LoadNetworkStatus: %v, %s, %t", r, netName, defaultNet) logging.Debugf("LoadNetworkStatus: %v, %s, %t", r, netName, defaultNet)
@ -215,6 +231,7 @@ func LoadNetworkStatus(r types.Result, netName string, defaultNet bool) (*Networ
} }
netstatus.DNS = result.DNS netstatus.DNS = result.DNS
netstatus.Gateway = GetGatewayFromResult(result)
return netstatus, nil return netstatus, nil
@ -449,6 +466,17 @@ func addCNIArgsInConfList(inBytes []byte, cniArgs *map[string]interface{}) ([]by
return configBytes, nil return configBytes, nil
} }
// CheckGatewayConfig check gatewayRequest and mark IsFilterGateway flag if
// gw filtering is required
func CheckGatewayConfig(delegates []*DelegateNetConf) {
// Check the Gateway
for i, delegate := range delegates {
if delegate.GatewayRequest == nil {
delegates[i].IsFilterGateway = true
}
}
}
// CheckSystemNamespaces checks whether given namespace is in systemNamespaces or not. // CheckSystemNamespaces checks whether given namespace is in systemNamespaces or not.
func CheckSystemNamespaces(namespace string, systemNamespaces []string) bool { func CheckSystemNamespaces(namespace string, systemNamespaces []string) bool {
for _, nsname := range systemNamespaces { for _, nsname := range systemNamespaces {

View File

@ -88,6 +88,7 @@ type NetworkStatus struct {
Mac string `json:"mac,omitempty"` Mac string `json:"mac,omitempty"`
Default bool `json:"default,omitempty"` Default bool `json:"default,omitempty"`
DNS types.DNS `json:"dns,omitempty"` DNS types.DNS `json:"dns,omitempty"`
Gateway []net.IP `json:"default-route,omitempty"`
} }
// DelegateNetConf for net-attach-def for pod // DelegateNetConf for net-attach-def for pod
@ -99,6 +100,8 @@ type DelegateNetConf struct {
IPRequest []string `json:"ipRequest,omitempty"` IPRequest []string `json:"ipRequest,omitempty"`
PortMappingsRequest []*PortMapEntry `json:"-"` PortMappingsRequest []*PortMapEntry `json:"-"`
BandwidthRequest *BandwidthEntry `json:"-"` BandwidthRequest *BandwidthEntry `json:"-"`
GatewayRequest []net.IP `json:"default-route,omitempty"`
IsFilterGateway bool
// MasterPlugin is only used internal housekeeping // MasterPlugin is only used internal housekeeping
MasterPlugin bool `json:"-"` MasterPlugin bool `json:"-"`
// Conflist plugin is only used internal housekeeping // Conflist plugin is only used internal housekeeping
@ -137,6 +140,8 @@ type NetworkSelectionElement struct {
BandwidthRequest *BandwidthEntry `json:"bandwidth,omitempty"` BandwidthRequest *BandwidthEntry `json:"bandwidth,omitempty"`
// CNIArgs contains additional CNI arguments for the network interface // CNIArgs contains additional CNI arguments for the network interface
CNIArgs *map[string]interface{} `json:"cni-args"` CNIArgs *map[string]interface{} `json:"cni-args"`
// GatewayRequest contains default route IP address for the pod
GatewayRequest []net.IP `json:"default-route,omitempty"`
} }
// K8sArgs is the valid CNI_ARGS used for Kubernetes // K8sArgs is the valid CNI_ARGS used for Kubernetes