diff --git a/multus/multus.go b/multus/multus.go index e8cf600f2..c9caa1817 100644 --- a/multus/multus.go +++ b/multus/multus.go @@ -148,7 +148,7 @@ func validateIfName(nsname string, ifname string) error { } func confAdd(rt *libcni.RuntimeConf, rawNetconf []byte, binDir string, exec invoke.Exec) (cnitypes.Result, error) { - logging.Debugf("conflistAdd: %v, %s, %s", rt, string(rawNetconf), binDir) + logging.Debugf("confAdd: %v, %s, %s", rt, string(rawNetconf), binDir) // In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go binDirs := filepath.SplitList(os.Getenv("CNI_PATH")) binDirs = append([]string{binDir}, binDirs...) @@ -167,6 +167,26 @@ func confAdd(rt *libcni.RuntimeConf, rawNetconf []byte, binDir string, exec invo return result, nil } +func confCheck(rt *libcni.RuntimeConf, rawNetconf []byte, binDir string, exec invoke.Exec) error { + logging.Debugf("confCheck: %v, %s, %s", rt, string(rawNetconf), binDir) + + binDirs := filepath.SplitList(os.Getenv("CNI_PATH")) + binDirs = append([]string{binDir}, binDirs...) + cniNet := libcni.NewCNIConfig(binDirs, exec) + + conf, err := libcni.ConfFromBytes(rawNetconf) + if err != nil { + return logging.Errorf("error in converting the raw bytes to conf: %v", err) + } + + err = cniNet.CheckNetwork(context.Background(), conf, rt) + if err != nil { + return logging.Errorf("error in getting result from DelNetwork: %v", err) + } + + return err +} + func confDel(rt *libcni.RuntimeConf, rawNetconf []byte, binDir string, exec invoke.Exec) error { logging.Debugf("conflistDel: %v, %s, %s", rt, string(rawNetconf), binDir) // In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go @@ -207,6 +227,26 @@ func conflistAdd(rt *libcni.RuntimeConf, rawnetconflist []byte, binDir string, e return result, nil } +func conflistCheck(rt *libcni.RuntimeConf, rawnetconflist []byte, binDir string, exec invoke.Exec) error { + logging.Debugf("conflistCheck: %v, %s, %s", rt, string(rawnetconflist), binDir) + + binDirs := filepath.SplitList(os.Getenv("CNI_PATH")) + binDirs = append([]string{binDir}, binDirs...) + cniNet := libcni.NewCNIConfig(binDirs, exec) + + confList, err := libcni.ConfListFromBytes(rawnetconflist) + if err != nil { + return logging.Errorf("conflistCheck: error converting the raw bytes into a conflist: %v", err) + } + + err = cniNet.CheckNetworkList(context.Background(), confList, rt) + if err != nil { + return logging.Errorf("conflistCheck: error in getting result from CheckNetworkList: %v", err) + } + + return err +} + func conflistDel(rt *libcni.RuntimeConf, rawnetconflist []byte, binDir string, exec invoke.Exec) error { logging.Debugf("conflistDel: %v, %s, %s", rt, string(rawnetconflist), binDir) // In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go @@ -305,6 +345,38 @@ func delegateAdd(exec invoke.Exec, ifName string, delegate *types.DelegateNetCon return result, nil } +func delegateCheck(exec invoke.Exec, ifName string, delegateConf *types.DelegateNetConf, rt *libcni.RuntimeConf, binDir string) error { + logging.Debugf("delegateCheck: %v, %s, %v, %v, %s", exec, ifName, delegateConf, rt, binDir) + if os.Setenv("CNI_IFNAME", ifName) != nil { + return logging.Errorf("delegateCheck: error setting envionment variable CNI_IFNAME") + } + + if logging.GetLoggingLevel() >= logging.VerboseLevel { + var confName string + if delegateConf.ConfListPlugin { + confName = delegateConf.ConfList.Name + } else { + confName = delegateConf.Conf.Name + } + logging.Verbosef("Check: %s:%s:%s:%s %s", rt.Args[1][1], rt.Args[2][1], confName, rt.IfName, string(delegateConf.Bytes)) + } + + var err error + if delegateConf.ConfListPlugin { + err = conflistCheck(rt, delegateConf.Bytes, binDir, exec) + if err != nil { + return logging.Errorf("delegateCheck: error invoking ConflistCheck - %q: %v", delegateConf.ConfList.Name, err) + } + } else { + err = confCheck(rt, delegateConf.Bytes, binDir, exec) + if err != nil { + return logging.Errorf("delegateCheck: error invoking DelegateCheck - %q: %v", delegateConf.Conf.Type, err) + } + } + + return err +} + func delegateDel(exec invoke.Exec, ifName string, delegateConf *types.DelegateNetConf, rt *libcni.RuntimeConf, binDir string) error { logging.Debugf("delegateDel: %v, %s, %v, %v, %s", exec, ifName, delegateConf, rt, binDir) if os.Setenv("CNI_IFNAME", ifName) != nil { @@ -491,16 +563,30 @@ func cmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) (c return result, nil } -func cmdGet(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) (cnitypes.Result, error) { - logging.Debugf("cmdGet: %v, %v, %v", args, exec, kubeClient) +func cmdCheck(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) error { + logging.Debugf("cmdCheck: %v, %v, %v", args, exec, kubeClient) in, err := types.LoadNetConf(args.StdinData) if err != nil { - return nil, err + return err } - // FIXME: call all delegates + k8sArgs, err := k8s.GetK8sArgs(args) + if err != nil { + return cmdErr(nil, "error getting k8s args: %v", err) + } - return in.PrevResult, nil + for idx, delegate := range in.Delegates { + ifName := getIfname(delegate, args.IfName, idx) + + runtimeConfig := types.MergeCNIRuntimeConfig(in.RuntimeConfig, delegate) + rt := types.CreateCNIRuntimeConf(args, k8sArgs, ifName, runtimeConfig) + err = delegateCheck(exec, ifName, delegate, rt, in.BinDir) + if err != nil { + return err + } + } + + return nil } func cmdDel(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) error { @@ -627,11 +713,7 @@ func main() { return result.Print() }, func(args *skel.CmdArgs) error { - result, err := cmdGet(args, nil, nil) - if err != nil { - return err - } - return result.Print() + return cmdCheck(args, nil, nil) }, func(args *skel.CmdArgs) error { return cmdDel(args, nil, nil) }, cniversion.All, "meta-plugin that delegates to other CNI plugins") diff --git a/multus/multus_test.go b/multus/multus_test.go index d9288327b..a1969efbf 100644 --- a/multus/multus_test.go +++ b/multus/multus_test.go @@ -25,6 +25,7 @@ import ( "reflect" "strings" "testing" + "bytes" "github.com/containernetworking/cni/pkg/skel" cnitypes "github.com/containernetworking/cni/pkg/types" @@ -62,6 +63,7 @@ type fakeExec struct { addIndex int delIndex int + chkIndex int plugins []*fakePlugin } @@ -116,11 +118,18 @@ func gatherCNIEnv() []string { func (f *fakeExec) ExecPlugin(ctx context.Context, pluginPath string, stdinData []byte, environ []string) ([]byte, error) { cmd := os.Getenv("CNI_COMMAND") var index int + var err error + var resultJSON []byte + switch cmd { case "ADD": Expect(len(f.plugins)).To(BeNumerically(">", f.addIndex)) index = f.addIndex f.addIndex++ + case "CHECK": + Expect(len(f.plugins)).To(BeNumerically("==", f.addIndex)) + index = f.chkIndex + f.chkIndex++ case "DEL": Expect(len(f.plugins)).To(BeNumerically(">", f.delIndex)) index = len(f.plugins) - f.delIndex - 1 @@ -133,8 +142,25 @@ func (f *fakeExec) ExecPlugin(ctx context.Context, pluginPath string, stdinData GinkgoT().Logf("[%s %d] exec plugin %q found %+v\n", cmd, index, pluginPath, plugin) + // strip prevResult from stdinData; tests don't need it + var m map[string]interface{} + reader := strings.NewReader(string(stdinData)) + writer := new(bytes.Buffer) + + dec := json.NewDecoder(reader) + enc := json.NewEncoder(writer) + err = dec.Decode(&m) + Expect(err).NotTo(HaveOccurred()) + for k := range m { + if k == "prevResult" { + delete(m, k) + } + } + err = enc.Encode(&m) + Expect(err).NotTo(HaveOccurred()) + if plugin.expectedConf != "" { - Expect(string(stdinData)).To(MatchJSON(plugin.expectedConf)) + Expect(writer).To(MatchJSON(plugin.expectedConf)) } if plugin.expectedIfname != "" { Expect(os.Getenv("CNI_IFNAME")).To(Equal(plugin.expectedIfname)) @@ -151,7 +177,7 @@ func (f *fakeExec) ExecPlugin(ctx context.Context, pluginPath string, stdinData return nil, plugin.err } - resultJSON, err := json.Marshal(plugin.result) + resultJSON, err = json.Marshal(plugin.result) Expect(err).NotTo(HaveOccurred()) return resultJSON, nil } @@ -169,7 +195,7 @@ func NewFakeClientInfo() *k8sclient.ClientInfo { } } -var _ = Describe("multus operations", func() { +var _ = Describe("multus operations cniVersion 0.2.0 config", func() { var testNS ns.NetNS var tmpDir string resultCNIVersion := "0.4.0" @@ -347,7 +373,7 @@ var _ = Describe("multus operations", func() { } }) - It("returns the previous result using cmdGet", func() { + It("returns the previous result using cmdCheck", func() { args := &skel.CmdArgs{ ContainerID: "123456789", Netns: testNS.Path(), @@ -411,8 +437,9 @@ var _ = Describe("multus operations", func() { // plugin 1 is the masterplugin Expect(reflect.DeepEqual(r, expectedResult1)).To(BeTrue()) - result, err = cmdGet(args, fExec, nil) - Expect(err).NotTo(HaveOccurred()) + // Check is not supported until v 0.4.0 + err = cmdCheck(args, fExec, nil) + Expect(err).To(HaveOccurred()) os.Setenv("CNI_COMMAND", "DEL") os.Setenv("CNI_IFNAME", "eth0") @@ -503,7 +530,7 @@ var _ = Describe("multus operations", func() { } }) - It("returns the previous result using cmdGet", func() { + It("returns the previous result using cmdCheck", func() { args := &skel.CmdArgs{ ContainerID: "123456789", Netns: testNS.Path(), @@ -567,8 +594,9 @@ var _ = Describe("multus operations", func() { // plugin 1 is the masterplugin Expect(reflect.DeepEqual(r, expectedResult1)).To(BeTrue()) - result, err = cmdGet(args, fExec, nil) - Expect(err).NotTo(HaveOccurred()) + // Check is not supported until v 0.4.0 + err = cmdCheck(args, fExec, nil) + Expect(err).To(HaveOccurred()) os.Setenv("CNI_COMMAND", "DEL") os.Setenv("CNI_IFNAME", "eth0") @@ -660,7 +688,7 @@ var _ = Describe("multus operations", func() { } }) - It("returns the previous result using cmdGet", func() { + It("returns the previous result using cmdCheck", func() { args := &skel.CmdArgs{ ContainerID: "123456789", Netns: testNS.Path(), @@ -724,8 +752,9 @@ var _ = Describe("multus operations", func() { // plugin 1 is the masterplugin Expect(reflect.DeepEqual(r, expectedResult1)).To(BeTrue()) - result, err = cmdGet(args, fExec, nil) - Expect(err).NotTo(HaveOccurred()) + // Check is not supported until v 0.4.0 + err = cmdCheck(args, fExec, nil) + Expect(err).To(HaveOccurred()) os.Setenv("CNI_COMMAND", "DEL") os.Setenv("CNI_IFNAME", "eth0") @@ -816,7 +845,7 @@ var _ = Describe("multus operations", func() { } }) - It("returns the previous result using cmdGet", func() { + It("returns the previous result using cmdCheck", func() { args := &skel.CmdArgs{ ContainerID: "123456789", Netns: testNS.Path(), @@ -880,8 +909,9 @@ var _ = Describe("multus operations", func() { // plugin 1 is the masterplugin Expect(reflect.DeepEqual(r, expectedResult1)).To(BeTrue()) - result, err = cmdGet(args, fExec, nil) - Expect(err).NotTo(HaveOccurred()) + // Check is not supported until v 0.4.0 + err = cmdCheck(args, fExec, nil) + Expect(err).To(HaveOccurred()) os.Setenv("CNI_COMMAND", "DEL") os.Setenv("CNI_IFNAME", "eth0") @@ -972,7 +1002,7 @@ var _ = Describe("multus operations", func() { } }) - It("returns the previous result using cmdGet", func() { + It("returns the previous result using cmdCheck", func() { args := &skel.CmdArgs{ ContainerID: "123456789", Netns: testNS.Path(), @@ -1034,8 +1064,9 @@ var _ = Describe("multus operations", func() { // plugin 1 is the masterplugin Expect(reflect.DeepEqual(r, expectedResult1)).To(BeTrue()) - result, err = cmdGet(args, fExec, nil) - Expect(err).NotTo(HaveOccurred()) + // Check is not supported until v 0.4.0 + err = cmdCheck(args, fExec, nil) + Expect(err).To(HaveOccurred()) os.Setenv("CNI_COMMAND", "DEL") os.Setenv("CNI_IFNAME", "eth0") @@ -2022,3 +2053,1195 @@ var _ = Describe("multus operations", func() { Expect(err).NotTo(HaveOccurred()) }) }) + +var _ = Describe("multus operations cniVersion 0.4.0 config", func() { + var testNS ns.NetNS + var tmpDir string + resultCNIVersion := "0.4.0" + + 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 with CNI Check", 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.4.0", + "type": "weave-net" + },{ + "name": "other1", + "cniVersion": "0.4.0", + "type": "other-plugin" + }] + }`), + } + + logging.SetLogLevel("verbose") + + // Touch the default network file. + configPath := "/tmp/foo.multus.conf" + os.OpenFile(configPath, os.O_RDONLY|os.O_CREATE, 0755) + + fExec := &fakeExec{} + expectedResult1 := ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.2/24"), + }, + }, + } + expectedConf1 := `{ + "name": "weave1", + "cniVersion": "0.4.0", + "type": "weave-net" + }` + fExec.addPlugin(nil, "eth0", expectedConf1, expectedResult1, nil) + + expectedResult2 := ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.5/24"), + }, + }, + } + expectedConf2 := `{ + "name": "other1", + "cniVersion": "0.4.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))) + // plugin 1 is the masterplugin + Expect(reflect.DeepEqual(result, expectedResult1)).To(BeTrue()) + + os.Setenv("CNI_COMMAND", "CHECK") + err = cmdCheck(args, fExec, nil) + Expect(err).NotTo(HaveOccurred()) + + 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 given faulty namespace", func() { + args := &skel.CmdArgs{ + ContainerID: "123456789", + Netns: "fsdadfad", + IfName: "eth0", + StdinData: []byte(`{ + "name": "node-cni-network", + "type": "multus", + "defaultnetworkfile": "/tmp/foo.multus.conf", + "defaultnetworkwaitseconds": 3, + "delegates": [{ + "name": "weave1", + "cniVersion": "0.4.0", + "type": "weave-net" + },{ + "name": "other1", + "cniVersion": "0.4.0", + "type": "other-plugin" + }] + }`), + } + // Netns is given garbage value + + // Touch the default network file. + configPath := "/tmp/foo.multus.conf" + os.OpenFile(configPath, os.O_RDONLY|os.O_CREATE, 0755) + + fExec := &fakeExec{} + expectedResult1 := ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.2/24"), + }, + }, + } + expectedConf1 := `{ + "name": "weave1", + "cniVersion": "0.4.0", + "type": "weave-net" + }` + fExec.addPlugin(nil, "eth0", expectedConf1, expectedResult1, nil) + + expectedResult2 := ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.5/24"), + }, + }, + } + expectedConf2 := `{ + "name": "other1", + "cniVersion": "0.4.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))) + // plugin 1 is the masterplugin + Expect(reflect.DeepEqual(result, 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("returns the previous result using cmdCheck", 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.4.0", + "plugins": [{ + "type": "weave-net", + "cniVersion": "0.4.0", + "name": "weave-net-name" + }] + },{ + "name": "other1", + "cniVersion": "0.4.0", + "plugins": [{ + "type": "other-plugin", + "cniVersion": "0.4.0", + "name": "other-name" + }] + }] + }`), + } + + logging.SetLogLevel("verbose") + + // Touch the default network file. + configPath := "/tmp/foo.multus.conf" + os.OpenFile(configPath, os.O_RDONLY|os.O_CREATE, 0755) + + fExec := &fakeExec{} + expectedResult1 := ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.2/24"), + }, + }, + } + expectedConf1 := `{ + "name": "weave1", + "cniVersion": "0.4.0", + "type": "weave-net" + }` + fExec.addPlugin(nil, "eth0", expectedConf1, expectedResult1, nil) + + expectedResult2 := ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.5/24"), + }, + }, + } + expectedConf2 := `{ + "name": "other1", + "cniVersion": "0.4.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))) + // plugin 1 is the masterplugin + Expect(reflect.DeepEqual(result, expectedResult1)).To(BeTrue()) + + os.Setenv("CNI_COMMAND", "CHECK") + err = cmdCheck(args, fExec, nil) + Expect(err).NotTo(HaveOccurred()) + + 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("fails to load NetConf with bad json in cmdAdd/Del", 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.4.0", + "type": "weave-net" + },{ + "name": "other1", + "cniVersion": "0.4.0", + "type": "other-plugin" + }] + `), + } + // Missing close bracket in StdinData + + // Touch the default network file. + configPath := "/tmp/foo.multus.conf" + os.OpenFile(configPath, os.O_RDONLY|os.O_CREATE, 0755) + + fExec := &fakeExec{} + expectedResult1 := ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.2/24"), + }, + }, + } + expectedConf1 := `{ + "name": "weave1", + "cniVersion": "0.4.0", + "type": "weave-net" + }` + fExec.addPlugin(nil, "eth0", expectedConf1, expectedResult1, nil) + + expectedResult2 := ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.5/24"), + }, + }, + } + expectedConf2 := `{ + "name": "other1", + "cniVersion": "0.4.0", + "type": "other-plugin" + }` + fExec.addPlugin(nil, "net1", expectedConf2, expectedResult2, nil) + + os.Setenv("CNI_COMMAND", "ADD") + os.Setenv("CNI_IFNAME", "eth0") + _, err := cmdAdd(args, fExec, nil) + Expect(err).To(HaveOccurred()) + + err = cmdDel(args, fExec, nil) + Expect(err).To(HaveOccurred()) + }) + + It("executes delegates and cleans up on failure", func() { + expectedConf1 := `{ + "name": "weave1", + "cniVersion": "0.4.0", + "type": "weave-net" + }` + expectedConf2 := `{ + "name": "other1", + "cniVersion": "0.4.0", + "type": "other-plugin" + }` + args := &skel.CmdArgs{ + ContainerID: "123456789", + Netns: testNS.Path(), + IfName: "eth0", + StdinData: []byte(fmt.Sprintf(`{ + "name": "node-cni-network", + "type": "multus", + "defaultnetworkfile": "/tmp/foo.multus.conf", + "defaultnetworkwaitseconds": 3, + "delegates": [%s,%s] + }`, expectedConf1, expectedConf2)), + } + + // Touch the default network file. + configPath := "/tmp/foo.multus.conf" + os.OpenFile(configPath, os.O_RDONLY|os.O_CREATE, 0755) + + fExec := &fakeExec{} + expectedResult1 := ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.2/24"), + }, + }, + } + fExec.addPlugin(nil, "eth0", expectedConf1, expectedResult1, nil) + + // This plugin invocation should fail + err := fmt.Errorf("expected plugin failure") + fExec.addPlugin(nil, "net1", expectedConf2, nil, err) + + os.Setenv("CNI_COMMAND", "ADD") + os.Setenv("CNI_IFNAME", "eth0") + _, err = cmdAdd(args, fExec, nil) + Expect(fExec.addIndex).To(Equal(2)) + Expect(fExec.delIndex).To(Equal(2)) + Expect(err).To(MatchError("Multus: [/]: error adding container to network \"other1\": delegateAdd: error invoking DelegateAdd - \"other-plugin\": error in getting result from AddNetwork: expected plugin failure")) + + // Cleanup default network file. + if _, errStat := os.Stat(configPath); errStat == nil { + errRemove := os.Remove(configPath) + Expect(errRemove).NotTo(HaveOccurred()) + } + + }) + + It("executes delegates and cleans up on failure with missing name field", func() { + expectedConf1 := `{ + "name": "weave1", + "cniVersion": "0.4.0", + "type": "weave-net" + }` + expectedConf2 := `{ + "name": "", + "cniVersion": "0.4.0", + "type": "other-plugin" + }` + // took out the name in expectedConf2, expecting a new value to be filled in by cmdAdd + + args := &skel.CmdArgs{ + ContainerID: "123456789", + Netns: testNS.Path(), + IfName: "eth0", + StdinData: []byte(fmt.Sprintf(`{ + "name": "node-cni-network", + "type": "multus", + "defaultnetworkfile": "/tmp/foo.multus.conf", + "defaultnetworkwaitseconds": 3, + "delegates": [%s,%s] + }`, expectedConf1, expectedConf2)), + } + + // Touch the default network file. + configPath := "/tmp/foo.multus.conf" + os.OpenFile(configPath, os.O_RDONLY|os.O_CREATE, 0755) + + fExec := &fakeExec{} + expectedResult1 := ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.2/24"), + }, + }, + } + fExec.addPlugin(nil, "eth0", expectedConf1, expectedResult1, nil) + + // This plugin invocation should fail + err := fmt.Errorf("expected plugin failure") + fExec.addPlugin(nil, "net1", expectedConf2, nil, err) + + os.Setenv("CNI_COMMAND", "ADD") + os.Setenv("CNI_IFNAME", "eth0") + _, err = cmdAdd(args, fExec, nil) + Expect(fExec.addIndex).To(Equal(2)) + Expect(fExec.delIndex).To(Equal(2)) + Expect(err).To(HaveOccurred()) + + // Cleanup default network file. + if _, errStat := os.Stat(configPath); errStat == nil { + errRemove := os.Remove(configPath) + Expect(errRemove).NotTo(HaveOccurred()) + } + + }) + + It("executes delegates with runtimeConfigs", func() { + podNet := `[{"name":"net1", + "mac": "c2:11:22:33:44:66", + "ips": [ "10.0.0.1" ], + "bandwidth": { + "ingressRate": 2048, + "ingressBurst": 1600, + "egressRate": 4096, + "egressBurst": 1600 + }, + "portMappings": [ + { + "hostPort": 8080, "containerPort": 80, "protocol": "tcp" + }, + { + "hostPort": 8000, "containerPort": 8001, "protocol": "udp" + }] + } + ]` + fakePod := testhelpers.NewFakePod("testpod", podNet, "") + net1 := `{ + "name": "net1", + "type": "mynet", + "capabilities": {"mac": true, "ips": true, "bandwidth": true, "portMappings": true}, + "cniVersion": "0.4.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.4.0", + "type": "weave-net" + }] + }`), + } + + fExec := &fakeExec{} + expectedResult1 := ¤t.Result{ + CNIVersion: resultCNIVersion, + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.2/24"), + }, + }, + } + expectedConf1 := `{ + "name": "weave1", + "cniVersion": "0.4.0", + "type": "weave-net" + }` + expectedNet1 := `{ + "name": "net1", + "type": "mynet", + "capabilities": { + "mac": true, + "ips": true, + "bandwidth": true, + "portMappings": true + }, + "runtimeConfig": { + "ips": [ "10.0.0.1" ], + "mac": "c2:11:22:33:44:66", + "bandwidth": { + "ingressRate": 2048, + "ingressBurst": 1600, + "egressRate": 4096, + "egressBurst": 1600 + }, + "portMappings": [ + { + "hostPort": 8080, + "containerPort": 80, + "protocol": "tcp" + }, + { + "hostPort": 8000, + "containerPort": 8001, + "protocol": "udp" + }] + }, + "cniVersion": "0.4.0" + }` + fExec.addPlugin(nil, "eth0", expectedConf1, expectedResult1, nil) + fExec.addPlugin(nil, "net1", expectedNet1, ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.3/24"), + }, + }, + }, nil) + + clientInfo := NewFakeClientInfo() + _, err := clientInfo.Client.Core().Pods(fakePod.ObjectMeta.Namespace).Create(fakePod) + Expect(err).NotTo(HaveOccurred()) + + _, err = clientInfo.AddNetAttachDef( + testhelpers.NewFakeNetAttachDef(fakePod.ObjectMeta.Namespace, "net1", net1)) + Expect(err).NotTo(HaveOccurred()) + + os.Setenv("CNI_COMMAND", "ADD") + os.Setenv("CNI_IFNAME", "eth0") + result, err := cmdAdd(args, fExec, clientInfo) + Expect(err).NotTo(HaveOccurred()) + Expect(fExec.addIndex).To(Equal(len(fExec.plugins))) + r := result.(*current.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.4.0" + }` + net2 := `{ + "name": "net2", + "type": "mynet2", + "cniVersion": "0.4.0" + }` + net3 := `{ + "name": "net3", + "type": "mynet3", + "cniVersion": "0.4.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.4.0", + "type": "weave-net" + }] + }`), + } + + fExec := &fakeExec{} + expectedResult1 := ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.2/24"), + }, + }, + } + expectedConf1 := `{ + "name": "weave1", + "cniVersion": "0.4.0", + "type": "weave-net" + }` + fExec.addPlugin(nil, "eth0", expectedConf1, expectedResult1, nil) + fExec.addPlugin(nil, "net1", net1, ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.3/24"), + }, + }, + }, nil) + fExec.addPlugin(nil, "net2", net2, ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.4/24"), + }, + }, + }, nil) + + clientInfo := NewFakeClientInfo() + _, err := clientInfo.Client.Core().Pods(fakePod.ObjectMeta.Namespace).Create(fakePod) + Expect(err).NotTo(HaveOccurred()) + + _, err = clientInfo.AddNetAttachDef( + testhelpers.NewFakeNetAttachDef(fakePod.ObjectMeta.Namespace, "net1", net1)) + Expect(err).NotTo(HaveOccurred()) + _, err = clientInfo.AddNetAttachDef( + testhelpers.NewFakeNetAttachDef(fakePod.ObjectMeta.Namespace, "net2", net2)) + Expect(err).NotTo(HaveOccurred()) + // net3 is not used; make sure it's not accessed + _, err = clientInfo.AddNetAttachDef( + testhelpers.NewFakeNetAttachDef(fakePod.ObjectMeta.Namespace, "net3", net3)) + Expect(err).NotTo(HaveOccurred()) + + os.Setenv("CNI_COMMAND", "ADD") + os.Setenv("CNI_IFNAME", "eth0") + result, err := cmdAdd(args, fExec, clientInfo) + Expect(err).NotTo(HaveOccurred()) + Expect(fExec.addIndex).To(Equal(len(fExec.plugins))) + // plugin 1 is the masterplugin + Expect(reflect.DeepEqual(result, expectedResult1)).To(BeTrue()) + }) + + It("executes kubernetes networks and delete it after pod removal", func() { + fakePod := testhelpers.NewFakePod("testpod", "net1", "") + net1 := `{ + "name": "net1", + "type": "mynet", + "cniVersion": "0.4.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.4.0", + "type": "weave-net" + }] + }`), + } + + fExec := &fakeExec{} + expectedResult1 := ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.2/24"), + }, + }, + } + expectedConf1 := `{ + "name": "weave1", + "cniVersion": "0.4.0", + "type": "weave-net" + }` + fExec.addPlugin(nil, "eth0", expectedConf1, expectedResult1, nil) + fExec.addPlugin(nil, "net1", net1, ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.3/24"), + }, + }, + }, nil) + + clientInfo := NewFakeClientInfo() + _, err := clientInfo.Client.Core().Pods(fakePod.ObjectMeta.Namespace).Create(fakePod) + Expect(err).NotTo(HaveOccurred()) + + _, err = clientInfo.AddNetAttachDef( + testhelpers.NewFakeNetAttachDef(fakePod.ObjectMeta.Namespace, "net1", net1)) + Expect(err).NotTo(HaveOccurred()) + + os.Setenv("CNI_COMMAND", "ADD") + os.Setenv("CNI_IFNAME", "eth0") + result, err := cmdAdd(args, fExec, clientInfo) + Expect(err).NotTo(HaveOccurred()) + Expect(fExec.addIndex).To(Equal(len(fExec.plugins))) + // plugin 1 is the masterplugin + Expect(reflect.DeepEqual(result, expectedResult1)).To(BeTrue()) + + os.Setenv("CNI_COMMAND", "DEL") + os.Setenv("CNI_IFNAME", "eth0") + // set fKubeClient to nil to emulate no pod info + clientInfo.DeletePod(fakePod.ObjectMeta.Namespace, fakePod.ObjectMeta.Name) + err = cmdDel(args, fExec, clientInfo) + Expect(err).NotTo(HaveOccurred()) + Expect(fExec.delIndex).To(Equal(len(fExec.plugins))) + }) + + It("executes kubernetes networks and delete it after pod removal", func() { + fakePod := testhelpers.NewFakePod("testpod", "net1", "") + net1 := `{ + "name": "net1", + "type": "mynet", + "cniVersion": "0.4.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.4.0", + "type": "weave-net" + }] + }`), + } + + fExec := &fakeExec{} + expectedResult1 := ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.2/24"), + }, + }, + } + expectedConf1 := `{ + "name": "weave1", + "cniVersion": "0.4.0", + "type": "weave-net" + }` + fExec.addPlugin(nil, "eth0", expectedConf1, expectedResult1, nil) + fExec.addPlugin(nil, "net1", net1, ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.3/24"), + }, + }, + }, nil) + + fKubeClient := NewFakeClientInfo() + fKubeClient.AddPod(fakePod) + _, err := fKubeClient.AddNetAttachDef( + testhelpers.NewFakeNetAttachDef(fakePod.ObjectMeta.Namespace, "net1", net1)) + Expect(err).NotTo(HaveOccurred()) + + 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))) + // plugin 1 is the masterplugin + Expect(reflect.DeepEqual(result, expectedResult1)).To(BeTrue()) + + os.Setenv("CNI_COMMAND", "DEL") + os.Setenv("CNI_IFNAME", "eth0") + // set fKubeClient to nil to emulate no pod info + fKubeClient.DeletePod(fakePod.ObjectMeta.Namespace, fakePod.ObjectMeta.Name) + err = cmdDel(args, fExec, fKubeClient) + Expect(err).NotTo(HaveOccurred()) + Expect(fExec.delIndex).To(Equal(len(fExec.plugins))) + }) + + It("executes kubernetes networks and delete it after pod removal", func() { + fakePod := testhelpers.NewFakePod("testpod", "net1", "") + net1 := `{ + "name": "net1", + "type": "mynet", + "cniVersion": "0.4.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.4.0", + "type": "weave-net" + }] + }`), + } + + fExec := &fakeExec{} + expectedResult1 := ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.2/24"), + }, + }, + } + expectedConf1 := `{ + "name": "weave1", + "cniVersion": "0.4.0", + "type": "weave-net" + }` + fExec.addPlugin(nil, "eth0", expectedConf1, expectedResult1, nil) + fExec.addPlugin(nil, "net1", net1, ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.3/24"), + }, + }, + }, nil) + + fKubeClient := NewFakeClientInfo() + fKubeClient.AddPod(fakePod) + _, err := fKubeClient.AddNetAttachDef( + testhelpers.NewFakeNetAttachDef(fakePod.ObjectMeta.Namespace, "net1", net1)) + Expect(err).NotTo(HaveOccurred()) + + 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))) + // plugin 1 is the masterplugin + Expect(reflect.DeepEqual(result, expectedResult1)).To(BeTrue()) + + os.Setenv("CNI_COMMAND", "DEL") + os.Setenv("CNI_IFNAME", "eth0") + // set fKubeClient to nil to emulate no pod info + fKubeClient.DeletePod(fakePod.ObjectMeta.Namespace, fakePod.ObjectMeta.Name) + err = cmdDel(args, fExec, fKubeClient) + Expect(err).NotTo(HaveOccurred()) + Expect(fExec.delIndex).To(Equal(len(fExec.plugins))) + }) + + 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.4.0", + "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.4.0", + "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()) + }) + + It("executes clusterNetwork delegate", func() { + fakePod := testhelpers.NewFakePod("testpod", "", "kube-system/net1") + net1 := `{ + "name": "net1", + "type": "mynet", + "cniVersion": "0.4.0" + }` + expectedResult1 := ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.2/24"), + }, + }, + } + 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", + "defaultNetworks": [], + "clusterNetwork": "net1", + "delegates": [] + }`), + } + + fExec := &fakeExec{} + fExec.addPlugin(nil, "eth0", net1, expectedResult1, nil) + + fKubeClient := NewFakeClientInfo() + fKubeClient.AddPod(fakePod) + _, err := fKubeClient.AddNetAttachDef(testhelpers.NewFakeNetAttachDef("kube-system", "net1", net1)) + Expect(err).NotTo(HaveOccurred()) + + 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(reflect.DeepEqual(result, expectedResult1)).To(BeTrue()) + + os.Setenv("CNI_COMMAND", "DEL") + os.Setenv("CNI_IFNAME", "eth0") + err = cmdDel(args, fExec, fKubeClient) + Expect(err).NotTo(HaveOccurred()) + Expect(fExec.delIndex).To(Equal(len(fExec.plugins))) + }) + + It("Verify the cache is created in dataDir", func() { + tmpCNIDir := tmpDir + "/cniData" + err := os.Mkdir(tmpCNIDir, 0777) + Expect(err).NotTo(HaveOccurred()) + + fakePod := testhelpers.NewFakePod("testpod", "net1", "") + net1 := `{ + "name": "net1", + "type": "mynet", + "cniVersion": "0.4.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(fmt.Sprintf(`{ + "name": "node-cni-network", + "type": "multus", + "kubeconfig": "/etc/kubernetes/node-kubeconfig.yaml", + "cniDir": "%s", + "delegates": [{ + "name": "weave1", + "cniVersion": "0.4.0", + "type": "weave-net" + }] + }`, tmpCNIDir)), + } + + fExec := &fakeExec{} + expectedResult1 := ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.2/24"), + }, + }, + } + expectedConf1 := `{ + "name": "weave1", + "cniVersion": "0.4.0", + "type": "weave-net" + }` + fExec.addPlugin(nil, "eth0", expectedConf1, expectedResult1, nil) + fExec.addPlugin(nil, "net1", net1, ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.3/24"), + }, + }, + }, nil) + + fKubeClient := NewFakeClientInfo() + fKubeClient.AddPod(fakePod) + _, err = fKubeClient.AddNetAttachDef( + testhelpers.NewFakeNetAttachDef(fakePod.ObjectMeta.Namespace, "net1", net1)) + Expect(err).NotTo(HaveOccurred()) + 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))) + // plugin 1 is the masterplugin + Expect(reflect.DeepEqual(result, expectedResult1)).To(BeTrue()) + + By("Verify cache file existence") + cacheFilePath := fmt.Sprintf("%s/%s", tmpCNIDir, "123456789") + _, err = os.Stat(cacheFilePath) + Expect(err).NotTo(HaveOccurred()) + + By("Delete and check net count is not incremented") + os.Setenv("CNI_COMMAND", "DEL") + os.Setenv("CNI_IFNAME", "eth0") + err = cmdDel(args, fExec, fKubeClient) + Expect(err).NotTo(HaveOccurred()) + Expect(fExec.delIndex).To(Equal(len(fExec.plugins))) + }) + + It("Delete pod without cache", func() { + tmpCNIDir := tmpDir + "/cniData" + err := os.Mkdir(tmpCNIDir, 0777) + Expect(err).NotTo(HaveOccurred()) + + fakePod := testhelpers.NewFakePod("testpod", "net1", "") + net1 := `{ + "name": "net1", + "type": "mynet", + "cniVersion": "0.4.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(fmt.Sprintf(`{ + "name": "node-cni-network", + "type": "multus", + "kubeconfig": "/etc/kubernetes/node-kubeconfig.yaml", + "cniDir": "%s", + "delegates": [{ + "name": "weave1", + "cniVersion": "0.4.0", + "type": "weave-net" + }] + }`, tmpCNIDir)), + } + + fExec := &fakeExec{} + expectedResult1 := ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.2/24"), + }, + }, + } + expectedConf1 := `{ + "name": "weave1", + "cniVersion": "0.4.0", + "type": "weave-net" + }` + fExec.addPlugin(nil, "eth0", expectedConf1, expectedResult1, nil) + fExec.addPlugin(nil, "net1", net1, ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.3/24"), + }, + }, + }, nil) + + fKubeClient := NewFakeClientInfo() + fKubeClient.AddPod(fakePod) + _, err = fKubeClient.AddNetAttachDef( + testhelpers.NewFakeNetAttachDef(fakePod.ObjectMeta.Namespace, "net1", net1)) + Expect(err).NotTo(HaveOccurred()) + 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))) + // plugin 1 is the masterplugin + Expect(reflect.DeepEqual(result, expectedResult1)).To(BeTrue()) + + By("Verify cache file existence") + cacheFilePath := fmt.Sprintf("%s/%s", tmpCNIDir, "123456789") + _, err = os.Stat(cacheFilePath) + Expect(err).NotTo(HaveOccurred()) + + err = os.Remove(cacheFilePath) + Expect(err).NotTo(HaveOccurred()) + + By("Delete and check pod/net count is incremented") + os.Setenv("CNI_COMMAND", "DEL") + os.Setenv("CNI_IFNAME", "eth0") + err = cmdDel(args, fExec, fKubeClient) + Expect(err).NotTo(HaveOccurred()) + Expect(fExec.delIndex).To(Equal(len(fExec.plugins))) + }) + + It("fails to execute confListDel given no 'plugins' key", 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.4.0", + "type": "weave-net" + },{ + "name": "other1", + "cniVersion": "0.4.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 := ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.2/24"), + }, + }, + } + expectedConf1 := `{ + "name": "weave1", + "cniVersion": "0.4.0", + "type": "weave-net" + }` + fExec.addPlugin(nil, "eth0", expectedConf1, expectedResult1, nil) + + expectedResult2 := ¤t.Result{ + CNIVersion: "0.4.0", + IPs: []*current.IPConfig{{ + Address: *testhelpers.EnsureCIDR("1.1.1.5/24"), + }, + }, + } + expectedConf2 := `{ + "name": "other1", + "cniVersion": "0.4.0", + "type": "other-plugin" + }` + fExec.addPlugin(nil, "net1", expectedConf2, expectedResult2, nil) + + os.Setenv("CNI_COMMAND", "ADD") + os.Setenv("CNI_IFNAME", "eth0") + + binDir := "/opt/cni/bin" + // use fExec for the exec param + rawnetconflist := []byte(`{"cniVersion":"0.4.0","name":"weave1","type":"weave-net"}`) + k8sargs, err := k8sclient.GetK8sArgs(args) + n, err := types.LoadNetConf(args.StdinData) + rt := types.CreateCNIRuntimeConf(args, k8sargs, args.IfName, n.RuntimeConfig) + + err = conflistDel(rt, rawnetconflist, binDir, fExec) + Expect(err).To(HaveOccurred()) + }) +})