check version incompatibility (#762)

* multus: entrypoint: disallow incompatible cni versions

When top level CNI version is 0.4.0 or more, nested CNI version
can't be less than 0.4.0 since these are incompatible. This
closes issue #737.

Signed-off-by: Balazs Nemeth <bnemeth@redhat.com>

* multus: thick: disallow incompatible cni versions

Similarly to disallowing incompatible versions in entrypoint.sh,
add the same logic in go for the thick plugin.

Signed-off-by: Balazs Nemeth <bnemeth@redhat.com>

* multus: add unit test for incompatible cni versions

Signed-off-by: Balazs Nemeth <bnemeth@redhat.com>
This commit is contained in:
Balazs Nemeth 2022-02-28 13:50:39 +01:00 committed by GitHub
parent 6dd45f38f9
commit 450e1d3414
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 153 additions and 35 deletions

View File

@ -139,10 +139,14 @@ func main() {
configurationOptions = append( configurationOptions = append(
configurationOptions, config.WithReadinessFileIndicator(*readinessIndicator)) configurationOptions, config.WithReadinessFileIndicator(*readinessIndicator))
} }
multusConfig := config.NewMultusConfig(multusPluginName, *cniVersion, *multusKubeconfig, configurationOptions...)
multusConfig, err := config.NewMultusConfig(multusPluginName, *cniVersion, *multusKubeconfig, configurationOptions...)
if err != nil {
_ = logging.Errorf("Failed to create multus config: %v", err)
os.Exit(3)
}
var configManager *config.Manager var configManager *config.Manager
var err error
if *multusMasterCni == "" { if *multusMasterCni == "" {
configManager, err = config.NewManager(*multusConfig, *multusAutoconfigDir) configManager, err = config.NewManager(*multusConfig, *multusAutoconfigDir)
} else { } else {

1
go.mod
View File

@ -3,6 +3,7 @@ module gopkg.in/k8snetworkplumbingwg/multus-cni.v3
go 1.16 go 1.16
require ( require (
github.com/blang/semver v3.5.1+incompatible
github.com/containernetworking/cni v0.8.1 github.com/containernetworking/cni v0.8.1
github.com/containernetworking/plugins v0.9.1 github.com/containernetworking/plugins v0.9.1
github.com/fsnotify/fsnotify v1.4.9 github.com/fsnotify/fsnotify v1.4.9

View File

@ -87,6 +87,25 @@ if ! type python3 &> /dev/null; then
alias python=python3 alias python=python3
fi fi
function checkCniVersion {
cniversion_python_tmpfile=$(mktemp)
cat << EOF > $cniversion_python_tmpfile
import json, sys
def version(v):
return [int(x) for x in v.split(".")]
v_040 = version("0.4.0")
v_top_level = sys.argv[2]
with open(sys.argv[1], "r") as f:
v_nested = json.load(f)["cniVersion"]
if version(v_top_level) >= v_040 and version(v_nested) < v_040:
msg = "Multus cni version is %s while master plugin cni version is %s"
print(msg % (v_top_level, v_nested))
EOF
python $cniversion_python_tmpfile $1 $2
}
# Parse parameters given as arguments to this script. # Parse parameters given as arguments to this script.
while [ "$1" != "" ]; do while [ "$1" != "" ]; do
PARAM=`echo $1 | awk -F= '{print $1}'` PARAM=`echo $1 | awk -F= '{print $1}'`
@ -374,6 +393,12 @@ EOF
MASTER_PLUGIN_LOCATION=$MULTUS_AUTOCONF_DIR/$MASTER_PLUGIN MASTER_PLUGIN_LOCATION=$MULTUS_AUTOCONF_DIR/$MASTER_PLUGIN
MASTER_PLUGIN_JSON="$(cat $MASTER_PLUGIN_LOCATION)" MASTER_PLUGIN_JSON="$(cat $MASTER_PLUGIN_LOCATION)"
log "Using $MASTER_PLUGIN_LOCATION as a source to generate the Multus configuration" log "Using $MASTER_PLUGIN_LOCATION as a source to generate the Multus configuration"
CHECK_CNI_VERSION=$(checkCniVersion $MASTER_PLUGIN_LOCATION $CNI_VERSION)
if [ "$CHECK_CNI_VERSION" != "" ] ; then
error "$CHECK_CNI_VERSION"
exit 1
fi
CONF=$(cat <<-EOF CONF=$(cat <<-EOF
{ {
$CNI_VERSION_STRING $CNI_VERSION_STRING

View File

@ -17,12 +17,15 @@ package config
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"path/filepath" "path/filepath"
"sort" "sort"
"strings" "strings"
"time" "time"
"github.com/blang/semver"
) )
const ( const (
@ -52,7 +55,7 @@ type MultusConf struct {
// NewMultusConfig creates a basic configuration generator. It can be mutated // NewMultusConfig creates a basic configuration generator. It can be mutated
// via the `With...` methods. // via the `With...` methods.
func NewMultusConfig(pluginName string, cniVersion string, kubeconfig string, configurationOptions ...Option) *MultusConf { func NewMultusConfig(pluginName string, cniVersion string, kubeconfig string, configurationOptions ...Option) (*MultusConf, error) {
multusConfig := &MultusConf{ multusConfig := &MultusConf{
Name: MultusDefaultNetworkName, Name: MultusDefaultNetworkName,
CNIVersion: cniVersion, CNIVersion: cniVersion,
@ -61,10 +64,45 @@ func NewMultusConfig(pluginName string, cniVersion string, kubeconfig string, co
Kubeconfig: kubeconfig, Kubeconfig: kubeconfig,
Delegates: []interface{}{}, Delegates: []interface{}{},
} }
for _, configOption := range configurationOptions {
configOption(multusConfig) err := multusConfig.Mutate(configurationOptions...)
return multusConfig, err
} }
return multusConfig
// CheckVersionCompatibility checks compatibilty of the
// top level cni version with the delegate cni version.
// Since version 0.4.0, CHECK was introduced, which
// causes incompatibility.
func CheckVersionCompatibility(mc *MultusConf) error {
const versionFmt = "delegate cni version is %s while top level cni version is %s"
v040, _ := semver.Make("0.4.0")
multusCNIVersion, err := semver.Make(mc.CNIVersion)
if err != nil {
return errors.New("couldn't get top level cni version")
}
if multusCNIVersion.GTE(v040) {
for _, delegate := range mc.Delegates {
delegatesMap, ok := delegate.(map[string]interface{})
if !ok {
return errors.New("couldn't get cni version of delegate")
}
delegateVersion, ok := delegatesMap["cniVersion"].(string)
if !ok {
return errors.New("couldn't get cni version of delegate")
}
v, err := semver.Make(delegateVersion)
if err != nil {
return err
}
if v.LT(v040) {
return fmt.Errorf(versionFmt, delegateVersion, mc.CNIVersion)
}
}
}
return nil
} }
// Generate generates the multus configuration from whatever state is currently // Generate generates the multus configuration from whatever state is currently
@ -76,10 +114,12 @@ func (mc *MultusConf) Generate() (string, error) {
// Mutate updates the MultusConf attributes according to the provided // Mutate updates the MultusConf attributes according to the provided
// configuration `Option`s // configuration `Option`s
func (mc *MultusConf) Mutate(configurationOptions ...Option) { func (mc *MultusConf) Mutate(configurationOptions ...Option) error {
for _, configOption := range configurationOptions { for _, configOption := range configurationOptions {
configOption(mc) configOption(mc)
} }
return CheckVersionCompatibility(mc)
} }
// WithNamespaceIsolation mutates the inner state to enable the // WithNamespaceIsolation mutates the inner state to enable the

View File

@ -45,46 +45,51 @@ var primaryCNIConfig = map[string]interface{}{
"logfile-maxage": 5, "logfile-maxage": 5,
} }
func newMultusConfigWithDelegates(pluginName string, cniVersion string, kubeconfig string, primaryCNIPluginConfig interface{}, configOptions ...Option) *MultusConf { func newMultusConfigWithDelegates(pluginName string, cniVersion string, kubeconfig string, primaryCNIPluginConfig interface{}, configOptions ...Option) (*MultusConf, error) {
multusConfig := NewMultusConfig(pluginName, cniVersion, kubeconfig, configOptions...) multusConfig, err := NewMultusConfig(pluginName, cniVersion, kubeconfig, configOptions...)
multusConfig.Delegates = []interface{}{primaryCNIPluginConfig} if err != nil {
return multusConfig return multusConfig, err
}
return multusConfig, multusConfig.Mutate(withDelegates(primaryCNIPluginConfig.(map[string]interface{})))
} }
func TestBasicMultusConfig(t *testing.T) { func TestBasicMultusConfig(t *testing.T) {
multusConfig := newMultusConfigWithDelegates( multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName, primaryCNIName,
cniVersion, cniVersion,
kubeconfig, kubeconfig,
primaryCNIConfig) primaryCNIConfig)
assertError(t, err, nil)
expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}" expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult) newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
} }
func TestMultusConfigWithNamespaceIsolation(t *testing.T) { func TestMultusConfigWithNamespaceIsolation(t *testing.T) {
multusConfig := newMultusConfigWithDelegates( multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName, primaryCNIName,
cniVersion, cniVersion,
kubeconfig, kubeconfig,
primaryCNIConfig, primaryCNIConfig,
WithNamespaceIsolation()) WithNamespaceIsolation())
assertError(t, err, nil)
expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"namespaceIsolation\":true,\"type\":\"myCNI\"}" expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"namespaceIsolation\":true,\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult) newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
} }
func TestMultusConfigWithReadinessIndicator(t *testing.T) { func TestMultusConfigWithReadinessIndicator(t *testing.T) {
multusConfig := newMultusConfigWithDelegates( multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName, primaryCNIName,
cniVersion, cniVersion,
kubeconfig, kubeconfig,
primaryCNIConfig, primaryCNIConfig,
WithReadinessFileIndicator("/a/b/u/it-lives")) WithReadinessFileIndicator("/a/b/u/it-lives"))
assertError(t, err, nil)
expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"readinessindicatorfile\":\"/a/b/u/it-lives\",\"type\":\"myCNI\"}" expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"readinessindicatorfile\":\"/a/b/u/it-lives\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult) newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
} }
func TestMultusConfigWithLoggingConfiguration(t *testing.T) { func TestMultusConfigWithLoggingConfiguration(t *testing.T) {
multusConfig := newMultusConfigWithDelegates( multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName, primaryCNIName,
cniVersion, cniVersion,
kubeconfig, kubeconfig,
@ -92,96 +97,104 @@ func TestMultusConfigWithLoggingConfiguration(t *testing.T) {
WithLogLevel("notice"), WithLogLevel("notice"),
WithLogToStdErr(), WithLogToStdErr(),
WithLogFile("/u/y/w/log.1")) WithLogFile("/u/y/w/log.1"))
assertError(t, err, nil)
expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"logFile\":\"/u/y/w/log.1\",\"logLevel\":\"notice\",\"logToStderr\":true,\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}" expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"logFile\":\"/u/y/w/log.1\",\"logLevel\":\"notice\",\"logToStderr\":true,\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult) newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
} }
func TestMultusConfigWithGlobalNamespace(t *testing.T) { func TestMultusConfigWithGlobalNamespace(t *testing.T) {
const globalNamespace = "come-along-ns" const globalNamespace = "come-along-ns"
multusConfig := newMultusConfigWithDelegates( multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName, primaryCNIName,
cniVersion, cniVersion,
kubeconfig, kubeconfig,
primaryCNIConfig, primaryCNIConfig,
WithGlobalNamespaces(globalNamespace)) WithGlobalNamespaces(globalNamespace))
assertError(t, err, nil)
expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"globalNamespaces\":\"come-along-ns\",\"type\":\"myCNI\"}" expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"globalNamespaces\":\"come-along-ns\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult) newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
} }
func TestMultusConfigWithAdditionalBinDir(t *testing.T) { func TestMultusConfigWithAdditionalBinDir(t *testing.T) {
const anotherCNIBinDir = "a-dir-somewhere" const anotherCNIBinDir = "a-dir-somewhere"
multusConfig := newMultusConfigWithDelegates( multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName, primaryCNIName,
cniVersion, cniVersion,
kubeconfig, kubeconfig,
primaryCNIConfig, primaryCNIConfig,
WithAdditionalBinaryFileDir(anotherCNIBinDir)) WithAdditionalBinaryFileDir(anotherCNIBinDir))
assertError(t, err, nil)
expectedResult := "{\"binDir\":\"a-dir-somewhere\",\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}" expectedResult := "{\"binDir\":\"a-dir-somewhere\",\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult) newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
} }
func TestMultusConfigWithCapabilities(t *testing.T) { func TestMultusConfigWithCapabilities(t *testing.T) {
multusConfig := newMultusConfigWithDelegates( multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName, primaryCNIName,
cniVersion, cniVersion,
kubeconfig, kubeconfig,
primaryCNIConfig, primaryCNIConfig,
withCapabilities( withCapabilities(
documentHelper(`{"capabilities": {"portMappings": true}}`))) documentHelper(`{"capabilities": {"portMappings": true}}`)))
assertError(t, err, nil)
expectedResult := "{\"capabilities\":{\"portMappings\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}" expectedResult := "{\"capabilities\":{\"portMappings\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult) newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
} }
func TestMultusConfigWithMultipleCapabilities(t *testing.T) { func TestMultusConfigWithMultipleCapabilities(t *testing.T) {
multusConfig := newMultusConfigWithDelegates( multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName, primaryCNIName,
cniVersion, cniVersion,
kubeconfig, kubeconfig,
primaryCNIConfig, primaryCNIConfig,
withCapabilities( withCapabilities(
documentHelper(`{"capabilities": {"portMappings": true, "tuning": true}}`))) documentHelper(`{"capabilities": {"portMappings": true, "tuning": true}}`)))
assertError(t, err, nil)
expectedResult := "{\"capabilities\":{\"portMappings\":true,\"tuning\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}" expectedResult := "{\"capabilities\":{\"portMappings\":true,\"tuning\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult) newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
} }
func TestMultusConfigWithMultipleCapabilitiesFilterOnlyEnabled(t *testing.T) { func TestMultusConfigWithMultipleCapabilitiesFilterOnlyEnabled(t *testing.T) {
multusConfig := newMultusConfigWithDelegates( multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName, primaryCNIName,
cniVersion, cniVersion,
kubeconfig, kubeconfig,
primaryCNIConfig, primaryCNIConfig,
withCapabilities( withCapabilities(
documentHelper(`{"capabilities": {"portMappings": true, "tuning": false}}`))) documentHelper(`{"capabilities": {"portMappings": true, "tuning": false}}`)))
assertError(t, err, nil)
expectedResult := "{\"capabilities\":{\"portMappings\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}" expectedResult := "{\"capabilities\":{\"portMappings\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult) newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
} }
func TestMultusConfigWithMultipleCapabilitiesDefinedOnAPlugin(t *testing.T) { func TestMultusConfigWithMultipleCapabilitiesDefinedOnAPlugin(t *testing.T) {
multusConfig := newMultusConfigWithDelegates( multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName, primaryCNIName,
cniVersion, cniVersion,
kubeconfig, kubeconfig,
primaryCNIConfig, primaryCNIConfig,
withCapabilities( withCapabilities(
documentHelper(`{"plugins": [ {"capabilities": {"portMappings": true, "tuning": true}} ] }`))) documentHelper(`{"plugins": [ {"capabilities": {"portMappings": true, "tuning": true}} ] }`)))
assertError(t, err, nil)
expectedResult := "{\"capabilities\":{\"portMappings\":true,\"tuning\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}" expectedResult := "{\"capabilities\":{\"portMappings\":true,\"tuning\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult) newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
} }
func TestMultusConfigWithCapabilitiesDefinedOnMultiplePlugins(t *testing.T) { func TestMultusConfigWithCapabilitiesDefinedOnMultiplePlugins(t *testing.T) {
multusConfig := newMultusConfigWithDelegates( multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName, primaryCNIName,
cniVersion, cniVersion,
kubeconfig, kubeconfig,
primaryCNIConfig, primaryCNIConfig,
withCapabilities( withCapabilities(
documentHelper(`{"plugins": [ {"capabilities": { "portMappings": true }}, {"capabilities": { "tuning": true }} ]}`))) documentHelper(`{"plugins": [ {"capabilities": { "portMappings": true }}, {"capabilities": { "tuning": true }} ]}`)))
assertError(t, err, nil)
expectedResult := "{\"capabilities\":{\"portMappings\":true,\"tuning\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}" expectedResult := "{\"capabilities\":{\"portMappings\":true,\"tuning\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult) newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
} }
func TestMultusConfigWithCapabilitiesDefinedOnMultiplePluginsFilterOnlyEnabled(t *testing.T) { func TestMultusConfigWithCapabilitiesDefinedOnMultiplePluginsFilterOnlyEnabled(t *testing.T) {
multusConfig := newMultusConfigWithDelegates( multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName, primaryCNIName,
cniVersion, cniVersion,
kubeconfig, kubeconfig,
@ -202,13 +215,48 @@ func TestMultusConfigWithCapabilitiesDefinedOnMultiplePluginsFilterOnlyEnabled(t
} }
] ]
}`))) }`)))
assertError(t, err, nil)
expectedResult := "{\"capabilities\":{\"portMappings\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}" expectedResult := "{\"capabilities\":{\"portMappings\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult) newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
} }
func assertError(t *testing.T, actual error, expected error) {
if actual != nil && expected != nil {
if actual.Error() != expected.Error() {
t.Fatalf("multus config generation failed.\nExpected:\n%v\nbut GOT:\n%v", expected.Error(), actual.Error())
}
}
if actual == nil && expected != nil {
t.Fatalf("multus config generation failed.\nExpected:\n%v\nbut didn't get error", expected.Error())
} else if actual != nil && expected == nil {
t.Fatalf("multus config generation failed.\nDidn't expect error\nbut GOT: %v\n", actual.Error())
}
}
func invalidDelegateCNIVersion(delegateCNIVersion, multusCNIVersion string) error {
return fmt.Errorf("delegate cni version is %s while top level cni version is %s", delegateCNIVersion, multusCNIVersion)
}
func TestVersionIncompatibility(t *testing.T) {
const delegateCNIVersion = "0.3.0"
primaryCNIConfigOld := primaryCNIConfig
tmpVer := primaryCNIConfig["cniVersion"]
primaryCNIConfig["cniVersion"] = delegateCNIVersion
_, err := newMultusConfigWithDelegates(
primaryCNIName,
cniVersion,
kubeconfig,
primaryCNIConfigOld)
primaryCNIConfig["cniVersion"] = tmpVer
assertError(t, invalidDelegateCNIVersion(delegateCNIVersion, cniVersion), err)
}
func TestMultusConfigWithOverriddenName(t *testing.T) { func TestMultusConfigWithOverriddenName(t *testing.T) {
newNetworkName := "mega-net-2000" newNetworkName := "mega-net-2000"
multusConfig := newMultusConfigWithDelegates( multusConfig, _ := newMultusConfigWithDelegates(
primaryCNIName, primaryCNIName,
cniVersion, cniVersion,
kubeconfig, kubeconfig,

View File

@ -78,8 +78,9 @@ func newManager(config MultusConf, multusConfigDir string, defaultCNIPluginName
} }
if err := configManager.loadPrimaryCNIConfigFromFile(); err != nil { if err := configManager.loadPrimaryCNIConfigFromFile(); err != nil {
return nil, fmt.Errorf("failed to load the primary CNI configuration as a multus delegate") return nil, fmt.Errorf("failed to load the primary CNI configuration as a multus delegate with error '%v'", err)
} }
return configManager, nil return configManager, nil
} }
@ -88,8 +89,7 @@ func (m *Manager) loadPrimaryCNIConfigFromFile() error {
if err != nil { if err != nil {
return logging.Errorf("failed to access the primary CNI configuration from %s: %v", m.primaryCNIConfigPath, err) return logging.Errorf("failed to access the primary CNI configuration from %s: %v", m.primaryCNIConfigPath, err)
} }
m.loadPrimaryCNIConfigurationData(primaryCNIConfigData) return m.loadPrimaryCNIConfigurationData(primaryCNIConfigData)
return nil
} }
// OverrideNetworkName overrides the name of the multus configuration with the // OverrideNetworkName overrides the name of the multus configuration with the
@ -104,15 +104,14 @@ func (m *Manager) OverrideNetworkName() error {
if networkName == "" { if networkName == "" {
return fmt.Errorf("the primary CNI Configuration does not feature the network name: %v", m.cniConfigData) return fmt.Errorf("the primary CNI Configuration does not feature the network name: %v", m.cniConfigData)
} }
m.multusConfig.Mutate(WithOverriddenName(networkName)) return m.multusConfig.Mutate(WithOverriddenName(networkName))
return nil
} }
func (m *Manager) loadPrimaryCNIConfigurationData(primaryCNIConfigData interface{}) { func (m *Manager) loadPrimaryCNIConfigurationData(primaryCNIConfigData interface{}) error {
cniConfigData := primaryCNIConfigData.(map[string]interface{}) cniConfigData := primaryCNIConfigData.(map[string]interface{})
m.cniConfigData = cniConfigData m.cniConfigData = cniConfigData
m.multusConfig.Mutate( return m.multusConfig.Mutate(
withDelegates(cniConfigData), withDelegates(cniConfigData),
withCapabilities(cniConfigData)) withCapabilities(cniConfigData))
} }

View File

@ -61,7 +61,7 @@ var _ = Describe(suiteName, func() {
defaultCniConfig = fmt.Sprintf("%s/%s", multusConfigDir, primaryCNIPluginName) defaultCniConfig = fmt.Sprintf("%s/%s", multusConfigDir, primaryCNIPluginName)
Expect(ioutil.WriteFile(defaultCniConfig, []byte(primaryCNIPluginTemplate), userRWPermission)).To(Succeed()) Expect(ioutil.WriteFile(defaultCniConfig, []byte(primaryCNIPluginTemplate), userRWPermission)).To(Succeed())
multusConf := NewMultusConfig( multusConf, _ := NewMultusConfig(
primaryCNIName, primaryCNIName,
cniVersion, cniVersion,
kubeconfig) kubeconfig)

1
vendor/modules.txt vendored
View File

@ -4,6 +4,7 @@ github.com/Microsoft/go-winio/pkg/guid
# github.com/beorn7/perks v1.0.1 # github.com/beorn7/perks v1.0.1
github.com/beorn7/perks/quantile github.com/beorn7/perks/quantile
# github.com/blang/semver v3.5.1+incompatible # github.com/blang/semver v3.5.1+incompatible
## explicit
github.com/blang/semver github.com/blang/semver
# github.com/cespare/xxhash/v2 v2.1.1 # github.com/cespare/xxhash/v2 v2.1.1
github.com/cespare/xxhash/v2 github.com/cespare/xxhash/v2