2018-04-20 17:30:12 +00:00
// Copyright (c) 2017 Intel Corporation
//
// 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 k8sclient
import (
"encoding/json"
"fmt"
2019-08-16 09:55:12 +00:00
"net"
2018-07-19 14:02:49 +00:00
"os"
2018-05-03 20:36:49 +00:00
"regexp"
2018-05-09 10:47:37 +00:00
"strings"
2018-04-20 17:30:12 +00:00
2018-07-30 10:59:13 +00:00
v1 "k8s.io/api/core/v1"
2018-04-20 17:30:12 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
2018-07-19 14:02:49 +00:00
"k8s.io/client-go/rest"
2018-04-20 17:30:12 +00:00
"k8s.io/client-go/tools/clientcmd"
2018-07-30 10:59:13 +00:00
"k8s.io/client-go/util/retry"
2018-04-20 17:30:12 +00:00
2018-04-27 18:43:20 +00:00
"github.com/containernetworking/cni/libcni"
2018-04-20 17:30:12 +00:00
"github.com/containernetworking/cni/pkg/skel"
cnitypes "github.com/containernetworking/cni/pkg/types"
2019-02-07 15:48:40 +00:00
"github.com/intel/multus-cni/kubeletclient"
2018-08-06 06:55:30 +00:00
"github.com/intel/multus-cni/logging"
2018-05-01 18:08:39 +00:00
"github.com/intel/multus-cni/types"
2018-04-20 17:30:12 +00:00
)
2018-09-10 15:27:07 +00:00
const (
2019-01-25 08:49:16 +00:00
resourceNameAnnot = "k8s.v1.cni.cncf.io/resourceName"
defaultNetAnnot = "v1.multus-cni.io/default-network"
networkAttachmentAnnot = "k8s.v1.cni.cncf.io/networks"
2018-09-10 15:27:07 +00:00
)
2018-04-20 17:30:12 +00:00
// NoK8sNetworkError indicates error, no network in kubernetes
2018-04-23 06:51:11 +00:00
type NoK8sNetworkError struct {
message string
}
2018-04-20 17:30:12 +00:00
2019-06-24 07:57:03 +00:00
// ClientInfo contains information given from k8s client
type ClientInfo struct {
2018-07-30 10:59:13 +00:00
Client KubeClient
Podnamespace string
Podname string
}
2018-04-23 06:51:11 +00:00
func ( e * NoK8sNetworkError ) Error ( ) string { return string ( e . message ) }
2018-04-20 17:30:12 +00:00
2018-06-14 18:15:02 +00:00
type defaultKubeClient struct {
client kubernetes . Interface
}
// defaultKubeClient implements KubeClient
var _ KubeClient = & defaultKubeClient { }
func ( d * defaultKubeClient ) GetRawWithPath ( path string ) ( [ ] byte , error ) {
return d . client . ExtensionsV1beta1 ( ) . RESTClient ( ) . Get ( ) . AbsPath ( path ) . DoRaw ( )
}
2018-04-20 17:30:12 +00:00
2018-06-14 18:15:02 +00:00
func ( d * defaultKubeClient ) GetPod ( namespace , name string ) ( * v1 . Pod , error ) {
return d . client . Core ( ) . Pods ( namespace ) . Get ( name , metav1 . GetOptions { } )
}
2018-07-30 10:59:13 +00:00
func ( d * defaultKubeClient ) UpdatePodStatus ( pod * v1 . Pod ) ( * v1 . Pod , error ) {
return d . client . Core ( ) . Pods ( pod . Namespace ) . UpdateStatus ( pod )
}
2019-06-24 07:57:03 +00:00
func setKubeClientInfo ( c * ClientInfo , client KubeClient , k8sArgs * types . K8sArgs ) {
2018-08-06 06:55:30 +00:00
logging . Debugf ( "setKubeClientInfo: %v, %v, %v" , c , client , k8sArgs )
2018-07-30 10:59:13 +00:00
c . Client = client
c . Podnamespace = string ( k8sArgs . K8S_POD_NAMESPACE )
c . Podname = string ( k8sArgs . K8S_POD_NAME )
}
2019-06-24 07:57:03 +00:00
// SetNetworkStatus sets network status into Pod annotation
2019-03-20 12:08:48 +00:00
func SetNetworkStatus ( client KubeClient , k8sArgs * types . K8sArgs , netStatus [ ] * types . NetworkStatus , conf * types . NetConf ) error {
logging . Debugf ( "SetNetworkStatus: %v, %v, %v, %v" , client , k8sArgs , netStatus , conf )
client , err := GetK8sClient ( conf . Kubeconfig , client )
if err != nil {
return logging . Errorf ( "SetNetworkStatus: %v" , err )
}
if client == nil {
if len ( conf . Delegates ) == 0 {
// No available kube client and no delegates, we can't do anything
2019-10-02 09:02:26 +00:00
return logging . Errorf ( "SetNetworkStatus: must have either Kubernetes config or delegates" )
2019-03-20 12:08:48 +00:00
}
logging . Debugf ( "SetNetworkStatus: kube client info is not defined, skip network status setup" )
return nil
}
2018-07-30 10:59:13 +00:00
2019-01-25 08:49:16 +00:00
podName := string ( k8sArgs . K8S_POD_NAME )
podNamespace := string ( k8sArgs . K8S_POD_NAMESPACE )
pod , err := client . GetPod ( podNamespace , podName )
2018-07-30 10:59:13 +00:00
if err != nil {
2019-01-25 08:49:16 +00:00
return logging . Errorf ( "SetNetworkStatus: failed to query the pod %v in out of cluster comm: %v" , podName , err )
2018-07-30 10:59:13 +00:00
}
2019-01-25 01:54:53 +00:00
var networkStatuses string
2018-07-30 10:59:13 +00:00
if netStatus != nil {
var networkStatus [ ] string
2019-01-25 01:54:53 +00:00
for _ , status := range netStatus {
data , err := json . MarshalIndent ( status , "" , " " )
2018-07-30 10:59:13 +00:00
if err != nil {
2018-08-06 06:55:30 +00:00
return logging . Errorf ( "SetNetworkStatus: error with Marshal Indent: %v" , err )
2018-07-30 10:59:13 +00:00
}
networkStatus = append ( networkStatus , string ( data ) )
}
2019-01-25 01:54:53 +00:00
networkStatuses = fmt . Sprintf ( "[%s]" , strings . Join ( networkStatus , "," ) )
2018-07-30 10:59:13 +00:00
}
2019-01-25 08:49:16 +00:00
_ , err = setPodNetworkAnnotation ( client , podNamespace , pod , networkStatuses )
2018-07-30 10:59:13 +00:00
if err != nil {
2019-01-25 08:49:16 +00:00
return logging . Errorf ( "SetNetworkStatus: failed to update the pod %v in out of cluster comm: %v" , podName , err )
2018-07-30 10:59:13 +00:00
}
return nil
}
func setPodNetworkAnnotation ( client KubeClient , namespace string , pod * v1 . Pod , networkstatus string ) ( * v1 . Pod , error ) {
2018-08-06 06:55:30 +00:00
logging . Debugf ( "setPodNetworkAnnotation: %v, %s, %v, %s" , client , namespace , pod , networkstatus )
2018-07-30 10:59:13 +00:00
//if pod annotations is empty, make sure it allocatable
if len ( pod . Annotations ) == 0 {
pod . Annotations = make ( map [ string ] string )
}
pod . Annotations [ "k8s.v1.cni.cncf.io/networks-status" ] = networkstatus
pod = pod . DeepCopy ( )
var err error
if resultErr := retry . RetryOnConflict ( retry . DefaultBackoff , func ( ) error {
if err != nil {
// Re-get the pod unless it's the first attempt to update
pod , err = client . GetPod ( pod . Namespace , pod . Name )
if err != nil {
return err
}
}
pod , err = client . UpdatePodStatus ( pod )
return err
} ) ; resultErr != nil {
2019-10-02 09:02:26 +00:00
return nil , logging . Errorf ( "setPodNetworkAnnotation: status update failed for pod %s/%s: %v" , pod . Namespace , pod . Name , resultErr )
2018-07-30 10:59:13 +00:00
}
return pod , nil
}
2018-04-20 17:30:12 +00:00
func parsePodNetworkObjectName ( podnetwork string ) ( string , string , string , error ) {
var netNsName string
var netIfName string
var networkName string
2018-08-06 06:55:30 +00:00
logging . Debugf ( "parsePodNetworkObjectName: %s" , podnetwork )
2018-04-20 17:30:12 +00:00
slashItems := strings . Split ( podnetwork , "/" )
if len ( slashItems ) == 2 {
netNsName = strings . TrimSpace ( slashItems [ 0 ] )
networkName = slashItems [ 1 ]
} else if len ( slashItems ) == 1 {
networkName = slashItems [ 0 ]
} else {
2019-10-02 09:02:26 +00:00
return "" , "" , "" , logging . Errorf ( "parsePodNetworkObjectName: Invalid network object (failed at '/')" )
2018-04-20 17:30:12 +00:00
}
atItems := strings . Split ( networkName , "@" )
networkName = strings . TrimSpace ( atItems [ 0 ] )
if len ( atItems ) == 2 {
netIfName = strings . TrimSpace ( atItems [ 1 ] )
} else if len ( atItems ) != 1 {
2019-10-02 09:02:26 +00:00
return "" , "" , "" , logging . Errorf ( "parsePodNetworkObjectName: Invalid network object (failed at '@')" )
2018-04-20 17:30:12 +00:00
}
2018-05-03 20:36:49 +00:00
// Check and see if each item matches the specification for valid attachment name.
// "Valid attachment names must be comprised of units of the DNS-1123 label format"
// [a-z0-9]([-a-z0-9]*[a-z0-9])?
// And we allow at (@), and forward slash (/) (units separated by commas)
// It must start and end alphanumerically.
allItems := [ ] string { netNsName , networkName , netIfName }
for i := range allItems {
matched , _ := regexp . MatchString ( "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$" , allItems [ i ] )
2018-05-09 10:47:37 +00:00
if ! matched && len ( [ ] rune ( allItems [ i ] ) ) > 0 {
2019-10-02 09:02:26 +00:00
return "" , "" , "" , logging . Errorf ( fmt . Sprintf ( "parsePodNetworkObjectName: Failed to parse: one or more items did not match comma-delimited format (must consist of lower case alphanumeric characters). Must start and end with an alphanumeric character), mismatch @ '%v'" , allItems [ i ] ) )
2018-05-03 20:36:49 +00:00
}
}
2018-08-06 06:55:30 +00:00
logging . Debugf ( "parsePodNetworkObjectName: parsed: %s, %s, %s" , netNsName , networkName , netIfName )
2018-04-20 17:30:12 +00:00
return netNsName , networkName , netIfName , nil
}
2018-06-20 02:27:42 +00:00
func parsePodNetworkAnnotation ( podNetworks , defaultNamespace string ) ( [ ] * types . NetworkSelectionElement , error ) {
var networks [ ] * types . NetworkSelectionElement
2018-04-20 17:30:12 +00:00
2018-08-06 06:55:30 +00:00
logging . Debugf ( "parsePodNetworkAnnotation: %s, %s" , podNetworks , defaultNamespace )
2018-06-20 02:27:42 +00:00
if podNetworks == "" {
2019-10-02 09:02:26 +00:00
return nil , logging . Errorf ( "parsePodNetworkAnnotation: pod annotation does not have \"network\" as key" )
2018-04-20 17:30:12 +00:00
}
2018-06-20 02:27:42 +00:00
if strings . IndexAny ( podNetworks , "[{\"" ) >= 0 {
if err := json . Unmarshal ( [ ] byte ( podNetworks ) , & networks ) ; err != nil {
2018-08-06 06:55:30 +00:00
return nil , logging . Errorf ( "parsePodNetworkAnnotation: failed to parse pod Network Attachment Selection Annotation JSON format: %v" , err )
2018-06-20 02:27:42 +00:00
}
} else {
// Comma-delimited list of network attachment object names
for _ , item := range strings . Split ( podNetworks , "," ) {
2018-05-03 20:36:49 +00:00
// Remove leading and trailing whitespace.
2018-06-20 02:27:42 +00:00
item = strings . TrimSpace ( item )
2018-05-03 20:36:49 +00:00
2018-04-20 17:30:12 +00:00
// Parse network name (i.e. <namespace>/<network name>@<ifname>)
2018-06-20 02:27:42 +00:00
netNsName , networkName , netIfName , err := parsePodNetworkObjectName ( item )
2018-04-20 17:30:12 +00:00
if err != nil {
2018-08-06 06:55:30 +00:00
return nil , logging . Errorf ( "parsePodNetworkAnnotation: %v" , err )
2018-04-20 17:30:12 +00:00
}
2018-06-20 02:27:42 +00:00
networks = append ( networks , & types . NetworkSelectionElement {
Name : networkName ,
Namespace : netNsName ,
InterfaceRequest : netIfName ,
} )
2018-04-20 17:30:12 +00:00
}
}
2019-08-16 09:55:12 +00:00
for _ , n := range networks {
if n . Namespace == "" {
n . Namespace = defaultNamespace
2018-06-20 02:27:42 +00:00
}
2019-08-22 15:19:06 +00:00
// compatibility pre v3.2, will be removed in v4.0
2019-08-16 09:55:12 +00:00
if n . DeprecatedInterfaceRequest != "" && n . InterfaceRequest == "" {
n . InterfaceRequest = n . DeprecatedInterfaceRequest
}
if n . MacRequest != "" {
// validate MAC address
if _ , err := net . ParseMAC ( n . MacRequest ) ; err != nil {
return nil , logging . Errorf ( "parsePodNetworkAnnotation: failed to mac: %v" , err )
}
}
if n . IPRequest != nil {
for _ , ip := range n . IPRequest {
// validate IP address
if strings . Contains ( ip , "/" ) {
if _ , _ , err := net . ParseCIDR ( ip ) ; err != nil {
return nil , logging . Errorf ( "failed to parse CIDR %q: %v" , ip , err )
}
} else if net . ParseIP ( ip ) == nil {
return nil , logging . Errorf ( "failed to parse IP address %q" , ip )
}
}
}
// compatibility pre v3.2, will be removed in v4.0
if n . DeprecatedInterfaceRequest != "" && n . InterfaceRequest == "" {
n . InterfaceRequest = n . DeprecatedInterfaceRequest
2019-08-22 15:19:06 +00:00
}
2018-06-20 02:27:42 +00:00
}
return networks , nil
2018-04-20 17:30:12 +00:00
}
2018-07-19 16:21:31 +00:00
func getCNIConfigFromFile ( name string , confdir string ) ( [ ] byte , error ) {
2018-08-06 06:55:30 +00:00
logging . Debugf ( "getCNIConfigFromFile: %s, %s" , name , confdir )
2018-04-27 18:43:20 +00:00
2018-04-23 17:30:41 +00:00
// In the absence of valid keys in a Spec, the runtime (or
// meta-plugin) should load and execute a CNI .configlist
// or .config (in that order) file on-disk whose JSON
// “name” key matches this Network object’ s name.
2018-04-20 17:30:12 +00:00
2018-04-27 18:43:20 +00:00
// In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go#getDefaultCNINetwork
2018-07-26 23:04:52 +00:00
files , err := libcni . ConfFiles ( confdir , [ ] string { ".conf" , ".json" , ".conflist" } )
2018-04-27 18:43:20 +00:00
switch {
case err != nil :
2019-10-02 09:02:26 +00:00
return nil , logging . Errorf ( "getCNIConfigFromFile: no networks found in %s" , confdir )
2018-04-27 18:43:20 +00:00
case len ( files ) == 0 :
2019-10-02 09:02:26 +00:00
return nil , logging . Errorf ( "getCNIConfigFromFile: no networks found in %s" , confdir )
2018-04-27 18:43:20 +00:00
}
for _ , confFile := range files {
2018-07-26 23:04:52 +00:00
var confList * libcni . NetworkConfigList
if strings . HasSuffix ( confFile , ".conflist" ) {
confList , err = libcni . ConfListFromFile ( confFile )
if err != nil {
2019-10-02 09:02:26 +00:00
return nil , logging . Errorf ( "getCNIConfigFromFile: error loading CNI conflist file %s: %v" , confFile , err )
2018-07-26 23:04:52 +00:00
}
2018-10-15 07:32:15 +00:00
if confList . Name == name || name == "" {
2018-07-26 23:04:52 +00:00
return confList . Bytes , nil
}
2018-04-27 18:43:20 +00:00
2018-07-26 23:04:52 +00:00
} else {
conf , err := libcni . ConfFromFile ( confFile )
if err != nil {
2019-10-02 09:02:26 +00:00
return nil , logging . Errorf ( "getCNIConfigFromFile: error loading CNI config file %s: %v" , confFile , err )
2018-07-26 23:04:52 +00:00
}
2018-10-15 07:32:15 +00:00
if conf . Network . Name == name || name == "" {
2018-07-26 23:04:52 +00:00
// 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 == "" {
2019-10-02 09:02:26 +00:00
return nil , logging . Errorf ( "getCNIConfigFromFile: error loading CNI config file %s: does not have a 'type' key; perhaps this is a .conflist?" , confFile )
2018-07-26 23:04:52 +00:00
}
return conf . Bytes , nil
2018-04-27 18:43:20 +00:00
}
}
}
2019-10-02 09:02:26 +00:00
return nil , logging . Errorf ( "getCNIConfigFromFile: no network available with the name %s in cni dir %s" , name , confdir )
2018-04-23 17:30:41 +00:00
}
2018-07-19 16:21:31 +00:00
// getCNIConfigFromSpec reads a CNI JSON configuration from the NetworkAttachmentDefinition
// object's Spec.Config field and fills in any missing details like the network name
func getCNIConfigFromSpec ( configData , netName string ) ( [ ] byte , error ) {
var rawConfig map [ string ] interface { }
var err error
2018-04-20 17:30:12 +00:00
2018-08-06 06:55:30 +00:00
logging . Debugf ( "getCNIConfigFromSpec: %s, %s" , configData , netName )
2018-07-19 16:21:31 +00:00
configBytes := [ ] byte ( configData )
err = json . Unmarshal ( configBytes , & rawConfig )
if err != nil {
2018-08-06 06:55:30 +00:00
return nil , logging . Errorf ( "getCNIConfigFromSpec: failed to unmarshal Spec.Config: %v" , err )
2018-04-23 17:30:41 +00:00
}
2018-07-19 16:21:31 +00:00
// Inject network name if missing from Config for the thick plugin case
if n , ok := rawConfig [ "name" ] ; ! ok || n == "" {
rawConfig [ "name" ] = netName
configBytes , err = json . Marshal ( rawConfig )
if err != nil {
2018-08-06 06:55:30 +00:00
return nil , logging . Errorf ( "getCNIConfigFromSpec: failed to re-marshal Spec.Config: %v" , err )
2018-07-19 16:21:31 +00:00
}
2018-04-23 17:30:41 +00:00
}
2018-07-19 16:21:31 +00:00
return configBytes , nil
2018-04-23 17:30:41 +00:00
}
2018-07-19 16:21:31 +00:00
func cniConfigFromNetworkResource ( customResource * types . NetworkAttachmentDefinition , confdir string ) ( [ ] byte , error ) {
var config [ ] byte
2018-04-23 17:30:41 +00:00
var err error
2018-08-06 06:55:30 +00:00
logging . Debugf ( "cniConfigFromNetworkResource: %v, %s" , customResource , confdir )
2018-07-19 16:21:31 +00:00
emptySpec := types . NetworkAttachmentDefinitionSpec { }
2018-07-26 23:04:52 +00:00
if customResource . Spec == emptySpec {
2018-06-20 02:27:42 +00:00
// Network Spec empty; generate delegate from CNI JSON config
// from the configuration directory that has the same network
// name as the custom resource
2018-07-19 16:21:31 +00:00
config , err = getCNIConfigFromFile ( customResource . Metadata . Name , confdir )
2018-04-23 17:30:41 +00:00
if err != nil {
2019-10-02 09:02:26 +00:00
return nil , logging . Errorf ( "cniConfigFromNetworkResource: %v" , err )
2018-04-23 17:30:41 +00:00
}
} else {
2018-07-19 16:21:31 +00:00
// Config contains a standard JSON-encoded CNI configuration
// or configuration list which defines the plugin chain to
// execute.
config , err = getCNIConfigFromSpec ( customResource . Spec . Config , customResource . Metadata . Name )
2018-04-23 17:30:41 +00:00
if err != nil {
2019-10-02 09:02:26 +00:00
return nil , logging . Errorf ( "cniConfigFromNetworkResource: %v" , err )
2018-04-23 17:30:41 +00:00
}
}
2018-04-20 17:30:12 +00:00
2018-04-23 17:30:41 +00:00
return config , nil
2018-04-20 17:30:12 +00:00
}
2019-02-07 15:48:40 +00:00
func getKubernetesDelegate ( client KubeClient , net * types . NetworkSelectionElement , confdir string , pod * v1 . Pod , resourceMap map [ string ] * types . ResourceInfo ) ( * types . DelegateNetConf , map [ string ] * types . ResourceInfo , error ) {
2018-09-10 15:27:07 +00:00
2018-08-30 11:15:30 +00:00
logging . Debugf ( "getKubernetesDelegate: %v, %v, %s" , client , net , confdir )
2018-07-19 21:57:08 +00:00
rawPath := fmt . Sprintf ( "/apis/k8s.cni.cncf.io/v1/namespaces/%s/network-attachment-definitions/%s" , net . Namespace , net . Name )
2018-06-20 02:27:42 +00:00
netData , err := client . GetRawWithPath ( rawPath )
2018-04-20 17:30:12 +00:00
if err != nil {
2019-10-02 09:02:26 +00:00
return nil , resourceMap , logging . Errorf ( "getKubernetesDelegate: cannot find get a network-attachment-definition (%s) in namespace (%s): %v" , net . Name , net . Namespace , err )
2018-04-20 17:30:12 +00:00
}
2018-07-12 13:42:56 +00:00
customResource := & types . NetworkAttachmentDefinition { }
2018-06-20 02:27:42 +00:00
if err := json . Unmarshal ( netData , customResource ) ; err != nil {
2019-10-02 09:02:26 +00:00
return nil , resourceMap , logging . Errorf ( "getKubernetesDelegate: failed to parse the network-attachment-definition: %v" , err )
2018-04-20 17:30:12 +00:00
}
2018-10-01 09:38:56 +00:00
// Get resourceName annotation from NetworkAttachmentDefinition
2018-09-10 15:27:07 +00:00
deviceID := ""
resourceName , ok := customResource . Metadata . Annotations [ resourceNameAnnot ]
2019-02-07 15:48:40 +00:00
if ok && pod . Name != "" && pod . Namespace != "" {
2018-09-10 15:27:07 +00:00
// ResourceName annotation is found; try to get device info from resourceMap
2018-09-19 15:41:08 +00:00
logging . Debugf ( "getKubernetesDelegate: found resourceName annotation : %s" , resourceName )
if resourceMap == nil {
2019-02-07 15:48:40 +00:00
ck , err := kubeletclient . GetResourceClient ( )
2018-09-19 15:41:08 +00:00
if err != nil {
2019-02-07 15:48:40 +00:00
return nil , resourceMap , logging . Errorf ( "getKubernetesDelegate: failed to get a ResourceClient instance: %v" , err )
2018-09-19 15:41:08 +00:00
}
2019-02-07 15:48:40 +00:00
resourceMap , err = ck . GetPodResourceMap ( pod )
2018-09-19 15:41:08 +00:00
if err != nil {
2019-02-07 15:48:40 +00:00
return nil , resourceMap , logging . Errorf ( "getKubernetesDelegate: failed to get resourceMap from ResourceClient: %v" , err )
2018-09-19 15:41:08 +00:00
}
2019-10-02 09:02:26 +00:00
logging . Debugf ( "getKubernetesDelegate: resourceMap instance: %+v" , resourceMap )
2018-09-18 12:14:58 +00:00
}
2018-09-10 15:27:07 +00:00
entry , ok := resourceMap [ resourceName ]
if ok {
if idCount := len ( entry . DeviceIDs ) ; idCount > 0 && idCount > entry . Index {
deviceID = entry . DeviceIDs [ entry . Index ]
2019-02-07 15:48:40 +00:00
logging . Debugf ( "getKubernetesDelegate: podName: %s deviceID: %s" , pod . Name , deviceID )
2018-09-10 15:27:07 +00:00
entry . Index ++ // increment Index for next delegate
}
}
}
2018-07-19 16:21:31 +00:00
configBytes , err := cniConfigFromNetworkResource ( customResource , confdir )
2018-04-20 17:30:12 +00:00
if err != nil {
2018-09-18 12:14:58 +00:00
return nil , resourceMap , err
2018-04-20 17:30:12 +00:00
}
2018-08-28 10:21:50 +00:00
delegate , err := types . LoadDelegateNetConf ( configBytes , net , deviceID )
2018-05-01 18:08:39 +00:00
if err != nil {
2018-09-18 12:14:58 +00:00
return nil , resourceMap , err
2018-04-20 17:30:12 +00:00
}
2018-09-18 12:14:58 +00:00
return delegate , resourceMap , nil
2018-08-03 12:32:28 +00:00
}
2019-06-24 07:57:03 +00:00
// KubeClient is abstraction layer for k8s client (used testing package)
2018-06-14 18:15:02 +00:00
type KubeClient interface {
GetRawWithPath ( path string ) ( [ ] byte , error )
GetPod ( namespace , name string ) ( * v1 . Pod , error )
2018-07-30 10:59:13 +00:00
UpdatePodStatus ( pod * v1 . Pod ) ( * v1 . Pod , error )
2018-06-14 18:15:02 +00:00
}
2019-06-24 07:57:03 +00:00
// GetK8sArgs gets k8s related args from CNI args
2018-07-26 23:04:52 +00:00
func GetK8sArgs ( args * skel . CmdArgs ) ( * types . K8sArgs , error ) {
k8sArgs := & types . K8sArgs { }
2018-10-11 08:57:20 +00:00
logging . Debugf ( "GetK8sArgs: %v" , args )
2018-07-26 23:04:52 +00:00
err := cnitypes . LoadArgs ( args . Args , k8sArgs )
if err != nil {
return nil , err
}
return k8sArgs , nil
}
2019-06-24 07:57:03 +00:00
// TryLoadPodDelegates attempts to load Kubernetes-defined delegates and add them to the Multus config.
2018-07-30 10:59:13 +00:00
// Returns the number of Kubernetes-defined delegates added or an error.
2019-06-24 07:57:03 +00:00
func TryLoadPodDelegates ( k8sArgs * types . K8sArgs , conf * types . NetConf , kubeClient KubeClient ) ( int , * ClientInfo , error ) {
2018-07-30 10:59:13 +00:00
var err error
2019-06-24 07:57:03 +00:00
clientInfo := & ClientInfo { }
2018-07-30 10:59:13 +00:00
2018-10-11 08:57:20 +00:00
logging . Debugf ( "TryLoadPodDelegates: %v, %v, %v" , k8sArgs , conf , kubeClient )
2018-07-30 10:59:13 +00:00
kubeClient , err = GetK8sClient ( conf . Kubeconfig , kubeClient )
if err != nil {
return 0 , nil , err
}
if kubeClient == nil {
if len ( conf . Delegates ) == 0 {
// No available kube client and no delegates, we can't do anything
2019-10-02 09:02:26 +00:00
return 0 , nil , logging . Errorf ( "TryLoadPodDelegates: must have either Kubernetes config or delegates" )
2018-07-30 10:59:13 +00:00
}
return 0 , nil , nil
}
setKubeClientInfo ( clientInfo , kubeClient , k8sArgs )
2019-01-25 08:49:16 +00:00
// Get the pod info. If cannot get it, we use cached delegates
pod , err := kubeClient . GetPod ( string ( k8sArgs . K8S_POD_NAMESPACE ) , string ( k8sArgs . K8S_POD_NAME ) )
if err != nil {
2019-10-02 09:02:26 +00:00
logging . Debugf ( "TryLoadPodDelegates: Err in loading K8s cluster default network from pod annotation: %v, use cached delegates" , err )
2019-01-25 08:49:16 +00:00
return 0 , nil , nil
}
2018-09-28 07:02:05 +00:00
2019-01-25 08:49:16 +00:00
delegate , err := tryLoadK8sPodDefaultNetwork ( kubeClient , pod , conf )
2018-09-28 07:02:05 +00:00
if err != nil {
2019-10-02 09:02:26 +00:00
return 0 , nil , logging . Errorf ( "TryLoadPodDelegates: error in loading K8s cluster default network from pod annotation: %v" , err )
2018-11-27 00:38:36 +00:00
}
2018-12-18 22:03:05 +00:00
if delegate != nil {
2019-10-02 09:02:26 +00:00
logging . Debugf ( "TryLoadPodDelegates: Overwrite the cluster default network with %v from pod annotations" , delegate )
2018-11-27 00:38:36 +00:00
2018-09-28 07:02:05 +00:00
conf . Delegates [ 0 ] = delegate
}
2019-01-25 08:49:16 +00:00
networks , err := GetPodNetwork ( pod )
if networks != nil {
delegates , err := GetNetworkDelegates ( kubeClient , pod , networks , conf . ConfDir , conf . NamespaceIsolation )
if err != nil {
if _ , ok := err . ( * NoK8sNetworkError ) ; ok {
return 0 , clientInfo , nil
}
2019-10-02 09:02:26 +00:00
return 0 , nil , logging . Errorf ( "TryLoadPodDelegates: error in getting k8s network from pod: %v" , err )
2018-07-30 10:59:13 +00:00
}
2019-01-25 08:49:16 +00:00
if err = conf . AddDelegates ( delegates ) ; err != nil {
return 0 , nil , err
}
return len ( delegates ) , clientInfo , nil
2018-07-30 10:59:13 +00:00
}
2019-01-25 08:49:16 +00:00
return 0 , clientInfo , nil
2018-07-30 10:59:13 +00:00
}
2019-06-24 07:57:03 +00:00
// GetK8sClient gets client info from kubeconfig
2018-07-19 14:02:49 +00:00
func GetK8sClient ( kubeconfig string , kubeClient KubeClient ) ( KubeClient , error ) {
2018-08-06 06:55:30 +00:00
logging . Debugf ( "GetK8sClient: %s, %v" , kubeconfig , kubeClient )
2018-07-19 14:02:49 +00:00
// If we get a valid kubeClient (eg from testcases) just return that
// one.
if kubeClient != nil {
return kubeClient , nil
}
2018-04-20 17:30:12 +00:00
2018-07-19 14:02:49 +00:00
var err error
var config * rest . Config
// Otherwise try to create a kubeClient from a given kubeConfig
if kubeconfig != "" {
// uses the current context in kubeconfig
config , err = clientcmd . BuildConfigFromFlags ( "" , kubeconfig )
if err != nil {
2019-10-02 09:02:26 +00:00
return nil , logging . Errorf ( "GetK8sClient: failed to get context for the kubeconfig %v: %v" , kubeconfig , err )
2018-07-19 14:02:49 +00:00
}
} else if os . Getenv ( "KUBERNETES_SERVICE_HOST" ) != "" && os . Getenv ( "KUBERNETES_SERVICE_PORT" ) != "" {
// Try in-cluster config where multus might be running in a kubernetes pod
config , err = rest . InClusterConfig ( )
if err != nil {
2019-10-02 09:02:26 +00:00
return nil , logging . Errorf ( "GetK8sClient: failed to get context for in-cluster kube config: %v" , err )
2018-07-19 14:02:49 +00:00
}
} else {
// No kubernetes config; assume we shouldn't talk to Kube at all
return nil , nil
}
2019-02-12 15:24:00 +00:00
// Specify that we use gRPC
config . AcceptContentTypes = "application/vnd.kubernetes.protobuf,application/json"
config . ContentType = "application/vnd.kubernetes.protobuf"
2018-07-19 14:02:49 +00:00
// creates the clientset
client , err := kubernetes . NewForConfig ( config )
2018-04-20 17:30:12 +00:00
if err != nil {
2018-05-01 18:08:39 +00:00
return nil , err
2018-04-20 17:30:12 +00:00
}
2018-07-19 14:02:49 +00:00
return & defaultKubeClient { client : client } , nil
}
2019-06-24 07:57:03 +00:00
// GetPodNetwork gets net-attach-def annotation from pod
2019-01-25 08:49:16 +00:00
func GetPodNetwork ( pod * v1 . Pod ) ( [ ] * types . NetworkSelectionElement , error ) {
logging . Debugf ( "GetPodNetwork: %v" , pod )
2018-04-20 17:30:12 +00:00
2019-01-25 08:49:16 +00:00
netAnnot := pod . Annotations [ networkAttachmentAnnot ]
defaultNamespace := pod . ObjectMeta . Namespace
2018-09-10 15:27:07 +00:00
2018-04-20 17:30:12 +00:00
if len ( netAnnot ) == 0 {
2018-05-01 18:08:39 +00:00
return nil , & NoK8sNetworkError { "no kubernetes network found" }
2018-04-20 17:30:12 +00:00
}
2018-06-20 02:27:42 +00:00
networks , err := parsePodNetworkAnnotation ( netAnnot , defaultNamespace )
2018-04-20 17:30:12 +00:00
if err != nil {
2018-05-01 18:08:39 +00:00
return nil , err
2018-04-20 17:30:12 +00:00
}
2019-01-25 08:49:16 +00:00
return networks , nil
}
2018-04-20 17:30:12 +00:00
2019-06-24 07:57:03 +00:00
// GetNetworkDelegates returns delegatenetconf from net-attach-def annotation in pod
2019-01-25 08:49:16 +00:00
func GetNetworkDelegates ( k8sclient KubeClient , pod * v1 . Pod , networks [ ] * types . NetworkSelectionElement , confdir string , confnamespaceIsolation bool ) ( [ ] * types . DelegateNetConf , error ) {
logging . Debugf ( "GetNetworkDelegates: %v, %v, %v, %v, %v" , k8sclient , pod , networks , confdir , confnamespaceIsolation )
2018-09-18 12:14:58 +00:00
// resourceMap holds Pod device allocation information; only initizized if CRD contains 'resourceName' annotation.
2018-09-19 15:41:08 +00:00
// This will only be initialized once and all delegate objects can reference this to look up device info.
2018-09-18 12:14:58 +00:00
var resourceMap map [ string ] * types . ResourceInfo
2018-06-20 02:27:42 +00:00
// Read all network objects referenced by 'networks'
var delegates [ ] * types . DelegateNetConf
2019-01-25 08:49:16 +00:00
defaultNamespace := pod . ObjectMeta . Namespace
2018-06-20 02:27:42 +00:00
for _ , net := range networks {
2018-12-18 22:03:05 +00:00
// The pods namespace (stored as defaultNamespace, does not equal the annotation's target namespace in net.Namespace)
// In the case that this is a mismatch when namespaceisolation is enabled, this should be an error.
2019-01-25 08:49:16 +00:00
if confnamespaceIsolation {
2018-12-18 22:03:05 +00:00
if defaultNamespace != net . Namespace {
2019-10-02 09:02:26 +00:00
return nil , logging . Errorf ( "GetNetworkDelegates: namespace isolation enabled, annotation violates permission, pod is in namespace %v but refers to target namespace %v" , defaultNamespace , net . Namespace )
2018-12-18 22:03:05 +00:00
}
}
2019-02-07 15:48:40 +00:00
delegate , updatedResourceMap , err := getKubernetesDelegate ( k8sclient , net , confdir , pod , resourceMap )
2018-06-20 02:27:42 +00:00
if err != nil {
2019-10-02 09:02:26 +00:00
return nil , logging . Errorf ( "GetNetworkDelegates: failed getting the delegate: %v" , err )
2018-06-20 02:27:42 +00:00
}
delegates = append ( delegates , delegate )
2018-09-19 15:41:08 +00:00
resourceMap = updatedResourceMap
2018-04-20 17:30:12 +00:00
}
2018-06-20 02:27:42 +00:00
return delegates , nil
2018-04-20 17:30:12 +00:00
}
2018-10-11 08:57:20 +00:00
2019-01-29 10:02:47 +00:00
func getDefaultNetDelegateCRD ( client KubeClient , net , confdir , namespace string ) ( * types . DelegateNetConf , error ) {
2019-02-22 02:50:37 +00:00
logging . Debugf ( "getDefaultNetDelegateCRD: %v, %v, %s, %s" , client , net , confdir , namespace )
2019-01-29 10:02:47 +00:00
rawPath := fmt . Sprintf ( "/apis/k8s.cni.cncf.io/v1/namespaces/%s/network-attachment-definitions/%s" , namespace , net )
2018-10-11 08:57:20 +00:00
netData , err := client . GetRawWithPath ( rawPath )
if err != nil {
2019-10-02 09:02:26 +00:00
return nil , logging . Errorf ( "getDefaultNetDelegateCRD: failed to get network resource: %v" , err )
2018-10-11 08:57:20 +00:00
}
customResource := & types . NetworkAttachmentDefinition { }
if err := json . Unmarshal ( netData , customResource ) ; err != nil {
2019-01-29 10:02:47 +00:00
return nil , logging . Errorf ( "getDefaultNetDelegateCRD: failed to get the netplugin data: %v" , err )
2018-10-11 08:57:20 +00:00
}
configBytes , err := cniConfigFromNetworkResource ( customResource , confdir )
if err != nil {
return nil , err
}
2018-11-07 08:09:54 +00:00
delegate , err := types . LoadDelegateNetConf ( configBytes , nil , "" )
2018-10-11 08:57:20 +00:00
if err != nil {
return nil , err
}
return delegate , nil
}
2019-01-29 10:02:47 +00:00
func getNetDelegate ( client KubeClient , netname , confdir , namespace string ) ( * types . DelegateNetConf , error ) {
2019-02-22 02:50:37 +00:00
logging . Debugf ( "getNetDelegate: %v, %v, %v, %s" , client , netname , confdir , namespace )
2018-10-11 08:57:20 +00:00
// option1) search CRD object for the network
2019-01-29 10:02:47 +00:00
delegate , err := getDefaultNetDelegateCRD ( client , netname , confdir , namespace )
2018-10-11 08:57:20 +00:00
if err == nil {
return delegate , nil
}
// option2) search CNI json config file
var configBytes [ ] byte
configBytes , err = getCNIConfigFromFile ( netname , confdir )
if err == nil {
2018-11-07 08:09:54 +00:00
delegate , err := types . LoadDelegateNetConf ( configBytes , nil , "" )
2018-10-11 08:57:20 +00:00
if err != nil {
return nil , err
}
return delegate , nil
}
// option3) search directry
fInfo , err := os . Stat ( netname )
if err == nil {
if fInfo . IsDir ( ) {
files , err := libcni . ConfFiles ( netname , [ ] string { ".conf" , ".conflist" } )
2018-11-27 16:34:20 +00:00
if err != nil {
return nil , err
}
2018-10-15 07:32:15 +00:00
if len ( files ) > 0 {
2018-10-11 08:57:20 +00:00
var configBytes [ ] byte
2018-10-15 07:32:15 +00:00
configBytes , err = getCNIConfigFromFile ( "" , netname )
2018-10-11 08:57:20 +00:00
if err == nil {
2018-11-07 08:09:54 +00:00
delegate , err := types . LoadDelegateNetConf ( configBytes , nil , "" )
2018-10-11 08:57:20 +00:00
if err != nil {
return nil , err
}
return delegate , nil
}
2018-10-15 07:32:15 +00:00
return nil , err
2018-10-11 08:57:20 +00:00
}
}
}
return nil , logging . Errorf ( "getNetDelegate: cannot find network: %v" , netname )
}
2019-06-24 07:57:03 +00:00
// GetDefaultNetworks parses 'defaultNetwork' config, gets network json and put it into netconf.Delegates.
2018-10-11 08:57:20 +00:00
func GetDefaultNetworks ( k8sArgs * types . K8sArgs , conf * types . NetConf , kubeClient KubeClient ) error {
logging . Debugf ( "GetDefaultNetworks: %v, %v, %v" , k8sArgs , conf , kubeClient )
var delegates [ ] * types . DelegateNetConf
kubeClient , err := GetK8sClient ( conf . Kubeconfig , kubeClient )
if err != nil {
return err
}
if kubeClient == nil {
if len ( conf . Delegates ) == 0 {
// No available kube client and no delegates, we can't do anything
2019-10-02 09:02:26 +00:00
return logging . Errorf ( "GetDefaultNetworks: must have either Kubernetes config or delegates" )
2018-10-11 08:57:20 +00:00
}
return nil
}
2019-01-29 10:02:47 +00:00
delegate , err := getNetDelegate ( kubeClient , conf . ClusterNetwork , conf . ConfDir , conf . MultusNamespace )
2018-10-11 08:57:20 +00:00
if err != nil {
return err
}
delegate . MasterPlugin = true
delegates = append ( delegates , delegate )
2018-11-30 07:33:09 +00:00
// Pod in kube-system namespace does not have default network for now.
2019-01-29 10:02:47 +00:00
if ! types . CheckSystemNamespaces ( string ( k8sArgs . K8S_POD_NAMESPACE ) , conf . SystemNamespaces ) {
2018-11-30 07:33:09 +00:00
for _ , netname := range conf . DefaultNetworks {
2019-01-29 10:02:47 +00:00
delegate , err := getNetDelegate ( kubeClient , netname , conf . ConfDir , conf . MultusNamespace )
2018-11-30 07:33:09 +00:00
if err != nil {
return err
}
delegates = append ( delegates , delegate )
2018-10-11 08:57:20 +00:00
}
}
if err = conf . AddDelegates ( delegates ) ; err != nil {
return err
}
return nil
}
2018-09-28 07:02:05 +00:00
// tryLoadK8sPodDefaultNetwork get pod default network from annotations
2019-01-25 08:49:16 +00:00
func tryLoadK8sPodDefaultNetwork ( kubeClient KubeClient , pod * v1 . Pod , conf * types . NetConf ) ( * types . DelegateNetConf , error ) {
var netAnnot string
logging . Debugf ( "tryLoadK8sPodDefaultNetwork: %v, %v, %v" , kubeClient , pod , conf )
2018-09-28 07:02:05 +00:00
2019-01-25 08:49:16 +00:00
netAnnot , ok := pod . Annotations [ defaultNetAnnot ]
if ! ok {
2018-11-27 00:38:36 +00:00
logging . Debugf ( "tryLoadK8sPodDefaultNetwork: Pod default network annotation is not defined" )
2018-09-28 07:02:05 +00:00
return nil , nil
}
2019-02-22 02:50:37 +00:00
// The CRD object of default network should only be defined in multusNamespace
networks , err := parsePodNetworkAnnotation ( netAnnot , conf . MultusNamespace )
2018-09-28 07:02:05 +00:00
if err != nil {
2018-11-27 00:38:36 +00:00
return nil , logging . Errorf ( "tryLoadK8sPodDefaultNetwork: failed to parse CRD object: %v" , err )
2018-09-28 07:02:05 +00:00
}
if len ( networks ) > 1 {
return nil , logging . Errorf ( "tryLoadK8sPodDefaultNetwork: more than one default network is specified: %s" , netAnnot )
}
2019-02-07 15:48:40 +00:00
delegate , _ , err := getKubernetesDelegate ( kubeClient , networks [ 0 ] , conf . ConfDir , pod , nil )
2018-09-28 07:02:05 +00:00
if err != nil {
return nil , logging . Errorf ( "tryLoadK8sPodDefaultNetwork: failed getting the delegate: %v" , err )
}
delegate . MasterPlugin = true
return delegate , nil
}