2017-07-21 12:37:50 +00:00
// Copyright (c) 2017 Intel Corporation
2016-12-13 14:48:12 +00:00
//
// 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.
2017-07-21 12:37:50 +00:00
// This is a "Multi-plugin".The delegate concept refered from CNI project
2016-12-13 14:48:12 +00:00
// It reads other plugin netconf, and then invoke them, e.g.
// flannel or sriov plugin.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
2018-04-20 17:30:12 +00:00
k8s "github.com/Intel-Corp/multus-cni/k8sclient"
"github.com/Intel-Corp/multus-cni/types"
2016-12-13 14:48:12 +00:00
"github.com/containernetworking/cni/pkg/invoke"
"github.com/containernetworking/cni/pkg/skel"
2017-06-29 16:19:09 +00:00
"github.com/containernetworking/cni/pkg/version"
2016-12-13 14:48:12 +00:00
)
2017-02-21 19:01:58 +00:00
const defaultCNIDir = "/var/lib/cni/multus"
2016-12-13 14:48:12 +00:00
var masterpluginEnabled bool
2017-08-11 16:24:43 +00:00
var defaultcninetwork bool
2016-12-13 14:48:12 +00:00
//taken from cni/plugins/meta/flannel/flannel.go
func isString ( i interface { } ) bool {
_ , ok := i . ( string )
return ok
}
func isBool ( i interface { } ) bool {
_ , ok := i . ( bool )
return ok
}
2018-04-17 16:03:02 +00:00
func loadNetConf ( bytes [ ] byte ) ( * types . NetConf , error ) {
netconf := & types . NetConf { }
2016-12-13 14:48:12 +00:00
if err := json . Unmarshal ( bytes , netconf ) ; err != nil {
return nil , fmt . Errorf ( "failed to load netconf: %v" , err )
}
2017-08-11 16:24:43 +00:00
if netconf . Kubeconfig != "" && netconf . Delegates != nil {
defaultcninetwork = true
}
2018-03-13 15:51:48 +00:00
if netconf . UseDefault {
if netconf . Kubeconfig == "" || ! defaultcninetwork {
return nil , fmt . Errorf ( ` If you have set always_use_default, you must also set the delegates & the kubeconfig, refer to the README ` )
}
2017-06-29 16:19:09 +00:00
return netconf , nil
}
2018-03-13 15:51:48 +00:00
if ! netconf . UseDefault && ( netconf . Kubeconfig != "" && ! defaultcninetwork ) {
return netconf , nil
}
2018-04-17 16:03:02 +00:00
2017-08-11 16:24:43 +00:00
if len ( netconf . Delegates ) == 0 && ! defaultcninetwork {
return nil , fmt . Errorf ( ` delegates or kubeconfig option is must, refer README.md ` )
2016-12-13 14:48:12 +00:00
}
if netconf . CNIDir == "" {
netconf . CNIDir = defaultCNIDir
}
return netconf , nil
}
func saveScratchNetConf ( containerID , dataDir string , netconf [ ] byte ) error {
if err := os . MkdirAll ( dataDir , 0700 ) ; err != nil {
return fmt . Errorf ( "failed to create the multus data directory(%q): %v" , dataDir , err )
}
path := filepath . Join ( dataDir , containerID )
err := ioutil . WriteFile ( path , netconf , 0600 )
if err != nil {
return fmt . Errorf ( "failed to write container data in the path(%q): %v" , path , err )
}
return err
}
func consumeScratchNetConf ( containerID , dataDir string ) ( [ ] byte , error ) {
path := filepath . Join ( dataDir , containerID )
defer os . Remove ( path )
2017-11-09 17:55:37 +00:00
return ioutil . ReadFile ( path )
2016-12-13 14:48:12 +00:00
}
func getifname ( ) ( f func ( ) string ) {
var interfaceIndex int
f = func ( ) string {
2017-02-21 19:01:58 +00:00
ifname := fmt . Sprintf ( "net%d" , interfaceIndex )
2016-12-13 14:48:12 +00:00
interfaceIndex ++
return ifname
}
return
}
func saveDelegates ( containerID , dataDir string , delegates [ ] map [ string ] interface { } ) error {
delegatesBytes , err := json . Marshal ( delegates )
if err != nil {
return fmt . Errorf ( "error serializing delegate netconf: %v" , err )
}
if err = saveScratchNetConf ( containerID , dataDir , delegatesBytes ) ; err != nil {
return fmt . Errorf ( "error in saving the delegates : %v" , err )
}
return err
}
func checkDelegate ( netconf map [ string ] interface { } ) error {
if netconf [ "type" ] == nil {
return fmt . Errorf ( "delegate must have the field 'type'" )
}
if ! isString ( netconf [ "type" ] ) {
return fmt . Errorf ( "delegate field 'type' must be a string" )
}
if netconf [ "masterplugin" ] != nil {
if ! isBool ( netconf [ "masterplugin" ] ) {
return fmt . Errorf ( "delegate field 'masterplugin' must be a bool" )
}
}
if netconf [ "masterplugin" ] != nil {
if netconf [ "masterplugin" ] . ( bool ) != false && masterpluginEnabled != true {
masterpluginEnabled = true
} else if netconf [ "masterplugin" ] . ( bool ) != false && masterpluginEnabled == true {
return fmt . Errorf ( "only one delegate can have 'masterplugin'" )
}
}
return nil
}
func isMasterplugin ( netconf map [ string ] interface { } ) bool {
if netconf [ "masterplugin" ] == nil {
return false
}
if netconf [ "masterplugin" ] . ( bool ) == true {
return true
}
return false
}
func delegateAdd ( podif func ( ) string , argif string , netconf map [ string ] interface { } , onlyMaster bool ) ( bool , error ) {
netconfBytes , err := json . Marshal ( netconf )
if err != nil {
return true , fmt . Errorf ( "Multus: error serializing multus delegate netconf: %v" , err )
}
if isMasterplugin ( netconf ) != onlyMaster {
return true , nil
}
if ! isMasterplugin ( netconf ) {
if os . Setenv ( "CNI_IFNAME" , podif ( ) ) != nil {
return true , fmt . Errorf ( "Multus: error in setting CNI_IFNAME" )
}
} else {
if os . Setenv ( "CNI_IFNAME" , argif ) != nil {
return true , fmt . Errorf ( "Multus: error in setting CNI_IFNAME" )
}
}
2018-03-15 05:18:31 +00:00
if netconf [ "ifnameRequest" ] != nil {
if os . Setenv ( "CNI_IFNAME" , netconf [ "ifnameRequest" ] . ( string ) ) != nil {
return true , fmt . Errorf ( "Multus: error in setting CNI_IFNAME" )
}
}
2016-12-13 14:48:12 +00:00
result , err := invoke . DelegateAdd ( netconf [ "type" ] . ( string ) , netconfBytes )
if err != nil {
return true , fmt . Errorf ( "Multus: error in invoke Delegate add - %q: %v" , netconf [ "type" ] . ( string ) , err )
}
if ! isMasterplugin ( netconf ) {
return true , nil
}
return false , result . Print ( )
}
func delegateDel ( podif func ( ) string , argif string , netconf map [ string ] interface { } ) error {
netconfBytes , err := json . Marshal ( netconf )
if err != nil {
return fmt . Errorf ( "Multus: error serializing multus delegate netconf: %v" , err )
}
if ! isMasterplugin ( netconf ) {
if os . Setenv ( "CNI_IFNAME" , podif ( ) ) != nil {
return fmt . Errorf ( "Multus: error in setting CNI_IFNAME" )
}
} else {
if os . Setenv ( "CNI_IFNAME" , argif ) != nil {
return fmt . Errorf ( "Multus: error in setting CNI_IFNAME" )
}
}
err = invoke . DelegateDel ( netconf [ "type" ] . ( string ) , netconfBytes )
if err != nil {
return fmt . Errorf ( "Multus: error in invoke Delegate del - %q: %v" , netconf [ "type" ] . ( string ) , err )
}
return err
}
2017-02-21 19:01:58 +00:00
func clearPlugins ( mIdx int , pIdx int , argIfname string , delegates [ ] map [ string ] interface { } ) error {
if os . Setenv ( "CNI_COMMAND" , "DEL" ) != nil {
return fmt . Errorf ( "Multus: error in setting CNI_COMMAND to DEL" )
}
podifName := getifname ( )
r := delegateDel ( podifName , argIfname , delegates [ mIdx ] )
if r != nil {
return r
}
for idx := 0 ; idx < pIdx && idx != mIdx ; idx ++ {
r := delegateDel ( podifName , argIfname , delegates [ idx ] )
if r != nil {
return r
}
}
return nil
}
2016-12-13 14:48:12 +00:00
func cmdAdd ( args * skel . CmdArgs ) error {
var result error
2017-08-11 16:24:43 +00:00
var nopodnet bool
2016-12-13 14:48:12 +00:00
n , err := loadNetConf ( args . StdinData )
if err != nil {
2017-08-11 16:24:43 +00:00
return fmt . Errorf ( "err in loading netconf: %v" , err )
2016-12-13 14:48:12 +00:00
}
2017-06-29 16:19:09 +00:00
if n . Kubeconfig != "" {
2018-04-20 17:30:12 +00:00
podDelegate , err := k8s . GetK8sNetwork ( args , n . Kubeconfig )
2017-12-02 17:20:09 +00:00
if err != nil {
2018-04-20 17:30:12 +00:00
if _ , ok := err . ( * k8s . NoK8sNetworkError ) ; ok {
2017-12-02 17:20:09 +00:00
nopodnet = true
if ! defaultcninetwork {
return fmt . Errorf ( "Multus: Err in getting k8s network from the pod spec annotation, check the pod spec or set delegate for the default network, Refer the README.md: %v" , err )
}
} else if ! defaultcninetwork {
return fmt . Errorf ( "Multus: Err in getting k8s network from pod: %v" , err )
2017-08-11 16:24:43 +00:00
}
}
2018-03-13 15:51:48 +00:00
// If it's empty just leave it as the netconfig states (e.g. just default)
2017-08-11 16:24:43 +00:00
if len ( podDelegate ) != 0 {
2018-03-13 15:51:48 +00:00
if n . UseDefault {
// In the case that we force the default
// We add the found configs from CRD
for _ , eachDelegate := range podDelegate {
eachDelegate [ "masterplugin" ] = false
2018-04-20 17:30:12 +00:00
n . Delegates = append ( n . Delegates , eachDelegate )
2018-03-13 15:51:48 +00:00
}
} else {
// Otherwise, only the CRD delegates are used.
n . Delegates = podDelegate
}
2017-08-11 16:24:43 +00:00
}
2017-06-29 16:19:09 +00:00
}
2016-12-13 14:48:12 +00:00
for _ , delegate := range n . Delegates {
if err := checkDelegate ( delegate ) ; err != nil {
return fmt . Errorf ( "Multus: Err in delegate conf: %v" , err )
}
}
2017-08-11 16:24:43 +00:00
if n . Kubeconfig == "" || nopodnet {
2017-06-29 16:19:09 +00:00
if err := saveDelegates ( args . ContainerID , n . CNIDir , n . Delegates ) ; err != nil {
return fmt . Errorf ( "Multus: Err in saving the delegates: %v" , err )
}
2016-12-13 14:48:12 +00:00
}
podifName := getifname ( )
2017-02-21 19:01:58 +00:00
var mIndex int
for index , delegate := range n . Delegates {
err , r := delegateAdd ( podifName , args . IfName , delegate , true )
if err != true {
2016-12-13 14:48:12 +00:00
result = r
2017-02-21 19:01:58 +00:00
mIndex = index
} else if ( err != false ) && r != nil {
2016-12-13 14:48:12 +00:00
return r
}
}
2017-02-21 19:01:58 +00:00
for index , delegate := range n . Delegates {
err , r := delegateAdd ( podifName , args . IfName , delegate , false )
if err != true {
2016-12-13 14:48:12 +00:00
result = r
2017-02-21 19:01:58 +00:00
} else if ( err != false ) && r != nil {
perr := clearPlugins ( mIndex , index , args . IfName , n . Delegates )
if perr != nil {
return perr
}
2016-12-13 14:48:12 +00:00
return r
}
}
return result
}
func cmdDel ( args * skel . CmdArgs ) error {
var result error
2017-08-11 16:24:43 +00:00
var nopodnet bool
2016-12-13 14:48:12 +00:00
in , err := loadNetConf ( args . StdinData )
if err != nil {
return err
}
2017-06-29 16:19:09 +00:00
if in . Kubeconfig != "" {
2018-04-20 17:30:12 +00:00
podDelegate , r := k8s . GetK8sNetwork ( args , in . Kubeconfig )
2018-04-23 06:48:04 +00:00
if r != nil {
if _ , ok := r . ( * NoK8sNetworkError ) ; ok {
nopodnet = true
// no network found from default and annotaed network,
// we do nothing to remove network for the pod!
if ! defaultcninetwork {
return fmt . Errorf ( "Multus: Err in getting k8s network from the poc spec, check the pod spec or set delegate for the default network, Refer the README.md: %v" , r )
}
} else {
return fmt . Errorf ( "Multus: Err in getting k8s network from pod: %v" , r )
2017-08-11 16:24:43 +00:00
}
2017-06-29 16:19:09 +00:00
}
2016-12-13 14:48:12 +00:00
2017-08-11 16:24:43 +00:00
if len ( podDelegate ) != 0 {
2018-03-13 15:51:48 +00:00
if in . UseDefault {
// In the case that we force the default
// We add the found configs from CRD (in reverse order)
2018-04-20 17:30:12 +00:00
for i := len ( podDelegate ) - 1 ; i >= 0 ; i -- {
2018-03-13 15:51:48 +00:00
podDelegate [ i ] [ "masterplugin" ] = false
2018-04-20 17:30:12 +00:00
in . Delegates = append ( in . Delegates , podDelegate [ i ] )
2018-03-13 15:51:48 +00:00
}
} else {
in . Delegates = podDelegate
}
2017-08-11 16:24:43 +00:00
}
}
2017-06-29 16:19:09 +00:00
2017-08-11 16:24:43 +00:00
if in . Kubeconfig == "" || nopodnet {
2017-06-29 16:19:09 +00:00
netconfBytes , err := consumeScratchNetConf ( args . ContainerID , in . CNIDir )
if err != nil {
2017-11-09 17:55:37 +00:00
if os . IsNotExist ( err ) {
// Per spec should ignore error if resources are missing / already removed
return nil
}
2017-06-29 16:19:09 +00:00
return fmt . Errorf ( "Multus: Err in reading the delegates: %v" , err )
}
2017-09-28 14:42:56 +00:00
if err := json . Unmarshal ( netconfBytes , & in . Delegates ) ; err != nil {
2017-06-29 16:19:09 +00:00
return fmt . Errorf ( "Multus: failed to load netconf: %v" , err )
}
2016-12-13 14:48:12 +00:00
}
podifName := getifname ( )
2017-09-28 14:42:56 +00:00
for _ , delegate := range in . Delegates {
2016-12-13 14:48:12 +00:00
r := delegateDel ( podifName , args . IfName , delegate )
if r != nil {
return r
}
result = r
}
return result
}
func main ( ) {
2017-06-29 16:19:09 +00:00
skel . PluginMain ( cmdAdd , cmdDel , version . All )
2016-12-13 14:48:12 +00:00
}