From b463642ac0923aac53c93fe8f0c8f3e3956fcb3a Mon Sep 17 00:00:00 2001 From: "Mark St.Godard" Date: Thu, 3 Nov 2016 17:10:13 -0500 Subject: [PATCH 1/6] Allow flannel CNI plugin stateDir to be configurable - Add optional 'stateDir' to flannel NetConf, if not present default to /var/lib/cni/flannel Signed-off-by: Jay Dunkelberger --- plugins/meta/flannel/flannel.go | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/plugins/meta/flannel/flannel.go b/plugins/meta/flannel/flannel.go index 334bd41b..768fbf28 100644 --- a/plugins/meta/flannel/flannel.go +++ b/plugins/meta/flannel/flannel.go @@ -37,12 +37,13 @@ import ( const ( defaultSubnetFile = "/run/flannel/subnet.env" - stateDir = "/var/lib/cni/flannel" + defaultStateDir = "/var/lib/cni/flannel" ) type NetConf struct { types.NetConf SubnetFile string `json:"subnetFile"` + StateDir string `json:"stateDir"` Delegate map[string]interface{} `json:"delegate"` } @@ -74,6 +75,7 @@ func (se *subnetEnv) missing() string { func loadFlannelNetConf(bytes []byte) (*NetConf, error) { n := &NetConf{ SubnetFile: defaultSubnetFile, + StateDir: defaultStateDir, } if err := json.Unmarshal(bytes, n); err != nil { return nil, fmt.Errorf("failed to load netconf: %v", err) @@ -130,7 +132,7 @@ func loadFlannelSubnetEnv(fn string) (*subnetEnv, error) { return se, nil } -func saveScratchNetConf(containerID string, netconf []byte) error { +func saveScratchNetConf(containerID, stateDir string, netconf []byte) error { if err := os.MkdirAll(stateDir, 0700); err != nil { return err } @@ -138,21 +140,21 @@ func saveScratchNetConf(containerID string, netconf []byte) error { return ioutil.WriteFile(path, netconf, 0600) } -func consumeScratchNetConf(containerID string) ([]byte, error) { +func consumeScratchNetConf(containerID, stateDir string) ([]byte, error) { path := filepath.Join(stateDir, containerID) defer os.Remove(path) return ioutil.ReadFile(path) } -func delegateAdd(cid string, netconf map[string]interface{}) error { +func delegateAdd(cid, stateDir string, netconf map[string]interface{}) error { netconfBytes, err := json.Marshal(netconf) if err != nil { return fmt.Errorf("error serializing delegate netconf: %v", err) } // save the rendered netconf for cmdDel - if err = saveScratchNetConf(cid, netconfBytes); err != nil { + if err = saveScratchNetConf(cid, stateDir, netconfBytes); err != nil { return err } @@ -232,11 +234,16 @@ func cmdAdd(args *skel.CmdArgs) error { }, } - return delegateAdd(args.ContainerID, n.Delegate) + return delegateAdd(args.ContainerID, n.StateDir, n.Delegate) } func cmdDel(args *skel.CmdArgs) error { - netconfBytes, err := consumeScratchNetConf(args.ContainerID) + nc, err := loadFlannelNetConf(args.StdinData) + if err != nil { + return err + } + + netconfBytes, err := consumeScratchNetConf(args.ContainerID, nc.StateDir) if err != nil { return err } From fa264e6e3622ef55a27f703164560134fe2efc3b Mon Sep 17 00:00:00 2001 From: "Mark St.Godard" Date: Sat, 5 Nov 2016 10:47:07 -0500 Subject: [PATCH 2/6] flannel: add integration test suite add new test for flannel plugin that delegates to the noop plugin and validates that 'storeDir' can be configurable --- plugins/meta/flannel/flannel_suite_test.go | 66 ++++++++ plugins/meta/flannel/flannel_test.go | 173 +++++++++++++++++++++ test | 2 +- 3 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 plugins/meta/flannel/flannel_suite_test.go create mode 100644 plugins/meta/flannel/flannel_test.go diff --git a/plugins/meta/flannel/flannel_suite_test.go b/plugins/meta/flannel/flannel_suite_test.go new file mode 100644 index 00000000..ec8c6119 --- /dev/null +++ b/plugins/meta/flannel/flannel_suite_test.go @@ -0,0 +1,66 @@ +// Copyright 2015 CNI authors +// +// 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_test + +import ( + "encoding/json" + "fmt" + "path/filepath" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/gexec" + + "testing" +) + +func TestFlannel(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Flannel Suite") +} + +const flannelPackage = "github.com/containernetworking/cni/plugins/meta/flannel" +const noopPackage = "github.com/containernetworking/cni/plugins/test/noop" + +var paths testPaths + +type testPaths struct { + PathToPlugin string + CNIPath string +} + +var _ = SynchronizedBeforeSuite(func() []byte { + noopBin, err := gexec.Build(noopPackage) + Expect(err).NotTo(HaveOccurred()) + noopDir, _ := filepath.Split(noopBin) + + pathToPlugin, err := gexec.Build(flannelPackage) + Expect(err).NotTo(HaveOccurred()) + flannelDir, _ := filepath.Split(pathToPlugin) + + paths := testPaths{ + PathToPlugin: pathToPlugin, + CNIPath: fmt.Sprintf("%s:%s", flannelDir, noopDir), + } + + data, err := json.Marshal(paths) + Expect(err).NotTo(HaveOccurred()) + return data +}, func(data []byte) { + Expect(json.Unmarshal(data, &paths)).To(Succeed()) +}) + +var _ = SynchronizedAfterSuite(func() {}, func() { + gexec.CleanupBuildArtifacts() +}) diff --git a/plugins/meta/flannel/flannel_test.go b/plugins/meta/flannel/flannel_test.go new file mode 100644 index 00000000..d7b47eb5 --- /dev/null +++ b/plugins/meta/flannel/flannel_test.go @@ -0,0 +1,173 @@ +// Copyright 2015 CNI authors +// +// 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_test + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "strings" + + "github.com/containernetworking/cni/pkg/skel" + + noop_debug "github.com/containernetworking/cni/plugins/test/noop/debug" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/gexec" +) + +var _ = Describe("Flannel", func() { + var ( + cmd *exec.Cmd + debugFileName string + input string + debug *noop_debug.Debug + expectedCmdArgs skel.CmdArgs + ) + + const delegateInput = ` +{ + "type": "noop", + "some": "other data" +} +` + + const inputTemplate = ` +{ + "name": "cni-flannel", + "type": "flannel", + "subnetFile": "%s", + "stateDir": "%s", + "delegate": ` + + delegateInput + + `}` + + const flannelSubnetEnv = ` +FLANNEL_NETWORK=10.1.0.0/16 +FLANNEL_SUBNET=10.1.17.1/24 +FLANNEL_MTU=1472 +FLANNEL_IPMASQ=true +` + + var cniCommand = func(command, input string) *exec.Cmd { + toReturn := exec.Command(paths.PathToPlugin) + toReturn.Env = []string{ + "CNI_COMMAND=" + command, + "CNI_CONTAINERID=some-container-id", + "CNI_NETNS=/some/netns/path", + "CNI_IFNAME=some-eth0", + "CNI_PATH=" + paths.CNIPath, + "CNI_ARGS=DEBUG=" + debugFileName, + } + toReturn.Stdin = strings.NewReader(input) + return toReturn + } + + BeforeEach(func() { + debugFile, err := ioutil.TempFile("", "cni_debug") + Expect(err).NotTo(HaveOccurred()) + Expect(debugFile.Close()).To(Succeed()) + debugFileName = debugFile.Name() + + debug = &noop_debug.Debug{ + ReportResult: `{ "ip4": { "ip": "1.2.3.4/32" } }`, + ReportVersionSupport: []string{"0.1.0", "0.2.0", "0.3.0"}, + } + Expect(debug.WriteDebug(debugFileName)).To(Succeed()) + }) + + AfterEach(func() { + os.Remove(debugFileName) + }) + + Describe("CNI lifecycle", func() { + Context("when subnetFile and stateDir are specified", func() { + var ( + subnetFile string + stateDir string + ) + + BeforeEach(func() { + var err error + file, err := ioutil.TempFile("", "subnet.env") + Expect(err).NotTo(HaveOccurred()) + _, err = file.WriteString(flannelSubnetEnv) + Expect(err).NotTo(HaveOccurred()) + subnetFile = file.Name() + + stateDir, err = ioutil.TempDir("", "stateDir") + Expect(err).NotTo(HaveOccurred()) + input = fmt.Sprintf(inputTemplate, subnetFile, stateDir) + + expectedCmdArgs = skel.CmdArgs{ + ContainerID: "some-container-id", + Netns: "/some/netns/path", + IfName: "some-eth0", + Args: "DEBUG=" + debugFileName, + Path: "/some/bin/path", + StdinData: []byte(input), + } + cmd = cniCommand("ADD", input) + }) + + AfterEach(func() { + os.Remove(subnetFile) + os.Remove(stateDir) + }) + + It("uses stateDir for storing network configuration", func() { + By("calling ADD") + session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter) + Expect(err).NotTo(HaveOccurred()) + Eventually(session).Should(gexec.Exit(0)) + Expect(session.Out.Contents()).To(MatchJSON(`{ "ip4": { "ip": "1.2.3.4/32" }, "dns":{} }`)) + + By("check that plugin writes to net config to stateDir") + path := fmt.Sprintf("%s/%s", stateDir, "some-container-id") + Expect(path).Should(BeAnExistingFile()) + + netConfBytes, err := ioutil.ReadFile(path) + Expect(err).NotTo(HaveOccurred()) + expected := `{ + "name" : "cni-flannel", + "type" : "noop", + "ipam" : { + "type" : "host-local", + "subnet" : "10.1.17.0/24", + "routes" : [ + { + "dst" : "10.1.0.0/16" + } + ] + }, + "mtu" : 1472, + "ipMasq" : false, + "some" : "other data" +} +` + Expect(netConfBytes).Should(MatchJSON(expected)) + + By("calling DEL") + cmd = cniCommand("DEL", input) + session, err = gexec.Start(cmd, GinkgoWriter, GinkgoWriter) + Expect(err).NotTo(HaveOccurred()) + Eventually(session).Should(gexec.Exit(0)) + + By("check that plugin removes net config from state dir") + Expect(path).ShouldNot(BeAnExistingFile()) + }) + }) + }) +}) diff --git a/test b/test index 8c2b3ce5..d32b134b 100755 --- a/test +++ b/test @@ -11,7 +11,7 @@ set -e source ./build -TESTABLE="libcni plugins/ipam/dhcp plugins/ipam/host-local plugins/main/loopback pkg/invoke pkg/ns pkg/skel pkg/types pkg/utils plugins/main/ipvlan plugins/main/macvlan plugins/main/bridge plugins/main/ptp plugins/test/noop pkg/utils/hwaddr pkg/ip pkg/version pkg/version/testhelpers" +TESTABLE="libcni plugins/ipam/dhcp plugins/ipam/host-local plugins/main/loopback pkg/invoke pkg/ns pkg/skel pkg/types pkg/utils plugins/main/ipvlan plugins/main/macvlan plugins/main/bridge plugins/main/ptp plugins/test/noop pkg/utils/hwaddr pkg/ip pkg/version pkg/version/testhelpers plugins/meta/flannel" FORMATTABLE="$TESTABLE pkg/testutils plugins/meta/flannel plugins/meta/tuning" # user has not provided PKG override From f0daefa63d3ee4ca0f63fe4d94ffff9b4d69a8a5 Mon Sep 17 00:00:00 2001 From: "Mark St.Godard" Date: Sat, 5 Nov 2016 23:27:14 -0500 Subject: [PATCH 3/6] flannel: add unit tests for config loading backfill unit tests to add coverage for loadFlannelNetConf and loadFlannelSubnetEnv --- plugins/meta/flannel/flannel_suite_test.go | 2 +- plugins/meta/flannel/flannel_test.go | 161 ++++++++++++++------- 2 files changed, 109 insertions(+), 54 deletions(-) diff --git a/plugins/meta/flannel/flannel_suite_test.go b/plugins/meta/flannel/flannel_suite_test.go index ec8c6119..fc96d4a5 100644 --- a/plugins/meta/flannel/flannel_suite_test.go +++ b/plugins/meta/flannel/flannel_suite_test.go @@ -11,7 +11,7 @@ // 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_test +package main import ( "encoding/json" diff --git a/plugins/meta/flannel/flannel_test.go b/plugins/meta/flannel/flannel_test.go index d7b47eb5..177c3c71 100644 --- a/plugins/meta/flannel/flannel_test.go +++ b/plugins/meta/flannel/flannel_test.go @@ -11,7 +11,7 @@ // 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_test +package main import ( "fmt" @@ -35,6 +35,8 @@ var _ = Describe("Flannel", func() { input string debug *noop_debug.Debug expectedCmdArgs skel.CmdArgs + subnetFile string + stateDir string ) const delegateInput = ` @@ -61,6 +63,14 @@ FLANNEL_MTU=1472 FLANNEL_IPMASQ=true ` + var writeSubnetEnv = func(contents string) string { + file, err := ioutil.TempFile("", "subnet.env") + Expect(err).NotTo(HaveOccurred()) + _, err = file.WriteString(contents) + Expect(err).NotTo(HaveOccurred()) + return file.Name() + } + var cniCommand = func(command, input string) *exec.Cmd { toReturn := exec.Command(paths.PathToPlugin) toReturn.Env = []string{ @@ -86,61 +96,50 @@ FLANNEL_IPMASQ=true ReportVersionSupport: []string{"0.1.0", "0.2.0", "0.3.0"}, } Expect(debug.WriteDebug(debugFileName)).To(Succeed()) + + // flannel subnet.env + subnetFile = writeSubnetEnv(flannelSubnetEnv) + + // flannel state dir + stateDir, err = ioutil.TempDir("", "stateDir") + Expect(err).NotTo(HaveOccurred()) + input = fmt.Sprintf(inputTemplate, subnetFile, stateDir) }) AfterEach(func() { os.Remove(debugFileName) + os.Remove(subnetFile) + os.Remove(stateDir) }) Describe("CNI lifecycle", func() { - Context("when subnetFile and stateDir are specified", func() { - var ( - subnetFile string - stateDir string - ) - BeforeEach(func() { - var err error - file, err := ioutil.TempFile("", "subnet.env") - Expect(err).NotTo(HaveOccurred()) - _, err = file.WriteString(flannelSubnetEnv) - Expect(err).NotTo(HaveOccurred()) - subnetFile = file.Name() + BeforeEach(func() { + expectedCmdArgs = skel.CmdArgs{ + ContainerID: "some-container-id", + Netns: "/some/netns/path", + IfName: "some-eth0", + Args: "DEBUG=" + debugFileName, + Path: "/some/bin/path", + StdinData: []byte(input), + } + cmd = cniCommand("ADD", input) + }) - stateDir, err = ioutil.TempDir("", "stateDir") - Expect(err).NotTo(HaveOccurred()) - input = fmt.Sprintf(inputTemplate, subnetFile, stateDir) + It("uses stateDir for storing network configuration", func() { + By("calling ADD") + session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter) + Expect(err).NotTo(HaveOccurred()) + Eventually(session).Should(gexec.Exit(0)) + Expect(session.Out.Contents()).To(MatchJSON(`{ "ip4": { "ip": "1.2.3.4/32" }, "dns":{} }`)) - expectedCmdArgs = skel.CmdArgs{ - ContainerID: "some-container-id", - Netns: "/some/netns/path", - IfName: "some-eth0", - Args: "DEBUG=" + debugFileName, - Path: "/some/bin/path", - StdinData: []byte(input), - } - cmd = cniCommand("ADD", input) - }) + By("check that plugin writes to net config to stateDir") + path := fmt.Sprintf("%s/%s", stateDir, "some-container-id") + Expect(path).Should(BeAnExistingFile()) - AfterEach(func() { - os.Remove(subnetFile) - os.Remove(stateDir) - }) - - It("uses stateDir for storing network configuration", func() { - By("calling ADD") - session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter) - Expect(err).NotTo(HaveOccurred()) - Eventually(session).Should(gexec.Exit(0)) - Expect(session.Out.Contents()).To(MatchJSON(`{ "ip4": { "ip": "1.2.3.4/32" }, "dns":{} }`)) - - By("check that plugin writes to net config to stateDir") - path := fmt.Sprintf("%s/%s", stateDir, "some-container-id") - Expect(path).Should(BeAnExistingFile()) - - netConfBytes, err := ioutil.ReadFile(path) - Expect(err).NotTo(HaveOccurred()) - expected := `{ + netConfBytes, err := ioutil.ReadFile(path) + Expect(err).NotTo(HaveOccurred()) + expected := `{ "name" : "cni-flannel", "type" : "noop", "ipam" : { @@ -157,16 +156,72 @@ FLANNEL_IPMASQ=true "some" : "other data" } ` - Expect(netConfBytes).Should(MatchJSON(expected)) + Expect(netConfBytes).Should(MatchJSON(expected)) - By("calling DEL") - cmd = cniCommand("DEL", input) - session, err = gexec.Start(cmd, GinkgoWriter, GinkgoWriter) - Expect(err).NotTo(HaveOccurred()) - Eventually(session).Should(gexec.Exit(0)) + By("calling DEL") + cmd = cniCommand("DEL", input) + session, err = gexec.Start(cmd, GinkgoWriter, GinkgoWriter) + Expect(err).NotTo(HaveOccurred()) + Eventually(session).Should(gexec.Exit(0)) - By("check that plugin removes net config from state dir") - Expect(path).ShouldNot(BeAnExistingFile()) + By("check that plugin removes net config from state dir") + Expect(path).ShouldNot(BeAnExistingFile()) + }) + }) + + Describe("loadFlannelNetConf", func() { + Context("when subnetFile and stateDir are specified", func() { + It("loads flannel network config", func() { + conf, err := loadFlannelNetConf([]byte(input)) + Expect(err).ShouldNot(HaveOccurred()) + Expect(conf.Name).To(Equal("cni-flannel")) + Expect(conf.Type).To(Equal("flannel")) + Expect(conf.SubnetFile).To(Equal(subnetFile)) + Expect(conf.StateDir).To(Equal(stateDir)) + }) + }) + + Context("when defaulting subnetFile and stateDir", func() { + BeforeEach(func() { + input = `{ +"name": "cni-flannel", +"type": "flannel", +"delegate": ` + + delegateInput + + `}` + }) + + It("loads flannel network config with defaults", func() { + conf, err := loadFlannelNetConf([]byte(input)) + Expect(err).ShouldNot(HaveOccurred()) + Expect(conf.Name).To(Equal("cni-flannel")) + Expect(conf.Type).To(Equal("flannel")) + Expect(conf.SubnetFile).To(Equal(defaultSubnetFile)) + Expect(conf.StateDir).To(Equal(defaultStateDir)) + }) + }) + + Describe("loadFlannelSubnetEnv", func() { + Context("when flannel subnet env is valid", func() { + It("loads flannel subnet config", func() { + conf, err := loadFlannelSubnetEnv(subnetFile) + Expect(err).ShouldNot(HaveOccurred()) + Expect(conf.nw.String()).To(Equal("10.1.0.0/16")) + Expect(conf.sn.String()).To(Equal("10.1.17.0/24")) + var mtu uint = 1472 + Expect(*conf.mtu).To(Equal(mtu)) + Expect(*conf.ipmasq).To(BeTrue()) + }) + }) + + Context("when flannel subnet env is invalid", func() { + BeforeEach(func() { + subnetFile = writeSubnetEnv("foo=bar") + }) + It("returns an error", func() { + _, err := loadFlannelSubnetEnv(subnetFile) + Expect(err).To(MatchError(ContainSubstring("missing FLANNEL_NETWORK, FLANNEL_SUBNET, FLANNEL_MTU, FLANNEL_IPMASQ"))) + }) }) }) }) From e6113c6517ef10aa3217c106b0d7747b8bffccd7 Mon Sep 17 00:00:00 2001 From: "Mark St.Godard" Date: Wed, 9 Nov 2016 09:34:35 -0600 Subject: [PATCH 4/6] flannel: update flannel documentation add optional 'stateDir' to flannel docs --- Documentation/flannel.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/flannel.md b/Documentation/flannel.md index 2399ac48..c0ab3fde 100644 --- a/Documentation/flannel.md +++ b/Documentation/flannel.md @@ -73,6 +73,7 @@ To use `ipvlan` instead of `bridge`, the following configuration can be specifie * `name` (string, required): the name of the network * `type` (string, required): "flannel" * `subnetFile` (string, optional): full path to the subnet file written out by flanneld. Defaults to /run/flannel/subnet.env +* `stateDir` (string, optional): path to directory where plugin will store generated network configuration files. Defaults to `/var/lib/cni/flannel` * `delegate` (dictionary, optional): specifies configuration options for the delegated plugin. flannel plugin will always set the following fields in the delegated plugin configuration: From 15de81eac69be29ae25e9679eb3ece6ce4c53f6a Mon Sep 17 00:00:00 2001 From: "Mark St.Godard" Date: Sun, 13 Nov 2016 12:16:32 -0600 Subject: [PATCH 5/6] flannel: updated flannel test to use pkg/testutils reworked the flannel test to use testutils CmdAddWithResult and CmdDelWithResult --- plugins/meta/flannel/flannel_suite_test.go | 40 ------ plugins/meta/flannel/flannel_test.go | 137 +++++++++------------ 2 files changed, 56 insertions(+), 121 deletions(-) diff --git a/plugins/meta/flannel/flannel_suite_test.go b/plugins/meta/flannel/flannel_suite_test.go index fc96d4a5..ccdffde5 100644 --- a/plugins/meta/flannel/flannel_suite_test.go +++ b/plugins/meta/flannel/flannel_suite_test.go @@ -14,13 +14,8 @@ package main import ( - "encoding/json" - "fmt" - "path/filepath" - . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/onsi/gomega/gexec" "testing" ) @@ -29,38 +24,3 @@ func TestFlannel(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Flannel Suite") } - -const flannelPackage = "github.com/containernetworking/cni/plugins/meta/flannel" -const noopPackage = "github.com/containernetworking/cni/plugins/test/noop" - -var paths testPaths - -type testPaths struct { - PathToPlugin string - CNIPath string -} - -var _ = SynchronizedBeforeSuite(func() []byte { - noopBin, err := gexec.Build(noopPackage) - Expect(err).NotTo(HaveOccurred()) - noopDir, _ := filepath.Split(noopBin) - - pathToPlugin, err := gexec.Build(flannelPackage) - Expect(err).NotTo(HaveOccurred()) - flannelDir, _ := filepath.Split(pathToPlugin) - - paths := testPaths{ - PathToPlugin: pathToPlugin, - CNIPath: fmt.Sprintf("%s:%s", flannelDir, noopDir), - } - - data, err := json.Marshal(paths) - Expect(err).NotTo(HaveOccurred()) - return data -}, func(data []byte) { - Expect(json.Unmarshal(data, &paths)).To(Succeed()) -}) - -var _ = SynchronizedAfterSuite(func() {}, func() { - gexec.CleanupBuildArtifacts() -}) diff --git a/plugins/meta/flannel/flannel_test.go b/plugins/meta/flannel/flannel_test.go index 177c3c71..a119b7f1 100644 --- a/plugins/meta/flannel/flannel_test.go +++ b/plugins/meta/flannel/flannel_test.go @@ -17,44 +17,40 @@ import ( "fmt" "io/ioutil" "os" - "os/exec" - "strings" + "github.com/containernetworking/cni/pkg/ns" "github.com/containernetworking/cni/pkg/skel" + "github.com/containernetworking/cni/pkg/testutils" - noop_debug "github.com/containernetworking/cni/plugins/test/noop/debug" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/onsi/gomega/gexec" ) var _ = Describe("Flannel", func() { var ( - cmd *exec.Cmd - debugFileName string - input string - debug *noop_debug.Debug - expectedCmdArgs skel.CmdArgs - subnetFile string - stateDir string + originalNS ns.NetNS + input string + subnetFile string + stateDir string ) - const delegateInput = ` -{ - "type": "noop", - "some": "other data" -} -` + BeforeEach(func() { + var err error + originalNS, err = ns.NewNS() + Expect(err).NotTo(HaveOccurred()) + }) + + AfterEach(func() { + Expect(originalNS.Close()).To(Succeed()) + }) const inputTemplate = ` { "name": "cni-flannel", "type": "flannel", "subnetFile": "%s", - "stateDir": "%s", - "delegate": ` + - delegateInput + - `}` + "stateDir": "%s" +}` const flannelSubnetEnv = ` FLANNEL_NETWORK=10.1.0.0/16 @@ -71,32 +67,8 @@ FLANNEL_IPMASQ=true return file.Name() } - var cniCommand = func(command, input string) *exec.Cmd { - toReturn := exec.Command(paths.PathToPlugin) - toReturn.Env = []string{ - "CNI_COMMAND=" + command, - "CNI_CONTAINERID=some-container-id", - "CNI_NETNS=/some/netns/path", - "CNI_IFNAME=some-eth0", - "CNI_PATH=" + paths.CNIPath, - "CNI_ARGS=DEBUG=" + debugFileName, - } - toReturn.Stdin = strings.NewReader(input) - return toReturn - } - BeforeEach(func() { - debugFile, err := ioutil.TempFile("", "cni_debug") - Expect(err).NotTo(HaveOccurred()) - Expect(debugFile.Close()).To(Succeed()) - debugFileName = debugFile.Name() - - debug = &noop_debug.Debug{ - ReportResult: `{ "ip4": { "ip": "1.2.3.4/32" } }`, - ReportVersionSupport: []string{"0.1.0", "0.2.0", "0.3.0"}, - } - Expect(debug.WriteDebug(debugFileName)).To(Succeed()) - + var err error // flannel subnet.env subnetFile = writeSubnetEnv(flannelSubnetEnv) @@ -107,41 +79,43 @@ FLANNEL_IPMASQ=true }) AfterEach(func() { - os.Remove(debugFileName) os.Remove(subnetFile) os.Remove(stateDir) }) Describe("CNI lifecycle", func() { + It("uses stateDir for storing network configuration", func() { + const IFNAME = "eth0" - BeforeEach(func() { - expectedCmdArgs = skel.CmdArgs{ + targetNs, err := ns.NewNS() + Expect(err).NotTo(HaveOccurred()) + defer targetNs.Close() + + args := &skel.CmdArgs{ ContainerID: "some-container-id", - Netns: "/some/netns/path", - IfName: "some-eth0", - Args: "DEBUG=" + debugFileName, - Path: "/some/bin/path", + Netns: targetNs.Path(), + IfName: IFNAME, StdinData: []byte(input), } - cmd = cniCommand("ADD", input) - }) - It("uses stateDir for storing network configuration", func() { - By("calling ADD") - session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter) - Expect(err).NotTo(HaveOccurred()) - Eventually(session).Should(gexec.Exit(0)) - Expect(session.Out.Contents()).To(MatchJSON(`{ "ip4": { "ip": "1.2.3.4/32" }, "dns":{} }`)) + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - By("check that plugin writes to net config to stateDir") - path := fmt.Sprintf("%s/%s", stateDir, "some-container-id") - Expect(path).Should(BeAnExistingFile()) + By("calling ADD") + _, err := testutils.CmdAddWithResult(targetNs.Path(), IFNAME, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) - netConfBytes, err := ioutil.ReadFile(path) - Expect(err).NotTo(HaveOccurred()) - expected := `{ + By("check that plugin writes to net config to stateDir") + path := fmt.Sprintf("%s/%s", stateDir, "some-container-id") + Expect(path).Should(BeAnExistingFile()) + + netConfBytes, err := ioutil.ReadFile(path) + Expect(err).NotTo(HaveOccurred()) + expected := `{ "name" : "cni-flannel", - "type" : "noop", + "type" : "bridge", "ipam" : { "type" : "host-local", "subnet" : "10.1.17.0/24", @@ -153,19 +127,22 @@ FLANNEL_IPMASQ=true }, "mtu" : 1472, "ipMasq" : false, - "some" : "other data" + "isGateway": true } ` - Expect(netConfBytes).Should(MatchJSON(expected)) + Expect(netConfBytes).Should(MatchJSON(expected)) - By("calling DEL") - cmd = cniCommand("DEL", input) - session, err = gexec.Start(cmd, GinkgoWriter, GinkgoWriter) + By("calling DEL") + err = testutils.CmdDelWithResult(targetNs.Path(), IFNAME, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + + By("check that plugin removes net config from state dir") + Expect(path).ShouldNot(BeAnExistingFile()) + return nil + }) Expect(err).NotTo(HaveOccurred()) - Eventually(session).Should(gexec.Exit(0)) - - By("check that plugin removes net config from state dir") - Expect(path).ShouldNot(BeAnExistingFile()) }) }) @@ -185,10 +162,8 @@ FLANNEL_IPMASQ=true BeforeEach(func() { input = `{ "name": "cni-flannel", -"type": "flannel", -"delegate": ` + - delegateInput + - `}` +"type": "flannel" +}` }) It("loads flannel network config with defaults", func() { From 6eac0ee904417fc49e8aca749b031e9ea82b5802 Mon Sep 17 00:00:00 2001 From: "Mark St.Godard" Date: Thu, 17 Nov 2016 15:54:29 -0600 Subject: [PATCH 6/6] flannel: rename stateDir to dataDir Rename StateDir to DataDir for flannel CNI plugin --- Documentation/flannel.md | 2 +- plugins/meta/flannel/flannel.go | 24 ++++++++++++------------ plugins/meta/flannel/flannel_test.go | 24 ++++++++++++------------ 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Documentation/flannel.md b/Documentation/flannel.md index c0ab3fde..0efb6905 100644 --- a/Documentation/flannel.md +++ b/Documentation/flannel.md @@ -73,7 +73,7 @@ To use `ipvlan` instead of `bridge`, the following configuration can be specifie * `name` (string, required): the name of the network * `type` (string, required): "flannel" * `subnetFile` (string, optional): full path to the subnet file written out by flanneld. Defaults to /run/flannel/subnet.env -* `stateDir` (string, optional): path to directory where plugin will store generated network configuration files. Defaults to `/var/lib/cni/flannel` +* `dataDir` (string, optional): path to directory where plugin will store generated network configuration files. Defaults to `/var/lib/cni/flannel` * `delegate` (dictionary, optional): specifies configuration options for the delegated plugin. flannel plugin will always set the following fields in the delegated plugin configuration: diff --git a/plugins/meta/flannel/flannel.go b/plugins/meta/flannel/flannel.go index 768fbf28..911ec947 100644 --- a/plugins/meta/flannel/flannel.go +++ b/plugins/meta/flannel/flannel.go @@ -37,13 +37,13 @@ import ( const ( defaultSubnetFile = "/run/flannel/subnet.env" - defaultStateDir = "/var/lib/cni/flannel" + defaultDataDir = "/var/lib/cni/flannel" ) type NetConf struct { types.NetConf SubnetFile string `json:"subnetFile"` - StateDir string `json:"stateDir"` + DataDir string `json:"dataDir"` Delegate map[string]interface{} `json:"delegate"` } @@ -75,7 +75,7 @@ func (se *subnetEnv) missing() string { func loadFlannelNetConf(bytes []byte) (*NetConf, error) { n := &NetConf{ SubnetFile: defaultSubnetFile, - StateDir: defaultStateDir, + DataDir: defaultDataDir, } if err := json.Unmarshal(bytes, n); err != nil { return nil, fmt.Errorf("failed to load netconf: %v", err) @@ -132,29 +132,29 @@ func loadFlannelSubnetEnv(fn string) (*subnetEnv, error) { return se, nil } -func saveScratchNetConf(containerID, stateDir string, netconf []byte) error { - if err := os.MkdirAll(stateDir, 0700); err != nil { +func saveScratchNetConf(containerID, dataDir string, netconf []byte) error { + if err := os.MkdirAll(dataDir, 0700); err != nil { return err } - path := filepath.Join(stateDir, containerID) + path := filepath.Join(dataDir, containerID) return ioutil.WriteFile(path, netconf, 0600) } -func consumeScratchNetConf(containerID, stateDir string) ([]byte, error) { - path := filepath.Join(stateDir, containerID) +func consumeScratchNetConf(containerID, dataDir string) ([]byte, error) { + path := filepath.Join(dataDir, containerID) defer os.Remove(path) return ioutil.ReadFile(path) } -func delegateAdd(cid, stateDir string, netconf map[string]interface{}) error { +func delegateAdd(cid, dataDir string, netconf map[string]interface{}) error { netconfBytes, err := json.Marshal(netconf) if err != nil { return fmt.Errorf("error serializing delegate netconf: %v", err) } // save the rendered netconf for cmdDel - if err = saveScratchNetConf(cid, stateDir, netconfBytes); err != nil { + if err = saveScratchNetConf(cid, dataDir, netconfBytes); err != nil { return err } @@ -234,7 +234,7 @@ func cmdAdd(args *skel.CmdArgs) error { }, } - return delegateAdd(args.ContainerID, n.StateDir, n.Delegate) + return delegateAdd(args.ContainerID, n.DataDir, n.Delegate) } func cmdDel(args *skel.CmdArgs) error { @@ -243,7 +243,7 @@ func cmdDel(args *skel.CmdArgs) error { return err } - netconfBytes, err := consumeScratchNetConf(args.ContainerID, nc.StateDir) + netconfBytes, err := consumeScratchNetConf(args.ContainerID, nc.DataDir) if err != nil { return err } diff --git a/plugins/meta/flannel/flannel_test.go b/plugins/meta/flannel/flannel_test.go index a119b7f1..4434f913 100644 --- a/plugins/meta/flannel/flannel_test.go +++ b/plugins/meta/flannel/flannel_test.go @@ -31,7 +31,7 @@ var _ = Describe("Flannel", func() { originalNS ns.NetNS input string subnetFile string - stateDir string + dataDir string ) BeforeEach(func() { @@ -49,7 +49,7 @@ var _ = Describe("Flannel", func() { "name": "cni-flannel", "type": "flannel", "subnetFile": "%s", - "stateDir": "%s" + "dataDir": "%s" }` const flannelSubnetEnv = ` @@ -73,18 +73,18 @@ FLANNEL_IPMASQ=true subnetFile = writeSubnetEnv(flannelSubnetEnv) // flannel state dir - stateDir, err = ioutil.TempDir("", "stateDir") + dataDir, err = ioutil.TempDir("", "dataDir") Expect(err).NotTo(HaveOccurred()) - input = fmt.Sprintf(inputTemplate, subnetFile, stateDir) + input = fmt.Sprintf(inputTemplate, subnetFile, dataDir) }) AfterEach(func() { os.Remove(subnetFile) - os.Remove(stateDir) + os.Remove(dataDir) }) Describe("CNI lifecycle", func() { - It("uses stateDir for storing network configuration", func() { + It("uses dataDir for storing network configuration", func() { const IFNAME = "eth0" targetNs, err := ns.NewNS() @@ -107,8 +107,8 @@ FLANNEL_IPMASQ=true }) Expect(err).NotTo(HaveOccurred()) - By("check that plugin writes to net config to stateDir") - path := fmt.Sprintf("%s/%s", stateDir, "some-container-id") + By("check that plugin writes to net config to dataDir") + path := fmt.Sprintf("%s/%s", dataDir, "some-container-id") Expect(path).Should(BeAnExistingFile()) netConfBytes, err := ioutil.ReadFile(path) @@ -147,18 +147,18 @@ FLANNEL_IPMASQ=true }) Describe("loadFlannelNetConf", func() { - Context("when subnetFile and stateDir are specified", func() { + Context("when subnetFile and dataDir are specified", func() { It("loads flannel network config", func() { conf, err := loadFlannelNetConf([]byte(input)) Expect(err).ShouldNot(HaveOccurred()) Expect(conf.Name).To(Equal("cni-flannel")) Expect(conf.Type).To(Equal("flannel")) Expect(conf.SubnetFile).To(Equal(subnetFile)) - Expect(conf.StateDir).To(Equal(stateDir)) + Expect(conf.DataDir).To(Equal(dataDir)) }) }) - Context("when defaulting subnetFile and stateDir", func() { + Context("when defaulting subnetFile and dataDir", func() { BeforeEach(func() { input = `{ "name": "cni-flannel", @@ -172,7 +172,7 @@ FLANNEL_IPMASQ=true Expect(conf.Name).To(Equal("cni-flannel")) Expect(conf.Type).To(Equal("flannel")) Expect(conf.SubnetFile).To(Equal(defaultSubnetFile)) - Expect(conf.StateDir).To(Equal(defaultStateDir)) + Expect(conf.DataDir).To(Equal(defaultDataDir)) }) })