forked from github/multus-cni
This changes introduce goreleaser, which does cross-compile and package, as well as add version into go code. This change also changes .travis.yml to allow to other users' forked repo.
451 lines
12 KiB
Go
451 lines
12 KiB
Go
// 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 main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/containernetworking/cni/pkg/skel"
|
|
cnitypes "github.com/containernetworking/cni/pkg/types"
|
|
"github.com/containernetworking/cni/pkg/types/020"
|
|
cniversion "github.com/containernetworking/cni/pkg/version"
|
|
"github.com/containernetworking/plugins/pkg/ns"
|
|
"github.com/containernetworking/plugins/pkg/testutils"
|
|
|
|
testhelpers "github.com/intel/multus-cni/testing"
|
|
|
|
. "github.com/onsi/ginkgo"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
func TestMultus(t *testing.T) {
|
|
RegisterFailHandler(Fail)
|
|
RunSpecs(t, "multus")
|
|
}
|
|
|
|
type fakePlugin struct {
|
|
expectedEnv []string
|
|
expectedConf string
|
|
expectedIfname string
|
|
result cnitypes.Result
|
|
err error
|
|
}
|
|
|
|
type fakeExec struct {
|
|
cniversion.PluginDecoder
|
|
|
|
addIndex int
|
|
delIndex int
|
|
plugins []*fakePlugin
|
|
}
|
|
|
|
func (f *fakeExec) addPlugin(expectedEnv []string, expectedIfname, expectedConf string, result *types020.Result, err error) {
|
|
f.plugins = append(f.plugins, &fakePlugin{
|
|
expectedEnv: expectedEnv,
|
|
expectedConf: expectedConf,
|
|
expectedIfname: expectedIfname,
|
|
result: result,
|
|
err: err,
|
|
})
|
|
}
|
|
|
|
func matchArray(a1, a2 []string) {
|
|
Expect(len(a1)).To(Equal(len(a2)))
|
|
for _, e1 := range a1 {
|
|
found := ""
|
|
for _, e2 := range a2 {
|
|
if e1 == e2 {
|
|
found = e2
|
|
break
|
|
}
|
|
}
|
|
// Compare element values for more descriptive test failure
|
|
Expect(e1).To(Equal(found))
|
|
}
|
|
}
|
|
|
|
// When faking plugin execution the ExecPlugin() call environ is not populated
|
|
// (while it would be for real exec). Filter the environment variables for
|
|
// CNI-specific ones that testcases will care about.
|
|
func gatherCNIEnv() []string {
|
|
filtered := make([]string, 0)
|
|
for _, env := range os.Environ() {
|
|
if strings.HasPrefix(env, "CNI_") {
|
|
filtered = append(filtered, env)
|
|
}
|
|
}
|
|
return filtered
|
|
}
|
|
|
|
func (f *fakeExec) ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error) {
|
|
cmd := os.Getenv("CNI_COMMAND")
|
|
var index int
|
|
switch cmd {
|
|
case "ADD":
|
|
Expect(len(f.plugins)).To(BeNumerically(">", f.addIndex))
|
|
index = f.addIndex
|
|
f.addIndex++
|
|
case "DEL":
|
|
Expect(len(f.plugins)).To(BeNumerically(">", f.delIndex))
|
|
index = len(f.plugins) - f.delIndex - 1
|
|
f.delIndex++
|
|
default:
|
|
// Should never be reached
|
|
Expect(false).To(BeTrue())
|
|
}
|
|
plugin := f.plugins[index]
|
|
|
|
GinkgoT().Logf("[%s %d] exec plugin %q found %+v\n", cmd, index, pluginPath, plugin)
|
|
|
|
if plugin.expectedConf != "" {
|
|
Expect(string(stdinData)).To(MatchJSON(plugin.expectedConf))
|
|
}
|
|
if plugin.expectedIfname != "" {
|
|
Expect(os.Getenv("CNI_IFNAME")).To(Equal(plugin.expectedIfname))
|
|
}
|
|
|
|
if len(plugin.expectedEnv) > 0 {
|
|
cniEnv := gatherCNIEnv()
|
|
for _, expectedCniEnvVar := range plugin.expectedEnv {
|
|
Expect(cniEnv).Should(ContainElement(expectedCniEnvVar))
|
|
}
|
|
}
|
|
|
|
if plugin.err != nil {
|
|
return nil, plugin.err
|
|
}
|
|
|
|
resultJSON, err := json.Marshal(plugin.result)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
return resultJSON, nil
|
|
}
|
|
|
|
func (f *fakeExec) FindInPath(plugin string, paths []string) (string, error) {
|
|
Expect(len(paths)).To(BeNumerically(">", 0))
|
|
return filepath.Join(paths[0], plugin), nil
|
|
}
|
|
|
|
var _ = Describe("multus operations", func() {
|
|
var testNS ns.NetNS
|
|
var tmpDir string
|
|
|
|
BeforeEach(func() {
|
|
// Create a new NetNS so we don't modify the host
|
|
var err error
|
|
testNS, err = testutils.NewNS()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
os.Setenv("CNI_NETNS", testNS.Path())
|
|
os.Setenv("CNI_PATH", "/some/path")
|
|
|
|
tmpDir, err = ioutil.TempDir("", "multus_tmp")
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
AfterEach(func() {
|
|
Expect(testNS.Close()).To(Succeed())
|
|
os.Unsetenv("CNI_PATH")
|
|
os.Unsetenv("CNI_ARGS")
|
|
err := os.RemoveAll(tmpDir)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
It("executes delegates", func() {
|
|
args := &skel.CmdArgs{
|
|
ContainerID: "123456789",
|
|
Netns: testNS.Path(),
|
|
IfName: "eth0",
|
|
StdinData: []byte(`{
|
|
"name": "node-cni-network",
|
|
"type": "multus",
|
|
"defaultnetworkfile": "/tmp/foo.multus.conf",
|
|
"defaultnetworkwaitseconds": 3,
|
|
"delegates": [{
|
|
"name": "weave1",
|
|
"cniVersion": "0.2.0",
|
|
"type": "weave-net"
|
|
},{
|
|
"name": "other1",
|
|
"cniVersion": "0.2.0",
|
|
"type": "other-plugin"
|
|
}]
|
|
}`),
|
|
}
|
|
|
|
// Touch the default network file.
|
|
configPath := "/tmp/foo.multus.conf"
|
|
os.OpenFile(configPath, os.O_RDONLY|os.O_CREATE, 0755)
|
|
|
|
fExec := &fakeExec{}
|
|
expectedResult1 := &types020.Result{
|
|
CNIVersion: "0.2.0",
|
|
IP4: &types020.IPConfig{
|
|
IP: *testhelpers.EnsureCIDR("1.1.1.2/24"),
|
|
},
|
|
}
|
|
expectedConf1 := `{
|
|
"name": "weave1",
|
|
"cniVersion": "0.2.0",
|
|
"type": "weave-net"
|
|
}`
|
|
fExec.addPlugin(nil, "eth0", expectedConf1, expectedResult1, nil)
|
|
|
|
expectedResult2 := &types020.Result{
|
|
CNIVersion: "0.2.0",
|
|
IP4: &types020.IPConfig{
|
|
IP: *testhelpers.EnsureCIDR("1.1.1.5/24"),
|
|
},
|
|
}
|
|
expectedConf2 := `{
|
|
"name": "other1",
|
|
"cniVersion": "0.2.0",
|
|
"type": "other-plugin"
|
|
}`
|
|
fExec.addPlugin(nil, "net1", expectedConf2, expectedResult2, nil)
|
|
|
|
os.Setenv("CNI_COMMAND", "ADD")
|
|
os.Setenv("CNI_IFNAME", "eth0")
|
|
result, err := cmdAdd(args, fExec, nil)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(fExec.addIndex).To(Equal(len(fExec.plugins)))
|
|
r := result.(*types020.Result)
|
|
// plugin 1 is the masterplugin
|
|
Expect(reflect.DeepEqual(r, expectedResult1)).To(BeTrue())
|
|
|
|
os.Setenv("CNI_COMMAND", "DEL")
|
|
os.Setenv("CNI_IFNAME", "eth0")
|
|
err = cmdDel(args, fExec, nil)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(fExec.delIndex).To(Equal(len(fExec.plugins)))
|
|
|
|
// Cleanup default network file.
|
|
if _, errStat := os.Stat(configPath); errStat == nil {
|
|
errRemove := os.Remove(configPath)
|
|
Expect(errRemove).NotTo(HaveOccurred())
|
|
}
|
|
|
|
})
|
|
|
|
It("executes delegates with interface name and MAC and IP addr", func() {
|
|
podNet := `[{"name":"net1",
|
|
"interface": "test1",
|
|
"ips":"1.2.3.4/24"},
|
|
{"name":"net2",
|
|
"mac": "c2:11:22:33:44:66",
|
|
"ips": "10.0.0.1"}
|
|
]`
|
|
fakePod := testhelpers.NewFakePod("testpod", podNet, "")
|
|
net1 := `{
|
|
"name": "net1",
|
|
"type": "mynet",
|
|
"cniVersion": "0.2.0"
|
|
}`
|
|
net2 := `{
|
|
"name": "net2",
|
|
"type": "mynet2",
|
|
"cniVersion": "0.2.0"
|
|
}`
|
|
args := &skel.CmdArgs{
|
|
ContainerID: "123456789",
|
|
Netns: testNS.Path(),
|
|
IfName: "eth0",
|
|
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
|
|
StdinData: []byte(`{
|
|
"name": "node-cni-network",
|
|
"type": "multus",
|
|
"kubeconfig": "/etc/kubernetes/node-kubeconfig.yaml",
|
|
"delegates": [{
|
|
"name": "weave1",
|
|
"cniVersion": "0.2.0",
|
|
"type": "weave-net"
|
|
}]
|
|
}`),
|
|
}
|
|
|
|
fExec := &fakeExec{}
|
|
expectedResult1 := &types020.Result{
|
|
CNIVersion: "0.2.0",
|
|
IP4: &types020.IPConfig{
|
|
IP: *testhelpers.EnsureCIDR("1.1.1.2/24"),
|
|
},
|
|
}
|
|
expectedConf1 := `{
|
|
"name": "weave1",
|
|
"cniVersion": "0.2.0",
|
|
"type": "weave-net"
|
|
}`
|
|
fExec.addPlugin(nil, "eth0", expectedConf1, expectedResult1, nil)
|
|
fExec.addPlugin([]string{"CNI_ARGS=IgnoreUnknown=true;IP=1.2.3.4/24"}, "test1", net1, &types020.Result{
|
|
CNIVersion: "0.2.0",
|
|
IP4: &types020.IPConfig{
|
|
IP: *testhelpers.EnsureCIDR("1.1.1.3/24"),
|
|
},
|
|
}, nil)
|
|
fExec.addPlugin([]string{"CNI_ARGS=IgnoreUnknown=true;MAC=c2:11:22:33:44:66;IP=10.0.0.1"}, "net2", net2, &types020.Result{
|
|
CNIVersion: "0.2.0",
|
|
IP4: &types020.IPConfig{
|
|
IP: *testhelpers.EnsureCIDR("1.1.1.4/24"),
|
|
},
|
|
}, nil)
|
|
|
|
fKubeClient := testhelpers.NewFakeKubeClient()
|
|
fKubeClient.AddPod(fakePod)
|
|
fKubeClient.AddNetConfig(fakePod.ObjectMeta.Namespace, "net1", net1)
|
|
fKubeClient.AddNetConfig(fakePod.ObjectMeta.Namespace, "net2", net2)
|
|
|
|
os.Setenv("CNI_COMMAND", "ADD")
|
|
os.Setenv("CNI_IFNAME", "eth0")
|
|
result, err := cmdAdd(args, fExec, fKubeClient)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(fExec.addIndex).To(Equal(len(fExec.plugins)))
|
|
Expect(fKubeClient.PodCount).To(Equal(3))
|
|
Expect(fKubeClient.NetCount).To(Equal(2))
|
|
r := result.(*types020.Result)
|
|
// plugin 1 is the masterplugin
|
|
Expect(reflect.DeepEqual(r, expectedResult1)).To(BeTrue())
|
|
})
|
|
|
|
It("executes delegates and kubernetes networks", func() {
|
|
fakePod := testhelpers.NewFakePod("testpod", "net1,net2", "")
|
|
net1 := `{
|
|
"name": "net1",
|
|
"type": "mynet",
|
|
"cniVersion": "0.2.0"
|
|
}`
|
|
net2 := `{
|
|
"name": "net2",
|
|
"type": "mynet2",
|
|
"cniVersion": "0.2.0"
|
|
}`
|
|
net3 := `{
|
|
"name": "net3",
|
|
"type": "mynet3",
|
|
"cniVersion": "0.2.0"
|
|
}`
|
|
args := &skel.CmdArgs{
|
|
ContainerID: "123456789",
|
|
Netns: testNS.Path(),
|
|
IfName: "eth0",
|
|
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
|
|
StdinData: []byte(`{
|
|
"name": "node-cni-network",
|
|
"type": "multus",
|
|
"kubeconfig": "/etc/kubernetes/node-kubeconfig.yaml",
|
|
"delegates": [{
|
|
"name": "weave1",
|
|
"cniVersion": "0.2.0",
|
|
"type": "weave-net"
|
|
}]
|
|
}`),
|
|
}
|
|
|
|
fExec := &fakeExec{}
|
|
expectedResult1 := &types020.Result{
|
|
CNIVersion: "0.2.0",
|
|
IP4: &types020.IPConfig{
|
|
IP: *testhelpers.EnsureCIDR("1.1.1.2/24"),
|
|
},
|
|
}
|
|
expectedConf1 := `{
|
|
"name": "weave1",
|
|
"cniVersion": "0.2.0",
|
|
"type": "weave-net"
|
|
}`
|
|
fExec.addPlugin(nil, "eth0", expectedConf1, expectedResult1, nil)
|
|
fExec.addPlugin(nil, "net1", net1, &types020.Result{
|
|
CNIVersion: "0.2.0",
|
|
IP4: &types020.IPConfig{
|
|
IP: *testhelpers.EnsureCIDR("1.1.1.3/24"),
|
|
},
|
|
}, nil)
|
|
fExec.addPlugin(nil, "net2", net2, &types020.Result{
|
|
CNIVersion: "0.2.0",
|
|
IP4: &types020.IPConfig{
|
|
IP: *testhelpers.EnsureCIDR("1.1.1.4/24"),
|
|
},
|
|
}, nil)
|
|
|
|
fKubeClient := testhelpers.NewFakeKubeClient()
|
|
fKubeClient.AddPod(fakePod)
|
|
fKubeClient.AddNetConfig(fakePod.ObjectMeta.Namespace, "net1", net1)
|
|
fKubeClient.AddNetConfig(fakePod.ObjectMeta.Namespace, "net2", net2)
|
|
// net3 is not used; make sure it's not accessed
|
|
fKubeClient.AddNetConfig(fakePod.ObjectMeta.Namespace, "net3", net3)
|
|
|
|
os.Setenv("CNI_COMMAND", "ADD")
|
|
os.Setenv("CNI_IFNAME", "eth0")
|
|
result, err := cmdAdd(args, fExec, fKubeClient)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(fExec.addIndex).To(Equal(len(fExec.plugins)))
|
|
Expect(fKubeClient.PodCount).To(Equal(3))
|
|
Expect(fKubeClient.NetCount).To(Equal(2))
|
|
r := result.(*types020.Result)
|
|
// plugin 1 is the masterplugin
|
|
Expect(reflect.DeepEqual(r, expectedResult1)).To(BeTrue())
|
|
})
|
|
|
|
It("ensure delegates get portmap runtime config", func() {
|
|
args := &skel.CmdArgs{
|
|
ContainerID: "123456789",
|
|
Netns: testNS.Path(),
|
|
IfName: "eth0",
|
|
StdinData: []byte(`{
|
|
"name": "node-cni-network",
|
|
"type": "multus",
|
|
"delegates": [{
|
|
"cniVersion": "0.3.1",
|
|
"name": "mynet-confList",
|
|
"plugins": [
|
|
{
|
|
"type": "firstPlugin",
|
|
"capabilities": {"portMappings": true}
|
|
}
|
|
]
|
|
}],
|
|
"runtimeConfig": {
|
|
"portMappings": [
|
|
{"hostPort": 8080, "containerPort": 80, "protocol": "tcp"}
|
|
]
|
|
}
|
|
}`),
|
|
}
|
|
|
|
fExec := &fakeExec{}
|
|
expectedConf1 := `{
|
|
"capabilities": {"portMappings": true},
|
|
"name": "mynet-confList",
|
|
"cniVersion": "0.3.1",
|
|
"type": "firstPlugin",
|
|
"runtimeConfig": {
|
|
"portMappings": [
|
|
{"hostPort": 8080, "containerPort": 80, "protocol": "tcp"}
|
|
]
|
|
}
|
|
}`
|
|
fExec.addPlugin(nil, "eth0", expectedConf1, nil, nil)
|
|
os.Setenv("CNI_COMMAND", "ADD")
|
|
os.Setenv("CNI_IFNAME", "eth0")
|
|
_, err := cmdAdd(args, fExec, nil)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
})
|